diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/gui/graphicsview | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/gui/graphicsview')
36 files changed, 34386 insertions, 0 deletions
diff --git a/src/gui/graphicsview/graphicsview.pri b/src/gui/graphicsview/graphicsview.pri new file mode 100644 index 0000000..02d9bb1 --- /dev/null +++ b/src/gui/graphicsview/graphicsview.pri @@ -0,0 +1,46 @@ +# Qt graphicsview module + +HEADERS += \ + graphicsview/qgraphicsitem.h \ + graphicsview/qgraphicsitem_p.h \ + graphicsview/qgraphicsitemanimation.h \ + graphicsview/qgraphicsscene.h \ + graphicsview/qgraphicsscene_p.h \ + graphicsview/qgraphicsscene_bsp_p.h \ + graphicsview/qgraphicssceneevent.h \ + graphicsview/qgraphicsview_p.h \ + graphicsview/qgraphicsview.h + +SOURCES += \ + graphicsview/qgraphicsitem.cpp \ + graphicsview/qgraphicsitemanimation.cpp \ + graphicsview/qgraphicsscene.cpp \ + graphicsview/qgraphicsscene_bsp.cpp \ + graphicsview/qgraphicssceneevent.cpp \ + graphicsview/qgraphicsview.cpp + +# Widgets on the canvas + +HEADERS += \ + graphicsview/qgraphicslayout.h \ + graphicsview/qgraphicslayout_p.h \ + graphicsview/qgraphicslayoutitem.h \ + graphicsview/qgraphicslayoutitem_p.h \ + graphicsview/qgraphicslinearlayout.h \ + graphicsview/qgraphicswidget.h \ + graphicsview/qgraphicswidget_p.h \ + graphicsview/qgridlayoutengine_p.h \ + graphicsview/qgraphicsproxywidget.h \ + graphicsview/qgraphicsgridlayout.h + +SOURCES += \ + graphicsview/qgraphicslayout.cpp \ + graphicsview/qgraphicslayout_p.cpp \ + graphicsview/qgraphicslayoutitem.cpp \ + graphicsview/qgraphicslinearlayout.cpp \ + graphicsview/qgraphicswidget.cpp \ + graphicsview/qgraphicswidget_p.cpp \ + graphicsview/qgridlayoutengine.cpp \ + graphicsview/qgraphicsproxywidget.cpp \ + graphicsview/qgraphicsgridlayout.cpp + diff --git a/src/gui/graphicsview/qgraphicsgridlayout.cpp b/src/gui/graphicsview/qgraphicsgridlayout.cpp new file mode 100644 index 0000000..1e21b54 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsgridlayout.cpp @@ -0,0 +1,651 @@ +/**************************************************************************** +** +** 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 QGraphicsGridLayout + \brief The QGraphicsGridLayout class provides a grid layout for managing + widgets in Graphics View. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + The most common way to use QGraphicsGridLayout is to construct an object + on the heap with no parent, add widgets and layouts by calling addItem(), + and finally assign the layout to a widget by calling + QGraphicsWidget::setLayout(). QGraphicsGridLayout automatically computes + the dimensions of the grid as you add items. + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsgridlayout.cpp 0 + + The layout takes ownership of the items. In some cases when the layout + item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a + ambiguity in ownership because the layout item belongs to two ownership hierarchies. + See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle + this. + You can access each item in the layout by calling count() and itemAt(). Calling + removeAt() will remove an item from the layout, without + destroying it. + + \sa QGraphicsLinearLayout, QGraphicsWidget +*/ + +#include "qglobal.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qapplication.h" +#include "qwidget.h" +#include "qgraphicslayout_p.h" +#include "qgraphicslayoutitem.h" +#include "qgraphicsgridlayout.h" +#include "qgraphicswidget.h" +#include "qgridlayoutengine_p.h" +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsGridLayoutPrivate : public QGraphicsLayoutPrivate +{ +public: + QGraphicsGridLayoutPrivate() { } + QLayoutStyleInfo styleInfo() const; + + QGridLayoutEngine engine; +#ifdef QT_DEBUG + void dump(int indent) const; +#endif +}; + +QLayoutStyleInfo QGraphicsGridLayoutPrivate::styleInfo() const +{ + static QWidget *wid = 0; + if (!wid) + wid = new QWidget; + QGraphicsItem *item = parentItem(); + QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style(); + return QLayoutStyleInfo(style, wid); +} + +/*! + Constructs a QGraphicsGridLayout instance. \a parent is passed to + QGraphicsLayout's constructor. +*/ +QGraphicsGridLayout::QGraphicsGridLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayout(*new QGraphicsGridLayoutPrivate(), parent) +{ +} + +/*! + Destroys the QGraphicsGridLayout object. +*/ +QGraphicsGridLayout::~QGraphicsGridLayout() +{ + for (int i = count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *item = itemAt(i); + // The following lines can be removed, but this removes the item + // from the layout more efficiently than the implementation of + // ~QGraphicsLayoutItem. + removeAt(i); + if (item) { + item->setParentLayoutItem(0); + if (item->ownedByLayout()) + delete item; + } + } +} + +/*! + Adds \a item to the grid on \a row and \a column. You can specify a + \a rowSpan and \a columnSpan and an optional \a alignment. +*/ +void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column, + int rowSpan, int columnSpan, Qt::Alignment alignment) +{ + Q_D(QGraphicsGridLayout); + if (row < 0 || column < 0) { + qWarning("QGraphicsGridLayout::addItem: invalid row/column: %d", + row < 0 ? row : column); + return; + } + if (columnSpan < 1 || rowSpan < 1) { + qWarning("QGraphicsGridLayout::addItem: invalid row span/column span: %d", + rowSpan < 1 ? rowSpan : columnSpan); + return; + } + if (!item) { + qWarning("QGraphicsGridLayout::addItem: cannot add null item"); + return; + } + + d->addChildLayoutItem(item); + + new QGridLayoutItem(&d->engine, item, row, column, rowSpan, columnSpan, alignment); + invalidate(); +} + +/*! + \fn QGraphicsGridLayout::addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0) + + Adds \a item to the grid on \a row and \a column. You can specify + an optional \a alignment for \a item. +*/ + +/*! + Sets the default horizontal spacing for the grid layout to \a spacing. +*/ +void QGraphicsGridLayout::setHorizontalSpacing(qreal spacing) +{ + Q_D(QGraphicsGridLayout); + d->engine.setSpacing(spacing, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the default horizontal spacing for the grid layout. +*/ +qreal QGraphicsGridLayout::horizontalSpacing() const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.spacing(d->styleInfo(), Qt::Horizontal); +} + +/*! + Sets the default vertical spacing for the grid layout to \a spacing. +*/ +void QGraphicsGridLayout::setVerticalSpacing(qreal spacing) +{ + Q_D(QGraphicsGridLayout); + d->engine.setSpacing(spacing, Qt::Vertical); + invalidate(); +} + +/*! + Returns the default vertical spacing for the grid layout. +*/ +qreal QGraphicsGridLayout::verticalSpacing() const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.spacing(d->styleInfo(), Qt::Vertical); +} + +/*! + Sets the grid layout's default spacing, both vertical and + horizontal, to \a spacing. + + \sa rowSpacing(), columnSpacing() +*/ +void QGraphicsGridLayout::setSpacing(qreal spacing) +{ + Q_D(QGraphicsGridLayout); + d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + +/*! + Sets the spacing for \a row to \a spacing. +*/ +void QGraphicsGridLayout::setRowSpacing(int row, qreal spacing) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSpacing(row, spacing, Qt::Vertical); + invalidate(); +} + +/*! + Returns the row spacing for \a row. +*/ +qreal QGraphicsGridLayout::rowSpacing(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSpacing(row, Qt::Vertical); +} + +/*! + Sets the spacing for \a column to \a spacing. +*/ +void QGraphicsGridLayout::setColumnSpacing(int column, qreal spacing) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSpacing(column, spacing, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the column spacing for \a column. +*/ +qreal QGraphicsGridLayout::columnSpacing(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSpacing(column, Qt::Horizontal); +} + +/*! + Sets the stretch factor for \a row to \a stretch. +*/ +void QGraphicsGridLayout::setRowStretchFactor(int row, int stretch) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowStretchFactor(row, stretch, Qt::Vertical); + invalidate(); +} + +/*! + Returns the stretch factor for \a row. +*/ +int QGraphicsGridLayout::rowStretchFactor(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowStretchFactor(row, Qt::Vertical); +} + +/*! + Sets the stretch factor for \a column to \a stretch. +*/ +void QGraphicsGridLayout::setColumnStretchFactor(int column, int stretch) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowStretchFactor(column, stretch, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the stretch factor for \a column. +*/ +int QGraphicsGridLayout::columnStretchFactor(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowStretchFactor(column, Qt::Horizontal); +} + +/*! + Sets the minimum height for row, \a row, to \a height. +*/ +void QGraphicsGridLayout::setRowMinimumHeight(int row, qreal height) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical); + invalidate(); +} + +/*! + Returns the minimum height for row, \a row. +*/ +qreal QGraphicsGridLayout::rowMinimumHeight(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::MinimumSize, row, Qt::Vertical); +} + +/*! + Sets the preferred height for row, \a row, to \a height. +*/ +void QGraphicsGridLayout::setRowPreferredHeight(int row, qreal height) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::PreferredSize, row, height, Qt::Vertical); + invalidate(); +} + +/*! + Returns the preferred height for row, \a row. +*/ +qreal QGraphicsGridLayout::rowPreferredHeight(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::PreferredSize, row, Qt::Vertical); +} + +/*! + Sets the maximum height for row, \a row, to \a height. +*/ +void QGraphicsGridLayout::setRowMaximumHeight(int row, qreal height) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical); + invalidate(); +} + +/*! + Returns the maximum height for row, \a row. +*/ +qreal QGraphicsGridLayout::rowMaximumHeight(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::MaximumSize, row, Qt::Vertical); +} + +/*! + Sets the fixed height for row, \a row, to \a height. +*/ +void QGraphicsGridLayout::setRowFixedHeight(int row, qreal height) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MinimumSize, row, height, Qt::Vertical); + d->engine.setRowSizeHint(Qt::MaximumSize, row, height, Qt::Vertical); + invalidate(); +} + +/*! + Sets the minimum width for \a column to \a width. +*/ +void QGraphicsGridLayout::setColumnMinimumWidth(int column, qreal width) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the minimum width for \a column. +*/ +qreal QGraphicsGridLayout::columnMinimumWidth(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::MinimumSize, column, Qt::Horizontal); +} + +/*! + Sets the preferred width for \a column to \a width. +*/ +void QGraphicsGridLayout::setColumnPreferredWidth(int column, qreal width) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::PreferredSize, column, width, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the preferred width for \a column. +*/ +qreal QGraphicsGridLayout::columnPreferredWidth(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::PreferredSize, column, Qt::Horizontal); +} + +/*! + Sets the maximum width of \a column to \a width. +*/ +void QGraphicsGridLayout::setColumnMaximumWidth(int column, qreal width) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the maximum width for \a column. +*/ +qreal QGraphicsGridLayout::columnMaximumWidth(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowSizeHint(Qt::MaximumSize, column, Qt::Horizontal); +} + +/*! + Sets the fixed width of \a column to \a width. +*/ +void QGraphicsGridLayout::setColumnFixedWidth(int column, qreal width) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowSizeHint(Qt::MinimumSize, column, width, Qt::Horizontal); + d->engine.setRowSizeHint(Qt::MaximumSize, column, width, Qt::Horizontal); + invalidate(); +} + +/*! + Sets the alignment of \a row to \a alignment. +*/ +void QGraphicsGridLayout::setRowAlignment(int row, Qt::Alignment alignment) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowAlignment(row, alignment, Qt::Vertical); + invalidate(); +} + +/*! + Returns the alignment of \a row. +*/ +Qt::Alignment QGraphicsGridLayout::rowAlignment(int row) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowAlignment(row, Qt::Vertical); +} + +/*! + Sets the alignment for \a column to \a alignment. +*/ +void QGraphicsGridLayout::setColumnAlignment(int column, Qt::Alignment alignment) +{ + Q_D(QGraphicsGridLayout); + d->engine.setRowAlignment(column, alignment, Qt::Horizontal); + invalidate(); +} + +/*! + Returns the alignment for \a column. +*/ +Qt::Alignment QGraphicsGridLayout::columnAlignment(int column) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.rowAlignment(column, Qt::Horizontal); +} + +/*! + Sets the alignment for \a item to \a alignment. +*/ +void QGraphicsGridLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment) +{ + Q_D(QGraphicsGridLayout); + d->engine.setAlignment(item, alignment); + invalidate(); +} + +/*! + Returns the alignment for \a item. +*/ +Qt::Alignment QGraphicsGridLayout::alignment(QGraphicsLayoutItem *item) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.alignment(item); +} + +/*! + Returns the number of rows in the grid layout. This is always one more + than the index of the last row that is occupied by a layout item (empty + rows are counted except for those at the end). +*/ +int QGraphicsGridLayout::rowCount() const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.effectiveLastRow(Qt::Vertical) + 1; +} + +/*! + Returns the number of columns in the grid layout. This is always one more + than the index of the last column that is occupied by a layout item (empty + columns are counted except for those at the end). +*/ +int QGraphicsGridLayout::columnCount() const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.effectiveLastRow(Qt::Horizontal) + 1; +} + +/*! + Returns a pointer to the layout item at (\a row, \a column). +*/ +QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int row, int column) const +{ + Q_D(const QGraphicsGridLayout); + if (row < 0 || row >= rowCount() || column < 0 || column >= columnCount()) { + qWarning("QGraphicsGridLayout::itemAt: invalid row, column %d, %d", row, column); + return 0; + } + if (QGridLayoutItem *item = d->engine.itemAt(row, column)) + return item->layoutItem(); + return 0; +} + +/*! + Returns the number of layout items in this grid layout. +*/ +int QGraphicsGridLayout::count() const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.itemCount(); +} + +/*! + Returns the layout item at \a index, or 0 if there is no layout item at + this index. +*/ +QGraphicsLayoutItem *QGraphicsGridLayout::itemAt(int index) const +{ + Q_D(const QGraphicsGridLayout); + if (index < 0 || index >= d->engine.itemCount()) { + qWarning("QGraphicsGridLayout::itemAt: invalid index %d", index); + return 0; + } + QGraphicsLayoutItem *item = 0; + if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) + item = gridItem->layoutItem(); + return item; +} + +/*! + Removes the layout item at \a index without destroying it. Ownership of + the item is transferred to the caller. + + \sa addItem() +*/ +void QGraphicsGridLayout::removeAt(int index) +{ + Q_D(QGraphicsGridLayout); + if (index < 0 || index >= d->engine.itemCount()) { + qWarning("QGraphicsGridLayout::removeAt: invalid index %d", index); + return; + } + if (QGridLayoutItem *gridItem = d->engine.itemAt(index)) { + if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem()) + layoutItem->setParentLayoutItem(0); + d->engine.removeItem(gridItem); + delete gridItem; + invalidate(); + } +} + +/*! + \reimp +*/ +void QGraphicsGridLayout::invalidate() +{ + Q_D(QGraphicsGridLayout); + d->engine.invalidate(); + QGraphicsLayout::invalidate(); +} + +#ifdef QT_DEBUG +void QGraphicsGridLayoutPrivate::dump(int indent) const +{ + if (qt_graphicsLayoutDebug()) { + engine.dump(indent + 1); + } +} +#endif + +/*! + Sets the bounding geometry of the grid layout to \a rect. +*/ +void QGraphicsGridLayout::setGeometry(const QRectF &rect) +{ + Q_D(QGraphicsGridLayout); + QGraphicsLayout::setGeometry(rect); + QRectF effectiveRect = geometry(); + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + Qt::LayoutDirection visualDir = d->visualDirection(); + d->engine.setVisualDirection(visualDir); + if (visualDir == Qt::RightToLeft) + qSwap(left, right); + effectiveRect.adjust(+left, +top, -right, -bottom); + d->engine.setGeometries(d->styleInfo(), effectiveRect); +#ifdef QT_DEBUG + if (qt_graphicsLayoutDebug()) { + static int counter = 0; + qDebug("==== BEGIN DUMP OF QGraphicsGridLayout (%d)====", counter++); + d->dump(1); + qDebug("==== END DUMP OF QGraphicsGridLayout ===="); + } +#endif +} + +/*! + \reimp +*/ +QSizeF QGraphicsGridLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_D(const QGraphicsGridLayout); + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + return d->engine.sizeHint(d->styleInfo(), which , constraint) + QSizeF(left + right, top + bottom); +} + + +#if 0 +// ### kill? (implement and kill?) +QRect QGraphicsGridLayout::cellRect(int row, int column, int rowSpan, int columnSpan) const +{ + Q_D(const QGraphicsGridLayout); + return QRect(); +// return d->engine.cellRect(parentLayoutable(), contentsGeometry(), row, column, rowSpan, columnSpan); +} + +QSizePolicy::ControlTypes QGraphicsGridLayout::controlTypes(LayoutSide side) const +{ + Q_D(const QGraphicsGridLayout); + return d->engine.controlTypes(side); +} +#endif + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsgridlayout.h b/src/gui/graphicsview/qgraphicsgridlayout.h new file mode 100644 index 0000000..5b40d6b --- /dev/null +++ b/src/gui/graphicsview/qgraphicsgridlayout.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSGRIDLAYOUT_H +#define QGRAPHICSGRIDLAYOUT_H + +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicslayout.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsGridLayoutPrivate; + +class Q_GUI_EXPORT QGraphicsGridLayout : public QGraphicsLayout +{ +public: + QGraphicsGridLayout(QGraphicsLayoutItem *parent = 0); + virtual ~QGraphicsGridLayout(); + + void addItem(QGraphicsLayoutItem *item, int row, int column, int rowSpan, int columnSpan, + Qt::Alignment alignment = 0); + inline void addItem(QGraphicsLayoutItem *item, int row, int column, Qt::Alignment alignment = 0); + + void setHorizontalSpacing(qreal spacing); + qreal horizontalSpacing() const; + void setVerticalSpacing(qreal spacing); + qreal verticalSpacing() const; + void setSpacing(qreal spacing); + + void setRowSpacing(int row, qreal spacing); + qreal rowSpacing(int row) const; + void setColumnSpacing(int column, qreal spacing); + qreal columnSpacing(int column) const; + + void setRowStretchFactor(int row, int stretch); + int rowStretchFactor(int row) const; + void setColumnStretchFactor(int column, int stretch); + int columnStretchFactor(int column) const; + + void setRowMinimumHeight(int row, qreal height); + qreal rowMinimumHeight(int row) const; + void setRowPreferredHeight(int row, qreal height); + qreal rowPreferredHeight(int row) const; + void setRowMaximumHeight(int row, qreal height); + qreal rowMaximumHeight(int row) const; + void setRowFixedHeight(int row, qreal height); + + void setColumnMinimumWidth(int column, qreal width); + qreal columnMinimumWidth(int column) const; + void setColumnPreferredWidth(int column, qreal width); + qreal columnPreferredWidth(int column) const; + void setColumnMaximumWidth(int column, qreal width); + qreal columnMaximumWidth(int column) const; + void setColumnFixedWidth(int column, qreal width); + + void setRowAlignment(int row, Qt::Alignment alignment); + Qt::Alignment rowAlignment(int row) const; + void setColumnAlignment(int column, Qt::Alignment alignment); + Qt::Alignment columnAlignment(int column) const; + + void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment); + Qt::Alignment alignment(QGraphicsLayoutItem *item) const; + + int rowCount() const; + int columnCount() const; + + QGraphicsLayoutItem *itemAt(int row, int column) const; + + // inherited from QGraphicsLayout + int count() const; + QGraphicsLayoutItem *itemAt(int index) const; + void removeAt(int index); + + void invalidate(); + + // inherited from QGraphicsLayoutItem + void setGeometry(const QRectF &rect); + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + // #### + //QRect cellRect(int row, int column, int rowSpan = 1, int columnSpan = 1) const; + //QSizePolicy::ControlTypes controlTypes(LayoutSide side) const; + +private: + Q_DISABLE_COPY(QGraphicsGridLayout) + Q_DECLARE_PRIVATE(QGraphicsGridLayout) +}; + +inline void QGraphicsGridLayout::addItem(QGraphicsLayoutItem *aitem, int arow, int acolumn, Qt::Alignment aalignment) +{ addItem(aitem, arow, acolumn, 1, 1, aalignment); } + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/gui/graphicsview/qgraphicsitem.cpp b/src/gui/graphicsview/qgraphicsitem.cpp new file mode 100644 index 0000000..fc81f39 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsitem.cpp @@ -0,0 +1,8917 @@ +/**************************************************************************** +** +** 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 QGraphicsItem + \brief The QGraphicsItem class is the base class for all graphical + items in a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + It provides a light-weight foundation for writing your own custom items. + This includes defining the item's geometry, collision detection, its + painting implementation and item interaction through its event handlers. + QGraphicsItem is part of \l{The Graphics View Framework} + + \img graphicsview-items.png + + For convenience, Qt provides a set of standard graphics items for the most + common shapes. These are: + + \list + \o QGraphicsEllipseItem provides an ellipse item + \o QGraphicsLineItem provides a line item + \o QGraphicsPathItem provides an arbitrary path item + \o QGraphicsPixmapItem provides a pixmap item + \o QGraphicsPolygonItem provides a polygon item + \o QGraphicsRectItem provides a rectangular item + \o QGraphicsSimpleTextItem provides a simple text label item + \o QGraphicsTextItem provides an advanced text browser item + \endlist + + All of an item's geometric information is based on its local coordinate + system. The item's position, pos(), is the only function that does not + operate in local coordinates, as it returns a position in parent + coordinates. \l {The Graphics View Coordinate System} describes the coordinate + system in detail. + + You can set whether an item should be visible (i.e., drawn, and accepting + events), by calling setVisible(). Hiding an item will also hide its + children. Similarly, you can enable or disable an item by calling + setEnabled(). If you disable an item, all its children will also be + disabled. By default, items are both visible and enabled. To toggle + whether an item is selected or not, first enable selection by setting + the ItemIsSelectable flag, and then call setSelected(). Normally, + selection is toggled by the scene, as a result of user interaction. + + To write your own graphics item, you first create a subclass of + QGraphicsItem, and then start by implementing its two pure virtual public + functions: boundingRect(), which returns an estimate of the area painted + by the item, and paint(), which implements the actual painting. For + example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 0 + + The boundingRect() function has many different purposes. QGraphicsScene + bases its item index on boundingRect(), and QGraphicsView uses it both for + culling invisible items, and for determining the area that needs to be + recomposed when drawing overlapping items. In addition, QGraphicsItem's + collision detection mechanisms use boundingRect() to provide an efficient + cut-off. The fine grained collision algorithm in collidesWithItem() is based + on calling shape(), which returns an accurate outline of the item's shape + as a QPainterPath. + + QGraphicsScene expects all items boundingRect() and shape() to remain + unchanged unless it is notified. If you want to change an item's geometry + in any way, you must first call prepareGeometryChange() to allow + QGraphicsScene to update its bookkeeping. + + Collision detection can be done in two ways: + + \list 1 + + \o Reimplement shape() to return an accurate shape for your item, and rely + on the default implementation of collidesWithItem() to do shape-shape + intersection. This can be rather expensive if the shapes are complex. + + \o Reimplement collidesWithItem() to provide your own custom item and shape + collision algorithm. + + \endlist + + The contains() function can be called to determine whether the item \e + contains a point or not. This function can also be reimplemented by the + item. The default behavior of contains() is based on calling shape(). + + Items can contain other items, and also be contained by other items. All + items can have a parent item and a list of children. Unless the item has + no parent, its position is in \e parent coordinates (i.e., the parent's + local coordinates). Parent items propagate both their position and their + transformation to all children. + + \img graphicsview-parentchild.png + + QGraphicsItem supports affine transformations in addition to its base + position, pos(). To change the item's transformation, you can either pass + a transformation matrix to setTransform(), or call one of the convenience + functions rotate(), scale(), translate(), or shear(). Item transformations + accumulate from parent to child, so if both a parent and child item are + rotated 90 degrees, the child's total transformation will be 180 degrees. + Similarly, if the item's parent is scaled to 2x its original size, its + children will also be twice as large. An item's transformation does not + affect its own local geometry; all geometry functions (e.g., contains(), + update(), and all the mapping functions) still operate in local + coordinates. For convenience, QGraphicsItem provides the functions + sceneTransform(), which returns the item's total transformation matrix + (including its position and all parents' positions and transformations), + and scenePos(), which returns its position in scene coordinates. To reset + an item's matrix, call resetTransform(). + + The paint() function is called by QGraphicsView to paint the item's + contents. The item has no background or default fill of its own; whatever + is behind the item will shine through all areas that are not explicitly + painted in this function. You can call update() to schedule a repaint, + optionally passing the rectangle that needs a repaint. Depending on + whether or not the item is visible in a view, the item may or may not be + repainted; there is no equivalent to QWidget::repaint() in QGraphicsItem. + + Items are painted by the view, starting with the parent items and then + drawing children, in ascending stacking order. You can set an item's + stacking order by calling setZValue(), and test it by calling + zValue(), where items with low z-values are painted before items with + high z-values. Stacking order applies to sibling items; parents are always + drawn before their children. + + QGraphicsItem receives events from QGraphicsScene through the virtual + function sceneEvent(). This function distributes the most common events + to a set of convenience event handlers: + + \list + \o contextMenuEvent() handles context menu events + \o focusInEvent() and focusOutEvent() handle focus in and out events + \o hoverEnterEvent(), hoverMoveEvent(), and hoverLeaveEvent() handles + hover enter, move and leave events + \o inputMethodEvent() handles input events, for accessibility support + \o keyPressEvent() and keyReleaseEvent handle key press and release events + \o mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), and + mouseDoubleClickEvent() handles mouse press, move, release, click and + doubleclick events + \endlist + + You can filter events for any other item by installing event + filters. This functionaly is separate from from Qt's regular + event filters (see QObject::installEventFilter()), which only + work on subclasses of QObject. After installing your item as an + event filter for another item by calling + installSceneEventFilter(), the filtered events will be received + by the virtual function sceneEventFilter(). You can remove item + event filters by calling removeSceneEventFilter(). + + Sometimes it's useful to register custom data with an item, be it a custom + item, or a standard item. You can call setData() on any item to store data + in it using a key-value pair (the key being an integer, and the value is a + QVariant). To get custom data from an item, call data(). This + functionality is completely untouched by Qt itself; it is provided for the + user's convenience. + + \sa QGraphicsScene, QGraphicsView, {The Graphics View Framework} +*/ + +/*! + \variable QGraphicsItem::UserType + + The lowest permitted type value for custom items (subclasses + of QGraphicsItem or any of the standard items). This value is + used in conjunction with a reimplementation of QGraphicsItem::type() + and declaring a Type enum value. Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 1 +*/ + +/*! + \enum QGraphicsItem::GraphicsItemFlag + + This enum describes different flags that you can set on an item to + toggle different features in the item's behavior. + + All flags are disabled by default. + + \value ItemIsMovable The item supports interactive movement using + the mouse. By clicking on the item and then dragging, the item + will move together with the mouse cursor. If the item has + children, all children are also moved. If the item is part of a + selection, all selected items are also moved. This feature is + provided as a convenience through the base implementation of + QGraphicsItem's mouse event handlers. + + \value ItemIsSelectable The item supports selection. Enabling this + feature will enable setSelected() to toggle selection for the + item. It will also let the item be selected automatically as a + result of calling QGraphicsScene::setSelectionArea(), by clicking + on an item, or by using rubber band selection in QGraphicsView. + + \value ItemIsFocusable The item supports keyboard input focus (i.e., it is + an input item). Enabling this flag will allow the item to accept focus, + which again allows the delivery of key events to + QGraphicsItem::keyPressEvent() and QGraphicsItem::keyReleaseEvent(). + + \value ItemClipsToShape The item clips to its own shape. The item cannot + draw or receive mouse, tablet, drag and drop or hover events outside ts + shape. It is disabled by default. This behavior is enforced by + QGraphicsView::drawItems() or QGraphicsScene::drawItems(). This flag was + introduced in Qt 4.3. + + \value ItemClipsChildrenToShape The item clips the painting of all its + descendants to its own shape. Items that are either direct or indirect + children of this item cannot draw outside this item's shape. By default, + this flag is disabled; children can draw anywhere. This behavior is + enforced by QGraphicsView::drawItems() or + QGraphicsScene::drawItems(). This flag was introduced in Qt 4.3. + + \value ItemIgnoresTransformations The item ignores inherited + transformations (i.e., its position is still anchored to its parent, but + the parent or view rotation, zoom or shear transformations are ignored). + This flag is useful for keeping text label items horizontal and unscaled, + so they will still be readable if the view is transformed. When set, the + item's view geometry and scene geometry will be maintained separately. You + must call deviceTransform() to map coordinates and detect collisions in + the view. By default, this flag is disabled. This flag was introduced in + Qt 4.3. \note With this flag set you can still scale the item itself, and + that scale transformation will influence the item's children. + + \value ItemIgnoresParentOpacity The item ignores its parent's opacity. The + item's effective opacity is the same as its own; it does not combine with + the parent's opacity. This flags allows your item to keep its absolute + opacity even if the parent is semitransparent. This flag was introduced in + Qt 4.5. + + \value ItemDoesntPropagateOpacityToChildren The item doesn't propagate its + opacity to its children. This flag allows you to create a semitransparent + item that does not affect the opacity of its children. This flag was + introduced in Qt 4.5. + + \value ItemStacksBehindParent The item is stacked behind its parent. By + default, child items are stacked on top of the parent item. But setting + this flag, the child will be stacked behind it. This flag is useful for + drop shadow effects and for decoration objects that follow the parent + item's geometry without drawing on top of it. +*/ + +/*! + \enum QGraphicsItem::GraphicsItemChange + + ItemVisibleHasChanged, + ItemEnabledHasChanged, + ItemSelectedHasChanged, + ItemParentHasChanged, + ItemSceneHasChanged + + This enum describes the state changes that are notified by + QGraphicsItem::itemChange(). The notifications are sent as the state + changes, and in some cases, adjustments can be made (see the documentation + for each change for details). + + Note: Be careful with calling functions on the QGraphicsItem itself inside + itemChange(), as certain function calls can lead to unwanted + recursion. For example, you cannot call setPos() in itemChange() on an + ItemPositionChange notification, as the setPos() function will again call + itemChange(ItemPositionChange). Instead, you can return the new, adjusted + position from itemChange(). + + \value ItemEnabledChange The item's enabled state changes. If the item is + presently enabled, it will become disabled, and vice verca. The value + argument is the new enabled state (i.e., true or false). Do not call + setEnabled() in itemChange() as this notification is delivered. Instead, + you can return the new state from itemChange(). + + \value ItemEnabledHasChanged The item's enabled state has changed. The + value argument is the new enabled state (i.e., true or false). Do not call + setEnabled() in itemChange() as this notification is delivered. The return + value is ignored. + + \value ItemMatrixChange The item's affine transformation matrix is + changing. This value is obsolete; you can use ItemTransformChange instead. + + \value ItemPositionChange The item's position changes. This notification + is only sent when the item's local position changes, relative to its + parent, has changed (i.e., as a result of calling setPos() or + moveBy()). The value argument is the new position (i.e., a QPointF). You + can call pos() to get the original position. Do not call setPos() or + moveBy() in itemChange() as this notification is delivered; instead, you + can return the new, adjusted position from itemChange(). After this + notification, QGraphicsItem immediately sends the ItemPositionHasChanged + notification if the position changed. + + \value ItemPositionHasChanged The item's position has changed. This + notification is only sent after the item's local position, relative to its + parent, has changed. The value argument is the new position (the same as + pos()), and QGraphicsItem ignores the return value for this notification + (i.e., a read-only notification). + + \value ItemTransformChange The item's transformation matrix changes. This + notification is only sent when the item's local transformation matrix + changes (i.e., as a result of calling setTransform(), or one of the + convenience transformation functions, such as rotate()). The value + argument is the new matrix (i.e., a QTransform); to get the old matrix, + call transform(). Do not call setTransform() or any of the transformation + convenience functions in itemChange() as this notification is delivered; + instead, you can return the new matrix from itemChange(). + + \value ItemTransformHasChanged The item's transformation matrix has + changed. This notification is only sent after the item's local + trasformation matrix has changed. The value argument is the new matrix + (same as transform()), and QGraphicsItem ignores the return value for this + notification (i.e., a read-only notification). + + \value ItemSelectedChange The item's selected state changes. If the item + is presently selected, it will become unselected, and vice verca. The + value argument is the new selected state (i.e., true or false). Do not + call setSelected() in itemChange() as this notification is delivered(); + instead, you can return the new selected state from itemChange(). + + \value ItemSelectedHasChanged The item's selected state has changed. The + value argument is the new selected state (i.e., true or false). Do not + call setSelected() in itemChange() as this notification is delivered. The + return value is ignored. + + \value ItemVisibleChange The item's visible state changes. If the item is + presently visible, it will become invisible, and vice verca. The value + argument is the new visible state (i.e., true or false). Do not call + setVisible() in itemChange() as this notification is delivered; instead, + you can return the new visible state from itemChange(). + + \value ItemVisibleHasChanged The item's visible state has changed. The + value argument is the new visible state (i.e., true or false). Do not call + setVisible() in itemChange() as this notification is delivered. The return + value is ignored. + + \value ItemParentChange The item's parent changes. The value argument is + the new parent item (i.e., a QGraphicsItem pointer). Do not call + setParentItem() in itemChange() as this notification is delivered; + instead, you can return the new parent from itemChange(). + + \value ItemParentHasChanged The item's parent has changed. The value + argument is the new parent (i.e., a pointer to a QGraphicsItem). Do not + call setParentItem() in itemChange() as this notification is + delivered. The return value is ignored. + + \value ItemChildAddedChange A child is added to this item. The value + argument is the new child item (i.e., a QGraphicsItem pointer). Do not + pass this item to any item's setParentItem() function as this notification + is delivered. The return value is unused; you cannot adjust anything in + this notification. Note that the new child might not be fully constructed + when this notification is sent; calling pure virtual functions on + the child can lead to a crash. + + \value ItemChildRemovedChange A child is removed from this item. The value + argument is the child item that is about to be removed (i.e., a + QGraphicsItem pointer). The return value is unused; you cannot adjust + anything in this notification. + + \value ItemSceneChange The item is moved to a new scene. This notification + is also sent when the item is added to its initial scene, and when it is + removed. The value argument is the new scene (i.e., a QGraphicsScene + pointer), or a null pointer if the item is removed from a scene. Do not + override this change by passing this item to QGraphicsScene::addItem() as + this notification is delivered; instead, you can return the new scene from + itemChange(). Use this feature with caution; objecting to a scene change can + quickly lead to unwanted recursion. + + \value ItemSceneHasChanged The item's scene has changed. The value + argument is the new scene (i.e., a pointer to a QGraphicsScene). Do not + call setScene() in itemChange() as this notification is delivered. The + return value is ignored. + + \value ItemCursorChange The item's cursor changes. The value argument is + the new cursor (i.e., a QCursor). Do not call setCursor() in itemChange() + as this notification is delivered. Instead, you can return a new cursor + from itemChange(). + + \value ItemCursorHasChanged The item's cursor has changed. The value + argument is the new cursor (i.e., a QCursor). Do not call setCursor() as + this notification is delivered. The return value is ignored. + + \value ItemToolTipChange The item's tooltip changes. The value argument is + the new tooltip (i.e., a QToolTip). Do not call setToolTip() in + itemChange() as this notification is delivered. Instead, you can return a + new tooltip from itemChange(). + + \value ItemToolTipHasChanged The item's tooltip has changed. The value + argument is the new tooltip (i.e., a QToolTip). Do not call setToolTip() + as this notification is delivered. The return value is ignored. + + \value ItemFlagsChange The item's flags change. The value argument is the + new flags (i.e., a quint32). Do not call setFlags() in itemChange() as + this notification is delivered. Instead, you can return the new flags from + itemChange(). + + \value ItemFlagsHaveChanged The item's flags have changed. The value + argument is the new flags (i.e., a quint32). Do not call setFlags() in + itemChange() as this notification is delivered. The return value is + ignored. + + \value ItemZValueChange The item's Z-value changes. The value argument is + the new Z-value (i.e., a double). Do not call setZValue() in itemChange() + as this notification is delivered. Instead, you can return a new Z-value + from itemChange(). + + \value ItemZValueHasChanged The item's Z-value has changed. The value + argument is the new Z-value (i.e., a double). Do not call setZValue() as + this notification is delivered. The return value is ignored. + + \value ItemOpacityChange The item's opacity changes. The value argument is + the new opacity (i.e., a double). Do not call setOpacity() in itemChange() + as this notification is delivered. Instead, you can return a new opacity + from itemChange(). + + \value ItemOpacityHasChanged The item's opacity has changed. The value + argument is the new opacity (i.e., a double). Do not call setOpacity() as + this notification is delivered. The return value is ignored. +*/ + +/*! + \enum QGraphicsItem::CacheMode + \since 4.4 + + This enum describes QGraphicsItem's cache modes. Caching is used to speed + up rendering by allocating and rendering to an off-screen pixel buffer, + which can be reused when the item requires redrawing. For some paint + devices, the cache is stored directly in graphics memory, which makes + rendering very quick. + + \value NoCache The default; all item caching is + disabled. QGraphicsItem::paint() is called every time the item needs + redrawing. + + \value ItemCoordinateCache Caching is enabled for the item's logical + (local) coordinate system. QGraphicsItem creates an off-screen pixel + buffer with a configurable size / resolution that you can pass to + QGraphicsItem::setCacheMode(). Rendering quality will typically degrade, + depending on the resolution of the cache and the item transformation. The + first time the item is redrawn, it will render itself into the cache, and + the cache is then reused for every subsequent expose. The cache is also + reused as the item is transformed. To adjust the resolution of the cache, + you can call setCacheMode() again. + + \value DeviceCoordinateCache Caching is enabled at the paint device level, + in device coordinates. This mode is for items that can move, but are not + rotated, scaled or sheared. If the item is transformed directly or + indirectly, the cache will be regenerated automatically. Unlike + ItemCoordinateCacheMode, DeviceCoordinateCache always renders at maximum + quality. + + \sa QGraphicsItem::setCacheMode() +*/ + +/*! + \enum QGraphicsItem::Extension + \internal + + Note: This is provided as a hook to avoid future problems related + to adding virtual functions. See also extension(), + supportsExtension() and setExtension(). +*/ + +#include "qgraphicsitem.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qgraphicsscene.h" +#include "qgraphicsscene_p.h" +#include "qgraphicssceneevent.h" +#include "qgraphicsview.h" +#include "qgraphicswidget.h" +#include "qgraphicsproxywidget.h" +#include <QtCore/qbitarray.h> +#include <QtCore/qdebug.h> +#include <QtCore/qpoint.h> +#include <QtCore/qstack.h> +#include <QtCore/qtimer.h> +#include <QtCore/qvariant.h> +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qapplication.h> +#include <QtGui/qbitmap.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpainterpath.h> +#include <QtGui/qpixmapcache.h> +#include <QtGui/qstyleoption.h> +#include <QtGui/qevent.h> + +#include <private/qgraphicsitem_p.h> +#include <private/qgraphicswidget_p.h> +#include <private/qtextcontrol_p.h> +#include <private/qtextdocumentlayout_p.h> +#include <private/qtextengine_p.h> + +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#include <private/qpixmap_x11_p.h> +#endif + +#include <math.h> + +QT_BEGIN_NAMESPACE + +// QRectF::intersects() returns false always if either the source or target +// rectangle's width or height are 0. This works around that problem. +static QRectF _q_adjustedRect(const QRectF &rect) +{ + static const qreal p = (qreal)0.00001; + QRectF r = rect; + if (!r.width()) + r.adjust(-p, 0, p, 0); + if (!r.height()) + r.adjust(0, -p, 0, p); + return r; +} + +static QRect _q_adjustedRect(const QRect &rect) +{ + QRect r = rect; + if (!r.width()) + r.adjust(0, 0, 1, 0); + if (!r.height()) + r.adjust(0, 0, 0, 1); + return r; +} + +/* + ### Move this into QGraphicsItemPrivate + */ +class QGraphicsItemCustomDataStore +{ +public: + QMap<const QGraphicsItem *, QMap<int, QVariant> > data; +}; +Q_GLOBAL_STATIC(QGraphicsItemCustomDataStore, qt_dataStore) + +/*! + \internal + + Returns a QPainterPath of \a path when stroked with the \a pen. + Ignoring dash pattern. +*/ +static QPainterPath qt_graphicsItem_shapeFromPath(const QPainterPath &path, const QPen &pen) +{ + // We unfortunately need this hack as QPainterPathStroker will set a width of 1.0 + // if we pass a value of 0.0 to QPainterPathStroker::setWidth() + const qreal penWidthZero = qreal(0.00000001); + + if (path == QPainterPath()) + return path; + QPainterPathStroker ps; + ps.setCapStyle(pen.capStyle()); + if (pen.widthF() <= 0.0) + ps.setWidth(penWidthZero); + else + ps.setWidth(pen.widthF()); + ps.setJoinStyle(pen.joinStyle()); + ps.setMiterLimit(pen.miterLimit()); + QPainterPath p = ps.createStroke(path); + p.addPath(path); + return p; +} + +/*! + \internal + + Propagates the ancestor flag \a flag with value \a enabled to all this + item's children. If \a root is false, the flag is also set on this item + (default is true). +*/ +void QGraphicsItemPrivate::updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag, + AncestorFlag flag, bool enabled, bool root) +{ + Q_Q(QGraphicsItem); + if (root) { + // For root items only. This is the item that has either enabled or + // disabled \a childFlag, or has been reparented. + switch (int(childFlag)) { + case -1: + flag = AncestorHandlesChildEvents; + enabled = q->handlesChildEvents(); + break; + case QGraphicsItem::ItemClipsChildrenToShape: + flag = AncestorClipsChildren; + enabled = flags & QGraphicsItem::ItemClipsChildrenToShape; + break; + case QGraphicsItem::ItemIgnoresTransformations: + flag = AncestorIgnoresTransformations; + enabled = flags & QGraphicsItem::ItemIgnoresTransformations; + break; + default: + return; + } + + // Inherit the enabled-state from our parents. + if ((parent && ((parent->d_ptr->ancestorFlags & flag) + || (int(parent->d_ptr->flags & childFlag) == childFlag) + || (childFlag == -1 && parent->d_ptr->handlesChildEvents)))) { + enabled = true; + ancestorFlags |= flag; + } + + // Top-level root items don't have any ancestors, so there are no + // ancestor flags either. + if (!parent) + ancestorFlags = 0; + } else { + // Don't set or propagate the ancestor flag if it's already correct. + if (((ancestorFlags & flag) && enabled) || (!(ancestorFlags & flag) && !enabled)) + return; + + // Set the flag. + if (enabled) + ancestorFlags |= flag; + else + ancestorFlags &= ~flag; + + // Don't process children if the item has the main flag set on itself. + if ((childFlag != -1 && int(flags & childFlag) == childFlag) || (int(childFlag) == -1 && handlesChildEvents)) + return; + } + + foreach (QGraphicsItem *child, children) + child->d_ptr->updateAncestorFlag(childFlag, flag, enabled, false); +} + +/*! + \internal + + Propagates item group membership. +*/ +void QGraphicsItemPrivate::setIsMemberOfGroup(bool enabled) +{ + Q_Q(QGraphicsItem); + isMemberOfGroup = enabled; + if (!qgraphicsitem_cast<QGraphicsItemGroup *>(q)) { + foreach (QGraphicsItem *child, children) + child->d_func()->setIsMemberOfGroup(enabled); + } +} + +/*! + \internal + + Maps any item pos properties of \a event to \a item's coordinate system. +*/ +void QGraphicsItemPrivate::remapItemPos(QEvent *event, QGraphicsItem *item) +{ + Q_Q(QGraphicsItem); + switch (event->type()) { + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: { + QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event); + mouseEvent->setPos(item->mapFromItem(q, mouseEvent->pos())); + mouseEvent->setLastPos(item->mapFromItem(q, mouseEvent->pos())); + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (mouseEvent->buttons() & i) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent->setButtonDownPos(button, item->mapFromItem(q, mouseEvent->buttonDownPos(button))); + } + } + break; + } + case QEvent::GraphicsSceneWheel: { + QGraphicsSceneWheelEvent *wheelEvent = static_cast<QGraphicsSceneWheelEvent *>(event); + wheelEvent->setPos(item->mapFromItem(q, wheelEvent->pos())); + break; + } + case QEvent::GraphicsSceneContextMenu: { + QGraphicsSceneContextMenuEvent *contextEvent = static_cast<QGraphicsSceneContextMenuEvent *>(event); + contextEvent->setPos(item->mapFromItem(q, contextEvent->pos())); + break; + } + case QEvent::GraphicsSceneHoverMove: { + QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event); + hoverEvent->setPos(item->mapFromItem(q, hoverEvent->pos())); + break; + } + default: + break; + } +} + +/*! + \internal + + Maps the point \a pos from scene to item coordinates. If \a view is passed and the item + is untransformable, this function will correctly map \a pos from the scene using the + view's transformation. +*/ +QPointF QGraphicsItemPrivate::genericMapFromScene(const QPointF &pos, + const QWidget *viewport) const +{ + Q_Q(const QGraphicsItem); + if (!itemIsUntransformable()) + return q->mapFromScene(pos); + QGraphicsView *view = 0; + if (viewport) + view = qobject_cast<QGraphicsView *>(viewport->parentWidget()); + if (!view) + return q->mapFromScene(pos); + // ### More ping pong than needed. + return q->deviceTransform(view->viewportTransform()).inverted().map(view->mapFromScene(pos)); +} + +/*! + \internal + + Returns true if this item or any of its ancestors are untransformable. +*/ +bool QGraphicsItemPrivate::itemIsUntransformable() const +{ + return (flags & QGraphicsItem::ItemIgnoresTransformations) + || (ancestorFlags & AncestorIgnoresTransformations); +} + +/*! + \internal + + This helper function helped us add input method query support in + Qt 4.4.1 without having to reimplement the inputMethodQuery() + function in QGraphicsProxyWidget. ### Qt 5: Remove. We cannot + remove it in 4.5+ even if we do reimplement the function properly, + because apps compiled with 4.4 will not be able to call the + reimplementation. +*/ +QVariant QGraphicsItemPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const +{ + Q_UNUSED(query); + return QVariant(); +} + +/*! + \internal + + If \a deleting is true, then this item is being deleted, and \a parent is + null. Make sure not to trigger any pure virtual function calls (e.g., + prepareGeometryChange). +*/ +void QGraphicsItemPrivate::setParentItemHelper(QGraphicsItem *newParent, bool deleting) +{ + Q_Q(QGraphicsItem); + if (newParent == q) { + qWarning("QGraphicsItem::setParentItem: cannot assign %p as a parent of itself", this); + return; + } + if (newParent == parent) + return; + + QVariant variant; + qVariantSetValue<QGraphicsItem *>(variant, newParent); + newParent = qVariantValue<QGraphicsItem *>(q->itemChange(QGraphicsItem::ItemParentChange, variant)); + if (newParent == parent) + return; + + if (QGraphicsWidget *w = isWidget ? static_cast<QGraphicsWidget *>(q) : q->parentWidget()) { + // Update the child focus chain; when reparenting a widget that has a + // focus child, ensure that that focus child clears its focus child + // chain from our parents before it's reparented. + if (QGraphicsWidget *focusChild = w->focusWidget()) + focusChild->clearFocus(); + } + + // We anticipate geometry changes. If the item is deleted, it will be + // removed from the index at a later stage, and the whole scene will be + // updated. + if (!deleting) + q_ptr->prepareGeometryChange(); + + if (parent) { + // Remove from current parent + parent->d_ptr->removeChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildRemovedChange, variant); + } + + // Update toplevelitem list. If this item is being deleted, its parent + // will be 0 but we don't want to register/unregister it in the TLI list. + if (scene && !deleting) { + if (parent && !newParent) { + scene->d_func()->registerTopLevelItem(q); + } else if (!parent && newParent) { + scene->d_func()->unregisterTopLevelItem(q); + } + } + + if ((parent = newParent)) { + bool implicitUpdate = false; + if (parent->d_func()->scene && parent->d_func()->scene != scene) { + // Move this item to its new parent's scene + parent->d_func()->scene->addItem(q); + implicitUpdate = true; + } else if (!parent->d_func()->scene && scene) { + // Remove this item from its former scene + scene->removeItem(q); + } + + parent->d_ptr->addChild(q); + qVariantSetValue<QGraphicsItem *>(variant, q); + parent->itemChange(QGraphicsItem::ItemChildAddedChange, variant); + if (!implicitUpdate) + updateHelper(); + + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (parent->isVisible() != visible) { + if (!parent->isVisible() || !explicitlyHidden) + setVisibleHelper(parent->isVisible(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + if (parent->isEnabled() != enabled) { + if (!parent->isEnabled() || !explicitlyDisabled) + setEnabledHelper(parent->isEnabled(), /* explicit = */ false, /* update = */ !implicitUpdate); + } + + } else { + // Inherit ancestor flags from the new parent. + updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); + updateAncestorFlag(QGraphicsItem::ItemClipsChildrenToShape); + updateAncestorFlag(QGraphicsItem::ItemIgnoresTransformations); + + // Update item visible / enabled. + if (!visible && !explicitlyHidden) + setVisibleHelper(true, /* explicit = */ false); + if (!enabled && !explicitlyDisabled) + setEnabledHelper(true, /* explicit = */ false); + + // If the item is being deleted, the whole scene will be updated. + if (!deleting) + updateHelper(); + } + + if (scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + scene->d_func()->invalidateSortCache(); + } + + // Resolve opacity. + if (parent) + resolveEffectiveOpacity(parent->effectiveOpacity()); + else + resolveEffectiveOpacity(1.0); + + // Resolve depth. + resolveDepth(parent ? parent->d_ptr->depth : -1); + + // Invalidate transform cache. + invalidateSceneTransformCache(); + + // Deliver post-change notification + q->itemChange(QGraphicsItem::ItemParentHasChanged, qVariantFromValue<QGraphicsItem *>(parent)); +} + +/*! + \internal + + Empty all cached pixmaps from the pixmap cache. +*/ +void QGraphicsItemCache::purge() +{ + QPixmapCache::remove(key); + QMutableMapIterator<QPaintDevice *, DeviceData> it(deviceData); + while (it.hasNext()) { + DeviceData &data = it.next().value(); + QPixmapCache::remove(data.key); + data.cacheIndent = QPoint(); + } + deviceData.clear(); + allExposed = true; + exposed.clear(); +} + +/*! + Constructs a QGraphicsItem with the given \a parent. + + If \a parent is 0, you can add the item to a scene by calling + QGraphicsScene::addItem(). The item will then become a top-level item. + + \sa QGraphicsScene::addItem(), setParentItem() +*/ +QGraphicsItem::QGraphicsItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : d_ptr(new QGraphicsItemPrivate) +{ + d_ptr->q_ptr = this; + setParentItem(parent); + + if (scene && parent && parent->scene() != scene) { + qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is" + " different from parent's scene (%p)", + scene, parent->scene()); + return; + } + if (scene && !parent) + scene->addItem(this); +} + +/*! + \internal +*/ +QGraphicsItem::QGraphicsItem(QGraphicsItemPrivate &dd, QGraphicsItem *parent, + QGraphicsScene *scene) + : d_ptr(&dd) +{ + d_ptr->q_ptr = this; + setParentItem(parent); + + if (scene && parent && parent->scene() != scene) { + qWarning("QGraphicsItem::QGraphicsItem: ignoring scene (%p), which is" + " different from parent's scene (%p)", + scene, parent->scene()); + return; + } + if (scene && !parent) + scene->addItem(this); +} + +/*! + Destroys the QGraphicsItem and all its children. If this item is currently + associated with a scene, the item will be removed from the scene before it + is deleted. +*/ +QGraphicsItem::~QGraphicsItem() +{ + if (d_ptr->scene && !d_ptr->parent) + d_ptr->scene->d_func()->unregisterTopLevelItem(this); + + clearFocus(); + + d_ptr->removeExtraItemCache(); + QList<QGraphicsItem *> oldChildren = d_ptr->children; + qDeleteAll(oldChildren); + Q_ASSERT(d_ptr->children.isEmpty()); + + d_ptr->setParentItemHelper(0, /* deleting = */ true); + if (d_ptr->scene) + d_ptr->scene->d_func()->_q_removeItemLater(this); + + delete d_ptr; + + qt_dataStore()->data.remove(this); +} + +/*! + Returns the current scene for the item, or 0 if the item is not stored in + a scene. + + To add or move an item to a scene, call QGraphicsScene::addItem(). +*/ +QGraphicsScene *QGraphicsItem::scene() const +{ + return d_ptr->scene; +} + +/*! + Returns a pointer to this item's item group, or 0 if this item is not + member of a group. + + \sa QGraphicsItemGroup, QGraphicsScene::createItemGroup() +*/ +QGraphicsItemGroup *QGraphicsItem::group() const +{ + if (!d_ptr->isMemberOfGroup) + return 0; + QGraphicsItem *parent = const_cast<QGraphicsItem *>(this); + while ((parent = parent->d_ptr->parent)) { + if (QGraphicsItemGroup *group = qgraphicsitem_cast<QGraphicsItemGroup *>(parent)) + return group; + } + // Unreachable; if d_ptr->isMemberOfGroup is != 0, then one parent of this + // item is a group item. + return 0; +} + +/*! + Adds this item to the item group \a group. If \a group is 0, this item is + removed from any current group and added as a child of the previous + group's parent. + + \sa group(), QGraphicsScene::createItemGroup() +*/ +void QGraphicsItem::setGroup(QGraphicsItemGroup *group) +{ + if (!group) { + if (QGraphicsItemGroup *group = this->group()) + group->removeFromGroup(this); + } else { + group->addToGroup(this); + } +} + +/*! + Returns a pointer to this item's parent item. If this item does not have a + parent, 0 is returned. + + \sa setParentItem(), children() +*/ +QGraphicsItem *QGraphicsItem::parentItem() const +{ + return d_ptr->parent; +} + +/*! + Returns this item's top-level item. The top-level item is the item's + topmost ancestor item whose parent is 0. If an item has no parent, its own + pointer is returned (i.e., a top-level item is its own top-level item). + + \sa parentItem() +*/ +QGraphicsItem *QGraphicsItem::topLevelItem() const +{ + QGraphicsItem *parent = const_cast<QGraphicsItem *>(this); + while (QGraphicsItem *grandPa = parent->parentItem()) + parent = grandPa; + return parent; +} + +/*! + \since 4.4 + + Returns a pointer to the item's parent widget. The item's parent widget is + the closest parent item that is a widget. + + \sa parentItem(), childItems() +*/ +QGraphicsWidget *QGraphicsItem::parentWidget() const +{ + QGraphicsItem *p = parentItem(); + while (p && !p->isWidget()) + p = p->parentItem(); + return (p && p->isWidget()) ? static_cast<QGraphicsWidget *>(p) : 0; +} + +/*! + \since 4.4 + + Returns a pointer to the item's top level widget (i.e., the item's + ancestor whose parent is 0, or whose parent is not a widget), or 0 if this + item does not have a top level widget. If the item is its own top level + widget, this function returns a pointer to the item itself. +*/ +QGraphicsWidget *QGraphicsItem::topLevelWidget() const +{ + if (const QGraphicsWidget *p = parentWidget()) + return p->topLevelWidget(); + return isWidget() ? static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this)) : 0; +} + +/*! + \since 4.4 + + Returns the item's window, or 0 if this item does not have a window. If + the item is a window, it will return itself. Otherwise it will return the + closest ancestor that is a window. + + \sa QGraphicsWidget::isWindow() +*/ +QGraphicsWidget *QGraphicsItem::window() const +{ + if (isWidget() && static_cast<const QGraphicsWidget *>(this)->isWindow()) + return static_cast<QGraphicsWidget *>(const_cast<QGraphicsItem *>(this)); + if (QGraphicsWidget *parent = parentWidget()) + return parent->window(); + return 0; +} + +/*! + Sets this item's parent item to \a parent. If this item already has a + parent, it is first removed from the previous parent. If \a parent is 0, + this item will become a top-level item. + + Note that this implicitly adds this graphics item to the scene of + the parent. You should not \l{QGraphicsScene::addItem()}{add} the + item to the scene yourself. + + \sa parentItem(), children() +*/ +void QGraphicsItem::setParentItem(QGraphicsItem *parent) +{ + d_ptr->setParentItemHelper(parent, /* deleting = */ false); +} + +/*! + \obsolete + + Use childItems() instead. + + \sa setParentItem() +*/ +QList<QGraphicsItem *> QGraphicsItem::children() const +{ + return childItems(); +} + +/*! + \since 4.4 + + Returns a list of this item's children. The items are returned in no + particular order. + + \sa setParentItem() +*/ +QList<QGraphicsItem *> QGraphicsItem::childItems() const +{ + return d_ptr->children; +} + +/*! + \since 4.4 + Returns true if this item is a widget (i.e., QGraphicsWidget); otherwise, + returns false. +*/ +bool QGraphicsItem::isWidget() const +{ + return d_ptr->isWidget; +} + +/*! + \since 4.4 + Returns true if the item is a QGraphicsWidget window, otherwise returns + false. + + \sa QGraphicsWidget::windowFlags() +*/ +bool QGraphicsItem::isWindow() const +{ + return isWidget() && (static_cast<const QGraphicsWidget *>(this)->windowType() & Qt::Window); +} + +/*! + Returns this item's flags. The flags describe what configurable features + of the item are enabled and not. For example, if the flags include + ItemIsFocusable, the item can accept input focus. + + By default, no flags are enabled. + + \sa setFlags(), setFlag() +*/ +QGraphicsItem::GraphicsItemFlags QGraphicsItem::flags() const +{ + return GraphicsItemFlags(d_ptr->flags); +} + +/*! + If \a enabled is true, the item flag \a flag is enabled; otherwise, it is + disabled. + + \sa flags(), setFlags() +*/ +void QGraphicsItem::setFlag(GraphicsItemFlag flag, bool enabled) +{ + if (enabled) + setFlags(flags() | flag); + else + setFlags(flags() & ~flag); +} + +/*! + \internal + + Sets the flag \a flag on \a item and all its children, to \a enabled. +*/ +static void _q_qgraphicsItemSetFlag(QGraphicsItem *item, QGraphicsItem::GraphicsItemFlag flag, + bool enabled) +{ + if (item->flags() & flag) { + // If this item already has the correct flag set, we don't have to + // propagate it. + return; + } + item->setFlag(flag, enabled); + foreach (QGraphicsItem *child, item->children()) + _q_qgraphicsItemSetFlag(child, flag, enabled); +} + +/*! + Sets the item flags to \a flags. All flags in \a flags are enabled; all + flags not in \a flags are disabled. + + If the item had focus and \a flags does not enable ItemIsFocusable, the + item loses focus as a result of calling this function. Similarly, if the + item was selected, and \a flags does not enabled ItemIsSelectable, the + item is automatically unselected. + + By default, no flags are enabled. + + \sa flags(), setFlag() +*/ +void QGraphicsItem::setFlags(GraphicsItemFlags flags) +{ + // Notify change and check for adjustment. + if (quint32(d_ptr->flags) == quint32(flags)) + return; + flags = GraphicsItemFlags(itemChange(ItemFlagsChange, quint32(flags)).toUInt()); + if (quint32(d_ptr->flags) == quint32(flags)) + return; + + // Flags that alter the geometry of the item (or its children). + int geomChangeFlagsMask = (ItemClipsChildrenToShape | ItemClipsToShape | ItemIgnoresTransformations); + bool fullUpdate = (flags & geomChangeFlagsMask) != (d_ptr->flags & geomChangeFlagsMask); + if (fullUpdate) + d_ptr->fullUpdateHelper(); + + // Keep the old flags to compare the diff. + GraphicsItemFlags oldFlags = this->flags(); + + // Update flags. + d_ptr->flags = flags; + + // Reresolve effective opacity if the opacity flags change. + static const quint32 opacityFlagsMask = ItemIgnoresParentOpacity | ItemDoesntPropagateOpacityToChildren; + if ((flags & opacityFlagsMask) != (oldFlags & opacityFlagsMask)) { + if (QGraphicsItem *p = d_ptr->parent) + d_ptr->resolveEffectiveOpacity(p->effectiveOpacity()); + else + d_ptr->resolveEffectiveOpacity(1.0); + } + + if (!(d_ptr->flags & ItemIsFocusable) && hasFocus()) { + // Clear focus on the item if it has focus when the focusable flag + // is unset. + clearFocus(); + } + + if (!(d_ptr->flags & ItemIsSelectable) && isSelected()) { + // Unselect the item if it is selected when the selectable flag is + // unset. + setSelected(false); + } + + if ((flags & ItemClipsChildrenToShape) != (oldFlags & ItemClipsChildrenToShape)) { + // Item children clipping changes. Propagate the ancestor flag to + // all children. + d_ptr->updateAncestorFlag(ItemClipsChildrenToShape); + } + + if ((flags & ItemIgnoresTransformations) != (oldFlags & ItemIgnoresTransformations)) { + // Item children clipping changes. Propagate the ancestor flag to + // all children. + d_ptr->updateAncestorFlag(ItemIgnoresTransformations); + } + + // ### Why updateHelper? + d_ptr->updateHelper(); + + // Notify change. + itemChange(ItemFlagsHaveChanged, quint32(flags)); +} + +/*! + \since 4.4 + Returns the cache mode for this item. The default mode is NoCache (i.e., + cache is disabled and all painting is immediate). + + \sa setCacheMode() +*/ +QGraphicsItem::CacheMode QGraphicsItem::cacheMode() const +{ + return QGraphicsItem::CacheMode(d_ptr->cacheMode); +} + +/*! + \since 4.4 + Sets the item's cache mode to \a mode. + + The optional \a logicalCacheSize argument is used only by + ItemCoordinateCache mode, and describes the resolution of the cache + buffer; if \a logicalCacheSize is (100, 100), QGraphicsItem will fit the + item into 100x100 pixels in graphics memory, regardless of the logical + size of the item itself. By default QGraphicsItem uses the size of + boundingRect(). For all other cache modes than ItemCoordinateCache, \a + logicalCacheSize is ignored. + + Caching can speed up rendering if your item spends a significant time + redrawing itself. In some cases the cache can also slow down rendering, in + particular when the item spends less time redrawing than QGraphicsItem + spends redrawing from the cache. When enabled, the item's paint() function + will be called only once for each call to update(); for any subsequent + repaint requests, the Graphics View framework will redraw from the + cache. This approach works particularly well with QGLWidget, which stores + all the cache as OpenGL textures. + + Be aware that QPixmapCache's cache limit may need to be changed to obtain + optimal performance. + + You can read more about the different cache modes in the CacheMode + documentation. + + \sa CacheMode, QPixmapCache::setCacheLimit() +*/ +void QGraphicsItem::setCacheMode(CacheMode mode, const QSize &logicalCacheSize) +{ + CacheMode lastMode = CacheMode(d_ptr->cacheMode); + d_ptr->cacheMode = mode; + bool noVisualChange = (mode == NoCache && lastMode == NoCache) + || (mode == NoCache && lastMode == DeviceCoordinateCache) + || (mode == DeviceCoordinateCache && lastMode == NoCache); + if (mode == NoCache) { + d_ptr->removeExtraItemCache(); + } else { + QGraphicsItemCache *cache = d_ptr->extraItemCache(); + + // Reset old cache + cache->purge(); + + if (mode == ItemCoordinateCache) { + if (cache->key.isEmpty()) { + // Generate new simple pixmap cache key. + QString tmp; + tmp.sprintf("qgv-%p", this); + cache->key = tmp; + } + if (lastMode == mode && cache->fixedSize == logicalCacheSize) + noVisualChange = true; + cache->fixedSize = logicalCacheSize; + } + } + if (!noVisualChange) + update(); +} + +#ifndef QT_NO_TOOLTIP +/*! + Returns the item's tool tip, or an empty QString if no tool tip has been + set. + + \sa setToolTip(), QToolTip +*/ +QString QGraphicsItem::toolTip() const +{ + return d_ptr->extra(QGraphicsItemPrivate::ExtraToolTip).toString(); +} + +/*! + Sets the item's tool tip to \a toolTip. If \a toolTip is empty, the item's + tool tip is cleared. + + \sa toolTip(), QToolTip +*/ +void QGraphicsItem::setToolTip(const QString &toolTip) +{ + QString newCursor = itemChange(ItemToolTipChange, toolTip).toString(); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraToolTip, toolTip); + itemChange(ItemToolTipHasChanged, toolTip); +} +#endif // QT_NO_TOOLTIP + +#ifndef QT_NO_CURSOR +/*! + Returns the current cursor shape for the item. The mouse cursor + will assume this shape when it's over this item. See the \link + Qt::CursorShape list of predefined cursor objects\endlink for a + range of useful shapes. + + An editor item might want to use an I-beam cursor: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 2 + + If no cursor has been set, the parent's cursor is used. + + \sa setCursor(), hasCursor(), unsetCursor(), QWidget::cursor, + QApplication::overrideCursor() +*/ +QCursor QGraphicsItem::cursor() const +{ + return qVariantValue<QCursor>(d_ptr->extra(QGraphicsItemPrivate::ExtraCursor)); +} + +/*! + Sets the current cursor shape for the item to \a cursor. The mouse cursor + will assume this shape when it's over this item. See the \link + Qt::CursorShape list of predefined cursor objects\endlink for a range of + useful shapes. + + An editor item might want to use an I-beam cursor: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 3 + + If no cursor has been set, the cursor of the item beneath is used. + + \sa cursor(), hasCursor(), unsetCursor(), QWidget::cursor, + QApplication::overrideCursor() +*/ +void QGraphicsItem::setCursor(const QCursor &cursor) +{ + QCursor newCursor = qVariantValue<QCursor>(itemChange(ItemCursorChange, + qVariantFromValue<QCursor>(cursor))); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraCursor, newCursor); + d_ptr->hasCursor = 1; + if (d_ptr->scene) { + foreach (QGraphicsView *view, d_ptr->scene->views()) { + // Note: Some of this logic is duplicated in QGraphicsView's mouse events. + if (view->underMouse()) { + foreach (QGraphicsItem *itemUnderCursor, view->items(view->mapFromGlobal(QCursor::pos()))) { + if (itemUnderCursor->hasCursor()) { + QMetaObject::invokeMethod(view, "_q_setViewportCursor", + Q_ARG(QCursor, itemUnderCursor->cursor())); + break; + } + } + break; + } + } + } + itemChange(ItemCursorHasChanged, qVariantFromValue<QCursor>(newCursor)); +} + +/*! + Returns true if this item has a cursor set; otherwise, false is returned. + + By default, items don't have any cursor set. cursor() will return a + standard pointing arrow cursor. + + \sa unsetCursor() +*/ +bool QGraphicsItem::hasCursor() const +{ + return d_ptr->hasCursor; +} + +/*! + Clears the cursor from this item. + + \sa hasCursor(), setCursor() +*/ +void QGraphicsItem::unsetCursor() +{ + d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraCursor); + d_ptr->hasCursor = 0; + if (d_ptr->scene) { + foreach (QGraphicsView *view, d_ptr->scene->views()) { + if (view->underMouse() && view->itemAt(view->mapFromGlobal(QCursor::pos())) == this) { + QMetaObject::invokeMethod(view, "_q_unsetViewportCursor"); + break; + } + } + } +} + +#endif // QT_NO_CURSOR + +/*! + Returns true if the item is visible; otherwise, false is returned. + + Note that the item's general visibility is unrelated to whether or not it + is actually being visualized by a QGraphicsView. + + \sa setVisible() +*/ +bool QGraphicsItem::isVisible() const +{ + return d_ptr->visible; +} + +/*! + \since 4.4 + Returns true if the item is visible to \a parent; otherwise, false is + returned. \a parent can be 0, in which case this function will return + whether the item is visible to the scene or not. + + An item may not be visible to its ancestors even if isVisible() is true. If + any ancestor is hidden, the item itself will be implicitly hidden, in which + case this function will return false. + + \sa isVisible(), setVisible() +*/ +bool QGraphicsItem::isVisibleTo(const QGraphicsItem *parent) const +{ + if (!d_ptr->visible) + return false; + if (parent == this) + return true; + if (parentItem() && parentItem()->isVisibleTo(parent)) + return true; + if (!parent && !parentItem()) + return true; + return false; +} + +/*! + \internal + + Sets this item's visibility to \a newVisible. If \a explicitly is true, + this item will be "explicitly" \a newVisible; otherwise, it.. will not be. +*/ +void QGraphicsItemPrivate::setVisibleHelper(bool newVisible, bool explicitly, bool update) +{ + Q_Q(QGraphicsItem); + + // Update explicit bit. + if (explicitly) + explicitlyHidden = newVisible ? 0 : 1; + + // Check if there's nothing to do. + if (visible == quint32(newVisible)) + return; + + // Modify the property. + newVisible = q_ptr->itemChange(QGraphicsItem::ItemVisibleChange, quint32(newVisible)).toBool(); + if (visible == quint32(newVisible)) + return; + visible = newVisible; + + // Schedule redrawing + if (update) { + QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + if (c) + c->purge(); + updateHelper(QRectF(), /* force = */ true); + } + + // Certain properties are dropped as an item becomes invisible. + if (!newVisible) { + if (scene) { + if (scene->d_func()->mouseGrabberItems.contains(q)) + q->ungrabMouse(); + if (scene->d_func()->keyboardGrabberItems.contains(q)) + q->ungrabKeyboard(); + } + if (q_ptr->hasFocus() && scene) { + // Hiding the closest non-window ancestor of the focus item + QGraphicsItem *focusItem = scene->focusItem(); + bool clear = true; + if (isWidget && !focusItem->isWindow()) { + do { + if (focusItem == q_ptr) { + clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true); + break; + } + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow()); + } + if (clear) + q_ptr->clearFocus(); + } + if (q_ptr->isSelected()) + q_ptr->setSelected(false); + } else { + if (isWidget && scene) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); + if (widget->windowType() == Qt::Popup) + scene->d_func()->addPopup(widget); + } + } + + // Update children with explicitly = false. + foreach (QGraphicsItem *child, children) { + if (!newVisible || !child->d_ptr->explicitlyHidden) + child->d_ptr->setVisibleHelper(newVisible, false); + } + + // Enable subfocus + if (newVisible && isWidget) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(q_ptr); + QGraphicsWidget *fw = widget->focusWidget(); + if (fw && fw != scene->focusItem()) + scene->setFocusItem(fw); + } + + // Deliver post-change notification. + q_ptr->itemChange(QGraphicsItem::ItemVisibleHasChanged, quint32(visible)); +} + +/*! + If \a visible is true, the item is made visible. Otherwise, the item is + made invisible. Invisible items are not painted, nor do they receive any + events. In particular, mouse events pass right through invisible items, + and are delivered to any item that may be behind. Invisible items are also + unselectable, they cannot take input focus, and are not detected by + QGraphicsScene's item location functions. + + If an item becomes invisible while grabbing the mouse, (i.e., while it is + receiving mouse events,) it will automatically lose the mouse grab, and + the grab is not regained by making the item visible again; it must receive + a new mouse press to regain the mouse grab. + + Similarly, an invisible item cannot have focus, so if the item has focus + when it becomes invisible, it will lose focus, and the focus is not + regained by simply making the item visible again. + + If you hide a parent item, all its children will also be hidden. If you + show a parent item, all children will be shown, unless they have been + explicitly hidden (i.e., if you call setVisible(false) on a child, it will + not be reshown even if its parent is hidden, and then shown again). + + Items are visible by default; it is unnecessary to call + setVisible() on a new item. + + \sa isVisible(), show(), hide() +*/ +void QGraphicsItem::setVisible(bool visible) +{ + d_ptr->setVisibleHelper(visible, /* explicit = */ true); +} + +/*! + \fn void QGraphicsItem::hide() + + Hides the item. (Items are visible by default.) + + This convenience function is equivalent to calling \c setVisible(false). + + \sa show(), setVisible() +*/ + +/*! + \fn void QGraphicsItem::show() + + Shows the item. (Items are visible by default.) + + This convenience function is equivalent to calling \c setVisible(true). + + \sa hide(), setVisible() +*/ + +/*! + Returns true if the item is enabled; otherwise, false is returned. + + \sa setEnabled() +*/ +bool QGraphicsItem::isEnabled() const +{ + return d_ptr->enabled; +} + +/*! + \internal + + Sets this item's visibility to \a newEnabled. If \a explicitly is true, + this item will be "explicitly" \a newEnabled; otherwise, it.. will not be. +*/ +void QGraphicsItemPrivate::setEnabledHelper(bool newEnabled, bool explicitly, bool update) +{ + // Update explicit bit. + if (explicitly) + explicitlyDisabled = newEnabled ? 0 : 1; + + // Check if there's nothing to do. + if (enabled == quint32(newEnabled)) + return; + + // Certain properties are dropped when an item is disabled. + if (!newEnabled) { + if (scene && scene->mouseGrabberItem() == q_ptr) + q_ptr->ungrabMouse(); + if (q_ptr->hasFocus()) { + // Disabling the closest non-window ancestor of the focus item + // causes focus to pop to the next item, otherwise it's cleared. + QGraphicsItem *focusItem = scene->focusItem(); + bool clear = true; + if (isWidget && !focusItem->isWindow() && q_ptr->isAncestorOf(focusItem)) { + do { + if (focusItem == q_ptr) { + clear = !static_cast<QGraphicsWidget *>(q_ptr)->focusNextPrevChild(true); + break; + } + } while ((focusItem = focusItem->parentWidget()) && !focusItem->isWindow()); + } + if (clear) + q_ptr->clearFocus(); + } + if (q_ptr->isSelected()) + q_ptr->setSelected(false); + } + + // Modify the property. + enabled = q_ptr->itemChange(QGraphicsItem::ItemEnabledChange, quint32(newEnabled)).toBool(); + + // Schedule redraw. + if (update) + updateHelper(); + + foreach (QGraphicsItem *child, children) { + if (!newEnabled || !child->d_ptr->explicitlyDisabled) + child->d_ptr->setEnabledHelper(newEnabled, /* explicitly = */ false); + } + + // Deliver post-change notification. + q_ptr->itemChange(QGraphicsItem::ItemEnabledHasChanged, quint32(enabled)); +} + +/*! + If \a enabled is true, the item is enabled; otherwise, it is disabled. + + Disabled items are visible, but they do not receive any events, and cannot + take focus nor be selected. Mouse events are discarded; they are not + propagated unless the item is also invisible, or if it does not accept + mouse events (see acceptedMouseButtons()). A disabled item cannot become the + mouse grabber, and as a result of this, an item loses the grab if it + becomes disabled when grabbing the mouse, just like it loses focus if it + had focus when it was disabled. + + Disabled items are traditionally drawn using grayed-out colors (see \l + QPalette::Disabled). + + If you disable a parent item, all its children will also be disabled. If + you enable a parent item, all children will be enabled, unless they have + been explicitly disabled (i.e., if you call setEnabled(false) on a child, + it will not be reenabled if its parent is disabled, and then enabled + again). + + Items are enabled by default. + + \note If you install an event filter, you can still intercept events + before they are delivered to items; this mechanism disregards the item's + enabled state. + + \sa isEnabled() +*/ +void QGraphicsItem::setEnabled(bool enabled) +{ + d_ptr->setEnabledHelper(enabled, /* explicitly = */ true); +} + +/*! + Returns true if this item is selected; otherwise, false is returned. + + Items that are in a group inherit the group's selected state. + + Items are not selected by default. + + \sa setSelected(), QGraphicsScene::setSelectionArea() +*/ +bool QGraphicsItem::isSelected() const +{ + if (QGraphicsItemGroup *group = this->group()) + return group->isSelected(); + return d_ptr->selected; +} + +/*! + If \a selected is true and this item is selectable, this item is selected; + otherwise, it is unselected. + + If the item is in a group, the whole group's selected state is toggled by + this function. If the group is selected, all items in the group are also + selected, and if the group is not selected, no item in the group is + selected. + + Only visible, enabled, selectable items can be selected. If \a selected + is true and this item is either invisible or disabled or unselectable, + this function does nothing. + + By default, items cannot be selected. To enable selection, set the + ItemIsSelectable flag. + + This function is provided for convenience, allowing individual toggling of + the selected state of an item. However, a more common way of selecting + items is to call QGraphicsScene::setSelectionArea(), which will call this + function for all visible, enabled, and selectable items within a specified + area on the scene. + + \sa isSelected(), QGraphicsScene::selectedItems() +*/ +void QGraphicsItem::setSelected(bool selected) +{ + if (QGraphicsItemGroup *group = this->group()) { + group->setSelected(selected); + return; + } + + if (!(d_ptr->flags & ItemIsSelectable) || !d_ptr->enabled || !d_ptr->visible) + selected = false; + if (d_ptr->selected == selected) + return; + bool newSelected = itemChange(ItemSelectedChange, quint32(selected)).toBool(); + if (d_ptr->selected == newSelected) + return; + d_ptr->selected = newSelected; + + d_ptr->updateHelper(); + + if (d_ptr->scene) { + QGraphicsScenePrivate *sceneD = d_ptr->scene->d_func(); + if (selected) { + sceneD->selectedItems << this; + } else { + // QGraphicsScene::selectedItems() lazily pulls out all items that are + // no longer selected. + } + if (!sceneD->selectionChanging) + emit d_ptr->scene->selectionChanged(); + } + + // Deliver post-change notification. + itemChange(QGraphicsItem::ItemSelectedHasChanged, quint32(d_ptr->selected)); +} + +/*! + \since 4.5 + + Returns this item's local opacity, which is between 0.0 (transparent) and + 1.0 (opaque). This value is combined with parent and ancestor values into + the effectiveOpacity(). The effective opacity decides how the item is + rendered. + + The opacity property decides the state of the painter passed to the + paint() function. If the item is cached, i.e., ItemCoordinateCache or + DeviceCoordinateCache, the effective property will be applied to the item's + cache as it is rendered. + + The default opacity is 1.0; fully opaque. + + \sa setOpacity(), paint(), ItemIgnoresParentOpacity, + ItemDoesntPropagateOpacityToChildren +*/ +qreal QGraphicsItem::opacity() const +{ + if (d_ptr->hasOpacity) { + QVariant o = d_ptr->extra(QGraphicsItemPrivate::ExtraOpacity); + if (!o.isNull()) + return o.toDouble(); + } + return qreal(1.0); +} + +/*! + \since 4.5 + + Returns this item's \e effective opacity, which is between 0.0 + (transparent) and 1.0 (opaque). This value is a combination of this item's + local opacity, and its parent and ancestors' opacities. The effective + opacity decides how the item is rendered. + + \sa opacity(), setOpacity(), paint(), ItemIgnoresParentOpacity, + ItemDoesntPropagateOpacityToChildren +*/ +qreal QGraphicsItem::effectiveOpacity() const +{ + QVariant effectiveOpacity = d_ptr->extra(QGraphicsItemPrivate::ExtraEffectiveOpacity); + return effectiveOpacity.isNull() ? qreal(1.0) : qreal(effectiveOpacity.toDouble()); +} + +/*! + \since 4.5 + + Sets this item's local \a opacity, between 0.0 (transparent) and 1.0 + (opaque). The item's local opacity is combined with parent and ancestor + opacities into the effectiveOpacity(). + + By default, opacity propagates from parent to child, so if a parent's + opacity is 0.5 and the child is also 0.5, the child's effective opacity + will be 0.25. + + The opacity property decides the state of the painter passed to the + paint() function. If the item is cached, i.e., ItemCoordinateCache or + DeviceCoordinateCache, the effective property will be applied to the + item's cache as it is rendered. + + There are two item flags that affect how the item's opacity is combined + with the parent: ItemIgnoresParentOpacity and + ItemDoesntPropagateOpacityToChildren. + + \sa opacity(), effectiveOpacity() +*/ +void QGraphicsItem::setOpacity(qreal opacity) +{ + // Notify change. + qreal newOpacity = itemChange(ItemOpacityChange, double(opacity)).toDouble(); + + // Normalize. + newOpacity = qBound<qreal>(0.0, newOpacity, 1.0); + + // No change? Done. + if (qFuzzyCompare(newOpacity, this->opacity())) + return; + + // Assign local opacity. + if (qFuzzyCompare(newOpacity, qreal(1.0))) { + // Opaque, unset opacity. + d_ptr->hasOpacity = 0; + d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraOpacity); + } else { + d_ptr->hasOpacity = 1; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraOpacity, double(newOpacity)); + } + + // Resolve effective opacity. + if (QGraphicsItem *p = d_ptr->parent) + d_ptr->resolveEffectiveOpacity(p->effectiveOpacity()); + else + d_ptr->resolveEffectiveOpacity(1.0); + + // Notify change. + itemChange(ItemOpacityHasChanged, newOpacity); + + // Update. + d_ptr->fullUpdateHelper(); +} + +/*! + Returns true if this item can accept drag and drop events; otherwise, + returns false. By default, items do not accept drag and drop events; items + are transparent to drag and drop. + + \sa setAcceptDrops() +*/ +bool QGraphicsItem::acceptDrops() const +{ + return d_ptr->acceptDrops; +} + +/*! + If \a on is true, this item will accept drag and drop events; otherwise, + it is transparent for drag and drop events. By default, items do not + accept drag and drop events. + + \sa acceptDrops() +*/ +void QGraphicsItem::setAcceptDrops(bool on) +{ + d_ptr->acceptDrops = on; +} + +/*! + Returns the mouse buttons that this item accepts mouse events for. By + default, all mouse buttons are accepted. + + If an item accepts a mouse button, it will become the mouse + grabber item when a mouse press event is delivered for that mouse + button. However, if the item does not accept the button, + QGraphicsScene will forward the mouse events to the first item + beneath it that does. + + \sa setAcceptedMouseButtons(), mousePressEvent() +*/ +Qt::MouseButtons QGraphicsItem::acceptedMouseButtons() const +{ + return Qt::MouseButtons(d_ptr->acceptedMouseButtons); +} + +/*! + Sets the mouse \a buttons that this item accepts mouse events for. + + By default, all mouse buttons are accepted. If an item accepts a + mouse button, it will become the mouse grabber item when a mouse + press event is delivered for that button. However, if the item + does not accept the mouse button, QGraphicsScene will forward the + mouse events to the first item beneath it that does. + + To disable mouse events for an item (i.e., make it transparent for mouse + events), call setAcceptedMouseButtons(0). + + \sa acceptedMouseButtons(), mousePressEvent() +*/ +void QGraphicsItem::setAcceptedMouseButtons(Qt::MouseButtons buttons) +{ + if (Qt::MouseButtons(d_ptr->acceptedMouseButtons) != buttons) { + if (buttons == 0 && d_ptr->scene && d_ptr->scene->mouseGrabberItem() == this + && d_ptr->scene->d_func()->lastMouseGrabberItemHasImplicitMouseGrab) { + ungrabMouse(); + } + d_ptr->acceptedMouseButtons = quint32(buttons); + } +} + +/*! + \since 4.4 + + Returns true if an item accepts hover events + (QGraphicsSceneHoverEvent); otherwise, returns false. By default, + items do not accept hover events. + + \sa setAcceptedMouseButtons() +*/ +bool QGraphicsItem::acceptHoverEvents() const +{ + return d_ptr->acceptsHover; +} + +/*! + \obsolete + + Call acceptHoverEvents() instead. +*/ +bool QGraphicsItem::acceptsHoverEvents() const +{ + return d_ptr->acceptsHover; +} + +/*! + \since 4.4 + + If \a enabled is true, this item will accept hover events; + otherwise, it will ignore them. By default, items do not accept + hover events. + + Hover events are delivered when there is no current mouse grabber + item. They are sent when the mouse cursor enters an item, when it + moves around inside the item, and when the cursor leaves an + item. Hover events are commonly used to highlight an item when + it's entered, and for tracking the mouse cursor as it hovers over + the item (equivalent to QWidget::mouseTracking). + + Parent items receive hover enter events before their children, and + leave events after their children. The parent does not receive a + hover leave event if the cursor enters a child, though; the parent + stays "hovered" until the cursor leaves its area, including its + children's areas. + + If a parent item handles child events (setHandlesChildEvents()), it will + receive hover move, drag move, and drop events as the cursor passes + through its children, but it does not receive hover enter and hover leave, + nor drag enter and drag leave events on behalf of its children. + + A QGraphicsWidget with window decorations will accept hover events + regardless of the value of acceptHoverEvents(). + + \sa acceptHoverEvents(), hoverEnterEvent(), hoverMoveEvent(), + hoverLeaveEvent() +*/ +void QGraphicsItem::setAcceptHoverEvents(bool enabled) +{ + d_ptr->acceptsHover = quint32(enabled); +} + +/*! + \obsolete + + Use setAcceptHoverEvents(\a enabled) instead. +*/ +void QGraphicsItem::setAcceptsHoverEvents(bool enabled) +{ + d_ptr->acceptsHover = quint32(enabled); +} + +/*! + Returns true if this item handles child events (i.e., all events + intended for any of its children are instead sent to this item); + otherwise, false is returned. + + This property is useful for item groups; it allows one item to + handle events on behalf of its children, as opposed to its + children handling their events individually. + + The default is to return false; children handle their own events. + The exception for this is if the item is a QGraphicsItemGroup, then + it defaults to return true. + + \sa setHandlesChildEvents() +*/ +bool QGraphicsItem::handlesChildEvents() const +{ + return d_ptr->handlesChildEvents; +} + +/*! + If \a enabled is true, this item is set to handle all events for + all its children (i.e., all events intented for any of its + children are instead sent to this item); otherwise, if \a enabled + is false, this item will only handle its own events. The default + value is false. + + This property is useful for item groups; it allows one item to + handle events on behalf of its children, as opposed to its + children handling their events individually. + + If a child item accepts hover events, its parent will receive + hover move events as the cursor passes through the child, but it + does not receive hover enter and hover leave events on behalf of + its child. + + \sa handlesChildEvents() +*/ +void QGraphicsItem::setHandlesChildEvents(bool enabled) +{ + if (d_ptr->handlesChildEvents == enabled) + return; + + d_ptr->handlesChildEvents = enabled; + d_ptr->updateAncestorFlag(QGraphicsItem::GraphicsItemFlag(-1)); +} + +/*! + Returns true if this item has keyboard input focus; otherwise, returns + false. + + \sa QGraphicsScene::focusItem(), setFocus(), QGraphicsScene::setFocusItem() +*/ +bool QGraphicsItem::hasFocus() const +{ + return (d_ptr->scene && d_ptr->scene->focusItem() == this); +} + +/*! + Gives keyboard input focus to this item. The \a focusReason argument will + be passed into any focus event generated by this function; it is used to + give an explanation of what caused the item to get focus. + + Only items that set the ItemIsFocusable flag can accept keyboard focus. + + If this item is not visible (i.e., isVisible() returns false), not + enabled, not associated with a scene, or if it already has input focus, + this function will do nothing. + + As a result of calling this function, this item will receive a focus in + event with \a focusReason. If another item already has focus, that item + will first receive a focus out event indicating that it has lost input + focus. + + \sa clearFocus(), hasFocus() +*/ +void QGraphicsItem::setFocus(Qt::FocusReason focusReason) +{ + if (!d_ptr->scene || !isEnabled() || hasFocus() || !(d_ptr->flags & ItemIsFocusable)) + return; + if (isVisible()) { + // Visible items immediately gain focus from scene. + d_ptr->scene->setFocusItem(this, focusReason); + } else if (d_ptr->isWidget) { + // Just set up subfocus. + static_cast<QGraphicsWidget *>(this)->d_func()->setFocusWidget(); + } +} + +/*! + Takes keyboard input focus from the item. + + If it has focus, a focus out event is sent to this item to tell it that it + is about to lose the focus. + + Only items that set the ItemIsFocusable flag, or widgets that set an + appropriate focus policy, can accept keyboard focus. + + \sa setFocus(), QGraphicsWidget::focusPolicy +*/ +void QGraphicsItem::clearFocus() +{ + if (!d_ptr->scene) + return; + if (d_ptr->isWidget) { + // Invisible widget items with focus must explicitly clear subfocus. + static_cast<QGraphicsWidget *>(this)->d_func()->clearFocusWidget(); + } + if (d_ptr->scene->focusItem() == this) { + // If this item has the scene's input focus, clear it. + d_ptr->scene->setFocusItem(0); + } +} + +/*! + \since 4.4 + Grabs the mouse input. + + This item will receive all mouse events for the scene until any of the + following events occurs: + + \list + \o The item becomes invisible + \o The item is removed from the scene + \o The item is deleted + \o The item call ungrabMouse() + \o Another item calls grabMouse(); the item will regain the mouse grab + when the other item calls ungrabMouse(). + \endlist + + When an item gains the mouse grab, it receives a QEvent::GrabMouse + event. When it loses the mouse grab, it receives a QEvent::UngrabMouse + event. These events can be used to detect when your item gains or loses + the mouse grab through other means than receiving mouse button events. + + It is almost never necessary to explicitly grab the mouse in Qt, as Qt + grabs and releases it sensibly. In particular, Qt grabs the mouse when you + press a mouse button, and keeps the mouse grabbed until you release the + last mouse button. Also, Qt::Popup widgets implicitly call grabMouse() + when shown, and ungrabMouse() when hidden. + + Note that only visible items can grab mouse input. Calling grabMouse() on + an invisible item has no effect. + + Keyboard events are not affected. + + \sa QGraphicsScene::mouseGrabberItem(), ungrabMouse(), grabKeyboard() +*/ +void QGraphicsItem::grabMouse() +{ + if (!d_ptr->scene) { + qWarning("QGraphicsItem::grabMouse: cannot grab mouse without scene"); + return; + } + if (!d_ptr->visible) { + qWarning("QGraphicsItem::grabMouse: cannot grab mouse while invisible"); + return; + } + d_ptr->scene->d_func()->grabMouse(this); +} + +/*! + \since 4.4 + Releases the mouse grab. + + \sa grabMouse(), ungrabKeyboard() +*/ +void QGraphicsItem::ungrabMouse() +{ + if (!d_ptr->scene) { + qWarning("QGraphicsItem::ungrabMouse: cannot ungrab mouse without scene"); + return; + } + d_ptr->scene->d_func()->ungrabMouse(this); +} + +/*! + \since 4.4 + Grabs the keyboard input. + + The item will receive all keyboard input to the scene until one of the + following events occur: + + \list + \o The item becomes invisible + \o The item is removed from the scene + \o The item is deleted + \o The item calls ungrabKeyboard() + \o Another item calls grabKeyboard(); the item will regain the keyboard grab + when the other item calls ungrabKeyboard(). + \endlist + + When an item gains the keyboard grab, it receives a QEvent::GrabKeyboard + event. When it loses the keyboard grab, it receives a + QEvent::UngrabKeyboard event. These events can be used to detect when your + item gains or loses the keyboard grab through other means than gaining + input focus. + + It is almost never necessary to explicitly grab the keyboard in Qt, as Qt + grabs and releases it sensibly. In particular, Qt grabs the keyboard when + your item gains input focus, and releases it when your item loses input + focus, or when the item is hidden. + + Note that only visible items can grab keyboard input. Calling + grabKeyboard() on an invisible item has no effect. + + Keyboard events are not affected. + + \sa ungrabKeyboard(), grabMouse(), setFocus() +*/ +void QGraphicsItem::grabKeyboard() +{ + if (!d_ptr->scene) { + qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard without scene"); + return; + } + if (!d_ptr->visible) { + qWarning("QGraphicsItem::grabKeyboard: cannot grab keyboard while invisible"); + return; + } + d_ptr->scene->d_func()->grabKeyboard(this); +} + +/*! + \since 4.4 + Releases the keyboard grab. + + \sa grabKeyboard(), ungrabMouse() +*/ +void QGraphicsItem::ungrabKeyboard() +{ + if (!d_ptr->scene) { + qWarning("QGraphicsItem::ungrabKeyboard: cannot ungrab keyboard without scene"); + return; + } + d_ptr->scene->d_func()->ungrabKeyboard(this); +} + +/*! + Returns the position of the item in parent coordinates. If the item has no + parent, its position is given in scene coordinates. + + The position of the item describes its origin (local coordinate + (0, 0)) in parent coordinates; this function returns the same as + mapToParent(0, 0). + + For convenience, you can also call scenePos() to determine the + item's position in scene coordinates, regardless of its parent. + + \sa x(), y(), setPos(), matrix(), {The Graphics View Coordinate System} +*/ +QPointF QGraphicsItem::pos() const +{ + return d_ptr->pos; +} + +/*! + \fn QGraphicsItem::x() const + + This convenience function is equivalent to calling pos().x(). + + \sa y() +*/ + +/*! + \fn QGraphicsItem::y() const + + This convenience function is equivalent to calling pos().y(). + + \sa x() +*/ + +/*! + Returns the item's position in scene coordinates. This is + equivalent to calling \c mapToScene(0, 0). + + \sa pos(), sceneTransform(), {The Graphics View Coordinate System} +*/ +QPointF QGraphicsItem::scenePos() const +{ + return mapToScene(0, 0); +} + +/*! + \internal + + Sets the position \a pos and notifies the change. If \a update is true, + the item is also updated; otherwise it is not updated before and after the + change. +*/ +void QGraphicsItemPrivate::setPosHelper(const QPointF &pos, bool update) +{ + Q_Q(QGraphicsItem); + if (this->pos == pos) + return; + + // Notify the item that the position is changing. + QPointF newPos = q->itemChange(QGraphicsItem::ItemPositionChange, pos).toPointF(); + if (newPos == this->pos) + return; + + // Update and repositition. + if (scene && update) { + fullUpdateHelper(true); + q->prepareGeometryChange(); + } + this->pos = newPos; + invalidateSceneTransformCache(); + + // Send post-notification. + q->itemChange(QGraphicsItem::ItemPositionHasChanged, newPos); +} + +/*! + Sets the position of the item to \a pos, which is in parent + coordinates. For items with no parent, \a pos is in scene + coordinates. + + The position of the item describes its origin (local coordinate + (0, 0)) in parent coordinates. + + \sa pos(), scenePos(), {The Graphics View Coordinate System} +*/ +void QGraphicsItem::setPos(const QPointF &pos) +{ + d_ptr->setPosHelper(pos, /* update = */ true); +} + +/*! + \fn void QGraphicsItem::setPos(qreal x, qreal y) + \overload + + This convenience function is equivalent to calling setPos(QPointF(\a x, \a + y)). +*/ + +/*! + \fn void QGraphicsItem::moveBy(qreal dx, qreal dy) + + Moves the item by \a dx points horizontally, and \a dy point + vertically. This function is equivalent to calling setPos(pos() + + QPointF(\a dx, \a dy)). +*/ + +/*! + If this item is part of a scene that is viewed by a QGraphicsView, this + convenience function will attempt to scroll the view to ensure that \a + rect is visible inside the view's viewport. If \a rect is a null rect (the + default), QGraphicsItem will default to the item's bounding rect. \a xmargin + and \a ymargin are the number of pixels the view should use for margins. + + If the specified rect cannot be reached, the contents are scrolled to the + nearest valid position. + + If this item is not viewed by a QGraphicsView, this function does nothing. + + \sa QGraphicsView::ensureVisible() +*/ +void QGraphicsItem::ensureVisible(const QRectF &rect, int xmargin, int ymargin) +{ + if (d_ptr->scene) { + QRectF sceneRect; + if (!rect.isNull()) + sceneRect = sceneTransform().mapRect(rect); + else + sceneRect = sceneBoundingRect(); + foreach (QGraphicsView *view, d_ptr->scene->d_func()->views) + view->ensureVisible(sceneRect, xmargin, ymargin); + } +} + +/*! + \fn void QGraphicsItem::ensureVisible(qreal x, qreal y, qreal w, qreal h, + int xmargin = 50, int ymargin = 50) + + This convenience function is equivalent to calling + ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin): +*/ + +/*! + \obsolete + + Returns the item's affine transformation matrix. This is a subset or the + item's full transformation matrix, and might not represent the item's full + transformation. + + Use transform() instead. + + \sa setTransform(), sceneTransform() +*/ +QMatrix QGraphicsItem::matrix() const +{ + return transform().toAffine(); +} + +/*! + \since 4.3 + + Returns this item's transformation matrix. If no matrix has been set, the + identity matrix is returned. + + \sa setTransform(), sceneTransform() +*/ +QTransform QGraphicsItem::transform() const +{ + if (!d_ptr->hasTransform) + return QTransform(); + return qVariantValue<QTransform>(d_ptr->extra(QGraphicsItemPrivate::ExtraTransform)); +} + +/*! + \obsolete + + Use sceneTransform() instead. + + \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System} +*/ +QMatrix QGraphicsItem::sceneMatrix() const +{ + return sceneTransform().toAffine(); +} + + +/*! + \since 4.3 + + Returns this item's scene transformation matrix. This matrix can be used + to map coordinates and geometrical shapes from this item's local + coordinate system to the scene's coordinate system. To map coordinates + from the scene, you must first invert the returned matrix. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 4 + + Unlike transform(), which returns only an item's local transformation, this + function includes the item's (and any parents') position. + + \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate System} +*/ +QTransform QGraphicsItem::sceneTransform() const +{ + // Check if there's any entry in the transform cache. + QGraphicsScenePrivate *sd = d_ptr->scene ? d_ptr->scene->d_func() : 0; + int index = d_ptr->sceneTransformIndex; + if (sd && index != -1 && sd->validTransforms.testBit(index)) + return sd->sceneTransformCache[index]; + + // Calculate local transform. + QTransform m; + if (d_ptr->hasTransform) { + m = transform(); + m *= QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y()); + } else { + // ### ? QTransform::fromTranslate(d_ptr->pos.x(), d_ptr->pos.y()) + m.translate(d_ptr->pos.x(), d_ptr->pos.y()); + } + + // Combine with parent and add to cache. + if (d_ptr->parent) { + m *= d_ptr->parent->sceneTransform(); + // Don't cache toplevels + if (sd) { + if (index == -1) { + if (!sd->freeSceneTransformSlots.isEmpty()) { + index = sd->freeSceneTransformSlots.last(); + sd->freeSceneTransformSlots.pop_back(); + } else { + index = sd->sceneTransformCache.size(); + } + d_ptr->sceneTransformIndex = index; + if (index >= sd->validTransforms.size()) { + sd->validTransforms.resize(index + 1); + sd->sceneTransformCache.resize(index + 1); + } + } + sd->validTransforms.setBit(index, 1); + sd->sceneTransformCache[index] = m; + } + } + return m; +} + +/*! + \since 4.3 + + Returns this item's device transformation matrix, using \a + viewportTransform to map from scene to device coordinates. This matrix can + be used to map coordinates and geometrical shapes from this item's local + coordinate system to the viewport's (or any device's) coordinate + system. To map coordinates from the viewport, you must first invert the + returned matrix. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 5 + + This function is the same as combining this item's scene transform with + the view's viewport transform, but it also understands the + ItemIgnoresTransformations flag. The device transform can be used to do + accurate coordinate mapping (and collision detection) for untransformable + items. + + \sa transform(), setTransform(), scenePos(), {The Graphics View Coordinate + System}, itemTransform() +*/ +QTransform QGraphicsItem::deviceTransform(const QTransform &viewportTransform) const +{ + // Find the topmost item that ignores view transformations. + const QGraphicsItem *untransformedAncestor = this; + QList<const QGraphicsItem *> parents; + while (untransformedAncestor && ((untransformedAncestor->d_ptr->ancestorFlags + & QGraphicsItemPrivate::AncestorIgnoresTransformations))) { + parents.prepend(untransformedAncestor); + untransformedAncestor = untransformedAncestor->parentItem(); + } + + if (!untransformedAncestor) { + // Assert in debug mode, continue in release. + Q_ASSERT_X(untransformedAncestor, "QGraphicsItem::deviceTransform", + "Invalid object structure!"); + return QTransform(); + } + + // First translate the base untransformable item. + QPointF mappedPoint = (untransformedAncestor->sceneTransform() * viewportTransform).map(QPointF(0, 0)); + QTransform matrix; + matrix.translate(mappedPoint.x(), mappedPoint.y()); + matrix = untransformedAncestor->transform() * matrix; + + // Then transform and translate all children. + for (int i = 0; i < parents.size(); ++i) { + const QGraphicsItem *parent = parents.at(i); + QPointF pos = parent->pos(); + QTransform moveMatrix; + moveMatrix.translate(pos.x(), pos.y()); + matrix = (parent->transform() * moveMatrix) * matrix; + } + + return matrix; +} + +/*! + \since 4.5 + + Returns a QTransform that maps coordinates from this item to \a other. If + \a ok is not null, and if there is no such transform, the boolean pointed + to by \a ok will be set to false; otherwise it will be set to true. + + This transform provides an alternative to the mapToItem() or mapFromItem() + functions, by returning the appropriate transform so that you can map + shapes and coordinates yourself. It also helps you write more efficient + code when repeatedly mapping between the same two items. + + \note In rare circumstances, there is no transform that maps between two + items. + + \sa mapToItem(), mapFromItem(), deviceTransform() +*/ +QTransform QGraphicsItem::itemTransform(const QGraphicsItem *other, bool *ok) const +{ + // Catch simple cases first. + if (other == 0) { + qWarning("QGraphicsItem::itemTransform: null pointer passed"); + return QTransform(); + } + if (other == this) { + if (ok) + *ok = true; + return QTransform(); + } + + QGraphicsItem *parent = d_ptr->parent; + const QGraphicsItem *otherParent = other->d_ptr->parent; + + // This is other's child + if (parent == other) { + if (ok) + *ok = true; + const QPointF &itemPos = d_ptr->pos; + if (d_ptr->hasTransform) + return transform() * QTransform::fromTranslate(itemPos.x(), itemPos.y()); + return QTransform::fromTranslate(itemPos.x(), itemPos.y()); + } + + // This is other's parent + if (otherParent == this) { + const QPointF &otherPos = other->d_ptr->pos; + if (other->d_ptr->hasTransform) { + QTransform otherToParent = other->transform(); + otherToParent *= QTransform::fromTranslate(otherPos.x(), otherPos.y()); + return otherToParent.inverted(ok); + } else { + if (ok) + *ok = true; + return QTransform::fromTranslate(-otherPos.x(), -otherPos.y()); + } + } + + // Siblings + if (parent == otherParent) { + bool hasTr = d_ptr->hasTransform; + bool otherHasTr = other->d_ptr->hasTransform; + const QPointF &itemPos = d_ptr->pos; + const QPointF &otherPos = other->d_ptr->pos; + + if (!hasTr && !otherHasTr) { + QPointF delta = itemPos - otherPos; + if (ok) + *ok = true; + return QTransform::fromTranslate(delta.x(), delta.y()); + } + + QTransform itemToParent = QTransform::fromTranslate(itemPos.x(), itemPos.y()); + if (hasTr) + itemToParent = transform() * itemToParent; + + QTransform otherToParent = QTransform::fromTranslate(otherPos.x(), otherPos.y()); + if (otherHasTr) + otherToParent = other->transform() * otherToParent; + + return itemToParent * otherToParent.inverted(ok); + } + + // Find the closest common ancestor. If the two items don't share an + // ancestor, then the only way is to combine their scene transforms. + const QGraphicsItem *commonAncestor = commonAncestorItem(other); + if (!commonAncestor) + return sceneTransform() * other->sceneTransform().inverted(ok); + + // If the two items are cousins (in sibling branches), map both to the + // common ancestor, and combine the two transforms. + bool cousins = other != commonAncestor && this != commonAncestor; + if (cousins) { + bool good = false; + QTransform thisToScene; + QTransform otherToScene; + thisToScene = itemTransform(commonAncestor, &good); + if (good) + otherToScene = other->itemTransform(commonAncestor, &good); + if (!good) { + if (ok) + *ok = false; + return QTransform(); + } + return thisToScene * otherToScene.inverted(ok); + } + + // One is an ancestor of the other; walk the chain. + bool parentOfOther = isAncestorOf(other); + const QGraphicsItem *child = parentOfOther ? other : this; + const QGraphicsItem *root = parentOfOther ? this : other; + + QTransform x; + const QGraphicsItem *p = child; + do { + const QGraphicsItemPrivate *pd = p->d_ptr; + if (pd->hasTransform) + x *= p->transform(); + x *= QTransform::fromTranslate(pd->pos.x(), pd->pos.y()); + } while ((p = p->d_ptr->parent) && p != root); + if (parentOfOther) + return x.inverted(ok); + if (ok) + *ok = true; + return x; +} + +/*! + \obsolete + + Sets the item's affine transformation matrix. This is a subset or the + item's full transformation matrix, and might not represent the item's full + transformation. + + Use setTransform() instead. + + \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} +*/ +void QGraphicsItem::setMatrix(const QMatrix &matrix, bool combine) +{ + QTransform oldTransform = this->transform(); + QTransform newTransform; + if (!combine) + newTransform = QTransform(matrix); + else + newTransform = QTransform(matrix) * oldTransform; + if (oldTransform == newTransform) + return; + + // Notify the item that the matrix is changing. + QVariant variant; + qVariantSetValue<QMatrix>(variant, newTransform.toAffine()); + newTransform = QTransform(qVariantValue<QMatrix>(itemChange(ItemMatrixChange, variant))); + if (oldTransform == newTransform) + return; + + // Update and set the new transformation. + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + d_ptr->hasTransform = !newTransform.isIdentity(); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + d_ptr->invalidateSceneTransformCache(); + + // Send post-notification. + itemChange(ItemTransformHasChanged, newTransform); +} + +/*! + \since 4.3 + + Sets the item's current transformation matrix to \a matrix. + + If \a combine is true, then \a matrix is combined with the current matrix; + otherwise, \a matrix \e replaces the current matrix. \a combine is false + by default. + + To simplify interation with items using a transformed view, QGraphicsItem + provides mapTo... and mapFrom... functions that can translate between + items' and the scene's coordinates. For example, you can call mapToScene() + to map an item coordiate to a scene coordinate, or mapFromScene() to map + from scene coordinates to item coordinates. + + \sa transform(), rotate(), scale(), shear(), translate(), {The Graphics View Coordinate System} +*/ +void QGraphicsItem::setTransform(const QTransform &matrix, bool combine) +{ + QTransform oldTransform = this->transform(); + QTransform newTransform; + if (!combine) + newTransform = matrix; + else + newTransform = matrix * oldTransform; + if (oldTransform == newTransform) + return; + + // Notify the item that the transformation matrix is changing. + QVariant variant; + qVariantSetValue<QTransform>(variant, newTransform); + newTransform = qVariantValue<QTransform>(itemChange(ItemTransformChange, variant)); + if (oldTransform == newTransform) + return; + + // Update and set the new transformation. + d_ptr->fullUpdateHelper(true); + prepareGeometryChange(); + d_ptr->hasTransform = !newTransform.isIdentity(); + d_ptr->setExtra(QGraphicsItemPrivate::ExtraTransform, newTransform); + d_ptr->invalidateSceneTransformCache(); + + // Send post-notification. + itemChange(ItemTransformHasChanged, newTransform); +} + +/*! + \obsolete + + Use resetTransform() instead. +*/ +void QGraphicsItem::resetMatrix() +{ + resetTransform(); +} + +/*! + \since 4.3 + + Resets this item's transformation matrix to the identity matrix. This is + equivalent to calling \c setTransform(QTransform()). + + \sa setTransform(), transform() +*/ +void QGraphicsItem::resetTransform() +{ + setTransform(QTransform(), false); +} + +/*! + Rotates the current item transformation \a angle degrees clockwise around + its origin. To translate around an arbitrary point (x, y), you need to + combine translation and rotation with setTransform(). + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 6 + + \sa setTransform(), transform(), scale(), shear(), translate() +*/ +void QGraphicsItem::rotate(qreal angle) +{ + setTransform(QTransform().rotate(angle), true); +} + +/*! + Scales the current item transformation by (\a sx, \a sy) around its + origin. To scale from an arbitrary point (x, y), you need to combine + translation and scaling with setTransform(). + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 7 + + \sa setTransform(), transform(), rotate(), shear(), translate() +*/ +void QGraphicsItem::scale(qreal sx, qreal sy) +{ + setTransform(QTransform::fromScale(sx, sy), true); +} + +/*! + Shears the current item transformation by (\a sh, \a sv). + + \sa setTransform(), transform(), rotate(), scale(), translate() +*/ +void QGraphicsItem::shear(qreal sh, qreal sv) +{ + setTransform(QTransform().shear(sh, sv), true); +} + +/*! + Translates the current item transformation by (\a dx, \a dy). + + If all you want is to move an item, you should call moveBy() or + setPos() instead; this function changes the item's translation, + which is conceptually separate from its position. + + \sa setTransform(), transform(), rotate(), scale(), shear() +*/ +void QGraphicsItem::translate(qreal dx, qreal dy) +{ + setTransform(QTransform::fromTranslate(dx, dy), true); +} + +/*! + This virtual function is called twice for all items by the + QGraphicsScene::advance() slot. In the first phase, all items are called + with \a phase == 0, indicating that items on the scene are about to + advance, and then all items are called with \a phase == 1. Reimplement + this function to update your item if you need simple scene-controlled + animation. + + The default implementation does nothing. + + For individual item animation, an alternative to this function is to + either use QGraphicsItemAnimation, or to multiple-inherit from QObject and + QGraphicsItem, and animate your item using QObject::startTimer() and + QObject::timerEvent(). + + \sa QGraphicsItemAnimation, QTimeLine +*/ +void QGraphicsItem::advance(int phase) +{ + Q_UNUSED(phase); +} + +/*! + Returns the Z-value, or the elevation, of the item. The Z-value decides + the stacking order of sibling (neighboring) items. + + The default Z-value is 0. + + \sa setZValue() +*/ +qreal QGraphicsItem::zValue() const +{ + return d_ptr->z; +} + +/*! + Sets the Z-value, or the elevation, of the item, to \a z. The elevation + decides the stacking order of sibling (neighboring) items. An item of high + Z-value will be drawn on top of an item with a lower Z-value if they share + the same parent item. In addition, children of an item will always be + drawn on top of the parent, regardless of the child's Z-value. Sibling + items that share the same Z-value will be drawn in order of insertion; the + last inserted child is stacked above previous children. + + \img graphicsview-zorder.png + + Children of different parents are stacked according to the Z-value of + each item's ancestor item which is an immediate child of the two + items' closest common ancestor. For example, a robot item might + define a torso item as the parent of a head item, two arm items, + and two upper-leg items. The upper-leg items would each be parents + of one lower-leg item, and each lower-leg item would be parents of + one foot item. The stacking order of the feet is the same as the + stacking order of each foot's ancestor that is an immediate child + of the two feet's common ancestor (i.e., the torso item); so the + feet are stacked in the same order as the upper-leg items, + regardless of each foot's Z-value. + + The Z-value does not affect the item's size in any way. + + The default Z-value is 0. + + \sa zValue() +*/ +void QGraphicsItem::setZValue(qreal z) +{ + qreal newZ = qreal(itemChange(ItemZValueChange, double(z)).toDouble()); + if (newZ == d_ptr->z) + return; + d_ptr->z = newZ; + d_ptr->fullUpdateHelper(); + + if (d_ptr->scene) { + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + d_ptr->scene->d_func()->invalidateSortCache(); + } + + itemChange(ItemZValueHasChanged, double(newZ)); +} + +/*! + Returns the bounding rect of this item's descendants (i.e., its + children, their children, etc.) in local coordinates. The + rectangle will contain all descendants after they have been mapped + to local coordinates. If the item has no children, this function + returns an empty QRectF. + + This does not include this item's own bounding rect; it only returns + its descendants' accumulated bounding rect. If you need to include this + item's bounding rect, you can add boundingRect() to childrenBoundingRect() + using QRectF::operator|(). + + This function is linear in complexity; it determines the size of the + returned bounding rect by iterating through all descendants. + + \sa boundingRect(), sceneBoundingRect() +*/ +QRectF QGraphicsItem::childrenBoundingRect() const +{ + QRectF childRect; + foreach (QGraphicsItem *child, children()) { + QPointF childPos = child->pos(); + QTransform matrix = child->transform() * QTransform::fromTranslate(childPos.x(), childPos.y()); + childRect |= matrix.mapRect(child->boundingRect() | child->childrenBoundingRect()); + } + return childRect; +} + +/*! + \fn virtual QRectF QGraphicsItem::boundingRect() const = 0 + + This pure virtual function defines the outer bounds of the item as + a rectangle; all painting must be restricted to inside an item's + bounding rect. QGraphicsView uses this to determine whether the + item requires redrawing. + + Although the item's shape can be arbitrary, the bounding rect is + always rectangular, and it is unaffected by the items' + transformation (scale(), rotate(), etc.). + + If you want to change the item's bounding rectangle, you must first call + prepareGeometryChange(). This notifies the scene of the imminent change, + so that its can update its item geometry index; otherwise, the scene will + be unaware of the item's new geometry, and the results are undefined + (typically, rendering artifacts are left around in the view). + + Reimplement this function to let QGraphicsView determine what + parts of the widget, if any, need to be redrawn. + + Note: For shapes that paint an outline / stroke, it is important + to include half the pen width in the bounding rect. It is not + necessary to compensate for antialiasing, though. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 8 + + \sa boundingRegion(), shape(), contains(), {The Graphics View Coordinate + System}, prepareGeometryChange() +*/ + +/*! + Returns the bounding rect of this item in scene coordinates, by combining + sceneTransform() with boundingRect(). + + \sa boundingRect(), {The Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::sceneBoundingRect() const +{ + // Find translate-only offset + QPointF offset; + const QGraphicsItem *parentItem = this; + const QGraphicsItemPrivate *itemd; + do { + itemd = parentItem->d_ptr; + if (itemd->hasTransform) + break; + offset += itemd->pos; + } while ((parentItem = itemd->parent)); + + QRectF br = boundingRect(); + br.translate(offset); + return !parentItem ? br : parentItem->sceneTransform().mapRect(br); +} + +/*! + Returns the shape of this item as a QPainterPath in local + coordinates. The shape is used for many things, including collision + detection, hit tests, and for the QGraphicsScene::items() functions. + + The default implementation calls boundingRect() to return a simple + rectangular shape, but subclasses can reimplement this function to return + a more accurate shape for non-rectangular items. For example, a round item + may choose to return an elliptic shape for better collision detection. For + example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 9 + + The outline of a shape can vary depending on the width and style of the + pen used when drawing. If you want to include this outline in the item's + shape, you can create a shape from the stroke using QPainterPathStroker. + + This function is called by the default implementations of contains() and + collidesWithPath(). + + \sa boundingRect(), contains(), prepareGeometryChange(), QPainterPathStroker +*/ +QPainterPath QGraphicsItem::shape() const +{ + QPainterPath path; + path.addRect(boundingRect()); + return path; +} + +/*! + Returns true if this item is clipped. An item is clipped if it has either + set the \l ItemClipsToShape flag, or if it or any of its ancestors has set + the \l ItemClipsChildrenToShape flag. + + Clipping affects the item's appearance (i.e., painting), as well as mouse + and hover event delivery. + + \sa clipPath(), shape(), setFlags() +*/ +bool QGraphicsItem::isClipped() const +{ + Q_D(const QGraphicsItem); + return (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + || (d->flags & QGraphicsItem::ItemClipsToShape); +} + +/*! + \since 4.5 + + Returns this item's clip path, or an empty QPainterPath if this item is + not clipped. The clip path constrains the item's appearance and + interaction (i.e., restricts the area the item can draw, and it also + restricts the area that the item receives events). + + You can enable clipping by setting the ItemClipsToShape or + ItemClipsChildrenToShape flags. The item's clip path is calculated by + intersecting all clipping ancestors' shapes. If the item sets + ItemClipsToShape, the final clip is intersected with the item's own shape. + + \note Clipping introduces a performance penalty for all items involved; + you should generally avoid using clipping if you can (e.g., if your items + always draw inside boundingRect() or shape() boundaries, clipping is not + necessary). + + \sa isClipped(), shape(), setFlags() +*/ +QPainterPath QGraphicsItem::clipPath() const +{ + Q_D(const QGraphicsItem); + QPainterPath clip; + if (!isClipped()) + return clip; + + // Start with the item's bounding rect. + clip.addRect(boundingRect()); + + bool clipAway = false; + if (d->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { + // Make list of parents up to the farthest ancestor that clips its + // children to its shape. + QVarLengthArray<const QGraphicsItem *, 32> clippingAncestors; + const QGraphicsItem *parent = parentItem(); + const QGraphicsItem *clipOwner = 0; + do { + if (parent->d_ptr->flags & ItemClipsChildrenToShape) { + clippingAncestors.append(parent); + clipOwner = parent; + } + } while ((parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) && (parent = parent->parentItem())); + + // Start with the topmost clip. + QPainterPath parentClip = clipOwner->shape(); + + // Intersect any in-between clips starting at the bottom and moving + // upwards. + for (int i = clippingAncestors.size() - 2; i >= 0; --i) { + const QGraphicsItem *item = clippingAncestors[i]; + // ### what if itemtransform fails + if (clipOwner) + parentClip = clipOwner->itemTransform(item).map(parentClip); + parentClip = parentClip.intersected(item->shape()); + if (parentClip.isEmpty()) { + clip = parentClip; + clipAway = true; + break; + } + clipOwner = item; + } + + if (!clipAway) { + // ### what if itemtransform fails + clip = clip.intersected(clipOwner->itemTransform(this).map(parentClip)); + if (clip.isEmpty()) + clipAway = true; + } + } + + if (!clipAway && d->flags & ItemClipsToShape) + clip = clip.intersected(shape()); + + return clip; +} + +/*! + Returns true if this item contains \a point, which is in local + coordinates; otherwise, false is returned. It is most often called from + QGraphicsView to determine what item is under the cursor, and for that + reason, the implementation of this function should be as light-weight as + possible. + + By default, this function calls shape(), but you can reimplement it in a + subclass to provide a (perhaps more efficient) implementation. + + \sa shape(), boundingRect(), collidesWithPath() +*/ +bool QGraphicsItem::contains(const QPointF &point) const +{ + return isClipped() ? clipPath().contains(point) : shape().contains(point); +} + +/*! + Returns true if this item collides with \a other; otherwise returns false. + The ways items collide is determined by \a mode. The default value for \a + mode is Qt::IntersectsItemShape; \a other collides with this item if it + either intersects, contains, or is contained by this item's shape. + + The default implementation is based on shape intersection, and it calls + shape() on both items. Because the complexity of arbitrary shape-shape + intersection grows with an order of magnitude when the shapes are complex, + this operation can be noticably time consuming. You have the option of + reimplementing this function in a subclass of QGraphicsItem to provide a + custom algorithm. This allows you to make use of natural constraints in + the shapes of your own items, in order to improve the performance of the + collision detection. For instance, two untransformed perfectly circular + items' collision can be determined very efficiently by comparing their + positions and radii. + + Keep in mind that when reimplementing this function and calling shape() or + boundingRect() on \a other, the returned coordinates must be mapped to + this item's coordinate system before any intersection can take place. + + \sa contains(), shape() +*/ +bool QGraphicsItem::collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode) const +{ + if (other == this) + return true; + if (!other) + return false; + // The items share the same clip if their closest clipper is the same, or + // if one clips the other. + bool clips = (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren); + bool otherClips = (other->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren); + if (clips || otherClips) { + const QGraphicsItem *closestClipper = isAncestorOf(other) ? this : parentItem(); + while (closestClipper && !(closestClipper->flags() & ItemClipsChildrenToShape)) + closestClipper = closestClipper->parentItem(); + const QGraphicsItem *otherClosestClipper = other->isAncestorOf(this) ? other : other->parentItem(); + while (otherClosestClipper && !(otherClosestClipper->flags() & ItemClipsChildrenToShape)) + otherClosestClipper = otherClosestClipper->parentItem(); + if (closestClipper == otherClosestClipper) { + d_ptr->localCollisionHack = 1; + bool res = collidesWithPath(mapFromItem(other, other->shape()), mode); + d_ptr->localCollisionHack = 0; + return res; + } + } + + QPainterPath otherShape = other->isClipped() ? other->clipPath() : other->shape(); + return collidesWithPath(mapFromItem(other, otherShape), mode); +} + +/*! + Returns true if this item collides with \a path. + + The collision is determined by \a mode. The default value for \a mode is + Qt::IntersectsItemShape; \a path collides with this item if it either + intersects, contains, or is contained by this item's shape. + + \sa collidesWithItem(), contains(), shape() +*/ +bool QGraphicsItem::collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode) const +{ + if (path.isEmpty()) { + // No collision with empty paths. + return false; + } + + QRectF rectA = _q_adjustedRect(boundingRect()); + QRectF rectB = _q_adjustedRect(path.controlPointRect()); + if (!rectA.intersects(rectB)) { + // This we can determine efficiently. If the two rects neither + // intersect nor contain eachother, then the two items do not collide. + return false; + } + + // For further testing, we need this item's shape or bounding rect. + QPainterPath thisShape; + if (mode == Qt::IntersectsItemShape || mode == Qt::ContainsItemShape) { + thisShape = (isClipped() && !d_ptr->localCollisionHack) ? clipPath() : shape(); + } else { + thisShape.addPolygon(_q_adjustedRect(boundingRect())); + thisShape.closeSubpath(); + } + if (thisShape == QPainterPath()) { + // Empty shape? No collision. + return false; + } + + // Use QPainterPath boolean operations to determine the collision, O(N*logN). + if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) + return path.intersects(thisShape); + return path.contains(thisShape); +} + +/*! + Returns a list of all items that collide with this item. + + The way collisions are detected is determined by \a mode. The default + value for \a mode is Qt::IntersectsItemShape; All items whose shape + intersects or is contained by this item's shape are returned. + + \sa QGraphicsScene::collidingItems(), collidesWithItem() +*/ +QList<QGraphicsItem *> QGraphicsItem::collidingItems(Qt::ItemSelectionMode mode) const +{ + if (d_ptr->scene) + return d_ptr->scene->collidingItems(this, mode); + return QList<QGraphicsItem *>(); +} + +/*! + Returns true if this item's bounding rect is completely obscured by the + opaque shape of any of colliding items above it (i.e., with a higher Z + value than this item). + + Its implementation is based on calling isObscuredBy(), which you can + reimplement to provide a custom obscurity algorithm. + + \sa opaqueArea() +*/ +bool QGraphicsItem::isObscured() const +{ + return isObscured(QRectF()); +} + +/*! + \internal + + Item obscurity helper function. + + Returns true if the subrect \a rect of \a item's bounding rect is obscured + by \a other (i.e., \a other's opaque area covers \a item's \a rect + completely. \a other is assumed to already be "on top of" \a item + wrt. stacking order. +*/ +static bool qt_QGraphicsItem_isObscured(const QGraphicsItem *item, + const QGraphicsItem *other, + const QRectF &rect) +{ + return other->mapToItem(item, other->opaqueArea()).contains(rect); +} + +/*! + \overload + \since 4.3 + + Returns true if \a rect is completely obscured by the opaque shape of any + of colliding items above it (i.e., with a higher Z value than this item). + + Unlike the default isObscured() function, this function does not call + isObscuredBy(). + + \sa opaqueArea() +*/ +bool QGraphicsItem::isObscured(const QRectF &rect) const +{ + Q_D(const QGraphicsItem); + if (!d->scene) + return false; + + QRectF br = boundingRect(); + QRectF testRect = rect.isNull() ? br : rect; + + foreach (QGraphicsItem *item, d->scene->items(mapToScene(br), Qt::IntersectsItemBoundingRect)) { + if (item == this) + break; + if (qt_QGraphicsItem_isObscured(this, item, testRect)) + return true; + } + return false; +} + +/*! + \fn bool QGraphicsItem::isObscured(qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling isObscured(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Returns true if this item's bounding rect is completely obscured by the + opaque shape of \a item. + + The base implementation maps \a item's opaqueArea() to this item's + coordinate system, and then checks if this item's boundingRect() is fully + contained within the mapped shape. + + You can reimplement this function to provide a custom algorithm for + determining whether this item is obscured by \a item. + + \sa opaqueArea(), isObscured() +*/ +bool QGraphicsItem::isObscuredBy(const QGraphicsItem *item) const +{ + if (!item) + return false; + return QGraphicsScenePrivate::closestItemFirst_withoutCache(item, this) + && qt_QGraphicsItem_isObscured(this, item, boundingRect()); +} + +/*! + This virtual function returns a shape representing the area where this + item is opaque. An area is opaque if it is filled using an opaque brush or + color (i.e., not transparent). + + This function is used by isObscuredBy(), which is called by underlying + items to determine if they are obscured by this item. + + The default implementation returns an empty QPainterPath, indicating that + this item is completely transparent and does not obscure any other items. + + \sa isObscuredBy(), isObscured(), shape() +*/ +QPainterPath QGraphicsItem::opaqueArea() const +{ + return QPainterPath(); +} + +/*! + \since 4.4 + + Returns the bounding region for this item. The coordinate space of the + returned region depends on \a itemToDeviceTransform. If you pass an + identity QTransform as a parameter, this function will return a local + coordinate region. + + The bounding region describes a coarse outline of the item's visual + contents. Although it's expensive to calculate, it's also more precise + than boundingRect(), and it can help to avoid unnecessary repainting when + an item is updated. This is particularily efficient for thin items (e.g., + lines or simple polygons). You can tune the granularity for the bounding + region by calling setBoundingRegionGranularity(). The default granularity + is 0; in which the item's bounding region is the same as its bounding + rect. + + \a itemToDeviceTransform is the transformation from item coordinates to + device coordinates. If you want this function to return a QRegion in scene + coordinates, you can pass sceneTransform() as an argument. + + \sa boundingRegionGranularity() +*/ +QRegion QGraphicsItem::boundingRegion(const QTransform &itemToDeviceTransform) const +{ + // ### Ideally we would have a better way to generate this region, + // preferably something in the lines of QPainterPath::toRegion(QTransform) + // coupled with a way to generate a painter path from a set of painter + // operations (e.g., QPicture::toPainterPath() or so). The current + // approach generates a bitmap with the size of the item's bounding rect + // in device coordinates, scaled by b.r.granularity, then paints the item + // into the bitmap, converts the result to a QRegion and scales the region + // back to device space with inverse granularity. + qreal granularity = boundingRegionGranularity(); + QRect deviceRect = _q_adjustedRect(itemToDeviceTransform.mapRect(boundingRect()).toRect()); + if (granularity == 0.0) + return QRegion(deviceRect); + + int pad = 1; + QSize bitmapSize(qMax(1, int(deviceRect.width() * granularity) + pad * 2), + qMax(1, int(deviceRect.height() * granularity) + pad * 2)); + QImage mask(bitmapSize, QImage::Format_ARGB32_Premultiplied); + mask.fill(0); + QPainter p(&mask); + p.setRenderHints(QPainter::Antialiasing); + + // Transform painter (### this code is from QGraphicsScene::drawItemHelper + // and doesn't work properly with perspective transformations). + QPointF viewOrigo = itemToDeviceTransform.map(QPointF(0, 0)); + QPointF offset = viewOrigo - deviceRect.topLeft(); + p.scale(granularity, granularity); + p.translate(offset); + p.translate(pad, pad); + p.setWorldTransform(itemToDeviceTransform, true); + p.translate(itemToDeviceTransform.inverted().map(QPointF(0, 0))); + + // Render + QStyleOptionGraphicsItem option; + const_cast<QGraphicsItem *>(this)->paint(&p, &option, 0); + p.end(); + + // Transform QRegion back to device space + QTransform unscale; + unscale.scale(1 / granularity, 1 / granularity); + QRegion r; + QBitmap colorMask = QBitmap::fromImage(mask.createMaskFromColor(0)); + foreach (const QRect &rect, QRegion( colorMask ).rects()) { + QRect xrect = unscale.mapRect(rect).translated(deviceRect.topLeft() - QPoint(pad, pad)); + r += xrect.adjusted(-1, -1, 1, 1) & deviceRect; + } + return r; +} + +/*! + \since 4.4 + + Returns the item's bounding region granularity; a value between and + including 0 and 1. The default value is 0 (i.e., the lowest granularity, + where the bounding region corresponds to the item's bounding rectangle). + +\omit +### NOTE +\endomit + + \sa setBoundingRegionGranularity() +*/ +qreal QGraphicsItem::boundingRegionGranularity() const +{ + return d_ptr->hasBoundingRegionGranularity + ? qVariantValue<qreal>(d_ptr->extra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity)) + : 0; +} + +/*! + \since 4.4 + Sets the bounding region granularity to \a granularity; a value between + and including 0 and 1. The default value is 0 (i.e., the lowest + granularity, where the bounding region corresponds to the item's bounding + rectangle). + + The granularity is used by boundingRegion() to calculate how fine the + bounding region of the item should be. The highest achievable granularity + is 1, where boundingRegion() will return the finest outline possible for + the respective device (e.g., for a QGraphicsView viewport, this gives you + a pixel-perfect bounding region). The lowest possible granularity is + 0. The value of \a granularity describes the ratio between device + resolution and the resolution of the bounding region (e.g., a value of + 0.25 will provide a region where each chunk corresponds to 4x4 device + units / pixels). + + \sa boundingRegionGranularity() +*/ +void QGraphicsItem::setBoundingRegionGranularity(qreal granularity) +{ + if (granularity < 0.0 || granularity > 1.0) { + qWarning("QGraphicsItem::setBoundingRegionGranularity: invalid granularity %g", granularity); + return; + } + if (granularity == 0.0) { + d_ptr->unsetExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity); + d_ptr->hasBoundingRegionGranularity = 0; + return; + } + d_ptr->hasBoundingRegionGranularity = 1; + d_ptr->setExtra(QGraphicsItemPrivate::ExtraBoundingRegionGranularity, + qVariantFromValue<qreal>(granularity)); +} + +/*! + \fn virtual void QGraphicsItem::paint(QPainter *painter, const + QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0 + + This function, which is usually called by QGraphicsView, paints the + contents of an item in local coordinates. + + Reimplement this function in a QGraphicsItem subclass to provide the + item's painting implementation, using \a painter. The \a option parameter + provides style options for the item, such as its state, exposed area and + its level-of-detail hints. The \a widget argument is optional. If + provided, it points to the widget that is being painted on; otherwise, it + is 0. For cached painting, \a widget is always 0. + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 10 + + The painter's pen is 0-width by default, and its pen is initialized to the + QPalette::Text brush from the paint device's palette. The brush is + initialized to QPalette::Window. + + Make sure to constrain all painting inside the boundaries of + boundingRect() to avoid rendering artifacts (as QGraphicsView does not + clip the painter for you). In particular, when QPainter renders the + outline of a shape using an assigned QPen, half of the outline will be + drawn outside, and half inside, the shape you're rendering (e.g., with a + pen width of 2 units, you must draw outlines 1 unit inside + boundingRect()). QGraphicsItem does not support use of cosmetic pens with + a non-zero width. + + All painting is done in local coordinates. + + \sa setCacheMode(), QPen::width(), {Item Coordinates} +*/ + +/*! + \internal + + Asks the scene to mark this item's scene rect as dirty, requesting a + redraw. This does not invalidate any cache. + + The \a force argument is for the update call in setVisible(), which is the + only case where the item's background should be marked as dirty even when + the item isn't visible. +*/ +void QGraphicsItemPrivate::updateHelper(const QRectF &rect, bool force, bool updateCache) +{ + // No scene, or if the scene is updating everything, means we have nothing + // to do. The only exception is if the scene tracks the growing scene rect. + QGraphicsItemCache *cache = maybeExtraItemCache(); + if (dirty && (!updateCache || !cache || cache->allExposed)) + return; + + if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect)) + return; + + if (updateCache && QGraphicsItem::CacheMode(cacheMode) != QGraphicsItem::NoCache) { + if (rect.isNull()) { + cache->allExposed = true; + cache->exposed.clear(); + } else { + cache->exposed.append(rect); + } + } + + if (!dirty && scene && (visible || force)) { + if (rect.isNull()) + dirty = 1; + scene->itemUpdated(q_ptr, rect); + } +} + +/*! + \internal + + Propagates updates to \a item and all its children. +*/ +void QGraphicsItemPrivate::fullUpdateHelper(bool childrenOnly) +{ + // No scene, or if the scene is updating everything, means we have nothing + // to do. The only exception is if the scene tracks the growing scene rect. + if (!scene || (scene && scene->d_func()->updateAll && scene->d_func()->hasSceneRect)) + return; + if (!childrenOnly && !dirty) + updateHelper(); + if (children.isEmpty() || dirtyChildren) + return; + if (flags & QGraphicsItem::ItemClipsChildrenToShape) { + // ### mark all children dirty? + // Unnecessary to update children as well. + return; + } + if (ancestorFlags & AncestorClipsChildren) { + Q_Q(QGraphicsItem); + // Check if we can avoid updating all children. + QGraphicsItem *p = parent; + QRectF br = q->boundingRect(); + while (p) { + if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) { + bool ok; + QTransform x = q->itemTransform(p, &ok); + if (!ok) + break; + if (x.mapRect(br).contains(p->boundingRect())) + return; + } + p = p->d_ptr->parent; + if (!p || !(p->d_ptr->ancestorFlags & AncestorClipsChildren)) + break; + // ### check one level only + break; + } + } + foreach (QGraphicsItem *child, children) + child->d_ptr->fullUpdateHelper(); + dirtyChildren = 1; +} + +/*! + \internal + + Resolves and propagates this item's effective opacity to its children. +*/ +void QGraphicsItemPrivate::resolveEffectiveOpacity(qreal parentEffectiveOpacity) +{ + Q_Q(QGraphicsItem); + QGraphicsItem::GraphicsItemFlags myFlags = q->flags(); + QGraphicsItem::GraphicsItemFlags parentFlags = parent ? parent->flags() : QGraphicsItem::GraphicsItemFlags(0); + + // My local opacity is always part of my effective opacity. + qreal myEffectiveOpacity = q->opacity(); + + // If I have a parent, and I don't ignore my parent's opacity, and my + // parent propagates to me, then combine my local opacity with my parent's + // effective opacity into my effective opacity. + if (parent + && !(myFlags & QGraphicsItem::ItemIgnoresParentOpacity) + && !(parentFlags & QGraphicsItem::ItemDoesntPropagateOpacityToChildren)) { + myEffectiveOpacity *= parentEffectiveOpacity; + } + + // Set this item's resolved opacity. + setExtra(ExtraEffectiveOpacity, myEffectiveOpacity); + + // Resolve children always. + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->resolveEffectiveOpacity(myEffectiveOpacity); +} + +/*! + \internal + + Resolves the stacking depth of this object and all its children. +*/ +void QGraphicsItemPrivate::resolveDepth(int parentDepth) +{ + depth = parentDepth + 1; + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->resolveDepth(depth); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::invalidateSceneTransformCache() +{ + if (!scene || (parent && sceneTransformIndex == -1)) + return; + if (sceneTransformIndex != -1) + scene->d_func()->validTransforms.setBit(sceneTransformIndex, 0); + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->invalidateSceneTransformCache(); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::addChild(QGraphicsItem *child) +{ + child->d_ptr->siblingIndex = children.size(); + children.append(child); +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::removeChild(QGraphicsItem *child) +{ + int idx = child->d_ptr->siblingIndex; + int size = children.size(); + for (int i = idx; i < size - 1; ++i) { + QGraphicsItem *p = children[i + 1]; + children[i] = p; + p->d_ptr->siblingIndex = i; + } + children.removeLast(); +} + +/*! + \internal +*/ +QGraphicsItemCache *QGraphicsItemPrivate::maybeExtraItemCache() const +{ + return (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); +} + +/*! + \internal +*/ +QGraphicsItemCache *QGraphicsItemPrivate::extraItemCache() const +{ + QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + if (!c) { + QGraphicsItemPrivate *that = const_cast<QGraphicsItemPrivate *>(this); + c = new QGraphicsItemCache; + that->setExtra(ExtraCacheData, qVariantFromValue<void *>(c)); + } + return c; +} + +/*! + \internal +*/ +void QGraphicsItemPrivate::removeExtraItemCache() +{ + QGraphicsItemCache *c = (QGraphicsItemCache *)qVariantValue<void *>(extra(ExtraCacheData)); + if (c) { + c->purge(); + delete c; + } + unsetExtra(ExtraCacheData); +} + +/*! + \internal + + Tells us if it is a proxy widget +*/ +bool QGraphicsItemPrivate::isProxyWidget() const +{ + return false; +} + +/*! + Schedules a redraw of the area covered by \a rect in this item. You can + call this function whenever your item needs to be redrawn, such as if it + changes appearance or size. + + This function does not cause an immediate paint; instead it schedules a + paint request that is processed by QGraphicsView after control reaches the + event loop. The item will only be redrawn if it is visible in any + associated view. + + As a side effect of the item being repainted, other items that overlap the + area \a rect may also be repainted. + + If the item is invisible (i.e., isVisible() returns false), this function + does nothing. + + \sa paint(), boundingRect() +*/ +void QGraphicsItem::update(const QRectF &rect) +{ + d_ptr->updateHelper(rect, /* force = */ false, /* updateCache = */ true); +} + +/*! + \internal + + Scrolls \a rect in \a pix by \a dx, \a dy. + + ### This can be done much more efficiently by using XCopyArea on X11 with + the same dst and src, and through moving pixels in the raster engine. It + can probably also be done much better on the other paint engines. +*/ +void _q_scrollPixmap(QPixmap *pix, const QRect &rect, int dx, int dy) +{ +#if 0 + QPainter painter(pix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); +#elif defined Q_WS_X11 + GC gc = XCreateGC(X11->display, pix->handle(), 0, 0); + + XRectangle xrect; + xrect.x = rect.x(); + xrect.y = rect.y(); + xrect.width = rect.width(); + xrect.height = rect.height(); + XSetClipRectangles(X11->display, gc, 0, 0, &xrect, 1, YXBanded); + + XCopyArea(X11->display, pix->handle(), pix->handle(), gc, + rect.x(), rect.y(), rect.width(), rect.height(), + rect.x()+dx, rect.y()+dy); + XFreeGC(X11->display, gc); +#else + QPixmap newPix = *pix; + QPainter painter(&newPix); + painter.setClipRect(rect); + painter.drawPixmap(rect.translated(dx, dy), *pix, rect); + painter.end(); + *pix = newPix; +#endif +} + +/*! + \since 4.4 + Scrolls the contents of \a rect by \a dx, \a dy. If \a rect is a null rect + (the default), the item's bounding rect is scrolled. + + Scrolling provides a fast alternative to simply redrawing when the + contents of the item (or parts of the item) are shifted vertically or + horizontally. Depending on the current transformation and the capabilities + of the paint device (i.e., the viewport), this operation may consist of + simply moving pixels from one location to another using memmove(). In most + cases this is faster than rerendering the entire area. + + After scrolling, the item will issue an update for the newly exposed + areas. If scrolling is not supported (e.g., you are rendering to an OpenGL + viewport, which does not benefit from scroll optimizations), this function + is equivalent to calling update(\a rect). + + \sa boundingRect() +*/ +void QGraphicsItem::scroll(qreal dx, qreal dy, const QRectF &rect) +{ + Q_D(QGraphicsItem); + if (dx == 0.0 && dy == 0.0) + return; + if (!d->scene) + return; + if (d->cacheMode != NoCache) { + QGraphicsItemCache *c; + bool scrollCache = qFuzzyCompare(dx - int(dx), qreal(0.0)) && qFuzzyCompare(dy - int(dy), qreal(0.0)) + && (c = (QGraphicsItemCache *)qVariantValue<void *>(d_ptr->extra(QGraphicsItemPrivate::ExtraCacheData))) + && (d->cacheMode == ItemCoordinateCache && !c->fixedSize.isValid()); + if (scrollCache) { + QPixmap pix; + if (QPixmapCache::find(c->key, pix)) { + // Adjust with 2 pixel margin. Notice the loss of precision + // when converting to QRect. + int adjust = 2; + QRectF br = boundingRect().adjusted(-adjust, -adjust, adjust, adjust); + QRect irect = rect.toRect().translated(-br.x(), -br.y()); + + _q_scrollPixmap(&pix, irect, dx, dy); + + QPixmapCache::insert(c->key, pix); + + // Translate the existing expose. + foreach (QRectF exposedRect, c->exposed) + c->exposed += exposedRect.translated(dx, dy) & rect; + + // Calculate exposure. + QRegion exposed; + QRect r = rect.toRect(); + exposed += r; + exposed -= r.translated(dx, dy); + foreach (QRect rect, exposed.rects()) + update(rect); + d_ptr->updateHelper(); + } else { + update(rect); + } + } else { + // ### This is very slow, and can be done much better. If the cache is + // local and matches the below criteria for rotation and scaling, we + // can easily scroll. And if the cache is in device coordinates, we + // can scroll both the viewport and the cache. + update(rect); + } + return; + } + + QRectF scrollRect = !rect.isNull() ? rect : boundingRect(); + int couldntScroll = d->scene->views().size(); + foreach (QGraphicsView *view, d->scene->views()) { + if (view->viewport()->inherits("QGLWidget")) { + // ### Please replace with a widget attribute; any widget that + // doesn't support partial updates / doesn't support scrolling + // should be skipped in this code. Qt::WA_NoPartialUpdates or so. + continue; + } + + static const QLineF up(0, 0, 0, -1); + static const QLineF down(0, 0, 0, 1); + static const QLineF left(0, 0, -1, 0); + static const QLineF right(0, 0, 1, 0); + + QTransform deviceTr; + if (d->itemIsUntransformable()) { + deviceTr = deviceTransform(view->viewportTransform()); + } else { + deviceTr = sceneTransform() * view->viewportTransform(); + } + + QRect deviceScrollRect = deviceTr.mapRect(scrollRect).toRect(); + QLineF v1 = deviceTr.map(right); + QLineF v2 = deviceTr.map(down); + QLineF u1 = v1.unitVector(); u1.translate(-v1.p1()); + QLineF u2 = v2.unitVector(); u2.translate(-v2.p1()); + bool noScroll = false; + + // Check if the delta resolves to ints in device space. + QPointF deviceDelta = deviceTr.map(QPointF(dx, dy)); + if ((deviceDelta.x() - int(deviceDelta.x())) + || (deviceDelta.y() - int(deviceDelta.y()))) { + noScroll = true; + } else { + // Check if the unit vectors have no fraction in device space. + qreal v1l = v1.length(); + if (v1l - int(v1l)) { + noScroll = true; + } else { + dx *= v1.length(); + } + qreal v2l = v2.length(); + if (v2l - int(v2l)) { + noScroll = true; + } else { + dy *= v2.length(); + } + } + + if (!noScroll) { + if (u1 == right) { + if (u2 == up) { + // flipped + dy = -dy; + } else if (u2 == down) { + // normal + } else { + noScroll = true; + } + } else if (u1 == left) { + if (u2 == up) { + // mirrored & flipped / rotated 180 degrees + dx = -dx; + dy = -dy; + } else if (u2 == down) { + // mirrored + dx = -dx; + } else { + noScroll = true; + } + } else if (u1 == up) { + if (u2 == left) { + // rotated -90 & mirrored + qreal tmp = dy; + dy = -dx; + dx = -tmp; + } else if (u2 == right) { + // rotated -90 + qreal tmp = dy; + dy = -dx; + dx = tmp; + } else { + noScroll = true; + } + } else if (u1 == down) { + if (u2 == left) { + // rotated 90 + qreal tmp = dy; + dy = dx; + dx = -tmp; + } else if (u2 == right) { + // rotated 90 & mirrored + qreal tmp = dy; + dy = dx; + dx = tmp; + } else { + noScroll = true; + } + } + } + + if (!noScroll) { + view->viewport()->scroll(int(dx), int(dy), deviceScrollRect); + --couldntScroll; + } + } + if (couldntScroll) + update(rect); +} + +/*! + \fn void QGraphicsItem::update(qreal x, qreal y, qreal width, qreal height) + \overload + + This convenience function is equivalent to calling update(QRectF(\a x, \a + y, \a width, \a height)). +*/ + +/*! + Maps the point \a point, which is in this item's coordinate system, to \a + item's coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), transform(), mapFromItem(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPointF &point) const +{ + if (item) + return itemTransform(item).map(point); + return mapToScene(point); +} + +/*! + \fn QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling mapToItem(\a item, + QPointF(\a x, \a y)). +*/ + +/*! + Maps the point \a point, which is in this item's coordinate system, to its + parent's coordinate system, and returns the mapped coordinate. If the item + has no parent, \a point will be mapped to the scene's coordinate system. + + \sa mapToItem(), mapToScene(), transform(), mapFromParent(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapToParent(const QPointF &point) const +{ + return d_ptr->pos + (d_ptr->hasTransform ? transform().map(point) : point); +} + +/*! + \fn QPointF QGraphicsItem::mapToParent(qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling mapToParent(QPointF(\a + x, \a y)). +*/ + +/*! + Maps the point \a point, which is in this item's coordinate system, to the + scene's coordinate system, and returns the mapped coordinate. + + \sa mapToItem(), mapToParent(), transform(), mapFromScene(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapToScene(const QPointF &point) const +{ + return sceneTransform().map(point); +} + +/*! + \fn QPointF QGraphicsItem::mapToScene(qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling mapToScene(QPointF(\a + x, \a y)). +*/ + +/*! + Maps the rectangle \a rect, which is in this item's coordinate system, to + \a item's coordinate system, and returns the mapped rectangle as a polygon. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QRectF &rect) const +{ + if (item) + return itemTransform(item).map(rect); + return mapToScene(rect); +} + +/*! + \fn QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapToItem(item, QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the rectangle \a rect, which is in this item's coordinate system, to + its parent's coordinate system, and returns the mapped rectangle as a + polygon. If the item has no parent, \a rect will be mapped to the scene's + coordinate system. + + \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View + Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToParent(const QRectF &rect) const +{ + if (!d_ptr->hasTransform) + return QPolygonF(rect.translated(d_ptr->pos)); + return transform().map(rect.translated(d_ptr->pos)); +} + +/*! + \fn QPolygonF QGraphicsItem::mapToParent(qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapToParent(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the rectangle \a rect, which is in this item's coordinate system, to + the scene's coordinate system, and returns the mapped rectangle as a polygon. + + \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View + Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToScene(const QRectF &rect) const +{ + return sceneTransform().map(rect); +} + +/*! + \fn QPolygonF QGraphicsItem::mapToScene(qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapToScene(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in this item's coordinate system, to + \a item's coordinate system, and returns the mapped rectangle as a new + rectangle (i.e., the bounding rectangle of the resulting polygon). + + If \a item is 0, this function returns the same as mapRectToScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const +{ + if (item) + return itemTransform(item).mapRect(rect); + return mapRectToScene(rect); +} + +/*! + \fn QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectToItem(item, QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in this item's coordinate system, to + its parent's coordinate system, and returns the mapped rectangle as a new + rectangle (i.e., the bounding rectangle of the resulting polygon). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectToParent(const QRectF &rect) const +{ + QRectF r = rect.translated(d_ptr->pos.x(), d_ptr->pos.y()); + return !d_ptr->hasTransform ? r : transform().mapRect(r); +} + +/*! + \fn QRectF QGraphicsItem::mapRectToParent(qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectToParent(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in this item's coordinate system, to + the scene coordinate system, and returns the mapped rectangle as a new + rectangle (i.e., the bounding rectangle of the resulting polygon). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectToScene(const QRectF &rect) const +{ + return sceneTransform().mapRect(rect); +} + +/*! + \fn QRectF QGraphicsItem::mapRectToScene(qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectToScene(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in \a item's coordinate system, to + this item's coordinate system, and returns the mapped rectangle as a new + rectangle (i.e., the bounding rectangle of the resulting polygon). + + If \a item is 0, this function returns the same as mapRectFromScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const +{ + if (item) + return item->itemTransform(this).mapRect(rect); + return mapRectFromScene(rect); +} + +/*! + \fn QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectFromItem(item, QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in this item's parent's coordinate + system, to this item's coordinate system, and returns the mapped rectangle + as a new rectangle (i.e., the bounding rectangle of the resulting + polygon). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectFromParent(const QRectF &rect) const +{ + QRectF r = rect.translated(-d_ptr->pos); + return d_ptr->hasTransform ? transform().inverted().mapRect(r) : r; +} + +/*! + \fn QRectF QGraphicsItem::mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectFromParent(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + \since 4.5 + + Maps the rectangle \a rect, which is in scene coordinates, to this item's + coordinate system, and returns the mapped rectangle as a new rectangle + (i.e., the bounding rectangle of the resulting polygon). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QRectF QGraphicsItem::mapRectFromScene(const QRectF &rect) const +{ + return sceneTransform().inverted().mapRect(rect); +} + +/*! + \fn QRectF QGraphicsItem::mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const + \since 4.5 + + This convenience function is equivalent to calling mapRectFromScene(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the polygon \a polygon, which is in this item's coordinate system, to + \a item's coordinate system, and returns the mapped polygon. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const +{ + if (item) + return itemTransform(item).map(polygon); + return mapToScene(polygon); +} + +/*! + Maps the polygon \a polygon, which is in this item's coordinate system, to + its parent's coordinate system, and returns the mapped polygon. If the + item has no parent, \a polygon will be mapped to the scene's coordinate + system. + + \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View + Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToParent(const QPolygonF &polygon) const +{ + QPolygonF p = polygon; + p.translate(d_ptr->pos); + return d_ptr->hasTransform ? transform().map(p) : p; +} + +/*! + Maps the polygon \a polygon, which is in this item's coordinate system, to + the scene's coordinate system, and returns the mapped polygon. + + \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View + Coordinate System} +*/ +QPolygonF QGraphicsItem::mapToScene(const QPolygonF &polygon) const +{ + return sceneTransform().map(polygon); +} + +/*! + Maps the path \a path, which is in this item's coordinate system, to + \a item's coordinate system, and returns the mapped path. + + If \a item is 0, this function returns the same as mapToScene(). + + \sa itemTransform(), mapToParent(), mapToScene(), mapFromItem(), {The + Graphics View Coordinate System} +*/ +QPainterPath QGraphicsItem::mapToItem(const QGraphicsItem *item, const QPainterPath &path) const +{ + if (item) + return itemTransform(item).map(path); + return mapToScene(path); +} + +/*! + Maps the path \a path, which is in this item's coordinate system, to + its parent's coordinate system, and returns the mapped path. If the + item has no parent, \a path will be mapped to the scene's coordinate + system. + + \sa mapToScene(), mapToItem(), mapFromParent(), {The Graphics View + Coordinate System} +*/ +QPainterPath QGraphicsItem::mapToParent(const QPainterPath &path) const +{ + return d_ptr->parent ? itemTransform(d_ptr->parent).map(path) : mapToScene(path); +} + +/*! + Maps the path \a path, which is in this item's coordinate system, to + the scene's coordinate system, and returns the mapped path. + + \sa mapToParent(), mapToItem(), mapFromScene(), {The Graphics View + Coordinate System} +*/ +QPainterPath QGraphicsItem::mapToScene(const QPainterPath &path) const +{ + return sceneTransform().map(path); +} + +/*! + Maps the point \a point, which is in \a item's coordinate system, to this + item's coordinate system, and returns the mapped coordinate. + + If \a item is 0, this function returns the same as mapFromScene(). + + \sa itemTransform(), mapFromParent(), mapFromScene(), transform(), mapToItem(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPointF &point) const +{ + if (item) + return item->itemTransform(this).map(point); + return mapFromScene(point); +} + +/*! + \fn QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling mapFromItem(\a item, + QPointF(\a x, \a y)). +*/ + +/*! + Maps the point \a point, which is in this item's parent's coordinate + system, to this item's coordinate system, and returns the mapped + coordinate. + + \sa mapFromItem(), mapFromScene(), transform(), mapToParent(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapFromParent(const QPointF &point) const +{ + if (d_ptr->hasTransform) + return transform().inverted().map(point - d_ptr->pos); + return point - d_ptr->pos; +} + +/*! + \fn QPointF QGraphicsItem::mapFromParent(qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling + mapFromParent(QPointF(\a x, \a y)). +*/ + +/*! + Maps the point \a point, which is in this item's scene's coordinate + system, to this item's coordinate system, and returns the mapped + coordinate. + + \sa mapFromItem(), mapFromParent(), transform(), mapToScene(), {The Graphics + View Coordinate System} +*/ +QPointF QGraphicsItem::mapFromScene(const QPointF &point) const +{ + return sceneTransform().inverted().map(point); +} + +/*! + \fn QPointF QGraphicsItem::mapFromScene(qreal x, qreal y) const + \overload + + This convenience function is equivalent to calling mapFromScene(QPointF(\a + x, \a y)). +*/ + +/*! + Maps the rectangle \a rect, which is in \a item's coordinate system, to + this item's coordinate system, and returns the mapped rectangle as a + polygon. + + If \a item is 0, this function returns the same as mapFromScene() + + \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The Graphics View Coordinate + System} +*/ +QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QRectF &rect) const +{ + if (item) + return item->itemTransform(this).map(rect); + return mapFromScene(rect); +} + +/*! + \fn QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapFromItem(item, QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the rectangle \a rect, which is in this item's parent's coordinate + system, to this item's coordinate system, and returns the mapped rectangle + as a polygon. + + \sa mapToParent(), mapFromItem(), transform(), {The Graphics View Coordinate + System} +*/ +QPolygonF QGraphicsItem::mapFromParent(const QRectF &rect) const +{ + QRectF r = rect.translated(-d_ptr->pos); + return d_ptr->hasTransform ? transform().inverted().map(r) : r; +} + +/*! + \fn QPolygonF QGraphicsItem::mapFromParent(qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapFromItem(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the rectangle \a rect, which is in this item's scene's coordinate + system, to this item's coordinate system, and returns the mapped rectangle + as a polygon. + + \sa mapToScene(), mapFromItem(), transform(), {The Graphics View Coordinate + System} +*/ +QPolygonF QGraphicsItem::mapFromScene(const QRectF &rect) const +{ + return sceneTransform().inverted().map(rect); +} + +/*! + \fn QPolygonF QGraphicsItem::mapFromScene(qreal x, qreal y, qreal w, qreal h) const + \since 4.3 + + This convenience function is equivalent to calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Maps the polygon \a polygon, which is in \a item's coordinate system, to + this item's coordinate system, and returns the mapped polygon. + + If \a item is 0, this function returns the same as mapFromScene(). + + \sa itemTransform(), mapToItem(), mapFromParent(), transform(), {The + Graphics View Coordinate System} +*/ +QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const +{ + if (item) + return item->itemTransform(this).map(polygon); + return mapFromScene(polygon); +} + +/*! + Maps the polygon \a polygon, which is in this item's parent's coordinate + system, to this item's coordinate system, and returns the mapped polygon. + + \sa mapToParent(), mapToItem(), transform(), {The Graphics View Coordinate + System} +*/ +QPolygonF QGraphicsItem::mapFromParent(const QPolygonF &polygon) const +{ + QPolygonF p = polygon; + p.translate(-d_ptr->pos); + return d_ptr->hasTransform ? transform().inverted().map(p) : p; +} + +/*! + Maps the polygon \a polygon, which is in this item's scene's coordinate + system, to this item's coordinate system, and returns the mapped polygon. + + \sa mapToScene(), mapFromParent(), transform(), {The Graphics View Coordinate + System} +*/ +QPolygonF QGraphicsItem::mapFromScene(const QPolygonF &polygon) const +{ + return sceneTransform().inverted().map(polygon); +} + +/*! + Maps the path \a path, which is in \a item's coordinate system, to + this item's coordinate system, and returns the mapped path. + + If \a item is 0, this function returns the same as mapFromScene(). + + \sa itemTransform(), mapFromParent(), mapFromScene(), mapToItem(), {The + Graphics View Coordinate System} +*/ +QPainterPath QGraphicsItem::mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const +{ + if (item) + return item->itemTransform(this).map(path); + return mapFromScene(path); +} + +/*! + Maps the path \a path, which is in this item's parent's coordinate + system, to this item's coordinate system, and returns the mapped path. + + \sa mapFromScene(), mapFromItem(), mapToParent(), {The Graphics View + Coordinate System} +*/ +QPainterPath QGraphicsItem::mapFromParent(const QPainterPath &path) const +{ + if (d_ptr->parent) + return d_ptr->parent->itemTransform(this).map(path); + return mapFromScene(path); +} + +/*! + Maps the path \a path, which is in this item's scene's coordinate + system, to this item's coordinate system, and returns the mapped path. + + \sa mapFromParent(), mapFromItem(), mapToScene(), {The Graphics View + Coordinate System} +*/ +QPainterPath QGraphicsItem::mapFromScene(const QPainterPath &path) const +{ + return sceneTransform().inverted().map(path); +} + +/*! + Returns true if this item is an ancestor of \a child (i.e., if this item + is \a child's parent, or one of \a child's parent's ancestors). + + \sa parentItem() +*/ +bool QGraphicsItem::isAncestorOf(const QGraphicsItem *child) const +{ + if (!child || child == this) + return false; + const QGraphicsItem *ancestor = child; + while ((ancestor = ancestor->d_ptr->parent)) { + if (ancestor == this) + return true; + } + return false; +} + +/*! + \since 4.4 + + Returns the closest common ancestor item of this item and \a other, or 0 + if either \a other is 0, or there is no common ancestor. + + \sa isAncestorOf() +*/ +QGraphicsItem *QGraphicsItem::commonAncestorItem(const QGraphicsItem *other) const +{ + if (!other) + return 0; + if (other == this) + return const_cast<QGraphicsItem *>(this); + const QGraphicsItem *thisw = this; + const QGraphicsItem *otherw = other; + int thisDepth = d_ptr->depth; + int otherDepth = other->d_ptr->depth; + while (thisDepth > otherDepth) { + thisw = thisw->d_ptr->parent; + --thisDepth; + } + while (otherDepth > thisDepth) { + otherw = otherw->d_ptr->parent; + --otherDepth; + } + while (thisw && thisw != otherw) { + thisw = thisw->d_ptr->parent; + otherw = otherw->d_ptr->parent; + } + return const_cast<QGraphicsItem *>(thisw); +} + +/*! + \since 4,4 + Returns true if this item is currently under the mouse cursor in one of + the views; otherwise, false is returned. + + \sa QGraphicsScene::views(), QCursor::pos() +*/ +bool QGraphicsItem::isUnderMouse() const +{ + Q_D(const QGraphicsItem); + if (!d->scene) + return false; + + QPoint cursorPos = QCursor::pos(); + foreach (QGraphicsView *view, d->scene->views()) { + if (contains(mapFromScene(view->mapToScene(view->mapFromGlobal(cursorPos))))) + return true; + } + return false; +} + +/*! + Returns this item's custom data for the key \a key as a QVariant. + + Custom item data is useful for storing arbitrary properties in any + item. Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 11 + + Qt does not use this feature for storing data; it is provided solely + for the convenience of the user. + + \sa setData() +*/ +QVariant QGraphicsItem::data(int key) const +{ + QGraphicsItemCustomDataStore *store = qt_dataStore(); + if (!store->data.contains(this)) + return QVariant(); + return store->data.value(this).value(key); +} + +/*! + Sets this item's custom data for the key \a key to \a value. + + Custom item data is useful for storing arbitrary properties for any + item. Qt does not use this feature for storing data; it is provided solely + for the convenience of the user. + + \sa data() +*/ +void QGraphicsItem::setData(int key, const QVariant &value) +{ + qt_dataStore()->data[this][key] = value; +} + +/*! + \fn T qgraphicsitem_cast(QGraphicsItem *item) + \relates QGraphicsItem + \since 4.2 + + Returns the given \a item cast to type T if \a item is of type T; + otherwise, 0 is returned. + + \note To make this function work correctly with custom items, reimplement + the \l{QGraphicsItem::}{type()} function for each custom QGraphicsItem + subclass. + + \sa QGraphicsItem::type(), QGraphicsItem::UserType +*/ + +/*! + Returns the type of an item as an int. All standard graphicsitem classes + are associated with a unique value; see QGraphicsItem::Type. This type + information is used by qgraphicsitem_cast() to distinguish between types. + + The default implementation (in QGraphicsItem) returns UserType. + + To enable use of qgraphicsitem_cast() with a custom item, reimplement this + function and declare a Type enum value equal to your custom item's type. + Custom items must return a value larger than or equal to UserType (65536). + + For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp QGraphicsItem type + + \sa UserType +*/ +int QGraphicsItem::type() const +{ + return (int)UserType; +} + +/*! + Installs an event filter for this item on \a filterItem, causing + all events for this item to first pass through \a filterItem's + sceneEventFilter() function. + + To filter another item's events, install this item as an event filter + for the other item. Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 12 + + An item can only filter events for other items in the same + scene. Also, an item cannot filter its own events; instead, you + can reimplement sceneEvent() directly. + + Items must belong to a scene for scene event filters to be installed and + used. + + \sa removeSceneEventFilter(), sceneEventFilter(), sceneEvent() +*/ +void QGraphicsItem::installSceneEventFilter(QGraphicsItem *filterItem) +{ + if (!d_ptr->scene) { + qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed" + " on items in a scene."); + return; + } + if (d_ptr->scene != filterItem->scene()) { + qWarning("QGraphicsItem::installSceneEventFilter: event filters can only be installed" + " on items in the same scene."); + return; + } + d_ptr->scene->d_func()->installSceneEventFilter(this, filterItem); +} + +/*! + Removes an event filter on this item from \a filterItem. + + \sa installSceneEventFilter() +*/ +void QGraphicsItem::removeSceneEventFilter(QGraphicsItem *filterItem) +{ + if (!d_ptr->scene || d_ptr->scene != filterItem->scene()) + return; + d_ptr->scene->d_func()->removeSceneEventFilter(this, filterItem); +} + +/*! + Filters events for the item \a watched. \a event is the filtered + event. + + Reimplementing this function in a subclass makes it possible + for the item to be used as an event filter for other items, + intercepting all the events send to those items before they are + able to respond. + + Reimplementations must return true to prevent further processing of + a given event, ensuring that it will not be delivered to the watched + item, or return false to indicate that the event should be propagated + further by the event system. + + \sa installSceneEventFilter() +*/ +bool QGraphicsItem::sceneEventFilter(QGraphicsItem *watched, QEvent *event) +{ + Q_UNUSED(watched); + Q_UNUSED(event); + return false; +} + +/*! + This virtual function receives events to this item. Reimplement + this function to intercept events before they are dispatched to + the specialized event handlers contextMenuEvent(), focusInEvent(), + focusOutEvent(), hoverEnterEvent(), hoverMoveEvent(), + hoverLeaveEvent(), keyPressEvent(), keyReleaseEvent(), + mousePressEvent(), mouseReleaseEvent(), mouseMoveEvent(), and + mouseDoubleClickEvent(). + + Returns true if the event was recognized and handled; otherwise, (e.g., if + the event type was not recognized,) false is returned. + + \a event is the intercepted event. +*/ +bool QGraphicsItem::sceneEvent(QEvent *event) +{ + if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents) { + if (event->type() == QEvent::HoverEnter || event->type() == QEvent::HoverLeave + || event->type() == QEvent::DragEnter || event->type() == QEvent::DragLeave) { + // Hover enter and hover leave events for children are ignored; + // hover move events are forwarded. + return true; + } + + QGraphicsItem *handler = this; + do { + handler = handler->d_ptr->parent; + Q_ASSERT(handler); + } while (handler->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorHandlesChildEvents); + // Forward the event to the closest parent that handles child + // events, mapping existing item-local coordinates to its + // coordinate system. + d_ptr->remapItemPos(event, handler); + handler->sceneEvent(event); + return true; + } + + if (!d_ptr->visible) { + // Eaten + return true; + } + + switch (event->type()) { + case QEvent::FocusIn: + focusInEvent(static_cast<QFocusEvent *>(event)); + break; + case QEvent::FocusOut: + focusOutEvent(static_cast<QFocusEvent *>(event)); + break; + case QEvent::GraphicsSceneContextMenu: + contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event)); + break; + case QEvent::GraphicsSceneDragEnter: + dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDragMove: + dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDragLeave: + dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDrop: + dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverEnter: + hoverEnterEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverMove: + hoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverLeave: + hoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseDoubleClick: + mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneWheel: + wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event)); + break; + case QEvent::KeyPress: { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { + if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + bool res = false; + if (k->key() == Qt::Key_Backtab + || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) { + if (d_ptr->isWidget) { + res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(false); + } else if (d_ptr->scene) { + res = d_ptr->scene->focusNextPrevChild(false); + } + } else if (k->key() == Qt::Key_Tab) { + if (d_ptr->isWidget) { + res = static_cast<QGraphicsWidget *>(this)->focusNextPrevChild(true); + } else if (d_ptr->scene) { + res = d_ptr->scene->focusNextPrevChild(true); + } + } + if (!res) + event->ignore(); + return true; + } + } + keyPressEvent(static_cast<QKeyEvent *>(event)); + break; + } + case QEvent::KeyRelease: + keyReleaseEvent(static_cast<QKeyEvent *>(event)); + break; + case QEvent::InputMethod: + inputMethodEvent(static_cast<QInputMethodEvent *>(event)); + break; + default: + return false; + } + + return true; +} + +/*! + This event handler can be reimplemented in a subclass to process context + menu events. The \a event parameter contains details about the event to + be handled. + + If you ignore the event, (i.e., by calling QEvent::ignore(),) \a event + will propagate to any item beneath this item. If no items accept the + event, it will be ignored by the scene, and propagate to the view. + + It's common to open a QMenu in response to receiving a context menu + event. Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 13 + + The default implementation ignores the event. + + \sa sceneEvent() +*/ +void QGraphicsItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + event->ignore(); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + drag enter events for this item. Drag enter events are generated as the + cursor enters the item's area. + + By accepting the event, (i.e., by calling QEvent::accept(),) the item will + accept drop events, in addition to receiving drag move and drag + leave. Otherwise, the event will be ignored and propagate to the item + beneath. If the event is accepted, the item will receive a drag move event + before control goes back to the event loop. + + A common implementation of dragEnterEvent accepts or ignores \a event + depending on the associated mime data in \a event. Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 14 + + Items do not receive drag and drop events by default; to enable this + feature, call \c setAcceptDrops(true). + + The default implementation does nothing. + + \sa dropEvent(), dragMoveEvent(), dragLeaveEvent() +*/ +void QGraphicsItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsItem); + // binary compatibility workaround between 4.4 and 4.5 + if (d->isProxyWidget()) + static_cast<QGraphicsProxyWidget*>(this)->dragEnterEvent(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + drag leave events for this item. Drag leave events are generated as the + cursor leaves the item's area. Most often you will not need to reimplement + this function, but it can be useful for resetting state in your item + (e.g., highlighting). + + Calling QEvent::ignore() or QEvent::accept() on \a event has no effect. + + Items do not receive drag and drop events by default; to enable this + feature, call \c setAcceptDrops(true). + + The default implementation does nothing. + + \sa dragEnterEvent(), dropEvent(), dragMoveEvent() +*/ +void QGraphicsItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsItem); + // binary compatibility workaround between 4.4 and 4.5 + if (d->isProxyWidget()) + static_cast<QGraphicsProxyWidget*>(this)->dragLeaveEvent(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + drag move events for this item. Drag move events are generated as the + cursor moves around inside the item's area. Most often you will not need + to reimplement this function; it is used to indicate that only parts of + the item can accept drops. + + Calling QEvent::ignore() or QEvent::accept() on \a event toggles whether + or not the item will accept drops at the position from the event. By + default, \a event is accepted, indicating that the item allows drops at + the specified position. + + Items do not receive drag and drop events by default; to enable this + feature, call \c setAcceptDrops(true). + + The default implementation does nothing. + + \sa dropEvent(), dragEnterEvent(), dragLeaveEvent() +*/ +void QGraphicsItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsItem); + // binary compatibility workaround between 4.4 and 4.5 + if (d->isProxyWidget()) + static_cast<QGraphicsProxyWidget*>(this)->dragMoveEvent(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + drop events for this item. Items can only receive drop events if the last + drag move event was accepted. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no effect. + + Items do not receive drag and drop events by default; to enable this + feature, call \c setAcceptDrops(true). + + The default implementation does nothing. + + \sa dragEnterEvent(), dragMoveEvent(), dragLeaveEvent() +*/ +void QGraphicsItem::dropEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsItem); + // binary compatibility workaround between 4.4 and 4.5 + if (d->isProxyWidget()) + static_cast<QGraphicsProxyWidget*>(this)->dropEvent(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + focus in events for this item. The default implementation calls + ensureVisible(). + + \sa focusOutEvent(), sceneEvent() +*/ +void QGraphicsItem::focusInEvent(QFocusEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + focus out events for this item. The default implementation does nothing. + + \sa focusInEvent(), sceneEvent() +*/ +void QGraphicsItem::focusOutEvent(QFocusEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + hover enter events for this item. The default implementation calls + update(); otherwise it does nothing. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no effect. + + \sa hoverMoveEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents() +*/ +void QGraphicsItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + d_ptr->updateHelper(); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + hover move events for this item. The default implementation does nothing. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no effect. + + \sa hoverEnterEvent(), hoverLeaveEvent(), sceneEvent(), setAcceptHoverEvents() +*/ +void QGraphicsItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + hover leave events for this item. The default implementation calls + update(); otherwise it does nothing. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no effect. + + \sa hoverEnterEvent(), hoverMoveEvent(), sceneEvent(), setAcceptHoverEvents() +*/ +void QGraphicsItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + d_ptr->updateHelper(); +} + +/*! + This event handler, for event \a event, can be reimplemented to + receive key press events for this item. The default implementation + ignores the event. If you reimplement this handler, the event will by + default be accepted. + + Note that key events are only received for items that set the + ItemIsFocusable flag, and that have keyboard input focus. + + \sa keyReleaseEvent(), setFocus(), QGraphicsScene::setFocusItem(), + sceneEvent() +*/ +void QGraphicsItem::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + key release events for this item. The default implementation + ignores the event. If you reimplement this handler, the event will by + default be accepted. + + Note that key events are only received for items that set the + ItemIsFocusable flag, and that have keyboard input focus. + + \sa keyPressEvent(), setFocus(), QGraphicsScene::setFocusItem(), + sceneEvent() +*/ +void QGraphicsItem::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +/*! + This event handler, for event \a event, can be reimplemented to + receive mouse press events for this item. Mouse press events are + only delivered to items that accept the mouse button that is + pressed. By default, an item accepts all mouse buttons, but you + can change this by calling setAcceptedMouseButtons(). + + The mouse press event decides which item should become the mouse + grabber (see QGraphicsScene::mouseGrabberItem()). If you do not + reimplement this function, the press event will propagate to any + topmost item beneath this item, and no other mouse events will be + delivered to this item. + + If you do reimplement this function, \a event will by default be + accepted (see QEvent::accept()), and this item is then the mouse + grabber. This allows the item to receive future move, release and + doubleclick events. If you call QEvent::ignore() on \a event, this + item will lose the mouse grab, and \a event will propagate to any + topmost item beneath. No further mouse events will be delivered to + this item unless a new mouse press event is received. + + The default implementation handles basic item interaction, such as + selection and moving. If you want to keep the base implementation + when reimplementing this function, call + QGraphicsItem::mousePressEvent() in your reimplementation. + + The event is \l{QEvent::ignore()}d for items that are neither + \l{QGraphicsItem::ItemIsMovable}{movable} nor + \l{QGraphicsItem::ItemIsSelectable}{selectable}. + + \sa mouseMoveEvent(), mouseReleaseEvent(), + mouseDoubleClickEvent(), sceneEvent() +*/ +void QGraphicsItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if (event->button() == Qt::LeftButton && (flags() & ItemIsSelectable)) { + bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; + if (!multiSelect) { + if (!d_ptr->selected) { + if (QGraphicsScene *scene = d_ptr->scene) { + ++scene->d_func()->selectionChanging; + scene->clearSelection(); + --scene->d_func()->selectionChanging; + } + setSelected(true); + } + } + } else if (!(flags() & ItemIsMovable)) { + event->ignore(); + } + if (d_ptr->isWidget) { + // Qt::Popup closes when you click outside. + QGraphicsWidget *w = static_cast<QGraphicsWidget *>(this); + if (w->windowFlags() & Qt::Popup) { + event->accept(); + if (!w->rect().contains(event->pos())) + w->close(); + } + } +} + +/*! + obsolete +*/ +bool _qt_movableAncestorIsSelected(const QGraphicsItem *item) +{ + const QGraphicsItem *parent = item->parentItem(); + return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent)); +} + +bool QGraphicsItemPrivate::movableAncestorIsSelected(const QGraphicsItem *item) +{ + const QGraphicsItem *parent = item->d_ptr->parent; + return parent && (((parent->flags() & QGraphicsItem::ItemIsMovable) && parent->isSelected()) || _qt_movableAncestorIsSelected(parent)); +} + +/*! + This event handler, for event \a event, can be reimplemented to + receive mouse move events for this item. If you do receive this + event, you can be certain that this item also received a mouse + press event, and that this item is the current mouse grabber. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no + effect. + + The default implementation handles basic item interaction, such as + selection and moving. If you want to keep the base implementation + when reimplementing this function, call + QGraphicsItem::mouseMoveEvent() in your reimplementation. + + Please note that mousePressEvent() decides which graphics item it + is that receives mouse events. See the mousePressEvent() + description for details. + + \sa mousePressEvent(), mouseReleaseEvent(), + mouseDoubleClickEvent(), sceneEvent() +*/ +void QGraphicsItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) && (flags() & ItemIsMovable)) { + // Determine the list of items that need to be moved. + QList<QGraphicsItem *> selectedItems; + QMap<QGraphicsItem *, QPointF> initialPositions; + if (d_ptr->scene) { + selectedItems = d_ptr->scene->selectedItems(); + initialPositions = d_ptr->scene->d_func()->movingItemsInitialPositions; + if (initialPositions.isEmpty()) { + foreach (QGraphicsItem *item, selectedItems) + initialPositions[item] = item->pos(); + initialPositions[this] = pos(); + } + d_ptr->scene->d_func()->movingItemsInitialPositions = initialPositions; + } + + // Find the active view. + QGraphicsView *view = 0; + if (event->widget()) + view = qobject_cast<QGraphicsView *>(event->widget()->parentWidget()); + + // Move all selected items + int i = 0; + bool movedMe = false; + while (i <= selectedItems.size()) { + QGraphicsItem *item = 0; + if (i < selectedItems.size()) + item = selectedItems.at(i); + else + item = this; + if (item == this) { + // Slightly clumsy-looking way to ensure that "this" is part + // of the list of items to move, this is to avoid allocations + // (appending this item to the list of selected items causes a + // detach). + if (movedMe) + break; + movedMe = true; + } + + if ((item->flags() & ItemIsMovable) && !QGraphicsItemPrivate::movableAncestorIsSelected(item)) { + QPointF currentParentPos; + QPointF buttonDownParentPos; + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations) { + // Items whose ancestors ignore transformations need to + // map screen coordinates to local coordinates, then map + // those to the parent. + QTransform viewToItemTransform = (item->deviceTransform(view->viewportTransform())).inverted(); + currentParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->screenPos())))); + buttonDownParentPos = mapToParent(viewToItemTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton))))); + } else if (item->flags() & ItemIgnoresTransformations) { + // Root items that ignore transformations need to + // calculate their diff by mapping viewport coordinates + // directly to parent coordinates. + QTransform viewToParentTransform = (item->transform().translate(item->d_ptr->pos.x(), item->d_ptr->pos.y())) + * (item->sceneTransform() * view->viewportTransform()).inverted(); + currentParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->screenPos()))); + buttonDownParentPos = viewToParentTransform.map(QPointF(view->mapFromGlobal(event->buttonDownScreenPos(Qt::LeftButton)))); + } else { + // All other items simply map from the scene. + currentParentPos = item->mapToParent(item->mapFromScene(event->scenePos())); + buttonDownParentPos = item->mapToParent(item->mapFromScene(event->buttonDownScenePos(Qt::LeftButton))); + } + + item->setPos(initialPositions.value(item) + currentParentPos - buttonDownParentPos); + + if (item->flags() & ItemIsSelectable) + item->setSelected(true); + } + ++i; + } + + } else { + event->ignore(); + } +} + +/*! + This event handler, for event \a event, can be reimplemented to + receive mouse release events for this item. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no + effect. + + The default implementation handles basic item interaction, such as + selection and moving. If you want to keep the base implementation + when reimplementing this function, call + QGraphicsItem::mouseReleaseEvent() in your reimplementation. + + Please note that mousePressEvent() decides which graphics item it + is that receives mouse events. See the mousePressEvent() + description for details. + + \sa mousePressEvent(), mouseMoveEvent(), mouseDoubleClickEvent(), + sceneEvent() +*/ +void QGraphicsItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (flags() & ItemIsSelectable) { + bool multiSelect = (event->modifiers() & Qt::ControlModifier) != 0; + if (event->scenePos() == event->buttonDownScenePos(Qt::LeftButton)) { + // The item didn't move + if (multiSelect) { + setSelected(!isSelected()); + } else { + bool selectionChanged = false; + if (QGraphicsScene *scene = d_ptr->scene) { + ++scene->d_func()->selectionChanging; + // Clear everything but this item. Bypass + // QGraphicsScene::clearSelection()'s default behavior by + // temporarily removing this item from the selection list. + if (d_ptr->selected) { + scene->d_func()->selectedItems.remove(this); + foreach (QGraphicsItem *item, scene->d_func()->selectedItems) { + if (item->isSelected()) { + selectionChanged = true; + break; + } + } + } + scene->clearSelection(); + if (d_ptr->selected) + scene->d_func()->selectedItems.insert(this); + --scene->d_func()->selectionChanging; + if (selectionChanged) + emit d_ptr->scene->selectionChanged(); + } + setSelected(true); + } + } + } + if (d_ptr->scene && !event->buttons()) + d_ptr->scene->d_func()->movingItemsInitialPositions.clear(); +} + +/*! + This event handler, for event \a event, can be reimplemented to + receive mouse doubleclick events for this item. + + When doubleclicking an item, the item will first receive a mouse + press event, followed by a release event (i.e., a click), then a + doubleclick event, and finally a release event. + + Calling QEvent::ignore() or QEvent::accept() on \a event has no + effect. + + The default implementation calls mousePressEvent(). If you want to + keep the base implementation when reimplementing this function, + call QGraphicsItem::mouseDoubleClickEvent() in your + reimplementation. + + Note that an item will not receive double click events if it is + neither \l {QGraphicsItem::ItemIsSelectable}{selectable} nor + \l{QGraphicsItem::ItemIsMovable}{movable} (single mouse clicks are + ignored in this case, and that stops the generation of double + clicks). + + \sa mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), sceneEvent() +*/ +void QGraphicsItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + mousePressEvent(event); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + wheel events for this item. If you reimplement this function, \a event + will be accepted by default. + + If you ignore the event, (i.e., by calling QEvent::ignore(),) it will + propagate to any item beneath this item. If no items accept the event, it + will be ignored by the scene, and propagate to the view (e.g., the view's + vertical scroll bar). + + The default implementation ignores the event. + + \sa sceneEvent() +*/ +void QGraphicsItem::wheelEvent(QGraphicsSceneWheelEvent *event) +{ + event->ignore(); +} + +/*! + This event handler, for event \a event, can be reimplemented to receive + input method events for this item. The default implementation ignores the + event. + + \sa inputMethodQuery(), sceneEvent() +*/ +void QGraphicsItem::inputMethodEvent(QInputMethodEvent *event) +{ + event->ignore(); +} + +/*! + This method is only relevant for input items. It is used by the + input method to query a set of properties of the item to be able + to support complex input method operations, such as support for + surrounding text and reconversions. \a query specifies which + property is queried. + + \sa inputMethodEvent() +*/ +QVariant QGraphicsItem::inputMethodQuery(Qt::InputMethodQuery query) const +{ + if (isWidget()) { + // ### Qt 5: Remove. The reimplementation in + // QGraphicsProxyWidget solves this problem (but requires a + // recompile to take effect). + return d_ptr->inputMethodQueryHelper(query); + } + + Q_UNUSED(query); + return QVariant(); +} + +/*! + This virtual function is called by QGraphicsItem to notify custom items + that some part of the item's state changes. By reimplementing this + function, your can react to a change, and in some cases, (depending on \a + change,) adjustments can be made. + + \a change is the parameter of the item that is changing. \a value is the + new value; the type of the value depends on \a change. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 15 + + The default implementation does nothing, and returns \a value. + + Note: Certain QGraphicsItem functions cannot be called in a + reimplementation of this function; see the GraphicsItemChange + documentation for details. + + \sa GraphicsItemChange +*/ +QVariant QGraphicsItem::itemChange(GraphicsItemChange change, const QVariant &value) +{ + Q_UNUSED(change); + return value; +} + +/*! + \internal + + Note: This is provided as a hook to avoid future problems related + to adding virtual functions. +*/ +bool QGraphicsItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal + + Note: This is provided as a hook to avoid future problems related + to adding virtual functions. +*/ +void QGraphicsItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal + + Note: This is provided as a hook to avoid future problems related + to adding virtual functions. +*/ +QVariant QGraphicsItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \internal + + Adds this item to the scene's index. Called in conjunction with + removeFromIndex() to ensure the index bookkeeping is correct when + the item's position, transformation or shape changes. +*/ +void QGraphicsItem::addToIndex() +{ + if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { + // ### add to child index only if applicable + return; + } + if (d_ptr->scene) + d_ptr->scene->d_func()->addToIndex(this); + d_ptr->updateHelper(); +} + +/*! + \internal + + Removes this item from the scene's index. Called in conjunction + with addToIndex() to ensure the index bookkeeping is correct when + the item's position, transformation or shape changes. +*/ +void QGraphicsItem::removeFromIndex() +{ + if (d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) { + // ### remove from child index only if applicable + return; + } + d_ptr->updateHelper(); + if (d_ptr->scene) + d_ptr->scene->d_func()->removeFromIndex(this); +} + +/*! + Prepares the item for a geometry change. Call this function before + changing the bounding rect of an item to keep QGraphicsScene's index up to + date. + + prepareGeometryChange() will call update() if this is necessary. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 16 + + \sa boundingRect() +*/ +void QGraphicsItem::prepareGeometryChange() +{ + if (d_ptr->scene) { + d_ptr->updateHelper(); + + QGraphicsScenePrivate *scenePrivate = d_ptr->scene->d_func(); + scenePrivate->removeFromIndex(this); + } +} + +/*! + \internal + + Highlights \a item as selected. + + NOTE: This function is a duplicate of qt_graphicsItem_highlightSelected() in + qgraphicssvgitem.cpp! +*/ +static void qt_graphicsItem_highlightSelected( + QGraphicsItem *item, QPainter *painter, const QStyleOptionGraphicsItem *option) +{ + const QRectF murect = painter->transform().mapRect(QRectF(0, 0, 1, 1)); + if (qFuzzyCompare(qMax(murect.width(), murect.height()) + 1, 1)) + return; + + const QRectF mbrect = painter->transform().mapRect(item->boundingRect()); + if (qMin(mbrect.width(), mbrect.height()) < qreal(1.0)) + return; + + qreal itemPenWidth; + switch (item->type()) { + case QGraphicsEllipseItem::Type: + itemPenWidth = static_cast<QGraphicsEllipseItem *>(item)->pen().widthF(); + break; + case QGraphicsPathItem::Type: + itemPenWidth = static_cast<QGraphicsPathItem *>(item)->pen().widthF(); + break; + case QGraphicsPolygonItem::Type: + itemPenWidth = static_cast<QGraphicsPolygonItem *>(item)->pen().widthF(); + break; + case QGraphicsRectItem::Type: + itemPenWidth = static_cast<QGraphicsRectItem *>(item)->pen().widthF(); + break; + case QGraphicsSimpleTextItem::Type: + itemPenWidth = static_cast<QGraphicsSimpleTextItem *>(item)->pen().widthF(); + break; + case QGraphicsLineItem::Type: + itemPenWidth = static_cast<QGraphicsLineItem *>(item)->pen().widthF(); + break; + default: + itemPenWidth = 1.0; + } + const qreal pad = itemPenWidth / 2; + + const qreal penWidth = 0; // cosmetic pen + + const QColor fgcolor = option->palette.windowText().color(); + const QColor bgcolor( // ensure good contrast against fgcolor + fgcolor.red() > 127 ? 0 : 255, + fgcolor.green() > 127 ? 0 : 255, + fgcolor.blue() > 127 ? 0 : 255); + + painter->setPen(QPen(bgcolor, penWidth, Qt::SolidLine)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); + + painter->setPen(QPen(option->palette.windowText(), 0, Qt::DashLine)); + painter->setBrush(Qt::NoBrush); + painter->drawRect(item->boundingRect().adjusted(pad, pad, -pad, -pad)); +} + +/*! + \class QAbstractGraphicsShapeItem + \brief The QAbstractGraphicsShapeItem class provides a common base for + all path items. + \since 4.2 + \ingroup multimedia + + This class does not fully implement an item by itself; in particular, it + does not implement boundingRect() and paint(), which are inherited by + QGraphicsItem. + + You can subclass this item to provide a simple base implementation of + accessors for the item's pen and brush. + + \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPathItem, + QGraphicsPolygonItem, QGraphicsTextItem, QGraphicsLineItem, + QGraphicsPixmapItem, {The Graphics View Framework} +*/ + +class QAbstractGraphicsShapeItemPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QAbstractGraphicsShapeItem) +public: + + QBrush brush; + QPen pen; + + // Cached bounding rectangle + mutable QRectF boundingRect; +}; + +/*! + Constructs a QAbstractGraphicsShapeItem. \a parent is passed to + QGraphicsItem's constructor. +*/ +QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QAbstractGraphicsShapeItemPrivate, parent, scene) +{ +} + +/*! + \internal +*/ +QAbstractGraphicsShapeItem::QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd, + QGraphicsItem *parent, + QGraphicsScene *scene) + : QGraphicsItem(dd, parent, scene) +{ +} + +/*! + Destroys a QAbstractGraphicsShapeItem. +*/ +QAbstractGraphicsShapeItem::~QAbstractGraphicsShapeItem() +{ +} + +/*! + Returns the item's pen. If no pen has been set, this function returns + QPen(), a default black solid line pen with 0 width. +*/ +QPen QAbstractGraphicsShapeItem::pen() const +{ + Q_D(const QAbstractGraphicsShapeItem); + return d->pen; +} + +/*! + Sets the pen for this item to \a pen. + + The pen is used to draw the item's outline. + + \sa pen() +*/ +void QAbstractGraphicsShapeItem::setPen(const QPen &pen) +{ + Q_D(QAbstractGraphicsShapeItem); + prepareGeometryChange(); + d->pen = pen; + d->boundingRect = QRectF(); + update(); +} + +/*! + Returns the item's brush, or an empty brush if no brush has been set. + + \sa setBrush() +*/ +QBrush QAbstractGraphicsShapeItem::brush() const +{ + Q_D(const QAbstractGraphicsShapeItem); + return d->brush; +} + +/*! + Sets the item's brush to \a brush. + + The item's brush is used to fill the item. + + If you use a brush with a QGradient, the gradient + is relative to the item's coordinate system. + + \sa brush() +*/ +void QAbstractGraphicsShapeItem::setBrush(const QBrush &brush) +{ + Q_D(QAbstractGraphicsShapeItem); + d->brush = brush; + update(); +} + +/*! + \reimp +*/ +bool QAbstractGraphicsShapeItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QGraphicsItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QAbstractGraphicsShapeItem::opaqueArea() const +{ + Q_D(const QAbstractGraphicsShapeItem); + if (d->brush.isOpaque()) + return isClipped() ? clipPath() : shape(); + return QGraphicsItem::opaqueArea(); +} + +/*! + \class QGraphicsPathItem + \brief The QGraphicsPathItem class provides a path item that you + can add to a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's path, pass a QPainterPath to QGraphicsPathItem's + constructor, or call the setPath() function. The path() function + returns the current path. + + \image graphicsview-pathitem.png + + QGraphicsPathItem uses the path to provide a reasonable + implementation of boundingRect(), shape(), and contains(). The + paint() function draws the path using the item's associated pen + and brush, which you can set by calling the setPen() and + setBrush() functions. + + \sa QGraphicsRectItem, QGraphicsEllipseItem, QGraphicsPolygonItem, + QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics + View Framework} +*/ + +class QGraphicsPathItemPrivate : public QAbstractGraphicsShapeItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsPathItem) +public: + QPainterPath path; +}; + +/*! + Constructs a QGraphicsPath item using \a path as the default path. \a + parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPathItem::QGraphicsPathItem(const QPainterPath &path, + QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene) +{ + if (!path.isEmpty()) + setPath(path); +} + +/*! + Constructs a QGraphicsPath. \a parent is passed to + QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPathItem::QGraphicsPathItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsPathItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsPathItem. +*/ +QGraphicsPathItem::~QGraphicsPathItem() +{ +} + +/*! + Returns the item's path as a QPainterPath. If no item has been set, an + empty QPainterPath is returned. + + \sa setPath() +*/ +QPainterPath QGraphicsPathItem::path() const +{ + Q_D(const QGraphicsPathItem); + return d->path; +} + +/*! + Sets the item's path to be the given \a path. + + \sa path() +*/ +void QGraphicsPathItem::setPath(const QPainterPath &path) +{ + Q_D(QGraphicsPathItem); + if (d->path == path) + return; + prepareGeometryChange(); + d->path = path; + d->boundingRect = QRectF(); + update(); +} + +/*! + \reimp +*/ +QRectF QGraphicsPathItem::boundingRect() const +{ + Q_D(const QGraphicsPathItem); + if (d->boundingRect.isNull()) { + qreal pw = pen().widthF(); + if (pw == 0.0) + d->boundingRect = d->path.controlPointRect(); + else { + d->boundingRect = shape().controlPointRect(); + } + } + return d->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPathItem::shape() const +{ + Q_D(const QGraphicsPathItem); + return qt_graphicsItem_shapeFromPath(d->path, d->pen); +} + +/*! + \reimp +*/ +bool QGraphicsPathItem::contains(const QPointF &point) const +{ + return QAbstractGraphicsShapeItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsPathItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_D(QGraphicsPathItem); + Q_UNUSED(widget); + painter->setPen(d->pen); + painter->setBrush(d->brush); + painter->drawPath(d->path); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsPathItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QAbstractGraphicsShapeItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPathItem::opaqueArea() const +{ + return QAbstractGraphicsShapeItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsPathItem::type() const +{ + return Type; +} + +/*! + \internal +*/ +bool QGraphicsPathItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsPathItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsPathItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsRectItem + \brief The QGraphicsRectItem class provides a rectangle item that you + can add to a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's rectangle, pass a QRectF to QGraphicsRectItem's + constructor, or call the setRect() function. The rect() function + returns the current rectangle. + + \image graphicsview-rectitem.png + + QGraphicsRectItem uses the rectangle and the pen width to provide + a reasonable implementation of boundingRect(), shape(), and + contains(). The paint() function draws the rectangle using the + item's associated pen and brush, which you can set by calling the + setPen() and setBrush() functions. + + \note The rendering of invalid rectangles, such as those with negative + widths or heights, is undefined. If you cannot be sure that you are + using valid rectangles (for example, if you are creating + rectangles using data from an unreliable source) then you should + use QRectF::normalized() to create normalized rectangles, and use + those instead. + + \sa QGraphicsPathItem, QGraphicsEllipseItem, QGraphicsPolygonItem, + QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics + View Framework} +*/ + +class QGraphicsRectItemPrivate : public QAbstractGraphicsShapeItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsRectItem) +public: + QRectF rect; +}; + +/*! + Constructs a QGraphicsRectItem, using \a rect as the default rectangle. + \a parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsRectItem::QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene) +{ + setRect(rect); +} + +/*! + \fn QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal width, qreal height, + QGraphicsItem *parent) + + Constructs a QGraphicsRectItem with a default rectangle defined + by (\a x, \a y) and the given \a width and \a height. + + \a parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsRectItem::QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, + QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene) +{ + setRect(QRectF(x, y, w, h)); +} + +/*! + Constructs a QGraphicsRectItem. \a parent is passed to + QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsRectItem::QGraphicsRectItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsRectItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsRectItem. +*/ +QGraphicsRectItem::~QGraphicsRectItem() +{ +} + +/*! + Returns the item's rectangle. + + \sa setRect() +*/ +QRectF QGraphicsRectItem::rect() const +{ + Q_D(const QGraphicsRectItem); + return d->rect; +} + +/*! + \fn void QGraphicsRectItem::setRect(const QRectF &rectangle) + + Sets the item's rectangle to be the given \a rectangle. + + \sa rect() +*/ +void QGraphicsRectItem::setRect(const QRectF &rect) +{ + Q_D(QGraphicsRectItem); + if (d->rect == rect) + return; + prepareGeometryChange(); + d->rect = rect; + d->boundingRect = QRectF(); + update(); +} + +/*! + \fn void QGraphicsRectItem::setRect(qreal x, qreal y, qreal width, qreal height) + \fn void QGraphicsEllipseItem::setRect(qreal x, qreal y, qreal width, qreal height) + + Sets the item's rectangle to the rectangle defined by (\a x, \a y) + and the given \a width and \a height. + + This convenience function is equivalent to calling \c + {setRect(QRectF(x, y, width, height))} + + \sa rect() +*/ + +/*! + \reimp +*/ +QRectF QGraphicsRectItem::boundingRect() const +{ + Q_D(const QGraphicsRectItem); + if (d->boundingRect.isNull()) { + qreal halfpw = pen().widthF() / 2; + d->boundingRect = d->rect; + if (halfpw > 0.0) + d->boundingRect.adjust(-halfpw, -halfpw, halfpw, halfpw); + } + return d->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsRectItem::shape() const +{ + Q_D(const QGraphicsRectItem); + QPainterPath path; + path.addRect(d->rect); + return qt_graphicsItem_shapeFromPath(path, d->pen); +} + +/*! + \reimp +*/ +bool QGraphicsRectItem::contains(const QPointF &point) const +{ + return QAbstractGraphicsShapeItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_D(QGraphicsRectItem); + Q_UNUSED(widget); + painter->setPen(d->pen); + painter->setBrush(d->brush); + painter->drawRect(d->rect); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsRectItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QAbstractGraphicsShapeItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsRectItem::opaqueArea() const +{ + return QAbstractGraphicsShapeItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsRectItem::type() const +{ + return Type; +} + +/*! + \internal +*/ +bool QGraphicsRectItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsRectItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsRectItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsEllipseItem + \brief The QGraphicsEllipseItem class provides an ellipse item that you + can add to a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsEllipseItem respresents an ellipse with a fill and an outline, + and you can also use it for ellipse segments (see startAngle(), + spanAngle()). + + \table + \row + \o \inlineimage graphicsview-ellipseitem.png + \o \inlineimage graphicsview-ellipseitem-pie.png + \endtable + + To set the item's ellipse, pass a QRectF to QGraphicsEllipseItem's + constructor, or call setRect(). The rect() function returns the + current ellipse geometry. + + QGraphicsEllipseItem uses the rect and the pen width to provide a + reasonable implementation of boundingRect(), shape(), and contains(). The + paint() function draws the ellipse using the item's associated pen and + brush, which you can set by calling setPen() and setBrush(). + + \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsPolygonItem, + QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics + View Framework} +*/ + +class QGraphicsEllipseItemPrivate : public QAbstractGraphicsShapeItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsEllipseItem) +public: + inline QGraphicsEllipseItemPrivate() + : startAngle(0), spanAngle(360 * 16) + { } + + QRectF rect; + int startAngle; + int spanAngle; +}; + +/*! + Constructs a QGraphicsEllipseItem using \a rect as the default rectangle. + \a parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsEllipseItem::QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene) +{ + setRect(rect); +} + +/*! + \fn QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal width, qreal height, QGraphicsItem *parent) + \since 4.3 + + Constructs a QGraphicsEllipseItem using the rectangle defined by (\a x, \a + y) and the given \a width and \a height, as the default rectangle. \a + parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsEllipseItem::QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, + QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene) +{ + setRect(x,y,w,h); +} + + + +/*! + Constructs a QGraphicsEllipseItem. \a parent is passed to + QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsEllipseItem::QGraphicsEllipseItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsEllipseItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsEllipseItem. +*/ +QGraphicsEllipseItem::~QGraphicsEllipseItem() +{ +} + +/*! + Returns the item's ellipse geometry as a QRectF. + + \sa setRect(), QPainter::drawEllipse() +*/ +QRectF QGraphicsEllipseItem::rect() const +{ + Q_D(const QGraphicsEllipseItem); + return d->rect; +} + +/*! + Sets the item's ellipse geometry to \a rect. The rectangle's left edge + defines the left edge of the ellipse, and the rectangle's top edge + describes the top of the ellipse. The height and width of the rectangle + describe the height and width of the ellipse. + + \sa rect(), QPainter::drawEllipse() +*/ +void QGraphicsEllipseItem::setRect(const QRectF &rect) +{ + Q_D(QGraphicsEllipseItem); + if (d->rect == rect) + return; + prepareGeometryChange(); + d->rect = rect; + d->boundingRect = QRectF(); + update(); +} + +/*! + Returns the start angle for an ellipse segment in 16ths of a degree. This + angle is used together with spanAngle() for representing an ellipse + segment (a pie). By default, the start angle is 0. + + \sa setStartAngle(), spanAngle() +*/ +int QGraphicsEllipseItem::startAngle() const +{ + Q_D(const QGraphicsEllipseItem); + return d->startAngle; +} + +/*! + Sets the start angle for an ellipse segment to \a angle, which is in 16ths + of a degree. This angle is used together with spanAngle() for representing + an ellipse segment (a pie). By default, the start angle is 0. + + \sa startAngle(), setSpanAngle(), QPainter::drawPie() +*/ +void QGraphicsEllipseItem::setStartAngle(int angle) +{ + Q_D(QGraphicsEllipseItem); + if (angle != d->startAngle) { + prepareGeometryChange(); + d->boundingRect = QRectF(); + d->startAngle = angle; + update(); + } +} + +/*! + Returns the span angle of an ellipse segment in 16ths of a degree. This + angle is used together with startAngle() for representing an ellipse + segment (a pie). By default, this function returns 5760 (360 * 16, a full + ellipse). + + \sa setSpanAngle(), startAngle() +*/ +int QGraphicsEllipseItem::spanAngle() const +{ + Q_D(const QGraphicsEllipseItem); + return d->spanAngle; +} + +/*! + Sets the span angle for an ellipse segment to \a angle, which is in 16ths + of a degree. This angle is used together with startAngle() to represent an + ellipse segment (a pie). By default, the span angle is 5760 (360 * 16, a + full ellipse). + + \sa spanAngle(), setStartAngle(), QPainter::drawPie() +*/ +void QGraphicsEllipseItem::setSpanAngle(int angle) +{ + Q_D(QGraphicsEllipseItem); + if (angle != d->spanAngle) { + prepareGeometryChange(); + d->boundingRect = QRectF(); + d->spanAngle = angle; + update(); + } +} + +/*! + \reimp +*/ +QRectF QGraphicsEllipseItem::boundingRect() const +{ + Q_D(const QGraphicsEllipseItem); + if (d->boundingRect.isNull()) { + qreal pw = pen().widthF(); + if (pw == 0.0 && d->spanAngle == 360 * 16) + d->boundingRect = d->rect; + else + d->boundingRect = shape().controlPointRect(); + } + return d->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsEllipseItem::shape() const +{ + Q_D(const QGraphicsEllipseItem); + QPainterPath path; + if (d->rect.isNull()) + return path; + if (d->spanAngle != 360 * 16) { + path.moveTo(d->rect.center()); + path.arcTo(d->rect, d->startAngle / 16.0, d->spanAngle / 16.0); + } else { + path.addEllipse(d->rect); + } + + return qt_graphicsItem_shapeFromPath(path, d->pen); +} + +/*! + \reimp +*/ +bool QGraphicsEllipseItem::contains(const QPointF &point) const +{ + return QAbstractGraphicsShapeItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsEllipseItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_D(QGraphicsEllipseItem); + Q_UNUSED(widget); + painter->setPen(d->pen); + painter->setBrush(d->brush); + if ((d->spanAngle != 0) && (qAbs(d->spanAngle) % (360 * 16) == 0)) + painter->drawEllipse(d->rect); + else + painter->drawPie(d->rect, d->startAngle, d->spanAngle); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsEllipseItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QAbstractGraphicsShapeItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsEllipseItem::opaqueArea() const +{ + return QAbstractGraphicsShapeItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsEllipseItem::type() const +{ + return Type; +} + + +/*! + \internal +*/ +bool QGraphicsEllipseItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsEllipseItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsEllipseItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsPolygonItem + \brief The QGraphicsPolygonItem class provides a polygon item that you + can add to a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's polygon, pass a QPolygonF to + QGraphicsPolygonItem's constructor, or call the setPolygon() + function. The polygon() function returns the current polygon. + + \image graphicsview-polygonitem.png + + QGraphicsPolygonItem uses the polygon and the pen width to provide + a reasonable implementation of boundingRect(), shape(), and + contains(). The paint() function draws the polygon using the + item's associated pen and brush, which you can set by calling the + setPen() and setBrush() functions. + + \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem, + QGraphicsTextItem, QGraphicsLineItem, QGraphicsPixmapItem, {The Graphics + View Framework} +*/ + +class QGraphicsPolygonItemPrivate : public QAbstractGraphicsShapeItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsPolygonItem) +public: + inline QGraphicsPolygonItemPrivate() + : fillRule(Qt::OddEvenFill) + { } + + QPolygonF polygon; + Qt::FillRule fillRule; +}; + +/*! + Constructs a QGraphicsPolygonItem with \a polygon as the default + polygon. \a parent is passed to QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPolygonItem::QGraphicsPolygonItem(const QPolygonF &polygon, + QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene) +{ + setPolygon(polygon); +} + +/*! + Constructs a QGraphicsPolygonItem. \a parent is passed to + QAbstractGraphicsShapeItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPolygonItem::QGraphicsPolygonItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsPolygonItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsPolygonItem. +*/ +QGraphicsPolygonItem::~QGraphicsPolygonItem() +{ +} + +/*! + Returns the item's polygon, or an empty polygon if no polygon + has been set. + + \sa setPolygon() +*/ +QPolygonF QGraphicsPolygonItem::polygon() const +{ + Q_D(const QGraphicsPolygonItem); + return d->polygon; +} + +/*! + Sets the item's polygon to be the given \a polygon. + + \sa polygon() +*/ +void QGraphicsPolygonItem::setPolygon(const QPolygonF &polygon) +{ + Q_D(QGraphicsPolygonItem); + if (d->polygon == polygon) + return; + prepareGeometryChange(); + d->polygon = polygon; + d->boundingRect = QRectF(); + update(); +} + +/*! + Returns the fill rule of the polygon. The default fill rule is + Qt::OddEvenFill. + + \sa setFillRule(), QPainterPath::fillRule(), QPainter::drawPolygon() +*/ +Qt::FillRule QGraphicsPolygonItem::fillRule() const +{ + Q_D(const QGraphicsPolygonItem); + return d->fillRule; +} + +/*! + Sets the fill rule of the polygon to \a rule. The default fill rule is + Qt::OddEvenFill. + + \sa fillRule(), QPainterPath::fillRule(), QPainter::drawPolygon() +*/ +void QGraphicsPolygonItem::setFillRule(Qt::FillRule rule) +{ + Q_D(QGraphicsPolygonItem); + if (rule != d->fillRule) { + d->fillRule = rule; + update(); + } +} + +/*! + \reimp +*/ +QRectF QGraphicsPolygonItem::boundingRect() const +{ + Q_D(const QGraphicsPolygonItem); + if (d->boundingRect.isNull()) { + qreal pw = pen().widthF(); + if (pw == 0.0) + d->boundingRect = d->polygon.boundingRect(); + else + d->boundingRect = shape().controlPointRect(); + } + return d->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPolygonItem::shape() const +{ + Q_D(const QGraphicsPolygonItem); + QPainterPath path; + path.addPolygon(d->polygon); + return qt_graphicsItem_shapeFromPath(path, d->pen); +} + +/*! + \reimp +*/ +bool QGraphicsPolygonItem::contains(const QPointF &point) const +{ + return QAbstractGraphicsShapeItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsPolygonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_D(QGraphicsPolygonItem); + Q_UNUSED(widget); + painter->setPen(d->pen); + painter->setBrush(d->brush); + painter->drawPolygon(d->polygon, d->fillRule); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsPolygonItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QAbstractGraphicsShapeItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPolygonItem::opaqueArea() const +{ + return QAbstractGraphicsShapeItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsPolygonItem::type() const +{ + return Type; +} + +/*! + \internal +*/ +bool QGraphicsPolygonItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsPolygonItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsPolygonItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsLineItem + \brief The QGraphicsLineItem class provides a line item that you can add to a + QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's line, pass a QLineF to QGraphicsLineItem's + constructor, or call the setLine() function. The line() function + returns the current line. By default the line is black with a + width of 0, but you can change this by calling setPen(). + + \img graphicsview-lineitem.png + + QGraphicsLineItem uses the line and the pen width to provide a reasonable + implementation of boundingRect(), shape(), and contains(). The paint() + function draws the line using the item's associated pen. + + \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem, + QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsPixmapItem, {The + Graphics View Framework} +*/ + +class QGraphicsLineItemPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsLineItem) +public: + QLineF line; + QPen pen; +}; + +/*! + Constructs a QGraphicsLineItem, using \a line as the default line. \a + parent is passed to QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsLineItem::QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene) +{ + setLine(line); +} + +/*! + Constructs a QGraphicsLineItem, using the line between (\a x1, \a y1) and + (\a x2, \a y2) as the default line. \a parent is passed to + QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsLineItem::QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene) +{ + setLine(x1, y1, x2, y2); +} + + + +/*! + Constructs a QGraphicsLineItem. \a parent is passed to QGraphicsItem's + constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsLineItem::QGraphicsLineItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsLineItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsLineItem. +*/ +QGraphicsLineItem::~QGraphicsLineItem() +{ +} + +/*! + Returns the item's pen, or a black solid 0-width pen if no pen has + been set. + + \sa setPen() +*/ +QPen QGraphicsLineItem::pen() const +{ + Q_D(const QGraphicsLineItem); + return d->pen; +} + +/*! + Sets the item's pen to \a pen. If no pen is set, the line will be painted + using a black solid 0-width pen. + + \sa pen() +*/ +void QGraphicsLineItem::setPen(const QPen &pen) +{ + Q_D(QGraphicsLineItem); + prepareGeometryChange(); + d->pen = pen; + update(); +} + +/*! + Returns the item's line, or a null line if no line has been set. + + \sa setLine() +*/ +QLineF QGraphicsLineItem::line() const +{ + Q_D(const QGraphicsLineItem); + return d->line; +} + +/*! + Sets the item's line to be the given \a line. + + \sa line() +*/ +void QGraphicsLineItem::setLine(const QLineF &line) +{ + Q_D(QGraphicsLineItem); + if (d->line == line) + return; + prepareGeometryChange(); + d->line = line; + update(); +} + +/*! + \fn void QGraphicsLineItem::setLine(qreal x1, qreal y1, qreal x2, qreal y2) + \overload + + Sets the item's line to be the line between (\a x1, \a y1) and (\a + x2, \a y2). + + This is the same as calling \c {setLine(QLineF(x1, y1, x2, y2))}. +*/ + +/*! + \reimp +*/ +QRectF QGraphicsLineItem::boundingRect() const +{ + Q_D(const QGraphicsLineItem); + if (d->pen.widthF() == 0.0) { + const qreal x1 = d->line.p1().x(); + const qreal x2 = d->line.p2().x(); + const qreal y1 = d->line.p1().y(); + const qreal y2 = d->line.p2().y(); + qreal lx = qMin(x1, x2); + qreal rx = qMax(x1, x2); + qreal ty = qMin(y1, y2); + qreal by = qMax(y1, y2); + return QRectF(lx, ty, rx - lx, by - ty); + } + return shape().controlPointRect(); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsLineItem::shape() const +{ + Q_D(const QGraphicsLineItem); + QPainterPath path; + if (d->line == QLineF()) + return path; + + path.moveTo(d->line.p1()); + path.lineTo(d->line.p2()); + return qt_graphicsItem_shapeFromPath(path, d->pen); +} + +/*! + \reimp +*/ +bool QGraphicsLineItem::contains(const QPointF &point) const +{ + return QGraphicsItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsLineItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_D(QGraphicsLineItem); + Q_UNUSED(widget); + painter->setPen(d->pen); + painter->drawLine(d->line); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsLineItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QGraphicsItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsLineItem::opaqueArea() const +{ + return QGraphicsItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsLineItem::type() const +{ + return Type; +} + +/*! + \internal +*/ +bool QGraphicsLineItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsLineItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsLineItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsPixmapItem + \brief The QGraphicsPixmapItem class provides a pixmap item that you can add to + a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's pixmap, pass a QPixmap to QGraphicsPixmapItem's + constructor, or call the setPixmap() function. The pixmap() + function returns the current pixmap. + + QGraphicsPixmapItem uses pixmap's optional alpha mask to provide a + reasonable implementation of boundingRect(), shape(), and contains(). + + \image graphicsview-pixmapitem.png + + The pixmap is drawn at the item's (0, 0) coordinate, as returned by + offset(). You can change the drawing offset by calling setOffset(). + + You can set the pixmap's transformation mode by calling + setTransformationMode(). By default, Qt::FastTransformation is used, which + provides fast, non-smooth scaling. Qt::SmoothTransformation enables + QPainter::SmoothPixmapTransform on the painter, and the quality depends on + the platform and viewport. The result is usually not as good as calling + QPixmap::scale() directly. Call transformationMode() to get the current + transformation mode for the item. + + \sa QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem, + QGraphicsTextItem, QGraphicsPolygonItem, QGraphicsLineItem, {The + Graphics View Framework} +*/ + +/*! + \enum QGraphicsPixmapItem::ShapeMode + + This enum describes how QGraphicsPixmapItem calculates its shape and + opaque area. + + The default value is MaskShape. + + \value MaskShape The shape is determined by calling QPixmap::mask(). + This shape includes only the opaque pixels of the pixmap. + Because the shape is more complex, however, it can be slower than the other modes, + and uses more memory. + + \value BoundingRectShape The shape is determined by tracing the outline of + the pixmap. This is the fastest shape mode, but it does not take into account + any transparent areas on the pixmap. + + \value HeuristicMaskShape The shape is determine by calling + QPixmap::createHeuristicMask(). The performance and memory consumption + is similar to MaskShape. +*/ +extern QPainterPath qt_regionToPath(const QRegion ®ion); + +class QGraphicsPixmapItemPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsPixmapItem) +public: + QGraphicsPixmapItemPrivate() + : transformationMode(Qt::FastTransformation), + shapeMode(QGraphicsPixmapItem::MaskShape), + hasShape(false) + {} + + QPixmap pixmap; + Qt::TransformationMode transformationMode; + QPointF offset; + QGraphicsPixmapItem::ShapeMode shapeMode; + QPainterPath shape; + bool hasShape; + + void updateShape() + { + shape = QPainterPath(); + switch (shapeMode) { + case QGraphicsPixmapItem::MaskShape: { + QBitmap mask = pixmap.mask(); + if (!mask.isNull()) { + shape = qt_regionToPath(QRegion(mask).translated(offset.toPoint())); + break; + } + // FALL THROUGH + } + case QGraphicsPixmapItem::BoundingRectShape: + shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height())); + break; + case QGraphicsPixmapItem::HeuristicMaskShape: +#ifndef QT_NO_IMAGE_HEURISTIC_MASK + shape = qt_regionToPath(QRegion(pixmap.createHeuristicMask()).translated(offset.toPoint())); +#else + shape.addRect(QRectF(offset.x(), offset.y(), pixmap.width(), pixmap.height())); +#endif + break; + } + } +}; + +/*! + Constructs a QGraphicsPixmapItem, using \a pixmap as the default pixmap. + \a parent is passed to QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPixmapItem::QGraphicsPixmapItem(const QPixmap &pixmap, + QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene) +{ + setPixmap(pixmap); +} + +/*! + Constructs a QGraphicsPixmapItem. \a parent is passed to QGraphicsItem's + constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsPixmapItem::QGraphicsPixmapItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsPixmapItemPrivate, parent, scene) +{ +} + +/*! + Destroys the QGraphicsPixmapItem. +*/ +QGraphicsPixmapItem::~QGraphicsPixmapItem() +{ +} + +/*! + Sets the item's pixmap to \a pixmap. + + \sa pixmap() +*/ +void QGraphicsPixmapItem::setPixmap(const QPixmap &pixmap) +{ + Q_D(QGraphicsPixmapItem); + prepareGeometryChange(); + d->pixmap = pixmap; + d->hasShape = false; + update(); +} + +/*! + Returns the item's pixmap, or an invalid QPixmap if no pixmap has been + set. + + \sa setPixmap() +*/ +QPixmap QGraphicsPixmapItem::pixmap() const +{ + Q_D(const QGraphicsPixmapItem); + return d->pixmap; +} + +/*! + Returns the transformation mode of the pixmap. The default mode is + Qt::FastTransformation, which provides quick transformation with no + smoothing. + + \sa setTransformationMode() +*/ +Qt::TransformationMode QGraphicsPixmapItem::transformationMode() const +{ + Q_D(const QGraphicsPixmapItem); + return d->transformationMode; +} + +/*! + Sets the pixmap item's transformation mode to \a mode, and toggles an + update of the item. The default mode is Qt::FastTransformation, which + provides quick transformation with no smoothing. + + Qt::SmoothTransformation enables QPainter::SmoothPixmapTransform on the + painter, and the quality depends on the platform and viewport. The result + is usually not as good as calling QPixmap::scale() directly. + + \sa transformationMode() +*/ +void QGraphicsPixmapItem::setTransformationMode(Qt::TransformationMode mode) +{ + Q_D(QGraphicsPixmapItem); + if (mode != d->transformationMode) { + d_ptr->updateHelper(); + d->transformationMode = mode; + update(); + } +} + +/*! + Returns the pixmap item's \e offset, which defines the point of the + top-left corner of the pixmap, in local coordinates. + + \sa setOffset() +*/ +QPointF QGraphicsPixmapItem::offset() const +{ + Q_D(const QGraphicsPixmapItem); + return d->offset; +} + +/*! + Sets the pixmap item's offset to \a offset. QGraphicsPixmapItem will draw + its pixmap using \a offset for its top-left corner. + + \sa offset() +*/ +void QGraphicsPixmapItem::setOffset(const QPointF &offset) +{ + Q_D(QGraphicsPixmapItem); + if (d->offset == offset) + return; + prepareGeometryChange(); + d->offset = offset; + d->hasShape = false; + update(); +} + +/*! + \fn void QGraphicsPixmapItem::setOffset(qreal x, qreal y) + \since 4.3 + + This convenience function is equivalent to calling setOffset(QPointF(\a x, \a y)). +*/ + +/*! + \reimp +*/ +QRectF QGraphicsPixmapItem::boundingRect() const +{ + Q_D(const QGraphicsPixmapItem); + qreal pw = 1.0; + if (d->pixmap.isNull()) + return QRectF(); + return QRectF(d->offset, d->pixmap.size()).adjusted(-pw/2, -pw/2, pw/2, pw/2); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPixmapItem::shape() const +{ + Q_D(const QGraphicsPixmapItem); + if (!d->hasShape) { + QGraphicsPixmapItemPrivate *thatD = const_cast<QGraphicsPixmapItemPrivate *>(d); + thatD->updateShape(); + thatD->hasShape = true; + } + return d_func()->shape; +} + +/*! + \reimp +*/ +bool QGraphicsPixmapItem::contains(const QPointF &point) const +{ + return QGraphicsItem::contains(point); +} + +/*! + \reimp +*/ +void QGraphicsPixmapItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_D(QGraphicsPixmapItem); + Q_UNUSED(widget); + + painter->setRenderHint(QPainter::SmoothPixmapTransform, + (d->transformationMode == Qt::SmoothTransformation)); + + QRectF exposed = option->exposedRect.adjusted(-1, -1, 1, 1); + exposed &= QRectF(d->offset.x(), d->offset.y(), d->pixmap.width(), d->pixmap.height()); + painter->drawPixmap(exposed, d->pixmap, exposed.translated(-d->offset)); + + if (option->state & QStyle::State_Selected) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsPixmapItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QGraphicsItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsPixmapItem::opaqueArea() const +{ + return shape(); +} + +/*! + \reimp +*/ +int QGraphicsPixmapItem::type() const +{ + return Type; +} + +/*! + Returns the item's shape mode. The shape mode describes how + QGraphicsPixmapItem calculates its shape. The default mode is MaskShape. + + \sa setShapeMode(), ShapeMode +*/ +QGraphicsPixmapItem::ShapeMode QGraphicsPixmapItem::shapeMode() const +{ + return d_func()->shapeMode; +} + +/*! + Sets the item's shape mode to \a mode. The shape mode describes how + QGraphicsPixmapItem calculates its shape. The default mode is MaskShape. + + \sa shapeMode(), ShapeMode +*/ +void QGraphicsPixmapItem::setShapeMode(ShapeMode mode) +{ + Q_D(QGraphicsPixmapItem); + if (d->shapeMode == mode) + return; + d->shapeMode = mode; + d->hasShape = false; +} + +/*! + \internal +*/ +bool QGraphicsPixmapItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsPixmapItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsPixmapItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsTextItem + \brief The QGraphicsTextItem class provides a text item that you can add to + a QGraphicsScene to display formatted text. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + If you only need to show plain text in an item, consider using QGraphicsSimpleTextItem + instead. + + To set the item's text, pass a QString to QGraphicsTextItem's + constructor, or call setHtml()/setPlainText(). + + QGraphicsTextItem uses the text's formatted size and the associated font + to provide a reasonable implementation of boundingRect(), shape(), + and contains(). You can set the font by calling setFont(). + + It is possible to make the item editable by setting the Qt::TextEditorInteraction flag + using setTextInteractionFlags(). + + The item's preferred text width can be set using setTextWidth() and obtained + using textWidth(). + + \note In order to align HTML text in the center, the item's text width must be set. + + \img graphicsview-textitem.png + + \note QGraphicsTextItem accepts \l{QGraphicsItem::acceptHoverEvents()}{hover events} + by default. You can change this with \l{QGraphicsItem::}{setAcceptHoverEvents()}. + + \sa QGraphicsSimpleTextItem, QGraphicsPathItem, QGraphicsRectItem, + QGraphicsEllipseItem, QGraphicsPixmapItem, QGraphicsPolygonItem, + QGraphicsLineItem, {The Graphics View Framework} +*/ + +class QGraphicsTextItemPrivate +{ +public: + QGraphicsTextItemPrivate() + : control(0), pageNumber(0), useDefaultImpl(false), tabChangesFocus(false) + { } + + mutable QTextControl *control; + QTextControl *textControl() const; + + inline QPointF controlOffset() const + { return QPointF(0., pageNumber * control->document()->pageSize().height()); } + inline void sendControlEvent(QEvent *e) + { if (control) control->processEvent(e, controlOffset()); } + + void _q_updateBoundingRect(const QSizeF &); + void _q_update(QRectF); + void _q_ensureVisible(QRectF); + bool _q_mouseOnEdge(QGraphicsSceneMouseEvent *); + + QRectF boundingRect; + int pageNumber; + bool useDefaultImpl; + bool tabChangesFocus; + + QGraphicsTextItem *qq; +}; + +/*! + Constructs a QGraphicsTextItem, using \a text as the default plain + text. \a parent is passed to QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsTextItem::QGraphicsTextItem(const QString &text, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate) +{ + dd->qq = this; + if (!text.isEmpty()) + setPlainText(text); + setAcceptDrops(true); + setAcceptHoverEvents(true); +} + +/*! + Constructs a QGraphicsTextItem. \a parent is passed to QGraphicsItem's + constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsTextItem::QGraphicsTextItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(parent, scene), dd(new QGraphicsTextItemPrivate) +{ + dd->qq = this; + setAcceptDrops(true); + setAcceptHoverEvents(true); +} + +/*! + Destroys the QGraphicsTextItem. +*/ +QGraphicsTextItem::~QGraphicsTextItem() +{ + delete dd; +} + +/*! + Returns the item's text converted to HTML, or an empty QString if no text has been set. + + \sa setHtml() +*/ +QString QGraphicsTextItem::toHtml() const +{ +#ifndef QT_NO_TEXTHTMLPARSER + if (dd->control) + return dd->control->toHtml(); +#endif + return QString(); +} + +/*! + Sets the item's text to \a text, assuming that text is HTML formatted. If + the item has keyboard input focus, this function will also call + ensureVisible() to ensure that the text is visible in all viewports. + + \sa toHtml(), hasFocus(), QGraphicsSimpleTextItem +*/ +void QGraphicsTextItem::setHtml(const QString &text) +{ + dd->textControl()->setHtml(text); +} + +/*! + Returns the item's text converted to plain text, or an empty QString if no text has been set. + + \sa setPlainText() +*/ +QString QGraphicsTextItem::toPlainText() const +{ + if (dd->control) + return dd->control->toPlainText(); + return QString(); +} + +/*! + Sets the item's text to \a text. If the item has keyboard input focus, + this function will also call ensureVisible() to ensure that the text is + visible in all viewports. + + \sa toHtml(), hasFocus() +*/ +void QGraphicsTextItem::setPlainText(const QString &text) +{ + dd->textControl()->setPlainText(text); +} + +/*! + Returns the item's font, which is used to render the text. + + \sa setFont() +*/ +QFont QGraphicsTextItem::font() const +{ + if (!dd->control) + return QFont(); + return dd->control->document()->defaultFont(); +} + +/*! + Sets the font used to render the text item to \a font. + + \sa font() +*/ +void QGraphicsTextItem::setFont(const QFont &font) +{ + dd->textControl()->document()->setDefaultFont(font); +} + +/*! + Sets the color for unformatted text to \a col. +*/ +void QGraphicsTextItem::setDefaultTextColor(const QColor &col) +{ + QTextControl *c = dd->textControl(); + QPalette pal = c->palette(); + pal.setColor(QPalette::Text, col); + c->setPalette(pal); +} + +/*! + Returns the default text color that is used to for unformatted text. +*/ +QColor QGraphicsTextItem::defaultTextColor() const +{ + return dd->textControl()->palette().color(QPalette::Text); +} + +/*! + \reimp +*/ +QRectF QGraphicsTextItem::boundingRect() const +{ + return dd->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsTextItem::shape() const +{ + if (!dd->control) + return QPainterPath(); + QPainterPath path; + path.addRect(dd->boundingRect); + return path; +} + +/*! + \reimp +*/ +bool QGraphicsTextItem::contains(const QPointF &point) const +{ + return dd->boundingRect.contains(point); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(widget); + if (dd->control) { + painter->save(); + QRectF r = option->exposedRect; + painter->translate(-dd->controlOffset()); + r.translate(dd->controlOffset()); + + QTextDocument *doc = dd->control->document(); + QTextDocumentLayout *layout = qobject_cast<QTextDocumentLayout *>(doc->documentLayout()); + + // the layout might need to expand the root frame to + // the viewport if NoWrap is set + if (layout) + layout->setViewport(dd->boundingRect); + + dd->control->drawContents(painter, r); + + if (layout) + layout->setViewport(QRect()); + + painter->restore(); + } + + if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus)) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsTextItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QGraphicsItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsTextItem::opaqueArea() const +{ + return QGraphicsItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsTextItem::type() const +{ + return Type; +} + +/*! + Sets the preferred width for the item's text. If the actual text + is wider than the specified width then it will be broken into + multiple lines. + + If \a width is set to -1 then the text will not be broken into + multiple lines unless it is enforced through an explicit line + break or a new paragraph. + + The default value is -1. + + Note that QGraphicsTextItem keeps a QTextDocument internally, + which is used to calculate the text width. + + \sa textWidth(), QTextDocument::setTextWidth() +*/ +void QGraphicsTextItem::setTextWidth(qreal width) +{ + dd->textControl()->setTextWidth(width); +} + +/*! + Returns the text width. + + The width is calculated with the QTextDocument that + QGraphicsTextItem keeps internally. + + \sa setTextWidth(), QTextDocument::textWidth() +*/ +qreal QGraphicsTextItem::textWidth() const +{ + if (!dd->control) + return -1; + return dd->control->textWidth(); +} + +/*! + Adjusts the text item to a reasonable size. +*/ +void QGraphicsTextItem::adjustSize() +{ + if (dd->control) + dd->control->adjustSize(); +} + +/*! + Sets the text document \a document on the item. +*/ +void QGraphicsTextItem::setDocument(QTextDocument *document) +{ + dd->textControl()->setDocument(document); + dd->_q_updateBoundingRect(dd->control->size()); +} + +/*! + Returns the item's text document. +*/ +QTextDocument *QGraphicsTextItem::document() const +{ + return dd->textControl()->document(); +} + +/*! + \reimp +*/ +bool QGraphicsTextItem::sceneEvent(QEvent *event) +{ + QEvent::Type t = event->type(); + if (!dd->tabChangesFocus && (t == QEvent::KeyPress || t == QEvent::KeyRelease)) { + int k = ((QKeyEvent *)event)->key(); + if (k == Qt::Key_Tab || k == Qt::Key_Backtab) { + dd->sendControlEvent(event); + return true; + } + } + return QGraphicsItem::sceneEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + if ((QGraphicsItem::d_ptr->flags & (ItemIsSelectable | ItemIsMovable)) + && (event->buttons() & Qt::LeftButton) && dd->_q_mouseOnEdge(event)) { + // User left-pressed on edge of selectable/movable item, use + // base impl. + dd->useDefaultImpl = true; + } else if (event->buttons() == event->button() + && dd->control->textInteractionFlags() == Qt::NoTextInteraction) { + // User pressed first button on non-interactive item. + dd->useDefaultImpl = true; + } + if (dd->useDefaultImpl) { + QGraphicsItem::mousePressEvent(event); + if (!event->isAccepted()) + dd->useDefaultImpl = false; + return; + } + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + if (dd->useDefaultImpl) { + QGraphicsItem::mouseMoveEvent(event); + return; + } + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + if (dd->useDefaultImpl) { + QGraphicsItem::mouseReleaseEvent(event); + if (dd->control->textInteractionFlags() == Qt::NoTextInteraction + && !event->buttons()) { + // User released last button on non-interactive item. + dd->useDefaultImpl = false; + } else if ((event->buttons() & Qt::LeftButton) == 0) { + // User released the left button on an interactive item. + dd->useDefaultImpl = false; + } + return; + } + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + if (dd->useDefaultImpl) { + QGraphicsItem::mouseDoubleClickEvent(event); + return; + } + + if (!hasFocus()) { + QGraphicsItem::mouseDoubleClickEvent(event); + return; + } + + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::keyPressEvent(QKeyEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::keyReleaseEvent(QKeyEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::focusInEvent(QFocusEvent *event) +{ + dd->sendControlEvent(event); + update(); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::focusOutEvent(QFocusEvent *event) +{ + dd->sendControlEvent(event); + update(); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::dragEnterEvent(QGraphicsSceneDragDropEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::dropEvent(QGraphicsSceneDragDropEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::inputMethodEvent(QInputMethodEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsTextItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + dd->sendControlEvent(event); +} + +/*! + \reimp +*/ +QVariant QGraphicsTextItem::inputMethodQuery(Qt::InputMethodQuery query) const +{ + QVariant v; + if (dd->control) + v = dd->control->inputMethodQuery(query); + if (v.type() == QVariant::RectF) + v = v.toRectF().translated(-dd->controlOffset()); + else if (v.type() == QVariant::PointF) + v = v.toPointF() - dd->controlOffset(); + else if (v.type() == QVariant::Rect) + v = v.toRect().translated(-dd->controlOffset().toPoint()); + else if (v.type() == QVariant::Point) + v = v.toPoint() - dd->controlOffset().toPoint(); + return v; +} + +/*! + \internal +*/ +bool QGraphicsTextItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsTextItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsTextItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \internal +*/ +void QGraphicsTextItemPrivate::_q_update(QRectF rect) +{ + if (rect.isValid()) { + rect.translate(-controlOffset()); + } else { + rect = boundingRect; + } + if (rect.intersects(boundingRect)) + qq->update(rect); +} + +/*! + \internal +*/ +void QGraphicsTextItemPrivate::_q_updateBoundingRect(const QSizeF &size) +{ + if (!control) return; // can't happen + const QSizeF pageSize = control->document()->pageSize(); + // paged items have a constant (page) size + if (size == boundingRect.size() || pageSize.height() != -1) + return; + qq->prepareGeometryChange(); + boundingRect.setSize(size); + qq->update(); +} + +/*! + \internal +*/ +void QGraphicsTextItemPrivate::_q_ensureVisible(QRectF rect) +{ + if (qq->hasFocus()) { + rect.translate(-controlOffset()); + qq->ensureVisible(rect, /*xmargin=*/0, /*ymargin=*/0); + } +} + +QTextControl *QGraphicsTextItemPrivate::textControl() const +{ + if (!control) { + QGraphicsTextItem *that = const_cast<QGraphicsTextItem *>(qq); + control = new QTextControl(that); + control->setTextInteractionFlags(Qt::NoTextInteraction); + + QObject::connect(control, SIGNAL(updateRequest(QRectF)), + qq, SLOT(_q_update(QRectF))); + QObject::connect(control, SIGNAL(documentSizeChanged(QSizeF)), + qq, SLOT(_q_updateBoundingRect(QSizeF))); + QObject::connect(control, SIGNAL(visibilityRequest(QRectF)), + qq, SLOT(_q_ensureVisible(QRectF))); + QObject::connect(control, SIGNAL(linkActivated(QString)), + qq, SIGNAL(linkActivated(QString))); + QObject::connect(control, SIGNAL(linkHovered(QString)), + qq, SIGNAL(linkHovered(QString))); + + const QSizeF pgSize = control->document()->pageSize(); + if (pgSize.height() != -1) { + qq->prepareGeometryChange(); + that->dd->boundingRect.setSize(pgSize); + qq->update(); + } else { + that->dd->_q_updateBoundingRect(control->size()); + } + } + return control; +} + +/*! + \internal +*/ +bool QGraphicsTextItemPrivate::_q_mouseOnEdge(QGraphicsSceneMouseEvent *event) +{ + QPainterPath path; + path.addRect(qq->boundingRect()); + + QPainterPath docPath; + const QTextFrameFormat format = control->document()->rootFrame()->frameFormat(); + docPath.addRect( + qq->boundingRect().adjusted( + format.leftMargin(), + format.topMargin(), + -format.rightMargin(), + -format.bottomMargin())); + + return path.subtracted(docPath).contains(event->pos()); +} + +/*! + \fn QGraphicsTextItem::linkActivated(const QString &link) + + This signal is emitted when the user clicks on a link on a text item + that enables Qt::LinksAccessibleByMouse or Qt::LinksAccessibleByKeyboard. + \a link is the link that was clicked. + + \sa setTextInteractionFlags() +*/ + +/*! + \fn QGraphicsTextItem::linkHovered(const QString &link) + + This signal is emitted when the user hovers over a link on a text item + that enables Qt::LinksAccessibleByMouse. \a link is + the link that was hovered over. + + \sa setTextInteractionFlags() +*/ + +/*! + Sets the flags \a flags to specify how the text item should react to user + input. + + The default for a QGraphicsTextItem is Qt::NoTextInteraction. Setting a + value different to Qt::NoTextInteraction will also set the ItemIsFocusable + QGraphicsItem flag. + + By default, the text is read-only. To transform the item into an editor, + set the Qt::TextEditable flag. +*/ +void QGraphicsTextItem::setTextInteractionFlags(Qt::TextInteractionFlags flags) +{ + if (flags == Qt::NoTextInteraction) + setFlags(this->flags() & ~QGraphicsItem::ItemIsFocusable); + else + setFlags(this->flags() | QGraphicsItem::ItemIsFocusable); + dd->textControl()->setTextInteractionFlags(flags); +} + +/*! + Returns the current text interaction flags. + + \sa setTextInteractionFlags() +*/ +Qt::TextInteractionFlags QGraphicsTextItem::textInteractionFlags() const +{ + if (!dd->control) + return Qt::NoTextInteraction; + return dd->control->textInteractionFlags(); +} + +/*! + \since 4.5 + + If \a b is true, the \gui Tab key will cause the widget to change focus; + otherwise, the tab key will insert a tab into the document. + + In some occasions text edits should not allow the user to input tabulators + or change indentation using the \gui Tab key, as this breaks the focus + chain. The default is false. + + \sa tabChangesFocus(), ItemIsFocusable, textInteractionFlags() +*/ +void QGraphicsTextItem::setTabChangesFocus(bool b) +{ + dd->tabChangesFocus = b; +} + +/*! + \since 4.5 + + Returns true if the \gui Tab key will cause the widget to change focus; + otherwise, false is returned. + + \sa setTabChangesFocus() +*/ +bool QGraphicsTextItem::tabChangesFocus() const +{ + return dd->tabChangesFocus; +} + +/*! + \property QGraphicsTextItem::openExternalLinks + + Specifies whether QGraphicsTextItem should automatically open links using + QDesktopServices::openUrl() instead of emitting the + linkActivated signal. + + The default value is false. +*/ +void QGraphicsTextItem::setOpenExternalLinks(bool open) +{ + dd->textControl()->setOpenExternalLinks(open); +} + +bool QGraphicsTextItem::openExternalLinks() const +{ + if (!dd->control) + return false; + return dd->control->openExternalLinks(); +} + +/*! + \property QGraphicsTextItem::textCursor + + This property represents the visible text cursor in an editable + text item. + + By default, if the item's text has not been set, this property + contains a null text cursor; otherwise it contains a text cursor + placed at the start of the item's document. +*/ +void QGraphicsTextItem::setTextCursor(const QTextCursor &cursor) +{ + dd->textControl()->setTextCursor(cursor); +} + +QTextCursor QGraphicsTextItem::textCursor() const +{ + if (!dd->control) + return QTextCursor(); + return dd->control->textCursor(); +} + +class QGraphicsSimpleTextItemPrivate : public QAbstractGraphicsShapeItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSimpleTextItem) +public: + inline QGraphicsSimpleTextItemPrivate() { + pen.setStyle(Qt::NoPen); + brush.setStyle(Qt::SolidPattern); + } + QString text; + QFont font; + QRectF boundingRect; + + void updateBoundingRect(); +}; + +static QRectF setupTextLayout(QTextLayout *layout) +{ + layout->setCacheEnabled(true); + layout->beginLayout(); + while (layout->createLine().isValid()) + ; + layout->endLayout(); + qreal maxWidth = 0; + qreal y = 0; + for (int i = 0; i < layout->lineCount(); ++i) { + QTextLine line = layout->lineAt(i); + maxWidth = qMax(maxWidth, line.naturalTextWidth()); + line.setPosition(QPointF(0, y)); + y += line.height(); + } + return QRectF(0, 0, maxWidth, y); +} + +void QGraphicsSimpleTextItemPrivate::updateBoundingRect() +{ + Q_Q(QGraphicsSimpleTextItem); + QRectF br; + if (text.isEmpty()) { + br = QRectF(); + } else { + QString tmp = text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + QStackTextEngine engine(tmp, font); + QTextLayout layout(&engine); + br = setupTextLayout(&layout); + } + if (br != boundingRect) { + q->prepareGeometryChange(); + boundingRect = br; + q->update(); + } +} + +/*! + \class QGraphicsSimpleTextItem + \brief The QGraphicsSimpleTextItem class provides a simple text path item + that you can add to a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + To set the item's text, you can either pass a QString to + QGraphicsSimpleTextItem's constructor, or call setText() to change the + text later. To set the text fill color, call setBrush(). + + The simple text item can have both a fill and an outline; setBrush() will + set the text fill (i.e., text color), and setPen() sets the pen that will + be used to draw the text outline. (The latter can be slow, especially for + complex pens, and items with long text content.) If all you want is to + draw a simple line of text, you should call setBrush() only, and leave the + pen unset; QGraphicsSimpleTextItem's pen is by default Qt::NoPen. + + QGraphicsSimpleTextItem uses the text's formatted size and the associated + font to provide a reasonable implementation of boundingRect(), shape(), + and contains(). You can set the font by calling setFont(). + + QGraphicsSimpleText does not display rich text; instead, you can use + QGraphicsTextItem, which provides full text control capabilities. + + \img graphicsview-simpletextitem.png + + \sa QGraphicsTextItem, QGraphicsPathItem, QGraphicsRectItem, QGraphicsEllipseItem, + QGraphicsPixmapItem, QGraphicsPolygonItem, QGraphicsLineItem, {The + Graphics View Framework} +*/ + +/*! + Constructs a QGraphicsSimpleTextItem. + + \a parent is passed to QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene) +{ +} + +/*! + Constructs a QGraphicsSimpleTextItem, using \a text as the default plain text. + + \a parent is passed to QGraphicsItem's constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsSimpleTextItem::QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QAbstractGraphicsShapeItem(*new QGraphicsSimpleTextItemPrivate, parent, scene) +{ + setText(text); +} + +/*! + Destroys the QGraphicsSimpleTextItem. +*/ +QGraphicsSimpleTextItem::~QGraphicsSimpleTextItem() +{ +} + +/*! + Sets the item's text to \a text. The text will be displayed as + plain text. Newline characters ('\n') as well as characters of + type QChar::LineSeparator will cause item to break the text into + multiple lines. +*/ +void QGraphicsSimpleTextItem::setText(const QString &text) +{ + Q_D(QGraphicsSimpleTextItem); + if (d->text == text) + return; + d->text = text; + d->updateBoundingRect(); +} + +/*! + Returns the item's text. +*/ +QString QGraphicsSimpleTextItem::text() const +{ + Q_D(const QGraphicsSimpleTextItem); + return d->text; +} + +/*! + Sets the font that is used to draw the item's text to \a font. +*/ +void QGraphicsSimpleTextItem::setFont(const QFont &font) +{ + Q_D(QGraphicsSimpleTextItem); + d->font = font; + d->updateBoundingRect(); +} + +/*! + Returns the font that is used to draw the item's text. +*/ +QFont QGraphicsSimpleTextItem::font() const +{ + Q_D(const QGraphicsSimpleTextItem); + return d->font; +} + +/*! + \reimp +*/ +QRectF QGraphicsSimpleTextItem::boundingRect() const +{ + Q_D(const QGraphicsSimpleTextItem); + return d->boundingRect; +} + +/*! + \reimp +*/ +QPainterPath QGraphicsSimpleTextItem::shape() const +{ + Q_D(const QGraphicsSimpleTextItem); + QPainterPath path; + path.addRect(d->boundingRect); + return path; +} + +/*! + \reimp +*/ +bool QGraphicsSimpleTextItem::contains(const QPointF &point) const +{ + Q_D(const QGraphicsSimpleTextItem); + return d->boundingRect.contains(point); +} + +/*! + \reimp +*/ +void QGraphicsSimpleTextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(widget); + Q_D(QGraphicsSimpleTextItem); + + painter->setFont(d->font); + + QString tmp = d->text; + tmp.replace(QLatin1Char('\n'), QChar::LineSeparator); + QStackTextEngine engine(tmp, d->font); + QTextLayout layout(&engine); + setupTextLayout(&layout); + + QPen p; + p.setBrush(d->brush); + painter->setPen(p); + if (d->pen.style() == Qt::NoPen && d->brush.style() == Qt::SolidPattern) { + painter->setBrush(Qt::NoBrush); + } else { + QTextLayout::FormatRange range; + range.start = 0; + range.length = layout.text().length(); + range.format.setTextOutline(d->pen); + QList<QTextLayout::FormatRange> formats; + formats.append(range); + layout.setAdditionalFormats(formats); + } + + layout.draw(painter, QPointF(0, 0)); + + if (option->state & (QStyle::State_Selected | QStyle::State_HasFocus)) + qt_graphicsItem_highlightSelected(this, painter, option); +} + +/*! + \reimp +*/ +bool QGraphicsSimpleTextItem::isObscuredBy(const QGraphicsItem *item) const +{ + return QAbstractGraphicsShapeItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsSimpleTextItem::opaqueArea() const +{ + return QAbstractGraphicsShapeItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsSimpleTextItem::type() const +{ + return Type; +} + +/*! + \internal +*/ +bool QGraphicsSimpleTextItem::supportsExtension(Extension extension) const +{ + Q_UNUSED(extension); + return false; +} + +/*! + \internal +*/ +void QGraphicsSimpleTextItem::setExtension(Extension extension, const QVariant &variant) +{ + Q_UNUSED(extension); + Q_UNUSED(variant); +} + +/*! + \internal +*/ +QVariant QGraphicsSimpleTextItem::extension(const QVariant &variant) const +{ + Q_UNUSED(variant); + return QVariant(); +} + +/*! + \class QGraphicsItemGroup + \brief The QGraphicsItemGroup class provides treating a group of items as + one. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + A QGraphicsItemGroup is a special type of compound item that + treats itself and all its children as one item (i.e., all events + and geometries for all children are merged together). It's common + to use item groups in presentation tools, when the user wants to + group several smaller items into one big item in order to simplify + moving and copying of items. + + If all you want is to store items inside other items, you can use + any QGraphicsItem directly by passing a suitable parent to + setParentItem(). + + The boundingRect() function of QGraphicsItemGroup returns the + bounding rectangle of all items in the item group. In addition, + item groups have handlesChildEvents() enabled by default, so all + events sent to a member of the group go to the item group (i.e., + selecting one item in a group will select them all). + QGraphicsItemGroup ignores the ItemIgnoresTransformations flag on its + children (i.e., with respect to the geometry of the group item, the + children are treated as if they were transformable). + + There are two ways to construct an item group. The easiest and + most common approach is to pass a list of items (e.g., all + selected items) to QGraphicsScene::createItemGroup(), which + returns a new QGraphicsItemGroup item. The other approach is to + manually construct a QGraphicsItemGroup item, add it to the scene + calling QGraphicsScene::addItem(), and then add items to the group + manually, one at a time by calling addToGroup(). To dismantle + ("ungroup") an item group, you can either call + QGraphicsScene::destroyItemGroup(), or you can manually remove all + items from the group by calling removeFromGroup(). + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsitem.cpp 17 + + The operation of adding and removing items preserves the items' + scene-relative position and transformation, as opposed to calling + setParentItem(), where only the child item's parent-relative + position and transformation are kept. + + The addtoGroup() function reparents the target item to this item + group, keeping the item's position and transformation intact + relative to the scene. Visually, this means that items added via + addToGroup() will remain completely unchanged as a result of this + operation, regardless of the item or the group's current position + or transformation; although the item's position and matrix are + likely to change. + + The removeFromGroup() function has similar semantics to + setParentItem(); it reparents the item to the parent item of the + item group. As with addToGroup(), the item's scene-relative + position and transformation remain intact. + + \sa QGraphicsItem, {The Graphics View Framework} +*/ + +class QGraphicsItemGroupPrivate : public QGraphicsItemPrivate +{ +public: + QRectF itemsBoundingRect; +}; + +/*! + Constructs a QGraphicsItemGroup. \a parent is passed to QGraphicsItem's + constructor. + + \sa QGraphicsScene::addItem() +*/ +QGraphicsItemGroup::QGraphicsItemGroup(QGraphicsItem *parent +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene +#endif + ) + : QGraphicsItem(*new QGraphicsItemGroupPrivate, parent, scene) +{ + setHandlesChildEvents(true); +} + +/*! + Destroys the QGraphicsItemGroup. +*/ +QGraphicsItemGroup::~QGraphicsItemGroup() +{ +} + +/*! + Adds the given \a item to this item group. The item will be + reparented to this group, but its position and transformation + relative to the scene will stay intact. + + \sa removeFromGroup(), QGraphicsScene::createItemGroup() +*/ +void QGraphicsItemGroup::addToGroup(QGraphicsItem *item) +{ + Q_D(QGraphicsItemGroup); + if (!item) { + qWarning("QGraphicsItemGroup::addToGroup: cannot add null item"); + return; + } + if (item == this) { + qWarning("QGraphicsItemGroup::addToGroup: cannot add a group to itself"); + return; + } + + QTransform oldSceneMatrix = item->sceneTransform(); + item->setPos(mapFromItem(item, 0, 0)); + item->setParentItem(this); + item->setTransform(oldSceneMatrix + * sceneTransform().inverted() + * QTransform::fromTranslate(-item->x(), -item->y())); + item->d_func()->setIsMemberOfGroup(true); + prepareGeometryChange(); + d->itemsBoundingRect |= (item->transform() * QTransform::fromTranslate(item->x(), item->y())) + .mapRect(item->boundingRect() | item->childrenBoundingRect()); + update(); +} + +/*! + Removes the specified \a item from this group. The item will be + reparented to this group's parent item, or to 0 if this group has + no parent. Its position and transformation relative to the scene + will stay intact. + + \sa addToGroup(), QGraphicsScene::destroyItemGroup() +*/ +void QGraphicsItemGroup::removeFromGroup(QGraphicsItem *item) +{ + Q_D(QGraphicsItemGroup); + if (!item) { + qWarning("QGraphicsItemGroup::removeFromGroup: cannot remove null item"); + return; + } + + QGraphicsItem *newParent = d_ptr->parent; + QPointF oldPos = item->mapToItem(newParent, 0, 0); + item->setParentItem(newParent); + // ### This function should remap the item's matrix to keep the item's + // transformation unchanged relative to the scene. + item->setPos(oldPos); + item->d_func()->setIsMemberOfGroup(item->group() != 0); + + // ### Quite expensive. But removeFromGroup() isn't called very often. + prepareGeometryChange(); + d->itemsBoundingRect = childrenBoundingRect(); +} + +/*! + \reimp + + Returns the bounding rect of this group item, and all its children. +*/ +QRectF QGraphicsItemGroup::boundingRect() const +{ + Q_D(const QGraphicsItemGroup); + return d->itemsBoundingRect; +} + +/*! + \reimp +*/ +void QGraphicsItemGroup::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(widget); + if (option->state & QStyle::State_Selected) { + Q_D(QGraphicsItemGroup); + painter->setBrush(Qt::NoBrush); + painter->drawRect(d->itemsBoundingRect); + } +} + +/*! + \reimp +*/ +bool QGraphicsItemGroup::isObscuredBy(const QGraphicsItem *item) const +{ + return QGraphicsItem::isObscuredBy(item); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsItemGroup::opaqueArea() const +{ + return QGraphicsItem::opaqueArea(); +} + +/*! + \reimp +*/ +int QGraphicsItemGroup::type() const +{ + return Type; +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug debug, QGraphicsItem *item) +{ + if (!item) { + debug << "QGraphicsItem(0)"; + return debug; + } + + QStringList flags; + if (item->isVisible()) flags << QLatin1String("isVisible"); + if (item->isEnabled()) flags << QLatin1String("isEnabled"); + if (item->isSelected()) flags << QLatin1String("isSelected"); + if (item->hasFocus()) flags << QLatin1String("HasFocus"); + + debug << "QGraphicsItem(this =" << ((void*)item) + << ", parent =" << ((void*)item->parentItem()) + << ", pos =" << item->pos() + << ", z =" << item->zValue() << ", flags = {" + << flags.join(QLatin1String("|")) << " })"; + return debug; +} + +QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change) +{ + const char *str = "UnknownChange"; + switch (change) { + case QGraphicsItem::ItemChildAddedChange: + str = "ItemChildAddedChange"; + break; + case QGraphicsItem::ItemChildRemovedChange: + str = "ItemChildRemovedChange"; + break; + case QGraphicsItem::ItemCursorChange: + str = "ItemCursorChange"; + break; + case QGraphicsItem::ItemCursorHasChanged: + str = "ItemCursorHasChanged"; + break; + case QGraphicsItem::ItemEnabledChange: + str = "ItemEnabledChange"; + break; + case QGraphicsItem::ItemEnabledHasChanged: + str = "ItemEnabledHasChanged"; + break; + case QGraphicsItem::ItemFlagsChange: + str = "ItemFlagsChange"; + break; + case QGraphicsItem::ItemFlagsHaveChanged: + str = "ItemFlagsHaveChanged"; + break; + case QGraphicsItem::ItemMatrixChange: + str = "ItemMatrixChange"; + break; + case QGraphicsItem::ItemParentChange: + str = "ItemParentChange"; + break; + case QGraphicsItem::ItemParentHasChanged: + str = "ItemParentHasChanged"; + break; + case QGraphicsItem::ItemPositionChange: + str = "ItemPositionChange"; + break; + case QGraphicsItem::ItemPositionHasChanged: + str = "ItemPositionHasChanged"; + break; + case QGraphicsItem::ItemSceneChange: + str = "ItemSceneChange"; + break; + case QGraphicsItem::ItemSceneHasChanged: + str = "ItemSceneHasChanged"; + break; + case QGraphicsItem::ItemSelectedChange: + str = "ItemSelectedChange"; + break; + case QGraphicsItem::ItemSelectedHasChanged: + str = "ItemSelectedHasChanged"; + break; + case QGraphicsItem::ItemToolTipChange: + str = "ItemToolTipChange"; + break; + case QGraphicsItem::ItemToolTipHasChanged: + str = "ItemToolTipHasChanged"; + break; + case QGraphicsItem::ItemTransformChange: + str = "ItemTransformChange"; + break; + case QGraphicsItem::ItemTransformHasChanged: + str = "ItemTransformHasChanged"; + break; + case QGraphicsItem::ItemVisibleChange: + str = "ItemVisibleChange"; + break; + case QGraphicsItem::ItemVisibleHasChanged: + str = "ItemVisibleHasChanged"; + break; + case QGraphicsItem::ItemZValueChange: + str = "ItemZValueChange"; + break; + case QGraphicsItem::ItemZValueHasChanged: + str = "ItemZValueHasChanged"; + break; + case QGraphicsItem::ItemOpacityChange: + str = "ItemOpacityChange"; + break; + case QGraphicsItem::ItemOpacityHasChanged: + str = "ItemOpacityHasChanged"; + break; + } + debug << str; + return debug; +} + +QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag) +{ + const char *str = "UnknownFlag"; + switch (flag) { + case QGraphicsItem::ItemIsMovable: + str = "ItemIsMovable"; + break; + case QGraphicsItem::ItemIsSelectable: + str = "ItemIsSelectable"; + break; + case QGraphicsItem::ItemIsFocusable: + str = "ItemIsFocusable"; + break; + case QGraphicsItem::ItemClipsToShape: + str = "ItemClipsToShape"; + break; + case QGraphicsItem::ItemClipsChildrenToShape: + str = "ItemClipsChildrenToShape"; + break; + case QGraphicsItem::ItemIgnoresTransformations: + str = "ItemIgnoresTransformations"; + break; + case QGraphicsItem::ItemIgnoresParentOpacity: + str = "ItemIgnoresParentOpacity"; + break; + case QGraphicsItem::ItemDoesntPropagateOpacityToChildren: + str = "ItemDoesntPropagateOpacityToChildren"; + break; + case QGraphicsItem::ItemStacksBehindParent: + str = "ItemStacksBehindParent"; + break; + } + debug << str; + return debug; +} + +QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags) +{ + debug << "("; + bool f = false; + for (int i = 0; i < 9; ++i) { + if (flags & (1 << i)) { + if (f) + debug << "|"; + f = true; + debug << QGraphicsItem::GraphicsItemFlag(int(flags & (1 << i))); + } + } + debug << ")"; + return debug; +} + +#endif + +QT_END_NAMESPACE + +#include "moc_qgraphicsitem.cpp" + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsitem.h b/src/gui/graphicsview/qgraphicsitem.h new file mode 100644 index 0000000..b98882d --- /dev/null +++ b/src/gui/graphicsview/qgraphicsitem.h @@ -0,0 +1,1016 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSITEM_H +#define QGRAPHICSITEM_H + +#include <QtCore/qglobal.h> +#include <QtCore/qobject.h> +#include <QtCore/qvariant.h> +#include <QtCore/qrect.h> +#include <QtGui/qpainterpath.h> +#include <QtGui/qpixmap.h> + +class tst_QGraphicsItem; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QBrush; +class QCursor; +class QFocusEvent; +class QGraphicsItemGroup; +class QGraphicsSceneContextMenuEvent; +class QGraphicsSceneDragDropEvent; +class QGraphicsSceneEvent; +class QGraphicsSceneHoverEvent; +class QGraphicsSceneMouseEvent; +class QGraphicsSceneWheelEvent; +class QGraphicsScene; +class QGraphicsWidget; +class QInputMethodEvent; +class QKeyEvent; +class QMatrix; +class QMenu; +class QPainter; +class QPen; +class QPointF; +class QRectF; +class QStyleOptionGraphicsItem; + +class QGraphicsItemPrivate; +class Q_GUI_EXPORT QGraphicsItem +{ +public: + enum GraphicsItemFlag { + ItemIsMovable = 0x1, + ItemIsSelectable = 0x2, + ItemIsFocusable = 0x4, + ItemClipsToShape = 0x8, + ItemClipsChildrenToShape = 0x10, + ItemIgnoresTransformations = 0x20, + ItemIgnoresParentOpacity = 0x40, + ItemDoesntPropagateOpacityToChildren = 0x80, + ItemStacksBehindParent = 0x100 + }; + Q_DECLARE_FLAGS(GraphicsItemFlags, GraphicsItemFlag) + + enum GraphicsItemChange { + ItemPositionChange, + ItemMatrixChange, + ItemVisibleChange, + ItemEnabledChange, + ItemSelectedChange, + ItemParentChange, + ItemChildAddedChange, + ItemChildRemovedChange, + ItemTransformChange, + ItemPositionHasChanged, + ItemTransformHasChanged, + ItemSceneChange, + ItemVisibleHasChanged, + ItemEnabledHasChanged, + ItemSelectedHasChanged, + ItemParentHasChanged, + ItemSceneHasChanged, + ItemCursorChange, + ItemCursorHasChanged, + ItemToolTipChange, + ItemToolTipHasChanged, + ItemFlagsChange, + ItemFlagsHaveChanged, + ItemZValueChange, + ItemZValueHasChanged, + ItemOpacityChange, + ItemOpacityHasChanged + }; + + enum CacheMode { + NoCache, + ItemCoordinateCache, + DeviceCoordinateCache + }; + + QGraphicsItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + virtual ~QGraphicsItem(); + + QGraphicsScene *scene() const; + + QGraphicsItem *parentItem() const; + QGraphicsItem *topLevelItem() const; + QGraphicsWidget *parentWidget() const; + QGraphicsWidget *topLevelWidget() const; + QGraphicsWidget *window() const; + void setParentItem(QGraphicsItem *parent); + QList<QGraphicsItem *> children() const; // ### obsolete + QList<QGraphicsItem *> childItems() const; + bool isWidget() const; + bool isWindow() const; + + QGraphicsItemGroup *group() const; + void setGroup(QGraphicsItemGroup *group); + + GraphicsItemFlags flags() const; + void setFlag(GraphicsItemFlag flag, bool enabled = true); + void setFlags(GraphicsItemFlags flags); + + CacheMode cacheMode() const; + void setCacheMode(CacheMode mode, const QSize &cacheSize = QSize()); + +#ifndef QT_NO_TOOLTIP + QString toolTip() const; + void setToolTip(const QString &toolTip); +#endif + +#ifndef QT_NO_CURSOR + QCursor cursor() const; + void setCursor(const QCursor &cursor); + bool hasCursor() const; + void unsetCursor(); +#endif + + bool isVisible() const; + bool isVisibleTo(const QGraphicsItem *parent) const; + void setVisible(bool visible); + inline void hide() { setVisible(false); } + inline void show() { setVisible(true); } + + bool isEnabled() const; + void setEnabled(bool enabled); + + bool isSelected() const; + void setSelected(bool selected); + + bool acceptDrops() const; + void setAcceptDrops(bool on); + + qreal opacity() const; + qreal effectiveOpacity() const; + void setOpacity(qreal opacity); + + Qt::MouseButtons acceptedMouseButtons() const; + void setAcceptedMouseButtons(Qt::MouseButtons buttons); + + bool acceptsHoverEvents() const; // obsolete + void setAcceptsHoverEvents(bool enabled); // obsolete + bool acceptHoverEvents() const; + void setAcceptHoverEvents(bool enabled); + + bool handlesChildEvents() const; + void setHandlesChildEvents(bool enabled); + + bool hasFocus() const; + void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason); + void clearFocus(); + + void grabMouse(); + void ungrabMouse(); + void grabKeyboard(); + void ungrabKeyboard(); + + // Positioning in scene coordinates + QPointF pos() const; + inline qreal x() const { return pos().x(); } + inline qreal y() const { return pos().y(); } + QPointF scenePos() const; + void setPos(const QPointF &pos); + inline void setPos(qreal x, qreal y); + inline void moveBy(qreal dx, qreal dy) { setPos(pos().x() + dx, pos().y() + dy); } + + void ensureVisible(const QRectF &rect = QRectF(), int xmargin = 50, int ymargin = 50); + inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50); + + // Local transformation + QMatrix matrix() const; + QMatrix sceneMatrix() const; + void setMatrix(const QMatrix &matrix, bool combine = false); + void resetMatrix(); + QTransform transform() const; + QTransform sceneTransform() const; + QTransform deviceTransform(const QTransform &viewportTransform) const; + QTransform itemTransform(const QGraphicsItem *other, bool *ok = 0) const; + void setTransform(const QTransform &matrix, bool combine = false); + void resetTransform(); + + void rotate(qreal angle); + void scale(qreal sx, qreal sy); + void shear(qreal sh, qreal sv); + void translate(qreal dx, qreal dy); + virtual void advance(int phase); + + // Stacking order + qreal zValue() const; + void setZValue(qreal z); + + // Hit test + virtual QRectF boundingRect() const = 0; + QRectF childrenBoundingRect() const; + QRectF sceneBoundingRect() const; + virtual QPainterPath shape() const; + bool isClipped() const; + QPainterPath clipPath() const; + virtual bool contains(const QPointF &point) const; + virtual bool collidesWithItem(const QGraphicsItem *other, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + virtual bool collidesWithPath(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> collidingItems(Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + bool isObscured() const; + bool isObscured(const QRectF &rect) const; // ### Qt 5: merge with isObscured(), add QRectF arg to isObscuredBy() + inline bool isObscured(qreal x, qreal y, qreal w, qreal h) const; + virtual bool isObscuredBy(const QGraphicsItem *item) const; + virtual QPainterPath opaqueArea() const; + + QRegion boundingRegion(const QTransform &itemToDeviceTransform) const; + qreal boundingRegionGranularity() const; + void setBoundingRegionGranularity(qreal granularity); + + // Drawing + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0) = 0; + void update(const QRectF &rect = QRectF()); + inline void update(qreal x, qreal y, qreal width, qreal height); + void scroll(qreal dx, qreal dy, const QRectF &rect = QRectF()); + + // Coordinate mapping + QPointF mapToItem(const QGraphicsItem *item, const QPointF &point) const; + QPointF mapToParent(const QPointF &point) const; + QPointF mapToScene(const QPointF &point) const; + QPolygonF mapToItem(const QGraphicsItem *item, const QRectF &rect) const; + QPolygonF mapToParent(const QRectF &rect) const; + QPolygonF mapToScene(const QRectF &rect) const; + QRectF mapRectToItem(const QGraphicsItem *item, const QRectF &rect) const; + QRectF mapRectToParent(const QRectF &rect) const; + QRectF mapRectToScene(const QRectF &rect) const; + QPolygonF mapToItem(const QGraphicsItem *item, const QPolygonF &polygon) const; + QPolygonF mapToParent(const QPolygonF &polygon) const; + QPolygonF mapToScene(const QPolygonF &polygon) const; + QPainterPath mapToItem(const QGraphicsItem *item, const QPainterPath &path) const; + QPainterPath mapToParent(const QPainterPath &path) const; + QPainterPath mapToScene(const QPainterPath &path) const; + QPointF mapFromItem(const QGraphicsItem *item, const QPointF &point) const; + QPointF mapFromParent(const QPointF &point) const; + QPointF mapFromScene(const QPointF &point) const; + QPolygonF mapFromItem(const QGraphicsItem *item, const QRectF &rect) const; + QPolygonF mapFromParent(const QRectF &rect) const; + QPolygonF mapFromScene(const QRectF &rect) const; + QRectF mapRectFromItem(const QGraphicsItem *item, const QRectF &rect) const; + QRectF mapRectFromParent(const QRectF &rect) const; + QRectF mapRectFromScene(const QRectF &rect) const; + QPolygonF mapFromItem(const QGraphicsItem *item, const QPolygonF &polygon) const; + QPolygonF mapFromParent(const QPolygonF &polygon) const; + QPolygonF mapFromScene(const QPolygonF &polygon) const; + QPainterPath mapFromItem(const QGraphicsItem *item, const QPainterPath &path) const; + QPainterPath mapFromParent(const QPainterPath &path) const; + QPainterPath mapFromScene(const QPainterPath &path) const; + + inline QPointF mapToItem(const QGraphicsItem *item, qreal x, qreal y) const; + inline QPointF mapToParent(qreal x, qreal y) const; + inline QPointF mapToScene(qreal x, qreal y) const; + inline QPolygonF mapToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const; + inline QPolygonF mapToParent(qreal x, qreal y, qreal w, qreal h) const; + inline QPolygonF mapToScene(qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectToItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectToParent(qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectToScene(qreal x, qreal y, qreal w, qreal h) const; + inline QPointF mapFromItem(const QGraphicsItem *item, qreal x, qreal y) const; + inline QPointF mapFromParent(qreal x, qreal y) const; + inline QPointF mapFromScene(qreal x, qreal y) const; + inline QPolygonF mapFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const; + inline QPolygonF mapFromParent(qreal x, qreal y, qreal w, qreal h) const; + inline QPolygonF mapFromScene(qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectFromItem(const QGraphicsItem *item, qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectFromParent(qreal x, qreal y, qreal w, qreal h) const; + inline QRectF mapRectFromScene(qreal x, qreal y, qreal w, qreal h) const; + + bool isAncestorOf(const QGraphicsItem *child) const; + QGraphicsItem *commonAncestorItem(const QGraphicsItem *other) const; + bool isUnderMouse() const; + + // Custom data + QVariant data(int key) const; + void setData(int key, const QVariant &value); + + enum { + Type = 1, + UserType = 65536 + }; + virtual int type() const; + + void installSceneEventFilter(QGraphicsItem *filterItem); + void removeSceneEventFilter(QGraphicsItem *filterItem); + +protected: + virtual bool sceneEventFilter(QGraphicsItem *watched, QEvent *event); + virtual bool sceneEvent(QEvent *event); + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event); + virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event); + virtual void dropEvent(QGraphicsSceneDragDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + virtual void wheelEvent(QGraphicsSceneWheelEvent *event); + virtual void inputMethodEvent(QInputMethodEvent *event); + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); + + enum Extension { + UserExtension = 0x80000000 + }; + virtual bool supportsExtension(Extension extension) const; + virtual void setExtension(Extension extension, const QVariant &variant); + virtual QVariant extension(const QVariant &variant) const; + +protected: + QGraphicsItem(QGraphicsItemPrivate &dd, + QGraphicsItem *parent, QGraphicsScene *scene); + QGraphicsItemPrivate *d_ptr; + + void addToIndex(); + void removeFromIndex(); + void prepareGeometryChange(); + +private: + Q_DISABLE_COPY(QGraphicsItem) + Q_DECLARE_PRIVATE(QGraphicsItem) + friend class QGraphicsItemGroup; + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; + friend class QGraphicsSceneFindItemBspTreeVisitor; + friend class QGraphicsView; + friend class QGraphicsViewPrivate; + friend class QGraphicsWidget; + friend class QGraphicsWidgetPrivate; + friend class QGraphicsProxyWidgetPrivate; + friend class ::tst_QGraphicsItem; + friend bool qt_closestLeaf(const QGraphicsItem *, const QGraphicsItem *); + friend bool qt_closestItemFirst(const QGraphicsItem *, const QGraphicsItem *); +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsItem::GraphicsItemFlags) + +inline void QGraphicsItem::setPos(qreal ax, qreal ay) +{ setPos(QPointF(ax, ay)); } +inline void QGraphicsItem::ensureVisible(qreal ax, qreal ay, qreal w, qreal h, int xmargin, int ymargin) +{ ensureVisible(QRectF(ax, ay, w, h), xmargin, ymargin); } +inline void QGraphicsItem::update(qreal ax, qreal ay, qreal width, qreal height) +{ update(QRectF(ax, ay, width, height)); } +inline bool QGraphicsItem::isObscured(qreal ax, qreal ay, qreal w, qreal h) const +{ return isObscured(QRectF(ax, ay, w, h)); } +inline QPointF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay) const +{ return mapToItem(item, QPointF(ax, ay)); } +inline QPointF QGraphicsItem::mapToParent(qreal ax, qreal ay) const +{ return mapToParent(QPointF(ax, ay)); } +inline QPointF QGraphicsItem::mapToScene(qreal ax, qreal ay) const +{ return mapToScene(QPointF(ax, ay)); } +inline QPointF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay) const +{ return mapFromItem(item, QPointF(ax, ay)); } +inline QPointF QGraphicsItem::mapFromParent(qreal ax, qreal ay) const +{ return mapFromParent(QPointF(ax, ay)); } +inline QPointF QGraphicsItem::mapFromScene(qreal ax, qreal ay) const +{ return mapFromScene(QPointF(ax, ay)); } +inline QPolygonF QGraphicsItem::mapToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const +{ return mapToItem(item, QRectF(ax, ay, w, h)); } +inline QPolygonF QGraphicsItem::mapToParent(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapToParent(QRectF(ax, ay, w, h)); } +inline QPolygonF QGraphicsItem::mapToScene(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapToScene(QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectToItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectToItem(item, QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectToParent(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectToParent(QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectToScene(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectToScene(QRectF(ax, ay, w, h)); } +inline QPolygonF QGraphicsItem::mapFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const +{ return mapFromItem(item, QRectF(ax, ay, w, h)); } +inline QPolygonF QGraphicsItem::mapFromParent(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapFromParent(QRectF(ax, ay, w, h)); } +inline QPolygonF QGraphicsItem::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapFromScene(QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectFromItem(const QGraphicsItem *item, qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectFromItem(item, QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectFromParent(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectFromParent(QRectF(ax, ay, w, h)); } +inline QRectF QGraphicsItem::mapRectFromScene(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapRectFromScene(QRectF(ax, ay, w, h)); } + +class QAbstractGraphicsShapeItemPrivate; +class Q_GUI_EXPORT QAbstractGraphicsShapeItem : public QGraphicsItem +{ +public: + QAbstractGraphicsShapeItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QAbstractGraphicsShapeItem(); + + QPen pen() const; + void setPen(const QPen &pen); + + QBrush brush() const; + void setBrush(const QBrush &brush); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + +protected: + QAbstractGraphicsShapeItem(QAbstractGraphicsShapeItemPrivate &dd, + QGraphicsItem *parent, QGraphicsScene *scene); + +private: + Q_DISABLE_COPY(QAbstractGraphicsShapeItem) + Q_DECLARE_PRIVATE(QAbstractGraphicsShapeItem) +}; + +class QGraphicsPathItemPrivate; +class Q_GUI_EXPORT QGraphicsPathItem : public QAbstractGraphicsShapeItem +{ +public: + QGraphicsPathItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsPathItem(const QPainterPath &path, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsPathItem(); + + QPainterPath path() const; + void setPath(const QPainterPath &path); + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 2 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsPathItem) + Q_DECLARE_PRIVATE(QGraphicsPathItem) +}; + +class QGraphicsRectItemPrivate; +class Q_GUI_EXPORT QGraphicsRectItem : public QAbstractGraphicsShapeItem +{ +public: + QGraphicsRectItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsRectItem(const QRectF &rect, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsRectItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsRectItem(); + + QRectF rect() const; + void setRect(const QRectF &rect); + inline void setRect(qreal x, qreal y, qreal w, qreal h); + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 3 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsRectItem) + Q_DECLARE_PRIVATE(QGraphicsRectItem) +}; + +inline void QGraphicsRectItem::setRect(qreal ax, qreal ay, qreal w, qreal h) +{ setRect(QRectF(ax, ay, w, h)); } + +class QGraphicsEllipseItemPrivate; +class Q_GUI_EXPORT QGraphicsEllipseItem : public QAbstractGraphicsShapeItem +{ +public: + QGraphicsEllipseItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsEllipseItem(const QRectF &rect, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsEllipseItem(qreal x, qreal y, qreal w, qreal h, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsEllipseItem(); + + QRectF rect() const; + void setRect(const QRectF &rect); + inline void setRect(qreal x, qreal y, qreal w, qreal h); + + int startAngle() const; + void setStartAngle(int angle); + + int spanAngle() const; + void setSpanAngle(int angle); + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 4 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsEllipseItem) + Q_DECLARE_PRIVATE(QGraphicsEllipseItem) +}; + +inline void QGraphicsEllipseItem::setRect(qreal ax, qreal ay, qreal w, qreal h) +{ setRect(QRectF(ax, ay, w, h)); } + +class QGraphicsPolygonItemPrivate; +class Q_GUI_EXPORT QGraphicsPolygonItem : public QAbstractGraphicsShapeItem +{ +public: + QGraphicsPolygonItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsPolygonItem(const QPolygonF &polygon, + QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsPolygonItem(); + + QPolygonF polygon() const; + void setPolygon(const QPolygonF &polygon); + + Qt::FillRule fillRule() const; + void setFillRule(Qt::FillRule rule); + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 5 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsPolygonItem) + Q_DECLARE_PRIVATE(QGraphicsPolygonItem) +}; + +class QGraphicsLineItemPrivate; +class Q_GUI_EXPORT QGraphicsLineItem : public QGraphicsItem +{ +public: + QGraphicsLineItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsLineItem(const QLineF &line, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsLineItem(qreal x1, qreal y1, qreal x2, qreal y2, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsLineItem(); + + QPen pen() const; + void setPen(const QPen &pen); + + QLineF line() const; + void setLine(const QLineF &line); + inline void setLine(qreal x1, qreal y1, qreal x2, qreal y2) + { setLine(QLineF(x1, y1, x2, y2)); } + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 6 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsLineItem) + Q_DECLARE_PRIVATE(QGraphicsLineItem) +}; + +class QGraphicsPixmapItemPrivate; +class Q_GUI_EXPORT QGraphicsPixmapItem : public QGraphicsItem +{ +public: + enum ShapeMode { + MaskShape, + BoundingRectShape, + HeuristicMaskShape + }; + + QGraphicsPixmapItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsPixmapItem(const QPixmap &pixmap, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsPixmapItem(); + + QPixmap pixmap() const; + void setPixmap(const QPixmap &pixmap); + + Qt::TransformationMode transformationMode() const; + void setTransformationMode(Qt::TransformationMode mode); + + QPointF offset() const; + void setOffset(const QPointF &offset); + inline void setOffset(qreal x, qreal y); + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 7 }; + int type() const; + + ShapeMode shapeMode() const; + void setShapeMode(ShapeMode mode); + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsPixmapItem) + Q_DECLARE_PRIVATE(QGraphicsPixmapItem) +}; + +inline void QGraphicsPixmapItem::setOffset(qreal ax, qreal ay) +{ setOffset(QPointF(ax, ay)); } + +class QGraphicsTextItemPrivate; +class QTextDocument; +class QTextCursor; +class Q_GUI_EXPORT QGraphicsTextItem : public QObject, public QGraphicsItem +{ + Q_OBJECT + QDOC_PROPERTY(bool openExternalLinks READ openExternalLinks WRITE setOpenExternalLinks) + QDOC_PROPERTY(QTextCursor textCursor READ textCursor WRITE setTextCursor) + +public: + QGraphicsTextItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsTextItem(const QString &text, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsTextItem(); + + QString toHtml() const; + void setHtml(const QString &html); + + QString toPlainText() const; + void setPlainText(const QString &text); + + QFont font() const; + void setFont(const QFont &font); + + void setDefaultTextColor(const QColor &c); + QColor defaultTextColor() const; + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 8 }; + int type() const; + + void setTextWidth(qreal width); + qreal textWidth() const; + + void adjustSize(); + + void setDocument(QTextDocument *document); + QTextDocument *document() const; + + void setTextInteractionFlags(Qt::TextInteractionFlags flags); + Qt::TextInteractionFlags textInteractionFlags() const; + + void setTabChangesFocus(bool b); + bool tabChangesFocus() const; + + void setOpenExternalLinks(bool open); + bool openExternalLinks() const; + + void setTextCursor(const QTextCursor &cursor); + QTextCursor textCursor() const; + +Q_SIGNALS: + void linkActivated(const QString &); + void linkHovered(const QString &); + +protected: + bool sceneEvent(QEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + void dragEnterEvent(QGraphicsSceneDragDropEvent *event); + void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); + void dragMoveEvent(QGraphicsSceneDragDropEvent *event); + void dropEvent(QGraphicsSceneDragDropEvent *event); + void inputMethodEvent(QInputMethodEvent *event); + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsTextItem) + Q_PRIVATE_SLOT(dd, void _q_updateBoundingRect(const QSizeF &)) + Q_PRIVATE_SLOT(dd, void _q_update(QRectF)) + Q_PRIVATE_SLOT(dd, void _q_ensureVisible(QRectF)) + QGraphicsTextItemPrivate *dd; + friend class QGraphicsTextItemPrivate; +}; + +class QGraphicsSimpleTextItemPrivate; +class Q_GUI_EXPORT QGraphicsSimpleTextItem : public QAbstractGraphicsShapeItem +{ +public: + QGraphicsSimpleTextItem(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + QGraphicsSimpleTextItem(const QString &text, QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsSimpleTextItem(); + + void setText(const QString &text); + QString text() const; + + void setFont(const QFont &font); + QFont font() const; + + QRectF boundingRect() const; + QPainterPath shape() const; + bool contains(const QPointF &point) const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 9 }; + int type() const; + +protected: + bool supportsExtension(Extension extension) const; + void setExtension(Extension extension, const QVariant &variant); + QVariant extension(const QVariant &variant) const; + +private: + Q_DISABLE_COPY(QGraphicsSimpleTextItem) + Q_DECLARE_PRIVATE(QGraphicsSimpleTextItem) +}; + +class QGraphicsItemGroupPrivate; +class Q_GUI_EXPORT QGraphicsItemGroup : public QGraphicsItem +{ +public: + QGraphicsItemGroup(QGraphicsItem *parent = 0 +#ifndef Q_QDOC + // obsolete argument + , QGraphicsScene *scene = 0 +#endif + ); + ~QGraphicsItemGroup(); + + void addToGroup(QGraphicsItem *item); + void removeFromGroup(QGraphicsItem *item); + + QRectF boundingRect() const; + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + + bool isObscuredBy(const QGraphicsItem *item) const; + QPainterPath opaqueArea() const; + + enum { Type = 10 }; + int type() const; + +private: + Q_DISABLE_COPY(QGraphicsItemGroup) + Q_DECLARE_PRIVATE(QGraphicsItemGroup) +}; + +template <class T> inline T qgraphicsitem_cast(QGraphicsItem *item) +{ + return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type) + || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0; +} + +template <class T> inline T qgraphicsitem_cast(const QGraphicsItem *item) +{ + return int(static_cast<T>(0)->Type) == int(QGraphicsItem::Type) + || (item && int(static_cast<T>(0)->Type) == item->type()) ? static_cast<T>(item) : 0; +} + +#ifndef QT_NO_DEBUG_STREAM +Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem *item); +Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemChange change); +Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlag flag); +Q_GUI_EXPORT QDebug operator<<(QDebug debug, QGraphicsItem::GraphicsItemFlags flags); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QGraphicsItem *) +Q_DECLARE_METATYPE(QGraphicsScene *) + +QT_BEGIN_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSITEM_H + diff --git a/src/gui/graphicsview/qgraphicsitem_p.h b/src/gui/graphicsview/qgraphicsitem_p.h new file mode 100644 index 0000000..af0b077 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsitem_p.h @@ -0,0 +1,287 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSITEM_P_H +#define QGRAPHICSITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsitem.h" + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +QT_BEGIN_NAMESPACE + +class QGraphicsItemPrivate; + +class QGraphicsItemCache +{ +public: + QGraphicsItemCache() : allExposed(false) { } + + // ItemCoordinateCache only + QRect boundingRect; + QSize fixedSize; + QString key; + + // DeviceCoordinateCache only + struct DeviceData { + QTransform lastTransform; + QPoint cacheIndent; + QString key; + }; + QMap<QPaintDevice *, DeviceData> deviceData; + + // List of logical exposed rects + QVector<QRectF> exposed; + bool allExposed; + + // Empty cache + void purge(); +}; + +class Q_AUTOTEST_EXPORT QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsItem) +public: + enum Extra { + ExtraTransform, + ExtraToolTip, + ExtraCursor, + ExtraCacheData, + ExtraMaxDeviceCoordCacheSize, + ExtraBoundingRegionGranularity, + ExtraOpacity, + ExtraEffectiveOpacity + }; + + enum AncestorFlag { + NoFlag = 0, + AncestorHandlesChildEvents = 0x1, + AncestorClipsChildren = 0x2, + AncestorIgnoresTransformations = 0x4 + }; + + inline QGraphicsItemPrivate() + : z(0), + scene(0), + parent(0), + siblingIndex(-1), + index(-1), + depth(0), + acceptedMouseButtons(0x1f), + visible(1), + explicitlyHidden(0), + enabled(1), + explicitlyDisabled(0), + selected(0), + acceptsHover(0), + acceptDrops(0), + isMemberOfGroup(0), + handlesChildEvents(0), + itemDiscovered(0), + hasTransform(0), + hasCursor(0), + ancestorFlags(0), + cacheMode(0), + hasBoundingRegionGranularity(0), + flags(0), + hasOpacity(0), + isWidget(0), + dirty(0), + dirtyChildren(0), + localCollisionHack(0), + globalStackingOrder(-1), + sceneTransformIndex(-1), + q_ptr(0) + { + } + + inline virtual ~QGraphicsItemPrivate() + { } + + void updateAncestorFlag(QGraphicsItem::GraphicsItemFlag childFlag, + AncestorFlag flag = NoFlag, bool enabled = false, bool root = true); + void setIsMemberOfGroup(bool enabled); + void remapItemPos(QEvent *event, QGraphicsItem *item); + QPointF genericMapFromScene(const QPointF &pos, const QWidget *viewport) const; + bool itemIsUntransformable() const; + + // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. + virtual QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; + static bool movableAncestorIsSelected(const QGraphicsItem *item); + + void setPosHelper(const QPointF &pos, bool update); + void setVisibleHelper(bool newVisible, bool explicitly, bool update = true); + void setEnabledHelper(bool newEnabled, bool explicitly, bool update = true); + void updateHelper(const QRectF &rect = QRectF(), bool force = false, bool updateCache = false); + void fullUpdateHelper(bool childrenOnly = false); + void resolveEffectiveOpacity(qreal effectiveParentOpacity); + void resolveDepth(int parentDepth); + void invalidateSceneTransformCache(); + void addChild(QGraphicsItem *child); + void removeChild(QGraphicsItem *child); + void setParentItemHelper(QGraphicsItem *parent, bool deleting); + + virtual void resolveFont(uint inheritedMask) + { + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->resolveFont(inheritedMask); + } + + virtual void resolvePalette(uint inheritedMask) + { + for (int i = 0; i < children.size(); ++i) + children.at(i)->d_ptr->resolveFont(inheritedMask); + } + + virtual bool isProxyWidget() const; + + inline QVariant extra(Extra type) const + { + for (int i = 0; i < extras.size(); ++i) { + const ExtraStruct &extra = extras.at(i); + if (extra.type == type) + return extra.value; + } + return QVariant(); + } + + inline void setExtra(Extra type, const QVariant &value) + { + int index = -1; + for (int i = 0; i < extras.size(); ++i) { + if (extras.at(i).type == type) { + index = i; + break; + } + } + + if (index == -1) { + extras << ExtraStruct(type, value); + } else { + extras[index].value = value; + } + } + + inline void unsetExtra(Extra type) + { + for (int i = 0; i < extras.size(); ++i) { + if (extras.at(i).type == type) { + extras.removeAt(i); + return; + } + } + } + + struct ExtraStruct { + ExtraStruct(Extra type, QVariant value) + : type(type), value(value) + { } + + Extra type; + QVariant value; + + bool operator<(Extra extra) const + { return type < extra; } + }; + QList<ExtraStruct> extras; + + QGraphicsItemCache *maybeExtraItemCache() const; + QGraphicsItemCache *extraItemCache() const; + void removeExtraItemCache(); + + QPointF pos; + qreal z; + QGraphicsScene *scene; + QGraphicsItem *parent; + QList<QGraphicsItem *> children; + int siblingIndex; + int index; + int depth; + + // Packed 32 bytes + quint32 acceptedMouseButtons : 5; + quint32 visible : 1; + quint32 explicitlyHidden : 1; + quint32 enabled : 1; + quint32 explicitlyDisabled : 1; + quint32 selected : 1; + quint32 acceptsHover : 1; + quint32 acceptDrops : 1; + quint32 isMemberOfGroup : 1; + quint32 handlesChildEvents : 1; + quint32 itemDiscovered : 1; + quint32 hasTransform : 1; + quint32 hasCursor : 1; + quint32 ancestorFlags : 3; + quint32 cacheMode : 2; + quint32 hasBoundingRegionGranularity : 1; + quint32 flags : 9; + + // New 32 bytes + quint32 hasOpacity : 1; + quint32 isWidget : 1; + quint32 dirty : 1; + quint32 dirtyChildren : 1; + quint32 localCollisionHack : 1; + + // Optional stacking order + int globalStackingOrder; + int sceneTransformIndex; + + QGraphicsItem *q_ptr; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +#endif diff --git a/src/gui/graphicsview/qgraphicsitemanimation.cpp b/src/gui/graphicsview/qgraphicsitemanimation.cpp new file mode 100644 index 0000000..1b58b9c --- /dev/null +++ b/src/gui/graphicsview/qgraphicsitemanimation.cpp @@ -0,0 +1,599 @@ +/**************************************************************************** +** +** 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 QGraphicsItemAnimation + \brief The QGraphicsItemAnimation class provides simple animation + support for QGraphicsItem. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + The QGraphicsItemAnimation class animates a QGraphicsItem. You can + schedule changes to the item's transformation matrix at + specified steps. The QGraphicsItemAnimation class has a + current step value. When this value changes the transformations + scheduled at that step are performed. The current step of the + animation is set with the \c setStep() function. + + QGraphicsItemAnimation will do a simple linear interpolation + between the nearest adjacent scheduled changes to calculate the + matrix. For instance, if you set the position of an item at values + 0.0 and 1.0, the animation will show the item moving in a straight + line between these positions. The same is true for scaling and + rotation. + + It is usual to use the class with a QTimeLine. The timeline's + \l{QTimeLine::}{valueChanged()} signal is then connected to the + \c setStep() slot. For example, you can set up an item for rotation + by calling \c setRotationAt() for different step values. + The animations timeline is set with the setTimeLine() function. + + An example animation with a timeline follows: + + \snippet doc/src/snippets/timeline/main.cpp 0 + + Note that steps lie between 0.0 and 1.0. It may be necessary to use + \l{QTimeLine::}{setUpdateInterval()}. The default update interval + is 40 ms. A scheduled transformation cannot be removed when set, + so scheduling several transformations of the same kind (e.g., + rotations) at the same step is not recommended. + + \sa QTimeLine, {The Graphics View Framework} +*/ + +#include "qgraphicsitemanimation.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qgraphicsitem.h" + +#include <QtCore/qtimeline.h> +#include <QtCore/qpoint.h> +#include <QtCore/qpointer.h> +#include <QtCore/qpair.h> +#include <QtGui/qmatrix.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsItemAnimationPrivate +{ +public: + inline QGraphicsItemAnimationPrivate() + : q(0), timeLine(0), item(0), step(0) + { } + + QGraphicsItemAnimation *q; + + QPointer<QTimeLine> timeLine; + QGraphicsItem *item; + + QPointF startPos; + QMatrix startMatrix; + + qreal step; + + struct Pair { + Pair(qreal a, qreal b) : step(a), value(b) {} + bool operator <(const Pair &other) const + { return step < other.step; } + bool operator==(const Pair &other) const + { return step == other.step; } + qreal step; + qreal value; + }; + QList<Pair> xPosition; + QList<Pair> yPosition; + QList<Pair> rotation; + QList<Pair> verticalScale; + QList<Pair> horizontalScale; + QList<Pair> verticalShear; + QList<Pair> horizontalShear; + QList<Pair> xTranslation; + QList<Pair> yTranslation; + + qreal linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue = 0); + void insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method); +}; + +qreal QGraphicsItemAnimationPrivate::linearValueForStep(qreal step, QList<Pair> *source, qreal defaultValue) +{ + if (source->isEmpty()) + return defaultValue; + step = qMin<qreal>(qMax<qreal>(step, 0), 1); + + if (step == 1) + return source->last().value; + + qreal stepBefore = 0; + qreal stepAfter = 1; + qreal valueBefore = source->first().step == 0 ? source->first().value : defaultValue; + qreal valueAfter = source->last().value; + + // Find the closest step and value before the given step. + for (int i = 0; i < source->size() && step >= source->at(i).step; ++i) { + stepBefore = source->at(i).step; + valueBefore = source->at(i).value; + } + + // Find the closest step and value after the given step. + for (int j = source->size() - 1; j >= 0 && step < source->at(j).step; --j) { + stepAfter = source->at(j).step; + valueAfter = source->at(j).value; + } + + // Do a simple linear interpolation. + return valueBefore + (valueAfter - valueBefore) * ((step - stepBefore) / (stepAfter - stepBefore)); +} + +void QGraphicsItemAnimationPrivate::insertUniquePair(qreal step, qreal value, QList<Pair> *binList, const char* method) +{ + if (step < 0.0 || step > 1.0) { + qWarning("QGraphicsItemAnimation::%s: invalid step = %f", method, step); + return; + } + + Pair pair(step, value); + + QList<Pair>::iterator result = qBinaryFind(binList->begin(), binList->end(), pair); + if (result != binList->end()) + result->value = value; + else { + *binList << pair; + qSort(binList->begin(), binList->end()); + } +} + +/*! + Constructs an animation object with the given \a parent. +*/ +QGraphicsItemAnimation::QGraphicsItemAnimation(QObject *parent) + : QObject(parent), d(new QGraphicsItemAnimationPrivate) +{ + d->q = this; +} + +/*! + Destroys the animation object. +*/ +QGraphicsItemAnimation::~QGraphicsItemAnimation() +{ + delete d; +} + +/*! + Returns the item on which the animation object operates. + + \sa setItem() +*/ +QGraphicsItem *QGraphicsItemAnimation::item() const +{ + return d->item; +} + +/*! + Sets the specified \a item to be used in the animation. + + \sa item() +*/ +void QGraphicsItemAnimation::setItem(QGraphicsItem *item) +{ + d->item = item; + d->startPos = d->item->pos(); +} + +/*! + Returns the timeline object used to control the rate at which the animation + occurs. + + \sa setTimeLine() +*/ +QTimeLine *QGraphicsItemAnimation::timeLine() const +{ + return d->timeLine; +} + +/*! + Sets the timeline object used to control the rate of animation to the \a timeLine + specified. + + \sa timeLine() +*/ +void QGraphicsItemAnimation::setTimeLine(QTimeLine *timeLine) +{ + if (d->timeLine == timeLine) + return; + if (d->timeLine) + delete d->timeLine; + if (!timeLine) + return; + d->timeLine = timeLine; + connect(timeLine, SIGNAL(valueChanged(qreal)), this, SLOT(setStep(qreal))); +} + +/*! + Returns the position of the item at the given \a step value. + + \sa setPosAt() +*/ +QPointF QGraphicsItemAnimation::posAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::posAt: invalid step = %f", step); + + return QPointF(d->linearValueForStep(step, &d->xPosition, d->startPos.x()), + d->linearValueForStep(step, &d->yPosition, d->startPos.y())); +} + +/*! + \fn void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &point) + + Sets the position of the item at the given \a step value to the \a point specified. + + \sa posAt() +*/ +void QGraphicsItemAnimation::setPosAt(qreal step, const QPointF &pos) +{ + d->insertUniquePair(step, pos.x(), &d->xPosition, "setPosAt"); + d->insertUniquePair(step, pos.y(), &d->yPosition, "setPosAt"); +} + +/*! + Returns all explicitly inserted positions. + + \sa posAt(), setPosAt() +*/ +QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::posList() const +{ + QList<QPair<qreal, QPointF> > list; + for (int i = 0; i < d->xPosition.size(); ++i) + list << QPair<qreal, QPointF>(d->xPosition.at(i).step, QPointF(d->xPosition.at(i).value, d->yPosition.at(i).value)); + + return list; +} + +/*! + Returns the matrix used to transform the item at the specified \a step value. +*/ +QMatrix QGraphicsItemAnimation::matrixAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::matrixAt: invalid step = %f", step); + + QMatrix matrix; + if (!d->rotation.isEmpty()) + matrix.rotate(rotationAt(step)); + if (!d->verticalScale.isEmpty()) + matrix.scale(horizontalScaleAt(step), verticalScaleAt(step)); + if (!d->verticalShear.isEmpty()) + matrix.shear(horizontalShearAt(step), verticalShearAt(step)); + if (!d->xTranslation.isEmpty()) + matrix.translate(xTranslationAt(step), yTranslationAt(step)); + return matrix; +} + +/*! + Returns the angle at which the item is rotated at the specified \a step value. + + \sa setRotationAt() +*/ +qreal QGraphicsItemAnimation::rotationAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::rotationAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->rotation); +} + +/*! + Sets the rotation of the item at the given \a step value to the \a angle specified. + + \sa rotationAt() +*/ +void QGraphicsItemAnimation::setRotationAt(qreal step, qreal angle) +{ + d->insertUniquePair(step, angle, &d->rotation, "setRotationAt"); +} + +/*! + Returns all explicitly inserted rotations. + + \sa rotationAt(), setRotationAt() +*/ +QList<QPair<qreal, qreal> > QGraphicsItemAnimation::rotationList() const +{ + QList<QPair<qreal, qreal> > list; + for (int i = 0; i < d->rotation.size(); ++i) + list << QPair<qreal, qreal>(d->rotation.at(i).step, d->rotation.at(i).value); + + return list; +} + +/*! + Returns the horizontal translation of the item at the specified \a step value. + + \sa setTranslationAt() +*/ +qreal QGraphicsItemAnimation::xTranslationAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::xTranslationAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->xTranslation); +} + +/*! + Returns the vertical translation of the item at the specified \a step value. + + \sa setTranslationAt() +*/ +qreal QGraphicsItemAnimation::yTranslationAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::yTranslationAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->yTranslation); +} + +/*! + Sets the translation of the item at the given \a step value using the horizontal + and vertical coordinates specified by \a dx and \a dy. + + \sa xTranslationAt(), yTranslationAt() +*/ +void QGraphicsItemAnimation::setTranslationAt(qreal step, qreal dx, qreal dy) +{ + d->insertUniquePair(step, dx, &d->xTranslation, "setTranslationAt"); + d->insertUniquePair(step, dy, &d->yTranslation, "setTranslationAt"); +} + +/*! + Returns all explicitly inserted translations. + + \sa xTranslationAt(), yTranslationAt(), setTranslationAt() +*/ +QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::translationList() const +{ + QList<QPair<qreal, QPointF> > list; + for (int i = 0; i < d->xTranslation.size(); ++i) + list << QPair<qreal, QPointF>(d->xTranslation.at(i).step, QPointF(d->xTranslation.at(i).value, d->yTranslation.at(i).value)); + + return list; +} + +/*! + Returns the vertical scale for the item at the specified \a step value. + + \sa setScaleAt() +*/ +qreal QGraphicsItemAnimation::verticalScaleAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::verticalScaleAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->verticalScale, 1); +} + +/*! + Returns the horizontal scale for the item at the specified \a step value. + + \sa setScaleAt() +*/ +qreal QGraphicsItemAnimation::horizontalScaleAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::horizontalScaleAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->horizontalScale, 1); +} + +/*! + Sets the scale of the item at the given \a step value using the horizontal and + vertical scale factors specified by \a sx and \a sy. + + \sa verticalScaleAt(), horizontalScaleAt() +*/ +void QGraphicsItemAnimation::setScaleAt(qreal step, qreal sx, qreal sy) +{ + d->insertUniquePair(step, sx, &d->horizontalScale, "setScaleAt"); + d->insertUniquePair(step, sy, &d->verticalScale, "setScaleAt"); +} + +/*! + Returns all explicitly inserted scales. + + \sa verticalScaleAt(), horizontalScaleAt(), setScaleAt() +*/ +QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::scaleList() const +{ + QList<QPair<qreal, QPointF> > list; + for (int i = 0; i < d->horizontalScale.size(); ++i) + list << QPair<qreal, QPointF>(d->horizontalScale.at(i).step, QPointF(d->horizontalScale.at(i).value, d->verticalScale.at(i).value)); + + return list; +} + +/*! + Returns the vertical shear for the item at the specified \a step value. + + \sa setShearAt() +*/ +qreal QGraphicsItemAnimation::verticalShearAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::verticalShearAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->verticalShear, 0); +} + +/*! + Returns the horizontal shear for the item at the specified \a step value. + + \sa setShearAt() +*/ +qreal QGraphicsItemAnimation::horizontalShearAt(qreal step) const +{ + if (step < 0.0 || step > 1.0) + qWarning("QGraphicsItemAnimation::horizontalShearAt: invalid step = %f", step); + + return d->linearValueForStep(step, &d->horizontalShear, 0); +} + +/*! + Sets the shear of the item at the given \a step value using the horizontal and + vertical shear factors specified by \a sh and \a sv. + + \sa verticalShearAt(), horizontalShearAt() +*/ +void QGraphicsItemAnimation::setShearAt(qreal step, qreal sh, qreal sv) +{ + d->insertUniquePair(step, sh, &d->horizontalShear, "setShearAt"); + d->insertUniquePair(step, sv, &d->verticalShear, "setShearAt"); +} + +/*! + Returns all explicitly inserted shears. + + \sa verticalShearAt(), horizontalShearAt(), setShearAt() +*/ +QList<QPair<qreal, QPointF> > QGraphicsItemAnimation::shearList() const +{ + QList<QPair<qreal, QPointF> > list; + for (int i = 0; i < d->horizontalShear.size(); ++i) + list << QPair<qreal, QPointF>(d->horizontalShear.at(i).step, QPointF(d->horizontalShear.at(i).value, d->verticalShear.at(i).value)); + + return list; +} + +/*! + Clears the scheduled transformations used for the animation, but + retains the item and timeline. +*/ +void QGraphicsItemAnimation::clear() +{ + d->xPosition.clear(); + d->yPosition.clear(); + d->rotation.clear(); + d->verticalScale.clear(); + d->horizontalScale.clear(); + d->verticalShear.clear(); + d->horizontalShear.clear(); + d->xTranslation.clear(); + d->yTranslation.clear(); +} + +/*! + \fn void QGraphicsItemAnimation::setStep(qreal step) + + Sets the current \a step value for the animation, causing the + transformations scheduled at this step to be performed. +*/ +void QGraphicsItemAnimation::setStep(qreal x) +{ + if (x < 0.0 || x > 1.0) { + qWarning("QGraphicsItemAnimation::setStep: invalid step = %f", x); + return; + } + + beforeAnimationStep(x); + + d->step = x; + if (d->item) { + if (!d->xPosition.isEmpty() || !d->yPosition.isEmpty()) + d->item->setPos(posAt(x)); + if (!d->rotation.isEmpty() + || !d->verticalScale.isEmpty() + || !d->horizontalScale.isEmpty() + || !d->verticalShear.isEmpty() + || !d->horizontalShear.isEmpty() + || !d->xTranslation.isEmpty() + || !d->yTranslation.isEmpty()) { + d->item->setMatrix(d->startMatrix * matrixAt(x)); + } + } + + afterAnimationStep(x); +} + +/*! + Resets the item to its starting position and transformation. + + \obsolete + + You can call setStep(0) instead. +*/ +void QGraphicsItemAnimation::reset() +{ + if (!d->item) + return; + d->startPos = d->item->pos(); + d->startMatrix = d->item->matrix(); +} + +/*! + \fn void QGraphicsItemAnimation::beforeAnimationStep(qreal step) + + This method is meant to be overridden by subclassed that needs to + execute additional code before a new step takes place. The + animation \a step is provided for use in cases where the action + depends on its value. +*/ +void QGraphicsItemAnimation::beforeAnimationStep(qreal step) +{ + Q_UNUSED(step); +} + +/*! + \fn void QGraphicsItemAnimation::afterAnimationStep(qreal step) + + This method is meant to be overridden in subclasses that need to + execute additional code after a new step has taken place. The + animation \a step is provided for use in cases where the action + depends on its value. +*/ +void QGraphicsItemAnimation::afterAnimationStep(qreal step) +{ + Q_UNUSED(step); +} + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsitemanimation.h b/src/gui/graphicsview/qgraphicsitemanimation.h new file mode 100644 index 0000000..c1312d4 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsitemanimation.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSITEMANIMATION_H +#define QGRAPHICSITEMANIMATION_H + +#include <QtCore/qobject.h> + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QGraphicsItem; +class QMatrix; +class QPointF; +class QTimeLine; +template <class T1, class T2> struct QPair; + +class QGraphicsItemAnimationPrivate; +class Q_GUI_EXPORT QGraphicsItemAnimation : public QObject +{ + Q_OBJECT +public: + QGraphicsItemAnimation(QObject *parent = 0); + virtual ~QGraphicsItemAnimation(); + + QGraphicsItem *item() const; + void setItem(QGraphicsItem *item); + + QTimeLine *timeLine() const; + void setTimeLine(QTimeLine *timeLine); + + QPointF posAt(qreal step) const; + QList<QPair<qreal, QPointF> > posList() const; + void setPosAt(qreal step, const QPointF &pos); + + QMatrix matrixAt(qreal step) const; + + qreal rotationAt(qreal step) const; + QList<QPair<qreal, qreal> > rotationList() const; + void setRotationAt(qreal step, qreal angle); + + qreal xTranslationAt(qreal step) const; + qreal yTranslationAt(qreal step) const; + QList<QPair<qreal, QPointF> > translationList() const; + void setTranslationAt(qreal step, qreal dx, qreal dy); + + qreal verticalScaleAt(qreal step) const; + qreal horizontalScaleAt(qreal step) const; + QList<QPair<qreal, QPointF> > scaleList() const; + void setScaleAt(qreal step, qreal sx, qreal sy); + + qreal verticalShearAt(qreal step) const; + qreal horizontalShearAt(qreal step) const; + QList<QPair<qreal, QPointF> > shearList() const; + void setShearAt(qreal step, qreal sh, qreal sv); + + void clear(); + +public Q_SLOTS: + void setStep(qreal x); + void reset(); + +protected: + virtual void beforeAnimationStep(qreal step); + virtual void afterAnimationStep(qreal step); + +private: + Q_DISABLE_COPY(QGraphicsItemAnimation) + QGraphicsItemAnimationPrivate *d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QT_NO_GRAPHICSVIEW +#endif diff --git a/src/gui/graphicsview/qgraphicslayout.cpp b/src/gui/graphicsview/qgraphicslayout.cpp new file mode 100644 index 0000000..f78b8c8 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayout.cpp @@ -0,0 +1,423 @@ +/**************************************************************************** +** +** 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 "qapplication.h" + +#ifndef QT_NO_GRAPHICSVIEW +#include "qgraphicslayout.h" +#include "qgraphicslayout_p.h" +#include "qgraphicslayoutitem.h" +#include "qgraphicslayoutitem_p.h" +#include "qgraphicswidget.h" +#include "qgraphicswidget_p.h" +#include "qgraphicsscene.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsLayout + \brief The QGraphicsLayout class provides the base class for all layouts + in Graphics View. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsLayout is an abstract class that defines a virtual API for + arranging QGraphicsWidget children and other QGraphicsLayoutItem objects + for a QGraphicsWidget. QGraphicsWidget assigns responsibility to a + QGraphicsLayout through QGraphicsWidget::setLayout(). As the widget + is resized, the layout will automatically arrange the widget's children. + QGraphicsLayout inherits QGraphicsLayoutItem, so, it can be managed by + any layout, including its own subclasses. + + \section1 Writing a Custom Layout + + You can use QGraphicsLayout as a base to write your own custom layout + (e.g., a flowlayout), but it is more common to use one of its subclasses + instead - QGraphicsLinearLayout or QGraphicsGridLayout. When creating + a custom layout, the following functions must be reimplemented as a bare + minimum: + + \table + \header \o Function \o Description + \row \o QGraphicsLayoutItem::setGeometry() + \o Notifies you when the geometry of the layout is set. You can + store the geometry in your own layout class in a reimplementation + of this function. + \row \o QGraphicsLayoutItem::sizeHint() + \o Returns the layout's size hints. + \row \o QGraphicsLayout::count() + \o Returns the number of items in your layout. + \row \o QGraphicsLayout::itemAt() + \o Returns a pointer to an item in your layout. + \row \o QGraphicsLayout::removeAt() + \o Removes an item from your layout without destroying it. + \endtable + + For more details on how to implement each function, refer to the individual + function documentation. + + Each layout defines its own API for arranging widgets and layout items. + For example, with a grid layout, you require a row and a + column index with optional row and column spans, alignment, spacing, and more. + A linear layout, however, requires a single row or column index to position its + items. For a grid layout, the order of insertion does not affect the layout in + any way, but for a linear layout, the order is essential. When writing your own + layout subclass, you are free to choose the API that best suits your layout. + + \section1 Activating the Layout + + When the layout's geometry changes, QGraphicsLayout immediately rearranges + all of its managed items by calling setGeometry() on each item. This + rearrangement is called \e activating the layout. + + QGraphicsLayout updates its own geometry to match the contentsRect() of the + QGraphicsLayoutItem it is managing. Thus, it will automatically rearrange all + its items when the widget is resized. QGraphicsLayout caches the sizes of all + its managed items to avoid calling setGeometry() too often. + + \note A QGraphicsLayout will have the same geometry as the contentsRect() + of the widget (not the layout) it is assigned to. + + \section2 Activating the Layout Implicitly + + The layout can be activated implicitly using one of two ways: by calling + activate() or by calling invalidate(). Calling activate() activates the layout + immediately. In contrast, calling invalidate() is delayed, as it posts a + \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed widget. Due + to event compression, the activate() will only be called once after control has + returned to the event loop. This is referred to as \e invalidating the layout. + Invalidating the layout also invalidates any cached information. Also, the + invalidate() function is a virtual function. So, you can invalidate your own + cache in a subclass of QGraphicsLayout by reimplementing this function. + + \section1 Event Handling + + QGraphicsLayout listens to events for the widget it manages through the + virtual widgetEvent() event handler. When the layout is assigned to a + widget, all events delivered to the widget are first processed by + widgetEvent(). This allows the layout to be aware of any relevant state + changes on the widget such as visibility changes or layout direction changes. + + \section1 Margin Handling + + The margins of a QGraphicsLayout can be modified by reimplementing + setContentsMargins() and getContentsMargins(). + +*/ + +/*! + Contructs a QGraphicsLayout object. + + \a parent is passed to QGraphicsLayoutItem's constructor and the + QGraphicsLayoutItem's isLayout argument is set to \e true. +*/ +QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayoutItem(*new QGraphicsLayoutPrivate) +{ + setParentLayoutItem(parent); + if (parent && !parent->isLayout()) { + // If a layout has a parent that is not a layout it must be a QGraphicsWidget. + QGraphicsItem *itemParent = parent->graphicsItem(); + if (itemParent && itemParent->isWidget()) { + static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this); + } else { + qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is" + " neither a QGraphicsWidget nor QGraphicsLayout"); + } + } + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType); + setOwnedByLayout(true); +} + +/*! + \internal +*/ +QGraphicsLayout::QGraphicsLayout(QGraphicsLayoutPrivate &dd, QGraphicsLayoutItem *parent) + : QGraphicsLayoutItem(dd) +{ + setParentLayoutItem(parent); + if (parent && !parent->isLayout()) { + // If a layout has a parent that is not a layout it must be a QGraphicsWidget. + QGraphicsItem *itemParent = parent->graphicsItem(); + if (itemParent && itemParent->isWidget()) { + static_cast<QGraphicsWidget *>(itemParent)->d_func()->setLayout_helper(this); + } else { + qWarning("QGraphicsLayout::QGraphicsLayout: Attempt to create a layout with a parent that is" + " neither a QGraphicsWidget nor QGraphicsLayout"); + } + } + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding, QSizePolicy::DefaultType); + setOwnedByLayout(true); +} + +/*! + Destroys the QGraphicsLayout object. +*/ +QGraphicsLayout::~QGraphicsLayout() +{ +} + +/*! + Sets the contents margins to \a left, \a top, \a right and \a bottom. The + default contents margins for toplevel layouts are style dependent + (by querying the pixelMetric for QStyle::PM_LayoutLeftMargin, + QStyle::PM_LayoutTopMargin, QStyle::PM_LayoutRightMargin and + QStyle::PM_LayoutBottomMargin). + + For sublayouts the default margins are 0. + + Changing the contents margins automatically invalidates the layout. + + \sa invalidate() +*/ +void QGraphicsLayout::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom) +{ + Q_D(QGraphicsLayout); + if (d->left == left && d->top == top && d->right == right && d->bottom == bottom) + return; + d->left = left; + d->right = right; + d->top = top; + d->bottom = bottom; + invalidate(); +} + +/*! + \reimp +*/ +void QGraphicsLayout::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +{ + Q_D(const QGraphicsLayout); + d->getMargin(left, d->left, QStyle::PM_LayoutLeftMargin); + d->getMargin(top, d->top, QStyle::PM_LayoutTopMargin); + d->getMargin(right, d->right, QStyle::PM_LayoutRightMargin); + d->getMargin(bottom, d->bottom, QStyle::PM_LayoutBottomMargin); +} + +/*! + Activates the layout, causing all items in the layout to be immediately + rearranged. This function is based on calling count() and itemAt(), and + then calling setGeometry() on all items sequentially. When activated, + the layout will adjust its geometry to its parent's contentsRect(). + The parent will then invalidate any layout of its own. + + If called in sequence or recursively, e.g., by one of the arranged items + in response to being resized, this function will do nothing. + + Note that the layout is free to use geometry caching to optimize this + process. To forcefully invalidate any such cache, you can call + invalidate() before calling activate(). + + \sa invalidate() +*/ +void QGraphicsLayout::activate() +{ + Q_D(QGraphicsLayout); + if (d->activated) + return; + + d->activateRecursive(this); + + // we don't call activate on a sublayout, but somebody might. + // Therefore, we walk to the parentitem of the toplevel layout. + QGraphicsLayoutItem *parentItem = this; + while (parentItem && parentItem->isLayout()) + parentItem = parentItem->parentLayoutItem(); + if (!parentItem) + return; + Q_ASSERT(!parentItem->isLayout()); + + setGeometry(parentItem->contentsRect()); // relayout children + + // ### bug, should be parentItem ? + parentLayoutItem()->updateGeometry(); // bubble up; will set activated to false + // ### too many resizes? maybe we should walk up the chain to the + // ### top-level layouted layoutItem and call activate there. +} + +/*! + Returns true if the layout is currently being activated; otherwise, + returns false. If the layout is being activated, this means that it is + currently in the process of rearranging its items (i.e., the activate() + function has been called, and has not yet returned). + + \sa activate(), invalidate() +*/ +bool QGraphicsLayout::isActivated() const +{ + Q_D(const QGraphicsLayout); + return d->activated; +} + +/*! + Clears any cached geometry and size hint information in the layout, and + posts a \l{QEvent::LayoutRequest}{LayoutRequest} event to the managed + parent QGraphicsLayoutItem. + + \sa activate(), setGeometry() +*/ +void QGraphicsLayout::invalidate() +{ + // only mark layouts as invalid (activated = false) if we can post a LayoutRequest event. + QGraphicsLayoutItem *layoutItem = this; + while (layoutItem && layoutItem->isLayout()) { + // we could call updateGeometry(), but what if that method + // does not call the base implementation? In addition, updateGeometry() + // does more than we need. + layoutItem->d_func()->sizeHintCacheDirty = true; + layoutItem = layoutItem->parentLayoutItem(); + } + if (layoutItem) + layoutItem->d_func()->sizeHintCacheDirty = true; + + bool postIt = layoutItem ? !layoutItem->isLayout() : false; + if (postIt) { + layoutItem = this; + while (layoutItem && layoutItem->isLayout() + && static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated) { + static_cast<QGraphicsLayout*>(layoutItem)->d_func()->activated = false; + layoutItem = layoutItem->parentLayoutItem(); + } + if (layoutItem && !layoutItem->isLayout()) { + // If a layout has a parent that is not a layout it must be a QGraphicsWidget. + QApplication::postEvent(static_cast<QGraphicsWidget *>(layoutItem), new QEvent(QEvent::LayoutRequest)); + } + } +} + +/*! + \reimp +*/ +void QGraphicsLayout::updateGeometry() +{ + QGraphicsLayoutItem::updateGeometry(); + if (QGraphicsLayoutItem *parentItem = parentLayoutItem()) { + if (parentItem->isLayout()) { + parentItem->updateGeometry(); + } else { + invalidate(); + } + } +} + +/*! + This virtual event handler receives all events for the managed + widget. QGraphicsLayout uses this event handler to listen for layout + related events such as geometry changes, layout changes or layout + direction changes. + + \a e is a pointer to the event. + + You can reimplement this event handler to track similar events for your + own custom layout. + + \sa QGraphicsWidget::event(), QGraphicsItem::sceneEvent() +*/ +void QGraphicsLayout::widgetEvent(QEvent *e) +{ + switch (e->type()) { + case QEvent::GraphicsSceneResize: + if (isActivated()) { + setGeometry(parentLayoutItem()->contentsRect()); + } else { + activate(); // relies on that activate() will call updateGeometry() + } + break; + case QEvent::LayoutRequest: + activate(); + break; + case QEvent::LayoutDirectionChange: + invalidate(); + break; + default: + break; + } +} + +/*! + \fn virtual int QGraphicsLayout::count() const = 0 + + This pure virtual function must be reimplemented in a subclass of + QGraphicsLayout to return the number of items in the layout. + + The subclass is free to decide how to store the items. + + \sa itemAt(), removeAt() +*/ + +/*! + \fn virtual QGraphicsLayoutItem *QGraphicsLayout::itemAt(int i) const = 0 + + This pure virtual function must be reimplemented in a subclass of + QGraphicsLayout to return a pointer to the item at index \a i. The + reimplementation can assume that \a i is valid (i.e., it respects the + value of count()). + + The subclass is free to decide how to store the items. + + \sa count(), removeAt() +*/ + +/*! + \fn virtual void QGraphicsLayout::removeAt(int index) = 0 + + This pure virtual function must be reimplemented in a subclass of + QGraphicsLayout to remove the item at \a index. The + reimplementation can assume that \a index is valid (i.e., it + respects the value of count()). + + The implementation must ensure that the parentLayoutItem() of + the removed item does not point to this layout, since the item is + considered to be removed from the layout hierarchy. + + If the layout is to be reused between applications, we recommend + that the layout deletes the item, but the graphics view framework + does not depend on this. + + The subclass is free to decide how to store the items. + + \sa itemAt(), count() +*/ + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayout.h b/src/gui/graphicsview/qgraphicslayout.h new file mode 100644 index 0000000..fad6c3bb --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayout.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSLAYOUT_H +#define QGRAPHICSLAYOUT_H + +#include <QtGui/qgraphicslayoutitem.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsLayoutPrivate; +class QGraphicsLayoutItem; +class QGraphicsWidget; + +class Q_GUI_EXPORT QGraphicsLayout : public QGraphicsLayoutItem +{ +public: + QGraphicsLayout(QGraphicsLayoutItem *parent = 0); + ~QGraphicsLayout(); + + void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom); + void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; + + void activate(); + bool isActivated() const; + virtual void invalidate(); + virtual void updateGeometry(); + + virtual void widgetEvent(QEvent *e); + + virtual int count() const = 0; + virtual QGraphicsLayoutItem *itemAt(int i) const = 0; + virtual void removeAt(int index) = 0; + +protected: + QGraphicsLayout(QGraphicsLayoutPrivate &, QGraphicsLayoutItem *); + +private: + Q_DISABLE_COPY(QGraphicsLayout) + Q_DECLARE_PRIVATE(QGraphicsLayout) + friend class QGraphicsWidget; +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/gui/graphicsview/qgraphicslayout_p.cpp b/src/gui/graphicsview/qgraphicslayout_p.cpp new file mode 100644 index 0000000..f76f4dd --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayout_p.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** 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 "qgraphicslayout_p.h" +#include "qgraphicslayout.h" +#include "qgraphicswidget.h" +#include "qapplication.h" + +QT_BEGIN_NAMESPACE + +/*! + \internal + + \a mw is the new parent. all items in the layout will be a child of \a mw. + */ +void QGraphicsLayoutPrivate::reparentChildItems(QGraphicsItem *newParent) +{ + Q_Q(QGraphicsLayout); + int n = q->count(); + //bool mwVisible = mw && mw->isVisible(); + for (int i = 0; i < n; ++i) { + QGraphicsLayoutItem *layoutChild = q->itemAt(i); + if (!layoutChild) { + // Skip stretch items + continue; + } + if (layoutChild->isLayout()) { + QGraphicsLayout *l = static_cast<QGraphicsLayout*>(layoutChild); + l->d_func()->reparentChildItems(newParent); + } else if (QGraphicsItem *itemChild = layoutChild->graphicsItem()){ + QGraphicsItem *childParent = itemChild->parentItem(); +#ifdef QT_DEBUG + if (childParent && childParent != newParent && itemChild->isWidget() && qt_graphicsLayoutDebug()) { + QGraphicsWidget *w = static_cast<QGraphicsWidget*>(layoutChild); + qWarning("QGraphicsLayout::addChildLayout: widget %s \"%s\" in wrong parent; moved to correct parent", + w->metaObject()->className(), w->objectName().toLocal8Bit().constData()); + } +#endif + if (childParent != newParent) + itemChild->setParentItem(newParent); + } + } +} + +void QGraphicsLayoutPrivate::getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const +{ + if (!result) + return; + Q_Q(const QGraphicsLayout); + + QGraphicsLayoutItem *parent = q->parentLayoutItem(); + if (userMargin >= 0.0) { + *result = userMargin; + } else if (!parent) { + *result = 0.0; + } else if (parent->isLayout()) { // sublayouts have 0 margin by default + *result = 0.0; + } else { + *result = 0.0; + if (QGraphicsItem *layoutParentItem = parentItem()) { + if (layoutParentItem->isWidget()) + *result = (qreal)static_cast<QGraphicsWidget*>(layoutParentItem)->style()->pixelMetric(pm, 0); + } + } +} + +Qt::LayoutDirection QGraphicsLayoutPrivate::visualDirection() const +{ + if (QGraphicsItem *maybeWidget = parentItem()) { + if (maybeWidget->isWidget()) + return static_cast<QGraphicsWidget*>(maybeWidget)->layoutDirection(); + } + return QApplication::layoutDirection(); +} + +static bool removeLayoutItemFromLayout(QGraphicsLayout *lay, QGraphicsLayoutItem *layoutItem) +{ + if (!lay) + return false; + + QGraphicsLayoutItem *child; + for (int i = 0; (child = lay->itemAt(i)); ++i) { + if (child && child->isLayout()) { + if (removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(child), layoutItem)) + return true; + } else if (child == layoutItem) { + lay->removeAt(i); + return true; + } + } + return false; +} + +/*! + \internal + + This function is called from subclasses to add a layout item \a layoutItem + to a layout. + + It takes care of automatically reparenting graphics items, if needed. + + If \a layoutItem is a is already in a layout, it will remove it from that layout. + +*/ +void QGraphicsLayoutPrivate::addChildLayoutItem(QGraphicsLayoutItem *layoutItem) +{ + Q_Q(QGraphicsLayout); + if (QGraphicsLayoutItem *maybeLayout = layoutItem->parentLayoutItem()) { + if (maybeLayout->isLayout()) + removeLayoutItemFromLayout(static_cast<QGraphicsLayout*>(maybeLayout), layoutItem); + } + layoutItem->setParentLayoutItem(q); + if (layoutItem->isLayout()) { + if (QGraphicsItem *parItem = parentItem()) { + static_cast<QGraphicsLayout*>(layoutItem)->d_func()->reparentChildItems(parItem); + } + } else { + if (QGraphicsItem *item = layoutItem->graphicsItem()) { + QGraphicsItem *newParent = parentItem(); + QGraphicsItem *oldParent = item->parentItem(); + if (oldParent == newParent || !newParent) + return; + +#ifdef QT_DEBUG + if (oldParent && item->isWidget()) { + QGraphicsWidget *w = static_cast<QGraphicsWidget*>(item); + qWarning("QGraphicsLayout::addChildLayoutItem: %s \"%s\" in wrong parent; moved to correct parent", + w->metaObject()->className(), w->objectName().toLocal8Bit().constData()); + } +#endif + + item->setParentItem(newParent); + } + } +} + +void QGraphicsLayoutPrivate::activateRecursive(QGraphicsLayoutItem *item) +{ + if (item->isLayout()) { + QGraphicsLayout *layout = static_cast<QGraphicsLayout *>(item); + if (layout->d_func()->activated) + layout->invalidate(); + + for (int i = layout->count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *childItem = layout->itemAt(i); + if (childItem) + activateRecursive(childItem); + } + layout->d_func()->activated = true; + } +} + + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayout_p.h b/src/gui/graphicsview/qgraphicslayout_p.h new file mode 100644 index 0000000..a8895de --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayout_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSLAYOUT_P_H +#define QGRAPHICSLAYOUT_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include "qgraphicslayout.h" +#include "qgraphicslayoutitem_p.h" +#include <QtGui/qstyle.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsLayoutItem; +class QGraphicsWidget; + +#ifdef QT_DEBUG +inline bool qt_graphicsLayoutDebug() +{ + static int checked_env = -1; + if(checked_env == -1) + checked_env = !!qgetenv("QT_GRAPHICSLAYOUT_DEBUG").toInt(); + return checked_env; +} +#endif + +class Q_AUTOTEST_EXPORT QGraphicsLayoutPrivate : public QGraphicsLayoutItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsLayout) + +public: + QGraphicsLayoutPrivate() : QGraphicsLayoutItemPrivate(0, true), left(-1.0), top(-1.0), right(-1.0), bottom(-1.0), + activated(true) { } + + void reparentChildItems(QGraphicsItem *newParent); + void getMargin(qreal *result, qreal userMargin, QStyle::PixelMetric pm) const; + Qt::LayoutDirection visualDirection() const; + + void addChildLayoutItem(QGraphicsLayoutItem *item); + void activateRecursive(QGraphicsLayoutItem *item); + + qreal left, top, right, bottom; + bool activated; +}; + + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW + +#endif diff --git a/src/gui/graphicsview/qgraphicslayoutitem.cpp b/src/gui/graphicsview/qgraphicslayoutitem.cpp new file mode 100644 index 0000000..0ea7692 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayoutitem.cpp @@ -0,0 +1,852 @@ +/**************************************************************************** +** +** 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 "qgraphicslayout.h" +#include "qgraphicsscene.h" +#include "qgraphicslayoutitem.h" +#include "qgraphicslayoutitem_p.h" +#include "qwidget.h" + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +/* + COMBINE_SIZE() is identical to combineSize(), except that it + doesn't evaluate 'size' unless necessary. +*/ +#define COMBINE_SIZE(result, size) \ + do { \ + if ((result).width() < 0 || (result).height() < 0) \ + combineSize((result), (size)); \ + } while (false) + +static void combineSize(QSizeF &result, const QSizeF &size) +{ + if (result.width() < 0) + result.setWidth(size.width()); + if (result.height() < 0) + result.setHeight(size.height()); +} + +static void boundSize(QSizeF &result, const QSizeF &size) +{ + if (size.width() >= 0 && size.width() < result.width()) + result.setWidth(size.width()); + if (size.height() >= 0 && size.height() < result.height()) + result.setHeight(size.height()); +} + +static void expandSize(QSizeF &result, const QSizeF &size) +{ + if (size.width() >= 0 && size.width() > result.width()) + result.setWidth(size.width()); + if (size.height() >= 0 && size.height() > result.height()) + result.setHeight(size.height()); +} + +static void normalizeHints(qreal &minimum, qreal &preferred, qreal &maximum, qreal &descent) +{ + if (minimum >= 0 && maximum >= 0 && minimum > maximum) + minimum = maximum; + + if (preferred >= 0) { + if (minimum >= 0 && preferred < minimum) { + preferred = minimum; + } else if (maximum >= 0 && preferred > maximum) { + preferred = maximum; + } + } + + if (minimum >= 0 && descent > minimum) + descent = minimum; +} + +/*! + \internal +*/ +QGraphicsLayoutItemPrivate::QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *par, bool layout) + : parent(par), isLayout(layout), ownedByLayout(false), graphicsItem(0) +{ +} + +/*! + \internal +*/ +void QGraphicsLayoutItemPrivate::init() +{ + sizeHintCacheDirty = true; + sizePolicy = QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); +} + +/*! + \internal +*/ +QSizeF *QGraphicsLayoutItemPrivate::effectiveSizeHints(const QSizeF &constraint) const +{ + Q_Q(const QGraphicsLayoutItem); + if (!sizeHintCacheDirty && cachedConstraint == constraint) + return cachedSizeHints; + + for (int i = 0; i < Qt::NSizeHints; ++i) { + cachedSizeHints[i] = constraint; + combineSize(cachedSizeHints[i], userSizeHints[i]); + } + + QSizeF &minS = cachedSizeHints[Qt::MinimumSize]; + QSizeF &prefS = cachedSizeHints[Qt::PreferredSize]; + QSizeF &maxS = cachedSizeHints[Qt::MaximumSize]; + QSizeF &descentS = cachedSizeHints[Qt::MinimumDescent]; + + normalizeHints(minS.rwidth(), prefS.rwidth(), maxS.rwidth(), descentS.rwidth()); + normalizeHints(minS.rheight(), prefS.rheight(), maxS.rheight(), descentS.rheight()); + + // if the minimum, preferred and maximum sizes contradict each other + // (e.g. the minimum is larger than the maximum) we give priority to + // the maximum size, then the minimum size and finally the preferred size + COMBINE_SIZE(maxS, q->sizeHint(Qt::MaximumSize, maxS)); + combineSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + expandSize(maxS, prefS); + expandSize(maxS, minS); + boundSize(maxS, QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX)); + + COMBINE_SIZE(minS, q->sizeHint(Qt::MinimumSize, minS)); + expandSize(minS, QSizeF(0, 0)); + boundSize(minS, prefS); + boundSize(minS, maxS); + + COMBINE_SIZE(prefS, q->sizeHint(Qt::PreferredSize, prefS)); + expandSize(prefS, minS); + boundSize(prefS, maxS); + + // Not supported yet + // COMBINE_SIZE(descentS, q->sizeHint(Qt::MinimumDescent, constraint)); + + cachedConstraint = constraint; + sizeHintCacheDirty = false; + return cachedSizeHints; +} + + +/*! + \internal + + Returns the parent item of this layout, or 0 if this layout is + not installed on any widget. + + If this is the item that the layout is installed on, it will return "itself". + + If the layout is a sub-layout, this function returns the parent + widget of the parent layout. + + Note that it will traverse up the layout item hierarchy instead of just calling + QGraphicsItem::parentItem(). This is on purpose. + + \sa parent() +*/ +QGraphicsItem *QGraphicsLayoutItemPrivate::parentItem() const +{ + Q_Q(const QGraphicsLayoutItem); + + const QGraphicsLayoutItem *parent = q; + while (parent && parent->isLayout()) { + parent = parent->parentLayoutItem(); + } + return parent ? parent->graphicsItem() : 0; +} + +/*! + \class QGraphicsLayoutItem + \brief The QGraphicsLayoutItem class can be inherited to allow your custom + items to be managed by layouts. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsLayoutItem is an abstract class that defines a set of virtual + functions describing sizes, size policies, and size hints for any object + arranged by QGraphicsLayout. The API contains functions relevant + for both the item itself and for the user of the item as most of + QGraphicsLayoutItem's functions are also part of the subclass' public API. + + In most cases, existing layout-aware classes such as QGraphicsWidget and + QGraphicsLayout already provide the functionality you require. However, + subclassing these classes will enable you to create both graphical + elements that work well with layouts (QGraphicsWidget) or custom layouts + (QGraphicsLayout). + + \section1 Subclassing QGraphicsLayoutItem + + If you create a subclass of QGraphicsLayoutItem and reimplement its + virtual functions, you will enable the layout to resize and position your + item along with other QGraphicsLayoutItems including QGraphicsWidget + and QGraphicsLayout. + + You can start by reimplementing important functions: the protected + sizeHint() function, as well as the public setGeometry() + function. If you want your items to be aware of immediate geometry + changes, you can also reimplement updateGeometry(). + + The geometry, size hint, and size policy affect the item's size and + position. Calling setGeometry() will always resize and reposition the item + immediately. Normally, this function is called by QGraphicsLayout after + the layout has been activated, but it can also be called by the item's user + at any time. + + The sizeHint() function returns the item' minimum, preferred and maximum + size hints. You can override these properties by calling setMinimumSize(), + setPreferredSize() or setMaximumSize(). You can also use functions such as + setMinimumWidth() or setMaximumHeight() to set only the width or height + component if desired. + + The effectiveSizeHint() function, on the other hand, returns a size hint + for any given Qt::SizeHint, and guarantees that the returned size is bound + to the minimum and maximum sizes and size hints. You can set the item's + vertical and horizontal size policy by calling setSizePolicy(). The + sizePolicy property is used by the layout system to describe how this item + prefers to grow or shrink. + + \section1 Nesting QGraphicsLayoutItems + + QGraphicsLayoutItems can be nested within other QGraphicsLayoutItems, + similar to layouts that can contain sublayouts. This is done either by + passing a QGraphicsLayoutItem pointer to QGraphicsLayoutItem's + protected constructor, or by calling setParentLayoutItem(). The + parentLayoutItem() function returns a pointer to the item's layoutItem + parent. If the item's parent is 0 or if the the parent does not inherit + from QGraphicsItem, the parentLayoutItem() function then returns 0. + isLayout() returns true if the QGraphicsLayoutItem subclass is itself a + layout, or false otherwise. + + Qt uses QGraphicsLayoutItem to provide layout functionality in the + \l{The Graphics View Framework}, but in the future its use may spread + throughout Qt itself. + + \sa QGraphicsWidget, QGraphicsLayout, QGraphicsLinearLayout, + QGraphicsGridLayout +*/ + +/*! + Constructs the QGraphicsLayoutItem object. \a parent becomes the object's + parent. If \a isLayout is true the item is a layout, otherwise + \a isLayout is false. +*/ +QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItem *parent, bool isLayout) + : d_ptr(new QGraphicsLayoutItemPrivate(parent, isLayout)) +{ + Q_D(QGraphicsLayoutItem); + d->init(); + d->q_ptr = this; +} + +/*! + \internal +*/ +QGraphicsLayoutItem::QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd) + : d_ptr(&dd) +{ + Q_D(QGraphicsLayoutItem); + d->q_ptr = this; +} + +/*! + Destroys the QGraphicsLayoutItem object. +*/ +QGraphicsLayoutItem::~QGraphicsLayoutItem() +{ + QGraphicsLayoutItem *parentLI = parentLayoutItem(); + if (parentLI && parentLI->isLayout()) { + QGraphicsLayout *lay = static_cast<QGraphicsLayout*>(parentLI); + // this is not optimal + for (int i = lay->count() - 1; i >= 0; --i) { + if (lay->itemAt(i) == this) { + lay->removeAt(i); + break; + } + } + } + delete d_ptr; +} + +/*! + \fn virtual QSizeF QGraphicsLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const = 0; + + This pure virtual function returns the size hint for \a which of the + QGraphicsLayoutItem, using the width or height of \a constraint to + constrain the output. + + Reimplement this function in a subclass of QGraphicsLayoutItem to + provide the necessary size hints for your items. + + \sa effectiveSizeHint() +*/ + +/*! + Sets the size policy to \a policy. The size policy describes how the item + should grow horizontally and vertically when arranged in a layout. + + QGraphicsLayoutItem's default size policy is (QSizePolicy::Fixed, + QSizePolicy::Fixed, QSizePolicy::DefaultType), but it is common for + subclasses to change the default. For example, QGraphicsWidget defaults + to (QSizePolicy::Preferred, QSizePolicy::Preferred, + QSizePolicy::DefaultType). + + \sa sizePolicy(), QWidget::sizePolicy() +*/ +void QGraphicsLayoutItem::setSizePolicy(const QSizePolicy &policy) +{ + Q_D(QGraphicsLayoutItem); + if (d->sizePolicy == policy) + return; + d->sizePolicy = policy; + updateGeometry(); +} + +/*! + \overload + + This function is equivalent to calling + setSizePolicy(QSizePolicy(\a hPolicy, \a vPolicy, \a controlType)). + + \sa sizePolicy(), QWidget::sizePolicy() +*/ +void QGraphicsLayoutItem::setSizePolicy(QSizePolicy::Policy hPolicy, + QSizePolicy::Policy vPolicy, + QSizePolicy::ControlType controlType) +{ + setSizePolicy(QSizePolicy(hPolicy, vPolicy, controlType)); +} + +/*! + Returns the current size policy. + + \sa setSizePolicy(), QWidget::sizePolicy() +*/ +QSizePolicy QGraphicsLayoutItem::sizePolicy() const +{ + Q_D(const QGraphicsLayoutItem); + return d->sizePolicy; +} + +/*! + Sets the minimum size to \a size. This property overrides sizeHint() for + Qt::MinimumSize and ensures that effectiveSizeHint() will never return + a size smaller than \a size. In order to unset the minimum size, use an + invalid size. + + \sa minimumSize(), maximumSize(), preferredSize(), Qt::MinimumSize, + sizeHint(), setMinimumWidth(), setMinimumHeight() +*/ +void QGraphicsLayoutItem::setMinimumSize(const QSizeF &size) +{ + Q_D(QGraphicsLayoutItem); + if (size == d->userSizeHints[Qt::MinimumSize]) + return; + + d->userSizeHints[Qt::MinimumSize] = size; + updateGeometry(); +} + +/*! + \fn QGraphicsLayoutItem::setMinimumSize(qreal w, qreal h) + + This convenience function is equivalent to calling + setMinimumSize(QSizeF(\a w, \a h)). + + \sa minimumSize(), setMaximumSize(), setPreferredSize(), sizeHint() +*/ + +/*! + Returns the minimum size. + + \sa setMinimumSize(), preferredSize(), maximumSize(), Qt::MinimumSize, + sizeHint() +*/ +QSizeF QGraphicsLayoutItem::minimumSize() const +{ + return effectiveSizeHint(Qt::MinimumSize); +} + +/*! + Sets the minimum width to \a width. + + \sa minimumWidth(), setMinimumSize(), minimumSize() +*/ +void QGraphicsLayoutItem::setMinimumWidth(qreal width) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rwidth(); + if (width == userSizeHint) + return; + userSizeHint = width; + updateGeometry(); +} + +/*! + Sets the minimum height to \a height. + + \sa minimumHeight(), setMinimumSize(), minimumSize() +*/ +void QGraphicsLayoutItem::setMinimumHeight(qreal height) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::MinimumSize].rheight(); + if (height == userSizeHint) + return; + userSizeHint = height; + updateGeometry(); +} + + +/*! + Sets the preferred size to \a size. This property overrides sizeHint() for + Qt::PreferredSize and provides the default value for effectiveSizeHint(). + In order to unset the preferred size, use an invalid size. + + \sa preferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize, + sizeHint() +*/ +void QGraphicsLayoutItem::setPreferredSize(const QSizeF &size) +{ + Q_D(QGraphicsLayoutItem); + if (size == d->userSizeHints[Qt::PreferredSize]) + return; + + d->userSizeHints[Qt::PreferredSize] = size; + updateGeometry(); +} + +/*! + \fn QGraphicsLayoutItem::setPreferredSize(qreal w, qreal h) + + This convenience function is equivalent to calling + setPreferredSize(QSizeF(\a w, \a h)). + + \sa preferredSize(), setMaximumSize(), setMinimumSize(), sizeHint() +*/ + +/*! + Returns the preferred size. + + \sa setPreferredSize(), minimumSize(), maximumSize(), Qt::PreferredSize, + sizeHint() +*/ +QSizeF QGraphicsLayoutItem::preferredSize() const +{ + return effectiveSizeHint(Qt::PreferredSize); +} + +/*! + Sets the preferred height to \a height. + + \sa preferredWidth(), setPreferredSize(), preferredSize() +*/ +void QGraphicsLayoutItem::setPreferredHeight(qreal height) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rheight(); + if (height == userSizeHint) + return; + userSizeHint = height; + updateGeometry(); +} + +/*! + Sets the preferred width to \a width. + + \sa preferredHeight(), setPreferredSize(), preferredSize() +*/ +void QGraphicsLayoutItem::setPreferredWidth(qreal width) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::PreferredSize].rwidth(); + if (width == userSizeHint) + return; + userSizeHint = width; + updateGeometry(); +} + +/*! + Sets the maximum size to \a size. This property overrides sizeHint() for + Qt::MaximumSize and ensures that effectiveSizeHint() will never return a + size larger than \a size. In order to unset the maximum size, use an + invalid size. + + \sa maximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize, + sizeHint() +*/ +void QGraphicsLayoutItem::setMaximumSize(const QSizeF &size) +{ + Q_D(QGraphicsLayoutItem); + if (size == d->userSizeHints[Qt::MaximumSize]) + return; + + d->userSizeHints[Qt::MaximumSize] = size; + updateGeometry(); +} + +/*! + \fn QGraphicsLayoutItem::setMaximumSize(qreal w, qreal h) + + This convenience function is equivalent to calling + setMaximumSize(QSizeF(\a w, \a h)). + + \sa maximumSize(), setMinimumSize(), setPreferredSize(), sizeHint() +*/ + +/*! + Returns the maximum size. + + \sa setMaximumSize(), minimumSize(), preferredSize(), Qt::MaximumSize, + sizeHint() +*/ +QSizeF QGraphicsLayoutItem::maximumSize() const +{ + return effectiveSizeHint(Qt::MaximumSize); +} + +/*! + Sets the maximum width to \a width. + + \sa maximumWidth(), setMaximumSize(), maximumSize() +*/ +void QGraphicsLayoutItem::setMaximumWidth(qreal width) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rwidth(); + if (width == userSizeHint) + return; + userSizeHint = width; + updateGeometry(); +} + +/*! + Sets the maximum height to \a height. + + \sa maximumHeight(), setMaximumSize(), maximumSize() +*/ +void QGraphicsLayoutItem::setMaximumHeight(qreal height) +{ + Q_D(QGraphicsLayoutItem); + qreal &userSizeHint = d->userSizeHints[Qt::MaximumSize].rheight(); + if (height == userSizeHint) + return; + userSizeHint = height; + updateGeometry(); +} + +/*! + \fn qreal QGraphicsLayoutItem::minimumWidth() const + + Returns the minimum width. + + \sa setMinimumWidth(), setMinimumSize(), minimumSize() +*/ + +/*! + \fn qreal QGraphicsLayoutItem::minimumHeight() const + + Returns the minimum height. + + \sa setMinimumHeight(), setMinimumSize(), minimumSize() +*/ + +/*! + \fn qreal QGraphicsLayoutItem::preferredWidth() const + + Returns the preferred width. + + \sa setPreferredWidth(), setPreferredSize(), preferredSize() +*/ + +/*! + \fn qreal QGraphicsLayoutItem::preferredHeight() const + + Returns the preferred height. + + \sa setPreferredHeight(), setPreferredSize(), preferredSize() +*/ + +/*! + \fn qreal QGraphicsLayoutItem::maximumWidth() const + + Returns the maximum width. + + \sa setMaximumWidth(), setMaximumSize(), maximumSize() +*/ + +/*! + \fn qreal QGraphicsLayoutItem::maximumHeight() const + + Returns the maximum height. + + \sa setMaximumHeight(), setMaximumSize(), maximumSize() +*/ + +/*! + \fn virtual void QGraphicsLayoutItem::setGeometry(const QRectF &rect) = 0 + + This pure virtual function sets the geometry of the QGraphicsLayoutItem to + \a rect, which is in parent coordinates (e.g., the top-left corner of \a rect + is equivalent to the item's position in parent coordinates). + + Reimplement this function in a subclass of QGraphicsLayoutItem to enable + your item to receive geometry updates. + + If \a rect is outside of the bounds of minimumSize and maximumSize, it + will be adjusted to its closest size so that it is within the legal + bounds. + + \sa geometry() +*/ +void QGraphicsLayoutItem::setGeometry(const QRectF &rect) +{ + Q_D(QGraphicsLayoutItem); + QSizeF effectiveSize = rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize)) + .boundedTo(effectiveSizeHint(Qt::MaximumSize)); + d->geom = QRectF(rect.topLeft(), effectiveSize); +} + +/*! + \fn QRectF QGraphicsLayoutItem::geometry() const + + Returns the item's geometry (e.g., position and size) as a + QRectF. This function is equivalent to QRectF(pos(), size()). + + \sa setGeometry() +*/ +QRectF QGraphicsLayoutItem::geometry() const +{ + Q_D(const QGraphicsLayoutItem); + return d->geom; +} + +/*! + This virtual function provides the \a left, \a top, \a right and \a bottom + contents margins for this QGraphicsLayoutItem. The default implementation + assumes all contents margins are 0. The parameters point to values stored + in qreals. If any of the pointers is 0, that value will not be updated. + + \sa QGraphicsWidget::setContentsMargins() +*/ +void QGraphicsLayoutItem::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +{ + if (left) + *left = 0; + if (top) + *top = 0; + if (right) + *right = 0; + if (bottom) + *bottom = 0; +} + +/*! + Returns the contents rect in local coordinates. + + The contents rect defines the subrectangle used by an associated layout + when arranging subitems. This function is a convenience function that + adjusts the item's geometry() by its contents margins. Note that + getContentsMargins() is a virtual function that you can reimplement to + return the item's contents margins. + + \sa getContentsMargins(), geometry() +*/ +QRectF QGraphicsLayoutItem::contentsRect() const +{ + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + return QRectF(QPointF(), geometry().size()).adjusted(+left, +top, -right, -bottom); +} + +/*! + Returns the effective size hint for this QGraphicsLayoutItem. + + \a which is the size hint in question. + \a constraint is an optional argument that defines a special constrain + when calculating the effective size hint. By default, \a constraint is + QSizeF(-1, -1), which means there is no constraint to the size hint. + + If you want to specify the widget's size hint for a given width or height, + you can provide the fixed dimension in \a constraint. This is useful for + widgets that can grow only either vertically or horizontally, and need to + set either their width or their height to a special value. + + For example, a text paragraph item fit into a column width of 200 may + grow vertically. You can pass QSizeF(200, -1) as a constraint to get a + suitable minimum, preferred and maximum height). + + You can adjust the effective size hint by reimplementing sizeHint() + in a QGraphicsLayoutItem subclass, or by calling one of the following + functions: setMinimumSize(), setPreferredSize, or setMaximumSize() + (or a combination of both). + + This function caches each of the size hints and guarantees that + sizeHint() will be called only once for each value of \a which - unless + \a constraint is not specified and updateGeometry() has been called. + + \sa sizeHint() +*/ +QSizeF QGraphicsLayoutItem::effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + // ### should respect size policy??? + return d_ptr->effectiveSizeHints(constraint)[which]; +} + +/*! + This virtual function discards any cached size hint information. You + should always call this function if you change the return value of the + sizeHint() function. Subclasses must always call the base implementation + when reimplementing this function. + + \sa effectiveSizeHint() +*/ +void QGraphicsLayoutItem::updateGeometry() +{ + Q_D(QGraphicsLayoutItem); + d->sizeHintCacheDirty = true; +} + +/*! + Returns the parent of this QGraphicsLayoutItem, or 0 if there is no parent, + or if the parent does not inherit from QGraphicsLayoutItem + (QGraphicsLayoutItem is often used through multiple inheritance with + QObject-derived classes). + + \sa setParentLayoutItem() +*/ +QGraphicsLayoutItem *QGraphicsLayoutItem::parentLayoutItem() const +{ + return d_func()->parent; +} + +/*! + Sets the parent of this QGraphicsLayoutItem to \a parent. + + \sa parentLayoutItem() +*/ +void QGraphicsLayoutItem::setParentLayoutItem(QGraphicsLayoutItem *parent) +{ + d_func()->parent = parent; +} + +/*! + Returns true if this QGraphicsLayoutItem is a layout (e.g., is inherited + by an object that arranges other QGraphicsLayoutItem objects); otherwise + returns false. + + \sa QGraphicsLayout +*/ +bool QGraphicsLayoutItem::isLayout() const +{ + return d_func()->isLayout; +} + +/*! + Returns whether a layout should delete this item in its destructor. + If its true, then the layout will delete it. If its false, then it is + assumed that another object has the ownership of it, and the layout won't + delete this item. + + If the item inherits both QGraphicsItem and QGraphicsLayoutItem (such + as QGraphicsWidget does) the item is really part of two ownership + hierarchies. This property informs what the layout should do with its + child items when it is destructed. In the case of QGraphicsWidget, it + is preferred that when the layout is deleted it won't delete its children + (since they are also part of the graphics item hierarchy). + + By default this value is initialized to false in QGraphicsLayoutItem, + but it is overridden by QGraphicsLayout to return true. This is because + QGraphicsLayout is not normally part of the QGraphicsItem hierarchy, so the + parent layout should delete it. + Subclasses might override this default behaviour by calling + setOwnedByLayout(true). + + \sa setOwnedByLayout() +*/ +bool QGraphicsLayoutItem::ownedByLayout() const +{ + return d_func()->ownedByLayout; +} +/*! + Sets whether a layout should delete this item in its destructor or not. + \a ownership must be true to in order for the layout to delete it. + \sa ownedByLayout() +*/ +void QGraphicsLayoutItem::setOwnedByLayout(bool ownership) +{ + d_func()->ownedByLayout = ownership; +} + +/*! + * Returns the QGraphicsItem that this layout item represents. + * For QGraphicsWidget it will return itself. For custom items it can return an + * aggregated value. + * + * \sa setGraphicsItem() + */ +QGraphicsItem *QGraphicsLayoutItem::graphicsItem() const +{ + return d_func()->graphicsItem; +} + +/*! + * If the QGraphicsLayoutItem represents a QGraphicsItem, and it wants to take + * advantage of the automatic reparenting capabilities of QGraphicsLayout it + * should set this value. + * Note that if you delete \a item and not delete the layout item, you are + * responsible of calling setGraphicsItem(0) in order to avoid having a + * dangling pointer. + * + * \sa graphicsItem() + */ +void QGraphicsLayoutItem::setGraphicsItem(QGraphicsItem *item) +{ + d_func()->graphicsItem = item; +} + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslayoutitem.h b/src/gui/graphicsview/qgraphicslayoutitem.h new file mode 100644 index 0000000..31f5d90 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayoutitem.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSLAYOUTITEM_H +#define QGRAPHICSLAYOUTITEM_H + +#include <QtGui/qsizepolicy.h> +#include <QtGui/qevent.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsLayoutItemPrivate; +class QGraphicsItem; +class Q_GUI_EXPORT QGraphicsLayoutItem +{ +public: + QGraphicsLayoutItem(QGraphicsLayoutItem *parent = 0, bool isLayout = false); + virtual ~QGraphicsLayoutItem(); + + void setSizePolicy(const QSizePolicy &policy); + void setSizePolicy(QSizePolicy::Policy hPolicy, QSizePolicy::Policy vPolicy, QSizePolicy::ControlType controlType = QSizePolicy::DefaultType); + QSizePolicy sizePolicy() const; + + void setMinimumSize(const QSizeF &size); + inline void setMinimumSize(qreal w, qreal h); + QSizeF minimumSize() const; + void setMinimumWidth(qreal width); + inline qreal minimumWidth() const; + void setMinimumHeight(qreal height); + inline qreal minimumHeight() const; + + void setPreferredSize(const QSizeF &size); + inline void setPreferredSize(qreal w, qreal h); + QSizeF preferredSize() const; + void setPreferredWidth(qreal width); + inline qreal preferredWidth() const; + void setPreferredHeight(qreal height); + inline qreal preferredHeight() const; + + void setMaximumSize(const QSizeF &size); + inline void setMaximumSize(qreal w, qreal h); + QSizeF maximumSize() const; + void setMaximumWidth(qreal width); + inline qreal maximumWidth() const; + void setMaximumHeight(qreal height); + inline qreal maximumHeight() const; + + virtual void setGeometry(const QRectF &rect); + QRectF geometry() const; + virtual void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; + QRectF contentsRect() const; + + QSizeF effectiveSizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + + virtual void updateGeometry(); //### rename to sizeHintChanged() + + QGraphicsLayoutItem *parentLayoutItem() const; + void setParentLayoutItem(QGraphicsLayoutItem *parent); + + bool isLayout() const; + // ###Qt5: Make automatic reparenting work regardless of item/object/widget type. + QGraphicsItem *graphicsItem() const; + bool ownedByLayout() const; + +protected: + void setGraphicsItem(QGraphicsItem *item); + void setOwnedByLayout(bool ownedByLayout); + QGraphicsLayoutItem(QGraphicsLayoutItemPrivate &dd); + + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const = 0; + QGraphicsLayoutItemPrivate *d_ptr; + +private: + QSizeF *effectiveSizeHints(const QSizeF &constraint) const; + Q_DECLARE_PRIVATE(QGraphicsLayoutItem) + + friend class QGraphicsLayout; +}; + +inline void QGraphicsLayoutItem::setMinimumSize(qreal aw, qreal ah) +{ setMinimumSize(QSizeF(aw, ah)); } +inline void QGraphicsLayoutItem::setPreferredSize(qreal aw, qreal ah) +{ setPreferredSize(QSizeF(aw, ah)); } +inline void QGraphicsLayoutItem::setMaximumSize(qreal aw, qreal ah) +{ setMaximumSize(QSizeF(aw, ah)); } + +inline qreal QGraphicsLayoutItem::minimumWidth() const +{ return effectiveSizeHint(Qt::MinimumSize).width(); } +inline qreal QGraphicsLayoutItem::minimumHeight() const +{ return effectiveSizeHint(Qt::MinimumSize).height(); } + +inline qreal QGraphicsLayoutItem::preferredWidth() const +{ return effectiveSizeHint(Qt::PreferredSize).width(); } +inline qreal QGraphicsLayoutItem::preferredHeight() const +{ return effectiveSizeHint(Qt::PreferredSize).height(); } + +inline qreal QGraphicsLayoutItem::maximumWidth() const +{ return effectiveSizeHint(Qt::MaximumSize).width(); } +inline qreal QGraphicsLayoutItem::maximumHeight() const +{ return effectiveSizeHint(Qt::MaximumSize).height(); } + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/graphicsview/qgraphicslayoutitem_p.h b/src/gui/graphicsview/qgraphicslayoutitem_p.h new file mode 100644 index 0000000..fab0f39 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslayoutitem_p.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSLAYOUTITEM_P_H +#define QGRAPHICSLAYOUTITEM_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QSizeF> +#include <QtGui/QSizePolicy> + +QT_BEGIN_NAMESPACE + +class QGraphicsLayoutItem; +class Q_AUTOTEST_EXPORT QGraphicsLayoutItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsLayoutItem) +public: + virtual ~QGraphicsLayoutItemPrivate() {} + QGraphicsLayoutItemPrivate(QGraphicsLayoutItem *parent, bool isLayout); + void init(); + QSizeF *effectiveSizeHints(const QSizeF &constraint) const; + QGraphicsItem *parentItem() const; + + QSizePolicy sizePolicy; + QGraphicsLayoutItem *parent; + + QSizeF userSizeHints[Qt::NSizeHints]; + mutable QSizeF cachedSizeHints[Qt::NSizeHints]; + mutable QSizeF cachedConstraint; + + mutable quint32 sizeHintCacheDirty : 1; + quint32 isLayout : 1; + quint32 ownedByLayout : 1; + + QGraphicsLayoutItem *q_ptr; + QRectF geom; + QGraphicsItem *graphicsItem; +}; + +QT_END_NAMESPACE + +#endif //QGRAPHICSLAYOUTITEM_P_H + diff --git a/src/gui/graphicsview/qgraphicslinearlayout.cpp b/src/gui/graphicsview/qgraphicslinearlayout.cpp new file mode 100644 index 0000000..6a2d456 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslinearlayout.cpp @@ -0,0 +1,547 @@ +/**************************************************************************** +** +** 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 QGraphicsLinearLayout + \brief The QGraphicsLinearLayout class provides a horizontal or vertical + layout for managing widgets in Graphics View. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + The default orientation for a linear layout is Qt::Horizontal. You can + choose a vertical orientation either by calling setOrientation(), or by + passing Qt::Vertical to QGraphicsLinearLayout's constructor. + + The most common way to use QGraphicsLinearLayout is to construct an object + on the heap with no parent, add widgets and layouts by calling addItem(), + and finally assign the layout to a widget by calling + QGraphicsWidget::setLayout(). + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicslinearlayout.cpp 0 + + You can add widgets, layouts, stretches (addStretch(), insertStretch() or + setStretchFactor()), and spacings (setItemSpacing()) to a linear + layout. The layout takes ownership of the items. In some cases when the layout + item also inherits from QGraphicsItem (such as QGraphicsWidget) there will be a + ambiguity in ownership because the layout item belongs to two ownership hierarchies. + See the documentation of QGraphicsLayoutItem::setOwnedByLayout() how to handle + this. + You can access each item in the layout by calling count() and itemAt(). Calling + removeAt() or removeItem() will remove an item from the layout, without + destroying it. + + \section1 Size Hints and Size Policies in QGraphicsLinearLayout + + QGraphicsLinearLayout respects each item's size hints and size policies, + and when the layout contains more space than the items can fill, each item + is arranged according to the layout's alignment for that item. You can set + an alignment for each item by calling setAlignment(), and check the + alignment for any item by calling alignment(). By default, items are + centered both vertically and horizontally. + + \section1 Spacing within QGraphicsLinearLayout + + Between the items, the layout distributes some space. The actual amount of + space depends on the managed widget's current style, but the common + spacing is 4. You can also set your own spacing by calling setSpacing(), + and get the current spacing value by calling spacing(). If you want to + configure individual spacing for your items, you can call setItemSpacing(). + + \section1 Stretch Factor in QGraphicsLinearLayout + + You can assign a stretch factor to each item to control how much space it + will get compared to the other items. By default, two identical widgets + arranged in a linear layout will have the same size, but if the first + widget has a stretch factor of 1 and the second widget has a stretch + factor of 2, the first widget will get 1/3 of the available space, and the + second will get 2/3. + + QGraphicsLinearLayout calculates the distribution of sizes by adding up + the stretch factors of all items, and then dividing the available space + accordingly. The default stretch factor is 0 for all items; a factor of 0 + means the item does not have any defined stretch factor; effectively this + is the same as setting the stretch factor to 1. The stretch factor only + applies to the available space in the lengthwise direction of the layout + (following its orientation). If you want to control both the item's + horizontal and vertical stretch, you can use QGraphicsGridLayout instead. + + \section1 QGraphicsLinearLayout Compared to Other Layouts + + QGraphicsLinearLayout is very similar to QVBoxLayout and QHBoxLayout, but + in contrast to these classes, it is used to manage QGraphicsWidget and + QGraphicsLayout instead of QWidget and QLayout. + + \sa QGraphicsGridLayout, QGraphicsWidget +*/ + +#include "qapplication.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qwidget.h" +#include "qgraphicslayout_p.h" +#include "qgraphicslayoutitem.h" +#include "qgraphicslinearlayout.h" +#include "qgraphicswidget.h" +#include "qgridlayoutengine_p.h" +#ifdef QT_DEBUG +#include <QtCore/qdebug.h> +#endif + +QT_BEGIN_NAMESPACE + +class QGraphicsLinearLayoutPrivate : public QGraphicsLayoutPrivate +{ +public: + QGraphicsLinearLayoutPrivate(Qt::Orientation orientation) : orientation(orientation) { } + + void removeGridItem(QGridLayoutItem *gridItem); + QLayoutStyleInfo styleInfo() const; + void fixIndex(int *index) const; + int gridRow(int index) const; + int gridColumn(int index) const; + + Qt::Orientation orientation; + QGridLayoutEngine engine; +}; + +void QGraphicsLinearLayoutPrivate::removeGridItem(QGridLayoutItem *gridItem) +{ + int index = gridItem->firstRow(orientation); + engine.removeItem(gridItem); + engine.removeRow(index, orientation); +} + +void QGraphicsLinearLayoutPrivate::fixIndex(int *index) const +{ + int count = engine.rowCount(orientation); + if (uint(*index) > uint(count)) + *index = count; +} + +int QGraphicsLinearLayoutPrivate::gridRow(int index) const +{ + if (orientation == Qt::Horizontal) + return 0; + return int(qMin(uint(index), uint(engine.rowCount()))); +} + +int QGraphicsLinearLayoutPrivate::gridColumn(int index) const +{ + if (orientation == Qt::Vertical) + return 0; + return int(qMin(uint(index), uint(engine.columnCount()))); +} + +QLayoutStyleInfo QGraphicsLinearLayoutPrivate::styleInfo() const +{ + static QWidget *wid = 0; + if (!wid) + wid = new QWidget; + QGraphicsItem *item = parentItem(); + QStyle *style = (item && item->isWidget()) ? static_cast<QGraphicsWidget*>(item)->style() : qApp->style(); + return QLayoutStyleInfo(style, wid); +} + +/*! + Constructs a QGraphicsLinearLayout instance. You can pass the + \a orientation for the layout, either horizontal or vertical, and + \a parent is passed to QGraphicsLayout's constructor. +*/ +QGraphicsLinearLayout::QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent) + : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(orientation), parent) +{ +} + +/*! + Constructs a QGraphicsLinearLayout instance using Qt::Horizontal + orientation. \a parent is passed to QGraphicsLayout's constructor. +*/ +QGraphicsLinearLayout::QGraphicsLinearLayout(QGraphicsLayoutItem *parent) + : QGraphicsLayout(*new QGraphicsLinearLayoutPrivate(Qt::Horizontal), parent) +{ +} + +/*! + Destroys the QGraphicsLinearLayout object. +*/ +QGraphicsLinearLayout::~QGraphicsLinearLayout() +{ + for (int i = count() - 1; i >= 0; --i) { + QGraphicsLayoutItem *item = itemAt(i); + // The following lines can be removed, but this removes the item + // from the layout more efficiently than the implementation of + // ~QGraphicsLayoutItem. + removeAt(i); + if (item) { + item->setParentLayoutItem(0); + if (item->ownedByLayout()) + delete item; + } + } +} + +/*! + Change the layout orientation to \a orientation. Changing the layout + orientation will automatically invalidate the layout. + + \sa orientation() +*/ +void QGraphicsLinearLayout::setOrientation(Qt::Orientation orientation) +{ + Q_D(QGraphicsLinearLayout); + if (orientation != d->orientation) { + d->engine.transpose(); + d->orientation = orientation; + invalidate(); + } +} + +/*! + Returns the layout orientation. + \sa setOrientation() + */ +Qt::Orientation QGraphicsLinearLayout::orientation() const +{ + Q_D(const QGraphicsLinearLayout); + return d->orientation; +} + +/*! + \fn void QGraphicsLinearLayout::addItem(QGraphicsLayoutItem *item) + + This convenience function is equivalent to calling + insertItem(-1, \a item). +*/ + +/*! + \fn void QGraphicsLinearLayout::addStretch(int stretch) + + This convenience function is equivalent to calling + insertStretch(-1, \a stretch). +*/ + +/*! + Inserts \a item into the layout at \a index, or before any item that is + currently at \a index. + + \sa addItem(), itemAt(), insertStretch(), setItemSpacing() +*/ +void QGraphicsLinearLayout::insertItem(int index, QGraphicsLayoutItem *item) +{ + Q_D(QGraphicsLinearLayout); + if (!item) { + qWarning("QGraphicsLinearLayout::insertItem: cannot insert null item"); + return; + } + d->addChildLayoutItem(item); + + Q_ASSERT(item); + d->fixIndex(&index); + d->engine.insertRow(index, d->orientation); + new QGridLayoutItem(&d->engine, item, d->gridRow(index), d->gridColumn(index)); + invalidate(); +} + +/*! + Inserts a stretch of \a stretch at \a index, or before any item that is + currently at \a index. + + \sa addStretch(), setStretchFactor(), setItemSpacing(), insertItem() +*/ +void QGraphicsLinearLayout::insertStretch(int index, int stretch) +{ + Q_D(QGraphicsLinearLayout); + d->fixIndex(&index); + d->engine.insertRow(index, d->orientation); + d->engine.setRowStretchFactor(index, stretch, d->orientation); + invalidate(); +} + +/*! + Removes \a item from the layout without destroying it. Ownership of + \a item is transferred to the caller. + + \sa removeAt(), insertItem() +*/ +void QGraphicsLinearLayout::removeItem(QGraphicsLayoutItem *item) +{ + Q_D(QGraphicsLinearLayout); + if (QGridLayoutItem *gridItem = d->engine.findLayoutItem(item)) { + item->setParentLayoutItem(0); + d->removeGridItem(gridItem); + delete gridItem; + invalidate(); + } +} + +/*! + Removes the item at \a index without destroying it. Ownership of the item + is transferred to the caller. + + \sa removeItem(), insertItem() +*/ +void QGraphicsLinearLayout::removeAt(int index) +{ + Q_D(QGraphicsLinearLayout); + if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index))) { + if (QGraphicsLayoutItem *layoutItem = gridItem->layoutItem()) + layoutItem->setParentLayoutItem(0); + d->removeGridItem(gridItem); + delete gridItem; + invalidate(); + } +} + +/*! + Sets the layout's spacing to \a spacing. Spacing refers to the + vertical and horizontal distances between items. + + \sa setItemSpacing(), setStretchFactor(), QGraphicsGridLayout::setSpacing() +*/ +void QGraphicsLinearLayout::setSpacing(qreal spacing) +{ + Q_D(QGraphicsLinearLayout); + if (spacing < 0) { + qWarning("QGraphicsLinearLayout::setSpacing: invalid spacing %g", spacing); + return; + } + d->engine.setSpacing(spacing, Qt::Horizontal | Qt::Vertical); + invalidate(); +} + +/*! + Returns the layout's spacing. Spacing refers to the + vertical and horizontal distances between items. + + \sa setSpacing() + */ +qreal QGraphicsLinearLayout::spacing() const +{ + Q_D(const QGraphicsLinearLayout); + return d->engine.spacing(d->styleInfo(), d->orientation); +} + +/*! + Sets the spacing after item at \a index to \a spacing. +*/ +void QGraphicsLinearLayout::setItemSpacing(int index, qreal spacing) +{ + Q_D(QGraphicsLinearLayout); + d->engine.setRowSpacing(index, spacing, d->orientation); + invalidate(); +} +/*! + Returns the spacing after item at \a index. +*/ +qreal QGraphicsLinearLayout::itemSpacing(int index) const +{ + Q_D(const QGraphicsLinearLayout); + return d->engine.rowSpacing(index, d->orientation); +} + +/*! + Sets the stretch factor for \a item to \a stretch. If an item's stretch + factor changes, this function will invalidate the layout. + + Setting \a stretch to 0 removes the stretch factor from the item, and is + effectively equivalent to setting \a stretch to 1. + + \sa stretchFactor() +*/ +void QGraphicsLinearLayout::setStretchFactor(QGraphicsLayoutItem *item, int stretch) +{ + Q_D(QGraphicsLinearLayout); + if (!item) { + qWarning("QGraphicsLinearLayout::setStretchFactor: cannot assign" + " a stretch factor to a null item"); + return; + } + if (stretchFactor(item) == stretch) + return; + d->engine.setStretchFactor(item, stretch, d->orientation); + invalidate(); +} + +/*! + Returns the stretch factor for \a item. The default stretch factor is 0, + meaning that the item has no assigned stretch factor. + + \sa setStretchFactor() +*/ +int QGraphicsLinearLayout::stretchFactor(QGraphicsLayoutItem *item) const +{ + Q_D(const QGraphicsLinearLayout); + if (!item) { + qWarning("QGraphicsLinearLayout::setStretchFactor: cannot return" + " a stretch factor for a null item"); + return 0; + } + return d->engine.stretchFactor(item, d->orientation); +} + +/*! + Sets the alignment of \a item to \a alignment. If \a item's alignment + changes, the layout is automatically invalidated. + + \sa alignment(), invalidate() +*/ +void QGraphicsLinearLayout::setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment) +{ + Q_D(QGraphicsLinearLayout); + if (this->alignment(item) == alignment) + return; + d->engine.setAlignment(item, alignment); + invalidate(); +} + +/*! + Returns the alignment for \a item. The default alignment is + Qt::AlignCenter. + + The alignment decides how the item is positioned within its assigned space + in the case where there's more space available in the layout than the + widgets can occupy. + + \sa setAlignment() +*/ +Qt::Alignment QGraphicsLinearLayout::alignment(QGraphicsLayoutItem *item) const +{ + Q_D(const QGraphicsLinearLayout); + return d->engine.alignment(item); +} + +#if 0 // ### +QSizePolicy::ControlTypes QGraphicsLinearLayout::controlTypes(LayoutSide side) const +{ + return d->engine.controlTypes(side); +} +#endif + +/*! + \reimp +*/ +int QGraphicsLinearLayout::count() const +{ + Q_D(const QGraphicsLinearLayout); + return d->engine.rowCount(d->orientation); +} + +/*! + \reimp +*/ +QGraphicsLayoutItem *QGraphicsLinearLayout::itemAt(int index) const +{ + Q_D(const QGraphicsLinearLayout); + QGraphicsLayoutItem *item = 0; + if (QGridLayoutItem *gridItem = d->engine.itemAt(d->gridRow(index), d->gridColumn(index))) + item = gridItem->layoutItem(); + return item; +} + +/*! + \reimp +*/ +void QGraphicsLinearLayout::setGeometry(const QRectF &rect) +{ + Q_D(QGraphicsLinearLayout); + QGraphicsLayout::setGeometry(rect); + QRectF effectiveRect = geometry(); + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + Qt::LayoutDirection visualDir = d->visualDirection(); + d->engine.setVisualDirection(visualDir); + if (visualDir == Qt::RightToLeft) + qSwap(left, right); + effectiveRect.adjust(+left, +top, -right, -bottom); +#ifdef QT_DEBUG + if (qt_graphicsLayoutDebug()) { + static int counter = 0; + qDebug() << counter++ << "QGraphicsLinearLayout::setGeometry - " << rect; + dump(1); + } +#endif + d->engine.setGeometries(d->styleInfo(), effectiveRect); +#ifdef QT_DEBUG + if (qt_graphicsLayoutDebug()) { + qDebug() << "post dump"; + dump(1); + } +#endif +} + +/*! + \reimp +*/ +QSizeF QGraphicsLinearLayout::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_D(const QGraphicsLinearLayout); + qreal left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + return d->engine.sizeHint(d->styleInfo(), which , constraint) + QSizeF(left + right, top + bottom); +} + +/*! + \reimp +*/ +void QGraphicsLinearLayout::invalidate() +{ + Q_D(QGraphicsLinearLayout); + d->engine.invalidate(); + QGraphicsLayout::invalidate(); +} + +#ifdef QT_DEBUG +void QGraphicsLinearLayout::dump(int indent) const +{ + if (qt_graphicsLayoutDebug()) { + Q_D(const QGraphicsLinearLayout); + qDebug("%*s%s layout", indent, "", + d->orientation == Qt::Horizontal ? "Horizontal" : "Vertical"); + d->engine.dump(indent + 1); + } +} +#endif + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicslinearlayout.h b/src/gui/graphicsview/qgraphicslinearlayout.h new file mode 100644 index 0000000..05ad325 --- /dev/null +++ b/src/gui/graphicsview/qgraphicslinearlayout.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSLINEARLAYOUT_H +#define QGRAPHICSLINEARLAYOUT_H + +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qgraphicslayout.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsLinearLayoutPrivate; + +class Q_GUI_EXPORT QGraphicsLinearLayout : public QGraphicsLayout +{ +public: + QGraphicsLinearLayout(QGraphicsLayoutItem *parent = 0); + QGraphicsLinearLayout(Qt::Orientation orientation, QGraphicsLayoutItem *parent = 0); + virtual ~QGraphicsLinearLayout(); + + void setOrientation(Qt::Orientation orientation); + Qt::Orientation orientation() const; + + inline void addItem(QGraphicsLayoutItem *item) { insertItem(-1, item); } + inline void addStretch(int stretch = 1) { insertStretch(-1, stretch); } + + void insertItem(int index, QGraphicsLayoutItem *item); + void insertStretch(int index, int stretch = 1); + + void removeItem(QGraphicsLayoutItem *item); + void removeAt(int index); + + void setSpacing(qreal spacing); + qreal spacing() const; + void setItemSpacing(int index, qreal spacing); + qreal itemSpacing(int index) const; + + void setStretchFactor(QGraphicsLayoutItem *item, int stretch); + int stretchFactor(QGraphicsLayoutItem *item) const; + + void setAlignment(QGraphicsLayoutItem *item, Qt::Alignment alignment); + Qt::Alignment alignment(QGraphicsLayoutItem *item) const; + + void setGeometry(const QRectF &rect); + + int count() const; + QGraphicsLayoutItem *itemAt(int index) const; + + void invalidate(); + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + +#if 0 // ### + Q5SizePolicy::ControlTypes controlTypes(LayoutSide side) const; +#endif + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + +protected: +#if 0 + QSize contentsSizeHint(Qt::SizeHint which, const QSize &constraint = QSize()) const; +#endif + +private: + Q_DISABLE_COPY(QGraphicsLinearLayout) + Q_DECLARE_PRIVATE(QGraphicsLinearLayout) +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/gui/graphicsview/qgraphicsproxywidget.cpp b/src/gui/graphicsview/qgraphicsproxywidget.cpp new file mode 100644 index 0000000..1d2721b --- /dev/null +++ b/src/gui/graphicsview/qgraphicsproxywidget.cpp @@ -0,0 +1,1494 @@ +/**************************************************************************** +** +** 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 "qgraphicslayout.h" +#include "qgraphicsproxywidget.h" +#include "private/qgraphicsproxywidget_p.h" +#include "private/qwidget_p.h" +#include "private/qapplication_p.h" + +#include <QtCore/qdebug.h> +#include <QtGui/qevent.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qgraphicssceneevent.h> +#include <QtGui/qlayout.h> +#include <QtGui/qpainter.h> +#include <QtGui/qstyleoption.h> +#include <QtGui/qgraphicsview.h> + +QT_BEGIN_NAMESPACE + +//#define GRAPHICSPROXYWIDGET_DEBUG + +/*! + \class QGraphicsProxyWidget + \brief The QGraphicsProxyWidget class provides a proxy layer for embedding + a QWidget in a QGraphicsScene. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsProxyWidget embeds QWidget-based widgets, for example, a + QPushButton, QFontComboBox, or even QFileDialog, into + QGraphicsScene. It forwards events between the two objects and + translates between QWidget's integer-based geometry and + QGraphicsWidget's qreal-based geometry. QGraphicsProxyWidget + supports all core features of QWidget, including tab focus, + keyboard input, Drag & Drop, and popups. You can also embed + complex widgets, e.g., widgets with subwidgets. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 0 + + QGraphicsProxyWidget takes care of automatically embedding popup children + of embedded widgets through creating a child proxy for each popup. This + means that when an embedded QComboBox shows its popup list, a new + QGraphicsProxyWidget is created automatically, embedding the popup, and + positioning it correctly. + + \section1 Embedding a Widget with QGraphicsProxyWidget + + There are two ways to embed a widget using QGraphicsProxyWidget. The most + common way is to pass a widget pointer to QGraphicsScene::addWidget() + together with any relevant \l Qt::WindowFlags. This function returns a + pointer to a QGraphicsProxyWidget. You can then choose to reparent or + position either the proxy, or the embedded widget itself. + + For example, in the code snippet below, we embed a group box into the proxy: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 1 + + The image below is the output obtained with its contents margin and + contents rect labeled. + + \image qgraphicsproxywidget-embed.png + + Alternatively, you can start by creating a new QGraphicsProxyWidget item, + and then call setWidget() to embed a QWidget later. The widget() function + returns a pointer to the embedded widget. QGraphicsProxyWidget shares + ownership with QWidget, so if either of the two widgets are destroyed, the + other widget will be automatically destroyed as well. + + \section1 Synchronizing Widget States + + QGraphicsProxyWidget keeps its state in sync with the embedded widget. For + example, if the proxy is hidden or disabled, the embedded widget will be + hidden or disabled as well, and vice versa. When the widget is embedded by + calling addWidget(), QGraphicsProxyWidget copies the state from the widget + into the proxy, and after that, the two will stay synchronized where + possible. By default, when you embed a widget into a proxy, both the widget + and the proxy will be visible because a QGraphicsWidget is visible when + created (you do not have to call show()). If you explicitly hide the + embedded widget, the proxy will also become invisible. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsproxywidget.cpp 2 + + QGraphicsProxyWidget maintains symmetry for the following states: + + \table + \header \o QWidget state \o QGraphicsProxyWidget state \o Notes + \row \o QWidget::enabled + \o QGraphicsProxyWidget::enabled + \o + \row \o QWidget::visible + \o QGraphicsProxyWidget::visible + \o The explicit state is also symmetric. + \row \o QWidget::geometry + \o QGraphicsProxyWidget::geometry + \o Geometry is only guaranteed to be symmetric while + the embedded widget is visible. + \row \o QWidget::layoutDirection + \o QGraphicsProxyWidget::layoutDirection + \o + \row \o QWidget::style + \o QGraphicsProxyWidget::style + \o + \row \o QWidget::palette + \o QGraphicsProxyWidget::palette + \o + \row \o QWidget::font + \o QGraphicsProxyWidget::font + \o + \row \o QWidget::cursor + \o QGraphicsProxyWidget::cursor + \o The embedded widget overrides the proxy widget + cursor. The proxy cursor changes depending on + which embedded subwidget is currently under the + mouse. + \row \o QWidget::sizeHint() + \o QGraphicsProxyWidget::sizeHint() + \o All size hint functionality from the embedded + widget is forwarded by the proxy. + \row \o QWidget::getContentsMargins() + \o QGraphicsProxyWidget::getContentsMargins() + \o Updated once by setWidget(). + \row \o QWidget::windowTitle + \o QGraphicsProxyWidget::windowTitle + \o Updated once by setWidget(). + \endtable + + \note QGraphicsScene keeps the embedded widget in a special state that + prevents it from disturbing other widgets (both embedded and not embedded) + while the widget is embedded. In this state, the widget may differ slightly + in behavior from when it is not embedded. + + \sa QGraphicsScene::addWidget(), QGraphicsWidget +*/ + +extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::init() +{ + Q_Q(QGraphicsProxyWidget); + q->setFocusPolicy(Qt::WheelFocus); + q->setAcceptDrops(true); +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event) +{ + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setPos(event->pos()); + mouseEvent.setScreenPos(event->screenPos()); + mouseEvent.setButton(Qt::NoButton); + mouseEvent.setButtons(0); + mouseEvent.setModifiers(event->modifiers()); + sendWidgetMouseEvent(&mouseEvent); + event->setAccepted(mouseEvent.isAccepted()); +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event) +{ + if (!event || !widget || !widget->isVisible()) + return; + Q_Q(QGraphicsProxyWidget); + + // Find widget position and receiver. + QPointF pos = event->pos(); + QPointer<QWidget> alienWidget = widget->childAt(pos.toPoint()); + QPointer<QWidget> receiver = alienWidget ? alienWidget : widget; + + if (QWidgetPrivate::nearestGraphicsProxyWidget(receiver) != q) + return; //another proxywidget will handle the events + + // Translate QGraphicsSceneMouse events to QMouseEvents. + QEvent::Type type = QEvent::None; + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + type = QEvent::MouseButtonPress; + if (!embeddedMouseGrabber) + embeddedMouseGrabber = receiver; + else + receiver = embeddedMouseGrabber; + break; + case QEvent::GraphicsSceneMouseRelease: + type = QEvent::MouseButtonRelease; + if (embeddedMouseGrabber) + receiver = embeddedMouseGrabber; + break; + case QEvent::GraphicsSceneMouseDoubleClick: + type = QEvent::MouseButtonDblClick; + if (!embeddedMouseGrabber) + embeddedMouseGrabber = receiver; + else + receiver = embeddedMouseGrabber; + break; + case QEvent::GraphicsSceneMouseMove: + type = QEvent::MouseMove; + if (embeddedMouseGrabber) + receiver = embeddedMouseGrabber; + break; + default: + Q_ASSERT_X(false, "QGraphicsProxyWidget", "internal error"); + break; + } + + if (!lastWidgetUnderMouse) { + QApplicationPrivate::dispatchEnterLeave(embeddedMouseGrabber ? embeddedMouseGrabber : widget, 0); + lastWidgetUnderMouse = widget; + } + + // Map event position from us to the receiver + pos = mapToReceiver(pos, receiver); + + // Send mouse event. + QMouseEvent *mouseEvent = QMouseEvent::createExtendedMouseEvent(type, pos, + receiver->mapToGlobal(pos.toPoint()), event->button(), + event->buttons(), event->modifiers()); + + QWidget *embeddedMouseGrabberPtr = (QWidget *)embeddedMouseGrabber; + QApplicationPrivate::sendMouseEvent(receiver, mouseEvent, alienWidget, widget, + &embeddedMouseGrabberPtr, lastWidgetUnderMouse); + embeddedMouseGrabber = embeddedMouseGrabberPtr; + + // Handle enter/leave events when last button is released from mouse + // grabber child widget. + if (embeddedMouseGrabber && type == QEvent::MouseButtonRelease && !event->buttons()) { + Q_Q(QGraphicsProxyWidget); + if (q->rect().contains(event->pos()) && q->acceptsHoverEvents()) + lastWidgetUnderMouse = alienWidget ? alienWidget : widget; + else // released on the frame our outside the item, or doesn't accept hover events. + lastWidgetUnderMouse = 0; + + QApplicationPrivate::dispatchEnterLeave(lastWidgetUnderMouse, embeddedMouseGrabber); + embeddedMouseGrabber = 0; + +#ifndef QT_NO_CURSOR + // ### Restore the cursor, don't override it. + if (!lastWidgetUnderMouse) + q->unsetCursor(); +#endif + } + + event->setAccepted(mouseEvent->isAccepted()); + delete mouseEvent; +} + +void QGraphicsProxyWidgetPrivate::sendWidgetKeyEvent(QKeyEvent *event) +{ + Q_Q(QGraphicsProxyWidget); + if (!event || !widget || !widget->isVisible()) + return; + + QPointer<QWidget> receiver = widget->focusWidget(); + if (!receiver) + receiver = widget; + Q_ASSERT(receiver); + + do { + bool res = QApplication::sendEvent(receiver, event); + if ((res && event->isAccepted()) || (q->isWindow() && receiver == widget)) + break; + receiver = receiver->parentWidget(); + } while (receiver); +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason) +{ + QFocusEvent event(QEvent::FocusOut, reason); + QPointer<QWidget> widgetGuard = widget; + QApplication::sendEvent(widget, &event); + if (widgetGuard && event.isAccepted()) + QApplication::sendEvent(widget->style(), &event); +} + +/*! + \internal + + Reimplemented from QGraphicsItemPrivate. ### Qt 5: Move impl to + reimplementation QGraphicsProxyWidget::inputMethodQuery(). +*/ +QVariant QGraphicsProxyWidgetPrivate::inputMethodQueryHelper(Qt::InputMethodQuery query) const +{ + Q_Q(const QGraphicsProxyWidget); + if (!widget || !q->hasFocus()) + return QVariant(); + + QWidget *focusWidget = widget->focusWidget(); + if (!focusWidget) + focusWidget = widget; + QVariant v = focusWidget->inputMethodQuery(query); + QPointF focusWidgetPos = q->subWidgetRect(focusWidget).topLeft(); + switch (v.type()) { + case QVariant::RectF: + v = v.toRectF().translated(focusWidgetPos); + break; + case QVariant::PointF: + v = v.toPointF() + focusWidgetPos; + break; + case QVariant::Rect: + v = v.toRect().translated(focusWidgetPos.toPoint()); + break; + case QVariant::Point: + v = v.toPoint() + focusWidgetPos.toPoint(); + break; + default: + break; + } + return v; +} + +/*! + \internal +*/ +QWidget *QGraphicsProxyWidgetPrivate::findFocusChild(QWidget *child, bool next) const +{ + if (!widget) + return 0; + + // Run around the focus chain until we find a widget that can take tab focus. + if (!child) { + child = next ? (QWidget *)widget : widget->d_func()->focus_prev; + } else { + child = next ? child->d_func()->focus_next : child->d_func()->focus_prev; + if ((next && child == widget) || (!next && child == widget->d_func()->focus_prev)) { + return 0; + } + } + + QWidget *oldChild = child; + do { + if (child->isEnabled() + && child->isVisibleTo(widget) + && (child->focusPolicy() & Qt::TabFocus)) { + return child; + } + child = next ? child->d_func()->focus_next : child->d_func()->focus_prev; + } while (child != oldChild && !(next && child == widget) && !(!next && child == widget->d_func()->focus_prev)); + return 0; +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::_q_removeWidgetSlot() +{ + Q_Q(QGraphicsProxyWidget); + widget = 0; + delete q; +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::updateWidgetGeometryFromProxy() +{ +} + +/*! + \internal +*/ +void QGraphicsProxyWidgetPrivate::updateProxyGeometryFromWidget() +{ + Q_Q(QGraphicsProxyWidget); + if (!widget) + return; + + QRectF widgetGeometry = widget->geometry(); + QWidget *parentWidget = widget->parentWidget(); + if (widget->isWindow()) { + QGraphicsProxyWidget *proxyParent = 0; + if (parentWidget && (proxyParent = qobject_cast<QGraphicsProxyWidget *>(q->parentWidget()))) { + // Nested window proxy (e.g., combobox popup), map widget to the + // parent widget's global coordinates, and map that to the parent + // proxy's child coordinates. + widgetGeometry.moveTo(proxyParent->subWidgetRect(parentWidget).topLeft() + + parentWidget->mapFromGlobal(widget->pos())); + } + } + + // Adjust to size hint if the widget has never been resized. + if (!widget->size().isValid()) + widgetGeometry.setSize(widget->sizeHint()); + + // Assign new geometry. + posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + q->setGeometry(widgetGeometry); + posChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode; +} + +/*! + \internal + + Embeds \a subWin as a subwindow of this proxy widget. \a subWin must be a top-level + widget and a descendant of the widget managed by this proxy. A separate subproxy + will be created as a child of this proxy widget to manage \a subWin. +*/ +void QGraphicsProxyWidgetPrivate::embedSubWindow(QWidget *subWin) +{ + QWExtra *extra; + if (!((extra = subWin->d_func()->extra) && extra->proxyWidget)) { + QGraphicsProxyWidget *subProxy = new QGraphicsProxyWidget(q_func()); + subProxy->d_func()->setWidget_helper(subWin, false); + } +} + +/*! + \internal + + Removes ("unembeds") \a subWin and deletes the proxy holder item. This can + happen when QWidget::setParent() reparents the embedded window out of + "embedded space". +*/ +void QGraphicsProxyWidgetPrivate::unembedSubWindow(QWidget *subWin) +{ + foreach (QGraphicsItem *child, children) { + if (child->isWidget()) { + if (QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(child))) { + if (proxy->widget() == subWin) { + proxy->setWidget(0); + scene->removeItem(proxy); + delete proxy; + return; + } + } + } + } +} + +bool QGraphicsProxyWidgetPrivate::isProxyWidget() const +{ + return true; +} + +/*! + \internal +*/ +QPointF QGraphicsProxyWidgetPrivate::mapToReceiver(const QPointF &pos, const QWidget *receiver) const +{ + QPointF p = pos; + // Map event position from us to the receiver, preserving its + // precision (don't use QWidget::mapFrom here). + while (receiver && receiver != widget) { + p -= QPointF(receiver->pos()); + receiver = receiver->parentWidget(); + } + return p; +} + +/*! + Constructs a new QGraphicsProxy widget. \a parent and \a wFlags are passed + to QGraphicsItem's constructor. +*/ +QGraphicsProxyWidget::QGraphicsProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QGraphicsWidget(*new QGraphicsProxyWidgetPrivate, parent, 0, wFlags) +{ + Q_D(QGraphicsProxyWidget); + d->init(); +} + +/*! + Destroys the proxy widget and any embedded widget. +*/ +QGraphicsProxyWidget::~QGraphicsProxyWidget() +{ + Q_D(QGraphicsProxyWidget); + if (d->widget) { + QObject::disconnect(d->widget, SIGNAL(destroyed()), this, SLOT(_q_removeWidgetSlot())); + delete d->widget; + } +} + +/*! + Embeds \a widget into this proxy widget. The embedded widget must reside + exclusively either inside or outside of Graphics View. You cannot embed a + widget as long as it is is visible elsewhere in the UI, at the same time. + + \a widget must be a top-level widget whose parent is 0. + + When the widget is embedded, its state (e.g., visible, enabled, geometry, + size hints) is copied into the proxy widget. If the embedded widget is + explicitly hidden or disabled, the proxy widget will become explicitly + hidden or disabled after embedding is complete. The class documentation + has a full overview over the shared state. + + After this function returns, QGraphicsProxyWidget will keep its state + synchronized with that of \a widget whenever possible. + + If a widget is already embedded by this proxy when this function is + called, that widget will first be automatically unembedded. Passing 0 for + the \a widget argument will only unembed the widget, and the ownership of + the currently embedded widget will be passed on to the caller. + Every child widget that are embedded will also be embedded and their proxy + widget destroyed. + + Note that widgets with the Qt::WA_PaintOnScreen widget attribute + set and widgets that wrap an external application or controller + cannot be embedded. Examples are QGLWidget and QAxWidget. + + \sa widget() +*/ +void QGraphicsProxyWidget::setWidget(QWidget *widget) +{ + Q_D(QGraphicsProxyWidget); + d->setWidget_helper(widget, true); +} + +void QGraphicsProxyWidgetPrivate::setWidget_helper(QWidget *newWidget, bool autoShow) +{ + Q_Q(QGraphicsProxyWidget); + if (newWidget == widget) + return; + if (widget) { + QObject::disconnect(widget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot())); + widget->removeEventFilter(q); + widget->setAttribute(Qt::WA_DontShowOnScreen, false); + widget->d_func()->extra->proxyWidget = 0; + resolveFont(inheritedFontResolveMask); + resolvePalette(inheritedPaletteResolveMask); + widget->update(); + + foreach (QGraphicsItem *child, q->childItems()) { + if (child->d_ptr->isProxyWidget()) { + QGraphicsProxyWidget *childProxy = static_cast<QGraphicsProxyWidget *>(child); + QWidget * parent = childProxy->widget(); + while (parent->parentWidget() != 0) { + if (parent == widget) + break; + parent = parent->parentWidget(); + } + if (!childProxy->widget() || parent != widget) + continue; + childProxy->setWidget(0); + delete childProxy; + } + } + + widget = 0; +#ifndef QT_NO_CURSOR + q->unsetCursor(); +#endif + q->setAcceptHoverEvents(false); + if (!newWidget) + q->update(); + } + if (!newWidget) + return; + if (!newWidget->isWindow()) { + QWExtra *extra = newWidget->parentWidget()->d_func()->extra; + if (!extra || !extra->proxyWidget) { + qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p " + "which is not a toplevel widget, and is not a child of an embedded widget", newWidget); + return; + } + } + + // Register this proxy within the widget's private. + // ### This is a bit backdoorish + QWExtra *extra = newWidget->d_func()->extra; + if (!extra) { + newWidget->d_func()->createExtra(); + extra = newWidget->d_func()->extra; + } + QGraphicsProxyWidget **proxyWidget = &extra->proxyWidget; + if (*proxyWidget) { + if (*proxyWidget != q) { + qWarning("QGraphicsProxyWidget::setWidget: cannot embed widget %p" + "; already embedded", newWidget); + } + return; + } + *proxyWidget = q; + + newWidget->setAttribute(Qt::WA_DontShowOnScreen); + newWidget->ensurePolished(); + // Do not wait for this widget to close before the app closes ### + // shouldn't this widget inherit the attribute? + newWidget->setAttribute(Qt::WA_QuitOnClose, false); + q->setAcceptHoverEvents(true); + + if (newWidget->testAttribute(Qt::WA_NoSystemBackground)) + q->setAttribute(Qt::WA_NoSystemBackground); + if (newWidget->testAttribute(Qt::WA_OpaquePaintEvent)) + q->setAttribute(Qt::WA_OpaquePaintEvent); + + widget = newWidget; + + // Changes only go from the widget to the proxy. + enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + posChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + sizeChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + + if ((autoShow && !newWidget->testAttribute(Qt::WA_WState_ExplicitShowHide)) || !newWidget->testAttribute(Qt::WA_WState_Hidden)) { + newWidget->show(); + } + + // Copy the state from the widget onto the proxy. +#ifndef QT_NO_CURSOR + if (newWidget->testAttribute(Qt::WA_SetCursor)) + q->setCursor(widget->cursor()); +#endif + Qt::WFlags flags = newWidget->windowFlags(); + if (newWidget->windowType() == Qt::Window) + flags &= ~Qt::Window; + q->setWindowFlags(flags); + q->setEnabled(newWidget->isEnabled()); + q->setVisible(newWidget->isVisible()); + q->setLayoutDirection(newWidget->layoutDirection()); + if (newWidget->testAttribute(Qt::WA_SetStyle)) + q->setStyle(widget->style()); + + resolveFont(inheritedFontResolveMask); + resolvePalette(inheritedPaletteResolveMask); + + if (!newWidget->testAttribute(Qt::WA_Resized)) + newWidget->adjustSize(); + + int left, top, right, bottom; + newWidget->getContentsMargins(&left, &top, &right, &bottom); + q->setContentsMargins(left, top, right, bottom); + q->setWindowTitle(newWidget->windowTitle()); + + // size policies and constraints.. + q->setSizePolicy(newWidget->sizePolicy()); + QSize sz = newWidget->minimumSize(); + q->setMinimumSize(sz.isNull() ? QSizeF() : QSizeF(sz)); + sz = newWidget->maximumSize(); + q->setMaximumSize(sz.isNull() ? QSizeF() : QSizeF(sz)); + + updateProxyGeometryFromWidget(); + + // Hook up the event filter to keep the state up to date. + newWidget->installEventFilter(q); + QObject::connect(newWidget, SIGNAL(destroyed()), q, SLOT(_q_removeWidgetSlot())); + + // Changes no longer go only from the widget to the proxy. + enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + posChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode; +} + +/*! + Returns a pointer to the embedded widget. + + \sa setWidget() +*/ +QWidget *QGraphicsProxyWidget::widget() const +{ + Q_D(const QGraphicsProxyWidget); + return d->widget; +} + +/*! + Returns the rectangle for \a widget, which must be a descendant of + widget(), or widget() itself, in this proxy item's local coordinates. + + If no widget is embedded, \a widget is 0, or \a widget is not a + descendant of the embedded widget, this function returns an empty QRectF. + + \sa widget() +*/ +QRectF QGraphicsProxyWidget::subWidgetRect(const QWidget *widget) const +{ + Q_D(const QGraphicsProxyWidget); + if (!widget || !d->widget) + return QRectF(); + if (d->widget == widget || d->widget->isAncestorOf(widget)) + return QRectF(widget->mapTo(d->widget, QPoint(0, 0)), widget->size()); + return QRectF(); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::setGeometry(const QRectF &rect) +{ + Q_D(QGraphicsProxyWidget); + bool proxyResizesWidget = !d->posChangeMode && !d->sizeChangeMode; + if (proxyResizesWidget) { + d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + d->sizeChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + } + QGraphicsWidget::setGeometry(rect); + if (proxyResizesWidget) { + d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + d->sizeChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + } +} + +/*! + \reimp +*/ +QVariant QGraphicsProxyWidget::itemChange(GraphicsItemChange change, + const QVariant &value) +{ + Q_D(QGraphicsProxyWidget); + + switch (change) { + case ItemPositionChange: + // The item's position is either changed directly on the proxy, in + // which case the position change should propagate to the widget, + // otherwise it happens as a side effect when filtering QEvent::Move. + if (!d->posChangeMode) + d->posChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + break; + case ItemPositionHasChanged: + // Move the internal widget if we're in widget-to-proxy + // mode. Otherwise the widget has already moved. + if (d->widget && d->posChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode) + d->widget->move(value.toPoint()); + if (d->posChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode) + d->posChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + break; + case ItemVisibleChange: + if (!d->visibleChangeMode) + d->visibleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + break; + case ItemVisibleHasChanged: + if (d->widget && d->visibleChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode) + d->widget->setVisible(isVisible()); + if (d->visibleChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode) + d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + break; + case ItemEnabledChange: + if (!d->enabledChangeMode) + d->enabledChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + break; + case ItemEnabledHasChanged: + if (d->widget && d->enabledChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode) + d->widget->setEnabled(isEnabled()); + if (d->enabledChangeMode == QGraphicsProxyWidgetPrivate::ProxyToWidgetMode) + d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + break; + default: + break; + } + return QGraphicsWidget::itemChange(change, value); +} + +/*! + \reimp +*/ +bool QGraphicsProxyWidget::event(QEvent *event) +{ + Q_D(QGraphicsProxyWidget); + if (!d->widget) + return QGraphicsWidget::event(event); + + switch (event->type()) { + case QEvent::StyleChange: + // Propagate style changes to the embedded widget. + if (!d->styleChangeMode) { + d->styleChangeMode = QGraphicsProxyWidgetPrivate::ProxyToWidgetMode; + d->widget->setStyle(style()); + d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + } + break; + case QEvent::FontChange: { + // Propagate to widget. + QWidgetPrivate *wd = d->widget->d_func(); + int mask = d->font.resolve() | d->inheritedFontResolveMask; + wd->inheritedFontResolveMask = mask; + wd->resolveFont(); + break; + } + case QEvent::PaletteChange: { + // Propagate to widget. + QWidgetPrivate *wd = d->widget->d_func(); + int mask = d->palette.resolve() | d->inheritedPaletteResolveMask; + wd->inheritedPaletteResolveMask = mask; + wd->resolvePalette(); + break; + } + case QEvent::InputMethod: { + // Forward input method events if the focus widget enables + // input methods. + // ### Qt 4.5: this code must also go into a reimplementation + // of inputMethodEvent(). + QWidget *focusWidget = d->widget->focusWidget(); + if (focusWidget->testAttribute(Qt::WA_InputMethodEnabled)) + QApplication::sendEvent(focusWidget, event); + break; + } + case QEvent::ShortcutOverride: { + QWidget *focusWidget = d->widget->focusWidget(); + while (focusWidget) { + QApplication::sendEvent(focusWidget, event); + if (event->isAccepted()) + return true; + focusWidget = focusWidget->parentWidget(); + } + return false; + } + case QEvent::KeyPress: { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { + if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + QWidget *focusWidget = d->widget->focusWidget(); + while (focusWidget) { + bool res = QApplication::sendEvent(focusWidget, event); + if ((res && event->isAccepted()) || (isWindow() && focusWidget == d->widget)) { + event->accept(); + break; + } + focusWidget = focusWidget->parentWidget(); + } + return true; + } + } + break; + } + default: + break; + } + return QGraphicsWidget::event(event); +} + +/*! + \reimp +*/ +bool QGraphicsProxyWidget::eventFilter(QObject *object, QEvent *event) +{ + Q_D(QGraphicsProxyWidget); + + if (object == d->widget) { + switch (event->type()) { + case QEvent::LayoutRequest: + updateGeometry(); + break; + case QEvent::Resize: + // If the widget resizes itself, we resize the proxy too. + // Prevent feed-back by checking the geometry change mode. + if (!d->sizeChangeMode) + d->updateProxyGeometryFromWidget(); + break; + case QEvent::Move: + // If the widget moves itself, we move the proxy too. Prevent + // feed-back by checking the geometry change mode. + if (!d->posChangeMode) + d->updateProxyGeometryFromWidget(); + break; + case QEvent::Hide: + case QEvent::Show: + // If the widget toggles its visible state, the proxy will follow. + if (!d->visibleChangeMode) { + d->visibleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + setVisible(event->type() == QEvent::Show); + d->visibleChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + } + break; + case QEvent::EnabledChange: + // If the widget toggles its enabled state, the proxy will follow. + if (!d->enabledChangeMode) { + d->enabledChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + setEnabled(d->widget->isEnabled()); + d->enabledChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + } + break; + case QEvent::StyleChange: + // Propagate style changes to the proxy. + if (!d->styleChangeMode) { + d->styleChangeMode = QGraphicsProxyWidgetPrivate::WidgetToProxyMode; + setStyle(d->widget->style()); + d->styleChangeMode = QGraphicsProxyWidgetPrivate::NoMode; + } + break; + default: + break; + } + } + return QGraphicsWidget::eventFilter(object, event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::showEvent(QShowEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::hideEvent(QHideEvent *event) +{ + Q_UNUSED(event); +} + +#ifndef QT_NO_CONTEXTMENU +/*! + \reimp +*/ +void QGraphicsProxyWidget::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) +{ + Q_D(QGraphicsProxyWidget); + if (!event || !d->widget || !d->widget->isVisible()) + return; + + // Find widget position and receiver. + QPointF pos = event->pos(); + QPointer<QWidget> alienWidget = d->widget->childAt(pos.toPoint()); + QPointer<QWidget> receiver = alienWidget ? alienWidget : d->widget; + + // Map event position from us to the receiver + pos = d->mapToReceiver(pos, receiver); + + // Send mouse event. ### Doesn't propagate the event. + QContextMenuEvent contextMenuEvent(QContextMenuEvent::Reason(event->reason()), + pos.toPoint(), receiver->mapToGlobal(pos.toPoint()), event->modifiers()); + QApplication::sendEvent(receiver, &contextMenuEvent); + + event->setAccepted(contextMenuEvent.isAccepted()); +} +#endif // QT_NO_CONTEXTMENU + +/*! + \reimp +*/ +void QGraphicsProxyWidget::dragEnterEvent(QGraphicsSceneDragDropEvent *event) +{ +#ifdef QT_NO_DRAGANDDROP + Q_UNUSED(event); +#else + Q_D(QGraphicsProxyWidget); + if (!d->widget) + return; + + QDragEnterEvent proxyDragEnter(event->pos().toPoint(), event->dropAction(), event->mimeData(), event->buttons(), event->modifiers()); + proxyDragEnter.setAccepted(event->isAccepted()); + QApplication::sendEvent(d->widget, &proxyDragEnter); + event->setAccepted(proxyDragEnter.isAccepted()); + if (proxyDragEnter.isAccepted()) // we discard answerRect + event->setDropAction(proxyDragEnter.dropAction()); +#endif +} +/*! + \reimp +*/ +void QGraphicsProxyWidget::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_UNUSED(event); +#ifndef QT_NO_DRAGANDDROP + Q_D(QGraphicsProxyWidget); + if (!d->widget || !d->dragDropWidget) + return; + QDragLeaveEvent proxyDragLeave; + QApplication::sendEvent(d->dragDropWidget, &proxyDragLeave); + d->dragDropWidget = 0; +#endif +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +{ +#ifdef QT_NO_DRAGANDDROP + Q_UNUSED(event); +#else + Q_D(QGraphicsProxyWidget); + if (!d->widget) + return; + QPointF p = event->pos(); + event->ignore(); + QPointer<QWidget> subWidget = d->widget->childAt(p.toPoint()); + QPointer<QWidget> receiver = subWidget ? subWidget : d->widget; + bool eventDelivered = false; + for (; receiver; receiver = receiver->parentWidget()) { + if (!receiver->isEnabled() || !receiver->acceptDrops()) + continue; + // Map event position from us to the receiver + QPoint receiverPos = d->mapToReceiver(p, receiver).toPoint(); + if (receiver != d->dragDropWidget) { + // Try to enter before we leave + QDragEnterEvent dragEnter(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); + dragEnter.setDropAction(event->proposedAction()); + QApplication::sendEvent(receiver, &dragEnter); + event->setAccepted(dragEnter.isAccepted()); + event->setDropAction(dragEnter.dropAction()); + if (!event->isAccepted()) { + // propagate to the parent widget + continue; + } + + d->lastDropAction = event->dropAction(); + + if (d->dragDropWidget) { + QDragLeaveEvent dragLeave; + QApplication::sendEvent(d->dragDropWidget, &dragLeave); + } + d->dragDropWidget = receiver; + } + + QDragMoveEvent dragMove(receiverPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); + event->setDropAction(d->lastDropAction); + QApplication::sendEvent(receiver, &dragMove); + event->setAccepted(dragMove.isAccepted()); + event->setDropAction(dragMove.dropAction()); + if (event->isAccepted()) + d->lastDropAction = event->dropAction(); + eventDelivered = true; + break; + } + + if (!eventDelivered) { + if (d->dragDropWidget) { + // Leave the last drag drop item + QDragLeaveEvent dragLeave; + QApplication::sendEvent(d->dragDropWidget, &dragLeave); + d->dragDropWidget = 0; + } + // Propagate + event->setDropAction(Qt::IgnoreAction); + } +#endif +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::dropEvent(QGraphicsSceneDragDropEvent *event) +{ +#ifdef QT_NO_DRAGANDDROP + Q_UNUSED(event); +#else + Q_D(QGraphicsProxyWidget); + if (d->widget && d->dragDropWidget) { + QPoint widgetPos = d->mapToReceiver(event->pos(), d->dragDropWidget).toPoint(); + QDropEvent dropEvent(widgetPos, event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); + QApplication::sendEvent(d->dragDropWidget, &dropEvent); + event->setAccepted(dropEvent.isAccepted()); + d->dragDropWidget = 0; + } +#endif +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::hoverEnterEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); + Q_D(QGraphicsProxyWidget); + // If hoverMove was compressed away, make sure we update properly here. + if (d->lastWidgetUnderMouse) { + QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse); + d->lastWidgetUnderMouse = 0; + } +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::hoverMoveEvent"; +#endif + // Ignore events on the window frame. + if (!d->widget || !rect().contains(event->pos())) { + if (d->lastWidgetUnderMouse) { + QApplicationPrivate::dispatchEnterLeave(0, d->lastWidgetUnderMouse); + d->lastWidgetUnderMouse = 0; + } + return; + } + + d->embeddedMouseGrabber = 0; + d->sendWidgetMouseEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::grabMouseEvent(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::ungrabMouseEvent(QEvent *event) +{ + Q_D(QGraphicsProxyWidget); + Q_UNUSED(event); + d->embeddedMouseGrabber = 0; +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::mouseMoveEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::mouseMoveEvent"; +#endif + d->sendWidgetMouseEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::mousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::mousePressEvent"; +#endif + d->sendWidgetMouseEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::mouseDoubleClickEvent"; +#endif + d->sendWidgetMouseEvent(event); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QGraphicsProxyWidget::wheelEvent(QGraphicsSceneWheelEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::wheelEvent"; +#endif + if (!d->widget) + return; + + QPointF pos = event->pos(); + QPointer<QWidget> receiver = d->widget->childAt(pos.toPoint()); + if (!receiver) + receiver = d->widget; + + // Map event position from us to the receiver + pos = d->mapToReceiver(pos, receiver); + + // Send mouse event. + QWheelEvent wheelEvent(pos.toPoint(), event->screenPos(), event->delta(), + event->buttons(), event->modifiers(), event->orientation()); + QPointer<QWidget> focusWidget = d->widget->focusWidget(); + extern bool qt_sendSpontaneousEvent(QObject *, QEvent *); + qt_sendSpontaneousEvent(receiver, &wheelEvent); + event->setAccepted(wheelEvent.isAccepted()); + + // ### Remove, this should be done by proper focusIn/focusOut events. + if (focusWidget && !focusWidget->hasFocus()) { + focusWidget->update(); + focusWidget = d->widget->focusWidget(); + if (focusWidget && focusWidget->hasFocus()) + focusWidget->update(); + } +} +#endif + +/*! + \reimp +*/ +void QGraphicsProxyWidget::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::mouseReleaseEvent"; +#endif + d->sendWidgetMouseEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::keyPressEvent(QKeyEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::keyPressEvent"; +#endif + d->sendWidgetKeyEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QGraphicsProxyWidget); +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::keyReleaseEvent"; +#endif + d->sendWidgetKeyEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::focusInEvent(QFocusEvent *event) +{ +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::focusInEvent"; +#endif + Q_D(QGraphicsProxyWidget); + + if (d->focusFromWidgetToProxy) { + // Prevent recursion when the proxy autogains focus through the + // embedded widget calling setFocus(). ### Could be done with event + // filter on FocusIn instead? + return; + } + + switch (event->reason()) { + case Qt::TabFocusReason: { + if (QWidget *focusChild = d->findFocusChild(0, true)) + focusChild->setFocus(event->reason()); + break; + } + case Qt::BacktabFocusReason: + if (QWidget *focusChild = d->findFocusChild(0, false)) + focusChild->setFocus(event->reason()); + break; + default: + if (d->widget && d->widget->focusWidget()) { + d->widget->focusWidget()->setFocus(event->reason()); + return; + } + break; + } +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::focusOutEvent(QFocusEvent *event) +{ +#ifdef GRAPHICSPROXYWIDGET_DEBUG + qDebug() << "QGraphicsProxyWidget::focusOutEvent"; +#endif + Q_D(QGraphicsProxyWidget); + if (d->widget) { + // We need to explicitly remove subfocus from the embedded widget's + // focus widget. + if (QWidget *focusWidget = d->widget->focusWidget()) + d->removeSubFocusHelper(focusWidget, event->reason()); + } +} + +/*! + \reimp +*/ +bool QGraphicsProxyWidget::focusNextPrevChild(bool next) +{ + Q_D(QGraphicsProxyWidget); + if (!d->widget || !d->scene) + return QGraphicsWidget::focusNextPrevChild(next); + + Qt::FocusReason reason = next ? Qt::TabFocusReason : Qt::BacktabFocusReason; + QWidget *lastFocusChild = d->widget->focusWidget(); + if (QWidget *newFocusChild = d->findFocusChild(lastFocusChild, next)) { + newFocusChild->setFocus(reason); + return true; + } + + return QGraphicsWidget::focusNextPrevChild(next); +} + +/*! + \reimp +*/ +QSizeF QGraphicsProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_D(const QGraphicsProxyWidget); + if (!d->widget) + return QGraphicsWidget::sizeHint(which, constraint); + + QSizeF sh; + switch (which) { + case Qt::PreferredSize: + if (QLayout *l = d->widget->layout()) + sh = l->sizeHint(); + else + sh = d->widget->sizeHint(); + break; + case Qt::MinimumSize: + if (QLayout *l = d->widget->layout()) + sh = l->minimumSize(); + else + sh = d->widget->minimumSizeHint(); + break; + case Qt::MaximumSize: + if (QLayout *l = d->widget->layout()) + sh = l->maximumSize(); + else + sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + break; + case Qt::MinimumDescent: + sh = constraint; + break; + default: + break; + } + return sh; +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::resizeEvent(QGraphicsSceneResizeEvent *event) +{ + Q_D(QGraphicsProxyWidget); + if (d->widget) { + if (d->sizeChangeMode != QGraphicsProxyWidgetPrivate::WidgetToProxyMode) + d->widget->resize(event->newSize().toSize()); + } + QGraphicsWidget::resizeEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsProxyWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_D(QGraphicsProxyWidget); + Q_UNUSED(widget); + if (!d->widget || !d->widget->isVisible()) + return; + + // Filter out repaints on the window frame. + const QRect exposedWidgetRect = (option->exposedRect & rect()).toRect(); + if (exposedWidgetRect.isEmpty()) + return; + + // Disable QPainter's default pen being cosmetic. This allows widgets and + // styles to follow Qt's existing defaults without getting ugly cosmetic + // lines when scaled. + bool restore = !(painter->renderHints() & QPainter::NonCosmeticDefaultPen); + painter->setRenderHints(QPainter::NonCosmeticDefaultPen, true); + + d->widget->render(painter, exposedWidgetRect.topLeft(), exposedWidgetRect); + + // Restore the render hints if necessary. + if (restore) + painter->setRenderHints(QPainter::NonCosmeticDefaultPen, false); +} + +/*! + \reimp +*/ +int QGraphicsProxyWidget::type() const +{ + return Type; +} + +/*! + \since 4.5 + + Creates a proxy widget for the given \a child of the widget + contained in this proxy. + + This function makes it possible to aquire proxies for + non top-level widgets. For instance, you can embed a dialog, + and then transform only one of its widgets. + + If the widget is already embedded, return the existing proxy widget. + + \sa newProxyWidget(), QGraphicsScene::addWidget() +*/ +QGraphicsProxyWidget *QGraphicsProxyWidget::createProxyForChildWidget(QWidget *child) +{ + QGraphicsProxyWidget *proxy = child->graphicsProxyWidget(); + if (proxy) + return proxy; + if (!child->parentWidget()) { + qWarning("QGraphicsProxyWidget::createProxyForChildWidget: top-level widget not in a QGraphicsScene"); + return 0; + } + + QGraphicsProxyWidget *parentProxy = createProxyForChildWidget(child->parentWidget()); + if (!parentProxy) + return 0; + + if (!QMetaObject::invokeMethod(parentProxy, "newProxyWidget", Qt::DirectConnection, + Q_RETURN_ARG(QGraphicsProxyWidget*, proxy), Q_ARG(const QWidget*, child))) + return 0; + proxy->setParent(parentProxy); + proxy->setWidget(child); + return proxy; +} + +/*! + \fn QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *child) + \since 4.5 + + Creates a proxy widget for the given \a child of the widget contained in this + proxy. + + You should not call this function directly; use + QGraphicsProxyWidget::createProxyForChildWidget() instead. + + This function is a fake virtual slot that you can reimplement in + your subclass in order to control how new proxy widgets are + created. The default implementation returns a proxy created with + the QGraphicsProxyWidget() constructor with this proxy widget as + the parent. + + \sa createProxyForChildWidget() +*/ +QGraphicsProxyWidget *QGraphicsProxyWidget::newProxyWidget(const QWidget *) +{ + return new QGraphicsProxyWidget(this); +} + + + +QT_END_NAMESPACE + +#include "moc_qgraphicsproxywidget.cpp" + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsproxywidget.h b/src/gui/graphicsview/qgraphicsproxywidget.h new file mode 100644 index 0000000..b2c3c8f --- /dev/null +++ b/src/gui/graphicsview/qgraphicsproxywidget.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSPROXYWIDGET_H +#define QGRAPHICSPROXYWIDGET_H + +#include <QtGui/qgraphicswidget.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsProxyWidgetPrivate; + +class Q_GUI_EXPORT QGraphicsProxyWidget : public QGraphicsWidget +{ + Q_OBJECT +public: + QGraphicsProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + ~QGraphicsProxyWidget(); + + void setWidget(QWidget *widget); + QWidget *widget() const; + + QRectF subWidgetRect(const QWidget *widget) const; + + void setGeometry(const QRectF &rect); + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + enum { + Type = 12 + }; + int type() const; + + QGraphicsProxyWidget *createProxyForChildWidget(QWidget *child); + +protected: + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + + bool event(QEvent *event); + bool eventFilter(QObject *object, QEvent *event); + + void showEvent(QShowEvent *event); + void hideEvent(QHideEvent *event); + +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); +#endif + + void dragEnterEvent(QGraphicsSceneDragDropEvent *event); + void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); + void dragMoveEvent(QGraphicsSceneDragDropEvent *event); + void dropEvent(QGraphicsSceneDragDropEvent *event); + + void hoverEnterEvent(QGraphicsSceneHoverEvent *event); + void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + void grabMouseEvent(QEvent *event); + void ungrabMouseEvent(QEvent *event); + + void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + void mousePressEvent(QGraphicsSceneMouseEvent *event); + void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QGraphicsSceneWheelEvent *event); +#endif + + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + bool focusNextPrevChild(bool next); + // ### Qt 4.5: + // QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + // void inputMethodEvent(QInputMethodEvent *event); + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + void resizeEvent(QGraphicsSceneResizeEvent *event); + +protected Q_SLOTS: + QGraphicsProxyWidget *newProxyWidget(const QWidget *); + +private: + Q_DISABLE_COPY(QGraphicsProxyWidget) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr, QGraphicsProxyWidget) + Q_PRIVATE_SLOT(d_func(), void _q_removeWidgetSlot()) + + friend class QWidget; + friend class QWidgetPrivate; + friend class QGraphicsItem; +}; + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/gui/graphicsview/qgraphicsproxywidget_p.h b/src/gui/graphicsview/qgraphicsproxywidget_p.h new file mode 100644 index 0000000..5985eed --- /dev/null +++ b/src/gui/graphicsview/qgraphicsproxywidget_p.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSPROXYWIDGET_P_H +#define QGRAPHICSPROXYWIDGET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsproxywidget.h" +#include "private/qgraphicswidget_p.h" + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +QT_BEGIN_NAMESPACE + +class QGraphicsProxyWidgetPrivate : public QGraphicsWidgetPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsProxyWidget) +public: + QGraphicsProxyWidgetPrivate() + : dragDropWidget(0), + posChangeMode(NoMode), + sizeChangeMode(NoMode), + visibleChangeMode(NoMode), + enabledChangeMode(NoMode), + styleChangeMode(NoMode), + paletteChangeMode(NoMode), + focusFromWidgetToProxy(0) + { } + void init(); + void sendWidgetMouseEvent(QGraphicsSceneMouseEvent *event); + void sendWidgetMouseEvent(QGraphicsSceneHoverEvent *event); + void sendWidgetKeyEvent(QKeyEvent *event); + void setWidget_helper(QWidget *widget, bool autoShow); + + QWidget *findFocusChild(QWidget *child, bool next) const; + void removeSubFocusHelper(QWidget *widget, Qt::FocusReason reason); + + // ### Qt 5: Remove. Workaround for reimplementation added after Qt 4.4. + QVariant inputMethodQueryHelper(Qt::InputMethodQuery query) const; + + void _q_removeWidgetSlot(); + + void embedSubWindow(QWidget *); + void unembedSubWindow(QWidget *); + + bool isProxyWidget() const; + + QPointer<QWidget> widget; + QPointer<QWidget> lastWidgetUnderMouse; + QPointer<QWidget> embeddedMouseGrabber; + QWidget *dragDropWidget; + Qt::DropAction lastDropAction; + + void updateWidgetGeometryFromProxy(); + void updateProxyGeometryFromWidget(); + + QPointF mapToReceiver(const QPointF &pos, const QWidget *receiver) const; + + enum ChangeMode { + NoMode, + ProxyToWidgetMode, + WidgetToProxyMode + }; + quint32 posChangeMode : 2; + quint32 sizeChangeMode : 2; + quint32 visibleChangeMode : 2; + quint32 enabledChangeMode : 2; + quint32 styleChangeMode : 2; + quint32 paletteChangeMode : 2; + quint32 focusFromWidgetToProxy : 1; +}; + +QT_END_NAMESPACE + +#endif + +#endif diff --git a/src/gui/graphicsview/qgraphicsscene.cpp b/src/gui/graphicsview/qgraphicsscene.cpp new file mode 100644 index 0000000..553967c --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscene.cpp @@ -0,0 +1,5377 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +static const int QGRAPHICSSCENE_INDEXTIMER_TIMEOUT = 2000; + +/*! + \class QGraphicsScene + \brief The QGraphicsScene class provides a surface for managing a large + number of 2D graphical items. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + + The class serves as a container for QGraphicsItems. It is used together + with QGraphicsView for visualizing graphical items, such as lines, + rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is + part of \l{The Graphics View Framework}. + + QGraphicsScene also provides functionality that lets you efficiently + determine both the location of items, and for determining what items are + visible within an arbitrary area on the scene. With the QGraphicsView + widget, you can either visualize the whole scene, or zoom in and view only + parts of the scene. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 0 + + Note that QGraphicsScene has no visual appearance of its own; it only + manages the items. You need to create a QGraphicsView widget to visualize + the scene. + + To add items to a scene, you start off by constructing a QGraphicsScene + object. Then, you have two options: either add your existing QGraphicsItem + objects by calling addItem(), or you can call one of the convenience + functions addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(), + addRect(), or addText(), which all return a pointer to the newly added item. + The dimensions of the items added with these functions are relative to the + item's coordinate system, and the items position is initialized to (0, + 0) in the scene. + + You can then visualize the scene using QGraphicsView. When the scene + changes, (e.g., when an item moves or is transformed) QGraphicsScene + emits the changed() signal. To remove an item, call removeItem(). + + QGraphicsScene uses an indexing algorithm to manage the location of items + efficiently. By default, a BSP (Binary Space Partitioning) tree is used; an + algorithm suitable for large scenes where most items remain static (i.e., + do not move around). You can choose to disable this index by calling + setItemIndexMethod(). For more information about the available indexing + algorithms, see the itemIndexMethod property. + + The scene's bounding rect is set by calling setSceneRect(). Items can be + placed at any position on the scene, and the size of the scene is by + default unlimited. The scene rect is used only for internal bookkeeping, + maintaining the scene's item index. If the scene rect is unset, + QGraphicsScene will use the bounding area of all items, as returned by + itemsBoundingRect(), as the scene rect. However, itemsBoundingRect() is a + relatively time consuming function, as it operates by collecting + positional information for every item on the scene. Because of this, you + should always set the scene rect when operating on large scenes. + + One of QGraphicsScene's greatest strengths is its ability to efficiently + determine the location of items. Even with millions of items on the scene, + the items() functions can determine the location of an item within few + milliseconds. There are several overloads to items(): one that finds items + at a certain position, one that finds items inside or intersecting with a + polygon or a rectangle, and more. The list of returned items is sorted by + stacking order, with the topmost item being the first item in the list. + For convenience, there is also an itemAt() function that returns the + topmost item at a given position. + + QGraphicsScene maintains selection information for the scene. To select + items, call setSelectionArea(), and to clear the current selection, call + clearSelection(). Call selectedItems() to get the list of all selected + items. + + \section1 Event Handling and Propagation + + Another responsibility that QGraphicsScene has, is to propagate events + from QGraphicsView. To send an event to a scene, you construct an event + that inherits QEvent, and then send it using, for example, + QApplication::sendEvent(). event() is responsible for dispatching + the event to the individual items. Some common events are handled by + convenience event handlers. For example, key press events are handled by + keyPressEvent(), and mouse press events are handled by mousePressEvent(). + + Key events are delivered to the \e {focus item}. To set the focus item, + you can either call setFocusItem(), passing an item that accepts focus, or + the item itself can call QGraphicsItem::setFocus(). Call focusItem() to + get the current focus item. For compatibility with widgets, the scene also + maintains its own focus information. By default, the scene does not have + focus, and all key events are discarded. If setFocus() is called, or if an + item on the scene gains focus, the scene automatically gains focus. If the + scene has focus, hasFocus() will return true, and key events will be + forwarded to the focus item, if any. If the scene loses focus, (i.e., + someone calls clearFocus(),) while an item has focus, the scene will + maintain its item focus information, and once the scene regains focus, it + will make sure the last focus item regains focus. + + For mouse-over effects, QGraphicsScene dispatches \e {hover + events}. If an item accepts hover events (see + QGraphicsItem::acceptHoverEvents()), it will receive a \l + {QEvent::}{GraphicsSceneHoverEnter} event when the mouse enters + its area. As the mouse continues moving inside the item's area, + QGraphicsScene will send it \l {QEvent::}{GraphicsSceneHoverMove} + events. When the mouse leaves the item's area, the item will + receive a \l {QEvent::}{GraphicsSceneHoverLeave} event. + + All mouse events are delivered to the current \e {mouse grabber} + item. An item becomes the scene's mouse grabber if it accepts + mouse events (see QGraphicsItem::acceptedMouseButtons()) and it + receives a mouse press. It stays the mouse grabber until it + receives a mouse release when no other mouse buttons are + pressed. You can call mouseGrabberItem() to determine what item is + currently grabbing the mouse. + + \sa QGraphicsItem, QGraphicsView +*/ + +/*! + \enum QGraphicsScene::SceneLayer + \since 4.3 + + This enum describes the rendering layers in a QGraphicsScene. When + QGraphicsScene draws the scene contents, it renders each of these layers + separately, in order. + + Each layer represents a flag that can be OR'ed together when calling + functions such as invalidate() or QGraphicsView::invalidateScene(). + + \value ItemLayer The item layer. QGraphicsScene renders all items are in + this layer by calling the virtual function drawItems(). The item layer is + drawn after the background layer, but before the foreground layer. + + \value BackgroundLayer The background layer. QGraphicsScene renders the + scene's background in this layer by calling the virtual function + drawBackground(). The background layer is drawn first of all layers. + + \value ForegroundLayer The foreground layer. QGraphicsScene renders the + scene's foreground in this layer by calling the virtual function + drawForeground(). The foreground layer is drawn last of all layers. + + \value AllLayers All layers; this value represents a combination of all + three layers. + + \sa invalidate(), QGraphicsView::invalidateScene() +*/ + +/*! + \enum QGraphicsScene::ItemIndexMethod + + This enum describes the indexing algorithms QGraphicsScene provides for + managing positional information about items on the scene. + + \value BspTreeIndex A Binary Space Partitioning tree is applied. All + QGraphicsScene's item location algorithms are of an order close to + logarithmic complexity, by making use of binary search. Adding, moving and + removing items is logarithmic. This approach is best for static scenes + (i.e., scenes where most items do not move). + + \value NoIndex No index is applied. Item location is of linear complexity, + as all items on the scene are searched. Adding, moving and removing items, + however, is done in constant time. This approach is ideal for dynamic + scenes, where many items are added, moved or removed continuously. + + \sa setItemIndexMethod(), bspTreeDepth +*/ + +#include "qgraphicsscene.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qgraphicsitem.h" +#include "qgraphicsitem_p.h" +#include "qgraphicslayout.h" +#include "qgraphicsscene_p.h" +#include "qgraphicssceneevent.h" +#include "qgraphicsview.h" +#include "qgraphicsview_p.h" +#include "qgraphicswidget.h" +#include "qgraphicswidget_p.h" + +#include <QtCore/qdebug.h> +#include <QtCore/qlist.h> +#include <QtCore/qmath.h> +#include <QtCore/qrect.h> +#include <QtCore/qset.h> +#include <QtCore/qstack.h> +#include <QtCore/qtimer.h> +#include <QtCore/qvarlengtharray.h> +#include <QtGui/qapplication.h> +#include <QtGui/qdesktopwidget.h> +#include <QtGui/qevent.h> +#include <QtGui/qgraphicslayout.h> +#include <QtGui/qgraphicsproxywidget.h> +#include <QtGui/qgraphicswidget.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qpaintengine.h> +#include <QtGui/qpainter.h> +#include <QtGui/qpixmapcache.h> +#include <QtGui/qpolygon.h> +#include <QtGui/qstyleoption.h> +#include <QtGui/qtooltip.h> +#include <QtGui/qtransform.h> +#include <private/qapplication_p.h> +#include <private/qobject_p.h> +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#endif + +QT_BEGIN_NAMESPACE + +static inline bool QRectF_intersects(const QRectF &s, const QRectF &r) +{ + qreal xp = s.left(); + qreal yp = s.top(); + qreal w = s.width(); + qreal h = s.height(); + qreal l1 = xp; + qreal r1 = xp; + if (w < 0) + l1 += w; + else + r1 += w; + + qreal l2 = r.left(); + qreal r2 = r.left(); + if (w < 0) + l2 += r.width(); + else + r2 += r.width(); + + if (l1 >= r2 || l2 >= r1) + return false; + + qreal t1 = yp; + qreal b1 = yp; + if (h < 0) + t1 += h; + else + b1 += h; + + qreal t2 = r.top(); + qreal b2 = r.top(); + if (r.height() < 0) + t2 += r.height(); + else + b2 += r.height(); + + return !(t1 >= b2 || t2 >= b1); +} + +// QRectF::intersects() returns false always if either the source or target +// rectangle's width or height are 0. This works around that problem. +static QRectF _q_adjustedRect(const QRectF &rect) +{ + static const qreal p = (qreal)0.00001; + QRectF r = rect; + if (!r.width()) + r.adjust(-p, 0, p, 0); + if (!r.height()) + r.adjust(0, -p, 0, p); + return r; +} + +static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent) +{ + hover->setWidget(mouseEvent->widget()); + hover->setPos(mouseEvent->pos()); + hover->setScenePos(mouseEvent->scenePos()); + hover->setScreenPos(mouseEvent->screenPos()); + hover->setLastPos(mouseEvent->lastPos()); + hover->setLastScenePos(mouseEvent->lastScenePos()); + hover->setLastScreenPos(mouseEvent->lastScreenPos()); + hover->setModifiers(mouseEvent->modifiers()); + hover->setAccepted(mouseEvent->isAccepted()); +} + +/*! + \internal +*/ +QGraphicsScenePrivate::QGraphicsScenePrivate() + : changedSignalMask(0), + indexMethod(QGraphicsScene::BspTreeIndex), + bspTreeDepth(0), + lastItemCount(0), + hasSceneRect(false), + updateAll(false), + calledEmitUpdated(false), + selectionChanging(0), + dirtyItemResetPending(false), + regenerateIndex(true), + purgePending(false), + indexTimerId(0), + restartIndexTimer(false), + stickyFocus(false), + hasFocus(false), + focusItem(0), + lastFocusItem(0), + tabFocusFirst(0), + activeWindow(0), + activationRefCount(0), + lastMouseGrabberItem(0), + lastMouseGrabberItemHasImplicitMouseGrab(false), + dragDropItem(0), + enterWidget(0), + lastDropAction(Qt::IgnoreAction), + painterStateProtection(true), + sortCacheEnabled(false), + updatingSortCache(false), + style(0) +{ +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::init() +{ + Q_Q(QGraphicsScene); + + // Keep this index so we can check for connected slots later on. + changedSignalMask = (1 << q->metaObject()->indexOfSignal("changed(QList<QRectF>)")); + qApp->d_func()->scene_list.append(q); + q->update(); +} + +/*! + \internal +*/ +QList<QGraphicsItem *> QGraphicsScenePrivate::estimateItemsInRect(const QRectF &rect) const +{ + const_cast<QGraphicsScenePrivate *>(this)->purgeRemovedItems(); + const_cast<QGraphicsScenePrivate *>(this)->_q_updateSortCache(); + + if (indexMethod == QGraphicsScene::BspTreeIndex) { + // ### Only do this once in a while. + QGraphicsScenePrivate *that = const_cast<QGraphicsScenePrivate *>(this); + + // Get items from BSP tree + QList<QGraphicsItem *> items = that->bspTree.items(rect); + + // Fill in with any unindexed items + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + if (!item->d_ptr->itemDiscovered && item->d_ptr->visible && !(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + QRectF boundingRect = item->sceneBoundingRect(); + if (QRectF_intersects(boundingRect, rect)) { + item->d_ptr->itemDiscovered = 1; + items << item; + } + } + } + } + + // Reset the discovered state of all discovered items + for (int i = 0; i < items.size(); ++i) + items.at(i)->d_func()->itemDiscovered = 0; + return items; + } + + QList<QGraphicsItem *> itemsInRect; + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + continue; + if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0)) + itemsInRect << item; + } + } + for (int i = 0; i < indexedItems.size(); ++i) { + if (QGraphicsItem *item = indexedItems.at(i)) { + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + continue; + if (item->d_ptr->visible && item->effectiveOpacity() > qreal(0.0)) + itemsInRect << item; + } + } + + return itemsInRect; +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::addToIndex(QGraphicsItem *item) +{ + if (indexMethod == QGraphicsScene::BspTreeIndex) { + if (item->d_func()->index != -1) { + bspTree.insertItem(item, item->sceneBoundingRect()); + foreach (QGraphicsItem *child, item->children()) + child->addToIndex(); + } else { + // The BSP tree is regenerated if the number of items grows to a + // certain threshold, or if the bounding rect of the graph doubles in + // size. + startIndexTimer(); + } + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::removeFromIndex(QGraphicsItem *item) +{ + if (indexMethod == QGraphicsScene::BspTreeIndex) { + int index = item->d_func()->index; + if (index != -1) { + bspTree.removeItem(item, item->sceneBoundingRect()); + freeItemIndexes << index; + indexedItems[index] = 0; + item->d_func()->index = -1; + unindexedItems << item; + + foreach (QGraphicsItem *child, item->children()) + child->removeFromIndex(); + } + + startIndexTimer(); + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::resetIndex() +{ + purgeRemovedItems(); + if (indexMethod == QGraphicsScene::BspTreeIndex) { + for (int i = 0; i < indexedItems.size(); ++i) { + if (QGraphicsItem *item = indexedItems.at(i)) { + item->d_ptr->index = -1; + unindexedItems << item; + } + } + indexedItems.clear(); + freeItemIndexes.clear(); + regenerateIndex = true; + startIndexTimer(); + } +} + +static inline int intmaxlog(int n) +{ + return (n > 0 ? qMax(qCeil(qLn(qreal(n)) / qLn(qreal(2))), 5) : 0); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::_q_updateIndex() +{ + if (!indexTimerId) + return; + + Q_Q(QGraphicsScene); + q->killTimer(indexTimerId); + indexTimerId = 0; + + purgeRemovedItems(); + + // Add unindexedItems to indexedItems + QRectF unindexedItemsBoundingRect; + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + unindexedItemsBoundingRect |= item->sceneBoundingRect(); + if (!freeItemIndexes.isEmpty()) { + int freeIndex = freeItemIndexes.takeFirst(); + item->d_func()->index = freeIndex; + indexedItems[freeIndex] = item; + } else { + item->d_func()->index = indexedItems.size(); + indexedItems << item; + } + } + } + + // Update growing scene rect. + QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect; + growingItemsBoundingRect |= unindexedItemsBoundingRect; + + // Determine whether we should regenerate the BSP tree. + if (indexMethod == QGraphicsScene::BspTreeIndex) { + int depth = bspTreeDepth; + if (depth == 0) { + int oldDepth = intmaxlog(lastItemCount); + depth = intmaxlog(indexedItems.size()); + static const int slack = 100; + if (bspTree.leafCount() == 0 || (oldDepth != depth && qAbs(lastItemCount - indexedItems.size()) > slack)) { + // ### Crude algorithm. + regenerateIndex = true; + } + } + + // Regenerate the tree. + if (regenerateIndex) { + regenerateIndex = false; + bspTree.initialize(q->sceneRect(), depth); + unindexedItems = indexedItems; + lastItemCount = indexedItems.size(); + q->update(); + + // Take this opportunity to reset our largest-item counter for + // untransformable items. When the items are inserted into the BSP + // tree, we'll get an accurate calculation. + largestUntransformableItem = QRectF(); + } + } + + // Insert all unindexed items into the tree. + for (int i = 0; i < unindexedItems.size(); ++i) { + if (QGraphicsItem *item = unindexedItems.at(i)) { + QRectF rect = item->sceneBoundingRect(); + if (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren) + continue; + if (indexMethod == QGraphicsScene::BspTreeIndex) + bspTree.insertItem(item, rect); + + // If the item ignores view transformations, update our + // largest-item-counter to ensure that the view can accurately + // discover untransformable items when drawing. + if (item->d_ptr->itemIsUntransformable()) { + QGraphicsItem *topmostUntransformable = item; + while (topmostUntransformable && (topmostUntransformable->d_ptr->ancestorFlags + & QGraphicsItemPrivate::AncestorIgnoresTransformations)) { + topmostUntransformable = topmostUntransformable->parentItem(); + } + // ### Verify that this is the correct largest untransformable rectangle. + largestUntransformableItem |= item->mapToItem(topmostUntransformable, item->boundingRect()).boundingRect(); + } + } + } + unindexedItems.clear(); + + // Notify scene rect changes. + if (!hasSceneRect && growingItemsBoundingRect != oldGrowingItemsBoundingRect) + emit q->sceneRectChanged(growingItemsBoundingRect); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::_q_emitUpdated() +{ + Q_Q(QGraphicsScene); + calledEmitUpdated = false; + + // Ensure all views are connected if anything is connected. This disables + // the optimization that items send updates directly to the views, but it + // needs to happen in order to keep compatibility with the behavior from + // Qt 4.4 and backward. + if (!views.isEmpty() && (connectedSignals & changedSignalMask)) { + for (int i = 0; i < views.size(); ++i) { + QGraphicsView *view = views.at(i); + if (!view->d_func()->connectedToScene) { + view->d_func()->connectedToScene = true; + q->connect(q, SIGNAL(changed(QList<QRectF>)), + views.at(i), SLOT(updateScene(QList<QRectF>))); + } + } + } + + // Ensure all dirty items's current positions are recorded in the list of + // updated rects. + for (int i = 0; i < dirtyItems.size(); ++i) + updatedRects += dirtyItems.at(i)->sceneBoundingRect(); + + // Notify the changes to anybody interested. + QList<QRectF> oldUpdatedRects; + oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects; + updateAll = false; + updatedRects.clear(); + emit q->changed(oldUpdatedRects); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item) +{ + item->d_ptr->siblingIndex = topLevelItems.size(); + topLevelItems.append(item); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item) +{ + int idx = item->d_ptr->siblingIndex; + int size = topLevelItems.size(); + for (int i = idx; i < size - 1; ++i) { + QGraphicsItem *p = topLevelItems[i + 1]; + topLevelItems[i] = p; + p->d_ptr->siblingIndex = i; + } + topLevelItems.removeLast(); +} + +/*! + \internal + + Updates all items in the pending update list. At this point, the list is + unlikely to contain partially constructed items. +*/ +void QGraphicsScenePrivate::_q_updateLater() +{ + foreach (QGraphicsItem *item, pendingUpdateItems) + item->update(); + pendingUpdateItems.clear(); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::_q_polishItems() +{ + foreach (QGraphicsItem *item, unpolishedItems) { + if (!item->d_ptr->explicitlyHidden) { + item->itemChange(QGraphicsItem::ItemVisibleChange, true); + item->itemChange(QGraphicsItem::ItemVisibleHasChanged, true); + } + if (item->isWidget()) { + QEvent event(QEvent::Polish); + QApplication::sendEvent((QGraphicsWidget *)item, &event); + } + } + unpolishedItems.clear(); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::_q_resetDirtyItems() +{ + for (int i = 0; i < dirtyItems.size(); ++i) { + QGraphicsItem *item = dirtyItems.at(i); + item->d_ptr->dirty = 0; + item->d_ptr->dirtyChildren = 0; + } + dirtyItems.clear(); + dirtyItemResetPending = false; +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::resetDirtyItemsLater() +{ + Q_Q(QGraphicsScene); + if (dirtyItemResetPending) + return; + // dirtyItems.reserve(indexedItems.size() + unindexedItems.size()); + dirtyItemResetPending = true; + QMetaObject::invokeMethod(q, "_q_resetDirtyItems", Qt::QueuedConnection); +} + +/*! + \internal + + Schedules an item for removal. This function leaves some stale indexes + around in the BSP tree; these will be cleaned up the next time someone + triggers purgeRemovedItems(). + + Note: This function is called from QGraphicsItem's destructor. \a item is + being destroyed, so we cannot call any pure virtual functions on it (such + as boundingRect()). Also, it is unnecessary to update the item's own state + in any way. + + ### Refactoring: This function shares much functionality with removeItem() +*/ +void QGraphicsScenePrivate::_q_removeItemLater(QGraphicsItem *item) +{ + Q_Q(QGraphicsScene); + + // Clear focus on the item to remove any reference in the focusWidget + // chain. + item->clearFocus(); + + int index = item->d_func()->index; + if (index != -1) { + // Important: The index is useless until purgeRemovedItems() is + // called. + indexedItems[index] = (QGraphicsItem *)0; + if (!purgePending) { + purgePending = true; + q->update(); + } + removedItems << item; + } else { + // Recently added items are purged immediately. unindexedItems() never + // contains stale items. + unindexedItems.removeAll(item); + q->update(); + } + + // Reset the mouse grabber and focus item data. + if (item == focusItem) + focusItem = 0; + if (item == lastFocusItem) + lastFocusItem = 0; + if (item == activeWindow) { + // ### deactivate... + activeWindow = 0; + } + + // Disable selectionChanged() for individual items + ++selectionChanging; + int oldSelectedItemsSize = selectedItems.size(); + + // Update selected & hovered item bookkeeping + selectedItems.remove(item); + hoverItems.removeAll(item); + pendingUpdateItems.removeAll(item); + cachedItemsUnderMouse.removeAll(item); + unpolishedItems.removeAll(item); + dirtyItems.removeAll(item); + + // Remove from scene transform cache + int transformIndex = item->d_func()->sceneTransformIndex; + if (transformIndex != -1) { + validTransforms.setBit(transformIndex, 0); + freeSceneTransformSlots.append(transformIndex); + } + + // Reset the mouse grabber + if (mouseGrabberItems.contains(item)) + ungrabMouse(item, /* item is dying */ true); + + // Reset the keyboard grabber + if (keyboardGrabberItems.contains(item)) + ungrabKeyboard(item, /* item is dying */ true); + + // Reset the last mouse grabber item + if (item == lastMouseGrabberItem) + lastMouseGrabberItem = 0; + + // Reenable selectionChanged() for individual items + --selectionChanging; + if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize) + emit q->selectionChanged(); +} + +/*! + \internal + + Removes stale pointers from all data structures. +*/ +void QGraphicsScenePrivate::purgeRemovedItems() +{ + Q_Q(QGraphicsScene); + + if (!purgePending && removedItems.isEmpty()) + return; + + // Remove stale items from the BSP tree. + if (indexMethod != QGraphicsScene::NoIndex) + bspTree.removeItems(removedItems); + + // Purge this list. + removedItems.clear(); + freeItemIndexes.clear(); + for (int i = 0; i < indexedItems.size(); ++i) { + if (!indexedItems.at(i)) + freeItemIndexes << i; + } + purgePending = false; + + // No locality info for the items; update the whole scene. + q->update(); +} + +/*! + \internal + + Starts or restarts the timer used for reindexing unindexed items. +*/ +void QGraphicsScenePrivate::startIndexTimer() +{ + Q_Q(QGraphicsScene); + if (indexTimerId) { + restartIndexTimer = true; + } else { + indexTimerId = q->startTimer(QGRAPHICSSCENE_INDEXTIMER_TIMEOUT); + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget) +{ + Q_ASSERT(widget); + Q_ASSERT(!popupWidgets.contains(widget)); + popupWidgets << widget; + if (QGraphicsWidget *focusWidget = widget->focusWidget()) { + focusWidget->setFocus(Qt::PopupFocusReason); + } else { + grabKeyboard((QGraphicsItem *)widget); + if (focusItem && popupWidgets.size() == 1) { + QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason); + sendEvent(focusItem, &event); + } + } + grabMouse((QGraphicsItem *)widget); +} + +/*! + \internal + + Remove \a widget from the popup list. Important notes: + + \a widget is guaranteed to be in the list of popups, but it might not be + the last entry; you can hide any item in the pop list before the others, + and this must cause all later mouse grabbers to lose the grab. +*/ +void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDying) +{ + Q_ASSERT(widget); + int index = popupWidgets.indexOf(widget); + Q_ASSERT(index != -1); + + for (int i = popupWidgets.size() - 1; i >= index; --i) { + QGraphicsWidget *widget = popupWidgets.takeLast(); + ungrabMouse(widget, itemIsDying); + if (focusItem && popupWidgets.isEmpty()) { + QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason); + sendEvent(focusItem, &event); + } else { + ungrabKeyboard((QGraphicsItem *)widget, itemIsDying); + } + if (!itemIsDying && widget->isVisible()) { + widget->hide(); + widget->QGraphicsItem::d_ptr->explicitlyHidden = 0; + } + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit) +{ + // Append to list of mouse grabber items, and send a mouse grab event. + if (mouseGrabberItems.contains(item)) { + if (mouseGrabberItems.last() == item) + qWarning("QGraphicsItem::grabMouse: already a mouse grabber"); + else + qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p", + mouseGrabberItems.last()); + return; + } + + // Send ungrab event to the last grabber. + if (!mouseGrabberItems.isEmpty()) { + QGraphicsItem *last = mouseGrabberItems.last(); + if (lastMouseGrabberItemHasImplicitMouseGrab) { + // Implicit mouse grab is immediately lost. + last->ungrabMouse(); + } else { + // Just send ungrab event to current grabber. + QEvent ungrabEvent(QEvent::UngrabMouse); + sendEvent(last, &ungrabEvent); + } + } + + mouseGrabberItems << item; + lastMouseGrabberItemHasImplicitMouseGrab = implicit; + + // Send grab event to current grabber. + QEvent grabEvent(QEvent::GrabMouse); + sendEvent(item, &grabEvent); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::ungrabMouse(QGraphicsItem *item, bool itemIsDying) +{ + int index = mouseGrabberItems.indexOf(item); + if (index == -1) { + qWarning("QGraphicsItem::ungrabMouse: not a mouse grabber"); + return; + } + + if (item != mouseGrabberItems.last()) { + // Recursively ungrab the next mouse grabber until we reach this item + // to ensure state consistency. + ungrabMouse(mouseGrabberItems.at(index + 1), itemIsDying); + } + if (!popupWidgets.isEmpty() && item == popupWidgets.last()) { + // If the item is a popup, go via removePopup to ensure state + // consistency and that it gets hidden correctly - beware that + // removePopup() reenters this function to continue removing the grab. + removePopup((QGraphicsWidget *)item, itemIsDying); + return; + } + + // Send notification about mouse ungrab. + if (!itemIsDying) { + QEvent event(QEvent::UngrabMouse); + sendEvent(item, &event); + } + + // Remove the item from the list of grabbers. Whenever this happens, we + // reset the implicitGrab (there can be only ever be one implicit grabber + // in a scene, and it is always the latest grabber; if the implicit grab + // is lost, it is not automatically regained. + mouseGrabberItems.takeLast(); + lastMouseGrabberItemHasImplicitMouseGrab = false; + + // Send notification about mouse regrab. ### It's unfortunate that all the + // items get a GrabMouse event, but this is a rare case with a simple + // implementation and it does ensure a consistent state. + if (!itemIsDying && !mouseGrabberItems.isEmpty()) { + QGraphicsItem *last = mouseGrabberItems.last(); + QEvent event(QEvent::GrabMouse); + sendEvent(last, &event); + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::clearMouseGrabber() +{ + if (!mouseGrabberItems.isEmpty()) + mouseGrabberItems.first()->ungrabMouse(); + lastMouseGrabberItem = 0; +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::grabKeyboard(QGraphicsItem *item) +{ + if (keyboardGrabberItems.contains(item)) { + if (keyboardGrabberItems.last() == item) + qWarning("QGraphicsItem::grabKeyboard: already a keyboard grabber"); + else + qWarning("QGraphicsItem::grabKeyboard: already blocked by keyboard grabber: %p", + keyboardGrabberItems.last()); + return; + } + + // Send ungrab event to the last grabber. + if (!keyboardGrabberItems.isEmpty()) { + // Just send ungrab event to current grabber. + QEvent ungrabEvent(QEvent::UngrabKeyboard); + sendEvent(keyboardGrabberItems.last(), &ungrabEvent); + } + + keyboardGrabberItems << item; + + // Send grab event to current grabber. + QEvent grabEvent(QEvent::GrabKeyboard); + sendEvent(item, &grabEvent); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::ungrabKeyboard(QGraphicsItem *item, bool itemIsDying) +{ + int index = keyboardGrabberItems.lastIndexOf(item); + if (index == -1) { + qWarning("QGraphicsItem::ungrabKeyboard: not a keyboard grabber"); + return; + } + if (item != keyboardGrabberItems.last()) { + // Recursively ungrab the topmost keyboard grabber until we reach this + // item to ensure state consistency. + ungrabKeyboard(keyboardGrabberItems.at(index + 1), itemIsDying); + } + + // Send notification about keyboard ungrab. + if (!itemIsDying) { + QEvent event(QEvent::UngrabKeyboard); + sendEvent(item, &event); + } + + // Remove the item from the list of grabbers. + keyboardGrabberItems.takeLast(); + + // Send notification about mouse regrab. + if (!itemIsDying && !keyboardGrabberItems.isEmpty()) { + QGraphicsItem *last = keyboardGrabberItems.last(); + QEvent event(QEvent::GrabKeyboard); + sendEvent(last, &event); + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::clearKeyboardGrabber() +{ + if (!keyboardGrabberItems.isEmpty()) + ungrabKeyboard(keyboardGrabberItems.first()); +} + +/*! + Returns all items for the screen position in \a event. +*/ +QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &screenPos, + const QPointF &scenePos, + QWidget *widget) const +{ + Q_Q(const QGraphicsScene); + QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0; + QList<QGraphicsItem *> items; + if (view) + items = view->items(view->viewport()->mapFromGlobal(screenPos)); + else + items = q->items(scenePos); + return items; +} + +/*! + \internal + + Checks if item collides with the path and mode, but also checks that if it + doesn't collide, maybe its frame rect will. +*/ +bool QGraphicsScenePrivate::itemCollidesWithPath(QGraphicsItem *item, + const QPainterPath &path, + Qt::ItemSelectionMode mode) +{ + if (item->collidesWithPath(path, mode)) + return true; + if (item->isWidget()) { + // Check if this is a window, and if its frame rect collides. + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + if (widget->isWindow()) { + QRectF frameRect = widget->windowFrameRect(); + QPainterPath framePath; + framePath.addRect(frameRect); + bool intersects = path.intersects(frameRect); + if (mode == Qt::IntersectsItemShape || mode == Qt::IntersectsItemBoundingRect) + return intersects || path.contains(frameRect.topLeft()) + || framePath.contains(path.elementAt(0)); + return !intersects && path.contains(frameRect.topLeft()); + } + } + return false; +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event) +{ + for (int i = 0x1; i <= 0x10; i <<= 1) { + if (event->buttons() & i) { + mouseGrabberButtonDownPos.insert(Qt::MouseButton(i), + mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(), + event->widget())); + mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos()); + mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos()); + } + } +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter) +{ + sceneEventFilters.insert(watched, filter); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter) +{ + if (!sceneEventFilters.contains(watched)) + return; + + QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(watched); + QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(watched); + do { + if (it.value() == filter) + it = sceneEventFilters.erase(it); + else + ++it; + } while (it != end); +} + +/*! + \internal +*/ +bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event) +{ + if (item && !sceneEventFilters.contains(item)) + return false; + + QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(item); + QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(item); + while (it != end) { + // ### The filterer and filteree might both be deleted. + if (it.value()->sceneEventFilter(it.key(), event)) + return true; + ++it; + } + return false; +} + +/*! + \internal + + This is the final dispatch point for any events from the scene to the + item. It filters the event first - if the filter returns true, the event + is considered to have been eaten by the filter, and is therefore stopped + (the default filter returns false). Then/otherwise, if the item is + enabled, the event is sent; otherwise it is stopped. +*/ +bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event) +{ + if (filterEvent(item, event)) + return false; + return (item && item->isEnabled()) ? item->sceneEvent(event) : false; +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest, + QGraphicsSceneDragDropEvent *source) +{ + dest->setWidget(source->widget()); + dest->setPos(source->pos()); + dest->setScenePos(source->scenePos()); + dest->setScreenPos(source->screenPos()); + dest->setButtons(source->buttons()); + dest->setModifiers(source->modifiers()); + dest->setPossibleActions(source->possibleActions()); + dest->setProposedAction(source->proposedAction()); + dest->setDropAction(source->dropAction()); + dest->setSource(source->source()); + dest->setMimeData(source->mimeData()); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::sendDragDropEvent(QGraphicsItem *item, + QGraphicsSceneDragDropEvent *dragDropEvent) +{ + dragDropEvent->setPos(item->d_ptr->genericMapFromScene(dragDropEvent->scenePos(), dragDropEvent->widget())); + sendEvent(item, dragDropEvent); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::sendHoverEvent(QEvent::Type type, QGraphicsItem *item, + QGraphicsSceneHoverEvent *hoverEvent) +{ + QGraphicsSceneHoverEvent event(type); + event.setWidget(hoverEvent->widget()); + event.setPos(item->d_ptr->genericMapFromScene(hoverEvent->scenePos(), hoverEvent->widget())); + event.setScenePos(hoverEvent->scenePos()); + event.setScreenPos(hoverEvent->screenPos()); + event.setLastPos(item->d_ptr->genericMapFromScene(hoverEvent->lastScenePos(), hoverEvent->widget())); + event.setLastScenePos(hoverEvent->lastScenePos()); + event.setLastScreenPos(hoverEvent->lastScreenPos()); + event.setModifiers(hoverEvent->modifiers()); + sendEvent(item, &event); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) { + // ### This is a temporary fix for until we get proper mouse + // grab events. + clearMouseGrabber(); + return; + } + + QGraphicsItem *item = mouseGrabberItems.last(); + for (int i = 0x1; i <= 0x10; i <<= 1) { + Qt::MouseButton button = Qt::MouseButton(i); + mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget()))); + mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos())); + mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos())); + } + mouseEvent->setPos(item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget())); + mouseEvent->setLastPos(item->d_ptr->genericMapFromScene(mouseEvent->lastScenePos(), mouseEvent->widget())); + sendEvent(item, mouseEvent); +} + +/*! + \internal +*/ +void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent) +{ + Q_Q(QGraphicsScene); + + // Ignore by default, unless we find a mouse grabber that accepts it. + mouseEvent->ignore(); + + // Deliver to any existing mouse grabber. + if (!mouseGrabberItems.isEmpty()) { + // The event is ignored by default, but we disregard the event's + // accepted state after delivery; the mouse is grabbed, after all. + sendMouseEvent(mouseEvent); + return; + } + + // Start by determining the number of items at the current position. + // Reuse value from earlier calculations if possible. + if (cachedItemsUnderMouse.isEmpty()) { + cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(), + mouseEvent->scenePos(), + mouseEvent->widget()); + } + + // Update window activation. + QGraphicsWidget *newActiveWindow = windowForItem(cachedItemsUnderMouse.value(0)); + if (newActiveWindow != activeWindow) + q->setActiveWindow(newActiveWindow); + + // Set focus on the topmost enabled item that can take focus. + bool setFocus = false; + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) { + setFocus = true; + if (item != q->focusItem()) + q->setFocusItem(item, Qt::MouseFocusReason); + break; + } + } + } + + // If nobody could take focus, clear it. + if (!stickyFocus && !setFocus) + q->setFocusItem(0, Qt::MouseFocusReason); + + // Find a mouse grabber by sending mouse press events to all mouse grabber + // candidates one at a time, until the event is accepted. It's accepted by + // default, so the receiver has to explicitly ignore it for it to pass + // through. + foreach (QGraphicsItem *item, cachedItemsUnderMouse) { + if (!(item->acceptedMouseButtons() & mouseEvent->button())) { + // Skip items that don't accept the event's mouse button. + continue; + } + + grabMouse(item, /* implicit = */ true); + mouseEvent->accept(); + + // check if the item we are sending to are disabled (before we send the event) + bool disabled = !item->isEnabled(); + bool isWindow = item->isWindow(); + if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick && item != lastMouseGrabberItem) { + // If this item is different from the item that received the last + // mouse event, and mouseEvent is a doubleclick event, then the + // event is converted to a press. Known limitation: + // Triple-clicking will not generate a doubleclick, though. + QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress); + mousePress.accept(); + mousePress.setButton(mouseEvent->button()); + mousePress.setButtons(mouseEvent->buttons()); + mousePress.setScreenPos(mouseEvent->screenPos()); + mousePress.setScenePos(mouseEvent->scenePos()); + mousePress.setModifiers(mouseEvent->modifiers()); + mousePress.setWidget(mouseEvent->widget()); + mousePress.setButtonDownPos(mouseEvent->button(), + mouseEvent->buttonDownPos(mouseEvent->button())); + mousePress.setButtonDownScenePos(mouseEvent->button(), + mouseEvent->buttonDownScenePos(mouseEvent->button())); + mousePress.setButtonDownScreenPos(mouseEvent->button(), + mouseEvent->buttonDownScreenPos(mouseEvent->button())); + sendMouseEvent(&mousePress); + mouseEvent->setAccepted(mousePress.isAccepted()); + } else { + sendMouseEvent(mouseEvent); + } + + bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item; + if (disabled) { + ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); + break; + } + if (mouseEvent->isAccepted()) { + if (!mouseGrabberItems.isEmpty()) + storeMouseButtonsForMouseGrabber(mouseEvent); + lastMouseGrabberItem = item; + return; + } + ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents); + + // Don't propagate through windows. + if (isWindow) + break; + } + + // Is the event still ignored? Then the mouse press goes to the scene. + // Reset the mouse grabber, clear the selection, clear focus, and leave + // the event ignored so that it can propagate through the originating + // view. + if (!mouseEvent->isAccepted()) { + clearMouseGrabber(); + + QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0; + bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag; + if (!dontClearSelection) { + // Clear the selection if the originating view isn't in scroll + // hand drag mode. The view will clear the selection if no drag + // happened. + q->clearSelection(); + } + } +} + +QGraphicsWidget *QGraphicsScenePrivate::windowForItem(const QGraphicsItem *item) const +{ + if (!item) + return 0; + do { + if (item->isWidget()) + return static_cast<const QGraphicsWidget *>(item)->window(); + item = item->parentItem(); + } while (item); + return 0; +} + +QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QRectF &rect, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const +{ + QList<QGraphicsItem *> items; + + QPainterPath path; + + // The index returns a rough estimate of what items are inside the rect. + // Refine it by iterating through all returned items. + QRectF adjustedRect = _q_adjustedRect(rect); + foreach (QGraphicsItem *item, estimateItemsInRect(adjustedRect)) { + // Find the item's scene transform in a clever way. + QTransform x = item->sceneTransform(); + bool keep = false; + + // ### _q_adjustedRect is only needed because QRectF::intersects, + // QRectF::contains and QTransform::map() and friends don't work with + // flat rectangles. + QRectF br = _q_adjustedRect(item->boundingRect()); + if (mode >= Qt::ContainsItemBoundingRect) { + // Rect intersects/contains item's bounding rect + QRectF mbr = x.mapRect(br); + if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) + || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(mbr))) { + items << item; + keep = true; + } + } else { + // Rect intersects/contains item's shape + if (QRectF_intersects(adjustedRect, x.mapRect(br))) { + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) { + if (path == QPainterPath()) + path.addRect(rect); + if (itemCollidesWithPath(item, xinv.map(path), mode)) { + items << item; + keep = true; + } + } + } + } + + if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { + // Recurse into children that clip children. + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) { + if (x.type() <= QTransform::TxScale) { + // Rect + childItems_helper(&items, item, xinv.mapRect(rect), mode); + } else { + // Polygon + childItems_helper(&items, item, xinv.map(rect), mode); + } + } + } + } + + if (order != Qt::SortOrder(-1)) + sortItems(&items, order, sortCacheEnabled); + return items; +} + +QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPolygonF &polygon, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const +{ + QList<QGraphicsItem *> items; + + QRectF polyRect = _q_adjustedRect(polygon.boundingRect()); + QPainterPath path; + + // The index returns a rough estimate of what items are inside the rect. + // Refine it by iterating through all returned items. + foreach (QGraphicsItem *item, estimateItemsInRect(polyRect)) { + // Find the item's scene transform in a clever way. + QTransform x = item->sceneTransform(); + bool keep = false; + + // ### _q_adjustedRect is only needed because QRectF::intersects, + // QRectF::contains and QTransform::map() and friends don't work with + // flat rectangles. + QRectF br = _q_adjustedRect(item->boundingRect()); + if (mode >= Qt::ContainsItemBoundingRect) { + // Polygon contains/intersects item's bounding rect + if (path == QPainterPath()) + path.addPolygon(polygon); + if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(x.mapRect(br))) + || (mode == Qt::ContainsItemBoundingRect && path.contains(x.mapRect(br)))) { + items << item; + keep = true; + } + } else { + // Polygon contains/intersects item's shape + if (QRectF_intersects(polyRect, x.mapRect(br))) { + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) { + if (path == QPainterPath()) + path.addPolygon(polygon); + if (itemCollidesWithPath(item, xinv.map(path), mode)) { + items << item; + keep = true; + } + } + } + } + + if (keep && (item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) { + // Recurse into children that clip children. + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) + childItems_helper(&items, item, xinv.map(polygon), mode); + } + } + + if (order != Qt::SortOrder(-1)) + sortItems(&items, order, sortCacheEnabled); + return items; +} + +QList<QGraphicsItem *> QGraphicsScenePrivate::items_helper(const QPainterPath &path, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const +{ + QList<QGraphicsItem *> items; + // The index returns a rough estimate of what items are inside the rect. + // Refine it by iterating through all returned items. + foreach (QGraphicsItem *item, estimateItemsInRect(_q_adjustedRect(path.controlPointRect()))) { + // Find the item's scene transform in a clever way. + QTransform x = item->sceneTransform(); + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) { + QPainterPath mappedPath = xinv.map(path); + if (itemCollidesWithPath(item, mappedPath, mode)) { + items << item; + if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape) + childItems_helper(&items, item, mappedPath, mode); + } + } + } + + if (order != Qt::SortOrder(-1)) + sortItems(&items, order, sortCacheEnabled); + return items; +} + +void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QRectF &rect, + Qt::ItemSelectionMode mode) const +{ + QPainterPath path; + bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); + QRectF r = !parentClip ? _q_adjustedRect(rect) : _q_adjustedRect(rect).intersected(_q_adjustedRect(parent->boundingRect())); + if (r.isEmpty()) + return; + + QList<QGraphicsItem *> &children = parent->d_ptr->children; + for (int i = 0; i < children.size(); ++i) { + QGraphicsItem *item = children.at(i); + if (item->d_ptr->hasTransform && !item->transform().isInvertible()) + continue; + + // Skip invisible items and all their children. + if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0))) + continue; + + // ### _q_adjustedRect is only needed because QRectF::intersects, + // QRectF::contains and QTransform::map() and friends don't work with + // flat rectangles. + QRectF br = _q_adjustedRect(item->boundingRect()); + QRectF mbr = item->mapRectToParent(br); + bool keep = false; + if (mode >= Qt::ContainsItemBoundingRect) { + // Rect intersects/contains item's bounding rect + if ((mode == Qt::IntersectsItemBoundingRect && QRectF_intersects(rect, mbr)) + || (mode == Qt::ContainsItemBoundingRect && rect != mbr && rect.contains(br))) { + items->append(item); + keep = true; + } + } else { + // Rect intersects/contains item's shape + if (QRectF_intersects(rect, mbr)) { + if (path == QPainterPath()) + path.addRect(rect); + if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { + items->append(item); + keep = true; + } + } + } + + if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { + // Recurse into children. + if (!item->d_ptr->hasTransform || item->transform().type() <= QTransform::TxScale) { + // Rect + childItems_helper(items, item, item->mapRectFromParent(rect), mode); + } else { + // Polygon + childItems_helper(items, item, item->mapFromParent(rect), mode); + } + } + } +} + +void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QPolygonF &polygon, + Qt::ItemSelectionMode mode) const +{ + QPainterPath path; + bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); + QRectF polyRect = _q_adjustedRect(polygon.boundingRect()); + QRectF r = !parentClip ? polyRect : polyRect.intersected(_q_adjustedRect(parent->boundingRect())); + if (r.isEmpty()) + return; + + QList<QGraphicsItem *> &children = parent->d_ptr->children; + for (int i = 0; i < children.size(); ++i) { + QGraphicsItem *item = children.at(i); + if (item->d_ptr->hasTransform && !item->transform().isInvertible()) + continue; + + // Skip invisible items. + if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity() + 1, qreal(1.0))) + continue; + + // ### _q_adjustedRect is only needed because QRectF::intersects, + // QRectF::contains and QTransform::map() and friends don't work with + // flat rectangles. + QRectF br = _q_adjustedRect(item->boundingRect()); + bool keep = false; + if (mode >= Qt::ContainsItemBoundingRect) { + // Polygon contains/intersects item's bounding rect + if (path == QPainterPath()) + path.addPolygon(polygon); + if ((mode == Qt::IntersectsItemBoundingRect && path.intersects(item->mapRectToParent(br))) + || (mode == Qt::ContainsItemBoundingRect && path.contains(item->mapRectToParent(br)))) { + items->append(item); + keep = true; + } + } else { + // Polygon contains/intersects item's shape + if (QRectF_intersects(polyRect, item->mapRectToParent(br))) { + if (path == QPainterPath()) + path.addPolygon(polygon); + if (itemCollidesWithPath(item, item->mapFromParent(path), mode)) { + items->append(item); + keep = true; + } + } + } + + if ((keep || !(item->flags() & QGraphicsItem::ItemClipsChildrenToShape)) && !item->d_ptr->children.isEmpty()) { + // Recurse into children that clip children. + childItems_helper(items, item, item->mapFromParent(polygon), mode); + } + } +} + +void QGraphicsScenePrivate::childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QPainterPath &path, + Qt::ItemSelectionMode mode) const +{ + bool parentClip = (parent->flags() & QGraphicsItem::ItemClipsChildrenToShape); + QPainterPath intersectedPath = !parentClip ? path : path.intersected(parent->shape()); + if (intersectedPath.isEmpty()) + return; + + QList<QGraphicsItem *> &children = parent->d_ptr->children; + for (int i = 0; i < children.size(); ++i) { + QGraphicsItem *item = children.at(i); + + // Skip invisible items. + if (!item->d_ptr->visible || qFuzzyCompare(item->effectiveOpacity(), qreal(0.0))) + continue; + + QTransform x = item->sceneTransform(); + + bool ok; + QTransform xinv = x.inverted(&ok); + if (ok) { + QPainterPath mappedPath = xinv.map(path); + if (itemCollidesWithPath(item, mappedPath, mode)) { + items->append(item); + if (!item->d_ptr->children.isEmpty()) + childItems_helper(items, item, mappedPath, mode); + } + } + } +} + +void QGraphicsScenePrivate::invalidateSortCache() +{ + Q_Q(QGraphicsScene); + if (!sortCacheEnabled || updatingSortCache) + return; + + updatingSortCache = true; + QMetaObject::invokeMethod(q, "_q_updateSortCache", Qt::QueuedConnection); +} + +/*! + \internal + + Should not be exported, but we can't change that now. + ### Qt 5: Remove symbol / make static +*/ +inline bool qt_closestLeaf(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Return true if sibling item1 is on top of item2. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + bool f1 = d1->flags & QGraphicsItem::ItemStacksBehindParent; + bool f2 = d2->flags & QGraphicsItem::ItemStacksBehindParent; + if (f1 != f2) return f2; + qreal z1 = d1->z; + qreal z2 = d2->z; + return z1 != z2 ? z1 > z2 : d1->siblingIndex > d2->siblingIndex; +} + +/*! + \internal + + Should not be exported, but we can't change that now. +*/ +inline bool qt_closestItemFirst(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + return QGraphicsScenePrivate::closestItemFirst_withoutCache(item1, item2); +} + +/*! + Returns true if \a item1 is on top of \a item2. + + \internal +*/ +bool QGraphicsScenePrivate::closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + // Siblings? Just check their z-values. + const QGraphicsItemPrivate *d1 = item1->d_ptr; + const QGraphicsItemPrivate *d2 = item2->d_ptr; + if (d1->parent == d2->parent) + return qt_closestLeaf(item1, item2); + + // Find common ancestor, and each item's ancestor closest to the common + // ancestor. + int item1Depth = d1->depth; + int item2Depth = d2->depth; + const QGraphicsItem *p = item1; + const QGraphicsItem *t1 = item1; + while (item1Depth > item2Depth && (p = p->d_ptr->parent)) { + if (p == item2) { + // item2 is one of item1's ancestors; item1 is on top + return !(t1->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t1 = p; + --item1Depth; + } + p = item2; + const QGraphicsItem *t2 = item2; + while (item2Depth > item1Depth && (p = p->d_ptr->parent)) { + if (p == item1) { + // item1 is one of item2's ancestors; item1 is not on top + return (t2->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent); + } + t2 = p; + --item2Depth; + } + + // item1Ancestor is now at the same level as item2Ancestor, but not the same. + const QGraphicsItem *a1 = t1; + const QGraphicsItem *a2 = t2; + while (a1) { + const QGraphicsItem *p1 = a1; + const QGraphicsItem *p2 = a2; + a1 = a1->parentItem(); + a2 = a2->parentItem(); + if (a1 && a1 == a2) + return qt_closestLeaf(p1, p2); + } + + // No common ancestor? Then just compare the items' toplevels directly. + return qt_closestLeaf(t1->topLevelItem(), t2->topLevelItem()); +} + +/*! + Returns true if \a item2 is on top of \a item1. + + \internal +*/ +bool QGraphicsScenePrivate::closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2) +{ + return closestItemFirst_withoutCache(item2, item1); +} + +void QGraphicsScenePrivate::climbTree(QGraphicsItem *item, int *stackingOrder) +{ + if (!item->d_ptr->children.isEmpty()) { + QList<QGraphicsItem *> childList = item->d_ptr->children; + qSort(childList.begin(), childList.end(), qt_closestLeaf); + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (!(item->flags() & QGraphicsItem::ItemStacksBehindParent)) + climbTree(childList.at(i), stackingOrder); + } + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + for (int i = 0; i < childList.size(); ++i) { + QGraphicsItem *item = childList.at(i); + if (item->flags() & QGraphicsItem::ItemStacksBehindParent) + climbTree(childList.at(i), stackingOrder); + } + } else { + item->d_ptr->globalStackingOrder = (*stackingOrder)++; + } +} + +void QGraphicsScenePrivate::_q_updateSortCache() +{ + _q_updateIndex(); + + if (!sortCacheEnabled || !updatingSortCache) + return; + + updatingSortCache = false; + int stackingOrder = 0; + + QList<QGraphicsItem *> topLevels; + + for (int i = 0; i < indexedItems.size(); ++i) { + QGraphicsItem *item = indexedItems.at(i); + if (item && item->parentItem() == 0) + topLevels << item; + } + for (int i = 0; i < unindexedItems.size(); ++i) { + QGraphicsItem *item = unindexedItems.at(i); + if (item->parentItem() == 0) + topLevels << item; + } + + qSort(topLevels.begin(), topLevels.end(), qt_closestLeaf); + for (int i = 0; i < topLevels.size(); ++i) + climbTree(topLevels.at(i), &stackingOrder); +} + +void QGraphicsScenePrivate::sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, + bool sortCacheEnabled) +{ + if (sortCacheEnabled) { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withCache); + } + } else { + if (order == Qt::AscendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemFirst_withoutCache); + } else if (order == Qt::DescendingOrder) { + qSort(itemList->begin(), itemList->end(), closestItemLast_withoutCache); + } + } +} + +/*! + \internal + + Set the font and propagate the changes if the font is different from the + current font. +*/ +void QGraphicsScenePrivate::setFont_helper(const QFont &font) +{ + if (this->font == font && this->font.resolve() == font.resolve()) + return; + updateFont(font); +} + +/*! + \internal + + Resolve the scene's font against the application font, and propagate the + changes too all items in the scene. +*/ +void QGraphicsScenePrivate::resolveFont() +{ + QFont naturalFont = qApp->font(); + naturalFont.resolve(0); + QFont resolvedFont = font.resolve(naturalFont); + updateFont(resolvedFont); +} + +/*! + \internal + + Update the font, and whether or not it has changed, reresolve all fonts in + the scene. +*/ +void QGraphicsScenePrivate::updateFont(const QFont &font) +{ + Q_Q(QGraphicsScene); + + // Update local font setting. + this->font = font; + + // Resolve the fonts of all top-level widget items, or widget items + // whose parent is not a widget. + foreach (QGraphicsItem *item, q->items()) { + if (!item->parentItem()) { + // Resolvefont for an item is a noop operation, but + // every item can be a widget, or can have a widget + // childre. + item->d_ptr->resolveFont(font.resolve()); + } + } + + // Send the scene a FontChange event. + QEvent event(QEvent::FontChange); + QApplication::sendEvent(q, &event); +} + +/*! + \internal + + Set the palette and propagate the changes if the palette is different from + the current palette. +*/ +void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette) +{ + if (this->palette == palette && this->palette.resolve() == palette.resolve()) + return; + updatePalette(palette); +} + +/*! + \internal + + Resolve the scene's palette against the application palette, and propagate + the changes too all items in the scene. +*/ +void QGraphicsScenePrivate::resolvePalette() +{ + QPalette naturalPalette = qApp->palette(); + naturalPalette.resolve(0); + QPalette resolvedPalette = palette.resolve(naturalPalette); + updatePalette(resolvedPalette); +} + +/*! + \internal + + Update the palette, and whether or not it has changed, reresolve all + palettes in the scene. +*/ +void QGraphicsScenePrivate::updatePalette(const QPalette &palette) +{ + Q_Q(QGraphicsScene); + + // Update local palette setting. + this->palette = palette; + + // Resolve the palettes of all top-level widget items, or widget items + // whose parent is not a widget. + foreach (QGraphicsItem *item, q->items()) { + if (!item->parentItem()) { + // Resolvefont for an item is a noop operation, but + // every item can be a widget, or can have a widget + // childre. + item->d_ptr->resolvePalette(palette.resolve()); + } + } + + // Send the scene a PaletteChange event. + QEvent event(QEvent::PaletteChange); + QApplication::sendEvent(q, &event); +} + +/*! + Constructs a QGraphicsScene object. The \a parent parameter is + passed to QObject's constructor. +*/ +QGraphicsScene::QGraphicsScene(QObject *parent) + : QObject(*new QGraphicsScenePrivate, parent) +{ + d_func()->init(); +} + +/*! + Constructs a QGraphicsScene object, using \a sceneRect for its + scene rectangle. The \a parent parameter is passed to QObject's + constructor. + + \sa sceneRect +*/ +QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent) + : QObject(*new QGraphicsScenePrivate, parent) +{ + setSceneRect(sceneRect); + d_func()->init(); +} + +/*! + Constructs a QGraphicsScene object, using the rectangle specified + by (\a x, \a y), and the given \a width and \a height for its + scene rectangle. The \a parent parameter is passed to QObject's + constructor. + + \sa sceneRect +*/ +QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent) + : QObject(*new QGraphicsScenePrivate, parent) +{ + setSceneRect(x, y, width, height); + d_func()->init(); +} + +/*! + Destroys the QGraphicsScene object. +*/ +QGraphicsScene::~QGraphicsScene() +{ + Q_D(QGraphicsScene); + // Remove this scene from qApp's global scene list. + qApp->d_func()->scene_list.removeAll(this); + + clear(); + + // Remove this scene from all associated views. + for (int j = 0; j < d->views.size(); ++j) + d->views.at(j)->setScene(0); +} + +/*! + \property QGraphicsScene::sceneRect + \brief the scene rectangle; the bounding rectangle of the scene + + The scene rectangle defines the extent of the scene. It is + primarily used by QGraphicsView to determine the view's default + scrollable area, and by QGraphicsScene to manage item indexing. + + If unset, or if set to a null QRectF, sceneRect() will return the largest + bounding rect of all items on the scene since the scene was created (i.e., + a rectangle that grows when items are added to or moved in the scene, but + never shrinks). + + \sa width(), height(), QGraphicsView::sceneRect +*/ +QRectF QGraphicsScene::sceneRect() const +{ + Q_D(const QGraphicsScene); + const_cast<QGraphicsScenePrivate *>(d)->_q_updateIndex(); + return d->hasSceneRect ? d->sceneRect : d->growingItemsBoundingRect; +} +void QGraphicsScene::setSceneRect(const QRectF &rect) +{ + Q_D(QGraphicsScene); + if (rect != d->sceneRect) { + d->hasSceneRect = !rect.isNull(); + d->sceneRect = rect; + d->resetIndex(); + emit sceneRectChanged(rect); + } +} + +/*! + \fn qreal QGraphicsScene::width() const + + This convenience function is equivalent to calling sceneRect().width(). + + \sa height() +*/ + +/*! + \fn qreal QGraphicsScene::height() const + + This convenience function is equivalent to calling \c sceneRect().height(). + + \sa width() +*/ + +/*! + Renders the \a source rect from scene into \a target, using \a painter. This + function is useful for capturing the contents of the scene onto a paint + device, such as a QImage (e.g., to take a screenshot), or for printing + with QPrinter. For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 1 + + If \a source is a null rect, this function will use sceneRect() to + determine what to render. If \a target is a null rect, the dimensions of \a + painter's paint device will be used. + + The source rect contents will be transformed according to \a + aspectRatioMode to fit into the target rect. By default, the aspect ratio + is kept, and \a source is scaled to fit in \a target. + + \sa QGraphicsView::render() +*/ +void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source, + Qt::AspectRatioMode aspectRatioMode) +{ + Q_D(QGraphicsScene); + + // Default source rect = scene rect + QRectF sourceRect = source; + if (sourceRect.isNull()) + sourceRect = sceneRect(); + + // Default target rect = device rect + QRectF targetRect = target; + if (targetRect.isNull()) { + if (painter->device()->devType() == QInternal::Picture) + targetRect = sourceRect; + else + targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height()); + } + + // Find the ideal x / y scaling ratio to fit \a source into \a target. + qreal xratio = targetRect.width() / sourceRect.width(); + qreal yratio = targetRect.height() / sourceRect.height(); + + // Scale according to the aspect ratio mode. + switch (aspectRatioMode) { + case Qt::KeepAspectRatio: + xratio = yratio = qMin(xratio, yratio); + break; + case Qt::KeepAspectRatioByExpanding: + xratio = yratio = qMax(xratio, yratio); + break; + case Qt::IgnoreAspectRatio: + break; + } + + // Find all items to draw, and reverse the list (we want to draw + // in reverse order). + QList<QGraphicsItem *> itemList = items(sourceRect, Qt::IntersectsItemBoundingRect); + QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()]; + int numItems = itemList.size(); + for (int i = 0; i < numItems; ++i) + itemArray[numItems - i - 1] = itemList.at(i); + itemList.clear(); + + painter->save(); + + // Transform the painter. + painter->setClipRect(targetRect); + QTransform painterTransform; + painterTransform *= QTransform() + .translate(targetRect.left(), targetRect.top()) + .scale(xratio, yratio) + .translate(-sourceRect.left(), -sourceRect.top()); + painter->setWorldTransform(painterTransform, true); + + // Two unit vectors. + QLineF v1(0, 0, 1, 0); + QLineF v2(0, 0, 0, 1); + + // Generate the style options + QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems]; + for (int i = 0; i < numItems; ++i) { + QGraphicsItem *item = itemArray[i]; + + QStyleOptionGraphicsItem option; + option.state = QStyle::State_None; + option.rect = item->boundingRect().toRect(); + if (item->isSelected()) + option.state |= QStyle::State_Selected; + if (item->isEnabled()) + option.state |= QStyle::State_Enabled; + if (item->hasFocus()) + option.state |= QStyle::State_HasFocus; + if (d->hoverItems.contains(item)) + option.state |= QStyle::State_MouseOver; + if (item == mouseGrabberItem()) + option.state |= QStyle::State_Sunken; + + // Calculate a simple level-of-detail metric. + // ### almost identical code in QGraphicsView::paintEvent() + // and QGraphicsView::render() - consider refactoring + QTransform itemToDeviceTransform; + if (item->d_ptr->itemIsUntransformable()) { + itemToDeviceTransform = item->deviceTransform(painterTransform); + } else { + itemToDeviceTransform = item->sceneTransform() * painterTransform; + } + + option.levelOfDetail = qSqrt(itemToDeviceTransform.map(v1).length() * itemToDeviceTransform.map(v2).length()); + option.matrix = itemToDeviceTransform.toAffine(); //### discards perspective + + option.exposedRect = item->boundingRect(); + option.exposedRect &= itemToDeviceTransform.inverted().mapRect(targetRect); + + styleOptionArray[i] = option; + } + + // Render the scene. + drawBackground(painter, sourceRect); + drawItems(painter, numItems, itemArray, styleOptionArray); + drawForeground(painter, sourceRect); + + delete [] itemArray; + delete [] styleOptionArray; + + painter->restore(); +} + +/*! + \property QGraphicsScene::itemIndexMethod + \brief the item indexing method. + + QGraphicsScene applies an indexing algorithm to the scene, to speed up + item discovery functions like items() and itemAt(). Indexing is most + efficient for static scenes (i.e., where items don't move around). For + dynamic scenes, or scenes with many animated items, the index bookkeeping + can outweight the fast lookup speeds. + + For the common case, the default index method BspTreeIndex works fine. If + your scene uses many animations and you are experiencing slowness, you can + disable indexing by calling \c setItemIndexMethod(NoIndex). + + \sa bspTreeDepth +*/ +QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const +{ + Q_D(const QGraphicsScene); + return d->indexMethod; +} +void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method) +{ + Q_D(QGraphicsScene); + d->resetIndex(); + d->indexMethod = method; +} + +/*! + \property QGraphicsScene::bspTreeDepth + \brief the depth of QGraphicsScene's BSP index tree + \since 4.3 + + This property has no effect when NoIndex is used. + + This value determines the depth of QGraphicsScene's BSP tree. The depth + directly affects QGraphicsScene's performance and memory usage; the latter + growing exponentially with the depth of the tree. With an optimal tree + depth, QGraphicsScene can instantly determine the locality of items, even + for scenes with thousands or millions of items. This also greatly improves + rendering performance. + + By default, the value is 0, in which case Qt will guess a reasonable + default depth based on the size, location and number of items in the + scene. If these parameters change frequently, however, you may experience + slowdowns as QGraphicsScene retunes the depth internally. You can avoid + potential slowdowns by fixating the tree depth through setting this + property. + + The depth of the tree and the size of the scene rectangle decide the + granularity of the scene's partitioning. The size of each scene segment is + determined by the following algorithm: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 2 + + The BSP tree has an optimal size when each segment contains between 0 and + 10 items. + + \sa itemIndexMethod +*/ +int QGraphicsScene::bspTreeDepth() const +{ + Q_D(const QGraphicsScene); + return d->bspTreeDepth; +} +void QGraphicsScene::setBspTreeDepth(int depth) +{ + Q_D(QGraphicsScene); + if (d->bspTreeDepth == depth) + return; + + if (depth < 0) { + qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth); + return; + } + + d->bspTreeDepth = depth; + d->resetIndex(); +} + +/*! + \property QGraphicsScene::sortCacheEnabled + \brief whether sort caching is enabled + \since 4.5 + + When enabled, this property adds a cache that speeds up sorting and + transformations for scenes with deep hierarchies (i.e., items with many + levels of descendents), at the cost of using more memory (approx. 100 more + bytes of memory per item). + + Items that are not part of a deep hierarchy suffer no penalty from this + cache. +*/ +bool QGraphicsScene::isSortCacheEnabled() const +{ + Q_D(const QGraphicsScene); + return d->sortCacheEnabled; +} +void QGraphicsScene::setSortCacheEnabled(bool enabled) +{ + Q_D(QGraphicsScene); + if (enabled == d->sortCacheEnabled) + return; + if ((d->sortCacheEnabled = enabled)) + d->invalidateSortCache(); +} + +/*! + Calculates and returns the bounding rect of all items on the scene. This + function works by iterating over all items, and because if this, it can + be slow for large scenes. + + \sa sceneRect() +*/ +QRectF QGraphicsScene::itemsBoundingRect() const +{ + QRectF boundingRect; + foreach (QGraphicsItem *item, items()) + boundingRect |= item->sceneBoundingRect(); + return boundingRect; +} + +/*! + Returns a list of all items on the scene, in no particular order. + + \sa addItem(), removeItem() +*/ +QList<QGraphicsItem *> QGraphicsScene::items() const +{ + Q_D(const QGraphicsScene); + const_cast<QGraphicsScenePrivate *>(d)->purgeRemovedItems(); + + // If freeItemIndexes is empty, we know there are no holes in indexedItems and + // unindexedItems. + if (d->freeItemIndexes.isEmpty()) { + if (d->unindexedItems.isEmpty()) + return d->indexedItems; + return d->indexedItems + d->unindexedItems; + } + + // Rebuild the list of items to avoid holes. ### We could also just + // compress the item lists at this point. + QList<QGraphicsItem *> itemList; + foreach (QGraphicsItem *item, d->indexedItems + d->unindexedItems) { + if (item) + itemList << item; + } + return itemList; +} + +/*! + Returns all visible items at position \a pos in the scene. The items are + listed in descending Z order (i.e., the first item in the list is the + top-most item, and the last item is the bottom-most item). + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const +{ + QList<QGraphicsItem *> itemsAtPoint; + + // Find all items within a 1x1 rect area starting at pos. This can be + // inefficient for scenes that use small coordinates (like unity + // coordinates), or for detailed graphs. ### The index should support + // fetching items at a pos to avoid this limitation. + foreach (QGraphicsItem *item, items(QRectF(pos, QSizeF(1, 1)), Qt::IntersectsItemBoundingRect)) { + if (item->contains(item->mapFromScene(pos))) + itemsAtPoint << item; + } + return itemsAtPoint; +} + + +/*! + \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const + + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the specified \a rectangle. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rectangle are returned. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsScene); + return d->items_helper(rect, mode, Qt::AscendingOrder); +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode) const + \since 4.3 + + This convenience function is equivalent to calling items(QRectF(\a x, \a y, \a w, \a h), \a mode). +*/ + +/*! + \overload + + Returns all visible items that, depending on \a mode, are either inside or + intersect with the polygon \a polygon. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsScene); + return d->items_helper(polygon, mode, Qt::AscendingOrder); +} + +/*! + \overload + + Returns all visible items that, depending on \a path, are either inside or + intersect with the path \a path. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \sa itemAt() +*/ +QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsScene); + return d->items_helper(path, mode, Qt::AscendingOrder); +} + +/*! + Returns a list of all items that collide with \a item. Collisions are + determined by calling QGraphicsItem::collidesWithItem(); the collision + detection is determined by \a mode. By default, all items whose shape + intersects \a item or is contained inside \a item's shape are returned. + + The items are returned in descending Z order (i.e., the first item in the + list is the top-most item, and the last item is the bottom-most item). + + \sa items(), itemAt(), QGraphicsItem::collidesWithItem() +*/ +QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item, + Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsScene); + if (!item) { + qWarning("QGraphicsScene::collidingItems: cannot find collisions for null item"); + return QList<QGraphicsItem *>(); + } + + QList<QGraphicsItem *> tmp; + foreach (QGraphicsItem *itemInVicinity, d->estimateItemsInRect(item->sceneBoundingRect())) { + if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode)) + tmp << itemInVicinity; + } + d->sortItems(&tmp, Qt::AscendingOrder, d->sortCacheEnabled); + return tmp; +} + +/*! + \fn QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const + + Returns the topmost visible item at the specified \a position, or 0 if + there are no items at this position. + + \note The topmost item is the one with the highest Z-value. + + \sa items(), collidingItems(), QGraphicsItem::setZValue() +*/ +QGraphicsItem *QGraphicsScene::itemAt(const QPointF &pos) const +{ + QList<QGraphicsItem *> itemsAtPoint = items(pos); + return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first(); +} + +/*! + \fn QGraphicsScene::itemAt(qreal x, qreal y) const + \overload + + Returns the topmost item at the position specified by (\a x, \a + y), or 0 if there are no items at this position. + + This convenience function is equivalent to calling \c + {itemAt(QPointF(x, y))}. + + \note The topmost item is the one with the highest Z-value. +*/ + +/*! + Returns a list of all currently selected items. The items are + returned in no particular order. + + \sa setSelectionArea() +*/ +QList<QGraphicsItem *> QGraphicsScene::selectedItems() const +{ + Q_D(const QGraphicsScene); + + // Optimization: Lazily removes items that are not selected. + QGraphicsScene *that = const_cast<QGraphicsScene *>(this); + QSet<QGraphicsItem *> actuallySelectedSet; + foreach (QGraphicsItem *item, that->d_func()->selectedItems) { + if (item->isSelected()) + actuallySelectedSet << item; + } + + that->d_func()->selectedItems = actuallySelectedSet; + + return d->selectedItems.values(); +} + +/*! + Returns the selection area that was previously set with + setSelectionArea(), or an empty QPainterPath if no selection area has been + set. + + \sa setSelectionArea() +*/ +QPainterPath QGraphicsScene::selectionArea() const +{ + Q_D(const QGraphicsScene); + return d->selectionArea; +} + +/*! + Sets the selection area to \a path. All items within this area are + immediately selected, and all items outside are unselected. You can get + the list of all selected items by calling selectedItems(). + + For an item to be selected, it must be marked as \e selectable + (QGraphicsItem::ItemIsSelectable). + + \sa clearSelection(), selectionArea() +*/ +void QGraphicsScene::setSelectionArea(const QPainterPath &path) +{ + setSelectionArea(path, Qt::IntersectsItemShape); +} + +/*! + \overload + \since 4.3 + + Sets the selection area to \a path using \a mode to determine if items are + included in the selection area. + + \sa clearSelection(), selectionArea() +*/ +void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode) +{ + Q_D(QGraphicsScene); + + // Note: with boolean path operations, we can improve performance here + // quite a lot by "growing" the old path instead of replacing it. That + // allows us to only check the intersect area for changes, instead of + // reevaluating the whole path over again. + d->selectionArea = path; + + QSet<QGraphicsItem *> unselectItems = d->selectedItems; + + // Disable emitting selectionChanged() for individual items. + ++d->selectionChanging; + bool changed = false; + + // Set all items in path to selected. + foreach (QGraphicsItem *item, items(path, mode)) { + if (item->flags() & QGraphicsItem::ItemIsSelectable) { + if (!item->isSelected()) + changed = true; + unselectItems.remove(item); + item->setSelected(true); + } + } + + // Unselect all items outside path. + foreach (QGraphicsItem *item, unselectItems) { + item->setSelected(false); + changed = true; + } + + // Reenable emitting selectionChanged() for individual items. + --d->selectionChanging; + + if (!d->selectionChanging && changed) + emit selectionChanged(); +} + +/*! + Clears the current selection. + + \sa setSelectionArea(), selectedItems() +*/ +void QGraphicsScene::clearSelection() +{ + Q_D(QGraphicsScene); + + // Disable emitting selectionChanged + ++d->selectionChanging; + bool changed = !d->selectedItems.isEmpty(); + + foreach (QGraphicsItem *item, d->selectedItems) + item->setSelected(false); + d->selectedItems.clear(); + + // Reenable emitting selectionChanged() for individual items. + --d->selectionChanging; + + if (!d->selectionChanging && changed) + emit selectionChanged(); +} + +/*! + \since 4.4 + + Removes and deletes all items from the scene, but otherwise leaves the + state of the scene unchanged. + + \sa addItem() +*/ +void QGraphicsScene::clear() +{ + Q_D(QGraphicsScene); + // Recursive descent delete + for (int i = 0; i < d->indexedItems.size(); ++i) { + if (QGraphicsItem *item = d->indexedItems.at(i)) { + if (!item->parentItem()) + delete item; + } + } + QList<QGraphicsItem *> unindexedParents; + for (int i = 0; i < d->unindexedItems.size(); ++i) { + QGraphicsItem *item = d->unindexedItems.at(i); + if (!item->parentItem()) + unindexedParents << item; + } + d->unindexedItems.clear(); + qDeleteAll(unindexedParents); + d->indexedItems.clear(); + d->freeItemIndexes.clear(); + d->lastItemCount = 0; + d->bspTree.clear(); + d->largestUntransformableItem = QRectF(); +} + +/*! + Groups all items in \a items into a new QGraphicsItemGroup, and returns a + pointer to the group. The group is created with the common ancestor of \a + items as its parent, and with position (0, 0). The items are all + reparented to the group, and their positions and transformations are + mapped to the group. If \a items is empty, this function will return an + empty top-level QGraphicsItemGroup. + + QGraphicsScene has ownership of the group item; you do not need to delete + it. To dismantle (ungroup) a group, call destroyItemGroup(). + + \sa destroyItemGroup(), QGraphicsItemGroup::addToGroup() +*/ +QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items) +{ + // Build a list of the first item's ancestors + QList<QGraphicsItem *> ancestors; + int n = 0; + if (!items.isEmpty()) { + QGraphicsItem *parent = items.at(n++); + while ((parent = parent->parentItem())) + ancestors.append(parent); + } + + // Find the common ancestor for all items + QGraphicsItem *commonAncestor = 0; + if (!ancestors.isEmpty()) { + while (n < items.size()) { + int commonIndex = -1; + QGraphicsItem *parent = items.at(n++); + do { + int index = ancestors.indexOf(parent, qMax(0, commonIndex)); + if (index != -1) { + commonIndex = index; + break; + } + } while ((parent = parent->parentItem())); + + if (commonIndex == -1) { + commonAncestor = 0; + break; + } + + commonAncestor = ancestors.at(commonIndex); + } + } + + // Create a new group at that level + QGraphicsItemGroup *group = new QGraphicsItemGroup(commonAncestor); + if (!commonAncestor) + addItem(group); + foreach (QGraphicsItem *item, items) + group->addToGroup(item); + return group; +} + +/*! + Reparents all items in \a group to \a group's parent item, then removes \a + group from the scene, and finally deletes it. The items' positions and + transformations are mapped from the group to the group's parent. + + \sa createItemGroup(), QGraphicsItemGroup::removeFromGroup() +*/ +void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group) +{ + foreach (QGraphicsItem *item, group->children()) + group->removeFromGroup(item); + removeItem(group); + delete group; +} + +/*! + Adds or moves the item \a item and all its childen to the scene. + + If the item is visible (i.e., QGraphicsItem::isVisible() returns + true), QGraphicsScene will emit changed() once control goes back + to the event loop. + + If the item is already in a different scene, it will first be removed from + its old scene, and then added to this scene as a top-level. + + QGraphicsScene will send ItemSceneChange notifications to \a item while + it is added to the scene. If item does not currently belong to a scene, only one + notification is sent. If it does belong to scene already (i.e., it is + moved to this scene), QGraphicsScene will send an addition notification as + the item is removed from its previous scene. + + \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(), + addRect(), addText(), addWidget() +*/ +void QGraphicsScene::addItem(QGraphicsItem *item) +{ + Q_D(QGraphicsScene); + if (!item) { + qWarning("QGraphicsScene::addItem: cannot add null item"); + return; + } + if (item->scene() == this) { + qWarning("QGraphicsScene::addItem: item has already been added to this scene"); + return; + } + // Remove this item from its existing scene + if (QGraphicsScene *oldScene = item->scene()) + oldScene->removeItem(item); + + // Notify the item that its scene is changing, and allow the item to + // react. + QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange, + qVariantFromValue<QGraphicsScene *>(this))); + if (targetScene != this) { + if (targetScene && item->scene() != targetScene) + targetScene->addItem(item); + return; + } + + // Prevent reusing a recently deleted pointer: purge all removed items + // from our lists. + d->purgeRemovedItems(); + + // Invalidate any sort caching; arrival of a new item means we need to + // resort. + d->invalidateSortCache(); + + // Detach this item from its parent if the parent's scene is different + // from this scene. + if (QGraphicsItem *itemParent = item->parentItem()) { + if (itemParent->scene() != this) + item->setParentItem(0); + } + + // Add the item to this scene + item->d_func()->scene = targetScene; + + // Indexing requires sceneBoundingRect(), but because \a item might + // not be completely constructed at this point, we need to store it in + // a temporary list and schedule an indexing for later. + d->unindexedItems << item; + item->d_func()->index = -1; + d->startIndexTimer(); + + // Add to list of toplevels if this item is a toplevel. + if (!item->d_ptr->parent) + d->registerTopLevelItem(item); + + // Update the scene's sort cache settings. + item->d_ptr->globalStackingOrder = -1; + d->invalidateSortCache(); + + // Add to list of items that require an update. We cannot assume that the + // item is fully constructed, so calling item->update() can lead to a pure + // virtual function call to boundingRect(). + if (!d->updateAll) { + if (d->pendingUpdateItems.isEmpty()) + QMetaObject::invokeMethod(this, "_q_updateLater", Qt::QueuedConnection); + d->pendingUpdateItems << item; + } + + // Disable selectionChanged() for individual items + ++d->selectionChanging; + int oldSelectedItemSize = d->selectedItems.size(); + + // Update selection lists + if (item->isSelected()) + d->selectedItems << item; + if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup) + d->addPopup(static_cast<QGraphicsWidget *>(item)); + + // Update creation order focus chain. Make sure to leave the widget's + // internal tab order intact. + if (item->isWidget()) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + if (!d->tabFocusFirst) { + // No first tab focus widget - make this the first tab focus + // widget. + d->tabFocusFirst = widget; + } else if (!widget->parentWidget()) { + // Adding a widget that is not part of a tab focus chain. + QGraphicsWidget *last = d->tabFocusFirst->d_func()->focusPrev; + QGraphicsWidget *lastNew = widget->d_func()->focusPrev; + last->d_func()->focusNext = widget; + widget->d_func()->focusPrev = last; + d->tabFocusFirst->d_func()->focusPrev = lastNew; + lastNew->d_func()->focusNext = d->tabFocusFirst; + } + } + + // Add all children recursively + foreach (QGraphicsItem *child, item->children()) + addItem(child); + + // Resolve font and palette. + item->d_ptr->resolveFont(d->font.resolve()); + item->d_ptr->resolvePalette(d->palette.resolve()); + + if (!item->d_ptr->explicitlyHidden) { + if (d->unpolishedItems.isEmpty()) + QMetaObject::invokeMethod(this, "_q_polishItems", Qt::QueuedConnection); + d->unpolishedItems << item; + } + + // Reenable selectionChanged() for individual items + --d->selectionChanging; + if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize) + emit selectionChanged(); + + // Deliver post-change notification + item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(this)); +} + +/*! + Creates and adds an ellipse item to the scene, and returns the item + pointer. The geometry of the ellipse is defined by \a rect, and its pen + and brush are initialized to \a pen and \a brush. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addLine(), addPath(), addPixmap(), addRect(), addText(), addItem(), + addWidget() +*/ +QGraphicsEllipseItem *QGraphicsScene::addEllipse(const QRectF &rect, const QPen &pen, const QBrush &brush) +{ + QGraphicsEllipseItem *item = new QGraphicsEllipseItem(rect); + item->setPen(pen); + item->setBrush(brush); + addItem(item); + return item; +} + +/*! + \fn QGraphicsEllipseItem *QGraphicsScene::addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush) + \since 4.3 + + This convenience function is equivalent to calling addEllipse(QRectF(\a x, + \a y, \a w, \a h), \a pen, \a brush). +*/ + +/*! + Creates and adds a line item to the scene, and returns the item + pointer. The geometry of the line is defined by \a line, and its pen + is initialized to \a pen. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addPath(), addPixmap(), addRect(), addText(), addItem(), + addWidget() +*/ +QGraphicsLineItem *QGraphicsScene::addLine(const QLineF &line, const QPen &pen) +{ + QGraphicsLineItem *item = new QGraphicsLineItem(line); + item->setPen(pen); + addItem(item); + return item; +} + +/*! + \fn QGraphicsLineItem *QGraphicsScene::addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen) + \since 4.3 + + This convenience function is equivalent to calling addLine(QLineF(\a x1, + \a y1, \a x2, \a y2), \a pen). +*/ + +/*! + Creates and adds a path item to the scene, and returns the item + pointer. The geometry of the path is defined by \a path, and its pen and + brush are initialized to \a pen and \a brush. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPixmap(), addRect(), addText(), addItem(), + addWidget() +*/ +QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen, const QBrush &brush) +{ + QGraphicsPathItem *item = new QGraphicsPathItem(path); + item->setPen(pen); + item->setBrush(brush); + addItem(item); + return item; +} + +/*! + Creates and adds a pixmap item to the scene, and returns the item + pointer. The pixmap is defined by \a pixmap. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(), + addWidget() +*/ +QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap) +{ + QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap); + addItem(item); + return item; +} + +/*! + Creates and adds a polygon item to the scene, and returns the item + pointer. The polygon is defined by \a polygon, and its pen and + brush are initialized to \a pen and \a brush. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(), + addWidget() +*/ +QGraphicsPolygonItem *QGraphicsScene::addPolygon(const QPolygonF &polygon, + const QPen &pen, const QBrush &brush) +{ + QGraphicsPolygonItem *item = new QGraphicsPolygonItem(polygon); + item->setPen(pen); + item->setBrush(brush); + addItem(item); + return item; +} + +/*! + Creates and adds a rectangle item to the scene, and returns the item + pointer. The geometry of the rectangle is defined by \a rect, and its pen + and brush are initialized to \a pen and \a brush. + + Note that the item's geometry is provided in item coordinates, and its + position is initialized to (0, 0). For example, if a QRect(50, 50, 100, + 100) is added, its top-left corner will be at (50, 50) relative to the + origin in the items coordinate system. + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addText(), + addItem(), addWidget() +*/ +QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const QPen &pen, const QBrush &brush) +{ + QGraphicsRectItem *item = new QGraphicsRectItem(rect); + item->setPen(pen); + item->setBrush(brush); + addItem(item); + return item; +} + +/*! + \fn QGraphicsRectItem *QGraphicsScene::addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush) + \since 4.3 + + This convenience function is equivalent to calling addRect(QRectF(\a x, + \a y, \a w, \a h), \a pen, \a brush). +*/ + +/*! + Creates and adds a text item to the scene, and returns the item + pointer. The text string is initialized to \a text, and its font + is initialized to \a font. + + The item's position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(), + addItem(), addWidget() +*/ +QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font) +{ + QGraphicsTextItem *item = new QGraphicsTextItem(text); + item->setFont(font); + addItem(item); + return item; +} + +/*! + Creates and adds a QGraphicsSimpleTextItem to the scene, and returns the + item pointer. The text string is initialized to \a text, and its font is + initialized to \a font. + + The item's position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(), + addItem(), addWidget() +*/ +QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, const QFont &font) +{ + QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text); + item->setFont(font); + addItem(item); + return item; +} + +/*! + Creates a new QGraphicsProxyWidget for \a widget, adds it to the scene, + and returns a pointer to the proxy. \a wFlags set the default window flags + for the embedding proxy widget. + + The item's position is initialized to (0, 0). + + If the item is visible (i.e., QGraphicsItem::isVisible() returns true), + QGraphicsScene will emit changed() once control goes back to the event + loop. + + Note that widgets with the Qt::WA_PaintOnScreen widget attribute + set and widgets that wrap an external application or controller + are not supported. Examples are QGLWidget and QAxWidget. + + \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(), + addText(), addSimpleText(), addItem() +*/ +QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags) +{ + QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, wFlags); + proxy->setWidget(widget); + addItem(proxy); + return proxy; +} + +/*! + Removes the item \a item and all its children from the scene. The + ownership of \a item is passed on to the caller (i.e., + QGraphicsScene will no longer delete \a item when destroyed). + + \sa addItem() +*/ +void QGraphicsScene::removeItem(QGraphicsItem *item) +{ + // ### Refactoring: This function shares much functionality with _q_removeItemLater() + Q_D(QGraphicsScene); + if (!item) { + qWarning("QGraphicsScene::removeItem: cannot remove 0-item"); + return; + } + if (item->scene() != this) { + qWarning("QGraphicsScene::removeItem: item %p's scene (%p)" + " is different from this scene (%p)", + item, item->scene(), this); + return; + } + + // Notify the item that it's scene is changing to 0, allowing the item to + // react. + QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(item->itemChange(QGraphicsItem::ItemSceneChange, + qVariantFromValue<QGraphicsScene *>(0))); + if (targetScene != 0 && targetScene != this) { + targetScene->addItem(item); + return; + } + + // If the item has focus, remove it (and any focusWidget reference). + item->clearFocus(); + + // Clear its background + item->update(); + + // Note: This will access item's sceneBoundingRect(), which (as this is + // C++) is why we cannot call removeItem() from QGraphicsItem's + // destructor. + d->removeFromIndex(item); + + if (item == d->tabFocusFirst) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + widget->d_func()->fixFocusChainBeforeReparenting(0, 0); + } + // Set the item's scene ptr to 0. + item->d_func()->scene = 0; + + // Remove from parent, or unregister from toplevels. + if (QGraphicsItem *parentItem = item->parentItem()) { + if (parentItem->scene()) { + Q_ASSERT_X(parentItem->scene() == this, "QGraphicsScene::removeItem", + "Parent item's scene is different from this item's scene"); + item->setParentItem(0); + } + } else { + d->unregisterTopLevelItem(item); + } + + // Remove from our item lists. + int index = item->d_func()->index; + if (index != -1) { + d->freeItemIndexes << index; + d->indexedItems[index] = 0; + } else { + d->unindexedItems.removeAll(item); + } + + // Remove from scene transform cache + int transformIndex = item->d_func()->sceneTransformIndex; + if (transformIndex != -1) { + d->validTransforms.setBit(transformIndex, 0); + d->freeSceneTransformSlots.append(transformIndex); + item->d_func()->sceneTransformIndex = -1; + } + + if (item == d->focusItem) + d->focusItem = 0; + if (item == d->lastFocusItem) + d->lastFocusItem = 0; + if (item == d->activeWindow) { + // ### deactivate... + d->activeWindow = 0; + } + + // Disable selectionChanged() for individual items + ++d->selectionChanging; + int oldSelectedItemsSize = d->selectedItems.size(); + + // Update selected & hovered item bookkeeping + d->selectedItems.remove(item); + d->hoverItems.removeAll(item); + d->pendingUpdateItems.removeAll(item); + d->cachedItemsUnderMouse.removeAll(item); + d->unpolishedItems.removeAll(item); + d->dirtyItems.removeAll(item); + + //Ensure dirty flag have the correct default value so the next time it will be added it will receive updates + item->d_func()->dirty = 0; + item->d_func()->dirtyChildren = 0; + + // Remove all children recursively + foreach (QGraphicsItem *child, item->children()) + removeItem(child); + + // Reset the mouse grabber and focus item data. + if (d->mouseGrabberItems.contains(item)) + d->ungrabMouse(item); + + // Reset the keyboard grabber + if (d->keyboardGrabberItems.contains(item)) + item->ungrabKeyboard(); + + // Reset the last mouse grabber item + if (item == d->lastMouseGrabberItem) + d->lastMouseGrabberItem = 0; + + // Reenable selectionChanged() for individual items + --d->selectionChanging; + + if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemsSize) + emit selectionChanged(); + + // Deliver post-change notification + item->itemChange(QGraphicsItem::ItemSceneHasChanged, qVariantFromValue<QGraphicsScene *>(0)); +} + +/*! + Returns the scene's current focus item, or 0 if no item currently has + focus. + + The focus item receives keyboard input when the scene receives a + key event. + + \sa setFocusItem(), QGraphicsItem::hasFocus() +*/ +QGraphicsItem *QGraphicsScene::focusItem() const +{ + Q_D(const QGraphicsScene); + return d->focusItem; +} + +/*! + Sets the scene's focus item to \a item, with the focus reason \a + focusReason, after removing focus from any previous item that may have had + focus. + + If \a item is 0, or if it either does not accept focus (i.e., it does not + have the QGraphicsItem::ItemIsFocusable flag enabled), or is not visible + or not enabled, this function only removes focus from any previous + focusitem. + + If item is not 0, and the scene does not currently have focus (i.e., + hasFocus() returns false), this function will call setFocus() + automatically. + + \sa focusItem(), hasFocus(), setFocus() +*/ +void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason) +{ + Q_D(QGraphicsScene); + if (item == d->focusItem) + return; + if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable) + || !item->isVisible() || !item->isEnabled())) { + item = 0; + } + + if (item) { + setFocus(focusReason); + if (item == d->focusItem) + return; + } + + if (d->focusItem) { + QFocusEvent event(QEvent::FocusOut, focusReason); + d->lastFocusItem = d->focusItem; + d->focusItem = 0; + d->sendEvent(d->lastFocusItem, &event); + } + + if (item) { + if (item->isWidget()) { + // Update focus child chain. + static_cast<QGraphicsWidget *>(item)->d_func()->setFocusWidget(); + } + + d->focusItem = item; + QFocusEvent event(QEvent::FocusIn, focusReason); + d->sendEvent(item, &event); + } +} + +/*! + Returns true if the scene has focus; otherwise returns false. If the scene + has focus, it will will forward key events from QKeyEvent to any item that + has focus. + + \sa setFocus(), setFocusItem() +*/ +bool QGraphicsScene::hasFocus() const +{ + Q_D(const QGraphicsScene); + return d->hasFocus; +} + +/*! + Sets focus on the scene by sending a QFocusEvent to the scene, passing \a + focusReason as the reason. If the scene regains focus after having + previously lost it while an item had focus, the last focus item will + receive focus with \a focusReason as the reason. + + If the scene already has focus, this function does nothing. + + \sa hasFocus(), clearFocus(), setFocusItem() +*/ +void QGraphicsScene::setFocus(Qt::FocusReason focusReason) +{ + Q_D(QGraphicsScene); + if (d->hasFocus) + return; + QFocusEvent event(QEvent::FocusIn, focusReason); + QCoreApplication::sendEvent(this, &event); +} + +/*! + Clears focus from the scene. If any item has focus when this function is + called, it will lose focus, and regain focus again once the scene regains + focus. + + A scene that does not have focus ignores key events. + + \sa hasFocus(), setFocus(), setFocusItem() +*/ +void QGraphicsScene::clearFocus() +{ + Q_D(QGraphicsScene); + if (d->hasFocus) { + d->hasFocus = false; + setFocusItem(0, Qt::OtherFocusReason); + } +} + +/*! + \property QGraphicsScene::stickyFocus + \brief whether or not clicking the scene will clear focus + + If this property is false (the default), then clicking on the scene + background or on an item that does not accept focus, will clear + focus. Otherwise, focus will remain unchanged. + + The focus change happens in response to a mouse press. You can reimplement + mousePressEvent() in a subclass of QGraphicsScene to toggle this property + based on where the user has clicked. + + \sa clearFocus(), setFocusItem() +*/ +void QGraphicsScene::setStickyFocus(bool enabled) +{ + Q_D(QGraphicsScene); + d->stickyFocus = enabled; +} +bool QGraphicsScene::stickyFocus() const +{ + Q_D(const QGraphicsScene); + return d->stickyFocus; +} + +/*! + Returns the current mouse grabber item, or 0 if no item is currently + grabbing the mouse. The mouse grabber item is the item that receives all + mouse events sent to the scene. + + An item becomes a mouse grabber when it receives and accepts a + mouse press event, and it stays the mouse grabber until either of + the following events occur: + + \list + \o If the item receives a mouse release event when there are no other + buttons pressed, it loses the mouse grab. + \o If the item becomes invisible (i.e., someone calls \c {item->setVisible(false))}, + or if it becomes disabled (i.e., someone calls \c {item->setEnabled(false))}, + it loses the mouse grab. + \o If the item is removed from the scene, it loses the mouse grab. + \endlist + + If the item loses its mouse grab, the scene will ignore all mouse events + until a new item grabs the mouse (i.e., until a new item receives a mouse + press event). +*/ +QGraphicsItem *QGraphicsScene::mouseGrabberItem() const +{ + Q_D(const QGraphicsScene); + return !d->mouseGrabberItems.isEmpty() ? d->mouseGrabberItems.last() : 0; +} + +/*! + \property QGraphicsScene::backgroundBrush + \brief the background brush of the scene. + + Set this property to changes the scene's background to a different color, + gradient or texture. The default background brush is Qt::NoBrush. The + background is drawn before (behind) the items. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 3 + + QGraphicsScene::render() calls drawBackground() to draw the scene + background. For more detailed control over how the background is drawn, + you can reimplement drawBackground() in a subclass of QGraphicsScene. +*/ +QBrush QGraphicsScene::backgroundBrush() const +{ + Q_D(const QGraphicsScene); + return d->backgroundBrush; +} +void QGraphicsScene::setBackgroundBrush(const QBrush &brush) +{ + Q_D(QGraphicsScene); + d->backgroundBrush = brush; + foreach (QGraphicsView *view, d->views) { + view->resetCachedContent(); + view->viewport()->update(); + } + update(); +} + +/*! + \property QGraphicsScene::foregroundBrush + \brief the foreground brush of the scene. + + Change this property to set the scene's foreground to a different + color, gradient or texture. + + The foreground is drawn after (on top of) the items. The default + foreground brush is Qt::NoBrush ( i.e. the foreground is not + drawn). + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 4 + + QGraphicsScene::render() calls drawForeground() to draw the scene + foreground. For more detailed control over how the foreground is + drawn, you can reimplement the drawForeground() function in a + QGraphicsScene subclass. +*/ +QBrush QGraphicsScene::foregroundBrush() const +{ + Q_D(const QGraphicsScene); + return d->foregroundBrush; +} +void QGraphicsScene::setForegroundBrush(const QBrush &brush) +{ + Q_D(QGraphicsScene); + d->foregroundBrush = brush; + foreach (QGraphicsView *view, views()) + view->viewport()->update(); + update(); +} + +/*! + This method is used by input methods to query a set of properties of + the scene to be able to support complex input method operations as support + for surrounding text and reconversions. + + The \a query parameter specifies which property is queried. + + \sa QWidget::inputMethodQuery() +*/ +QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QGraphicsScene); + if (!d->focusItem) + return QVariant(); + const QTransform matrix = d->focusItem->sceneTransform(); + QVariant value = d->focusItem->inputMethodQuery(query); + if (value.type() == QVariant::RectF) + value = matrix.mapRect(value.toRectF()); + else if (value.type() == QVariant::PointF) + value = matrix.map(value.toPointF()); + else if (value.type() == QVariant::Rect) + value = matrix.mapRect(value.toRect()); + else if (value.type() == QVariant::Point) + value = matrix.map(value.toPoint()); + return value; +} + +/*! + \fn void QGraphicsScene::update(const QRectF &rect) + Schedules a redraw of the area \a rect on the scene. + + \sa sceneRect(), changed() +*/ +void QGraphicsScene::update(const QRectF &rect) +{ + Q_D(QGraphicsScene); + if (d->updateAll) + return; + + // Check if anyone's connected; if not, we can send updates directly to + // the views. Otherwise or if there are no views, use old behavior. + bool directUpdates = !(d->connectedSignals & d->changedSignalMask) && !d->views.isEmpty(); + if (rect.isNull()) { + d->updateAll = true; + d->updatedRects.clear(); + if (directUpdates) { + // Update all views. + for (int i = 0; i < d->views.size(); ++i) + d->views.at(i)->d_func()->updateAll(); + } + } else { + if (directUpdates) { + // Update all views. + for (int i = 0; i < d->views.size(); ++i) { + QGraphicsView *view = d->views.at(i); + view->d_func()->updateRegion(QRegion(view->mapFromScene(rect).boundingRect())); + } + } else { + d->updatedRects << rect; + } + } + + if (!directUpdates && !d->calledEmitUpdated) { + d->calledEmitUpdated = true; + QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection); + } +} + +/*! + \fn void QGraphicsScene::update(qreal x, qreal y, qreal w, qreal h) + \overload + \since 4.3 + + This function is equivalent to calling update(QRectF(\a x, \a y, \a w, + \a h)); +*/ + +/*! + Invalidates and schedules a redraw of the \a layers in \a rect on the + scene. Any cached content in \a layers is unconditionally invalidated and + redrawn. + + You can use this function overload to notify QGraphicsScene of changes to + the background or the foreground of the scene. This function is commonly + used for scenes with tile-based backgrounds to notify changes when + QGraphicsView has enabled + \l{QGraphicsView::CacheBackground}{CacheBackground}. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 5 + + Note that QGraphicsView currently supports background caching only (see + QGraphicsView::CacheBackground). This function is equivalent to calling + update() if any layer but BackgroundLayer is passed. + + \sa QGraphicsView::resetCachedContent() +*/ +void QGraphicsScene::invalidate(const QRectF &rect, SceneLayers layers) +{ + foreach (QGraphicsView *view, views()) + view->invalidateScene(rect, layers); + update(rect); +} + +/*! + \fn void QGraphicsScene::invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers) + \overload + \since 4.3 + + This convenience function is equivalent to calling invalidate(QRectF(\a x, \a + y, \a w, \a h), \a layers); +*/ + +/*! + Returns a list of all the views that display this scene. + + \sa QGraphicsView::scene() +*/ +QList <QGraphicsView *> QGraphicsScene::views() const +{ + Q_D(const QGraphicsScene); + return d->views; +} + +/*! + This slot \e advances the scene by one step, by calling + QGraphicsItem::advance() for all items on the scene. This is done in two + phases: in the first phase, all items are notified that the scene is about + to change, and in the second phase all items are notified that they can + move. In the first phase, QGraphicsItem::advance() is called passing a + value of 0 as an argument, and 1 is passed in the second phase. + + \sa QGraphicsItem::advance(), QGraphicsItemAnimation, QTimeLine +*/ +void QGraphicsScene::advance() +{ + for (int i = 0; i < 2; ++i) { + foreach (QGraphicsItem *item, items()) + item->advance(i); + } +} + +/*! + Processes the event \a event, and dispatches it to the respective + event handlers. + + In addition to calling the convenience event handlers, this + function is responsible for converting mouse move events to hover + events for when there is no mouse grabber item. Hover events are + delivered directly to items; there is no convenience function for + them. + + Unlike QWidget, QGraphicsScene does not have the convenience functions + \l{QWidget::}{enterEvent()} and \l{QWidget::}{leaveEvent()}. Use this + function to obtain those events instead. + + \sa contextMenuEvent(), keyPressEvent(), keyReleaseEvent(), + mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(), + mouseDoubleClickEvent(), focusInEvent(), focusOutEvent() +*/ +bool QGraphicsScene::event(QEvent *event) +{ + Q_D(QGraphicsScene); + + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: + case QEvent::GraphicsSceneHoverEnter: + case QEvent::GraphicsSceneHoverLeave: + case QEvent::GraphicsSceneHoverMove: + // Reset the under-mouse list to ensure that this event gets fresh + // item-under-mouse data. Be careful about this list; if people delete + // items from inside event handlers, this list can quickly end up + // having stale pointers in it. We need to clear it before dispatching + // events that use it. + d->cachedItemsUnderMouse.clear(); + default: + break; + } + + switch (event->type()) { + case QEvent::GraphicsSceneDragEnter: + dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDragMove: + dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDragLeave: + dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneDrop: + dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event)); + break; + case QEvent::GraphicsSceneContextMenu: + contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event)); + break; + case QEvent::KeyPress: + if (!d->focusItem) { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { + if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier? + bool res = false; + if (k->key() == Qt::Key_Backtab + || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) { + res = focusNextPrevChild(false); + } else if (k->key() == Qt::Key_Tab) { + res = focusNextPrevChild(true); + } + if (!res) + event->ignore(); + return true; + } + } + } + keyPressEvent(static_cast<QKeyEvent *>(event)); + break; + case QEvent::KeyRelease: + keyReleaseEvent(static_cast<QKeyEvent *>(event)); + break; + case QEvent::ShortcutOverride: { + QGraphicsItem *parent = focusItem(); + while (parent) { + d->sendEvent(parent, event); + if (event->isAccepted()) + return true; + parent = parent->parentItem(); + } + } + return false; + case QEvent::GraphicsSceneMouseMove: + mouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMousePress: + mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseRelease: + mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseDoubleClick: + mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneWheel: + wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event)); + break; + case QEvent::FocusIn: + focusInEvent(static_cast<QFocusEvent *>(event)); + break; + case QEvent::FocusOut: + focusOutEvent(static_cast<QFocusEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverEnter: + case QEvent::GraphicsSceneHoverLeave: + case QEvent::GraphicsSceneHoverMove: + d->dispatchHoverEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + case QEvent::Leave: + d->leaveScene(); + break; + case QEvent::GraphicsSceneHelp: + helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event)); + break; + case QEvent::InputMethod: + inputMethodEvent(static_cast<QInputMethodEvent *>(event)); + break; + case QEvent::WindowActivate: { + if (!d->activationRefCount++) { + // Notify all non-window widgets. + foreach (QGraphicsItem *item, items()) { + if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { + QEvent event(QEvent::WindowActivate); + QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event); + } + } + + // Restore window activation. + QGraphicsItem *nextFocusItem = d->focusItem ? d->focusItem : d->lastFocusItem; + if (nextFocusItem && nextFocusItem->window()) + setActiveWindow(static_cast<QGraphicsWidget *>(nextFocusItem)); + else if (d->tabFocusFirst && d->tabFocusFirst->isWindow()) + setActiveWindow(d->tabFocusFirst); + } + break; + } + case QEvent::WindowDeactivate: { + if (!--d->activationRefCount) { + // Remove window activation. + setActiveWindow(0); + + // Notify all non-window widgets. + foreach (QGraphicsItem *item, items()) { + if (item->isWidget() && item->isVisible() && !item->isWindow() && !item->parentWidget()) { + QEvent event(QEvent::WindowDeactivate); + QApplication::sendEvent(static_cast<QGraphicsWidget *>(item), &event); + } + } + } + break; + } + case QEvent::ApplicationFontChange: { + // Resolve the existing scene font. + d->resolveFont(); + break; + } + case QEvent::FontChange: + // Update the entire scene when the font changes. + update(); + break; + case QEvent::ApplicationPaletteChange: { + // Resolve the existing scene palette. + d->resolvePalette(); + break; + } + case QEvent::PaletteChange: + // Update the entire scene when the palette changes. + update(); + break; + case QEvent::StyleChange: + // Reresolve all widgets' styles. Update all top-level widgets' + // geometries that do not have an explicit style set. + update(); + break; + case QEvent::Timer: + if (d->indexTimerId && static_cast<QTimerEvent *>(event)->timerId() == d->indexTimerId) { + if (d->restartIndexTimer) { + d->restartIndexTimer = false; + } else { + // this call will kill the timer + d->_q_updateIndex(); + } + } + // Fallthrough intended - support timers in subclasses. + default: + return QObject::event(event); + } + return true; +} + +/*! + \reimp + + QGraphicsScene filters QApplication's events to detect palette and font + changes. +*/ +bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event) +{ + if (watched != qApp) + return false; + + switch (event->type()) { + case QEvent::ApplicationPaletteChange: + qApp->postEvent(this, new QEvent(QEvent::ApplicationPaletteChange)); + break; + case QEvent::ApplicationFontChange: + qApp->postEvent(this, new QEvent(QEvent::ApplicationFontChange)); + break; + default: + break; + } + return false; +} + +/*! + This event handler, for event \a contextMenuEvent, can be reimplemented in + a subclass to receive context menu events. The default implementation + forwards the event to the topmost item that accepts context menu events at + the position of the event. If no items accept context menu events at this + position, the event is ignored. + + \sa QGraphicsItem::contextMenuEvent() +*/ +void QGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent) +{ + Q_D(QGraphicsScene); + // Ignore by default. + contextMenuEvent->ignore(); + + // Send the event to all items at this position until one item accepts the + // event. + foreach (QGraphicsItem *item, d->itemsAtPosition(contextMenuEvent->screenPos(), + contextMenuEvent->scenePos(), + contextMenuEvent->widget())) { + contextMenuEvent->setPos(item->d_ptr->genericMapFromScene(contextMenuEvent->scenePos(), + contextMenuEvent->widget())); + contextMenuEvent->accept(); + if (!d->sendEvent(item, contextMenuEvent)) + break; + + if (contextMenuEvent->isAccepted()) + break; + } +} + +/*! + This event handler, for event \a event, can be reimplemented in a subclass + to receive drag enter events for the scene. + + The default implementation accepts the event and prepares the scene to + accept drag move events. + + \sa QGraphicsItem::dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(), + dropEvent() +*/ +void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsScene); + d->dragDropItem = 0; + d->lastDropAction = Qt::IgnoreAction; + event->accept(); +} + +/*! + This event handler, for event \a event, can be reimplemented in a subclass + to receive drag move events for the scene. + + \sa QGraphicsItem::dragMoveEvent(), dragEnterEvent(), dragLeaveEvent(), + dropEvent() +*/ +void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsScene); + event->ignore(); + + if (!d->mouseGrabberItems.isEmpty()) { + // Mouse grabbers that start drag events lose the mouse grab. + d->clearMouseGrabber(); + d->mouseGrabberButtonDownPos.clear(); + d->mouseGrabberButtonDownScenePos.clear(); + d->mouseGrabberButtonDownScreenPos.clear(); + } + + bool eventDelivered = false; + + // Find the topmost enabled items under the cursor. They are all + // candidates for accepting drag & drop events. + foreach (QGraphicsItem *item, d->itemsAtPosition(event->screenPos(), + event->scenePos(), + event->widget())) { + if (!item->isEnabled() || !item->acceptDrops()) + continue; + + if (item != d->dragDropItem) { + // Enter the new drag drop item. If it accepts the event, we send + // the leave to the parent item. + QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter); + d->cloneDragDropEvent(&dragEnter, event); + dragEnter.setDropAction(event->proposedAction()); + d->sendDragDropEvent(item, &dragEnter); + event->setAccepted(dragEnter.isAccepted()); + event->setDropAction(dragEnter.dropAction()); + if (!event->isAccepted()) { + // Propagate to the item under + continue; + } + + d->lastDropAction = event->dropAction(); + + if (d->dragDropItem) { + // Leave the last drag drop item. A perfect implementation + // would set the position of this event to the point where + // this event and the last event intersect with the item's + // shape, but that's not easy to do. :-) + QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave); + d->cloneDragDropEvent(&dragLeave, event); + d->sendDragDropEvent(d->dragDropItem, &dragLeave); + } + + // We've got a new drag & drop item + d->dragDropItem = item; + } + + // Send the move event. + event->setDropAction(d->lastDropAction); + event->accept(); + d->sendDragDropEvent(item, event); + if (event->isAccepted()) + d->lastDropAction = event->dropAction(); + eventDelivered = true; + break; + } + + if (!eventDelivered) { + if (d->dragDropItem) { + // Leave the last drag drop item + QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave); + d->cloneDragDropEvent(&dragLeave, event); + d->sendDragDropEvent(d->dragDropItem, &dragLeave); + d->dragDropItem = 0; + } + // Propagate + event->setDropAction(Qt::IgnoreAction); + } +} + +/*! + This event handler, for event \a event, can be reimplemented in a subclass + to receive drag leave events for the scene. + + \sa QGraphicsItem::dragLeaveEvent(), dragEnterEvent(), dragMoveEvent(), + dropEvent() +*/ +void QGraphicsScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_D(QGraphicsScene); + if (d->dragDropItem) { + // Leave the last drag drop item + d->sendDragDropEvent(d->dragDropItem, event); + d->dragDropItem = 0; + } +} + +/*! + This event handler, for event \a event, can be reimplemented in a subclass + to receive drop events for the scene. + + \sa QGraphicsItem::dropEvent(), dragEnterEvent(), dragMoveEvent(), + dragLeaveEvent() +*/ +void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event) +{ + Q_UNUSED(event); + Q_D(QGraphicsScene); + if (d->dragDropItem) { + // Drop on the last drag drop item + d->sendDragDropEvent(d->dragDropItem, event); + d->dragDropItem = 0; + } +} + +/*! + This event handler, for event \a focusEvent, can be reimplemented in a + subclass to receive focus in events. + + The default implementation sets focus on the scene, and then on the last + focus item. + + \sa QGraphicsItem::focusOutEvent() +*/ +void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent) +{ + Q_D(QGraphicsScene); + + d->hasFocus = true; + switch (focusEvent->reason()) { + case Qt::TabFocusReason: + if (!focusNextPrevChild(true)) + focusEvent->ignore(); + break; + case Qt::BacktabFocusReason: + if (!focusNextPrevChild(false)) + focusEvent->ignore(); + break; + default: + if (d->lastFocusItem) { + // Set focus on the last focus item + setFocusItem(d->lastFocusItem, focusEvent->reason()); + } + break; + } +} + +/*! + This event handler, for event \a focusEvent, can be reimplemented in a + subclass to receive focus out events. + + The default implementation removes focus from any focus item, then removes + focus from the scene. + + \sa QGraphicsItem::focusInEvent() +*/ +void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent) +{ + Q_D(QGraphicsScene); + d->hasFocus = false; + setFocusItem(0, focusEvent->reason()); + + // Remove all popups when the scene loses focus. + if (!d->popupWidgets.isEmpty()) + d->removePopup(d->popupWidgets.first()); +} + +/*! + This event handler, for event \a helpEvent, can be + reimplemented in a subclass to receive help events. The events + are of type QEvent::ToolTip, which are created when a tooltip is + requested. + + The default implementation shows the tooltip of the topmost + item, i.e., the item with the highest z-value, at the mouse + cursor position. If no item has a tooltip set, this function + does nothing. + + \sa QGraphicsItem::toolTip(), QGraphicsSceneHelpEvent +*/ +void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent) +{ +#ifdef QT_NO_TOOLTIP + Q_UNUSED(helpEvent); +#else + // Find the first item that does tooltips + Q_D(QGraphicsScene); + QList<QGraphicsItem *> itemsAtPos = d->itemsAtPosition(helpEvent->screenPos(), + helpEvent->scenePos(), + helpEvent->widget()); + QGraphicsItem *toolTipItem = 0; + for (int i = 0; i < itemsAtPos.size(); ++i) { + QGraphicsItem *tmp = itemsAtPos.at(i); + if (!tmp->toolTip().isEmpty()) { + toolTipItem = tmp; + break; + } + } + + // Show or hide the tooltip + QString text; + QPoint point; + if (toolTipItem && !toolTipItem->toolTip().isEmpty()) { + text = toolTipItem->toolTip(); + point = helpEvent->screenPos(); + } + QToolTip::showText(point, text); + helpEvent->setAccepted(!text.isEmpty()); +#endif +} + +bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const +{ + return item->acceptHoverEvents() + || (item->isWidget() && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()); +} + +/*! + This event handler, for event \a hoverEvent, can be reimplemented in a + subclass to receive hover enter events. The default implementation + forwards the event to the topmost item that accepts hover events at the + scene position from the event. + + \sa QGraphicsItem::hoverEvent(), QGraphicsItem::setAcceptHoverEvents() +*/ +bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent) +{ + // Find the first item that accepts hover events, reusing earlier + // calculated data is possible. + if (cachedItemsUnderMouse.isEmpty()) { + cachedItemsUnderMouse = itemsAtPosition(hoverEvent->screenPos(), + hoverEvent->scenePos(), + hoverEvent->widget()); + } + + QGraphicsItem *item = 0; + for (int i = 0; i < cachedItemsUnderMouse.size(); ++i) { + QGraphicsItem *tmp = cachedItemsUnderMouse.at(i); + if (itemAcceptsHoverEvents_helper(tmp)) { + item = tmp; + break; + } + } + + // Find the common ancestor item for the new topmost hoverItem and the + // last item in the hoverItem list. + QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0; + while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem)) + commonAncestorItem = commonAncestorItem->parentItem(); + if (commonAncestorItem && commonAncestorItem->window() != item->window()) { + // The common ancestor isn't in the same window as the two hovered + // items. + commonAncestorItem = 0; + } + + // Check if the common ancestor item is known. + int index = commonAncestorItem ? hoverItems.indexOf(commonAncestorItem) : -1; + // Send hover leaves to any existing hovered children of the common + // ancestor item. + for (int i = hoverItems.size() - 1; i > index; --i) { + QGraphicsItem *lastItem = hoverItems.takeLast(); + if (itemAcceptsHoverEvents_helper(lastItem)) + sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, hoverEvent); + } + + // Item is a child of a known item. Generate enter events for the + // missing links. + QList<QGraphicsItem *> parents; + QGraphicsItem *parent = item; + while (parent && parent != commonAncestorItem) { + parents.prepend(parent); + if (parent->isWindow()) { + // Stop at the window - we don't deliver beyond this point. + break; + } + parent = parent->parentItem(); + } + for (int i = 0; i < parents.size(); ++i) { + parent = parents.at(i); + hoverItems << parent; + if (itemAcceptsHoverEvents_helper(parent)) + sendHoverEvent(QEvent::GraphicsSceneHoverEnter, parent, hoverEvent); + } + + // Generate a move event for the item itself + if (item && !hoverItems.isEmpty() && item == hoverItems.last()) { + sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent); + return true; + } + return false; +} + +/*! + \internal + + Handles all actions necessary to clean up the scene when the mouse leaves + the view. +*/ +void QGraphicsScenePrivate::leaveScene() +{ + Q_Q(QGraphicsScene); +#ifndef QT_NO_TOOLTIP + // Remove any tooltips + QToolTip::showText(QPoint(), QString()); +#endif + // Send HoverLeave events to all existing hover items, topmost first. + QGraphicsView *senderWidget = qobject_cast<QGraphicsView *>(q->sender()); + QGraphicsSceneHoverEvent hoverEvent; + hoverEvent.setWidget(senderWidget); + + if (senderWidget) { + QPoint cursorPos = QCursor::pos(); + hoverEvent.setScenePos(senderWidget->mapToScene(senderWidget->mapFromGlobal(cursorPos))); + hoverEvent.setLastScenePos(hoverEvent.scenePos()); + hoverEvent.setScreenPos(cursorPos); + hoverEvent.setLastScreenPos(hoverEvent.screenPos()); + } + + while (!hoverItems.isEmpty()) { + QGraphicsItem *lastItem = hoverItems.takeLast(); + if (lastItem->acceptHoverEvents() + || (lastItem->isWidget() && static_cast<QGraphicsWidget*>(lastItem)->d_func()->hasDecoration())) + sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent); + } +} + +/*! + This event handler, for event \a keyEvent, can be reimplemented in a + subclass to receive keypress events. The default implementation forwards + the event to current focus item. + + \sa QGraphicsItem::keyPressEvent(), focusItem() +*/ +void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent) +{ + // ### Merge this function with keyReleaseEvent; they are identical + // ### (except this comment). + Q_D(QGraphicsScene); + QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0; + if (!item) + item = focusItem(); + if (item) { + QGraphicsItem *p = item; + do { + // Accept the event by default + keyEvent->accept(); + // Send it; QGraphicsItem::keyPressEvent ignores it. If the event + // is filtered out, stop propagating it. + if (!d->sendEvent(p, keyEvent)) + break; + } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } else { + keyEvent->ignore(); + } +} + +/*! + This event handler, for event \a keyEvent, can be reimplemented in a + subclass to receive key release events. The default implementation + forwards the event to current focus item. + + \sa QGraphicsItem::keyReleaseEvent(), focusItem() +*/ +void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent) +{ + // ### Merge this function with keyPressEvent; they are identical (except + // ### this comment). + Q_D(QGraphicsScene); + QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0; + if (!item) + item = focusItem(); + if (item) { + QGraphicsItem *p = item; + do { + // Accept the event by default + keyEvent->accept(); + // Send it; QGraphicsItem::keyPressEvent ignores it. If the event + // is filtered out, stop propagating it. + if (!d->sendEvent(p, keyEvent)) + break; + } while (!keyEvent->isAccepted() && !p->isWindow() && (p = p->parentItem())); + } else { + keyEvent->ignore(); + } +} + +/*! + This event handler, for event \a mouseEvent, can be reimplemented + in a subclass to receive mouse press events for the scene. + + The default implementation depends on the state of the scene. If + there is a mouse grabber item, then the event is sent to the mouse + grabber. Otherwise, it is forwarded to the topmost item that + accepts mouse events at the scene position from the event, and + that item promptly becomes the mouse grabber item. + + If there is no item at the given position on the scene, the + selection area is reset, any focus item loses its input focus, and + the event is then ignored. + + \sa QGraphicsItem::mousePressEvent(), + QGraphicsItem::setAcceptedMouseButtons() +*/ +void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + Q_D(QGraphicsScene); + d->mousePressEventHandler(mouseEvent); +} + +/*! + This event handler, for event \a mouseEvent, can be reimplemented + in a subclass to receive mouse move events for the scene. + + The default implementation depends on the mouse grabber state. If there is + a mouse grabber item, the event is sent to the mouse grabber. If there + are any items that accept hover events at the current position, the event + is translated into a hover event and accepted; otherwise it's ignored. + + \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseReleaseEvent(), + QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons() +*/ +void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + Q_D(QGraphicsScene); + if (d->mouseGrabberItems.isEmpty()) { + if (mouseEvent->buttons()) + return; + QGraphicsSceneHoverEvent hover; + _q_hoverFromMouseEvent(&hover, mouseEvent); + mouseEvent->setAccepted(d->dispatchHoverEvent(&hover)); + return; + } + + // Forward the event to the mouse grabber + d->sendMouseEvent(mouseEvent); + mouseEvent->accept(); +} + +/*! + This event handler, for event \a mouseEvent, can be reimplemented + in a subclass to receive mouse release events for the scene. + + The default implementation depends on the mouse grabber state. If + there is no mouse grabber, the event is ignored. Otherwise, if + there is a mouse grabber item, the event is sent to the mouse + grabber. If this mouse release represents the last pressed button + on the mouse, the mouse grabber item then loses the mouse grab. + + \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(), + QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons() +*/ +void QGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + Q_D(QGraphicsScene); + if (d->mouseGrabberItems.isEmpty()) { + mouseEvent->ignore(); + return; + } + + // Forward the event to the mouse grabber + d->sendMouseEvent(mouseEvent); + mouseEvent->accept(); + + // Reset the mouse grabber when the last mouse button has been released. + if (!mouseEvent->buttons()) { + if (!d->mouseGrabberItems.isEmpty()) { + d->lastMouseGrabberItem = d->mouseGrabberItems.last(); + if (d->lastMouseGrabberItemHasImplicitMouseGrab) + d->mouseGrabberItems.last()->ungrabMouse(); + } else { + d->lastMouseGrabberItem = 0; + } + + // Generate a hoverevent + QGraphicsSceneHoverEvent hoverEvent; + _q_hoverFromMouseEvent(&hoverEvent, mouseEvent); + d->dispatchHoverEvent(&hoverEvent); + } +} + +/*! + This event handler, for event \a mouseEvent, can be reimplemented + in a subclass to receive mouse doubleclick events for the scene. + + If someone doubleclicks on the scene, the scene will first receive + a mouse press event, followed by a release event (i.e., a click), + then a doubleclick event, and finally a release event. If the + doubleclick event is delivered to a different item than the one + that received the first press and release, it will be delivered as + a press event. However, tripleclick events are not delivered as + doubleclick events in this case. + + The default implementation is similar to mousePressEvent(). + + \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(), + QGraphicsItem::mouseReleaseEvent(), QGraphicsItem::setAcceptedMouseButtons() +*/ +void QGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent) +{ + Q_D(QGraphicsScene); + d->mousePressEventHandler(mouseEvent); +} + +/*! + This event handler, for event \a wheelEvent, can be reimplemented in a + subclass to receive mouse wheel events for the scene. + + By default, the event is delivered to the topmost visible item under the + cursor. If ignored, the event propagates to the item beneath, and again + until the event is accepted, or it reaches the scene. If no items accept + the event, it is ignored. + + \sa QGraphicsItem::wheelEvent() +*/ +void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent) +{ + Q_D(QGraphicsScene); + QList<QGraphicsItem *> wheelCandidates = d->itemsAtPosition(wheelEvent->screenPos(), + wheelEvent->scenePos(), + wheelEvent->widget()); + + bool hasSetFocus = false; + foreach (QGraphicsItem *item, wheelCandidates) { + if (!hasSetFocus && item->isEnabled() && (item->flags() & QGraphicsItem::ItemIsFocusable)) { + if (item->isWidget() && static_cast<QGraphicsWidget *>(item)->focusPolicy() == Qt::WheelFocus) { + hasSetFocus = true; + if (item != focusItem()) + setFocusItem(item, Qt::MouseFocusReason); + } + } + + wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(), + wheelEvent->widget())); + wheelEvent->accept(); + bool isWindow = item->isWindow(); + d->sendEvent(item, wheelEvent); + if (isWindow || wheelEvent->isAccepted()) + break; + } +} + +/*! + This event handler, for event \a event, can be reimplemented in a + subclass to receive input method events for the scene. + + The default implementation forwards the event to the focusItem(). + If no item currently has focus, this function does nothing. + + \sa QGraphicsItem::inputMethodEvent() +*/ +void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QGraphicsScene); + if (!d->focusItem) + return; + d->sendEvent(d->focusItem, event); +} + +/*! + Draws the background of the scene using \a painter, before any items and + the foreground are drawn. Reimplement this function to provide a custom + background for the scene. + + All painting is done in \e scene coordinates. The \a rect + parameter is the exposed rectangle. + + If all you want is to define a color, texture, or gradient for the + background, you can call setBackgroundBrush() instead. + + \sa drawForeground(), drawItems() +*/ +void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect) +{ + Q_D(QGraphicsScene); + + if (d->backgroundBrush.style() != Qt::NoBrush) { + if (d->painterStateProtection) + painter->save(); + painter->setBrushOrigin(0, 0); + painter->fillRect(rect, backgroundBrush()); + if (d->painterStateProtection) + painter->restore(); + } +} + +/*! + Draws the foreground of the scene using \a painter, after the background + and all items have been drawn. Reimplement this function to provide a + custom foreground for the scene. + + All painting is done in \e scene coordinates. The \a rect + parameter is the exposed rectangle. + + If all you want is to define a color, texture or gradient for the + foreground, you can call setForegroundBrush() instead. + + \sa drawBackground(), drawItems() +*/ +void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect) +{ + Q_D(QGraphicsScene); + + if (d->foregroundBrush.style() != Qt::NoBrush) { + if (d->painterStateProtection) + painter->save(); + painter->setBrushOrigin(0, 0); + painter->fillRect(rect, foregroundBrush()); + if (d->painterStateProtection) + painter->restore(); + } +} + +static void _q_paintItem(QGraphicsItem *item, QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget, + bool useWindowOpacity, bool painterStateProtection) +{ + if (!item->isWidget()) { + item->paint(painter, option, widget); + return; + } + QGraphicsWidget *widgetItem = static_cast<QGraphicsWidget *>(item); + QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(widgetItem); + const qreal windowOpacity = (proxy && proxy->widget() && useWindowOpacity) + ? proxy->widget()->windowOpacity() : 1.0; + const qreal oldPainterOpacity = painter->opacity(); + + if (qFuzzyCompare(windowOpacity + 1, qreal(1.0))) + return; + // Set new painter opacity. + if (windowOpacity < 1.0) + painter->setOpacity(oldPainterOpacity * windowOpacity); + + // set layoutdirection on the painter + Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection(); + painter->setLayoutDirection(widgetItem->layoutDirection()); + + if (widgetItem->isWindow() && widgetItem->windowType() != Qt::Popup && widgetItem->windowType() != Qt::ToolTip + && !(widgetItem->windowFlags() & Qt::FramelessWindowHint)) { + if (painterStateProtection) + painter->save(); + widgetItem->paintWindowFrame(painter, option, widget); + if (painterStateProtection) + painter->restore(); + } + + widgetItem->paint(painter, option, widget); + + // Restore layoutdirection on the painter. + painter->setLayoutDirection(oldLayoutDirection); + // Restore painter opacity. + if (windowOpacity < 1.0) + painter->setOpacity(oldPainterOpacity); +} + +static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed, + const QTransform &itemToPixmap, QPainter::RenderHints renderHints, + const QStyleOptionGraphicsItem *option, bool painterStateProtection) +{ + QPixmap subPix; + QPainter pixmapPainter; + QRect br = pixmapExposed.boundingRect(); + + // Don't use subpixmap if we get a full update. + if (pixmapExposed.isEmpty() || (pixmapExposed.numRects() == 1 && br.contains(pix->rect()))) { + pix->fill(Qt::transparent); + pixmapPainter.begin(pix); + } else { + subPix = QPixmap(br.size()); + subPix.fill(Qt::transparent); + pixmapPainter.begin(&subPix); + pixmapPainter.translate(-br.topLeft()); + if (!pixmapExposed.isEmpty()) { + // Applied to subPix; paint is adjusted to the coordinate space is + // correct. + pixmapPainter.setClipRegion(pixmapExposed); + } + } + + pixmapPainter.setRenderHints(pixmapPainter.renderHints(), false); + pixmapPainter.setRenderHints(renderHints, true); + pixmapPainter.setWorldTransform(itemToPixmap, true); + + // Render. + _q_paintItem(item, &pixmapPainter, option, 0, false, painterStateProtection); + pixmapPainter.end(); + + if (!subPix.isNull()) { + // Blit the subpixmap into the main pixmap. + pixmapPainter.begin(pix); + pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source); + pixmapPainter.setClipRegion(pixmapExposed); + pixmapPainter.drawPixmap(br.topLeft(), subPix); + pixmapPainter.end(); + } +} + +/*! + \internal + + Draws items directly, or using cache. +*/ +void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget, + bool painterStateProtection) +{ + QGraphicsItemPrivate *itemd = item->d_ptr; + QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode); + + // Render directly, using no cache. + if (cacheMode == QGraphicsItem::NoCache +#ifdef Q_WS_X11 + || !X11->use_xrender +#endif + ) { + _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, true, painterStateProtection); + return; + } + + const qreal oldPainterOpacity = painter->opacity(); + qreal newPainterOpacity = oldPainterOpacity; + QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0; + if (proxy && proxy->widget()) { + const qreal windowOpacity = proxy->widget()->windowOpacity(); + if (windowOpacity < 1.0) + newPainterOpacity *= windowOpacity; + } + + // Item's (local) bounding rect + QRectF brect = item->boundingRect(); + if (_q_adjustedRect(brect).isEmpty()) + return; + + // Fetch the off-screen transparent buffer and exposed area info. + QString pixmapKey; + QPixmap pix; + QGraphicsItemCache *itemCache = itemd->extraItemCache(); + if (cacheMode == QGraphicsItem::ItemCoordinateCache) { + if (itemCache->boundingRect != brect.toRect()) { + itemCache->boundingRect = brect.toRect(); + itemCache->allExposed = true; + itemCache->exposed.clear(); + } + pixmapKey = itemCache->key; + } else { + if ((pixmapKey = itemCache->deviceData.value(widget).key).isEmpty()) { + pixmapKey.sprintf("qgv-%p-%p", item, widget); + QGraphicsItemCache::DeviceData data; + data.key = pixmapKey; + itemCache->deviceData.insert(widget, data); + } + } + + // Find pixmap in cache. + if (!itemCache->allExposed) + QPixmapCache::find(pixmapKey, pix); + + // Render using item coordinate cache mode. + if (cacheMode == QGraphicsItem::ItemCoordinateCache) { + QSize pixmapSize; + bool fixedCacheSize = false; + if ((fixedCacheSize = itemCache->fixedSize.isValid())) { + pixmapSize = itemCache->fixedSize; + } else { + pixmapSize = brect.toAlignedRect().size(); + } + + // Create or recreate the pixmap. + int adjust = itemCache->fixedSize.isValid() ? 0 : 2; + QSize adjustSize(adjust*2, adjust*2); + QRectF br = brect.adjusted(-adjust, -adjust, adjust, adjust); + if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) { + pix = QPixmap(pixmapSize + adjustSize); + itemCache->exposed.clear(); + itemCache->allExposed = true; + } + + // Redraw any newly exposed areas. + if (itemCache->allExposed || !itemCache->exposed.isEmpty()) { + // Fit the item's bounding rect into the pixmap's coordinates. + const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height()); + QTransform itemToPixmap; + itemToPixmap.scale(scale.x(), scale.y()); + itemToPixmap.translate(-br.x(), -br.y()); + + // Generate the item's exposedRect and map its list of expose + // rects to device coordinates. + QStyleOptionGraphicsItem cacheOption = *option; + QRegion pixmapExposed; + QRectF exposedRect; + if (!itemCache->allExposed) { + for (int i = 0; i < itemCache->exposed.size(); ++i) { + QRectF r = itemCache->exposed.at(i); + exposedRect |= r; + pixmapExposed += itemToPixmap.mapRect(r).toAlignedRect(); + } + } else { + exposedRect = brect; + } + cacheOption.exposedRect = exposedRect; + + // Render. + _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), + &cacheOption, painterStateProtection); + + // Reinsert this pixmap into the cache. + QPixmapCache::insert(pixmapKey, pix); + + // Reset expose data. + itemCache->allExposed = false; + itemCache->exposed.clear(); + } + + // Redraw the exposed area using the transformed painter. Depending on + // the hardware, this may be a server-side operation, or an expensive + // qpixmap-image-transform-pixmap roundtrip. + if (newPainterOpacity != oldPainterOpacity) { + painter->setOpacity(newPainterOpacity); + painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size())); + painter->setOpacity(oldPainterOpacity); + } else { + painter->drawPixmap(br, pix, QRectF(QPointF(), pix.size())); + } + return; + } + + // Render using device coordinate cache mode. + if (cacheMode == QGraphicsItem::DeviceCoordinateCache) { + // Find the item's bounds in device coordinates. + QRectF deviceBounds = painter->worldTransform().mapRect(brect); + QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1); + if (deviceRect.isEmpty()) + return; + QRect viewRect = widget ? widget->rect() : QRect(); + if (widget && !viewRect.intersects(deviceRect)) + return; + + // Resort to direct rendering if the device rect exceeds the + // (optional) maximum bounds. (QGraphicsSvgItem uses this). + QSize maximumCacheSize = + itemd->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize(); + if (!maximumCacheSize.isEmpty() + && (deviceRect.width() > maximumCacheSize.width() + || deviceRect.height() > maximumCacheSize.height())) { + _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, + oldPainterOpacity != newPainterOpacity, painterStateProtection); + return; + } + + // Create or reuse offscreen pixmap, possibly scroll/blit from the old one. + bool pixModified = false; + QGraphicsItemCache::DeviceData *deviceData = &itemCache->deviceData[widget]; + bool invertable = true; + QTransform diff = deviceData->lastTransform.inverted(&invertable); + if (invertable) + diff *= painter->worldTransform(); + deviceData->lastTransform = painter->worldTransform(); + if (!invertable || diff.type() > QTransform::TxTranslate) { + pixModified = true; + itemCache->allExposed = true; + itemCache->exposed.clear(); + pix = QPixmap(); + } + + // ### This is a pretty bad way to determine when to start partial + // exposure for DeviceCoordinateCache but it's the least intrusive + // approach for now. +#if 0 + // Only if the device rect isn't fully contained. + bool allowPartialCacheExposure = !viewRect.contains(deviceRect); +#else + // Only if deviceRect is 20% taller or wider than the desktop. + QRect desktopRect = qApp->desktop()->availableGeometry(widget); + bool allowPartialCacheExposure = (desktopRect.width() * 1.2 < deviceRect.width() + || desktopRect.height() * 1.2 < deviceRect.height()); +#endif + QRegion scrollExposure; + if (deviceData->cacheIndent != QPoint() || allowPartialCacheExposure) { + // Part of pixmap is drawn. Either device contains viewrect (big + // item covers whole screen) or parts of device are outside the + // viewport. In either case the device rect must be the intersect + // between the two. + int dx = deviceRect.left() < viewRect.left() ? viewRect.left() - deviceRect.left() : 0; + int dy = deviceRect.top() < viewRect.top() ? viewRect.top() - deviceRect.top() : 0; + QPoint newCacheIndent(dx, dy); + deviceRect &= viewRect; + + if (pix.isNull()) { + deviceData->cacheIndent = QPoint(); + itemCache->allExposed = true; + itemCache->exposed.clear(); + pixModified = true; + } + + // Copy / "scroll" the old pixmap onto the new ole and calculate + // scrolled exposure. + if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) { + QPoint diff = newCacheIndent - deviceData->cacheIndent; + QPixmap newPix(deviceRect.size()); + // ### Investigate removing this fill (test with Plasma and + // graphicssystem raster). + newPix.fill(Qt::transparent); + if (!pix.isNull()) { + QPainter newPixPainter(&newPix); + newPixPainter.drawPixmap(-diff, pix); + newPixPainter.end(); + } + QRegion exposed; + exposed += newPix.rect(); + if (!pix.isNull()) + exposed -= QRect(-diff, pix.size()); + scrollExposure = exposed; + + pix = newPix; + pixModified = true; + } + deviceData->cacheIndent = newCacheIndent; + } else { + // Full pixmap is drawn. + deviceData->cacheIndent = QPoint(); + + // Auto-adjust the pixmap size. + if (deviceRect.size() != pix.size()) { + // exposed needs to cover the whole pixmap + pix = QPixmap(deviceRect.size()); + pixModified = true; + itemCache->allExposed = true; + itemCache->exposed.clear(); + } + } + + // Check for newly invalidated areas. + if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) { + // Construct an item-to-pixmap transform. + QPointF p = deviceRect.topLeft(); + QTransform itemToPixmap = QTransform::fromTranslate(-p.x(), -p.y()); + itemToPixmap = painter->worldTransform() * itemToPixmap; + + // Map the item's logical expose to pixmap coordinates. + QRegion pixmapExposed = scrollExposure; + if (!itemCache->allExposed) { + const QVector<QRectF> &exposed = itemCache->exposed; + for (int i = 0; i < exposed.size(); ++i) + pixmapExposed += itemToPixmap.mapRect(exposed.at(i)).toRect().adjusted(-1, -1, 1, 1); + } + + // Calculate the style option's exposedRect. + QRectF br; + if (itemCache->allExposed) { + br = item->boundingRect(); + } else { + const QVector<QRectF> &exposed = itemCache->exposed; + for (int i = 0; i < exposed.size(); ++i) + br |= exposed.at(i); + QTransform pixmapToItem = itemToPixmap.inverted(); + foreach (QRect r, scrollExposure.rects()) + br |= pixmapToItem.mapRect(r); + } + QStyleOptionGraphicsItem cacheOption = *option; + cacheOption.exposedRect = br.adjusted(-1, -1, 1, 1); + + // Render the exposed areas. + _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(), + &cacheOption, painterStateProtection); + + // Reset expose data. + pixModified = true; + itemCache->allExposed = false; + itemCache->exposed.clear(); + } + + if (pixModified) { + // Reinsert this pixmap into the cache + QPixmapCache::insert(pixmapKey, pix); + } + + // Redraw the exposed area using an untransformed painter. This + // effectively becomes a bitblit that does not transform the cache. + QTransform restoreTransform = painter->worldTransform(); + painter->setWorldTransform(QTransform()); + if (newPainterOpacity != oldPainterOpacity) { + painter->setOpacity(newPainterOpacity); + painter->drawPixmap(deviceRect.topLeft(), pix); + painter->setOpacity(oldPainterOpacity); + } else { + painter->drawPixmap(deviceRect.topLeft(), pix); + } + painter->setWorldTransform(restoreTransform); + return; + } +} + +/*! + Paints the given \a items using the provided \a painter, after the + background has been drawn, and before the foreground has been + drawn. All painting is done in \e scene coordinates. Before + drawing each item, the painter must be transformed using + QGraphicsItem::sceneMatrix(). + + The \a options parameter is the list of style option objects for + each item in \a items. The \a numItems parameter is the number of + items in \a items and options in \a options. The \a widget + parameter is optional; if specified, it should point to the widget + that is being painted on. + + The default implementation prepares the painter matrix, and calls + QGraphicsItem::paint() on all items. Reimplement this function to + provide custom painting of all items for the scene; gaining + complete control over how each item is drawn. In some cases this + can increase drawing performance significantly. + + Example: + + \snippet doc/src/snippets/graphicssceneadditemsnippet.cpp 0 + + \sa drawBackground(), drawForeground() +*/ +void QGraphicsScene::drawItems(QPainter *painter, + int numItems, + QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[], QWidget *widget) +{ + Q_D(QGraphicsScene); + + // Detect if painter state protection is disabled. + QTransform viewTransform = painter->worldTransform(); + QVarLengthArray<QGraphicsItem *, 16> childClippers; + + for (int i = 0; i < numItems; ++i) { + QGraphicsItem *item = items[i]; + if (!(item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) { + if (!childClippers.isEmpty()) { + // Item is not clipped to any ancestor: pop all current clippers. + for (int i = 0; i < childClippers.size(); ++i) + painter->restore(); + childClippers.clear(); + } + } else { + // Item is clipped to an ancestor, which may or may not be in our + // child clipper list. Let's start by finding the item's closest + // clipping ancestor. + QGraphicsItem *clipParent = item->parentItem(); + while (clipParent && !(clipParent->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape)) + clipParent = clipParent->parentItem(); + + // Pop any in-between clippers. If the clipper is unknown, pop + // them all. ### QVarLengthArray::lastIndexOf(). + int index = -1; + for (int n = childClippers.size() - 1; n >= 0; --n) { + if (childClippers[n] == clipParent) { + index = n; + break; + } + } + if (index != -1) { + int toPop = childClippers.size() - index - 1; + if (toPop > 0) { + for (int i = 0; i < toPop; ++i) + painter->restore(); + childClippers.resize(index + 1); + } + } + + // Sanity check + if (!childClippers.isEmpty()) + Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent); + + // If the clipper list is empty at this point, but we're still + // clipped to an ancestor, then we need to build the clip chain + // ourselves. There is only one case that can produce this issue: + // This item is stacked behind an ancestor: + // ItemStacksBehindParent. + if (childClippers.isEmpty()) { + Q_ASSERT(clipParent != 0); + // Build a stack of clippers. + QVarLengthArray<QGraphicsItem *, 16> clippers; + QGraphicsItem *p = clipParent; + do { + if (p->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape) + clippers.append(p); + } while ((p = p->parentItem()) && (p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)); + + // ### This code path can also use the itemTransform + // optimization, but it's hit very rarely. + for (int i = clippers.size() - 1; i >= 0; --i) { + QGraphicsItem *clipper = clippers[i]; + if (clipper->d_ptr->itemIsUntransformable()) { + painter->setWorldTransform(clipper->deviceTransform(viewTransform), false); + } else { + painter->setWorldTransform(clipper->sceneTransform() * viewTransform, false); + } + + childClippers.append(clipper); + painter->save(); + painter->setClipPath(clipper->shape(), Qt::IntersectClip); + } + Q_ASSERT(childClippers[childClippers.size() - 1] == clipParent); + } + } + + // Set up the painter transform + if (item->d_ptr->itemIsUntransformable()) { + painter->setWorldTransform(item->deviceTransform(viewTransform), false); + } else { + painter->setWorldTransform(item->sceneTransform() * viewTransform, false); + } + + // Save painter + bool saveState = (d->painterStateProtection || (item->flags() & QGraphicsItem::ItemClipsToShape)); + if (saveState) + painter->save(); + + // Set local clip + if (item->flags() & QGraphicsItem::ItemClipsToShape) + painter->setClipPath(item->shape(), Qt::IntersectClip); + + // Setup opacity + painter->setOpacity(item->effectiveOpacity()); + + // Draw the item + d->drawItemHelper(item, painter, &options[i], widget, d->painterStateProtection); + + if (saveState) + painter->restore(); + + if (item->flags() & QGraphicsItem::ItemClipsChildrenToShape) { + // Clip descendents to this item's shape, and keep the painter + // saved. + childClippers.append(item); + painter->save(); + painter->setClipPath(item->shape(), Qt::IntersectClip); + } + } + + for (int i = 0; i < childClippers.size(); ++i) + painter->restore(); + + painter->setWorldTransform(viewTransform); +} + +/*! + \since 4.4 + + Finds a new widget to give the keyboard focus to, as appropriate for Tab + and Shift+Tab, and returns true if it can find a new widget, or false if + it cannot. If \a next is true, this function searches forward; if \a next + is false, it searches backward. + + You can reimplement this function in a subclass of QGraphicsScene to + provide fine-grained control over how tab focus passes inside your + scene. The default implementation is based on the tab focus chain defined + by QGraphicsWidget::setTabOrder(). +*/ +bool QGraphicsScene::focusNextPrevChild(bool next) +{ + Q_D(QGraphicsScene); + + QGraphicsItem *item = focusItem(); + if (item && !item->isWidget()) { + // Tab out of the scene. + return false; + } + if (!item) { + if (d->lastFocusItem && !d->lastFocusItem->isWidget()) { + // Restore focus to the last focusable non-widget item that had + // focus. + setFocusItem(d->lastFocusItem, next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + return true; + } + } + if (!d->tabFocusFirst) { + // No widgets... + return false; + } + + // The item must be a widget. + QGraphicsWidget *widget = 0; + if (!item) { + widget = next ? d->tabFocusFirst : d->tabFocusFirst->d_func()->focusPrev; + } else { + QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item); + widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev; + if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev)) + return false; + } + QGraphicsWidget *widgetThatHadFocus = widget; + + // Run around the focus chain until we find a widget that can take tab focus. + do { + if (widget->flags() & QGraphicsItem::ItemIsFocusable + && widget->isEnabled() && widget->isVisibleTo(0) + && (widget->focusPolicy() & Qt::TabFocus) + && (!item || !item->isWindow() || item->isAncestorOf(widget)) + ) { + setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + return true; + } + widget = next ? widget->d_func()->focusNext : widget->d_func()->focusPrev; + if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev)) + return false; + } while (widget != widgetThatHadFocus); + + return false; +} + +/*! + \fn QGraphicsScene::changed(const QList<QRectF> ®ion) + + This signal is emitted by QGraphicsScene when control reaches the + event loop, if the scene content changes. The \a region parameter + contains a list of scene rectangles that indicate the area that + has been changed. + + \sa QGraphicsView::updateScene() +*/ + +/*! + \fn QGraphicsScene::sceneRectChanged(const QRectF &rect) + + This signal is emitted by QGraphicsScene whenever the scene rect changes. + The \a rect parameter is the new scene rectangle. + + \sa QGraphicsView::updateSceneRect() +*/ + +/*! + \fn QGraphicsScene::selectionChanged() + \since 4.3 + + This signal is emitted by QGraphicsScene whenever the selection + changes. You can call selectedItems() to get the new list of selected + items. + + The selection changes whenever an item is selected or unselected, a + selection area is set, cleared or otherwise changed, if a preselected item + is added to the scene, or if a selected item is removed from the scene. + + QGraphicsScene emits this signal only once for group selection operations. + For example, if you set a selection area, select or unselect a + QGraphicsItemGroup, or if you add or remove from the scene a parent item + that contains several selected items, selectionChanged() is emitted only + once after the operation has completed (instead of once for each item). + + \sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected() +*/ + +/*! + \internal + + This private function is called by QGraphicsItem, which is a friend of + QGraphicsScene. It is used by QGraphicsScene to record the rectangles that + need updating. It also launches a single-shot timer to ensure that + updated() will be emitted later. + + The \a item parameter is the item that changed, and \a rect is the + area of the item that changed given in item coordinates. +*/ +void QGraphicsScene::itemUpdated(QGraphicsItem *item, const QRectF &rect) +{ + Q_D(QGraphicsScene); + // Deliver the actual update. + if (!d->updateAll) { + if (d->views.isEmpty() || ((d->connectedSignals & d->changedSignalMask) && !item->d_ptr->itemIsUntransformable() + && qFuzzyCompare(item->boundingRegionGranularity(), qreal(0.0)))) { + // This block of code is kept for compatibility. Since 4.5, by default + // QGraphicsView does not connect the signal and we use the below + // method of delivering updates. + update(item->sceneBoundingRect()); + } else { + // ### Remove _q_adjustedRects(). + QRectF boundingRect = _q_adjustedRect(item->boundingRect()); + if (!rect.isNull()) + boundingRect &= _q_adjustedRect(rect); + + // Update each view directly. + for (int i = 0; i < d->views.size(); ++i) + d->views.at(i)->d_func()->itemUpdated(item, boundingRect); + } + } + if (item->d_ptr->dirty) { + d->dirtyItems << item; + d->resetDirtyItemsLater(); + } + + // Update d->largestUntransformableItem by mapping this item's bounding + // rect back to the topmost untransformable item's untransformed + // coordinate system (which sort of equals the 1:1 coordinate system of an + // untransformed view). + if (item->d_ptr->itemIsUntransformable()) { + QGraphicsItem *parent = item; + while (parent && (parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorIgnoresTransformations)) + parent = parent->parentItem(); + d->largestUntransformableItem |= item->mapToItem(parent, item->boundingRect()).boundingRect(); + } + + // Only track the automatically growing scene rect if the scene has no + // defined scene rect. + if (!d->hasSceneRect) { + QRectF oldGrowingItemsBoundingRect = d->growingItemsBoundingRect; + d->growingItemsBoundingRect |= _q_adjustedRect(item->sceneBoundingRect()); + if (d->growingItemsBoundingRect != oldGrowingItemsBoundingRect) + emit sceneRectChanged(d->growingItemsBoundingRect); + } +} + +/*! + \since 4.4 + + Returns the scene's style, or the same as QApplication::style() if the + scene has not been explicitly assigned a style. + + \sa setStyle() +*/ +QStyle *QGraphicsScene::style() const +{ + Q_D(const QGraphicsScene); + // ### This function, and the use of styles in general, is non-reentrant. + return d->style ? d->style : qApp->style(); +} + +/*! + \since 4.4 + + Sets or replaces the style of the scene to \a style, and reparents the + style to this scene. Any previously assigned style is deleted. The scene's + style defaults to QApplication::style(), and serves as the default for all + QGraphicsWidget items in the scene. + + Changing the style, either directly by calling this function, or + indirectly by calling QApplication::setStyle(), will automatically update + the style for all widgets in the scene that do not have a style explicitly + assigned to them. + + If \a style is 0, QGraphicsScene will revert to QApplication::style(). + + \sa style() +*/ +void QGraphicsScene::setStyle(QStyle *style) +{ + Q_D(QGraphicsScene); + // ### This function, and the use of styles in general, is non-reentrant. + if (style == d->style) + return; + + // Delete the old style, + delete d->style; + if ((d->style = style)) + d->style->setParent(this); + + // Notify the scene. + QEvent event(QEvent::StyleChange); + QApplication::sendEvent(this, &event); + + // Notify all widgets that don't have a style explicitly set. + foreach (QGraphicsItem *item, items()) { + if (item->isWidget()) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + if (!widget->testAttribute(Qt::WA_SetStyle)) + QApplication::sendEvent(widget, &event); + } + } +} + +/*! + \property QGraphicsScene::font + \since 4.4 + \brief the scene's default font + + This property provides the scene's font. The scene font defaults to, + and resolves all its entries from, QApplication::font. + + If the scene's font changes, either directly through setFont() or + indirectly when the application font changes, QGraphicsScene first + sends itself a \l{QEvent::FontChange}{FontChange} event, and it then + sends \l{QEvent::FontChange}{FontChange} events to all top-level + widget items in the scene. These items respond by resolving their own + fonts to the scene, and they then notify their children, who again + notify their children, and so on, until all widget items have updated + their fonts. + + Changing the scene font, (directly or indirectly through + QApplication::setFont(),) automatically schedules a redraw the entire + scene. + + \sa QWidget::font, QApplication::setFont(), palette, style() +*/ +QFont QGraphicsScene::font() const +{ + Q_D(const QGraphicsScene); + return d->font; +} +void QGraphicsScene::setFont(const QFont &font) +{ + Q_D(QGraphicsScene); + QFont naturalFont = qApp->font(); + naturalFont.resolve(0); + QFont resolvedFont = font.resolve(naturalFont); + d->setFont_helper(resolvedFont); +} + +/*! + \property QGraphicsScene::palette + \since 4.4 + \brief the scene's default palette + + This property provides the scene's palette. The scene palette defaults to, + and resolves all its entries from, QApplication::palette. + + If the scene's palette changes, either directly through setPalette() or + indirectly when the application palette changes, QGraphicsScene first + sends itself a \l{QEvent::PaletteChange}{PaletteChange} event, and it then + sends \l{QEvent::PaletteChange}{PaletteChange} events to all top-level + widget items in the scene. These items respond by resolving their own + palettes to the scene, and they then notify their children, who again + notify their children, and so on, until all widget items have updated + their palettes. + + Changing the scene palette, (directly or indirectly through + QApplication::setPalette(),) automatically schedules a redraw the entire + scene. + + \sa QWidget::palette, QApplication::setPalette(), font, style() +*/ +QPalette QGraphicsScene::palette() const +{ + Q_D(const QGraphicsScene); + return d->palette; +} +void QGraphicsScene::setPalette(const QPalette &palette) +{ + Q_D(QGraphicsScene); + QPalette naturalPalette = qApp->palette(); + naturalPalette.resolve(0); + QPalette resolvedPalette = palette.resolve(naturalPalette); + d->setPalette_helper(resolvedPalette); +} + +/*! + \since 4.4 + + Returns the current active window, or 0 if there is no window is currently + active. + + \sa QGraphicsScene::setActiveWindow() +*/ +QGraphicsWidget *QGraphicsScene::activeWindow() const +{ + Q_D(const QGraphicsScene); + return d->activeWindow; +} + +/*! + \since 4.4 + Activates \a widget, which must be a widget in this scene. You can also + pass 0 for \a widget, in which case QGraphicsScene will deactivate any + currently active window. + + \sa activeWindow(), QGraphicsWidget::isActiveWindow() +*/ +void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget) +{ + Q_D(QGraphicsScene); + if (widget && widget->scene() != this) { + qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene", + widget); + return; + } + + // Activate the widget's window. + QGraphicsWidget *window = widget ? widget->window() : 0; + if (window == d->activeWindow) + return; + + // Deactivate the last active window. + if (d->activeWindow) { + if (QGraphicsWidget *fw = d->activeWindow->focusWidget()) { + // Remove focus from the current focus item. + if (fw == focusItem()) + setFocusItem(0, Qt::ActiveWindowFocusReason); + } + + QEvent event(QEvent::WindowDeactivate); + QApplication::sendEvent(d->activeWindow, &event); + } + + // Update activate state. + d->activeWindow = window; + QEvent event(QEvent::ActivationChange); + QApplication::sendEvent(this, &event); + + // Activate + if (window) { + QEvent event(QEvent::WindowActivate); + QApplication::sendEvent(window, &event); + + QList<QGraphicsItem *> siblingWindows; + QGraphicsItem *parent = window->parentItem(); + // Raise ### inefficient for toplevels + foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) { + if (sibling != window && sibling->isWidget() + && static_cast<QGraphicsWidget *>(sibling)->isWindow()) { + siblingWindows << sibling; + } + } + + // Find the highest z value. + qreal z = window->zValue(); + for (int i = 0; i < siblingWindows.size(); ++i) + z = qMax(z, siblingWindows.at(i)->zValue()); + + // This will probably never overflow. + const qreal litt = qreal(0.001); + window->setZValue(z + litt); + + if (QGraphicsWidget *focusChild = window->focusWidget()) + focusChild->setFocus(Qt::ActiveWindowFocusReason); + } +} + +QT_END_NAMESPACE + +#include "moc_qgraphicsscene.cpp" + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscene.h b/src/gui/graphicsview/qgraphicsscene.h new file mode 100644 index 0000000..9802f87 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscene.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENE_H +#define QGRAPHICSSCENE_H + +#include <QtCore/qobject.h> +#include <QtCore/qpoint.h> +#include <QtCore/qrect.h> +#include <QtGui/qbrush.h> +#include <QtGui/qfont.h> +#include <QtGui/qtransform.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qpen.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +template<typename T> class QList; +class QFocusEvent; +class QFont; +class QFontMetrics; +class QGraphicsEllipseItem; +class QGraphicsItem; +class QGraphicsItemGroup; +class QGraphicsLineItem; +class QGraphicsPathItem; +class QGraphicsPixmapItem; +class QGraphicsPolygonItem; +class QGraphicsProxyWidget; +class QGraphicsRectItem; +class QGraphicsSceneContextMenuEvent; +class QGraphicsSceneDragDropEvent; +class QGraphicsSceneEvent; +class QGraphicsSceneHelpEvent; +class QGraphicsSceneHoverEvent; +class QGraphicsSceneMouseEvent; +class QGraphicsSceneWheelEvent; +class QGraphicsSimpleTextItem; +class QGraphicsTextItem; +class QGraphicsView; +class QGraphicsWidget; +class QHelpEvent; +class QInputMethodEvent; +class QKeyEvent; +class QLineF; +class QPainterPath; +class QPixmap; +class QPointF; +class QPolygonF; +class QRectF; +class QSizeF; +class QStyle; +class QStyleOptionGraphicsItem; + +class QGraphicsScenePrivate; +class Q_GUI_EXPORT QGraphicsScene : public QObject +{ + Q_OBJECT + Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush) + Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush) + Q_PROPERTY(ItemIndexMethod itemIndexMethod READ itemIndexMethod WRITE setItemIndexMethod) + Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect) + Q_PROPERTY(int bspTreeDepth READ bspTreeDepth WRITE setBspTreeDepth) + Q_PROPERTY(QPalette palette READ palette WRITE setPalette) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(bool sortCacheEnabled READ isSortCacheEnabled WRITE setSortCacheEnabled) + Q_PROPERTY(bool stickyFocus READ stickyFocus WRITE setStickyFocus) + +public: + enum ItemIndexMethod { + BspTreeIndex, + NoIndex = -1 + }; + + enum SceneLayer { + ItemLayer = 0x1, + BackgroundLayer = 0x2, + ForegroundLayer = 0x4, + AllLayers = 0xffff + }; + Q_DECLARE_FLAGS(SceneLayers, SceneLayer) + + QGraphicsScene(QObject *parent = 0); + QGraphicsScene(const QRectF &sceneRect, QObject *parent = 0); + QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent = 0); + virtual ~QGraphicsScene(); + + QRectF sceneRect() const; + inline qreal width() const { return sceneRect().width(); } + inline qreal height() const { return sceneRect().height(); } + void setSceneRect(const QRectF &rect); + inline void setSceneRect(qreal x, qreal y, qreal w, qreal h) + { setSceneRect(QRectF(x, y, w, h)); } + + void render(QPainter *painter, + const QRectF &target = QRectF(), const QRectF &source = QRectF(), + Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); + + ItemIndexMethod itemIndexMethod() const; + void setItemIndexMethod(ItemIndexMethod method); + + bool isSortCacheEnabled() const; + void setSortCacheEnabled(bool enabled); + + int bspTreeDepth() const; + void setBspTreeDepth(int depth); + + QRectF itemsBoundingRect() const; + + QList<QGraphicsItem *> items() const; + QList<QGraphicsItem *> items(const QPointF &pos) const; + QList<QGraphicsItem *> items(const QRectF &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(const QPolygonF &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> collidingItems(const QGraphicsItem *item, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QGraphicsItem *itemAt(const QPointF &pos) const; + + inline QList<QGraphicsItem *> items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const + { return items(QRectF(x, y, w, h), mode); } + inline QGraphicsItem *itemAt(qreal x, qreal y) const + { return itemAt(QPointF(x, y)); } + + QList<QGraphicsItem *> selectedItems() const; + QPainterPath selectionArea() const; + void setSelectionArea(const QPainterPath &path); + void setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode); + + QGraphicsItemGroup *createItemGroup(const QList<QGraphicsItem *> &items); + void destroyItemGroup(QGraphicsItemGroup *group); + + void addItem(QGraphicsItem *item); + QGraphicsEllipseItem *addEllipse(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush()); + QGraphicsLineItem *addLine(const QLineF &line, const QPen &pen = QPen()); + QGraphicsPathItem *addPath(const QPainterPath &path, const QPen &pen = QPen(), const QBrush &brush = QBrush()); + QGraphicsPixmapItem *addPixmap(const QPixmap &pixmap); + QGraphicsPolygonItem *addPolygon(const QPolygonF &polygon, const QPen &pen = QPen(), const QBrush &brush = QBrush()); + QGraphicsRectItem *addRect(const QRectF &rect, const QPen &pen = QPen(), const QBrush &brush = QBrush()); + QGraphicsTextItem *addText(const QString &text, const QFont &font = QFont()); + QGraphicsSimpleTextItem *addSimpleText(const QString &text, const QFont &font = QFont()); + QGraphicsProxyWidget *addWidget(QWidget *widget, Qt::WindowFlags wFlags = 0); + inline QGraphicsEllipseItem *addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush()) + { return addEllipse(QRectF(x, y, w, h), pen, brush); } + inline QGraphicsLineItem *addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen = QPen()) + { return addLine(QLineF(x1, y1, x2, y2), pen); } + inline QGraphicsRectItem *addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen = QPen(), const QBrush &brush = QBrush()) + { return addRect(QRectF(x, y, w, h), pen, brush); } + void removeItem(QGraphicsItem *item); + + QGraphicsItem *focusItem() const; + void setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason = Qt::OtherFocusReason); + bool hasFocus() const; + void setFocus(Qt::FocusReason focusReason = Qt::OtherFocusReason); + void clearFocus(); + + void setStickyFocus(bool enabled); + bool stickyFocus() const; + + QGraphicsItem *mouseGrabberItem() const; + + QBrush backgroundBrush() const; + void setBackgroundBrush(const QBrush &brush); + + QBrush foregroundBrush() const; + void setForegroundBrush(const QBrush &brush); + + virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + QList <QGraphicsView *> views() const; + + inline void update(qreal x, qreal y, qreal w, qreal h) + { update(QRectF(x, y, w, h)); } + inline void invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers = AllLayers) + { invalidate(QRectF(x, y, w, h), layers); } + + QStyle *style() const; + void setStyle(QStyle *style); + + QFont font() const; + void setFont(const QFont &font); + + QPalette palette() const; + void setPalette(const QPalette &palette); + + QGraphicsWidget *activeWindow() const; + void setActiveWindow(QGraphicsWidget *widget); + +public Q_SLOTS: + void update(const QRectF &rect = QRectF()); + void invalidate(const QRectF &rect = QRectF(), SceneLayers layers = AllLayers); + void advance(); + void clearSelection(); + void clear(); + +protected: + bool event(QEvent *event); + bool eventFilter(QObject *watched, QEvent *event); + virtual void contextMenuEvent(QGraphicsSceneContextMenuEvent *event); + virtual void dragEnterEvent(QGraphicsSceneDragDropEvent *event); + virtual void dragMoveEvent(QGraphicsSceneDragDropEvent *event); + virtual void dragLeaveEvent(QGraphicsSceneDragDropEvent *event); + virtual void dropEvent(QGraphicsSceneDragDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void helpEvent(QGraphicsSceneHelpEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void mousePressEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseMoveEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); + virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); + virtual void wheelEvent(QGraphicsSceneWheelEvent *event); + virtual void inputMethodEvent(QInputMethodEvent *event); + + virtual void drawBackground(QPainter *painter, const QRectF &rect); + virtual void drawForeground(QPainter *painter, const QRectF &rect); + virtual void drawItems(QPainter *painter, int numItems, + QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[], + QWidget *widget = 0); + +protected Q_SLOTS: + bool focusNextPrevChild(bool next); + +Q_SIGNALS: + void changed(const QList<QRectF> ®ion); + void sceneRectChanged(const QRectF &rect); + void selectionChanged(); + +private: + void itemUpdated(QGraphicsItem *item, const QRectF &rect); + + Q_DECLARE_PRIVATE(QGraphicsScene) + Q_DISABLE_COPY(QGraphicsScene) + Q_PRIVATE_SLOT(d_func(), void _q_updateIndex()) + Q_PRIVATE_SLOT(d_func(), void _q_emitUpdated()) + Q_PRIVATE_SLOT(d_func(), void _q_removeItemLater(QGraphicsItem *item)) + Q_PRIVATE_SLOT(d_func(), void _q_updateLater()) + Q_PRIVATE_SLOT(d_func(), void _q_polishItems()) + Q_PRIVATE_SLOT(d_func(), void _q_updateSortCache()) + Q_PRIVATE_SLOT(d_func(), void _q_resetDirtyItems()) + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsView; + friend class QGraphicsViewPrivate; + friend class QGraphicsWidget; + friend class QGraphicsWidgetPrivate; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsScene::SceneLayers) + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/graphicsview/qgraphicsscene_bsp.cpp b/src/gui/graphicsview/qgraphicsscene_bsp.cpp new file mode 100644 index 0000000..f8fa450 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscene_bsp.cpp @@ -0,0 +1,328 @@ +/**************************************************************************** +** +** 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 "qgraphicsscene_bsp_p.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include <QtCore/qstring.h> +#include <private/qgraphicsitem_p.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneInsertItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor +{ +public: + QGraphicsItem *item; + + void visit(QList<QGraphicsItem *> *items) + { items->prepend(item); } +}; + +class QGraphicsSceneRemoveItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor +{ +public: + QGraphicsItem *item; + + void visit(QList<QGraphicsItem *> *items) + { items->removeAll(item); } +}; + +class QGraphicsSceneFindItemBspTreeVisitor : public QGraphicsSceneBspTreeVisitor +{ +public: + QList<QGraphicsItem *> *foundItems; + + void visit(QList<QGraphicsItem *> *items) + { + for (int i = 0; i < items->size(); ++i) { + QGraphicsItem *item = items->at(i); + if (!item->d_func()->itemDiscovered && item->isVisible()) { + item->d_func()->itemDiscovered = 1; + foundItems->prepend(item); + } + } + } +}; + +QGraphicsSceneBspTree::QGraphicsSceneBspTree() + : leafCnt(0) +{ + insertVisitor = new QGraphicsSceneInsertItemBspTreeVisitor; + removeVisitor = new QGraphicsSceneRemoveItemBspTreeVisitor; + findVisitor = new QGraphicsSceneFindItemBspTreeVisitor; +} + +QGraphicsSceneBspTree::~QGraphicsSceneBspTree() +{ + delete insertVisitor; + delete removeVisitor; + delete findVisitor; +} + +void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth) +{ + this->rect = rect; + leafCnt = 0; + nodes.resize((1 << (depth + 1)) - 1); + nodes.fill(Node()); + leaves.resize(1 << depth); + leaves.fill(QList<QGraphicsItem *>()); + + initialize(rect, depth, 0); +} + +void QGraphicsSceneBspTree::clear() +{ + leafCnt = 0; + nodes.clear(); + leaves.clear(); +} + +void QGraphicsSceneBspTree::insertItem(QGraphicsItem *item, const QRectF &rect) +{ + insertVisitor->item = item; + climbTree(insertVisitor, rect); +} + +void QGraphicsSceneBspTree::removeItem(QGraphicsItem *item, const QRectF &rect) +{ + removeVisitor->item = item; + climbTree(removeVisitor, rect); +} + +void QGraphicsSceneBspTree::removeItems(const QSet<QGraphicsItem *> &items) +{ + for (int i = 0; i < leaves.size(); ++i) { + QList<QGraphicsItem *> newItemList; + const QList<QGraphicsItem *> &oldItemList = leaves[i]; + for (int j = 0; j < oldItemList.size(); ++j) { + QGraphicsItem *item = oldItemList.at(j); + if (!items.contains(item)) + newItemList << item; + } + leaves[i] = newItemList; + } +} + +QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QRectF &rect) +{ + QList<QGraphicsItem *> tmp; + findVisitor->foundItems = &tmp; + climbTree(findVisitor, rect); + return tmp; +} + +QList<QGraphicsItem *> QGraphicsSceneBspTree::items(const QPointF &pos) +{ + QList<QGraphicsItem *> tmp; + findVisitor->foundItems = &tmp; + climbTree(findVisitor, pos); + return tmp; +} + +int QGraphicsSceneBspTree::leafCount() const +{ + return leafCnt; +} + +QString QGraphicsSceneBspTree::debug(int index) const +{ + const Node *node = &nodes.at(index); + + QString tmp; + if (node->type == Node::Leaf) { + QRectF rect = rectForIndex(index); + if (!leaves[node->leafIndex].isEmpty()) { + tmp += QString::fromLatin1("[%1, %2, %3, %4] contains %5 items\n") + .arg(rect.left()).arg(rect.top()) + .arg(rect.width()).arg(rect.height()) + .arg(leaves[node->leafIndex].size()); + } + } else { + if (node->type == Node::Horizontal) { + tmp += debug(firstChildIndex(index)); + tmp += debug(firstChildIndex(index) + 1); + } else { + tmp += debug(firstChildIndex(index)); + tmp += debug(firstChildIndex(index) + 1); + } + } + + return tmp; +} + +void QGraphicsSceneBspTree::initialize(const QRectF &rect, int depth, int index) +{ + Node *node = &nodes[index]; + if (index == 0) { + node->type = Node::Horizontal; + node->offset = rect.center().x(); + } + + if (depth) { + Node::Type type; + QRectF rect1, rect2; + qreal offset1, offset2; + + if (node->type == Node::Horizontal) { + type = Node::Vertical; + rect1.setRect(rect.left(), rect.top(), rect.width(), rect.height() / 2); + rect2.setRect(rect1.left(), rect1.bottom(), rect1.width(), rect.height() - rect1.height()); + offset1 = rect1.center().x(); + offset2 = rect2.center().x(); + } else { + type = Node::Horizontal; + rect1.setRect(rect.left(), rect.top(), rect.width() / 2, rect.height()); + rect2.setRect(rect1.right(), rect1.top(), rect.width() - rect1.width(), rect1.height()); + offset1 = rect1.center().y(); + offset2 = rect2.center().y(); + } + + int childIndex = firstChildIndex(index); + + Node *child = &nodes[childIndex]; + child->offset = offset1; + child->type = type; + + child = &nodes[childIndex + 1]; + child->offset = offset2; + child->type = type; + + initialize(rect1, depth - 1, childIndex); + initialize(rect2, depth - 1, childIndex + 1); + } else { + node->type = Node::Leaf; + node->leafIndex = leafCnt++; + } +} + +void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index) +{ + if (nodes.isEmpty()) + return; + + const Node &node = nodes.at(index); + int childIndex = firstChildIndex(index); + + switch (node.type) { + case Node::Leaf: { + visitor->visit(&leaves[node.leafIndex]); + break; + } + case Node::Vertical: + if (pos.x() < node.offset) { + climbTree(visitor, pos, childIndex); + } else { + climbTree(visitor, pos, childIndex + 1); + } + break; + case Node::Horizontal: + if (pos.y() < node.offset) { + climbTree(visitor, pos, childIndex); + } else { + climbTree(visitor, pos, childIndex + 1); + } + break; + } +} + +void QGraphicsSceneBspTree::climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index) +{ + if (nodes.isEmpty()) + return; + + const Node &node = nodes.at(index); + int childIndex = firstChildIndex(index); + + switch (node.type) { + case Node::Leaf: { + visitor->visit(&leaves[node.leafIndex]); + break; + } + case Node::Vertical: + if (rect.left() < node.offset) { + climbTree(visitor, rect, childIndex); + if (rect.right() >= node.offset) + climbTree(visitor, rect, childIndex + 1); + } else { + climbTree(visitor, rect, childIndex + 1); + } + break; + case Node::Horizontal: + int childIndex = firstChildIndex(index); + if (rect.top() < node.offset) { + climbTree(visitor, rect, childIndex); + if (rect.bottom() >= node.offset) + climbTree(visitor, rect, childIndex + 1); + } else { + climbTree(visitor, rect, childIndex + 1); + } + } +} + +QRectF QGraphicsSceneBspTree::rectForIndex(int index) const +{ + if (index <= 0) + return rect; + + int parentIdx = parentIndex(index); + QRectF rect = rectForIndex(parentIdx); + const Node *parent = &nodes.at(parentIdx); + + if (parent->type == Node::Horizontal) { + if (index & 1) + rect.setRight(parent->offset); + else + rect.setLeft(parent->offset); + } else { + if (index & 1) + rect.setBottom(parent->offset); + else + rect.setTop(parent->offset); + } + + return rect; +} + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsscene_bsp_p.h b/src/gui/graphicsview/qgraphicsscene_bsp_p.h new file mode 100644 index 0000000..e6ceb78 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscene_bsp_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENEBSPTREE_P_H +#define QGRAPHICSSCENEBSPTREE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qlist.h> + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include <QtCore/qrect.h> +#include <QtCore/qset.h> +#include <QtCore/qvector.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsItem; +class QGraphicsSceneBspTreeVisitor; +class QGraphicsSceneInsertItemBspTreeVisitor; +class QGraphicsSceneRemoveItemBspTreeVisitor; +class QGraphicsSceneFindItemBspTreeVisitor; + +class QGraphicsSceneBspTree +{ +public: + struct Node + { + enum Type { Horizontal, Vertical, Leaf }; + union { + qreal offset; + int leafIndex; + }; + Type type; + }; + + QGraphicsSceneBspTree(); + ~QGraphicsSceneBspTree(); + + void initialize(const QRectF &rect, int depth); + void clear(); + + void insertItem(QGraphicsItem *item, const QRectF &rect); + void removeItem(QGraphicsItem *item, const QRectF &rect); + void removeItems(const QSet<QGraphicsItem *> &items); + + QList<QGraphicsItem *> items(const QRectF &rect); + QList<QGraphicsItem *> items(const QPointF &pos); + int leafCount() const; + + inline int firstChildIndex(int index) const + { return index * 2 + 1; } + + inline int parentIndex(int index) const + { return index > 0 ? ((index & 1) ? ((index - 1) / 2) : ((index - 2) / 2)) : -1; } + + QString debug(int index) const; + +private: + void initialize(const QRectF &rect, int depth, int index); + void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QPointF &pos, int index = 0); + void climbTree(QGraphicsSceneBspTreeVisitor *visitor, const QRectF &rect, int index = 0); + + void findItems(QList<QGraphicsItem *> *foundItems, const QRectF &rect, int index); + void findItems(QList<QGraphicsItem *> *foundItems, const QPointF &pos, int index); + QRectF rectForIndex(int index) const; + + QVector<Node> nodes; + QVector<QList<QGraphicsItem *> > leaves; + int leafCnt; + QRectF rect; + + QGraphicsSceneInsertItemBspTreeVisitor *insertVisitor; + QGraphicsSceneRemoveItemBspTreeVisitor *removeVisitor; + QGraphicsSceneFindItemBspTreeVisitor *findVisitor; +}; + +class QGraphicsSceneBspTreeVisitor +{ +public: + virtual ~QGraphicsSceneBspTreeVisitor() { } + virtual void visit(QList<QGraphicsItem *> *items) = 0; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +#endif // QGRAPHICSSCENEBSPTREE_P_H diff --git a/src/gui/graphicsview/qgraphicsscene_p.h b/src/gui/graphicsview/qgraphicsscene_p.h new file mode 100644 index 0000000..7f441da --- /dev/null +++ b/src/gui/graphicsview/qgraphicsscene_p.h @@ -0,0 +1,268 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENE_P_H +#define QGRAPHICSSCENE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsscene.h" + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include "qgraphicsscene_bsp_p.h" +#include "qgraphicsitem_p.h" + +#include <private/qobject_p.h> +#include <QtCore/qbitarray.h> +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> +#include <QtCore/qset.h> +#include <QtGui/qfont.h> +#include <QtGui/qpalette.h> +#include <QtGui/qstyle.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsView; +class QGraphicsWidget; + +class QGraphicsScenePrivate : public QObjectPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsScene) +public: + QGraphicsScenePrivate(); + void init(); + + quint32 changedSignalMask; + + QGraphicsScene::ItemIndexMethod indexMethod; + int bspTreeDepth; + + QList<QGraphicsItem *> estimateItemsInRect(const QRectF &rect) const; + void addToIndex(QGraphicsItem *item); + void removeFromIndex(QGraphicsItem *item); + void resetIndex(); + + QGraphicsSceneBspTree bspTree; + void _q_updateIndex(); + int lastItemCount; + + QRectF sceneRect; + bool hasSceneRect; + QRectF growingItemsBoundingRect; + QRectF largestUntransformableItem; + + void _q_emitUpdated(); + QList<QRectF> updatedRects; + bool updateAll; + bool calledEmitUpdated; + + QPainterPath selectionArea; + int selectionChanging; + QSet<QGraphicsItem *> selectedItems; + QList<QGraphicsItem *> unindexedItems; + QList<QGraphicsItem *> indexedItems; + QList<QGraphicsItem *> dirtyItems; + QList<QGraphicsItem *> pendingUpdateItems; + QList<QGraphicsItem *> unpolishedItems; + QList<QGraphicsItem *> topLevelItems; + QMap<QGraphicsItem *, QPointF> movingItemsInitialPositions; + void registerTopLevelItem(QGraphicsItem *item); + void unregisterTopLevelItem(QGraphicsItem *item); + void _q_updateLater(); + void _q_polishItems(); + + void _q_resetDirtyItems(); + void resetDirtyItemsLater(); + bool dirtyItemResetPending; + + QList<int> freeItemIndexes; + bool regenerateIndex; + + bool purgePending; + void _q_removeItemLater(QGraphicsItem *item); + QSet<QGraphicsItem *> removedItems; + void purgeRemovedItems(); + + QBrush backgroundBrush; + QBrush foregroundBrush; + + int indexTimerId; + bool restartIndexTimer; + void startIndexTimer(); + + bool stickyFocus; + bool hasFocus; + QGraphicsItem *focusItem; + QGraphicsItem *lastFocusItem; + QGraphicsWidget *tabFocusFirst; + QGraphicsWidget *activeWindow; + int activationRefCount; + + QList<QGraphicsWidget *> popupWidgets; + void addPopup(QGraphicsWidget *widget); + void removePopup(QGraphicsWidget *widget, bool itemIsDying = false); + + QGraphicsItem *lastMouseGrabberItem; + bool lastMouseGrabberItemHasImplicitMouseGrab; + QList<QGraphicsItem *> mouseGrabberItems; + void grabMouse(QGraphicsItem *item, bool implicit = false); + void ungrabMouse(QGraphicsItem *item, bool itemIsDying = false); + void clearMouseGrabber(); + + QList<QGraphicsItem *> keyboardGrabberItems; + void grabKeyboard(QGraphicsItem *item); + void ungrabKeyboard(QGraphicsItem *item, bool itemIsDying = false); + void clearKeyboardGrabber(); + + QGraphicsItem *dragDropItem; + QGraphicsWidget *enterWidget; + Qt::DropAction lastDropAction; + QList<QGraphicsItem *> cachedItemsUnderMouse; + QList<QGraphicsItem *> hoverItems; + QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownPos; + QMap<Qt::MouseButton, QPointF> mouseGrabberButtonDownScenePos; + QMap<Qt::MouseButton, QPoint> mouseGrabberButtonDownScreenPos; + QList<QGraphicsItem *> itemsAtPosition(const QPoint &screenPos, + const QPointF &scenePos, + QWidget *widget) const; + static bool itemCollidesWithPath(QGraphicsItem *item, const QPainterPath &path, Qt::ItemSelectionMode mode); + void storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event); + + QList<QGraphicsView *> views; + bool painterStateProtection; + + QMultiMap<QGraphicsItem *, QGraphicsItem *> sceneEventFilters; + void installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter); + void removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter); + bool filterEvent(QGraphicsItem *item, QEvent *event); + bool sendEvent(QGraphicsItem *item, QEvent *event); + + bool dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent); + bool itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const; + void leaveScene(); + + void cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest, + QGraphicsSceneDragDropEvent *source); + void sendDragDropEvent(QGraphicsItem *item, + QGraphicsSceneDragDropEvent *dragDropEvent); + void sendHoverEvent(QEvent::Type type, QGraphicsItem *item, + QGraphicsSceneHoverEvent *hoverEvent); + void sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent); + void mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent); + QGraphicsWidget *windowForItem(const QGraphicsItem *item) const; + + QList<QGraphicsItem *> items_helper(const QRectF &rect, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const; + QList<QGraphicsItem *> items_helper(const QPolygonF &rect, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const; + QList<QGraphicsItem *> items_helper(const QPainterPath &rect, + Qt::ItemSelectionMode mode, + Qt::SortOrder order) const; + void childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QRectF &rect, + Qt::ItemSelectionMode mode) const; + void childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QPolygonF &polygon, + Qt::ItemSelectionMode mode) const; + void childItems_helper(QList<QGraphicsItem *> *items, + const QGraphicsItem *parent, + const QPainterPath &path, + Qt::ItemSelectionMode mode) const; + + bool sortCacheEnabled; + bool updatingSortCache; + void invalidateSortCache(); + static void climbTree(QGraphicsItem *item, int *stackingOrder); + void _q_updateSortCache(); + + static bool closestItemFirst_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + static bool closestItemLast_withoutCache(const QGraphicsItem *item1, const QGraphicsItem *item2); + + static inline bool closestItemFirst_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder < item2->d_ptr->globalStackingOrder; + } + static inline bool closestItemLast_withCache(const QGraphicsItem *item1, const QGraphicsItem *item2) + { + return item1->d_ptr->globalStackingOrder >= item2->d_ptr->globalStackingOrder; + } + + static void sortItems(QList<QGraphicsItem *> *itemList, Qt::SortOrder order, bool cached); + + void drawItemHelper(QGraphicsItem *item, QPainter *painter, + const QStyleOptionGraphicsItem *option, QWidget *widget, + bool painterStateProtection); + + QStyle *style; + QFont font; + void setFont_helper(const QFont &font); + void resolveFont(); + void updateFont(const QFont &font); + QPalette palette; + void setPalette_helper(const QPalette &palette); + void resolvePalette(); + void updatePalette(const QPalette &palette); + + mutable QVector<QTransform> sceneTransformCache; + mutable QBitArray validTransforms; + mutable QVector<int> freeSceneTransformSlots; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +#endif diff --git a/src/gui/graphicsview/qgraphicssceneevent.cpp b/src/gui/graphicsview/qgraphicssceneevent.cpp new file mode 100644 index 0000000..b819c2c --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneevent.cpp @@ -0,0 +1,1678 @@ +/**************************************************************************** +** +** 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 QGraphicsSceneEvent + \brief The QGraphicsSceneEvent class provides a base class for all + graphics view related events. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + When a QGraphicsView receives Qt mouse, keyboard, and drag and + drop events (QMouseEvent, QKeyEvent, QDragEvent, etc.), it + translates them into instances of QGraphicsSceneEvent subclasses + and forwards them to the QGraphicsScene it displays. The scene + then forwards the events to the relevant items. + + For example, when a QGraphicsView receives a QMouseEvent of type + MousePress as a response to a user click, the view sends a + QGraphicsSceneMouseEvent of type GraphicsSceneMousePress to the + underlying QGraphicsScene through its + \l{QGraphicsScene::}{mousePressEvent()} function. The default + QGraphicsScene::mousePressEvent() implementation determines which + item was clicked and forwards the event to + QGraphicsItem::mousePressEvent(). + + \omit ### Beskrive widget() \endomit + + Subclasses such as QGraphicsSceneMouseEvent and + QGraphicsSceneContextMenuEvent provide the coordinates from the + original QEvent in screen, scene, and item coordinates (see + \l{QGraphicsSceneMouseEvent::}{screenPos()}, + \l{QGraphicsSceneMouseEvent::}{scenePos()}, and + \l{QGraphicsSceneMouseEvent::}{pos()}). The item coordinates are + set by the QGraphicsScene before it forwards the event to the + event to a QGraphicsItem. The mouse events also add the + possibility to retrieve the coordinates from the last event + received by the view (see + \l{QGraphicsSceneMouseEvent::}{lastScreenPos()}, + \l{QGraphicsSceneMouseEvent::}{lastScenePos()}, and + \l{QGraphicsSceneMouseEvent::}{lastPos()}). + + \sa QEvent +*/ + +/*! + \class QGraphicsSceneMouseEvent + \brief The QGraphicsSceneMouseEvent class provides mouse events + in the graphics view framework. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + When a QGraphicsView receives a QMouseEvent, it translates it to + a QGraphicsSceneMouseEvent. The event is then forwarded to the + QGraphicsScene associated with the view. + + In addition to containing the item, scene, and screen coordinates + of the event (as pos(), scenePos(), and screenPos()), mouse + events also contain the coordinates of the previous mouse + event received by the view. These can be retrieved with + lastPos(), lastScreenPos(), and lastScenePos(). + + \sa QGraphicsSceneContextMenuEvent, + QGraphicsSceneHoverEvent, QGraphicsSceneWheelEvent, + QMouseEvent +*/ + +/*! + \class QGraphicsSceneWheelEvent + \brief The QGraphicsSceneWheelEvent class provides wheel events + in the graphics view framework. + \brief The QGraphicsSceneWheelEvent class provides wheel events in the + graphics view framework. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + \l{QWheelEvent}{QWheelEvent}s received by a QGraphicsView are translated + into QGraphicsSceneWheelEvents; it translates the QWheelEvent::globalPos() + into item, scene, and screen coordinates (pos(), scenePos(), and + screenPos()). + + \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent, + QGraphicsSceneHoverEvent, QWheelEvent +*/ + +/*! + \class QGraphicsSceneContextMenuEvent + \brief The QGraphicsSceneContextMenuEvent class provides context + menu events in the graphics view framework. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + A QContextMenuEvent received by a QGraphicsView is translated + into a QGraphicsSceneContextMenuEvent. The + QContextMenuEvent::globalPos() is translated into item, scene, and + screen coordinates (pos(), scenePos(), and screenPos()). + + \sa QGraphicsSceneMouseEvent, QGraphicsSceneWheelEvent, + QContextMenuEvent +*/ + +/*! + \enum QGraphicsSceneContextMenuEvent::Reason + + This enum describes the reason why the context event was sent. + + \value Mouse The mouse caused the event to be sent. On most + platforms, this means the right mouse button was clicked. + + \value Keyboard The keyboard caused this event to be sent. On + Windows and Mac OS X, this means the menu button was pressed. + + \value Other The event was sent by some other means (i.e. not + by the mouse or keyboard). +*/ + +/*! + \class QGraphicsSceneHoverEvent + \brief The QGraphicsSceneHoverEvent class provides hover events + in the graphics view framework. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + When a QGraphicsView receives a QHoverEvent event, it translates + it into QGraphicsSceneHoverEvent. The event is then forwarded to + the QGraphicsScene associated with the view. + + \sa QGraphicsSceneMouseEvent, QGraphicsSceneContextMenuEvent, + QGraphicsSceneWheelEvent, QHoverEvent +*/ + +/*! + \class QGraphicsSceneHelpEvent + \brief The QGraphicsSceneHelpEvent class provides events when a + tooltip is requested. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + When a QGraphicsView receives a QEvent of type + QEvent::ToolTip, it creates a QGraphicsSceneHelpEvent, which is + forwarded to the scene. You can set a tooltip on a QGraphicsItem + with \l{QGraphicsItem::}{setToolTip()}; by default QGraphicsScene + displays the tooltip of the QGraphicsItem with the highest + z-value (i.e, the top-most item) under the mouse position. + + QGraphicsView does not forward events when + \l{QWhatsThis}{"What's This"} and \l{QStatusTipEvent}{status tip} + help is requested. If you need this, you can reimplement + QGraphicsView::viewportEvent() and forward QStatusTipEvent + events and \l{QEvent}{QEvents} of type QEvent::WhatsThis to the + scene. + + \sa QEvent +*/ + +/*! + \class QGraphicsSceneDragDropEvent + \brief The QGraphicsSceneDragDropEvent class provides events for + drag and drop in the graphics view framework. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsView inherits the drag and drop functionality provided + by QWidget. When it receives a drag and drop event, it translates + it to a QGraphicsSceneDragDropEvent. + + QGraphicsSceneDragDropEvent stores events of type + GraphicsSceneDragEnter, GraphicsSceneDragLeave, + GraphicsSceneDragMove, or GraphicsSceneDrop. + + QGraphicsSceneDragDropEvent contains the position of the mouse + cursor in both item, scene, and screen coordinates; this can be + retrieved with pos(), scenePos(), and screenPos(). + + The scene sends the event to the first QGraphicsItem under the + mouse cursor that accepts drops; a graphics item is set to accept + drops with \l{QGraphicsItem::}{setAcceptDrops()}. +*/ + +/*! + \class QGraphicsSceneResizeEvent + \brief The QGraphicsSceneResizeEvent class provides events for widget + resizing in the graphics view framework. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + A QGraphicsWidget sends itself a QGraphicsSceneResizeEvent immediately + when its geometry changes. + + It's similar to QResizeEvent, but its sizes, oldSize() and newSize(), use + QSizeF instead of QSize. + + \sa QGraphicsWidget::setGeometry(), QGraphicsWidget::resize() +*/ + +/*! + \class QGraphicsSceneMoveEvent + \brief The QGraphicsSceneMoveEvent class provides events for widget + moving in the graphics view framework. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + A QGraphicsWidget sends itself a QGraphicsSceneMoveEvent immediately when + its local position changes. The delivery is implemented as part of + QGraphicsItem::itemChange(). + + It's similar to QMoveEvent, but its positions, oldPos() and newPos(), use + QPointF instead of QPoint. + + \sa QGraphicsItem::setPos(), QGraphicsItem::ItemPositionChange, + QGraphicsItem::ItemPositionHasChanged +*/ + +#include "qgraphicssceneevent.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#ifndef QT_NO_DEBUG +#include <QtCore/qdebug.h> +#endif +#include <QtCore/qmap.h> +#include <QtCore/qpoint.h> +#include <QtCore/qsize.h> +#include <QtCore/qstring.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsSceneEventPrivate +{ +public: + inline QGraphicsSceneEventPrivate() + : widget(0), + q_ptr(0) + { } + + inline virtual ~QGraphicsSceneEventPrivate() + { } + + QWidget *widget; + QGraphicsSceneEvent *q_ptr; +}; + +/*! + \internal + + Constructs a generic graphics scene event of the specified \a type. +*/ +QGraphicsSceneEvent::QGraphicsSceneEvent(Type type) + : QEvent(type), d_ptr(new QGraphicsSceneEventPrivate) +{ + d_ptr->q_ptr = this; +} + +/*! + \internal + + Constructs a generic graphics scene event. +*/ +QGraphicsSceneEvent::QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type) + : QEvent(type), d_ptr(&dd) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys the event. +*/ +QGraphicsSceneEvent::~QGraphicsSceneEvent() +{ + delete d_ptr; +} + +/*! + Returns the widget where the event originated, or 0 if the event + originates from another application. +*/ +QWidget *QGraphicsSceneEvent::widget() const +{ + return d_ptr->widget; +} + +/*! + \internal + + Sets the \a widget related to this event. + + \sa widget() +*/ +void QGraphicsSceneEvent::setWidget(QWidget *widget) +{ + d_ptr->widget = widget; +} + +class QGraphicsSceneMouseEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneMouseEvent) +public: + inline QGraphicsSceneMouseEventPrivate() + : button(Qt::NoButton), + buttons(0), modifiers(0) + { } + + QPointF pos; + QPointF scenePos; + QPoint screenPos; + QPointF lastPos; + QPointF lastScenePos; + QPoint lastScreenPos; + QMap<Qt::MouseButton, QPointF> buttonDownPos; + QMap<Qt::MouseButton, QPointF> buttonDownScenePos; + QMap<Qt::MouseButton, QPoint> buttonDownScreenPos; + Qt::MouseButton button; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; +}; + +/*! + \internal + + Constructs a generic graphics scene mouse event of the specified \a type. +*/ +QGraphicsSceneMouseEvent::QGraphicsSceneMouseEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneMouseEventPrivate, type) +{ +} + +/*! + Destroys the event. +*/ +QGraphicsSceneMouseEvent::~QGraphicsSceneMouseEvent() +{ +} + +/*! + Returns the mouse cursor position in item coordinates. + + \sa scenePos(), screenPos(), lastPos() +*/ +QPointF QGraphicsSceneMouseEvent::pos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->pos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->pos = pos; +} + +/*! + Returns the mouse cursor position in scene coordinates. + + \sa pos(), screenPos(), lastScenePos() +*/ +QPointF QGraphicsSceneMouseEvent::scenePos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->scenePos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->scenePos = pos; +} + +/*! + Returns the mouse cursor position in screen coordinates. + + \sa pos(), scenePos(), lastScreenPos() +*/ +QPoint QGraphicsSceneMouseEvent::screenPos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->screenPos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->screenPos = pos; +} + +/*! + Returns the mouse cursor position in item coordinates where the specified + \a button was clicked. + + \sa buttonDownScenePos(), buttonDownScreenPos(), pos() +*/ +QPointF QGraphicsSceneMouseEvent::buttonDownPos(Qt::MouseButton button) const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->buttonDownPos.value(button); +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setButtonDownPos(Qt::MouseButton button, const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->buttonDownPos.insert(button, pos); +} + +/*! + Returns the mouse cursor position in scene coordinates where the + specified \a button was clicked. + + \sa buttonDownPos(), buttonDownScreenPos(), scenePos() +*/ +QPointF QGraphicsSceneMouseEvent::buttonDownScenePos(Qt::MouseButton button) const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->buttonDownScenePos.value(button); +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->buttonDownScenePos.insert(button, pos); +} + +/*! + Returns the mouse cursor position in screen coordinates where the + specified \a button was clicked. + + \sa screenPos(), buttonDownPos(), buttonDownScenePos() +*/ +QPoint QGraphicsSceneMouseEvent::buttonDownScreenPos(Qt::MouseButton button) const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->buttonDownScreenPos.value(button); +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->buttonDownScreenPos.insert(button, pos); +} + +/*! + Returns the last recorded mouse cursor position in item + coordinates. + + \sa lastScenePos(), lastScreenPos(), pos() +*/ +QPointF QGraphicsSceneMouseEvent::lastPos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->lastPos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setLastPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->lastPos = pos; +} + +/*! + Returns the last recorded mouse cursor position in scene + coordinates. The last recorded position is the position of + the previous mouse event received by the view that created + the event. + + \sa lastPos(), lastScreenPos(), scenePos() +*/ +QPointF QGraphicsSceneMouseEvent::lastScenePos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->lastScenePos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setLastScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->lastScenePos = pos; +} + +/*! + Returns the last recorded mouse cursor position in screen + coordinates. The last recorded position is the position of + the previous mouse event received by the view that created + the event. + + \sa lastPos(), lastScenePos(), screenPos() +*/ +QPoint QGraphicsSceneMouseEvent::lastScreenPos() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->lastScreenPos; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setLastScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneMouseEvent); + d->lastScreenPos = pos; +} + +/*! + Returns the combination of mouse buttons that were pressed at the + time the event was sent. + + \sa button(), modifiers() +*/ +Qt::MouseButtons QGraphicsSceneMouseEvent::buttons() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->buttons; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setButtons(Qt::MouseButtons buttons) +{ + Q_D(QGraphicsSceneMouseEvent); + d->buttons = buttons; +} + +/*! + Returns the mouse button (if any) that caused the event. + + \sa buttons(), modifiers() +*/ +Qt::MouseButton QGraphicsSceneMouseEvent::button() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->button; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setButton(Qt::MouseButton button) +{ + Q_D(QGraphicsSceneMouseEvent); + d->button = button; +} + +/*! + Returns the keyboard modifiers in use at the time the event was + sent. + + \sa buttons(), button() +*/ +Qt::KeyboardModifiers QGraphicsSceneMouseEvent::modifiers() const +{ + Q_D(const QGraphicsSceneMouseEvent); + return d->modifiers; +} + +/*! + \internal +*/ +void QGraphicsSceneMouseEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneMouseEvent); + d->modifiers = modifiers; +} + +class QGraphicsSceneWheelEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneWheelEvent) +public: + inline QGraphicsSceneWheelEventPrivate() + : buttons(0), modifiers(0), delta(0), orientation(Qt::Horizontal) + { } + + QPointF pos; + QPointF scenePos; + QPoint screenPos; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + int delta; + Qt::Orientation orientation; +}; + +/*! + \internal + + Constructs a QGraphicsSceneWheelEvent of type \a type, which + is always QEvent::GraphicsSceneWheel. +*/ +QGraphicsSceneWheelEvent::QGraphicsSceneWheelEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneWheelEventPrivate, type) +{ +} + +/*! + Destroys the QGraphicsSceneWheelEvent. +*/ +QGraphicsSceneWheelEvent::~QGraphicsSceneWheelEvent() +{ +} + +/*! + Returns the position of the cursor in item coordinates when the + wheel event occurred. + + \sa scenePos(), screenPos() +*/ +QPointF QGraphicsSceneWheelEvent::pos() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->pos; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneWheelEvent); + d->pos = pos; +} + +/*! + Returns the position of the cursor in scene coordinates when the wheel + event occurred. + + \sa pos(), screenPos() +*/ +QPointF QGraphicsSceneWheelEvent::scenePos() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->scenePos; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneWheelEvent); + d->scenePos = pos; +} + +/*! + Returns the position of the cursor in screen coordinates when the wheel + event occurred. + + \sa pos(), scenePos() +*/ +QPoint QGraphicsSceneWheelEvent::screenPos() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->screenPos; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneWheelEvent); + d->screenPos = pos; +} + +/*! + Returns the mouse buttons that were pressed when the wheel event occurred. + + \sa modifiers() +*/ +Qt::MouseButtons QGraphicsSceneWheelEvent::buttons() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->buttons; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setButtons(Qt::MouseButtons buttons) +{ + Q_D(QGraphicsSceneWheelEvent); + d->buttons = buttons; +} + +/*! + Returns the keyboard modifiers that were active when the wheel event + occurred. + + \sa buttons() +*/ +Qt::KeyboardModifiers QGraphicsSceneWheelEvent::modifiers() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->modifiers; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneWheelEvent); + d->modifiers = modifiers; +} + +/*! + Returns the distance that the wheel is rotated, in eighths (1/8s) + of a degree. A positive value indicates that the wheel was + rotated forwards away from the user; a negative value indicates + that the wheel was rotated backwards toward the user. + + Most mouse types work in steps of 15 degrees, in which case the delta + value is a multiple of 120 (== 15 * 8). +*/ +int QGraphicsSceneWheelEvent::delta() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->delta; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setDelta(int delta) +{ + Q_D(QGraphicsSceneWheelEvent); + d->delta = delta; +} + +/*! + Returns the wheel orientation. +*/ +Qt::Orientation QGraphicsSceneWheelEvent::orientation() const +{ + Q_D(const QGraphicsSceneWheelEvent); + return d->orientation; +} + +/*! + \internal +*/ +void QGraphicsSceneWheelEvent::setOrientation(Qt::Orientation orientation) +{ + Q_D(QGraphicsSceneWheelEvent); + d->orientation = orientation; +} + +class QGraphicsSceneContextMenuEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneContextMenuEvent) + public: + inline QGraphicsSceneContextMenuEventPrivate() + : modifiers(0), reason(QGraphicsSceneContextMenuEvent::Other) + { } + + QPointF pos; + QPointF scenePos; + QPoint screenPos; + Qt::KeyboardModifiers modifiers; + QGraphicsSceneContextMenuEvent::Reason reason; +}; + +/*! + \internal + + Constructs a graphics scene context menu event of the specified \a type. +*/ +QGraphicsSceneContextMenuEvent::QGraphicsSceneContextMenuEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneContextMenuEventPrivate, type) +{ +} + +/*! + Destroys the event. +*/ +QGraphicsSceneContextMenuEvent::~QGraphicsSceneContextMenuEvent() +{ +} + +/*! + Returns the position of the mouse cursor in item coordinates at the moment + the the context menu was requested. + + \sa scenePos(), screenPos() +*/ +QPointF QGraphicsSceneContextMenuEvent::pos() const +{ + Q_D(const QGraphicsSceneContextMenuEvent); + return d->pos; +} + +/*! + \fn void QGraphicsSceneContextMenuEvent::setPos(const QPointF &point) + \internal + + Sets the position associated with the context menu to the given \a point + in item coordinates. +*/ +void QGraphicsSceneContextMenuEvent::setPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneContextMenuEvent); + d->pos = pos; +} + +/*! + Returns the position of the mouse cursor in scene coordinates at the moment the + the context menu was requested. + + \sa pos(), screenPos() +*/ +QPointF QGraphicsSceneContextMenuEvent::scenePos() const +{ + Q_D(const QGraphicsSceneContextMenuEvent); + return d->scenePos; +} + +/*! + \fn void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &point) + \internal + + Sets the position associated with the context menu to the given \a point + in scene coordinates. +*/ +void QGraphicsSceneContextMenuEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneContextMenuEvent); + d->scenePos = pos; +} + +/*! + Returns the position of the mouse cursor in screen coordinates at the moment the + the context menu was requested. + + \sa pos(), scenePos() +*/ +QPoint QGraphicsSceneContextMenuEvent::screenPos() const +{ + Q_D(const QGraphicsSceneContextMenuEvent); + return d->screenPos; +} + +/*! + \fn void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &point) + \internal + + Sets the position associated with the context menu to the given \a point + in screen coordinates. +*/ +void QGraphicsSceneContextMenuEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneContextMenuEvent); + d->screenPos = pos; +} + +/*! + Returns the keyboard modifiers in use when the context menu was requested. +*/ +Qt::KeyboardModifiers QGraphicsSceneContextMenuEvent::modifiers() const +{ + Q_D(const QGraphicsSceneContextMenuEvent); + return d->modifiers; +} + +/*! + \internal + + Sets the keyboard modifiers associated with the context menu to the \a + modifiers specified. +*/ +void QGraphicsSceneContextMenuEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneContextMenuEvent); + d->modifiers = modifiers; +} + +/*! + Returns the reason for the context menu event. + + \sa QGraphicsSceneContextMenuEvent::Reason +*/ +QGraphicsSceneContextMenuEvent::Reason QGraphicsSceneContextMenuEvent::reason() const +{ + Q_D(const QGraphicsSceneContextMenuEvent); + return d->reason; +} + +/*! + \internal + Sets the reason for the context menu event to \a reason. + + \sa reason() +*/ +void QGraphicsSceneContextMenuEvent::setReason(Reason reason) +{ + Q_D(QGraphicsSceneContextMenuEvent); + d->reason = reason; +} + +class QGraphicsSceneHoverEventPrivate : public QGraphicsSceneEventPrivate +{ +public: + QPointF pos; + QPointF scenePos; + QPoint screenPos; + QPointF lastPos; + QPointF lastScenePos; + QPoint lastScreenPos; + Qt::KeyboardModifiers modifiers; +}; + +/*! + \internal + + Constructs a graphics scene hover event of the specified \a type. +*/ +QGraphicsSceneHoverEvent::QGraphicsSceneHoverEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneHoverEventPrivate, type) +{ +} + +/*! + Destroys the event. +*/ +QGraphicsSceneHoverEvent::~QGraphicsSceneHoverEvent() +{ +} + +/*! + Returns the position of the mouse cursor in item coordinates at the moment + the the hover event was sent. + + \sa scenePos(), screenPos() +*/ +QPointF QGraphicsSceneHoverEvent::pos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->pos; +} + +/*! + \fn void QGraphicsSceneHoverEvent::setPos(const QPointF &point) + \internal + + Sets the position associated with the hover event to the given \a point in + item coordinates. +*/ +void QGraphicsSceneHoverEvent::setPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->pos = pos; +} + +/*! + Returns the position of the mouse cursor in scene coordinates at the + moment the the hover event was sent. + + \sa pos(), screenPos() +*/ +QPointF QGraphicsSceneHoverEvent::scenePos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->scenePos; +} + +/*! + \fn void QGraphicsSceneHoverEvent::setScenePos(const QPointF &point) + \internal + + Sets the position associated with the hover event to the given \a point in + scene coordinates. +*/ +void QGraphicsSceneHoverEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->scenePos = pos; +} + +/*! + Returns the position of the mouse cursor in screen coordinates at the + moment the the hover event was sent. + + \sa pos(), scenePos() +*/ +QPoint QGraphicsSceneHoverEvent::screenPos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->screenPos; +} + +/*! + \fn void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &point) + \internal + + Sets the position associated with the hover event to the given \a point in + screen coordinates. +*/ +void QGraphicsSceneHoverEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->screenPos = pos; +} + +/*! + \since 4.4 + + Returns the last recorded mouse cursor position in item coordinates. + + \sa lastScenePos(), lastScreenPos(), pos() +*/ +QPointF QGraphicsSceneHoverEvent::lastPos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->lastPos; +} + +/*! + \internal +*/ +void QGraphicsSceneHoverEvent::setLastPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->lastPos = pos; +} + +/*! + \since 4.4 + + Returns the last recorded, the scene coordinates of the previous mouse or + hover event received by the view, that created the event mouse cursor + position in scene coordinates. + + \sa lastPos(), lastScreenPos(), scenePos() +*/ +QPointF QGraphicsSceneHoverEvent::lastScenePos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->lastScenePos; +} + +/*! + \internal +*/ +void QGraphicsSceneHoverEvent::setLastScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->lastScenePos = pos; +} + +/*! + \since 4.4 + + Returns the last recorded mouse cursor position in screen coordinates. The + last recorded position is the position of the previous mouse or hover + event received by the view that created the event. + + \sa lastPos(), lastScenePos(), screenPos() +*/ +QPoint QGraphicsSceneHoverEvent::lastScreenPos() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->lastScreenPos; +} + +/*! + \internal +*/ +void QGraphicsSceneHoverEvent::setLastScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneHoverEvent); + d->lastScreenPos = pos; +} + +/*! + \since 4.4 + + Returns the keyboard modifiers at the moment the the hover event was sent. +*/ +Qt::KeyboardModifiers QGraphicsSceneHoverEvent::modifiers() const +{ + Q_D(const QGraphicsSceneHoverEvent); + return d->modifiers; +} + +/*! + \fn void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers) + \internal + + Sets the modifiers for the current hover event to \a modifiers. +*/ +void QGraphicsSceneHoverEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneHoverEvent); + d->modifiers = modifiers; +} + +class QGraphicsSceneHelpEventPrivate : public QGraphicsSceneEventPrivate +{ +public: + QPointF scenePos; + QPoint screenPos; +}; + +/*! + \internal + + Constructs a graphics scene help event of the specified \a type. +*/ +QGraphicsSceneHelpEvent::QGraphicsSceneHelpEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneHelpEventPrivate, type) +{ +} + +/*! + Destroys the event. +*/ +QGraphicsSceneHelpEvent::~QGraphicsSceneHelpEvent() +{ +} + +/*! + Returns the position of the mouse cursor in scene coordinates at the + moment the the help event was sent. + + \sa screenPos() +*/ +QPointF QGraphicsSceneHelpEvent::scenePos() const +{ + Q_D(const QGraphicsSceneHelpEvent); + return d->scenePos; +} + +/*! + \fn void QGraphicsSceneHelpEvent::setScenePos(const QPointF &point) + \internal + + Sets the position associated with the context menu to the given \a point + in scene coordinates. +*/ +void QGraphicsSceneHelpEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneHelpEvent); + d->scenePos = pos; +} + +/*! + Returns the position of the mouse cursor in screen coordinates at the + moment the the help event was sent. + + \sa scenePos() +*/ +QPoint QGraphicsSceneHelpEvent::screenPos() const +{ + Q_D(const QGraphicsSceneHelpEvent); + return d->screenPos; +} + +/*! + \fn void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &point) + \internal + + Sets the position associated with the context menu to the given \a point + in screen coordinates. +*/ +void QGraphicsSceneHelpEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneHelpEvent); + d->screenPos = pos; +} + +class QGraphicsSceneDragDropEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneDragDropEvent) +public: + inline QGraphicsSceneDragDropEventPrivate() + : source(0), mimeData(0) + { } + + QPointF pos; + QPointF scenePos; + QPoint screenPos; + Qt::MouseButtons buttons; + Qt::KeyboardModifiers modifiers; + Qt::DropActions possibleActions; + Qt::DropAction proposedAction; + Qt::DropAction dropAction; + QWidget *source; + const QMimeData *mimeData; +}; + +/*! + \internal + + Constructs a new QGraphicsSceneDragDropEvent of the + specified \a type. The type can be either + QEvent::GraphicsSceneDragEnter, QEvent::GraphicsSceneDragLeave, + QEvent::GraphicsSceneDragMove, or QEvent::GraphicsSceneDrop. +*/ +QGraphicsSceneDragDropEvent::QGraphicsSceneDragDropEvent(Type type) + : QGraphicsSceneEvent(*new QGraphicsSceneDragDropEventPrivate, type) +{ +} + +/*! + Destroys the object. +*/ +QGraphicsSceneDragDropEvent::~QGraphicsSceneDragDropEvent() +{ +} + +/*! + Returns the mouse position of the event relative to the + view that sent the event. + + \sa QGraphicsView, screenPos(), scenePos() +*/ +QPointF QGraphicsSceneDragDropEvent::pos() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->pos; +} + +/*! + \internal + Sets the position of the mouse to \a pos; this should be + relative to the widget that generated the event, which normally + is a QGraphicsView. + + \sa pos(), setScenePos(), setScreenPos() +*/ + +void QGraphicsSceneDragDropEvent::setPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->pos = pos; +} + +/*! + Returns the position of the mouse in scene coordinates. + + \sa pos(), screenPos() +*/ +QPointF QGraphicsSceneDragDropEvent::scenePos() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->scenePos; +} + +/*! + \internal + Sets the scene position of the mouse to \a pos. + + \sa scenePos(), setScreenPos(), setPos() +*/ +void QGraphicsSceneDragDropEvent::setScenePos(const QPointF &pos) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->scenePos = pos; +} + +/*! + Returns the position of the mouse relative to the screen. + + \sa pos(), scenePos() +*/ +QPoint QGraphicsSceneDragDropEvent::screenPos() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->screenPos; +} + +/*! + \internal + Sets the mouse position relative to the screen to \a pos. + + \sa screenPos(), setScenePos(), setPos() +*/ +void QGraphicsSceneDragDropEvent::setScreenPos(const QPoint &pos) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->screenPos = pos; +} + +/*! + Returns a Qt::MouseButtons value indicating which buttons + were pressed on the mouse when this mouse event was + generated. + + \sa Qt::MouseButtons +*/ +Qt::MouseButtons QGraphicsSceneDragDropEvent::buttons() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->buttons; +} + +/*! + \internal + Sets the mouse buttons that were pressed when the event was + created to \a buttons. + + \sa Qt::MouseButtons, buttons() +*/ +void QGraphicsSceneDragDropEvent::setButtons(Qt::MouseButtons buttons) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->buttons = buttons; +} + +/*! + Returns the keyboard modifiers that were pressed when the drag + and drop event was created. + + \sa Qt::KeyboardModifiers +*/ +Qt::KeyboardModifiers QGraphicsSceneDragDropEvent::modifiers() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->modifiers; +} + +/*! + \internal + Sets the keyboard modifiers that were pressed when the event + was created to \a modifiers. + + \sa Qt::KeyboardModifiers, modifiers() +*/ + +void QGraphicsSceneDragDropEvent::setModifiers(Qt::KeyboardModifiers modifiers) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->modifiers = modifiers; +} + +/*! + Returns the possible drop actions that the drag and + drop can result in. + + \sa Qt::DropActions +*/ + +Qt::DropActions QGraphicsSceneDragDropEvent::possibleActions() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->possibleActions; +} + +/*! + \internal + Sets the possible drop actions that the drag can + result in to \a actions. + + \sa Qt::DropActions, possibleActions() +*/ +void QGraphicsSceneDragDropEvent::setPossibleActions(Qt::DropActions actions) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->possibleActions = actions; +} + +/*! + Returns the drop action that is proposed, i.e., preferred. + The action must be one of the possible actions as defined by + \c possibleActions(). + + \sa Qt::DropAction, possibleActions() +*/ + +Qt::DropAction QGraphicsSceneDragDropEvent::proposedAction() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->proposedAction; +} + +/*! + \internal + Sets the proposed action to \a action. The proposed action + is a Qt::DropAction that is one of the possible actions as + given by \c possibleActions(). + + \sa proposedAction(), Qt::DropAction, possibleActions() +*/ + +void QGraphicsSceneDragDropEvent::setProposedAction(Qt::DropAction action) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->proposedAction = action; +} + +/*! + Sets the proposed action as accepted, i.e, the drop action + is set to the proposed action. This is equal to: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicssceneevent.cpp 0 + + When using this function, one should not call \c accept(). + + \sa dropAction(), setDropAction(), proposedAction() +*/ + +void QGraphicsSceneDragDropEvent::acceptProposedAction() +{ + Q_D(QGraphicsSceneDragDropEvent); + d->dropAction = d->proposedAction; +} + +/*! + Returns the action that was performed in this drag and drop. + This should be set by the receiver of the drop and is + returned by QDrag::start(). + + \sa setDropAction(), acceptProposedAction() +*/ + +Qt::DropAction QGraphicsSceneDragDropEvent::dropAction() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->dropAction; +} + +/*! + This function lets the receiver of the drop set the drop + action that was performed to \a action, which should be one + of the + \l{QGraphicsSceneDragDropEvent::possibleActions()}{possible + actions}. Call \c accept() in stead of \c + acceptProposedAction() if you use this function. + + \sa dropAction(), accept(), possibleActions() +*/ +void QGraphicsSceneDragDropEvent::setDropAction(Qt::DropAction action) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->dropAction = action; +} + +/*! + This function returns the QGraphicsView that created the + QGraphicsSceneDragDropEvent. +*/ +QWidget *QGraphicsSceneDragDropEvent::source() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->source; +} + +/*! + \internal + This function set the source widget, i.e., the widget that + created the drop event, to \a source. +*/ +void QGraphicsSceneDragDropEvent::setSource(QWidget *source) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->source = source; +} + +/*! + This function returns the MIME data of the event. +*/ +const QMimeData *QGraphicsSceneDragDropEvent::mimeData() const +{ + Q_D(const QGraphicsSceneDragDropEvent); + return d->mimeData; +} + +/*! + \internal + This function sets the MIME data for the event. +*/ +void QGraphicsSceneDragDropEvent::setMimeData(const QMimeData *data) +{ + Q_D(QGraphicsSceneDragDropEvent); + d->mimeData = data; +} + +class QGraphicsSceneResizeEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneResizeEvent) +public: + inline QGraphicsSceneResizeEventPrivate() + { } + + QSizeF oldSize; + QSizeF newSize; +}; + +/*! + Constructs a QGraphicsSceneResizeEvent. +*/ +QGraphicsSceneResizeEvent::QGraphicsSceneResizeEvent() + : QGraphicsSceneEvent(*new QGraphicsSceneResizeEventPrivate, QEvent::GraphicsSceneResize) +{ +} + +/*! + Destroys the QGraphicsSceneResizeEvent. +*/ +QGraphicsSceneResizeEvent::~QGraphicsSceneResizeEvent() +{ +} + +/*! + Returns the old size (i.e., the size immediately before the widget was + resized). + + \sa newSize(), QGraphicsWidget::resize() +*/ +QSizeF QGraphicsSceneResizeEvent::oldSize() const +{ + Q_D(const QGraphicsSceneResizeEvent); + return d->oldSize; +} + +/*! + \internal +*/ +void QGraphicsSceneResizeEvent::setOldSize(const QSizeF &size) +{ + Q_D(QGraphicsSceneResizeEvent); + d->oldSize = size; +} + +/*! + Returns the new size (i.e., the current size). + + \sa oldSize(), QGraphicsWidget::resize() +*/ +QSizeF QGraphicsSceneResizeEvent::newSize() const +{ + Q_D(const QGraphicsSceneResizeEvent); + return d->newSize; +} + +/*! + \internal +*/ +void QGraphicsSceneResizeEvent::setNewSize(const QSizeF &size) +{ + Q_D(QGraphicsSceneResizeEvent); + d->newSize = size; +} + +class QGraphicsSceneMoveEventPrivate : public QGraphicsSceneEventPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsSceneMoveEvent) +public: + inline QGraphicsSceneMoveEventPrivate() + { } + + QPointF oldPos; + QPointF newPos; +}; + +/*! + Constructs a QGraphicsSceneMoveEvent. +*/ +QGraphicsSceneMoveEvent::QGraphicsSceneMoveEvent() + : QGraphicsSceneEvent(*new QGraphicsSceneMoveEventPrivate, QEvent::GraphicsSceneMove) +{ +} + +/*! + Destroys the QGraphicsSceneMoveEvent. +*/ +QGraphicsSceneMoveEvent::~QGraphicsSceneMoveEvent() +{ +} + +/*! + Returns the old position (i.e., the position immediatly before the widget + was moved). + + \sa newPos(), QGraphicsItem::setPos() +*/ +QPointF QGraphicsSceneMoveEvent::oldPos() const +{ + Q_D(const QGraphicsSceneMoveEvent); + return d->oldPos; +} + +/*! + \internal +*/ +void QGraphicsSceneMoveEvent::setOldPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMoveEvent); + d->oldPos = pos; +} + +/*! + Returns the new position (i.e., the current position). + + \sa oldPos(), QGraphicsItem::setPos() +*/ +QPointF QGraphicsSceneMoveEvent::newPos() const +{ + Q_D(const QGraphicsSceneMoveEvent); + return d->newPos; +} + +/*! + \internal +*/ +void QGraphicsSceneMoveEvent::setNewPos(const QPointF &pos) +{ + Q_D(QGraphicsSceneMoveEvent); + d->newPos = pos; +} + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicssceneevent.h b/src/gui/graphicsview/qgraphicssceneevent.h new file mode 100644 index 0000000..be50e96 --- /dev/null +++ b/src/gui/graphicsview/qgraphicssceneevent.h @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSSCENEEVENT_H +#define QGRAPHICSSCENEEVENT_H + +#include <QtCore/qcoreevent.h> +#include <QtCore/qpoint.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QMimeData; +class QPointF; +class QSizeF; +class QWidget; + +class QGraphicsSceneEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneEvent : public QEvent +{ +public: + QGraphicsSceneEvent(Type type); + ~QGraphicsSceneEvent(); + + QWidget *widget() const; + void setWidget(QWidget *widget); + +protected: + QGraphicsSceneEvent(QGraphicsSceneEventPrivate &dd, Type type = None); + QGraphicsSceneEventPrivate *d_ptr; + Q_DECLARE_PRIVATE(QGraphicsSceneEvent) +}; + +class QGraphicsSceneMouseEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneMouseEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneMouseEvent(Type type = None); + ~QGraphicsSceneMouseEvent(); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + + QPointF buttonDownPos(Qt::MouseButton button) const; + void setButtonDownPos(Qt::MouseButton button, const QPointF &pos); + + QPointF buttonDownScenePos(Qt::MouseButton button) const; + void setButtonDownScenePos(Qt::MouseButton button, const QPointF &pos); + + QPoint buttonDownScreenPos(Qt::MouseButton button) const; + void setButtonDownScreenPos(Qt::MouseButton button, const QPoint &pos); + + QPointF lastPos() const; + void setLastPos(const QPointF &pos); + + QPointF lastScenePos() const; + void setLastScenePos(const QPointF &pos); + + QPoint lastScreenPos() const; + void setLastScreenPos(const QPoint &pos); + + Qt::MouseButtons buttons() const; + void setButtons(Qt::MouseButtons buttons); + + Qt::MouseButton button() const; + void setButton(Qt::MouseButton button); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneMouseEvent) +}; + +class QGraphicsSceneWheelEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneWheelEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneWheelEvent(Type type = None); + ~QGraphicsSceneWheelEvent(); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + + Qt::MouseButtons buttons() const; + void setButtons(Qt::MouseButtons buttons); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + + int delta() const; + void setDelta(int delta); + + Qt::Orientation orientation() const; + void setOrientation(Qt::Orientation orientation); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneWheelEvent) +}; + +class QGraphicsSceneContextMenuEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneContextMenuEvent : public QGraphicsSceneEvent +{ +public: + enum Reason { Mouse, Keyboard, Other }; + + QGraphicsSceneContextMenuEvent(Type type = None); + ~QGraphicsSceneContextMenuEvent(); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + + Reason reason() const; + void setReason(Reason reason); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneContextMenuEvent) +}; + +class QGraphicsSceneHoverEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneHoverEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneHoverEvent(Type type = None); + ~QGraphicsSceneHoverEvent(); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + + QPointF lastPos() const; + void setLastPos(const QPointF &pos); + + QPointF lastScenePos() const; + void setLastScenePos(const QPointF &pos); + + QPoint lastScreenPos() const; + void setLastScreenPos(const QPoint &pos); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneHoverEvent) +}; + +class QGraphicsSceneHelpEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneHelpEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneHelpEvent(Type type = None); + ~QGraphicsSceneHelpEvent(); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneHelpEvent) +}; + +class QGraphicsSceneDragDropEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneDragDropEvent : public QGraphicsSceneEvent +{ +public: + QGraphicsSceneDragDropEvent(Type type = None); + ~QGraphicsSceneDragDropEvent(); + + QPointF pos() const; + void setPos(const QPointF &pos); + + QPointF scenePos() const; + void setScenePos(const QPointF &pos); + + QPoint screenPos() const; + void setScreenPos(const QPoint &pos); + + Qt::MouseButtons buttons() const; + void setButtons(Qt::MouseButtons buttons); + + Qt::KeyboardModifiers modifiers() const; + void setModifiers(Qt::KeyboardModifiers modifiers); + + Qt::DropActions possibleActions() const; + void setPossibleActions(Qt::DropActions actions); + + Qt::DropAction proposedAction() const; + void setProposedAction(Qt::DropAction action); + void acceptProposedAction(); + + Qt::DropAction dropAction() const; + void setDropAction(Qt::DropAction action); + + QWidget *source() const; + void setSource(QWidget *source); + + const QMimeData *mimeData() const; + void setMimeData(const QMimeData *data); + +private: + Q_DECLARE_PRIVATE(QGraphicsSceneDragDropEvent) +}; + +class QGraphicsSceneResizeEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneResizeEvent : public QGraphicsSceneEvent +{ + Q_DECLARE_PRIVATE(QGraphicsSceneResizeEvent) +public: + QGraphicsSceneResizeEvent(); + ~QGraphicsSceneResizeEvent(); + + QSizeF oldSize() const; + void setOldSize(const QSizeF &size); + + QSizeF newSize() const; + void setNewSize(const QSizeF &size); +}; + +class QGraphicsSceneMoveEventPrivate; +class Q_GUI_EXPORT QGraphicsSceneMoveEvent : public QGraphicsSceneEvent +{ + Q_DECLARE_PRIVATE(QGraphicsSceneMoveEvent) +public: + QGraphicsSceneMoveEvent(); + ~QGraphicsSceneMoveEvent(); + + QPointF oldPos() const; + void setOldPos(const QPointF &pos); + + QPointF newPos() const; + void setNewPos(const QPointF &pos); +}; + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/gui/graphicsview/qgraphicsview.cpp b/src/gui/graphicsview/qgraphicsview.cpp new file mode 100644 index 0000000..a661441 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsview.cpp @@ -0,0 +1,3871 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +//#define QGRAPHICSVIEW_DEBUG + +static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50; + +static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9 + +/*! + \class QGraphicsView + \brief The QGraphicsView class provides a widget for displaying the + contents of a QGraphicsScene. + \since 4.2 + \ingroup multimedia + \ingroup graphicsview-api + \mainclass + + QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable + viewport. To create a scene with geometrical items, see QGraphicsScene's + documentation. QGraphicsView is part of \l{The Graphics View Framework}. + + To visualize a scene, you start by constructing a QGraphicsView object, + passing the address of the scene you want to visualize to QGraphicsView's + constructor. Alternatively, you can call setScene() to set the scene at a + later point. After you call show(), the view will by default scroll to the + center of the scene and display any items that are visible at this + point. For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 0 + + You can explicitly scroll to any position on the scene by using the + scroll bars, or by calling centerOn(). By passing a point to centerOn(), + QGraphicsView will scroll its viewport to ensure that the point is + centered in the view. An overload is provided for scrolling to a + QGraphicsItem, in which case QGraphicsView will see to that the center of + the item is centered in the view. If all you want is to ensure that a + certain area is visible, (but not necessarily centered,) you can call + ensureVisible() instead. + + QGraphicsView can be used to visualize a whole scene, or only parts of it. + The visualized area is by default detected automatically when the view is + displayed for the first time (by calling + QGraphicsScene::itemsBoundingRect()). To set the visualized area rectangle + yourself, you can call setSceneRect(). This will adjust the scroll bars' + ranges appropriately. Note that although the scene supports a virtually + unlimited size, the range of the scroll bars will never exceed the range of + an integer (INT_MIN, INT_MAX). When the scene is larger than the scroll + bars' values, you can choose to use translate() to navigate the scene + instead. + + QGraphicsView visualizes the scene by calling render(). By default, the + items are drawn onto the viewport by using a regular QPainter, and using + default render hints. To change the default render hints that + QGraphicsView passes to QPainter when painting items, you can call + setRenderHints(). + + By default, QGraphicsView provides a regular QWidget for the viewport + widget. You can access this widget by calling viewport(), or you can + replace it by calling setViewport(). To render using OpenGL, simply call + setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport + widget. + + QGraphicsView supports affine transformations, using QMatrix. You can + either pass a matrix to setMatrix(), or you can call one of the + convenience functions rotate(), scale(), translate() or shear(). The most + two common transformations are scaling, which is used to implement + zooming, and rotation. QGraphicsView keeps the center of the view fixed + during a transformation. + + You can interact with the items on the scene by using the mouse and + keyboard. QGraphicsView translates the mouse and key events into \e scene + events, (events that inherit QGraphicsSceneEvent,), and forward them to + the visualized scene. In the end, it's the individual item that handles + the events and reacts to them. For example, if you click on a selectable + item, the item will typically let the scene know that it has been + selected, and it will also redraw itself to display a selection + rectangle. Similiary, if you click and drag the mouse to move a movable + item, it's the item that handles the mouse moves and moves itself. Item + interaction is enabled by default, and you can toggle it by calling + setInteractive(). + + You can also provide your own custom scene interaction, by creating a + subclass of QGraphicsView, and reimplementing the mouse and key event + handlers. To simplify how you programmatically interact with items in the + view, QGraphicsView provides the mapping functions mapToScene() and + mapFromScene(), and the item accessors items() and itemAt(). These + functions allow you to map points, rectangles, polygons and paths between + view coordinates and scene coordinates, and to find items on the scene + using view coordinates. + + \img graphicsview-view.png + + \sa QGraphicsScene, QGraphicsItem, QGraphicsSceneEvent +*/ + +/*! + \enum QGraphicsView::ViewportAnchor + + This enums describe the possible anchors that QGraphicsView can + use when the user resizes the view or when the view is + transformed. + + \value NoAnchor No anchor, i.e. the view leaves the scene's + position unchanged. + \value AnchorViewCenter The scene point at the center of the view + is used as the anchor. + \value AnchorUnderMouse The point under the mouse is used as the anchor. + + \sa resizeAnchor, transformationAnchor +*/ + +/*! + \enum QGraphicsView::ViewportUpdateMode + + \since 4.3 + + This enum describes how QGraphicsView updates its viewport when the scene + contents change or are exposed. + + \value FullViewportUpdate When any visible part of the scene changes or is + reexposed, QGraphicsView will update the entire viewport. This approach is + fastest when QGraphicsView spends more time figuring out what to draw than + it would spend drawing (e.g., when very many small items are repeatedly + updated). This is the preferred update mode for viewports that do not + support partial updates, such as QGLWidget, and for viewports that need to + disable scroll optimization. + + \value MinimalViewportUpdate QGraphicsView will determine the minimal + viewport region that requires a redraw, minimizing the time spent drawing + by avoiding a redraw of areas that have not changed. This is + QGraphicsView's default mode. Although this approach provides the best + performance in general, if there are many small visible changes on the + scene, QGraphicsView might end up spending more time finding the minimal + approach than it will spend drawing. + + \value SmartViewportUpdate QGraphicsView will attempt to find an optimal + update mode by analyzing the areas that require a redraw. + + \value BoundingRectViewportUpdate The bounding rectangle of all changes in + the viewport will be redrawn. This mode has the advantage that + QGraphicsView searches only one region for changes, minimizing time spent + determining what needs redrawing. The disadvantage is that areas that have + not changed also need to be redrawn. + + \value NoViewportUpdate QGraphicsView will never update its viewport when + the scene changes; the user is expected to control all updates. This mode + disables all (potentially slow) item visibility testing in QGraphicsView, + and is suitable for scenes that either require a fixed frame rate, or where + the viewport is otherwise updated externally. + + \sa viewportUpdateMode +*/ + +/*! + \enum QGraphicsView::OptimizationFlag + + \since 4.3 + + This enum describes flags that you can enable to improve rendering + performance in QGraphicsView. By default, none of these flags are set. + Note that setting a flag usually imposes a side effect, and this effect + can vary between paint devices and platforms. + + \value DontClipPainter QGraphicsView sometimes clips the painter when + rendering the scene contents. This can generally improve performance + (e.g., rendering only small parts of a large pixmap), and protects against + rendering mistakes (e.g., drawing outside bounding rectangles, or outside + the exposed area). In some situations, however, the painter clip can slow + down rendering; especially when all painting is restricted to inside + exposed areas. By enabling this flag, QGraphicsView will completely + disable its implicit clipping. Note that rendering artifacts from using a + semi-transparent foreground or background brush can occur if clipping is + disabled. + + \value DontSavePainterState When rendering, QGraphicsView protects the + painter state (see QPainter::save()) when rendering the background or + foreground, and when rendering each item. This allows you to leave the + painter in an altered state (i.e., you can call QPainter::setPen() or + QPainter::setBrush() without restoring the state after painting). However, + if the items consistently do restore the state, you should enable this + flag to prevent QGraphicsView from doing the same. + + \value DontAdjustForAntialiasing Disables QGraphicsView's antialiasing + auto-adjustment of exposed areas. Items that render antialiased lines on + the boundaries of their QGraphicsItem::boundingRect() can end up rendering + parts of the line outside. To prevent rendering artifacts, QGraphicsView + expands all exposed regions by 2 pixels in all directions. If you enable + this flag, QGraphicsView will no longer perform these adjustments, + minimizing the areas that require redrawing, which improves performance. A + common side effect is that items that do draw with antialiasing can leave + painting traces behind on the scene as they are moved. +*/ + +/*! + \enum QGraphicsView::CacheModeFlag + + This enum describes the flags that you can set for a QGraphicsView's cache + mode. + + \value CacheNone All painting is done directly onto the viewport. + + \value CacheBackground The background is cached. This affects both custom + backgrounds, and backgrounds based on the backgroundBrush property. When + this flag is enabled, QGraphicsView will allocate one pixmap with the full + size of the viewport. + + \sa cacheMode +*/ + +/*! + \enum QGraphicsView::DragMode + + This enum describes the default action for the view when pressing and + dragging the mouse over the viewport. + + \value NoDrag Nothing happens; the mouse event is ignored. + + \value ScrollHandDrag The cursor changes into a pointing hand, and + dragging the mouse around will scroll the scrolbars. This mode works both + in \l{QGraphicsView::interactive}{interactive} and non-interactive mode. + + \value RubberBandDrag A rubber band will appear. Dragging the mouse will + set the rubber band geometry, and all items covered by the rubber band are + selected. This mode is disabled for non-interactive views. + + \sa dragMode, QGraphicsScene::setSelectionArea() +*/ + +#include "qgraphicsview.h" +#include "qgraphicsview_p.h" + +#ifndef QT_NO_GRAPHICSVIEW + +#include "qgraphicsitem.h" +#include "qgraphicsitem_p.h" +#include "qgraphicsscene.h" +#include "qgraphicsscene_p.h" +#include "qgraphicssceneevent.h" +#include "qgraphicswidget.h" + +#include <QtCore/qdatetime.h> +#include <QtCore/qdebug.h> +#include <QtCore/qmath.h> +#include <QtGui/qapplication.h> +#include <QtGui/qdesktopwidget.h> +#include <QtGui/qevent.h> +#include <QtGui/qlayout.h> +#include <QtGui/qtransform.h> +#include <QtGui/qmatrix.h> +#include <QtGui/qpainter.h> +#include <QtGui/qscrollbar.h> +#include <QtGui/qstyleoption.h> +#ifdef Q_WS_X11 +#include <private/qt_x11_p.h> +#endif + +QT_BEGIN_NAMESPACE + +inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision +{ + if (d <= (qreal) INT_MIN) + return INT_MIN; + else if (d >= (qreal) INT_MAX) + return INT_MAX; + return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1); +} + +/*! + \internal +*/ +QGraphicsViewPrivate::QGraphicsViewPrivate() + : renderHints(QPainter::TextAntialiasing), + dragMode(QGraphicsView::NoDrag), + sceneInteractionAllowed(true), hasSceneRect(false), + connectedToScene(false), + mousePressButton(Qt::NoButton), + identityMatrix(true), + dirtyScroll(true), + accelerateScrolling(true), + leftIndent(0), topIndent(0), + lastMouseEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0), + useLastMouseEvent(false), + keepLastCenterPoint(true), + alignment(Qt::AlignCenter), + transforming(false), + transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor), + viewportUpdateMode(QGraphicsView::MinimalViewportUpdate), + optimizationFlags(0), + scene(0), +#ifndef QT_NO_RUBBERBAND + rubberBanding(false), + rubberBandSelectionMode(Qt::IntersectsItemShape), +#endif + handScrolling(false), handScrollMotions(0), cacheMode(0), + mustAllocateStyleOptions(false), + mustResizeBackgroundPixmap(true), +#ifndef QT_NO_CURSOR + hasStoredOriginalCursor(false), +#endif + lastDragDropEvent(0), + fullUpdatePending(true), + dirtyRectCount(0), + updatingLater(false), + updateSceneSlotReimplementedChecked(false) +{ + styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS); +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::recalculateContentSize() +{ + Q_Q(QGraphicsView); + + QSize maxSize = q->maximumViewportSize(); + int width = maxSize.width(); + int height = maxSize.height(); + QRectF viewRect = matrix.mapRect(q->sceneRect()); + + bool frameOnlyAround = (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q)); + if (frameOnlyAround) { + if (hbarpolicy == Qt::ScrollBarAlwaysOn) + height -= frameWidth * 2; + if (vbarpolicy == Qt::ScrollBarAlwaysOn) + width -= frameWidth * 2; + } + + // Adjust the maximum width and height of the viewport based on the width + // of visible scroll bars. + int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q); + if (frameOnlyAround) + scrollBarExtent += frameWidth * 2; + + bool useHorizontalScrollBar = (viewRect.width() > width) && hbarpolicy != Qt::ScrollBarAlwaysOff; + bool useVerticalScrollBar = (viewRect.height() > height) && vbarpolicy != Qt::ScrollBarAlwaysOff; + if (useHorizontalScrollBar && !useVerticalScrollBar) { + if (viewRect.height() > height - scrollBarExtent) + useVerticalScrollBar = true; + } + if (useVerticalScrollBar && !useHorizontalScrollBar) { + if (viewRect.width() > width - scrollBarExtent) + useHorizontalScrollBar = true; + } + if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn) + height -= scrollBarExtent; + if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn) + width -= scrollBarExtent; + + // Setting the ranges of these scroll bars can/will cause the values to + // change, and scrollContentsBy() will be called correspondingly. This + // will reset the last center point. + QPointF savedLastCenterPoint = lastCenterPoint; + + // Remember the former indent settings + qreal oldLeftIndent = leftIndent; + qreal oldTopIndent = topIndent; + + // If the whole scene fits horizontally, we center the scene horizontally, + // and ignore the horizontal scroll bars. + int left = q_round_bound(viewRect.left()); + int right = q_round_bound(viewRect.right() - width); + if (left >= right) { + q->horizontalScrollBar()->setRange(0, 0); + + switch (alignment & Qt::AlignHorizontal_Mask) { + case Qt::AlignLeft: + leftIndent = -viewRect.left(); + break; + case Qt::AlignRight: + leftIndent = width - viewRect.width() - viewRect.left() - 1; + break; + case Qt::AlignHCenter: + default: + leftIndent = width / 2 - (viewRect.left() + viewRect.right()) / 2; + break; + } + } else { + q->horizontalScrollBar()->setRange(left, right); + q->horizontalScrollBar()->setPageStep(width); + q->horizontalScrollBar()->setSingleStep(width / 20); + leftIndent = 0; + } + + // If the whole scene fits vertically, we center the scene vertically, and + // ignore the vertical scroll bars. + int top = q_round_bound(viewRect.top()); + int bottom = q_round_bound(viewRect.bottom() - height); + if (top >= bottom) { + q->verticalScrollBar()->setRange(0, 0); + + switch (alignment & Qt::AlignVertical_Mask) { + case Qt::AlignTop: + topIndent = -viewRect.top(); + break; + case Qt::AlignBottom: + topIndent = height - viewRect.height() - viewRect.top() - 1; + break; + case Qt::AlignVCenter: + default: + topIndent = height / 2 - (viewRect.top() + viewRect.bottom()) / 2; + break; + } + } else { + q->verticalScrollBar()->setRange(top, bottom); + q->verticalScrollBar()->setPageStep(height); + q->verticalScrollBar()->setSingleStep(height / 20); + topIndent = 0; + } + + // Restorethe center point from before the ranges changed. + lastCenterPoint = savedLastCenterPoint; + + // Issue a full update if the indents change. + // ### If the transform is still the same, we can get away with just a + // scroll instead. + if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) { + dirtyScroll = true; + q->viewport()->update(); + } else if (q->isRightToLeft() && !leftIndent) { + // In reverse mode, the horizontal scroll always changes after the content + // size has changed, as the scroll is calculated by summing the min and + // max values of the range and subtracting the current value. In normal + // mode the scroll remains unchanged unless the indent has changed. + dirtyScroll = true; + } + + if (cacheMode & QGraphicsView::CacheBackground) { + // Invalidate the background pixmap + mustResizeBackgroundPixmap = true; + } +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor) +{ + Q_Q(QGraphicsView); + switch (anchor) { + case QGraphicsView::AnchorUnderMouse: { + if (q->underMouse()) { + // Last scene pos: lastMouseMoveScenePoint + // Current mouse pos: + QPointF transformationDiff = q->mapToScene(q->viewport()->rect().center()) + - q->mapToScene(q->mapFromGlobal(QCursor::pos())); + q->centerOn(lastMouseMoveScenePoint + transformationDiff);; + } else { + q->centerOn(lastCenterPoint); + } + break; + } + case QGraphicsView::AnchorViewCenter: + q->centerOn(lastCenterPoint); + break; + case QGraphicsView::NoAnchor: + break; + } +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::updateLastCenterPoint() +{ + Q_Q(QGraphicsView); + lastCenterPoint = q->mapToScene(q->viewport()->rect().center()); +} + +/*! + \internal + + Returns the horizontal scroll value (the X value of the left edge of the + viewport). +*/ +qint64 QGraphicsViewPrivate::horizontalScroll() const +{ + if (dirtyScroll) + const_cast<QGraphicsViewPrivate *>(this)->updateScroll(); + return scrollX; +} + +/*! + \internal + + Returns the vertical scroll value (the X value of the top edge of the + viewport). +*/ +qint64 QGraphicsViewPrivate::verticalScroll() const +{ + if (dirtyScroll) + const_cast<QGraphicsViewPrivate *>(this)->updateScroll(); + return scrollY; +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::updateScroll() +{ + Q_Q(QGraphicsView); + scrollX = qint64(-leftIndent); + if (q->isRightToLeft()) { + if (!leftIndent) { + scrollX += q->horizontalScrollBar()->minimum(); + scrollX += q->horizontalScrollBar()->maximum(); + scrollX -= q->horizontalScrollBar()->value(); + } + } else { + scrollX += q->horizontalScrollBar()->value(); + } + + scrollY = qint64(q->verticalScrollBar()->value() - topIndent); + + dirtyScroll = false; +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::replayLastMouseEvent() +{ + if (!useLastMouseEvent || !scene) + return; + mouseMoveEventHandler(&lastMouseEvent); +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event) +{ + useLastMouseEvent = true; + lastMouseEvent = QMouseEvent(QEvent::MouseMove, event->pos(), event->globalPos(), + event->button(), event->buttons(), event->modifiers()); +} + +void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event) +{ + Q_Q(QGraphicsView); + + storeMouseEvent(event); + lastMouseEvent.setAccepted(false); + + if (!sceneInteractionAllowed) + return; + if (handScrolling) + return; + if (!scene) + return; + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove); + mouseEvent.setWidget(q->viewport()); + mouseEvent.setButtonDownScenePos(mousePressButton, mousePressScenePoint); + mouseEvent.setButtonDownScreenPos(mousePressButton, mousePressScreenPoint); + mouseEvent.setScenePos(q->mapToScene(event->pos())); + mouseEvent.setScreenPos(event->globalPos()); + mouseEvent.setLastScenePos(lastMouseMoveScenePoint); + mouseEvent.setLastScreenPos(lastMouseMoveScreenPoint); + mouseEvent.setButtons(event->buttons()); + mouseEvent.setButton(event->button()); + mouseEvent.setModifiers(event->modifiers()); + lastMouseMoveScenePoint = mouseEvent.scenePos(); + lastMouseMoveScreenPoint = mouseEvent.screenPos(); + mouseEvent.setAccepted(false); + QApplication::sendEvent(scene, &mouseEvent); + + // Remember whether the last event was accepted or not. + lastMouseEvent.setAccepted(mouseEvent.isAccepted()); + + if (mouseEvent.isAccepted() && mouseEvent.buttons() != 0) { + // The event was delivered to a mouse grabber; the press is likely to + // have set a cursor, and we must not change it. + return; + } + +#ifndef QT_NO_CURSOR + // Find the topmost item under the mouse with a cursor. + foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) { + if (item->hasCursor()) { + _q_setViewportCursor(item->cursor()); + return; + } + } + + // No items with cursors found; revert to the view cursor. + if (hasStoredOriginalCursor) { + // Restore the original viewport cursor. + hasStoredOriginalCursor = false; + q->viewport()->setCursor(originalCursor); + } +#endif +} + +/*! + \internal +*/ +#ifndef QT_NO_RUBBERBAND +QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRect &rect) const +{ + QStyleHintReturnMask mask; + QStyleOptionRubberBand option; + option.initFrom(widget); + option.rect = rect; + option.opaque = false; + option.shape = QRubberBand::Rectangle; + + QRegion tmp; + tmp += rect; + if (widget->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, widget, &mask)) + tmp &= mask.region; + return tmp; +} +#endif + +/*! + \internal +*/ +#ifndef QT_NO_CURSOR +void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor) +{ + Q_Q(QGraphicsView); + QWidget *viewport = q->viewport(); + if (!hasStoredOriginalCursor) { + hasStoredOriginalCursor = true; + originalCursor = viewport->cursor(); + } + viewport->setCursor(cursor); +} +#endif + +/*! + \internal +*/ +#ifndef QT_NO_CURSOR +void QGraphicsViewPrivate::_q_unsetViewportCursor() +{ + Q_Q(QGraphicsView); + foreach (QGraphicsItem *item, q->items(lastMouseEvent.pos())) { + if (item->hasCursor()) { + _q_setViewportCursor(item->cursor()); + return; + } + } + + // Restore the original viewport cursor. + hasStoredOriginalCursor = false; + if (dragMode == QGraphicsView::ScrollHandDrag) + q->viewport()->setCursor(Qt::OpenHandCursor); + else + q->viewport()->setCursor(originalCursor); +} +#endif + +/*! + \internal +*/ +void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent *event) +{ + delete lastDragDropEvent; + lastDragDropEvent = new QGraphicsSceneDragDropEvent(event->type()); + lastDragDropEvent->setScenePos(event->scenePos()); + lastDragDropEvent->setScreenPos(event->screenPos()); + lastDragDropEvent->setButtons(event->buttons()); + lastDragDropEvent->setModifiers(event->modifiers()); + lastDragDropEvent->setPossibleActions(event->possibleActions()); + lastDragDropEvent->setProposedAction(event->proposedAction()); + lastDragDropEvent->setDropAction(event->dropAction()); + lastDragDropEvent->setMimeData(event->mimeData()); + lastDragDropEvent->setWidget(event->widget()); + lastDragDropEvent->setSource(event->source()); +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest, + QDropEvent *source) +{ +#ifndef QT_NO_DRAGANDDROP + Q_Q(QGraphicsView); + dest->setScenePos(q->mapToScene(source->pos())); + dest->setScreenPos(q->mapToGlobal(source->pos())); + dest->setButtons(source->mouseButtons()); + dest->setModifiers(source->keyboardModifiers()); + dest->setPossibleActions(source->possibleActions()); + dest->setProposedAction(source->proposedAction()); + dest->setDropAction(source->dropAction()); + dest->setMimeData(source->mimeData()); + dest->setWidget(q->viewport()); + dest->setSource(source->source()); +#else + Q_UNUSED(dest) + Q_UNUSED(source) +#endif +} + +/*! + \internal +*/ +QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const +{ + Q_Q(const QGraphicsView); + if (dirtyScroll) + const_cast<QGraphicsViewPrivate *>(this)->updateScroll(); + + if (item->d_ptr->itemIsUntransformable()) { + QTransform itv = item->deviceTransform(q->viewportTransform()); + return itv.mapRect(rect).toAlignedRect(); + } + + // Translate-only + QPointF offset; + const QGraphicsItem *parentItem = item; + const QGraphicsItemPrivate *itemd; + do { + itemd = parentItem->d_ptr; + if (itemd->hasTransform) + break; + offset += itemd->pos; + } while ((parentItem = itemd->parent)); + + QRectF baseRect = rect.translated(offset.x(), offset.y()); + if (!parentItem) { + if (identityMatrix) { + baseRect.translate(-scrollX, -scrollY); + return baseRect.toAlignedRect(); + } + return matrix.mapRect(baseRect).translated(-scrollX, -scrollY).toAlignedRect(); + } + + QTransform tr = parentItem->sceneTransform(); + if (!identityMatrix) + tr *= matrix; + QRectF r = tr.mapRect(baseRect); + r.translate(-scrollX, -scrollY); + return r.toAlignedRect(); +} + +/*! + \internal +*/ +QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const +{ + Q_Q(const QGraphicsView); + if (dirtyScroll) + const_cast<QGraphicsViewPrivate *>(this)->updateScroll(); + + // Accurate bounding region + QTransform itv = item->sceneTransform() * q->viewportTransform(); + return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect(); +} + +/*! + \internal +*/ +void QGraphicsViewPrivate::itemUpdated(QGraphicsItem *item, const QRectF &rect) +{ + if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate) + return; + if (item->d_ptr->dirty) + updateLater(); + + QRectF updateRect = rect; + if (item->isClipped()) { + // Minimize unnecessary redraw. + QGraphicsItem *p = item; + while ((p = p->d_ptr->parent)) { + if (p->flags() & QGraphicsItem::ItemClipsChildrenToShape) { + updateRect &= p->itemTransform(item).mapRect(p->boundingRect()); + if (updateRect.isNull()) + return; + } + + if (!(p->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorClipsChildren)) + break; + } + + if (updateRect.isNull()) + return; + } + + // Map the rect to view coordinates. + QRect vr = viewport->rect(); + + if (!item->d_ptr->hasBoundingRegionGranularity) { + QRect r = mapToViewRect(item, updateRect) & vr; + if (r.isNull()) + return; + this->updateRect(r); + } else { + QRegion r = mapToViewRegion(item, updateRect) & vr; + if (r.isEmpty()) + return; + updateRegion(r); + } +} + +void QGraphicsViewPrivate::updateLater() +{ + Q_Q(QGraphicsView); + if (updatingLater) + return; + updatingLater = true; + QMetaObject::invokeMethod(q, "_q_updateLaterSlot", Qt::QueuedConnection); +} + +void QGraphicsViewPrivate::_q_updateLaterSlot() +{ + Q_Q(QGraphicsView); + if (!scene) + return; + + QRect vr = viewport->rect(); + QTransform viewTransform = q->viewportTransform(); + const QList<QGraphicsItem *> &dirtyItems = scene->d_func()->dirtyItems; + for (int i = 0; i < dirtyItems.size(); ++i) { + const QGraphicsItem *item = dirtyItems.at(i); + QTransform x = item->sceneTransform() * viewTransform; + QRect viewRect = x.mapRect(item->boundingRect()).toAlignedRect() & vr; + if (!viewRect.isNull()) + updateRect(viewRect); + } + + dirtyRectCount += dirtyRects.size(); + + bool noUpdate = !fullUpdatePending && viewportUpdateMode == QGraphicsView::FullViewportUpdate; + if ((dirtyRectCount > 0 || !dirtyBoundingRect.isNull()) && !fullUpdatePending && !noUpdate) { + if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate + || (viewportUpdateMode == QGraphicsView::SmartViewportUpdate + && dirtyRectCount >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)) { + if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { + viewport->update(dirtyBoundingRect.adjusted(-2, -2, 2, 2)); + } else { + viewport->update(dirtyBoundingRect); + } + } else { + // ### Improve this block, which is very slow for complex regions. We + // need to strike the balance between having an accurate update + // region, and running fast. The below approach is the simplest way to + // create a region from a bunch of rects, but we might want to use + // other approaches; e.g., a grid of a fixed size representing + // quadrants of the viewport, which we mark as dirty depending on the + // rectangles in the list. Perhaps this should go into a + // QRegion::fromRects(rects, how) function. + QRegion region; + if (!(optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)) { + for (int i = 0; i < dirtyRegions.size(); ++i) { + QVector<QRect> rects = dirtyRegions.at(i).rects(); + for (int j = 0; j < rects.size(); ++j) + region += rects.at(j).adjusted(-2, -2, 2, 2); + } + for (int i = 0; i < dirtyRects.size(); ++i) + region += dirtyRects.at(i).adjusted(-2, -2, 2, 2); + } else { + for (int i = 0; i < dirtyRegions.size(); ++i) + region += dirtyRegions.at(i); + for (int i = 0; i < dirtyRects.size(); ++i) + region += dirtyRects.at(i); + } + + viewport->update(region); + } + } + + dirtyRegions.clear(); + dirtyRects.clear(); + dirtyRectCount = 0; + dirtyBoundingRect = QRect(); + updatingLater = false; +} + +void QGraphicsViewPrivate::updateAll() +{ + Q_Q(QGraphicsView); + q->viewport()->update(); + fullUpdatePending = true; + dirtyRectCount = 0; + dirtyBoundingRect = QRect(); + updatingLater = false; +} + +void QGraphicsViewPrivate::updateRegion(const QRegion &r) +{ + Q_Q(QGraphicsView); + + // Rect intersects viewport - update everything? + switch (viewportUpdateMode) { + case QGraphicsView::FullViewportUpdate: + fullUpdatePending = true; + q->viewport()->update(); + break; + case QGraphicsView::BoundingRectViewportUpdate: + dirtyBoundingRect |= r.boundingRect(); + if (dirtyBoundingRect == q->viewport()->rect()) { + fullUpdatePending = true; + q->viewport()->update(); + } else { + updateLater(); + } + break; + case QGraphicsView::SmartViewportUpdate: + dirtyBoundingRect |= r.boundingRect(); + if ((dirtyRectCount + r.numRects()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) + dirtyRegions << r; + dirtyRectCount += r.numRects(); + updateLater(); + break; + case QGraphicsView::MinimalViewportUpdate: + dirtyRegions << r; + dirtyRectCount += r.numRects(); + updateLater(); + break; + case QGraphicsView::NoViewportUpdate: + // Unreachable + break; + } + + // Compress the regions... + if (dirtyRectCount > QGRAPHICSVIEW_REGION_RECT_THRESHOLD && dirtyRegions.size() > 1) { + QRegion masterRegion; + for (int i=0; i<dirtyRegions.size(); ++i) { + masterRegion |= dirtyRegions.at(i); + } + dirtyRectCount = masterRegion.numRects(); + dirtyRegions.clear(); + dirtyRegions << masterRegion; + } +} + +void QGraphicsViewPrivate::updateRect(const QRect &r) +{ + Q_Q(QGraphicsView); + + // Rect intersects viewport - update everything? + switch (viewportUpdateMode) { + case QGraphicsView::FullViewportUpdate: + fullUpdatePending = true; + q->viewport()->update(); + break; + case QGraphicsView::BoundingRectViewportUpdate: + dirtyBoundingRect |= r; + if (dirtyBoundingRect == q->viewport()->rect()) { + fullUpdatePending = true; + q->viewport()->update(); + } else { + updateLater(); + } + break; + case QGraphicsView::SmartViewportUpdate: + dirtyBoundingRect |= r; + if ((dirtyRectCount + dirtyRects.size()) < QGRAPHICSVIEW_REGION_RECT_THRESHOLD) + dirtyRects << r; + updateLater(); + break; + case QGraphicsView::MinimalViewportUpdate: + dirtyRects << r; + updateLater(); + break; + case QGraphicsView::NoViewportUpdate: + // Unreachable + break; + } +} + +QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems) +{ + if (mustAllocateStyleOptions || (numItems > styleOptions.capacity())) + // too many items, let's allocate on-the-fly + return new QStyleOptionGraphicsItem[numItems]; + + // expand only whenever necessary + if (numItems > styleOptions.size()) + styleOptions.resize(numItems); + + mustAllocateStyleOptions = true; + return styleOptions.data(); +} + +void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array) +{ + mustAllocateStyleOptions = false; + if (array != styleOptions.data()) + delete [] array; +} + +extern QPainterPath qt_regionToPath(const QRegion ®ion); + +QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, + const QTransform &worldTransform, + bool *allItems) const +{ + Q_Q(const QGraphicsView); + QList<QGraphicsItem *> itemList; + QSet<QGraphicsItem *> tmp; + bool simpleTransform = worldTransform.type() <= QTransform::TxScale; + + QPainterPath path = qt_regionToPath(exposedRegion); + *allItems = path.contains(q->mapFromScene(scene->d_func()->growingItemsBoundingRect).boundingRect()); + QList<QRectF> exposedRects; + QList<QPolygonF> exposedPolys; + + // Transform the exposed viewport rects to scene rects or polygons + foreach (const QRect &rect, exposedRegion.rects()) { + QPolygonF exposedPoly = q->mapToScene(rect.adjusted(-1, -1, 1, 1)); + QRectF exposedRect = exposedPoly.boundingRect(); + if (!simpleTransform) + exposedPolys << exposedPoly; + exposedRects << exposedRect; + } + + // Find which items need to be drawn. + if (*allItems) { + // All items are guaranteed within the exposed region, don't bother using the index. + foreach (QGraphicsItem *item, scene->items()) { + // But we only want to include items that are visible + if (item->isVisible()) + itemList << item; + } + } else if (simpleTransform) { + // Simple rect lookups will do. + if (exposedRects.size() > 1) { + foreach (const QRectF &rect, exposedRects) { + foreach (QGraphicsItem *item, scene->d_func()->items_helper(rect, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) { + if (!tmp.contains(item)) { + tmp << item; + itemList << item; + } + } + } + } else { + itemList += scene->d_func()->items_helper(exposedRects[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */); + } + } else { + // Polygon lookup is necessary. + if (exposedRects.size() > 1) { + foreach (const QPolygonF &poly, exposedPolys) { + foreach (QGraphicsItem *item, scene->d_func()->items_helper(poly, Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */)) { + if (!tmp.contains(item)) { + tmp << item; + itemList << item; + } + } + } + } else { + itemList += scene->d_func()->items_helper(exposedPolys[0], Qt::IntersectsItemBoundingRect, Qt::SortOrder(-1) /* don't sort */); + } + } + + // Check for items that ignore inherited transformations, and add them if + // necessary. + QRectF untr = scene->d_func()->largestUntransformableItem; + if (!*allItems && !untr.isNull()) { + // Map the largest untransformable item subtree boundingrect from view + // to scene coordinates, and use this to expand all exposed rects in + // search for untransformable items. + QRectF ltri = matrix.inverted().mapRect(untr); + ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height()); + + foreach (const QRect &rect, exposedRegion.rects()) { + QRectF exposed = q->mapToScene(rect.adjusted(-1, -1, 1, 1)).boundingRect(); + exposed.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height()); + + foreach (QGraphicsItem *item, scene->d_func()->estimateItemsInRect(exposed)) { + if (item->d_ptr->itemIsUntransformable()) { + if (!tmp.contains(item)) { + QPainterPath rectPath; + rectPath.addRect(rect); + QPainterPath path = item->deviceTransform(q->viewportTransform()).inverted().map(rectPath); + if (item->collidesWithPath(path, Qt::IntersectsItemBoundingRect)) { + itemList << item; + tmp << item; + } + } + } + } + } + } + tmp.clear(); + + // Sort the items. + QGraphicsScenePrivate::sortItems(&itemList, Qt::DescendingOrder, + scene->d_func()->sortCacheEnabled); + + return itemList; +} + +void QGraphicsViewPrivate::generateStyleOptions(const QList<QGraphicsItem *> &itemList, + QGraphicsItem **itemArray, + QStyleOptionGraphicsItem *styleOptionArray, + const QTransform &worldTransform, + bool allItems, + const QRegion &exposedRegion) const +{ + // Two unit vectors. + QLineF v1(0, 0, 1, 0); + QLineF v2(0, 0, 0, 1); + QTransform itemToViewportTransform; + QRectF brect; + QTransform reverseMap; + + for (int i = 0; i < itemList.size(); ++i) { + QGraphicsItem *item = itemArray[i] = itemList[i]; + + QStyleOptionGraphicsItem &option = styleOptionArray[i]; + brect = item->boundingRect(); + option.state = QStyle::State_None; + option.rect = brect.toRect(); + option.exposedRect = QRectF(); + if (item->d_ptr->selected) + option.state |= QStyle::State_Selected; + if (item->d_ptr->enabled) + option.state |= QStyle::State_Enabled; + if (item->hasFocus()) + option.state |= QStyle::State_HasFocus; + if (scene->d_func()->hoverItems.contains(item)) + option.state |= QStyle::State_MouseOver; + if (item == scene->mouseGrabberItem()) + option.state |= QStyle::State_Sunken; + + // Calculate a simple level-of-detail metric. + // ### almost identical code in QGraphicsScene::render() + // and QGraphicsView::render() - consider refactoring + if (item->d_ptr->itemIsUntransformable()) { + itemToViewportTransform = item->deviceTransform(worldTransform); + } else { + itemToViewportTransform = item->sceneTransform() * worldTransform; + } + + if (itemToViewportTransform.type() <= QTransform::TxTranslate) { + // Translation and rotation only? The LOD is 1. + option.levelOfDetail = 1; + } else { + // LOD is the transformed area of a 1x1 rectangle. + option.levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); + } + option.matrix = itemToViewportTransform.toAffine(); //### discards perspective + + if (!allItems) { + // Determine the item's exposed area + reverseMap = itemToViewportTransform.inverted(); + foreach (const QRect &rect, exposedRegion.rects()) { + option.exposedRect |= reverseMap.mapRect(QRectF(rect.adjusted(-1, -1, 1, 1))); + if (option.exposedRect.contains(brect)) + break; + } + option.exposedRect &= brect; + } else { + // The whole item is exposed + option.exposedRect = brect; + } + } +} + +/*! + Constructs a QGraphicsView. \a parent is passed to QWidget's constructor. +*/ +QGraphicsView::QGraphicsView(QWidget *parent) + : QAbstractScrollArea(*new QGraphicsViewPrivate, parent) +{ + setViewport(0); + setAcceptDrops(true); + setBackgroundRole(QPalette::Base); + + // ### Ideally this would be enabled/disabled depending on whether any + // widgets in the current scene enabled input methods. We could do that + // using a simple reference count. The same goes for acceptDrops and mouse + // tracking. + setAttribute(Qt::WA_InputMethodEnabled); +} + +/*! + Constructs a QGraphicsView and sets the visualized scene to \a + scene. \a parent is passed to QWidget's constructor. +*/ +QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent) + : QAbstractScrollArea(*new QGraphicsViewPrivate, parent) +{ + setScene(scene); + setViewport(0); + setAcceptDrops(true); + setBackgroundRole(QPalette::Base); + setAttribute(Qt::WA_InputMethodEnabled); +} + +/*! + \internal + */ +QGraphicsView::QGraphicsView(QGraphicsViewPrivate &dd, QWidget *parent) + : QAbstractScrollArea(dd, parent) +{ + setViewport(0); + setAcceptDrops(true); + setBackgroundRole(QPalette::Base); + setAttribute(Qt::WA_InputMethodEnabled); +} + +/*! + Destructs the QGraphicsView object. +*/ +QGraphicsView::~QGraphicsView() +{ + Q_D(QGraphicsView); + if (d->scene) + d->scene->d_func()->views.removeAll(this); + delete d->lastDragDropEvent; +} + +/*! + \reimp +*/ +QSize QGraphicsView::sizeHint() const +{ + Q_D(const QGraphicsView); + if (d->scene) { + QSizeF baseSize = d->matrix.mapRect(sceneRect()).size(); + baseSize += QSizeF(d->frameWidth * 2, d->frameWidth * 2); + return baseSize.boundedTo((3 * QApplication::desktop()->size()) / 4).toSize(); + } + return QAbstractScrollArea::sizeHint(); +} + +/*! + \property QGraphicsView::renderHints + \brief the default render hints for the view + + These hints are + used to initialize QPainter before each visible item is drawn. QPainter + uses render hints to toggle rendering features such as antialiasing and + smooth pixmap transformation. + + QPainter::TextAntialiasing is enabled by default. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 1 +*/ +QPainter::RenderHints QGraphicsView::renderHints() const +{ + Q_D(const QGraphicsView); + return d->renderHints; +} +void QGraphicsView::setRenderHints(QPainter::RenderHints hints) +{ + Q_D(QGraphicsView); + if (hints == d->renderHints) + return; + d->renderHints = hints; + viewport()->update(); +} + +/*! + If \a enabled is true, the render hint \a hint is enabled; otherwise it + is disabled. + + \sa renderHints +*/ +void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled) +{ + Q_D(QGraphicsView); + QPainter::RenderHints oldHints = d->renderHints; + if (enabled) + d->renderHints |= hint; + else + d->renderHints &= ~hint; + if (oldHints != d->renderHints) + viewport()->update(); +} + +/*! + \property QGraphicsView::alignment + \brief the alignment of the scene in the view when the whole + scene is visible. + + If the whole scene is visible in the view, (i.e., there are no visible + scroll bars,) the view's alignment will decide where the scene will be + rendered in the view. For example, if the alignment is Qt::AlignCenter, + which is default, the scene will be centered in the view, and if the + alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in + the top-left corner of the view. +*/ +Qt::Alignment QGraphicsView::alignment() const +{ + Q_D(const QGraphicsView); + return d->alignment; +} +void QGraphicsView::setAlignment(Qt::Alignment alignment) +{ + Q_D(QGraphicsView); + if (d->alignment != alignment) { + d->alignment = alignment; + d->recalculateContentSize(); + } +} + +/*! + \property QGraphicsView::transformationAnchor + \brief how the view should position the scene during transformations. + + QGraphicsView uses this property to decide how to position the scene in + the viewport when the transformation matrix changes, and the coordinate + system of the view is transformed. The default behavior, AnchorViewCenter, + ensures that the scene point at the center of the view remains unchanged + during transformations (e.g., when rotating, the scene will appear to + rotate around the center of the view). + + Note that the effect of this property is noticeable when only a part of the + scene is visible (i.e., when there are scroll bars). Otherwise, if the + whole scene fits in the view, QGraphicsScene uses the view \l alignment to + position the scene in the view. + + \sa alignment, resizeAnchor +*/ +QGraphicsView::ViewportAnchor QGraphicsView::transformationAnchor() const +{ + Q_D(const QGraphicsView); + return d->transformationAnchor; +} +void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor) +{ + Q_D(QGraphicsView); + d->transformationAnchor = anchor; +} + +/*! + \property QGraphicsView::resizeAnchor + \brief how the view should position the scene when the view is resized. + + QGraphicsView uses this property to decide how to position the scene in + the viewport when the viewport widget's size changes. The default + behavior, NoAnchor, leaves the scene's position unchanged during a resize; + the top-left corner of the view will appear to be anchored while resizing. + + Note that the effect of this property is noticeable when only a part of the + scene is visible (i.e., when there are scroll bars). Otherwise, if the + whole scene fits in the view, QGraphicsScene uses the view \l alignment to + position the scene in the view. + + \sa alignment, transformationAnchor, Qt::WNorthWestGravity +*/ +QGraphicsView::ViewportAnchor QGraphicsView::resizeAnchor() const +{ + Q_D(const QGraphicsView); + return d->resizeAnchor; +} +void QGraphicsView::setResizeAnchor(ViewportAnchor anchor) +{ + Q_D(QGraphicsView); + d->resizeAnchor = anchor; +} + +/*! + \property QGraphicsView::viewportUpdateMode + \brief how the viewport should update its contents. + + \since 4.3 + + QGraphicsView uses this property to decide how to update areas of the + scene that have been reexposed or changed. Usually you do not need to + modify this property, but there are some cases where doing so can improve + rendering performance. See the ViewportUpdateMode documentation for + specific details. + + The default value is MinimalViewportUpdate, where QGraphicsView will + update as small an area of the viewport as possible when the contents + change. + + \sa ViewportUpdateMode, cacheMode +*/ +QGraphicsView::ViewportUpdateMode QGraphicsView::viewportUpdateMode() const +{ + Q_D(const QGraphicsView); + return d->viewportUpdateMode; +} +void QGraphicsView::setViewportUpdateMode(ViewportUpdateMode mode) +{ + Q_D(QGraphicsView); + d->viewportUpdateMode = mode; +} + +/*! + \property QGraphicsView::optimizationFlags + \brief flags that can be used to tune QGraphicsView's performance. + + \since 4.3 + + QGraphicsView uses clipping, extra bounding rect adjustments, and certain + other aids to improve rendering quality and performance for the common + case graphics scene. However, depending on the target platform, the scene, + and the viewport in use, some of these operations can degrade performance. + + The effect varies from flag to flag; see the OptimizationFlags + documentation for details. + + By default, no optimization flags are enabled. + + \sa setOptimizationFlag() +*/ +QGraphicsView::OptimizationFlags QGraphicsView::optimizationFlags() const +{ + Q_D(const QGraphicsView); + return d->optimizationFlags; +} +void QGraphicsView::setOptimizationFlags(OptimizationFlags flags) +{ + Q_D(QGraphicsView); + d->optimizationFlags = flags; +} + +/*! + Enables \a flag if \a enabled is true; otherwise disables \a flag. + + \sa optimizationFlags +*/ +void QGraphicsView::setOptimizationFlag(OptimizationFlag flag, bool enabled) +{ + Q_D(QGraphicsView); + if (enabled) + d->optimizationFlags |= flag; + else + d->optimizationFlags &= ~flag; +} + +/*! + \property QGraphicsView::dragMode + \brief the behavior for dragging the mouse over the scene while + the left mouse button is pressed. + + This property defines what should happen when the user clicks on the scene + background and drags the mouse (e.g., scrolling the viewport contents + using a pointing hand cursor, or selecting multiple items with a rubber + band). The default value, NoDrag, does nothing. + + This behavior only affects mouse clicks that are not handled by any item. + You can define a custom behavior by creating a subclass of QGraphicsView + and reimplementing mouseMoveEvent(). +*/ +QGraphicsView::DragMode QGraphicsView::dragMode() const +{ + Q_D(const QGraphicsView); + return d->dragMode; +} +void QGraphicsView::setDragMode(DragMode mode) +{ + Q_D(QGraphicsView); + if (d->dragMode == mode) + return; + +#ifndef QT_NO_CURSOR + if (d->dragMode == ScrollHandDrag) + viewport()->unsetCursor(); +#endif + + // If dragMode is unset while dragging, e.g. via a keyEvent, we + // don't unset the handScrolling state. When enabling scrolling + // again the mouseMoveEvent will automatically start scrolling, + // without a mousePress + if (d->dragMode == ScrollHandDrag && mode == NoDrag && d->handScrolling) + d->handScrolling = false; + + d->dragMode = mode; + +#ifndef QT_NO_CURSOR + if (d->dragMode == ScrollHandDrag) { + // Forget the stored viewport cursor when we enter scroll hand drag mode. + d->hasStoredOriginalCursor = false; + viewport()->setCursor(Qt::OpenHandCursor); + } +#endif +} + +#ifndef QT_NO_RUBBERBAND +/*! + \property QGraphicsView::rubberBandSelectionMode + \brief the behavior for selecting items with a rubber band selection rectangle. + \since 4.3 + + This property defines how items are selected when using the RubberBandDrag + drag mode. + + The default value is Qt::IntersectsItemShape; all items whose shape + intersects with or is contained by the rubber band are selected. + + \sa dragMode, items() +*/ +Qt::ItemSelectionMode QGraphicsView::rubberBandSelectionMode() const +{ + Q_D(const QGraphicsView); + return d->rubberBandSelectionMode; +} +void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode) +{ + Q_D(QGraphicsView); + d->rubberBandSelectionMode = mode; +} +#endif + +/*! + \property QGraphicsView::cacheMode + \brief which parts of the view are cached + + QGraphicsView can cache pre-rendered content in a QPixmap, which is then + drawn onto the viewport. The purpose of such caching is to speed up the + total rendering time for areas that are slow to render. Texture, gradient + and alpha blended backgrounds, for example, can be notibly slow to render; + especially with a transformed view. The CacheBackground flag enables + caching of the view's background. For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 2 + + The cache is invalidated every time the view is transformed. However, when + scrolling, only partial invalidation is required. + + By default, nothing is cached. + + \sa resetCachedContent(), QPixmapCache +*/ +QGraphicsView::CacheMode QGraphicsView::cacheMode() const +{ + Q_D(const QGraphicsView); + return d->cacheMode; +} +void QGraphicsView::setCacheMode(CacheMode mode) +{ + Q_D(QGraphicsView); + if (mode == d->cacheMode) + return; + d->cacheMode = mode; + resetCachedContent(); +} + +/*! + Resets any cached content. Calling this function will clear + QGraphicsView's cache. If the current cache mode is \l CacheNone, this + function does nothing. + + This function is called automatically for you when the backgroundBrush or + QGraphicsScene::backgroundBrush properties change; you only need to call + this function if you have reimplemented QGraphicsScene::drawBackground() + or QGraphicsView::drawBackground() to draw a custom background, and need + to trigger a full redraw. + + \sa cacheMode() +*/ +void QGraphicsView::resetCachedContent() +{ + Q_D(QGraphicsView); + if (d->cacheMode == CacheNone) + return; + + if (d->cacheMode & CacheBackground) { + // Background caching is enabled. + d->mustResizeBackgroundPixmap = true; + viewport()->update(); + } else if (d->mustResizeBackgroundPixmap) { + // Background caching is disabled. + // Cleanup, free some resources. + d->mustResizeBackgroundPixmap = false; + d->backgroundPixmap = QPixmap(); + d->backgroundPixmapExposed = QRegion(); + } +} + +/*! + Invalidates and schedules a redraw of \a layers inside \a rect. \a rect is + in scene coordinates. Any cached content for \a layers inside \a rect is + unconditionally invalidated and redrawn. + + You can call this function to notify QGraphicsView of changes to the + background or the foreground of the scene. It is commonly used for scenes + with tile-based backgrounds to notify changes when QGraphicsView has + enabled background caching. + + Note that QGraphicsView currently supports background caching only (see + QGraphicsView::CacheBackground). This function is equivalent to calling update() if any + layer but QGraphicsScene::BackgroundLayer is passed. + + \sa QGraphicsScene::invalidate(), update() +*/ +void QGraphicsView::invalidateScene(const QRectF &rect, QGraphicsScene::SceneLayers layers) +{ + Q_D(QGraphicsView); + if ((layers & QGraphicsScene::BackgroundLayer) && !d->mustResizeBackgroundPixmap) { + QRect viewRect = mapFromScene(rect).boundingRect(); + if (viewport()->rect().intersects(viewRect)) { + // The updated background area is exposed; schedule this area for + // redrawing. + d->backgroundPixmapExposed += viewRect; + if (d->scene) + d->scene->update(rect); + } + } +} + +/*! + \property QGraphicsView::interactive + \brief whether the view allowed scene interaction. + + If enabled, this view is set to allow scene interaction. Otherwise, this + view will not allow interaction, and any mouse or key events are ignored + (i.e., it will act as a read-only view). + + By default, this property is true. +*/ +bool QGraphicsView::isInteractive() const +{ + Q_D(const QGraphicsView); + return d->sceneInteractionAllowed; +} +void QGraphicsView::setInteractive(bool allowed) +{ + Q_D(QGraphicsView); + d->sceneInteractionAllowed = allowed; +} + +/*! + Returns a pointer to the scene that is currently visualized in the + view. If no scene is currently visualized, 0 is returned. + + \sa setScene() +*/ +QGraphicsScene *QGraphicsView::scene() const +{ + Q_D(const QGraphicsView); + return d->scene; +} + +/*! + Sets the current scene to \a scene. If \a scene is already being + viewed, this function does nothing. + + When a scene is set on a view, the QGraphicsScene::changed() signal + is automatically connected to this view's updateScene() slot, and the + view's scroll bars are adjusted to fit the size of the scene. +*/ +void QGraphicsView::setScene(QGraphicsScene *scene) +{ + Q_D(QGraphicsView); + if (d->scene == scene) + return; + + // Always update the viewport when the scene changes. + viewport()->update(); + + // Remove the previously assigned scene. + if (d->scene) { + disconnect(d->scene, SIGNAL(changed(QList<QRectF>)), + this, SLOT(updateScene(QList<QRectF>))); + disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)), + this, SLOT(updateSceneRect(QRectF))); + d->scene->d_func()->views.removeAll(this); + } + + // Assign the new scene and update the contents (scrollbars, etc.)). + if ((d->scene = scene)) { + connect(d->scene, SIGNAL(sceneRectChanged(QRectF)), + this, SLOT(updateSceneRect(QRectF))); + d->updateSceneSlotReimplementedChecked = false; + d->scene->d_func()->views << this; + d->recalculateContentSize(); + d->lastCenterPoint = sceneRect().center(); + d->keepLastCenterPoint = true; + } else { + d->recalculateContentSize(); + } +} + +/*! + \property QGraphicsView::sceneRect + \brief the area of the scene visualized by this view. + + The scene rectangle defines the extent of the scene, and in the view's case, + this means the area of the scene that you can navigate using the scroll + bars. + + If unset, or if a null QRectF is set, this property has the same value as + QGraphicsScene::sceneRect, and it changes with + QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected + by the scene. + + Note that, although the scene supports a virtually unlimited size, the + range of the scroll bars will never exceed the range of an integer + (INT_MIN, INT_MAX). When the scene is larger than the scroll bars' values, + you can choose to use translate() to navigate the scene instead. + + By default, this property contains a rectangle at the origin with zero + width and height. + + \sa QGraphicsScene::sceneRect +*/ +QRectF QGraphicsView::sceneRect() const +{ + Q_D(const QGraphicsView); + if (d->hasSceneRect) + return d->sceneRect; + if (d->scene) + return d->scene->sceneRect(); + return QRectF(); +} +void QGraphicsView::setSceneRect(const QRectF &rect) +{ + Q_D(QGraphicsView); + d->hasSceneRect = !rect.isNull(); + d->sceneRect = rect; + d->recalculateContentSize(); +} + +/*! + Returns the current transformation matrix for the view. If no current + transformation is set, the identity matrix is returned. + + \sa setMatrix(), rotate(), scale(), shear(), translate() +*/ +QMatrix QGraphicsView::matrix() const +{ + Q_D(const QGraphicsView); + return d->matrix.toAffine(); +} + +/*! + Sets the view's current transformation matrix to \a matrix. + + If \a combine is true, then \a matrix is combined with the current matrix; + otherwise, \a matrix \e replaces the current matrix. \a combine is false + by default. + + The transformation matrix tranforms the scene into view coordinates. Using + the default transformation, provided by the identity matrix, one pixel in + the view represents one unit in the scene (e.g., a 10x10 rectangular item + is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is + applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is + then drawn using 20x20 pixels in the view). + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 3 + + To simplify interation with items using a transformed view, QGraphicsView + provides mapTo... and mapFrom... functions that can translate between + scene and view coordinates. For example, you can call mapToScene() to map + a view coordinate to a floating point scene coordinate, or mapFromScene() + to map from floating point scene coordinates to view coordinates. + + \sa matrix(), rotate(), scale(), shear(), translate() +*/ +void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine) +{ + setTransform(QTransform(matrix), combine); +} + +/*! + Resets the view transformation matrix to the identity matrix. +*/ +void QGraphicsView::resetMatrix() +{ + resetTransform(); +} + +/*! + Rotates the current view transformation \a angle degrees clockwise. + + \sa setMatrix(), matrix(), scale(), shear(), translate() +*/ +void QGraphicsView::rotate(qreal angle) +{ + Q_D(QGraphicsView); + QTransform matrix = d->matrix; + matrix.rotate(angle); + setTransform(matrix); +} + +/*! + Scales the current view transformation by (\a sx, \a sy). + + \sa setMatrix(), matrix(), rotate(), shear(), translate() +*/ +void QGraphicsView::scale(qreal sx, qreal sy) +{ + Q_D(QGraphicsView); + QTransform matrix = d->matrix; + matrix.scale(sx, sy); + setTransform(matrix); +} + +/*! + Shears the current view transformation by (\a sh, \a sv). + + \sa setMatrix(), matrix(), rotate(), scale(), translate() +*/ +void QGraphicsView::shear(qreal sh, qreal sv) +{ + Q_D(QGraphicsView); + QTransform matrix = d->matrix; + matrix.shear(sh, sv); + setTransform(matrix); +} + +/*! + Translates the current view transformation by (\a dx, \a dy). + + \sa setMatrix(), matrix(), rotate(), shear() +*/ +void QGraphicsView::translate(qreal dx, qreal dy) +{ + Q_D(QGraphicsView); + QTransform matrix = d->matrix; + matrix.translate(dx, dy); + setTransform(matrix); +} + +/*! + Scrolls the contents of the viewport to ensure that the scene + coordinate \a pos, is centered in the view. + + Because \a pos is a floating point coordinate, and the scroll bars operate + on integer coordinates, the centering is only an approximation. + + \note If the item is close to or outside the border, it will be visible + in the view, but not centered. + + \sa ensureVisible() +*/ +void QGraphicsView::centerOn(const QPointF &pos) +{ + Q_D(QGraphicsView); + qreal width = viewport()->width(); + qreal height = viewport()->height(); + QPointF viewPoint = d->matrix.map(pos); + QPointF oldCenterPoint = pos; + + if (!d->leftIndent) { + if (isRightToLeft()) { + qint64 horizontal = 0; + horizontal += horizontalScrollBar()->minimum(); + horizontal += horizontalScrollBar()->maximum(); + horizontal -= int(viewPoint.x() - width / 2.0); + horizontalScrollBar()->setValue(horizontal); + } else { + horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0)); + } + } + if (!d->topIndent) + verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0)); + d->lastCenterPoint = oldCenterPoint; +} + +/*! + \fn QGraphicsView::centerOn(qreal x, qreal y) + \overload + + This function is provided for convenience. It's equivalent to calling + centerOn(QPointF(\a x, \a y)). +*/ + +/*! + \overload + + Scrolls the contents of the viewport to ensure that \a item + is centered in the view. + + \sa ensureVisible() +*/ +void QGraphicsView::centerOn(const QGraphicsItem *item) +{ + centerOn(item->sceneBoundingRect().center()); +} + +/*! + Scrolls the contents of the viewport so that the scene rectangle \a rect + is visible, with margins specified in pixels by \a xmargin and \a + ymargin. If the specified rect cannot be reached, the contents are + scrolled to the nearest valid position. The default value for both margins + is 50 pixels. + + \sa centerOn() +*/ +void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin) +{ + Q_D(QGraphicsView); + Q_UNUSED(xmargin); + Q_UNUSED(ymargin); + qreal width = viewport()->width(); + qreal height = viewport()->height(); + QRectF viewRect = d->matrix.mapRect(rect); + + qreal left = d->horizontalScroll(); + qreal right = left + width; + qreal top = d->verticalScroll(); + qreal bottom = top + height; + + if (viewRect.left() <= left + xmargin) { + // need to scroll from the left + if (!d->leftIndent) + horizontalScrollBar()->setValue(int(viewRect.left() - xmargin - 0.5)); + } + if (viewRect.right() >= right - xmargin) { + // need to scroll from the right + if (!d->leftIndent) + horizontalScrollBar()->setValue(int(viewRect.right() - width + xmargin + 0.5)); + } + if (viewRect.top() <= top + ymargin) { + // need to scroll from the top + if (!d->topIndent) + verticalScrollBar()->setValue(int(viewRect.top() - ymargin - 0.5)); + } + if (viewRect.bottom() >= bottom - ymargin) { + // need to scroll from the bottom + if (!d->topIndent) + verticalScrollBar()->setValue(int(viewRect.bottom() - height + ymargin + 0.5)); + } +} + +/*! + \fn QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h, + int xmargin, int ymargin) + \overload + + This function is provided for convenience. It's equivalent to calling + ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin). +*/ + +/*! + \overload + + Scrolls the contents of the viewport so that the center of item \a item is + visible, with margins specified in pixels by \a xmargin and \a ymargin. If + the specified point cannot be reached, the contents are scrolled to the + nearest valid position. The default value for both margins is 50 pixels. + + \sa centerOn() +*/ +void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin) +{ + ensureVisible(item->sceneBoundingRect(), xmargin, ymargin); +} + +/*! + Scales the view matrix and scrolls the scroll bars to ensure that the + scene rectangle \a rect fits inside the viewport. \a rect must be inside + the scene rect; otherwise, fitInView() cannot guarantee that the whole + rect is visible. + + This function keeps the view's rotation, translation, or shear. The view + is scaled according to \a aspectRatioMode. \a rect will be centered in the + view if it does not fit tightly. + + It's common to call fitInView() from inside a reimplementation of + resizeEvent(), to ensure that the whole scene, or parts of the scene, + scales automatically to fit the new size of the viewport as the view is + resized. Note though, that calling fitInView() from inside resizeEvent() + can lead to unwanted resize recursion, if the new transformation toggles + the automatic state of the scrollbars. You can toggle the scrollbar + policies to always on or always off to prevent this (see + horizontalScrollBarPolicy() and verticalScrollBarPolicy()). + + If \a rect is empty, or if the viewport is too small, this + function will do nothing. + + \sa setMatrix(), ensureVisible(), centerOn() +*/ +void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode) +{ + Q_D(QGraphicsView); + if (!d->scene || rect.isNull()) + return; + + // Reset the view scale to 1:1. + QRectF unity = d->matrix.mapRect(QRectF(0, 0, 1, 1)); + if (unity.isEmpty()) + return; + scale(1 / unity.width(), 1 / unity.height()); + + // Find the ideal x / y scaling ratio to fit \a rect in the view. + int margin = 2; + QRectF viewRect = viewport()->rect().adjusted(margin, margin, -margin, -margin); + if (viewRect.isEmpty()) + return; + QRectF sceneRect = d->matrix.mapRect(rect); + if (sceneRect.isEmpty()) + return; + qreal xratio = viewRect.width() / sceneRect.width(); + qreal yratio = viewRect.height() / sceneRect.height(); + + // Respect the aspect ratio mode. + switch (aspectRatioMode) { + case Qt::KeepAspectRatio: + xratio = yratio = qMin(xratio, yratio); + break; + case Qt::KeepAspectRatioByExpanding: + xratio = yratio = qMax(xratio, yratio); + break; + case Qt::IgnoreAspectRatio: + break; + } + + // Scale and center on the center of \a rect. + scale(xratio, yratio); + centerOn(rect.center()); +} + +/*! + \fn void QGraphicsView::fitInView(qreal x, qreal y, qreal w, qreal h, + Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio) + + \overload + + This convenience function is equivalent to calling + fitInView(QRectF(\a x, \a y, \a w, \a h), \a aspectRatioMode). + + \sa ensureVisible(), centerOn() +*/ + +/*! + \overload + + Ensures that \a item fits tightly inside the view, scaling the view + according to \a aspectRatioMode. + + \sa ensureVisible(), centerOn() +*/ +void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode) +{ + QPainterPath path = item->isClipped() ? item->clipPath() : item->shape(); + fitInView(item->sceneTransform().map(path).boundingRect(), aspectRatioMode); +} + +/*! + Renders the \a source rect, which is in view coordinates, from the scene + into \a target, which is in paint device coordinates, using \a + painter. This function is useful for capturing the contents of the view + onto a paint device, such as a QImage (e.g., to take a screenshot), or for + printing to QPrinter. For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 4 + + If \a source is a null rect, this function will use viewport()->rect() to + determine what to draw. If \a target is a null rect, the full dimensions + of \a painter's paint device (e.g., for a QPrinter, the page size) will be + used. + + The source rect contents will be transformed according to \a + aspectRatioMode to fit into the target rect. By default, the aspect ratio + is kept, and \a source is scaled to fit in \a target. + + \sa QGraphicsScene::render() +*/ +void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source, + Qt::AspectRatioMode aspectRatioMode) +{ + Q_D(QGraphicsView); + if (!d->scene || !(painter && painter->isActive())) + return; + + // Default source rect = viewport rect + QRect sourceRect = source; + if (source.isNull()) + sourceRect = viewport()->rect(); + + // Default target rect = device rect + QRectF targetRect = target; + if (target.isNull()) { + if (painter->device()->devType() == QInternal::Picture) + targetRect = sourceRect; + else + targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height()); + } + + // Find the ideal x / y scaling ratio to fit \a source into \a target. + qreal xratio = targetRect.width() / sourceRect.width(); + qreal yratio = targetRect.height() / sourceRect.height(); + + // Scale according to the aspect ratio mode. + switch (aspectRatioMode) { + case Qt::KeepAspectRatio: + xratio = yratio = qMin(xratio, yratio); + break; + case Qt::KeepAspectRatioByExpanding: + xratio = yratio = qMax(xratio, yratio); + break; + case Qt::IgnoreAspectRatio: + break; + } + + // Find all items to draw, and reverse the list (we want to draw + // in reverse order). + QPolygonF sourceScenePoly = mapToScene(sourceRect.adjusted(-1, -1, 1, 1)); + QList<QGraphicsItem *> itemList = d->scene->items(sourceScenePoly, + Qt::IntersectsItemBoundingRect); + QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()]; + int numItems = itemList.size(); + for (int i = 0; i < numItems; ++i) + itemArray[numItems - i - 1] = itemList.at(i); + itemList.clear(); + + // Setup painter matrix. + QTransform moveMatrix; + moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); + QTransform painterMatrix = d->matrix * moveMatrix; + painterMatrix *= QTransform() + .translate(targetRect.left(), targetRect.top()) + .scale(xratio, yratio) + .translate(-sourceRect.left(), -sourceRect.top()); + + // Two unit vectors. + QLineF v1(0, 0, 1, 0); + QLineF v2(0, 0, 0, 1); + + // Generate the style options + QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems); + QStyleOptionGraphicsItem* option = styleOptionArray; + for (int i = 0; i < numItems; ++i, ++option) { + QGraphicsItem *item = itemArray[i]; + + option->state = QStyle::State_None; + option->rect = item->boundingRect().toRect(); + if (item->isSelected()) + option->state |= QStyle::State_Selected; + if (item->isEnabled()) + option->state |= QStyle::State_Enabled; + if (item->hasFocus()) + option->state |= QStyle::State_HasFocus; + if (d->scene->d_func()->hoverItems.contains(item)) + option->state |= QStyle::State_MouseOver; + if (item == d->scene->mouseGrabberItem()) + option->state |= QStyle::State_Sunken; + + // Calculate a simple level-of-detail metric. + // ### almost identical code in QGraphicsScene::render() + // and QGraphicsView::paintEvent() - consider refactoring + QTransform itemToViewportTransform; + if (item->d_ptr->itemIsUntransformable()) { + itemToViewportTransform = item->deviceTransform(painterMatrix); + } else { + itemToViewportTransform = item->sceneTransform() * painterMatrix; + } + + option->levelOfDetail = qSqrt(itemToViewportTransform.map(v1).length() * itemToViewportTransform.map(v2).length()); + option->matrix = itemToViewportTransform.toAffine(); + + option->exposedRect = item->boundingRect(); + option->exposedRect &= itemToViewportTransform.inverted().mapRect(targetRect); + } + + painter->save(); + + // Clip in device coordinates to avoid QRegion transformations. + painter->setClipRect(targetRect); + QPainterPath path; + path.addPolygon(sourceScenePoly); + path.closeSubpath(); + painter->setClipPath(painterMatrix.map(path), Qt::IntersectClip); + + // Transform the painter. + painter->setTransform(painterMatrix, true); + + // Render the scene. + QRectF sourceSceneRect = sourceScenePoly.boundingRect(); + drawBackground(painter, sourceSceneRect); + drawItems(painter, numItems, itemArray, styleOptionArray); + drawForeground(painter, sourceSceneRect); + + delete [] itemArray; + d->freeStyleOptionsArray(styleOptionArray); + + painter->restore(); +} + +/*! + Returns a list of all the items in the associated scene. + + \sa QGraphicsScene::items() +*/ +QList<QGraphicsItem *> QGraphicsView::items() const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QList<QGraphicsItem *>(); + return d->scene->items(); +} + +/*! + Returns all items in the area \a path, which is in viewport coordinates, + also taking untransformable items into consideration. This function is + considerably slower than just checking the scene directly. There is + certainly room for improvement. +*/ +QList<QGraphicsItem *> QGraphicsViewPrivate::itemsInArea(const QPainterPath &path, + Qt::ItemSelectionMode mode) const +{ + Q_Q(const QGraphicsView); + + // Determine the size of the largest untransformable subtree of children + // mapped to scene coordinates. + QRectF untr = scene->d_func()->largestUntransformableItem; + QRectF ltri = matrix.inverted().mapRect(untr); + ltri.adjust(-untr.width(), -untr.height(), untr.width(), untr.height()); + + QRectF rect = path.controlPointRect(); + + // Find all possible items in the relevant area. + // ### Improve this algorithm; it might be searching a too large area. + QRectF adjustedRect = q->mapToScene(rect.adjusted(-1, -1, 1, 1).toRect()).boundingRect(); + adjustedRect.adjust(-ltri.width(), -ltri.height(), ltri.width(), ltri.height()); + + // First build a (potentially large) list of all items in the vicinity + // that might be untransformable. + QList<QGraphicsItem *> allCandidates = scene->d_func()->estimateItemsInRect(adjustedRect); + + // Then find the minimal list of items that are inside \a path, and + // convert it to a set. + QList<QGraphicsItem *> regularCandidates = scene->items(q->mapToScene(path), mode); + QSet<QGraphicsItem *> candSet = QSet<QGraphicsItem *>::fromList(regularCandidates); + + QTransform viewMatrix = q->viewportTransform(); + + QList<QGraphicsItem *> result; + + // Run through all candidates and keep all items that are in candSet, or + // are untransformable and collide with \a path. ### We can improve this + // algorithm. + QList<QGraphicsItem *>::Iterator it = allCandidates.begin(); + while (it != allCandidates.end()) { + QGraphicsItem *item = *it; + if (item->d_ptr->itemIsUntransformable()) { + // Check if this untransformable item collides with the + // original selection rect. + QTransform itemTransform = item->deviceTransform(viewMatrix); + if (QGraphicsScenePrivate::itemCollidesWithPath(item, itemTransform.inverted().map(path), mode)) + result << item; + } else { + if (candSet.contains(item)) + result << item; + } + ++it; + } + + // ### Insertion sort would be faster. + QGraphicsScenePrivate::sortItems(&result, Qt::AscendingOrder, + scene->d_func()->sortCacheEnabled); + return result; +} + +/*! + Returns a list of all the items at the position \a pos in the view. The + items are listed in descending Z order (i.e., the first item in the list + is the top-most item, and the last item is the bottom-most item). \a pos + is in viewport coordinates. + + This function is most commonly called from within mouse event handlers in + a subclass in QGraphicsView. \a pos is in untransformed viewport + coordinates, just like QMouseEvent::pos(). + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 5 + + \sa QGraphicsScene::items(), QGraphicsItem::zValue() +*/ +QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QList<QGraphicsItem *>(); + if (d->scene->d_func()->largestUntransformableItem.isNull()) { + if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) { + QTransform xinv = viewportTransform().inverted(); + return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1))); + } + return d->scene->items(mapToScene(pos.x(), pos.y(), 2, 2)); + } + + QPainterPath path; + path.addRect(QRectF(pos.x(), pos.y(), 1, 1)); + return d->itemsInArea(path); +} + +/*! + \fn QGraphicsView::items(int x, int y) const + + This function is provided for convenience. It's equivalent to calling + items(QPoint(\a x, \a y)). +*/ + +/*! + \overload + + Returns a list of all the items that, depending on \a mode, are either + contained by or intersect with \a rect. \a rect is in viewport + coordinates. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a rect are returned. + + \sa itemAt(), items(), mapToScene() +*/ +QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QList<QGraphicsItem *>(); + if (d->scene->d_func()->largestUntransformableItem.isNull()) + return d->scene->items(mapToScene(rect), mode); + + QPainterPath path; + path.addRect(rect); + return d->itemsInArea(path); +} + +/*! + \fn QList<QGraphicsItem *> QGraphicsView::items(int x, int y, int w, int h, Qt::ItemSelectionMode mode) const + \since 4.3 + + This convenience function is equivalent to calling items(QRectF(\a x, \a + y, \a w, \a h), \a mode). +*/ + +/*! + \overload + + Returns a list of all the items that, depending on \a mode, are either + contained by or intersect with \a polygon. \a polygon is in viewport + coordinates. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a polygon are returned. + + \sa itemAt(), items(), mapToScene() +*/ +QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QList<QGraphicsItem *>(); + if (d->scene->d_func()->largestUntransformableItem.isNull()) + return d->scene->items(mapToScene(polygon), mode); + + QPainterPath path; + path.addPolygon(polygon); + path.closeSubpath(); + return d->itemsInArea(path); +} + +/*! + \overload + + Returns a list of all the items that, depending on \a mode, are either + contained by or intersect with \a path. \a path is in viewport + coordinates. + + The default value for \a mode is Qt::IntersectsItemShape; all items whose + exact shape intersects with or is contained by \a path are returned. + + \sa itemAt(), items(), mapToScene() +*/ +QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QList<QGraphicsItem *>(); + if (d->scene->d_func()->largestUntransformableItem.isNull()) + return d->scene->items(mapToScene(path), mode); + return d->itemsInArea(path); +} + +/*! + Returns the item at position \a pos, which is in viewport coordinates. + If there are several items at this position, this function returns + the topmost item. + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 6 + + \sa items() +*/ +QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return 0; + QList<QGraphicsItem *> itemsAtPos = items(pos); + return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first(); +} + +/*! + \overload + \fn QGraphicsItem *QGraphicsView::itemAt(int x, int y) const + + This function is provided for convenience. It's equivalent to + calling itemAt(QPoint(\a x, \a y)). +*/ + +/*! + Returns the viewport coordinate \a point mapped to scene coordinates. + + Note: It can be useful to map the whole rectangle covered by the pixel at + \a point instead of the point itself. To do this, you can call + mapToScene(QRect(\a point, QSize(2, 2))). + + \sa mapFromScene() +*/ +QPointF QGraphicsView::mapToScene(const QPoint &point) const +{ + Q_D(const QGraphicsView); + QPointF p = point; + p.rx() += d->horizontalScroll(); + p.ry() += d->verticalScroll(); + return d->identityMatrix ? p : d->matrix.inverted().map(p); +} + +/*! + \fn QGraphicsView::mapToScene(int x, int y) const + + This function is provided for convenience. It's equivalent to calling + mapToScene(QPoint(\a x, \a y)). +*/ + +/*! + Returns the viewport rectangle \a rect mapped to a scene coordinate + polygon. + + \sa mapFromScene() +*/ +QPolygonF QGraphicsView::mapToScene(const QRect &rect) const +{ + Q_D(const QGraphicsView); + if (!rect.isValid()) + return QPolygonF(); + + QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll()); + QPointF tl = scrollOffset + rect.topLeft(); + QPointF tr = scrollOffset + rect.topRight(); + QPointF br = scrollOffset + rect.bottomRight(); + QPointF bl = scrollOffset + rect.bottomLeft(); + + QPolygonF poly; + poly.resize(4); + if (!d->identityMatrix) { + QTransform x = d->matrix.inverted(); + poly[0] = x.map(tl); + poly[1] = x.map(tr); + poly[2] = x.map(br); + poly[3] = x.map(bl); + } else { + poly[0] = tl; + poly[1] = tr; + poly[2] = br; + poly[3] = bl; + } + return poly; +} + +/*! + \fn QGraphicsView::mapToScene(int x, int y, int w, int h) const + + This function is provided for convenience. It's equivalent to calling + mapToScene(QRect(\a x, \a y, \a w, \a h)). +*/ + +/*! + Returns the viewport polygon \a polygon mapped to a scene coordinate + polygon. + + \sa mapFromScene() +*/ +QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const +{ + QPolygonF poly; + foreach (const QPoint &point, polygon) + poly << mapToScene(point); + return poly; +} + +/*! + Returns the viewport painter path \a path mapped to a scene coordinate + painter path. + + \sa mapFromScene() +*/ +QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const +{ + Q_D(const QGraphicsView); + QTransform moveMatrix; + moveMatrix.translate(d->horizontalScroll(), d->verticalScroll()); + return (moveMatrix * d->matrix.inverted()).map(path); +} + +/*! + Returns the scene coordinate \a point to viewport coordinates. + + \sa mapToScene() +*/ +QPoint QGraphicsView::mapFromScene(const QPointF &point) const +{ + Q_D(const QGraphicsView); + QPointF p = d->identityMatrix ? point : d->matrix.map(point); + p.rx() -= d->horizontalScroll(); + p.ry() -= d->verticalScroll(); + return p.toPoint(); +} + +/*! + \fn QGraphicsView::mapFromScene(qreal x, qreal y) const + + This function is provided for convenience. It's equivalent to + calling mapFromScene(QPointF(\a x, \a y)). +*/ + +/*! + Returns the scene rectangle \a rect to a viewport coordinate + polygon. + + \sa mapToScene() +*/ +QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const +{ + Q_D(const QGraphicsView); + QPointF tl; + QPointF tr; + QPointF br; + QPointF bl; + if (!d->identityMatrix) { + const QTransform &x = d->matrix; + tl = x.map(rect.topLeft()); + tr = x.map(rect.topRight()); + br = x.map(rect.bottomRight()); + bl = x.map(rect.bottomLeft()); + } else { + tl = rect.topLeft(); + tr = rect.topRight(); + br = rect.bottomRight(); + bl = rect.bottomLeft(); + } + QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll()); + tl -= scrollOffset; + tr -= scrollOffset; + br -= scrollOffset; + bl -= scrollOffset; + + QPolygon poly; + poly.resize(4); + poly[0] = tl.toPoint(); + poly[1] = tr.toPoint(); + poly[2] = br.toPoint(); + poly[3] = bl.toPoint(); + return poly; +} + +/*! + \fn QGraphicsView::mapFromScene(qreal x, qreal y, qreal w, qreal h) const + + This function is provided for convenience. It's equivalent to + calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)). +*/ + +/*! + Returns the scene coordinate polygon \a polygon to a viewport coordinate + polygon. + + \sa mapToScene() +*/ +QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const +{ + QPolygon poly; + foreach (const QPointF &point, polygon) + poly << mapFromScene(point); + return poly; +} + +/*! + Returns the scene coordinate painter path \a path to a viewport coordinate + painter path. + + \sa mapToScene() +*/ +QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const +{ + Q_D(const QGraphicsView); + QTransform moveMatrix; + moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); + return (d->matrix * moveMatrix).map(path); +} + +/*! + \reimp +*/ +QVariant QGraphicsView::inputMethodQuery(Qt::InputMethodQuery query) const +{ + Q_D(const QGraphicsView); + if (!d->scene) + return QVariant(); + + QVariant value = d->scene->inputMethodQuery(query); + if (value.type() == QVariant::RectF) + value = mapFromScene(value.toRectF()).boundingRect(); + else if (value.type() == QVariant::PointF) + value = mapFromScene(value.toPointF()); + else if (value.type() == QVariant::Rect) + value = mapFromScene(value.toRect()).boundingRect(); + else if (value.type() == QVariant::Point) + value = mapFromScene(value.toPoint()); + return value; +} + +/*! + \property QGraphicsView::backgroundBrush + \brief the background brush of the scene. + + This property sets the background brush for the scene in this view. It is + used to override the scene's own background, and defines the behavior of + drawBackground(). To provide custom background drawing for this view, you + can reimplement drawBackground() instead. + + By default, this property contains a brush with the Qt::NoBrush pattern. + + \sa QGraphicsScene::backgroundBrush, foregroundBrush +*/ +QBrush QGraphicsView::backgroundBrush() const +{ + Q_D(const QGraphicsView); + return d->backgroundBrush; +} +void QGraphicsView::setBackgroundBrush(const QBrush &brush) +{ + Q_D(QGraphicsView); + d->backgroundBrush = brush; + viewport()->update(); + + if (d->cacheMode & CacheBackground) { + // Invalidate the background pixmap + d->mustResizeBackgroundPixmap = true; + } +} + +/*! + \property QGraphicsView::foregroundBrush + \brief the foreground brush of the scene. + + This property sets the foreground brush for the scene in this view. It is + used to override the scene's own foreground, and defines the behavior of + drawForeground(). To provide custom foreground drawing for this view, you + can reimplement drawForeground() instead. + + By default, this property contains a brush with the Qt::NoBrush pattern. + + \sa QGraphicsScene::foregroundBrush, backgroundBrush +*/ +QBrush QGraphicsView::foregroundBrush() const +{ + Q_D(const QGraphicsView); + return d->foregroundBrush; +} +void QGraphicsView::setForegroundBrush(const QBrush &brush) +{ + Q_D(QGraphicsView); + d->foregroundBrush = brush; + viewport()->update(); +} + +/*! + Schedules an update of the scene rectangles \a rects. + + \sa QGraphicsScene::changed() +*/ +void QGraphicsView::updateScene(const QList<QRectF> &rects) +{ + // ### Note: Since 4.5, this slot is only called if the user explicitly + // establishes a connection between the scene and the view, as the scene + // and view are no longer connected. We need to keep it working (basically + // leave it as it is), but the new delivery path is through + // QGraphicsScenePrivate::itemUpdate(). + Q_D(QGraphicsView); + if (d->fullUpdatePending || d->viewportUpdateMode == QGraphicsView::NoViewportUpdate) + return; + + // Extract and reset dirty scene rect info. + QVector<QRect> dirtyViewportRects; + for (int i = 0; i < d->dirtyRegions.size(); ++i) + dirtyViewportRects += d->dirtyRegions.at(i).rects(); + d->dirtyRegions.clear(); + + bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate; + bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) + || (d->viewportUpdateMode == QGraphicsView::SmartViewportUpdate + && ((dirtyViewportRects.size() + rects.size()) >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD)); + + QRegion updateRegion; + QRect boundingRect; + QRect viewportRect = viewport()->rect(); + bool redraw = false; + QTransform transform = viewportTransform(); + + // Convert scene rects to viewport rects. + foreach (const QRectF &rect, rects) { + QRect xrect = transform.mapRect(rect).toRect(); + if (!(d->optimizationFlags & DontAdjustForAntialiasing)) + xrect.adjust(-2, -2, 2, 2); + if (!viewportRect.intersects(xrect)) + continue; + dirtyViewportRects << xrect; + } + + foreach (const QRect &rect, dirtyViewportRects) { + // Add the exposed rect to the update region. In rect update + // mode, we only count the bounding rect of items. + if (!boundingRectUpdate) { + updateRegion += rect; + } else { + boundingRect |= rect; + } + redraw = true; + if (fullUpdate) { + // If fullUpdate is true and we found a visible dirty rect, + // we're done. + break; + } + } + + if (!redraw) + return; + + if (fullUpdate) + viewport()->update(); + else if (boundingRectUpdate) + viewport()->update(boundingRect); + else + viewport()->update(updateRegion); +} + +/*! + Notifies QGraphicsView that the scene's scene rect has changed. \a rect + is the new scene rect. If the view already has an explicitly set scene + rect, this function does nothing. + + \sa sceneRect, QGraphicsScene::sceneRectChanged() +*/ +void QGraphicsView::updateSceneRect(const QRectF &rect) +{ + Q_D(QGraphicsView); + if (!d->hasSceneRect) { + d->sceneRect = rect; + d->recalculateContentSize(); + } +} + +/*! + This slot is called by QAbstractScrollArea after setViewport() has been + called. Reimplement this function in a subclass of QGraphicsView to + initialize the new viewport \a widget before it is used. + + \sa setViewport() +*/ +void QGraphicsView::setupViewport(QWidget *widget) +{ + Q_D(QGraphicsView); + + if (!widget) { + qWarning("QGraphicsView::setupViewport: cannot initialize null widget"); + return; + } + + const bool isGLWidget = widget->inherits("QGLWidget"); + + d->accelerateScrolling = !(isGLWidget + || widget->testAttribute(Qt::WA_MSWindowsUseDirect3D) + || qApp->testAttribute(Qt::AA_MSWindowsUseDirect3DByDefault)); + + widget->setFocusPolicy(Qt::StrongFocus); + + if (!isGLWidget) { + // autoFillBackground enables scroll acceleration. + widget->setAutoFillBackground(true); + } + + widget->setMouseTracking(true); + widget->setAcceptDrops(acceptDrops()); +} + +/*! + \reimp +*/ +bool QGraphicsView::event(QEvent *event) +{ + Q_D(QGraphicsView); + + if (d->sceneInteractionAllowed) { + switch (event->type()) { + case QEvent::ShortcutOverride: + if (d->scene) + return QApplication::sendEvent(d->scene, event); + break; + case QEvent::KeyPress: + if (d->scene) { + QKeyEvent *k = static_cast<QKeyEvent *>(event); + if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) { + // Send the key events to the scene. This will invoke the + // scene's tab focus handling, and if the event is + // accepted, we return (prevent further event delivery), + // and the base implementation will call QGraphicsView's + // focusNextPrevChild() function. If the event is ignored, + // we fall back to standard tab focus handling. + QApplication::sendEvent(d->scene, event); + if (event->isAccepted()) + return true; + // Ensure the event doesn't propagate just because the + // scene ignored it. If the event propagates, then tab + // handling will be called twice (this and parent). + event->accept(); + } + } + break; + default: + break; + } + } + + return QAbstractScrollArea::event(event); +} + +/*! + \reimp +*/ +bool QGraphicsView::viewportEvent(QEvent *event) +{ + Q_D(QGraphicsView); + + if (!d->scene) + return QAbstractScrollArea::viewportEvent(event); + + switch (event->type()) { + case QEvent::Enter: + QApplication::sendEvent(d->scene, event); + break; + case QEvent::WindowActivate: + QApplication::sendEvent(d->scene, event); + break; + case QEvent::WindowDeactivate: + // ### This is a temporary fix for until we get proper mouse + // grab events. mouseGrabberItem should be set to 0 if we lose + // the mouse grab. + // Remove all popups when the scene loses focus. + if (!d->scene->d_func()->popupWidgets.isEmpty()) + d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first()); + QApplication::sendEvent(d->scene, event); + break; + case QEvent::Leave: + // ### This is a temporary fix for until we get proper mouse grab + // events. activeMouseGrabberItem should be set to 0 if we lose the + // mouse grab. + if ((QApplication::activePopupWidget() && QApplication::activePopupWidget() != window()) + || (QApplication::activeModalWidget() && QApplication::activeModalWidget() != window()) + || (QApplication::activeWindow() != window())) { + if (!d->scene->d_func()->popupWidgets.isEmpty()) + d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first()); + } + d->useLastMouseEvent = false; + QApplication::sendEvent(d->scene, event); + break; +#ifndef QT_NO_TOOLTIP + case QEvent::ToolTip: { + QHelpEvent *toolTip = static_cast<QHelpEvent *>(event); + QGraphicsSceneHelpEvent helpEvent(QEvent::GraphicsSceneHelp); + helpEvent.setWidget(viewport()); + helpEvent.setScreenPos(toolTip->globalPos()); + helpEvent.setScenePos(mapToScene(toolTip->pos())); + QApplication::sendEvent(d->scene, &helpEvent); + toolTip->setAccepted(helpEvent.isAccepted()); + return true; + } +#endif + case QEvent::Paint: + // Reset full update + d->fullUpdatePending = false; + if (d->scene) { + // Check if this view reimplements the updateScene slot; if it + // does, we can't do direct update delivery and have to fall back + // to connecting the changed signal. + if (!d->updateSceneSlotReimplementedChecked) { + d->updateSceneSlotReimplementedChecked = true; + const QMetaObject *mo = metaObject(); + if (mo != &QGraphicsView::staticMetaObject) { + if (mo->indexOfSlot("updateScene(QList<QRectF>)") + != QGraphicsView::staticMetaObject.indexOfSlot("updateScene(QList<QRectF>)")) { + connect(d->scene, SIGNAL(changed(QList<QRectF>)), + this, SLOT(updateScene(QList<QRectF>))); + } + } + } + d->scene->d_func()->updateAll = false; + } + break; + default: + break; + } + + return QAbstractScrollArea::viewportEvent(event); +} + +#ifndef QT_NO_CONTEXTMENU +/*! + \reimp +*/ +void QGraphicsView::contextMenuEvent(QContextMenuEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + + d->mousePressViewPoint = event->pos(); + d->mousePressScenePoint = mapToScene(d->mousePressViewPoint); + d->mousePressScreenPoint = event->globalPos(); + d->lastMouseMoveScenePoint = d->mousePressScenePoint; + d->lastMouseMoveScreenPoint = d->mousePressScreenPoint; + + QGraphicsSceneContextMenuEvent contextEvent(QEvent::GraphicsSceneContextMenu); + contextEvent.setWidget(viewport()); + contextEvent.setScenePos(d->mousePressScenePoint); + contextEvent.setScreenPos(d->mousePressScreenPoint); + contextEvent.setModifiers(event->modifiers()); + contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason())); + contextEvent.setAccepted(event->isAccepted()); + QApplication::sendEvent(d->scene, &contextEvent); + event->setAccepted(contextEvent.isAccepted()); +} +#endif // QT_NO_CONTEXTMENU + +/*! + \reimp +*/ +void QGraphicsView::dropEvent(QDropEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + + // Generate a scene event. + QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDrop); + d->populateSceneDragDropEvent(&sceneEvent, event); + + // Send it to the scene. + QApplication::sendEvent(d->scene, &sceneEvent); + + // Accept the originating event if the scene accepted the scene event. + event->setAccepted(sceneEvent.isAccepted()); + if (sceneEvent.isAccepted()) + event->setDropAction(sceneEvent.dropAction()); + + delete d->lastDragDropEvent; + d->lastDragDropEvent = 0; + +#else + Q_UNUSED(event) +#endif +} + +/*! + \reimp +*/ +void QGraphicsView::dragEnterEvent(QDragEnterEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + + // Disable replaying of mouse move events. + d->useLastMouseEvent = false; + + // Generate a scene event. + QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragEnter); + d->populateSceneDragDropEvent(&sceneEvent, event); + + // Store it for later use. + d->storeDragDropEvent(&sceneEvent); + + // Send it to the scene. + QApplication::sendEvent(d->scene, &sceneEvent); + + // Accept the originating event if the scene accepted the scene event. + if (sceneEvent.isAccepted()) { + event->setAccepted(true); + event->setDropAction(sceneEvent.dropAction()); + } +#else + Q_UNUSED(event) +#endif +} + +/*! + \reimp +*/ +void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + if (!d->lastDragDropEvent) { + qWarning("QGraphicsView::dragLeaveEvent: drag leave received before drag enter"); + return; + } + + // Generate a scene event. + QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragLeave); + sceneEvent.setScenePos(d->lastDragDropEvent->scenePos()); + sceneEvent.setScreenPos(d->lastDragDropEvent->screenPos()); + sceneEvent.setButtons(d->lastDragDropEvent->buttons()); + sceneEvent.setModifiers(d->lastDragDropEvent->modifiers()); + sceneEvent.setPossibleActions(d->lastDragDropEvent->possibleActions()); + sceneEvent.setProposedAction(d->lastDragDropEvent->proposedAction()); + sceneEvent.setDropAction(d->lastDragDropEvent->dropAction()); + sceneEvent.setMimeData(d->lastDragDropEvent->mimeData()); + sceneEvent.setWidget(d->lastDragDropEvent->widget()); + sceneEvent.setSource(d->lastDragDropEvent->source()); + delete d->lastDragDropEvent; + d->lastDragDropEvent = 0; + + // Send it to the scene. + QApplication::sendEvent(d->scene, &sceneEvent); + + // Accept the originating event if the scene accepted the scene event. + if (sceneEvent.isAccepted()) + event->setAccepted(true); +#else + Q_UNUSED(event) +#endif +} + +/*! + \reimp +*/ +void QGraphicsView::dragMoveEvent(QDragMoveEvent *event) +{ +#ifndef QT_NO_DRAGANDDROP + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + + // Generate a scene event. + QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragMove); + d->populateSceneDragDropEvent(&sceneEvent, event); + + // Store it for later use. + d->storeDragDropEvent(&sceneEvent); + + // Send it to the scene. + QApplication::sendEvent(d->scene, &sceneEvent); + + // Ignore the originating event if the scene ignored the scene event. + event->setAccepted(sceneEvent.isAccepted()); + if (sceneEvent.isAccepted()) + event->setDropAction(sceneEvent.dropAction()); +#else + Q_UNUSED(event) +#endif +} + +/*! + \reimp +*/ +void QGraphicsView::focusInEvent(QFocusEvent *event) +{ + Q_D(QGraphicsView); + QAbstractScrollArea::focusInEvent(event); + if (d->scene) + QApplication::sendEvent(d->scene, event); + // Pass focus on if the scene cannot accept focus. + if (!d->scene || !event->isAccepted()) + QAbstractScrollArea::focusInEvent(event); +} + +/*! + \reimp +*/ +bool QGraphicsView::focusNextPrevChild(bool next) +{ + return QAbstractScrollArea::focusNextPrevChild(next); +} + +/*! + \reimp +*/ +void QGraphicsView::focusOutEvent(QFocusEvent *event) +{ + Q_D(QGraphicsView); + QAbstractScrollArea::focusOutEvent(event); + if (d->scene) + QApplication::sendEvent(d->scene, event); +} + +/*! + \reimp +*/ +void QGraphicsView::keyPressEvent(QKeyEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) { + QAbstractScrollArea::keyPressEvent(event); + return; + } + QApplication::sendEvent(d->scene, event); + if (!event->isAccepted()) + QAbstractScrollArea::keyPressEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsView::keyReleaseEvent(QKeyEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + QApplication::sendEvent(d->scene, event); + if (!event->isAccepted()) + QAbstractScrollArea::keyReleaseEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) + return; + + d->storeMouseEvent(event); + d->mousePressViewPoint = event->pos(); + d->mousePressScenePoint = mapToScene(d->mousePressViewPoint); + d->mousePressScreenPoint = event->globalPos(); + d->lastMouseMoveScenePoint = d->mousePressScenePoint; + d->lastMouseMoveScreenPoint = d->mousePressScreenPoint; + d->mousePressButton = event->button(); + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick); + mouseEvent.setWidget(viewport()); + mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint); + mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint); + mouseEvent.setScenePos(mapToScene(d->mousePressViewPoint)); + mouseEvent.setScreenPos(d->mousePressScreenPoint); + mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint); + mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint); + mouseEvent.setButtons(event->buttons()); + mouseEvent.setButtons(event->buttons()); + mouseEvent.setAccepted(false); + mouseEvent.setButton(event->button()); + mouseEvent.setModifiers(event->modifiers()); + QApplication::sendEvent(d->scene, &mouseEvent); +} + +/*! + \reimp +*/ +void QGraphicsView::mousePressEvent(QMouseEvent *event) +{ + Q_D(QGraphicsView); + + // Store this event for replaying, finding deltas, and for + // scroll-dragging; even in non-interactive mode, scroll hand dragging is + // allowed, so we store the event at the very top of this function. + d->storeMouseEvent(event); + d->lastMouseEvent.setAccepted(false); + + if (d->sceneInteractionAllowed) { + // Store some of the event's button-down data. + d->mousePressViewPoint = event->pos(); + d->mousePressScenePoint = mapToScene(d->mousePressViewPoint); + d->mousePressScreenPoint = event->globalPos(); + d->lastMouseMoveScenePoint = d->mousePressScenePoint; + d->lastMouseMoveScreenPoint = d->mousePressScreenPoint; + d->mousePressButton = event->button(); + + if (d->scene) { + // Convert and deliver the mouse event to the scene. + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress); + mouseEvent.setWidget(viewport()); + mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint); + mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint); + mouseEvent.setScenePos(d->mousePressScenePoint); + mouseEvent.setScreenPos(d->mousePressScreenPoint); + mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint); + mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint); + mouseEvent.setButtons(event->buttons()); + mouseEvent.setButton(event->button()); + mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setAccepted(false); + QApplication::sendEvent(d->scene, &mouseEvent); + + // Update the original mouse event accepted state. + bool isAccepted = mouseEvent.isAccepted(); + event->setAccepted(isAccepted); + + // Update the last mouse event accepted state. + d->lastMouseEvent.setAccepted(isAccepted); + + if (isAccepted) + return; + } + } + +#ifndef QT_NO_RUBBERBAND + if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) { + if (d->sceneInteractionAllowed) { + // Rubberbanding is only allowed in interactive mode. + event->accept(); + d->rubberBanding = true; + d->rubberBandRect = QRect(); + if (d->scene) { + // Initiating a rubber band always clears the selection. + d->scene->clearSelection(); + } + } + } else +#endif + if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) { + // Left-button press in scroll hand mode initiates hand scrolling. + event->accept(); + d->handScrolling = true; + d->handScrollMotions = 0; +#ifndef QT_NO_CURSOR + viewport()->setCursor(Qt::ClosedHandCursor); +#endif + } +} + +/*! + \reimp +*/ +void QGraphicsView::mouseMoveEvent(QMouseEvent *event) +{ + Q_D(QGraphicsView); + +#ifndef QT_NO_RUBBERBAND + if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed) { + d->storeMouseEvent(event); + if (d->rubberBanding) { + // Check for enough drag distance + if ((d->mousePressViewPoint - event->pos()).manhattanLength() + < QApplication::startDragDistance()) { + return; + } + + // Update old rubberband + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) { + if (d->viewportUpdateMode != FullViewportUpdate) + viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); + else + viewport()->update(); + } + + // Stop rubber banding if the user has let go of all buttons (even + // if we didn't get the release events). + if (!event->buttons()) { + d->rubberBanding = false; + d->rubberBandRect = QRect(); + return; + } + + // Update rubberband position + const QPoint &mp = d->mousePressViewPoint; + QPoint ep = event->pos(); + d->rubberBandRect = QRect(qMin(mp.x(), ep.x()), qMin(mp.y(), ep.y()), + qAbs(mp.x() - ep.x()) + 1, qAbs(mp.y() - ep.y()) + 1); + + // Update new rubberband + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){ + if (d->viewportUpdateMode != FullViewportUpdate) + viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); + else + viewport()->update(); + } + // Set the new selection area + QPainterPath selectionArea; + selectionArea.addPolygon(mapToScene(d->rubberBandRect)); + selectionArea.closeSubpath(); + if (d->scene) + d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode); + return; + } + } else +#endif // QT_NO_RUBBERBAND + if (d->dragMode == QGraphicsView::ScrollHandDrag) { + if (d->handScrolling) { + QScrollBar *hBar = horizontalScrollBar(); + QScrollBar *vBar = verticalScrollBar(); + QPoint delta = event->pos() - d->lastMouseEvent.pos(); + hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x())); + vBar->setValue(vBar->value() - delta.y()); + + // Detect how much we've scrolled to disambiguate scrolling from + // clicking. + ++d->handScrollMotions; + } + } + + d->mouseMoveEventHandler(event); +} + +/*! + \reimp +*/ +void QGraphicsView::mouseReleaseEvent(QMouseEvent *event) +{ + Q_D(QGraphicsView); + +#ifndef QT_NO_RUBBERBAND + if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed && !event->buttons()) { + if (d->rubberBanding) { + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){ + if (d->viewportUpdateMode != FullViewportUpdate) + viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); + else + viewport()->update(); + } + d->rubberBanding = false; + d->rubberBandRect = QRect(); + } + } else +#endif + if (d->dragMode == QGraphicsView::ScrollHandDrag) { +#ifndef QT_NO_CURSOR + // Restore the open hand cursor. ### There might be items + // under the mouse that have a valid cursor at this time, so + // we could repeat the steps from mouseMoveEvent(). + viewport()->setCursor(Qt::OpenHandCursor); +#endif + d->handScrolling = false; + + if (d->scene && d->sceneInteractionAllowed && !d->lastMouseEvent.isAccepted() && d->handScrollMotions <= 6) { + // If we've detected very little motion during the hand drag, and + // no item accepted the last event, we'll interpret that as a + // click to the scene, and reset the selection. + d->scene->clearSelection(); + } + } + + d->storeMouseEvent(event); + + if (!d->sceneInteractionAllowed) + return; + + if (!d->scene) + return; + + QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease); + mouseEvent.setWidget(viewport()); + mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint); + mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint); + mouseEvent.setScenePos(mapToScene(event->pos())); + mouseEvent.setScreenPos(event->globalPos()); + mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint); + mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint); + mouseEvent.setButtons(event->buttons()); + mouseEvent.setButton(event->button()); + mouseEvent.setModifiers(event->modifiers()); + mouseEvent.setAccepted(false); + QApplication::sendEvent(d->scene, &mouseEvent); + + // Update the last mouse event selected state. + d->lastMouseEvent.setAccepted(mouseEvent.isAccepted()); + +#ifndef QT_NO_CURSOR + if (mouseEvent.isAccepted() && mouseEvent.buttons() == 0 && viewport()->testAttribute(Qt::WA_SetCursor)) { + // The last mouse release on the viewport will trigger clearing the cursor. + d->_q_unsetViewportCursor(); + } +#endif +} + +#ifndef QT_NO_WHEELEVENT +/*! + \reimp +*/ +void QGraphicsView::wheelEvent(QWheelEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene || !d->sceneInteractionAllowed) { + QAbstractScrollArea::wheelEvent(event); + return; + } + + event->ignore(); + + QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel); + wheelEvent.setWidget(viewport()); + wheelEvent.setScenePos(mapToScene(event->pos())); + wheelEvent.setScreenPos(event->globalPos()); + wheelEvent.setButtons(event->buttons()); + wheelEvent.setModifiers(event->modifiers()); + wheelEvent.setDelta(event->delta()); + wheelEvent.setOrientation(event->orientation()); + wheelEvent.setAccepted(false); + QApplication::sendEvent(d->scene, &wheelEvent); + event->setAccepted(wheelEvent.isAccepted()); + if (!event->isAccepted()) + QAbstractScrollArea::wheelEvent(event); +} +#endif // QT_NO_WHEELEVENT + +/*! + \reimp +*/ +void QGraphicsView::paintEvent(QPaintEvent *event) +{ + Q_D(QGraphicsView); + if (!d->scene) { + QAbstractScrollArea::paintEvent(event); + return; + } + + // Set up painter state protection. + d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState); + + // Determine the exposed region + QRegion exposedRegion = event->region(); + if (!d->accelerateScrolling) + exposedRegion = viewport()->rect(); + else if (d->viewportUpdateMode == BoundingRectViewportUpdate) + exposedRegion = event->rect(); + QRectF exposedSceneRect = mapToScene(exposedRegion.boundingRect().adjusted(0, 0, 1, 1)).boundingRect(); + + // Set up the painter + QPainter painter(viewport()); + QTransform original = painter.worldTransform(); +#ifndef QT_NO_RUBBERBAND + if (d->rubberBanding && !d->rubberBandRect.isNull()) + painter.save(); +#endif + // Set up render hints + painter.setRenderHints(painter.renderHints(), false); + painter.setRenderHints(d->renderHints, true); + + // Set up viewport transform + const QTransform viewTransform = viewportTransform(); + painter.setTransform(viewTransform, true); + +#ifdef QGRAPHICSVIEW_DEBUG + QTime stopWatch; + stopWatch.start(); + qDebug() << "QGraphicsView::paintEvent(" << exposedRegion << ")"; +#endif + + // Find all exposed items + bool allItems = false; + QList<QGraphicsItem *> itemList = d->findItems(exposedRegion, viewTransform, &allItems); + +#ifdef QGRAPHICSVIEW_DEBUG + int exposedTime = stopWatch.elapsed(); +#endif + + if ((d->cacheMode & CacheBackground) +#ifdef Q_WS_X11 + && X11->use_xrender +#endif + ) { + // Recreate the background pixmap, and flag the whole background as + // exposed. + if (d->mustResizeBackgroundPixmap) { + d->backgroundPixmap = QPixmap(viewport()->size()); + QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole()); + if (!bgBrush.isOpaque()) + d->backgroundPixmap.fill(Qt::transparent); + QPainter p(&d->backgroundPixmap); + p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush); + d->backgroundPixmapExposed = QRegion(viewport()->rect()); + d->mustResizeBackgroundPixmap = false; + } + + // Redraw exposed areas + if (!d->backgroundPixmapExposed.isEmpty()) { + QPainter backgroundPainter(&d->backgroundPixmap); + backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip); + backgroundPainter.setTransform(viewportTransform()); + backgroundPainter.setCompositionMode(QPainter::CompositionMode_Source); + drawBackground(&backgroundPainter, exposedSceneRect); + d->backgroundPixmapExposed = QRegion(); + } + + // Blit the background from the background pixmap + QTransform oldMatrix = painter.worldTransform(); + painter.setWorldTransform(original); + painter.drawPixmap(QPoint(), d->backgroundPixmap); + painter.setWorldTransform(oldMatrix); + } else { + if (!(d->optimizationFlags & DontSavePainterState)) + painter.save(); + drawBackground(&painter, exposedSceneRect); + if (!(d->optimizationFlags & DontSavePainterState)) + painter.restore(); + } + +#ifdef QGRAPHICSVIEW_DEBUG + int backgroundTime = stopWatch.elapsed() - exposedTime; +#endif + + // Generate the style options + QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()]; + QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(itemList.size()); + + d->generateStyleOptions(itemList, itemArray, styleOptionArray, viewTransform, + allItems, exposedRegion); + + // Items + drawItems(&painter, itemList.size(), itemArray, styleOptionArray); + +#ifdef QGRAPHICSVIEW_DEBUG + int itemsTime = stopWatch.elapsed() - exposedTime - backgroundTime; +#endif + + // Foreground + drawForeground(&painter, exposedSceneRect); + + delete [] itemArray; + d->freeStyleOptionsArray(styleOptionArray); + +#ifdef QGRAPHICSVIEW_DEBUG + int foregroundTime = stopWatch.elapsed() - exposedTime - backgroundTime - itemsTime; +#endif + +#ifndef QT_NO_RUBBERBAND + // Rubberband + if (d->rubberBanding && !d->rubberBandRect.isNull()) { + painter.restore(); + QStyleOptionRubberBand option; + option.initFrom(viewport()); + option.rect = d->rubberBandRect; + option.shape = QRubberBand::Rectangle; + + QStyleHintReturnMask mask; + if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) { + // painter clipping for masked rubberbands + painter.setClipRegion(mask.region, Qt::IntersectClip); + } + + viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport()); + } +#endif + + painter.end(); + +#ifdef QGRAPHICSVIEW_DEBUG + qDebug() << "\tItem discovery....... " << exposedTime << "msecs (" << itemList.size() << "items," + << (exposedTime > 0 ? (itemList.size() * 1000.0 / exposedTime) : -1) << "/ sec )"; + qDebug() << "\tDrawing background... " << backgroundTime << "msecs (" << exposedRegion.numRects() << "segments )"; + qDebug() << "\tDrawing items........ " << itemsTime << "msecs (" + << (itemsTime > 0 ? (itemList.size() * 1000.0 / itemsTime) : -1) << "/ sec )"; + qDebug() << "\tDrawing foreground... " << foregroundTime << "msecs (" << exposedRegion.numRects() << "segments )"; + qDebug() << "\tTotal rendering time: " << stopWatch.elapsed() << "msecs (" + << (stopWatch.elapsed() > 0 ? (1000.0 / stopWatch.elapsed()) : -1.0) << "fps )"; +#endif + + // Restore painter state protection. + d->scene->d_func()->painterStateProtection = true; +} + +/*! + \reimp +*/ +void QGraphicsView::resizeEvent(QResizeEvent *event) +{ + Q_D(QGraphicsView); + // Save the last center point - the resize may scroll the view, which + // changes the center point. + QPointF oldLastCenterPoint = d->lastCenterPoint; + + QAbstractScrollArea::resizeEvent(event); + d->recalculateContentSize(); + + // Restore the center point again. + if (d->resizeAnchor == NoAnchor && !d->keepLastCenterPoint) { + d->updateLastCenterPoint(); + } else { + d->lastCenterPoint = oldLastCenterPoint; + } + d->centerView(d->resizeAnchor); + d->keepLastCenterPoint = false; + + if (d->cacheMode & CacheBackground) { + // Invalidate the background pixmap + d->mustResizeBackgroundPixmap = true; + } +} + +/*! + \reimp +*/ +void QGraphicsView::scrollContentsBy(int dx, int dy) +{ + Q_D(QGraphicsView); + d->dirtyScroll = true; + if (d->transforming) + return; + if (isRightToLeft()) + dx = -dx; + + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate + && d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) { + for (int i = 0; i < d->dirtyRects.size(); ++i) + d->dirtyRects[i].translate(dx, dy); + for (int i = 0; i < d->dirtyRegions.size(); ++i) + d->dirtyRegions[i].translate(dx, dy); + } + +#ifndef QT_NO_RUBBERBAND + // Update old rubberband + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isNull()) { + if (d->viewportUpdateMode != FullViewportUpdate) + viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect)); + else + viewport()->update(); + } +#endif + + if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){ + if (d->accelerateScrolling && d->viewportUpdateMode != FullViewportUpdate) + viewport()->scroll(dx, dy); + else + viewport()->update(); + } + d->updateLastCenterPoint(); + + if ((d->cacheMode & CacheBackground) +#ifdef Q_WS_X11 + && X11->use_xrender +#endif + ) { + // Scroll the background pixmap + QRegion exposed; + if (!d->backgroundPixmap.isNull()) + d->backgroundPixmap.scroll(dx, dy, d->backgroundPixmap.rect(), &exposed); + + // Invalidate the background pixmap + d->backgroundPixmapExposed.translate(dx, dy); + d->backgroundPixmapExposed += exposed; + } + + // Always replay on scroll. + if (d->sceneInteractionAllowed) + d->replayLastMouseEvent(); +} + +/*! + \reimp +*/ +void QGraphicsView::showEvent(QShowEvent *event) +{ + Q_D(QGraphicsView); + d->recalculateContentSize(); + d->centerView(d->transformationAnchor); + QAbstractScrollArea::showEvent(event); +} + +/*! + \reimp +*/ +void QGraphicsView::inputMethodEvent(QInputMethodEvent *event) +{ + Q_D(QGraphicsView); + if (d->scene) + QApplication::sendEvent(d->scene, event); +} + +/*! + Draws the background of the scene using \a painter, before any items and + the foreground are drawn. Reimplement this function to provide a custom + background for this view. + + If all you want is to define a color, texture or gradient for the + background, you can call setBackgroundBrush() instead. + + All painting is done in \e scene coordinates. \a rect is the exposed + rectangle. + + The default implementation fills \a rect using the view's backgroundBrush. + If no such brush is defined (the default), the scene's drawBackground() + function is called instead. + + \sa drawForeground(), QGraphicsScene::drawBackground() +*/ +void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect) +{ + Q_D(QGraphicsView); + if (d->scene && d->backgroundBrush.style() == Qt::NoBrush) { + d->scene->drawBackground(painter, rect); + return; + } + + painter->fillRect(rect, d->backgroundBrush); +} + +/*! + Draws the foreground of the scene using \a painter, after the background + and all items are drawn. Reimplement this function to provide a custom + foreground for this view. + + If all you want is to define a color, texture or gradient for the + foreground, you can call setForegroundBrush() instead. + + All painting is done in \e scene coordinates. \a rect is the exposed + rectangle. + + The default implementation fills \a rect using the view's foregroundBrush. + If no such brush is defined (the default), the scene's drawForeground() + function is called instead. + + \sa drawBackground(), QGraphicsScene::drawForeground() +*/ +void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect) +{ + Q_D(QGraphicsView); + if (d->scene && d->foregroundBrush.style() == Qt::NoBrush) { + d->scene->drawForeground(painter, rect); + return; + } + + painter->fillRect(rect, d->foregroundBrush); +} + +/*! + Draws the items \a items in the scene using \a painter, after the + background and before the foreground are drawn. \a numItems is the number + of items in \a items and options in \a options. \a options is a list of + styleoptions; one for each item. Reimplement this function to provide + custom item drawing for this view. + + The default implementation calls the scene's drawItems() function. + + \sa drawForeground(), drawBackground(), QGraphicsScene::drawItems() +*/ +void QGraphicsView::drawItems(QPainter *painter, int numItems, + QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[]) +{ + Q_D(QGraphicsView); + if (d->scene) + d->scene->drawItems(painter, numItems, items, options, viewport()); +} + +/*! + Returns the current transformation matrix for the view. If no current + transformation is set, the identity matrix is returned. + + \sa setTransform(), rotate(), scale(), shear(), translate() +*/ +QTransform QGraphicsView::transform() const +{ + Q_D(const QGraphicsView); + return d->matrix; +} + +/*! + Returns a matrix that maps viewport coordinates to scene coordinates. + + \sa mapToScene(), mapFromScene() +*/ +QTransform QGraphicsView::viewportTransform() const +{ + Q_D(const QGraphicsView); + QTransform moveMatrix; + moveMatrix.translate(-d->horizontalScroll(), -d->verticalScroll()); + return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix; +} + +/*! + Sets the view's current transformation matrix to \a matrix. + + If \a combine is true, then \a matrix is combined with the current matrix; + otherwise, \a matrix \e replaces the current matrix. \a combine is false + by default. + + The transformation matrix tranforms the scene into view coordinates. Using + the default transformation, provided by the identity matrix, one pixel in + the view represents one unit in the scene (e.g., a 10x10 rectangular item + is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is + applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is + then drawn using 20x20 pixels in the view). + + Example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 7 + + To simplify interation with items using a transformed view, QGraphicsView + provides mapTo... and mapFrom... functions that can translate between + scene and view coordinates. For example, you can call mapToScene() to map + a view coordiate to a floating point scene coordinate, or mapFromScene() + to map from floating point scene coordinates to view coordinates. + + \sa transform(), rotate(), scale(), shear(), translate() +*/ +void QGraphicsView::setTransform(const QTransform &matrix, bool combine ) +{ + Q_D(QGraphicsView); + QTransform oldMatrix = d->matrix; + if (!combine) + d->matrix = matrix; + else + d->matrix = matrix * d->matrix; + if (oldMatrix == d->matrix) + return; + + d->identityMatrix = d->matrix.isIdentity(); + d->transforming = true; + if (d->scene) { + d->recalculateContentSize(); + d->centerView(d->transformationAnchor); + } else { + d->updateLastCenterPoint(); + } + + if (d->sceneInteractionAllowed) + d->replayLastMouseEvent(); + d->transforming = false; + + // Any matrix operation requires a full update. + viewport()->update(); +} + +/*! + Resets the view transformation to the identity matrix. + + \sa transform(), setTransform() +*/ +void QGraphicsView::resetTransform() +{ + setTransform(QTransform()); +} + +QT_END_NAMESPACE + +#include "moc_qgraphicsview.cpp" + +#endif // QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicsview.h b/src/gui/graphicsview/qgraphicsview.h new file mode 100644 index 0000000..e77e45c --- /dev/null +++ b/src/gui/graphicsview/qgraphicsview.h @@ -0,0 +1,314 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSVIEW_H +#define QGRAPHICSVIEW_H + +#include <QtCore/qmetatype.h> +#include <QtGui/qpainter.h> +#include <QtGui/qscrollarea.h> +#include <QtGui/qgraphicsscene.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsItem; +class QPainterPath; +class QPolygonF; +class QStyleOptionGraphicsItem; + +class QGraphicsViewPrivate; +class Q_GUI_EXPORT QGraphicsView : public QAbstractScrollArea +{ + Q_OBJECT + Q_FLAGS(QPainter::RenderHints CacheMode OptimizationFlags) + Q_ENUMS(ViewportAnchor DragMode ViewportUpdateMode) + Q_PROPERTY(QBrush backgroundBrush READ backgroundBrush WRITE setBackgroundBrush) + Q_PROPERTY(QBrush foregroundBrush READ foregroundBrush WRITE setForegroundBrush) + Q_PROPERTY(bool interactive READ isInteractive WRITE setInteractive) + Q_PROPERTY(QRectF sceneRect READ sceneRect WRITE setSceneRect) + Q_PROPERTY(Qt::Alignment alignment READ alignment WRITE setAlignment) + Q_PROPERTY(QPainter::RenderHints renderHints READ renderHints WRITE setRenderHints) + Q_PROPERTY(DragMode dragMode READ dragMode WRITE setDragMode) + Q_PROPERTY(CacheMode cacheMode READ cacheMode WRITE setCacheMode) + Q_PROPERTY(ViewportAnchor transformationAnchor READ transformationAnchor WRITE setTransformationAnchor) + Q_PROPERTY(ViewportAnchor resizeAnchor READ resizeAnchor WRITE setResizeAnchor) + Q_PROPERTY(ViewportUpdateMode viewportUpdateMode READ viewportUpdateMode WRITE setViewportUpdateMode) +#ifndef QT_NO_RUBBERBAND + Q_PROPERTY(Qt::ItemSelectionMode rubberBandSelectionMode READ rubberBandSelectionMode WRITE setRubberBandSelectionMode) +#endif + Q_PROPERTY(OptimizationFlags optimizationFlags READ optimizationFlags WRITE setOptimizationFlags) + +public: + enum ViewportAnchor { + NoAnchor, + AnchorViewCenter, + AnchorUnderMouse + }; + + enum CacheModeFlag { + CacheNone = 0x0, + CacheBackground = 0x1 + }; + Q_DECLARE_FLAGS(CacheMode, CacheModeFlag) + + enum DragMode { + NoDrag, + ScrollHandDrag, + RubberBandDrag + }; + + enum ViewportUpdateMode { + FullViewportUpdate, + MinimalViewportUpdate, + SmartViewportUpdate, + NoViewportUpdate, + BoundingRectViewportUpdate + }; + + enum OptimizationFlag { + DontClipPainter = 0x1, + DontSavePainterState = 0x2, + DontAdjustForAntialiasing = 0x4 + }; + Q_DECLARE_FLAGS(OptimizationFlags, OptimizationFlag) + + QGraphicsView(QWidget *parent = 0); + QGraphicsView(QGraphicsScene *scene, QWidget *parent = 0); + ~QGraphicsView(); + + QSize sizeHint() const; + + QPainter::RenderHints renderHints() const; + void setRenderHint(QPainter::RenderHint hint, bool enabled = true); + void setRenderHints(QPainter::RenderHints hints); + + Qt::Alignment alignment() const; + void setAlignment(Qt::Alignment alignment); + + ViewportAnchor transformationAnchor() const; + void setTransformationAnchor(ViewportAnchor anchor); + + ViewportAnchor resizeAnchor() const; + void setResizeAnchor(ViewportAnchor anchor); + + ViewportUpdateMode viewportUpdateMode() const; + void setViewportUpdateMode(ViewportUpdateMode mode); + + OptimizationFlags optimizationFlags() const; + void setOptimizationFlag(OptimizationFlag flag, bool enabled = true); + void setOptimizationFlags(OptimizationFlags flags); + + DragMode dragMode() const; + void setDragMode(DragMode mode); + +#ifndef QT_NO_RUBBERBAND + Qt::ItemSelectionMode rubberBandSelectionMode() const; + void setRubberBandSelectionMode(Qt::ItemSelectionMode mode); +#endif + + CacheMode cacheMode() const; + void setCacheMode(CacheMode mode); + void resetCachedContent(); + + bool isInteractive() const; + void setInteractive(bool allowed); + + QGraphicsScene *scene() const; + void setScene(QGraphicsScene *scene); + + QRectF sceneRect() const; + void setSceneRect(const QRectF &rect); + inline void setSceneRect(qreal x, qreal y, qreal w, qreal h); + + QMatrix matrix() const; + void setMatrix(const QMatrix &matrix, bool combine = false); + void resetMatrix(); + QTransform transform() const; + QTransform viewportTransform() const; + void setTransform(const QTransform &matrix, bool combine = false); + void resetTransform(); + void rotate(qreal angle); + void scale(qreal sx, qreal sy); + void shear(qreal sh, qreal sv); + void translate(qreal dx, qreal dy); + + void centerOn(const QPointF &pos); + inline void centerOn(qreal x, qreal y); + void centerOn(const QGraphicsItem *item); + void ensureVisible(const QRectF &rect, int xmargin = 50, int ymargin = 50); + inline void ensureVisible(qreal x, qreal y, qreal w, qreal h, int xmargin = 50, int ymargin = 50); + void ensureVisible(const QGraphicsItem *item, int xmargin = 50, int ymargin = 50); + void fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio); + inline void fitInView(qreal x, qreal y, qreal w, qreal h, + Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio); + void fitInView(const QGraphicsItem *item, + Qt::AspectRatioMode aspectRadioMode = Qt::IgnoreAspectRatio); + + void render(QPainter *painter, const QRectF &target = QRectF(), const QRect &source = QRect(), + Qt::AspectRatioMode aspectRatioMode = Qt::KeepAspectRatio); + + QList<QGraphicsItem *> items() const; + QList<QGraphicsItem *> items(const QPoint &pos) const; + inline QList<QGraphicsItem *> items(int x, int y) const; + QList<QGraphicsItem *> items(const QRect &rect, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + inline QList<QGraphicsItem *> items(int x, int y, int w, int h, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(const QPolygon &polygon, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QList<QGraphicsItem *> items(const QPainterPath &path, Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + QGraphicsItem *itemAt(const QPoint &pos) const; + inline QGraphicsItem *itemAt(int x, int y) const; + + QPointF mapToScene(const QPoint &point) const; + QPolygonF mapToScene(const QRect &rect) const; + QPolygonF mapToScene(const QPolygon &polygon) const; + QPainterPath mapToScene(const QPainterPath &path) const; + QPoint mapFromScene(const QPointF &point) const; + QPolygon mapFromScene(const QRectF &rect) const; + QPolygon mapFromScene(const QPolygonF &polygon) const; + QPainterPath mapFromScene(const QPainterPath &path) const; + inline QPointF mapToScene(int x, int y) const; + inline QPolygonF mapToScene(int x, int y, int w, int h) const; + inline QPoint mapFromScene(qreal x, qreal y) const; + inline QPolygon mapFromScene(qreal x, qreal y, qreal w, qreal h) const; + + QVariant inputMethodQuery(Qt::InputMethodQuery query) const; + + QBrush backgroundBrush() const; + void setBackgroundBrush(const QBrush &brush); + + QBrush foregroundBrush() const; + void setForegroundBrush(const QBrush &brush); + +public Q_SLOTS: + void updateScene(const QList<QRectF> &rects); + void invalidateScene(const QRectF &rect = QRectF(), QGraphicsScene::SceneLayers layers = QGraphicsScene::AllLayers); + void updateSceneRect(const QRectF &rect); + +protected Q_SLOTS: + void setupViewport(QWidget *widget); + +protected: + QGraphicsView(QGraphicsViewPrivate &, QWidget *parent = 0); + bool event(QEvent *event); + bool viewportEvent(QEvent *event); + +#ifndef QT_NO_CONTEXTMENU + void contextMenuEvent(QContextMenuEvent *event); +#endif + void dragEnterEvent(QDragEnterEvent *event); + void dragLeaveEvent(QDragLeaveEvent *event); + void dragMoveEvent(QDragMoveEvent *event); + void dropEvent(QDropEvent *event); + void focusInEvent(QFocusEvent *event); + bool focusNextPrevChild(bool next); + void focusOutEvent(QFocusEvent *event); + void keyPressEvent(QKeyEvent *event); + void keyReleaseEvent(QKeyEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event); +#ifndef QT_NO_WHEELEVENT + void wheelEvent(QWheelEvent *event); +#endif + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + void scrollContentsBy(int dx, int dy); + void showEvent(QShowEvent *event); + void inputMethodEvent(QInputMethodEvent *event); + + virtual void drawBackground(QPainter *painter, const QRectF &rect); + virtual void drawForeground(QPainter *painter, const QRectF &rect); + virtual void drawItems(QPainter *painter, int numItems, + QGraphicsItem *items[], + const QStyleOptionGraphicsItem options[]); + +private: + Q_DECLARE_PRIVATE(QGraphicsView) + Q_DISABLE_COPY(QGraphicsView) +#ifndef QT_NO_CURSOR + Q_PRIVATE_SLOT(d_func(), void _q_setViewportCursor(const QCursor &)) + Q_PRIVATE_SLOT(d_func(), void _q_unsetViewportCursor()) +#endif + Q_PRIVATE_SLOT(d_func(), void _q_updateLaterSlot()) + friend class QGraphicsSceneWidget; + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::CacheMode) +Q_DECLARE_OPERATORS_FOR_FLAGS(QGraphicsView::OptimizationFlags) + +inline void QGraphicsView::setSceneRect(qreal ax, qreal ay, qreal aw, qreal ah) +{ setSceneRect(QRectF(ax, ay, aw, ah)); } +inline void QGraphicsView::centerOn(qreal ax, qreal ay) +{ centerOn(QPointF(ax, ay)); } +inline void QGraphicsView::ensureVisible(qreal ax, qreal ay, qreal aw, qreal ah, int xmargin, int ymargin) +{ ensureVisible(QRectF(ax, ay, aw, ah), xmargin, ymargin); } +inline void QGraphicsView::fitInView(qreal ax, qreal ay, qreal w, qreal h, Qt::AspectRatioMode mode) +{ fitInView(QRectF(ax, ay, w, h), mode); } +inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay) const +{ return items(QPoint(ax, ay)); } +inline QList<QGraphicsItem *> QGraphicsView::items(int ax, int ay, int w, int h, Qt::ItemSelectionMode mode) const +{ return items(QRect(ax, ay, w, h), mode); } +inline QGraphicsItem *QGraphicsView::itemAt(int ax, int ay) const +{ return itemAt(QPoint(ax, ay)); } +inline QPointF QGraphicsView::mapToScene(int ax, int ay) const +{ return mapToScene(QPoint(ax, ay)); } +inline QPolygonF QGraphicsView::mapToScene(int ax, int ay, int w, int h) const +{ return mapToScene(QRect(ax, ay, w, h)); } +inline QPoint QGraphicsView::mapFromScene(qreal ax, qreal ay) const +{ return mapFromScene(QPointF(ax, ay)); } +inline QPolygon QGraphicsView::mapFromScene(qreal ax, qreal ay, qreal w, qreal h) const +{ return mapFromScene(QRectF(ax, ay, w, h)); } + +#endif // QT_NO_GRAPHICSVIEW + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QGRAPHICSVIEW_H diff --git a/src/gui/graphicsview/qgraphicsview_p.h b/src/gui/graphicsview/qgraphicsview_p.h new file mode 100644 index 0000000..2109673 --- /dev/null +++ b/src/gui/graphicsview/qgraphicsview_p.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSVIEW_P_H +#define QGRAPHICSVIEW_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qgraphicsview.h" + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +#include <QtGui/qevent.h> +#include "qgraphicssceneevent.h" +#include <private/qabstractscrollarea_p.h> + +QT_BEGIN_NAMESPACE + +class Q_GUI_EXPORT QGraphicsViewPrivate : public QAbstractScrollAreaPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsView) +public: + QGraphicsViewPrivate(); + + void recalculateContentSize(); + void centerView(QGraphicsView::ViewportAnchor anchor); + + QPainter::RenderHints renderHints; + + QGraphicsView::DragMode dragMode; + bool sceneInteractionAllowed; + QRectF sceneRect; + bool hasSceneRect; + void updateLastCenterPoint(); + bool connectedToScene; + + qint64 horizontalScroll() const; + qint64 verticalScroll() const; + + QList<QGraphicsItem *> itemsInArea(const QPainterPath &path, + Qt::ItemSelectionMode mode = Qt::IntersectsItemShape) const; + + QPointF mousePressItemPoint; + QPointF mousePressScenePoint; + QPoint mousePressViewPoint; + QPoint mousePressScreenPoint; + QPointF lastMouseMoveScenePoint; + QPoint lastMouseMoveScreenPoint; + Qt::MouseButton mousePressButton; + QTransform matrix; + bool identityMatrix; + qint64 scrollX, scrollY; + bool dirtyScroll; + void updateScroll(); + + bool accelerateScrolling; + qreal leftIndent; + qreal topIndent; + + // Replaying mouse events + QMouseEvent lastMouseEvent; + bool useLastMouseEvent; + void replayLastMouseEvent(); + void storeMouseEvent(QMouseEvent *event); + void mouseMoveEventHandler(QMouseEvent *event); + + QPointF lastCenterPoint; + bool keepLastCenterPoint; + Qt::Alignment alignment; + bool transforming; + + QGraphicsView::ViewportAnchor transformationAnchor; + QGraphicsView::ViewportAnchor resizeAnchor; + QGraphicsView::ViewportUpdateMode viewportUpdateMode; + QGraphicsView::OptimizationFlags optimizationFlags; + + QPointer<QGraphicsScene> scene; +#ifndef QT_NO_RUBBERBAND + QRect rubberBandRect; + QRegion rubberBandRegion(const QWidget *widget, const QRect &rect) const; + bool rubberBanding; + Qt::ItemSelectionMode rubberBandSelectionMode; +#endif + bool handScrolling; + int handScrollMotions; + + QGraphicsView::CacheMode cacheMode; + + QVector<QStyleOptionGraphicsItem> styleOptions; + bool mustAllocateStyleOptions; + QStyleOptionGraphicsItem *allocStyleOptionsArray(int numItems); + void freeStyleOptionsArray(QStyleOptionGraphicsItem *array); + + QBrush backgroundBrush; + QBrush foregroundBrush; + QPixmap backgroundPixmap; + bool mustResizeBackgroundPixmap; + QRegion backgroundPixmapExposed; + +#ifndef QT_NO_CURSOR + QCursor originalCursor; + bool hasStoredOriginalCursor; + void _q_setViewportCursor(const QCursor &cursor); + void _q_unsetViewportCursor(); +#endif + + QGraphicsSceneDragDropEvent *lastDragDropEvent; + void storeDragDropEvent(const QGraphicsSceneDragDropEvent *event); + void populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest, + QDropEvent *source); + + QRect mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const; + QRegion mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const; + void itemUpdated(QGraphicsItem *item, const QRectF &rect); + bool fullUpdatePending; + QList<QRect> dirtyRects; + QList<QRegion> dirtyRegions; + int dirtyRectCount; + QRect dirtyBoundingRect; + void updateLater(); + bool updatingLater; + void _q_updateLaterSlot(); + void updateAll(); + void updateRect(const QRect &rect); + void updateRegion(const QRegion ®ion); + bool updateSceneSlotReimplementedChecked; + + QList<QGraphicsItem *> findItems(const QRegion &exposedRegion, + const QTransform &worldTransform, + bool *allItems) const; + + void generateStyleOptions(const QList<QGraphicsItem *> &itemList, + QGraphicsItem **itemArray, + QStyleOptionGraphicsItem *styleOptionArray, + const QTransform &worldTransform, + bool allItems, + const QRegion &exposedRegion) const; +}; + +QT_END_NAMESPACE + +#endif // QT_NO_GRAPHICSVIEW + +#endif diff --git a/src/gui/graphicsview/qgraphicswidget.cpp b/src/gui/graphicsview/qgraphicswidget.cpp new file mode 100644 index 0000000..5cc18f9 --- /dev/null +++ b/src/gui/graphicsview/qgraphicswidget.cpp @@ -0,0 +1,2273 @@ +/**************************************************************************** +** +** 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 "qgraphicswidget.h" +#include "qgraphicswidget_p.h" +#include "qgraphicslayout.h" +#include "qgraphicslayout_p.h" +#include "qgraphicsscene.h" +#include "qgraphicssceneevent.h" + +#ifndef QT_NO_ACTION +#include <private/qaction_p.h> +#endif +#include <private/qapplication_p.h> +#include <private/qgraphicsscene_p.h> +#ifndef QT_NO_SHORTCUT +#include <private/qshortcutmap_p.h> +#endif +#include <QtCore/qmutex.h> +#include <QtGui/qapplication.h> +#include <QtGui/qgraphicsview.h> +#include <QtGui/qgraphicsproxywidget.h> +#include <QtGui/qpalette.h> +#include <QtGui/qstyleoption.h> + +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QGraphicsWidget + \brief The QGraphicsWidget class is the base class for all widget + items in a QGraphicsScene. + \since 4.4 + \ingroup multimedia + \ingroup graphicsview-api + + QGraphicsWidget is an extended base item that provides extra functionality + over QGraphicsItem. It is similar to QWidget in many ways: + + \list + \o Provides a \l palette, a \l font and a \l style(). + \o Has a defined geometry(). + \o Supports layouts with setLayout() and layout(). + \o Supports shortcuts and actions with grabShortcut() and insertAction() + \endlist + + Unlike QGraphicsItem, QGraphicsWidget is not an abstract class; you can + create instances of a QGraphicsWidget without having to subclass it. + This approach is useful for widgets that only serve the purpose of + organizing child widgets into a layout. + + QGraphicsWidget can be used as a base item for your own custom item if + you require advanced input focus handling, e.g., tab focus and activation, or + layouts. + + Since QGraphicsWidget resembles QWidget and has similar API, it is + easier to port a widget from QWidget to QGraphicsWidget, instead of + QGraphicsItem. + + \note QWidget-based widgets can be directly embedded into a + QGraphicsScene using QGraphicsProxyWidget. + + Noticeable differences between QGraphicsWidget and QWidget are: + + \table + \header \o QGraphicsWidget + \o QWidget + \row \o Coordinates and geometry are defined with qreals (doubles or + floats, depending on the platform). + \o QWidget uses integer geometry (QPoint, QRect). + \row \o The widget is already visible by default; you do not have to + call show() to display the widget. + \o QWidget is hidden by default until you call show(). + \row \o A subset of widget attributes are supported. + \o All widget attributes are supported. + \row \o A top-level item's style defaults to QGraphicsScene::style + \o A top-level widget's style defaults to QApplication::style + \row \o Graphics View provides a custom drag and drop framework, different + from QWidget. + \o Standard drag and drop framework. + \row \o Widget items do not support modality. + \o Full modality support. + \endtable + + QGraphicsWidget supports a subset of Qt's widget attributes, + (Qt::WidgetAttribute), as shown in the table below. Any attributes not + listed in this table are unsupported, or otherwise unused. + + \table + \header \o Widget Attribute \o Usage + \row \o Qt::WA_SetLayoutDirection + \o Set by setLayoutDirection(), cleared by + unsetLayoutDirection(). You can test this attribute to + check if the widget has been explicitly assigned a + \l{QGraphicsWidget::layoutDirection()} + {layoutDirection}. If the attribute is not set, the + \l{QGraphicsWidget::layoutDirection()} + {layoutDirection()} is inherited. + \row \o Qt::WA_RightToLeft + \o Toggled by setLayoutDirection(). Inherited from the + parent/scene. If set, the widget's layout will order + horizontally arranged widgets from right to left. + \row \o Qt::WA_SetStyle + \o Set and cleared by setStyle(). If this attribute is + set, the widget has been explicitly assigned a style. + If it is unset, the widget will use the scene's or the + application's style. + \row \o Qt::WA_Resized + \o Set by setGeometry() and resize(). + \row \o Qt::WA_SetPalette + \o Set by setPalette(). + \row \o Qt::WA_SetFont + \o Set by setPalette(). + \row \o Qt::WA_WindowPropagation + \o Enables propagation to window widgets. + \endtable + + Although QGraphicsWidget inherits from both QObject and QGraphicsItem, + you should use the functions provided by QGraphicsItem, \e not QObject, to + manage the relationships between parent and child items. These functions + control the stacking order of items as well as their ownership. + + \note The QObject::parent() should always return 0 for QGraphicsWidgets, + but this policy is not strictly defined. + + \sa QGraphicsProxyWidget, QGraphicsItem, {Widgets and Layouts} +*/ + +/*! + \property QGraphicsWidget::enabled + \brief whether the item is enabled or not + + This property is declared in QGraphicsItem. + + By default, this property is true. + + \sa QGraphicsItem::isEnabled(), QGraphicsItem::setEnabled() +*/ + +/*! + \property QGraphicsWidget::visible + \brief whether the item is visible or not + + This property is declared in QGraphicsItem. + + By default, this property is true. + + \sa QGraphicsItem::isVisible(), QGraphicsItem::setVisible(), show(), + hide() +*/ + +/*! + \property QGraphicsWidget::opacity + \brief the opacity of the widget +*/ + +/*! + \property QGraphicsWidget::pos + \brief the position of the widget +*/ + +/*! + Constructs a QGraphicsWidget instance. The optional \a parent argument is + passed to QGraphicsItem's constructor. The optional \a wFlags argument + specifies the widget's window flags (e.g., whether the widget should be a + window, a tool, a popup, etc). +*/ +QGraphicsWidget::QGraphicsWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) + : QGraphicsItem(*new QGraphicsWidgetPrivate, 0, 0), QGraphicsLayoutItem(0, false) +{ + Q_D(QGraphicsWidget); + d->init(parent, wFlags); +} + +/*! + \internal + + Constructs a new QGraphicsWidget, using \a dd as parent. +*/ +QGraphicsWidget::QGraphicsWidget(QGraphicsWidgetPrivate &dd, QGraphicsItem *parent, QGraphicsScene *scene, Qt::WindowFlags wFlags) + : QGraphicsItem(dd, 0, scene), QGraphicsLayoutItem(0, false) +{ + Q_D(QGraphicsWidget); + d->init(parent, wFlags); +} + +/* + \internal + \class QGraphicsWidgetStyles + + We use this thread-safe class to maintain a hash of styles for widgets + styles. Note that QApplication::style() itself isn't thread-safe, QStyle + isn't thread-safe, and we don't have a thread-safe factory for creating + the default style, nor cloning a style. +*/ +class QGraphicsWidgetStyles +{ +public: + QStyle *styleForWidget(const QGraphicsWidget *widget) const + { + QMutexLocker locker(&mutex); + return styles.value(widget, 0); + } + + void setStyleForWidget(QGraphicsWidget *widget, QStyle *style) + { + QMutexLocker locker(&mutex); + if (style) + styles[widget] = style; + else + styles.remove(widget); + } + +private: + QMap<const QGraphicsWidget *, QStyle *> styles; + mutable QMutex mutex; +}; +Q_GLOBAL_STATIC(QGraphicsWidgetStyles, widgetStyles) + +/*! + Destroys the QGraphicsWidget instance. +*/ +QGraphicsWidget::~QGraphicsWidget() +{ + Q_D(QGraphicsWidget); +#ifndef QT_NO_ACTION + // Remove all actions from this widget + for (int i = 0; i < d->actions.size(); ++i) { + QActionPrivate *apriv = d->actions.at(i)->d_func(); + apriv->graphicsWidgets.removeAll(this); + } + d->actions.clear(); +#endif + + if (QGraphicsScene *scn = scene()) { + QGraphicsScenePrivate *sceneD = scn->d_func(); + if (sceneD->tabFocusFirst == this) + sceneD->tabFocusFirst = (d->focusNext == this ? 0 : d->focusNext); + } + d->focusPrev->d_func()->focusNext = d->focusNext; + d->focusNext->d_func()->focusPrev = d->focusPrev; + + // Play it really safe + d->focusNext = this; + d->focusPrev = this; + + clearFocus(); + + //we check if we have a layout previously + if (d->layout) { + delete d->layout; + foreach (QGraphicsItem * item, childItems()) { + // In case of a custom layout which doesn't remove and delete items, we ensure that + // the parent layout item does not point to the deleted layout. This code is here to + // avoid regression from 4.4 to 4.5, because according to 4.5 docs it is not really needed. + if (item->isWidget()) { + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item); + if (widget->parentLayoutItem() == d->layout) + widget->setParentLayoutItem(0); + } + } + } + + // Remove this graphics widget from widgetStyles + widgetStyles()->setStyleForWidget(this, 0); +} + +/*! + \property QGraphicsWidget::size + \brief the size of the widget + + Calling resize() resizes the widget to a \a size bounded by minimumSize() + and maximumSize(). This property only affects the widget's width and + height (e.g., its right and bottom edges); the widget's position and + top-left corner remains unaffected. + + Resizing a widget triggers the widget to immediately receive a + \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} event with the + widget's old and new size. If the widget has a layout assigned when this + event arrives, the layout will be activated and it will automatically + update any child widgets's geometry. + + This property does not affect any layout of the parent widget. If the + widget itself is managed by a parent layout; e.g., it has a parent widget + with a layout assigned, that layout will not activate. + + By default, this property contains a size with zero width and height. + + \sa setGeometry(), QGraphicsSceneResizeEvent, QGraphicsLayout +*/ +QSizeF QGraphicsWidget::size() const +{ + return QGraphicsLayoutItem::geometry().size(); +} + +void QGraphicsWidget::resize(const QSizeF &size) +{ + setGeometry(QRectF(pos(), size)); +} + +/*! + \fn void QGraphicsWidget::resize(qreal w, qreal h) + + This convenience function is equivalent to calling resize(QSizeF(w, h)). + + \sa setGeometry(), setTransform() +*/ + +/*! + \property QGraphicsWidget::geometry + \brief the geometry of the widget + + Sets the item's geometry to \a rect. The item's position and size are + modified as a result of calling this function. The item is first moved, + then resized. + + A side effect of calling this function is that the widget will receive + a move event and a resize event. Also, if the widget has a layout + assigned, the layout will activate. + + \sa geometry(), resize() +*/ +void QGraphicsWidget::setGeometry(const QRectF &rect) +{ + QGraphicsWidgetPrivate *wd = QGraphicsWidget::d_func(); + const QGraphicsLayoutItemPrivate *d = QGraphicsLayoutItem::d_ptr; + setAttribute(Qt::WA_Resized); + QRectF newGeom = rect; + newGeom.setSize(rect.size().expandedTo(effectiveSizeHint(Qt::MinimumSize)) + .boundedTo(effectiveSizeHint(Qt::MaximumSize))); + if (newGeom == d->geom) + return; + + // Update and prepare to change the geometry (remove from index). + if (wd->scene) { + if (rect.topLeft() != d->geom.topLeft()) + wd->fullUpdateHelper(true); + else + update(); + } + prepareGeometryChange(); + + // setPos triggers ItemPositionChange, which can adjust position + QPointF oldPos = d->geom.topLeft(); + wd->inSetGeometry = 1; + wd->setPosHelper(newGeom.topLeft(), /* update = */ false); + wd->inSetGeometry = 0; + newGeom.moveTopLeft(pos()); + + if (newGeom == d->geom) + return; + + // Update the layout item geometry + bool moved = oldPos != pos(); + if (moved) { + // Send move event. + QGraphicsSceneMoveEvent event; + event.setOldPos(oldPos); + event.setNewPos(pos()); + QApplication::sendEvent(this, &event); + } + QSizeF oldSize = size(); + QGraphicsLayoutItem::setGeometry(newGeom); + + // Send resize event + bool resized = newGeom.size() != oldSize; + if (resized) { + QGraphicsSceneResizeEvent re; + re.setOldSize(oldSize); + re.setNewSize(newGeom.size()); + QApplication::sendEvent(this, &re); + } +} + +/*! + \fn QRectF QGraphicsWidget::rect() const + + Returns the item's local rect as a QRectF. This function is equivalent + to QRectF(QPointF(), size()). + + \sa setGeometry(), resize() +*/ + +/*! + \fn void QGraphicsWidget::setGeometry(qreal x, qreal y, qreal w, qreal h) + + This convenience function is equivalent to calling setGeometry(QRectF( + \a x, \a y, \a w, \a h)). + + \sa geometry(), resize() +*/ + +/*! + Sets the widget's contents margins to \a left, \a top, \a right and \a + bottom. + + Contents margins are used by the assigned layout to define the placement + of subwidgets and layouts. Margins are particularily useful for widgets + that constrain subwidgets to only a section of its own geometry. For + example, a group box with a layout will place subwidgets inside its frame, + but below the title. + + Changing a widget's contents margins will always trigger an update(), and + any assigned layout will be activated automatically. The widget will then + receive a \l{QEvent::ContentsRectChange}{ContentsRectChange} event. + + \sa getContentsMargins(), setGeometry() +*/ +void QGraphicsWidget::setContentsMargins(qreal left, qreal top, qreal right, qreal bottom) +{ + Q_D(QGraphicsWidget); + + if (left == d->leftMargin + && top == d->topMargin + && right == d->rightMargin + && bottom == d->bottomMargin) { + return; + } + + d->leftMargin = left; + d->topMargin = top; + d->rightMargin = right; + d->bottomMargin = bottom; + + if (QGraphicsLayout *l = d->layout) + l->invalidate(); + else + updateGeometry(); + + QEvent e(QEvent::ContentsRectChange); + QApplication::sendEvent(this, &e); +} + +/*! + Gets the widget's contents margins. The margins are stored in \a left, \a + top, \a right and \a bottom, as pointers to qreals. Each argument can + be \e {omitted} by passing 0. + + \sa setContentsMargins() +*/ +void QGraphicsWidget::getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +{ + Q_D(const QGraphicsWidget); + if (left) + *left = d->leftMargin; + if (top) + *top = d->topMargin; + if (right) + *right = d->rightMargin; + if (bottom) + *bottom = d->bottomMargin; +} + +/*! + Sets the widget's window frame margins to \a left, \a top, \a right and + \a bottom. The default frame margins are provided by the style, and they + depend on the current window flags. + + If you would like to draw your own window decoration, you can set your + own frame margins to override the default margins. + + \sa unsetWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect() +*/ +void QGraphicsWidget::setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom) +{ + Q_D(QGraphicsWidget); + bool unchanged = left == d->leftWindowFrameMargin && top == d->topWindowFrameMargin + && right == d->rightWindowFrameMargin && bottom == d->bottomWindowFrameMargin; + if (d->setWindowFrameMargins && unchanged) + return; + if (!unchanged) + prepareGeometryChange(); + d->leftWindowFrameMargin = left; + d->topWindowFrameMargin = top; + d->rightWindowFrameMargin = right; + d->bottomWindowFrameMargin = bottom; + d->setWindowFrameMargins = true; +} + +/*! + Gets the widget's window frame margins. The margins are stored in \a left, + \a top, \a right and \a bottom as pointers to qreals. Each argument can + be \e {omitted} by passing 0. + + \sa setWindowFrameMargins(), windowFrameRect() +*/ +void QGraphicsWidget::getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +{ + Q_D(const QGraphicsWidget); + if (left) + *left = d->leftWindowFrameMargin; + if (top) + *top = d->topWindowFrameMargin; + if (right) + *right = d->rightWindowFrameMargin; + if (bottom) + *bottom = d->bottomWindowFrameMargin; +} + +/*! + Resets the window frame margins to the default value, provided by the style. + + \sa setWindowFrameMargins(), getWindowFrameMargins(), windowFrameRect() +*/ +void QGraphicsWidget::unsetWindowFrameMargins() +{ + Q_D(QGraphicsWidget); + if ((d->windowFlags & Qt::Window) && (d->windowFlags & Qt::WindowType_Mask) != Qt::Popup && + (d->windowFlags & Qt::WindowType_Mask) != Qt::ToolTip && !(d->windowFlags & Qt::FramelessWindowHint)) { + QStyleOptionTitleBar bar; + d->initStyleOptionTitleBar(&bar); + QStyle *style = this->style(); + qreal margin = style->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth); + qreal titleBarHeight = d->titleBarHeight(bar); + setWindowFrameMargins(margin, titleBarHeight, margin, margin); + } else { + setWindowFrameMargins(0, 0, 0, 0); + } + d->setWindowFrameMargins = false; +} + +/*! + Returns the widget's geometry in parent coordinates including any window + frame. + + \sa windowFrameRect(), getWindowFrameMargins(), setWindowFrameMargins() +*/ +QRectF QGraphicsWidget::windowFrameGeometry() const +{ + Q_D(const QGraphicsWidget); + return geometry().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, + d->rightWindowFrameMargin, d->bottomWindowFrameMargin); +} + +/*! + Returns the widget's local rect including any window frame. + + \sa windowFrameGeometry(), getWindowFrameMargins(), setWindowFrameMargins() +*/ +QRectF QGraphicsWidget::windowFrameRect() const +{ + Q_D(const QGraphicsWidget); + return rect().adjusted(-d->leftWindowFrameMargin, -d->topWindowFrameMargin, + d->rightWindowFrameMargin, d->bottomWindowFrameMargin); +} + +/*! + Populates a style option object for this widget based on its current + state, and stores the output in \a option. The default implementation + populates \a option with the following properties. + + \table + \header + \o Style Option Property + \o Value + \row + \o state & QStyle::State_Enabled + \o Corresponds to QGraphicsItem::isEnabled(). + \row + \o state & QStyle::State_HasFocus + \o Corresponds to QGraphicsItem::hasFocus(). + \row + \o state & QStyle::State_MouseOver + \o Corresponds to QGraphicsItem::isUnderMouse(). + \row + \o direction + \o Corresponds to QGraphicsWidget::layoutDirection(). + \row + \o rect + \o Corresponds to QGraphicsWidget::rect().toRect(). + \row + \o palette + \o Corresponds to QGraphicsWidget::palette(). + \row + \o fontMetrics + \o Corresponds to QFontMetrics(QGraphicsWidget::font()). + \endtable + + Subclasses of QGraphicsWidget should call the base implementation, and + then test the type of \a option using qstyleoption_cast<>() or test + QStyleOption::Type before storing widget-specific options. + + For example: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 0 + + \sa QStyleOption::initFrom() +*/ +void QGraphicsWidget::initStyleOption(QStyleOption *option) const +{ + Q_ASSERT(option); + + option->state = QStyle::State_None; + if (isEnabled()) + option->state |= QStyle::State_Enabled; + if (hasFocus()) + option->state |= QStyle::State_HasFocus; + // if (window->testAttribute(Qt::WA_KeyboardFocusChange)) // ### Window + // option->state |= QStyle::State_KeyboardFocusChange; + if (isUnderMouse()) + option->state |= QStyle::State_MouseOver; + if (QGraphicsWidget *w = window()) { + if (w->isActiveWindow()) + option->state |= QStyle::State_Active; + } + if (isWindow()) + option->state |= QStyle::State_Window; + /* + ### +#ifdef Q_WS_MAC + extern bool qt_mac_can_clickThrough(const QGraphicsWidget *w); //qwidget_mac.cpp + if (!(option->state & QStyle::State_Active) && !qt_mac_can_clickThrough(widget)) + option->state &= ~QStyle::State_Enabled; + + switch (QMacStyle::widgetSizePolicy(widget)) { + case QMacStyle::SizeSmall: + option->state |= QStyle::State_Small; + break; + case QMacStyle::SizeMini: + option->state |= QStyle::State_Mini; + break; + default: + ; + } +#endif +#ifdef QT_KEYPAD_NAVIGATION + if (widget->hasEditFocus()) + state |= QStyle::State_HasEditFocus; +#endif + */ + option->direction = layoutDirection(); + option->rect = rect().toRect(); // ### truncation! + option->palette = palette(); + if (!isEnabled()) { + option->palette.setCurrentColorGroup(QPalette::Disabled); + } else if (isActiveWindow()) { + option->palette.setCurrentColorGroup(QPalette::Active); + } else { + option->palette.setCurrentColorGroup(QPalette::Inactive); + } + option->fontMetrics = QFontMetrics(font()); +} + +/*! + \reimp +*/ +QSizeF QGraphicsWidget::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + Q_D(const QGraphicsWidget); + QSizeF sh; + if (d->layout) { + sh = d->layout->effectiveSizeHint(which, constraint); + sh += QSizeF(d->leftMargin + d->rightMargin, d->topMargin + d->bottomMargin); + } else { + switch (which) { + case Qt::MinimumSize: + sh = QSizeF(0, 0); + break; + case Qt::PreferredSize: + sh = QSizeF(50, 50); //rather arbitrary + break; + case Qt::MaximumSize: + sh = QSizeF(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); + break; + default: + qWarning("QGraphicsWidget::sizeHint(): Don't know how to handle the value of 'which'"); + break; + } + } + return sh; +} + +/*! + Returns this widget's layout, or 0 if no layout is currently managing this + widget. + + \sa setLayout() +*/ +QGraphicsLayout *QGraphicsWidget::layout() const +{ + Q_D(const QGraphicsWidget); + return d->layout; +} + +/*! + \fn void QGraphicsWidget::setLayout(QGraphicsLayout *layout) + + Sets the layout for this widget to \a layout. Any existing layout manager + is deleted before the new layout is assigned. If \a layout is 0, the + widget is left without a layout. Existing subwidgets' geometries will + remain unaffected. + + All widgets that are currently managed by \a layout or all of its + sublayouts, are automatically reparented to this item. The layout is then + invalidated, and the child widget geometries are adjusted according to + this item's geometry() and contentsMargins(). Children who are not + explicitly managed by \a layout remain unaffected by the layout after + it has been assigned to this widget. + + QGraphicsWidget takes ownership of \a layout. + + \sa layout(), QGraphicsLinearLayout::addItem(), QGraphicsLayout::invalidate() +*/ +void QGraphicsWidget::setLayout(QGraphicsLayout *l) +{ + Q_D(QGraphicsWidget); + if (d->layout == l) + return; + d->setLayout_helper(l); + if (!l) + return; + + // Prevent assigning a layout that is already assigned to another widget. + QGraphicsLayoutItem *oldParent = l->parentLayoutItem(); + if (oldParent && oldParent != this) { + qWarning("QGraphicsWidget::setLayout: Attempting to set a layout on %s" + " \"%s\", when the layout already has a parent", + metaObject()->className(), qPrintable(objectName())); + return; + } + + // Install and activate the layout. + l->setParentLayoutItem(this); + l->d_func()->reparentChildItems(this); + l->invalidate(); +} + +/*! + Adjusts the size of the widget to its effective preferred size hint. + + This function is called implicitly when the item is shown for the first + time. + + \sa effectiveSizeHint(), Qt::MinimumSize +*/ +void QGraphicsWidget::adjustSize() +{ + QSizeF sz = effectiveSizeHint(Qt::PreferredSize); + // What if sz is not valid?! + if (sz.isValid()) + resize(sz); +} + +/*! + \property QGraphicsWidget::layoutDirection + \brief the layout direction for this widget. + + This property modifies this widget's and all of its descendants' + Qt::WA_RightToLeft attribute. It also sets this widget's + Qt::WA_SetLayoutDirection attribute. + + The widget's layout direction determines the order in which the layout + manager horizontally arranges subwidgets of this widget. The default + value depends on the language and locale of the application, and is + typically in the same direction as words are read and written. With + Qt::LeftToRight, the layout starts placing subwidgets from the left + side of this widget towards the right. Qt::RightToLeft does the opposite - + the layout will place widgets starting from the right edge moving towards + the left. + + Subwidgets inherit their layout direction from the parent. Top-level + widget items inherit their layout direction from + QGraphicsScene::layoutDirection. If you change a widget's layout direction + by calling setLayoutDirection(), the widget will send itself a + \l{QEvent::LayoutDirectionChange}{LayoutDirectionChange} event, and then + propagate the new layout direction to all its descendants. + + \sa QWidget::layoutDirection, QApplication::layoutDirection +*/ +Qt::LayoutDirection QGraphicsWidget::layoutDirection() const +{ + return testAttribute(Qt::WA_RightToLeft) ? Qt::RightToLeft : Qt::LeftToRight; +} +void QGraphicsWidget::setLayoutDirection(Qt::LayoutDirection direction) +{ + Q_D(QGraphicsWidget); + setAttribute(Qt::WA_SetLayoutDirection, true); + d->setLayoutDirection_helper(direction); +} +void QGraphicsWidget::unsetLayoutDirection() +{ + Q_D(QGraphicsWidget); + setAttribute(Qt::WA_SetLayoutDirection, false); + d->resolveLayoutDirection(); +} + +/*! + Returns a pointer to the widget's style. If this widget does not have any + explicitly assigned style, the scene's style is returned instead. In turn, + if the scene does not have any assigned style, this function returns + QApplication::style(). + + \sa setStyle() +*/ +QStyle *QGraphicsWidget::style() const +{ + if (QStyle *style = widgetStyles()->styleForWidget(this)) + return style; + // ### This is not thread-safe. QApplication::style() is not thread-safe. + return scene() ? scene()->style() : QApplication::style(); +} + +/*! + Sets the widget's style to \a style. QGraphicsWidget does \e not take + ownership of \a style. + + If no style is assigned, or \a style is 0, the widget will use + QGraphicsScene::style() (if this has been set). Otherwise the widget will + use QApplication::style(). + + This function sets the Qt::WA_SetStyle attribute if \a style is not 0; + otherwise it clears the attribute. + + \sa style() +*/ +void QGraphicsWidget::setStyle(QStyle *style) +{ + setAttribute(Qt::WA_SetStyle, style != 0); + widgetStyles()->setStyleForWidget(this, style); + + // Deliver StyleChange to the widget itself (doesn't propagate). + QEvent event(QEvent::StyleChange); + QApplication::sendEvent(this, &event); +} + +/*! + \property QGraphicsWidget::font + \brief the widgets' font + + This property provides the widget's font. + + QFont consists of font properties that have been explicitly defined and + properties implicitly inherited from the widget's parent. Hence, font() + can return a different font compared to the one set with setFont(). + This scheme allows you to define single entries in a font without + affecting the font's inherited entries. + + When a widget's font changes, it resolves its entries against its + parent widget. If the widget does not have a parent widget, it resolves + its entries against the scene. The widget then sends itself a + \l{QEvent::FontChange}{FontChange} event and notifies all its + descendants so that they can resolve their fonts as well. + + By default, this property contains the application's default font. + + \sa QApplication::font(), QGraphicsScene::font, QFont::resolve() +*/ +QFont QGraphicsWidget::font() const +{ + Q_D(const QGraphicsWidget); + return d->font; +} +void QGraphicsWidget::setFont(const QFont &font) +{ + Q_D(QGraphicsWidget); + setAttribute(Qt::WA_SetFont, font.resolve() != 0); + + QFont naturalFont = d->naturalWidgetFont(); + QFont resolvedFont = font.resolve(naturalFont); + d->setFont_helper(resolvedFont); +} + +/*! + \property QGraphicsWidget::palette + \brief the widget's palette + + This property provides the widget's palette. The palette provides colors + and brushes for color groups (e.g., QPalette::Button) and states (e.g., + QPalette::Inactive), loosely defining the general look of the widget and + its children. + + QPalette consists of color groups that have been explicitly defined, and + groups that are implicitly inherited from the widget's parent. Because of + this, palette() can return a different palette than what has been set with + setPalette(). This scheme allows you to define single entries in a palette + without affecting the palette's inherited entries. + + When a widget's palette changes, it resolves its entries against its + parent widget, or if it doesn't have a parent widget, it resolves against + the scene. It then sends itself a \l{QEvent::PaletteChange}{PaletteChange} + event, and notifies all its descendants so they can resolve their palettes + as well. + + By default, this property contains the application's default palette. + + \sa QApplication::palette(), QGraphicsScene::palette, QPalette::resolve() +*/ +QPalette QGraphicsWidget::palette() const +{ + Q_D(const QGraphicsWidget); + return d->palette; +} +void QGraphicsWidget::setPalette(const QPalette &palette) +{ + Q_D(QGraphicsWidget); + setAttribute(Qt::WA_SetPalette, palette.resolve() != 0); + + QPalette naturalPalette = d->naturalWidgetPalette(); + QPalette resolvedPalette = palette.resolve(naturalPalette); + d->setPalette_helper(resolvedPalette); +} + +/*! + If this widget is currently managed by a layout, this function notifies + the layout that the widget's size hints have changed and the layout + may need to resize and reposition the widget accordingly. + + Call this function if the widget's sizeHint() has changed. + + \sa QGraphicsLayout::invalidate() +*/ +void QGraphicsWidget::updateGeometry() +{ + QGraphicsLayoutItem::updateGeometry(); + QGraphicsLayoutItem *parentItem = parentLayoutItem(); + + if (parentItem && parentItem->isLayout()) { + parentItem->updateGeometry(); + } else { + if (parentItem) { + QGraphicsWidget *parentWid = parentWidget(); //### + if (parentWid->isVisible()) + QApplication::postEvent(parentWid, new QEvent(QEvent::LayoutRequest)); + } + bool wasResized = testAttribute(Qt::WA_Resized); + resize(size()); // this will restrict the size + setAttribute(Qt::WA_Resized, wasResized); + } +} + +/*! + \reimp + + QGraphicsWidget uses the base implementation of this function to catch and + deliver events related to state changes in the item. Because of this, it is + very important that subclasses call the base implementation. + + For example, QGraphicsWidget uses ItemVisibleChange to deliver \l Show and + \l Hide events, ItemPositionHasChanged to deliver \l Move events, and + ItemParentChange both to deliver \l ParentChange events, and for managing + the focus chain. + + \sa propertyChange() +*/ +QVariant QGraphicsWidget::itemChange(GraphicsItemChange change, const QVariant &value) +{ + Q_D(QGraphicsWidget); + switch (change) { + case ItemEnabledHasChanged: { + // Send EnabledChange after the enabled state has changed. + QEvent event(QEvent::EnabledChange); + QApplication::sendEvent(this, &event); + break; + } + case ItemVisibleChange: + if (value.toBool()) { + // Send Show event before the item has been shown. + QShowEvent event; + QApplication::sendEvent(this, &event); + bool resized = testAttribute(Qt::WA_Resized); + if (!resized) { + adjustSize(); + setAttribute(Qt::WA_Resized, false); + } + } + break; + case ItemVisibleHasChanged: + if (!value.toBool()) { + // Send Hide event after the item has been hidden. + QHideEvent event; + QApplication::sendEvent(this, &event); + } + break; + case ItemPositionHasChanged: + if (!d->inSetGeometry) { + // Ensure setGeometry is called (avoid recursion when setPos is + // called from within setGeometry). + setGeometry(QRectF(pos(), size())); + } + break; + case ItemParentChange: { + QGraphicsItem *parent = qVariantValue<QGraphicsItem *>(value); + d->fixFocusChainBeforeReparenting((parent && parent->isWidget()) ? static_cast<QGraphicsWidget *>(parent) : 0); + + // Deliver ParentAboutToChange. + QEvent event(QEvent::ParentAboutToChange); + QApplication::sendEvent(this, &event); + break; + } + case ItemParentHasChanged: { + // reset window type on parent change in order to automagically remove decorations etc. + Qt::WindowFlags wflags = d->windowFlags & ~Qt::WindowType_Mask; + d->adjustWindowFlags(&wflags); + setWindowFlags(wflags); + // Deliver ParentChange. + QEvent event(QEvent::ParentChange); + QApplication::sendEvent(this, &event); + break; + } + case ItemCursorChange: { + // Deliver CursorChange. + QEvent event(QEvent::CursorChange); + QApplication::sendEvent(this, &event); + break; + } + case ItemToolTipChange: { + // Deliver ToolTipChange. + QEvent event(QEvent::ToolTipChange); + QApplication::sendEvent(this, &event); + break; + } + default: + break; + } + return QGraphicsItem::itemChange(change, value); +} + +/*! + \internal + + This virtual function is used to notify changes to any property (both + dynamic properties, and registered with Q_PROPERTY) in the + widget. Depending on the property itself, the notification can be + delivered before or after the value has changed. + + \a propertyName is the name of the property (e.g., "size" or "font"), and + \a value is the (proposed) new value of the property. The function returns + the new value, which may be different from \a value if the notification + supports adjusting the property value. The base implementation simply + returns \a value for any \a propertyName. + + QGraphicsWidget delivers notifications for the following properties: + + \table \o propertyName \o Property + \row \o layoutDirection \o QGraphicsWidget::layoutDirection + \row \o size \o QGraphicsWidget::size + \row \o font \o QGraphicsWidget::font + \row \o palette \o QGraphicsWidget::palette + \endtable + + \sa itemChange() +*/ +QVariant QGraphicsWidget::propertyChange(const QString &propertyName, const QVariant &value) +{ + Q_UNUSED(propertyName); + return value; +} + +/*! + QGraphicsWidget's implementation of sceneEvent() simply passes \a event to + QGraphicsWidget::event(). You can handle all events for your widget in + event() or in any of the convenience functions; you should not have to + reimplement this function in a subclass of QGraphicsWidget. + + \sa QGraphicsItem::sceneEvent() +*/ +bool QGraphicsWidget::sceneEvent(QEvent *event) +{ + return QCoreApplication::sendEvent(this, event) || QGraphicsItem::sceneEvent(event); +} + +/*! + This event handler, for \a event, receives events for the window frame if + this widget is a window. Its base implementation provides support for + default window frame interaction such as moving, resizing, etc. + + You can reimplement this handler in a subclass of QGraphicsWidget to + provide your own custom window frame interaction support. + + Returns true if \a event has been recognized and processed; otherwise, + returns false. + + \sa event() +*/ +bool QGraphicsWidget::windowFrameEvent(QEvent *event) +{ + Q_D(QGraphicsWidget); + switch (event->type()) { + case QEvent::GraphicsSceneMousePress: + d->windowFrameMousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneMouseMove: + if (d->grabbedSection != Qt::NoSection) { + d->windowFrameMouseMoveEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + event->accept(); + } + break; + case QEvent::GraphicsSceneMouseRelease: + d->windowFrameMouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverMove: + d->windowFrameHoverMoveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + case QEvent::GraphicsSceneHoverLeave: + d->windowFrameHoverLeaveEvent(static_cast<QGraphicsSceneHoverEvent *>(event)); + break; + default: + break; + } + return event->isAccepted(); +} + +/*! + \since 4.4 + + Returns the window frame section at position \a pos, or + Qt::NoSection if there is no window frame section at this + position. + + This function is used in QGraphicsWidget's base implementation for window + frame interaction. + + You can reimplement this function if you want to customize how a window + can be interactively moved or resized. For instance, if you only want to + allow a window to be resized by the bottom right corner, you can + reimplement this function to return Qt::NoSection for all sections except + Qt::BottomRightSection. + + \sa windowFrameEvent(), paintWindowFrame(), windowFrameGeometry() +*/ +Qt::WindowFrameSection QGraphicsWidget::windowFrameSectionAt(const QPointF &pos) const +{ + Q_D(const QGraphicsWidget); + + const QRectF r = windowFrameRect(); + if (!r.contains(pos)) + return Qt::NoSection; + + const qreal left = r.left(); + const qreal top = r.top(); + const qreal right = r.right(); + const qreal bottom = r.bottom(); + const qreal x = pos.x(); + const qreal y = pos.y(); + + const qreal cornerMargin = 20; + //### Not sure of this one, it should be the same value for all edges. + const qreal windowFrameWidth = d->leftWindowFrameMargin; + + Qt::WindowFrameSection s = Qt::NoSection; + if (x <= left + cornerMargin) { + if (y <= top + windowFrameWidth || (x <= left + windowFrameWidth && y <= top + cornerMargin)) { + s = Qt::TopLeftSection; + } else if (y >= bottom - windowFrameWidth || (x <= left + windowFrameWidth && y >= bottom - windowFrameWidth)) { + s = Qt::BottomLeftSection; + } else if (x <= left + windowFrameWidth) { + s = Qt::LeftSection; + } + } else if (x >= right - cornerMargin) { + if (y <= top + windowFrameWidth || (x >= right - windowFrameWidth && y <= top + cornerMargin)) { + s = Qt::TopRightSection; + } else if (y >= bottom - windowFrameWidth || (x >= right - windowFrameWidth && y >= bottom - windowFrameWidth)) { + s = Qt::BottomRightSection; + } else if (x >= right - windowFrameWidth) { + s = Qt::RightSection; + } + } else if (y <= top + windowFrameWidth) { + s = Qt::TopSection; + } else if (y >= bottom - windowFrameWidth) { + s = Qt::BottomSection; + } + if (s == Qt::NoSection) { + QRectF r1 = r; + r1.setHeight(d->topWindowFrameMargin); + if (r1.contains(pos)) + s = Qt::TitleBarArea; + } + return s; +} + +/*! + \reimp + + QGraphicsWidget handles the following events: + + \table \o Event \o Usage + \row \o Polish + \o Delivered to the widget some time after it has been + shown. + \row \o GraphicsSceneMove + \o Delivered to the widget after its local position has + changed. + \row \o GraphicsSceneResize + \o Delivered to the widget after its size has changed. + \row \o Show + \o Delivered to the widget before it has been shown. + \row \o Hide + \o Delivered to the widget after it has been hidden. + \row \o PaletteChange + \o Delivered to the widget after its palette has changed. + \row \o FontChange + \o Delivered to the widget after its font has changed. + \row \o EnabledChange + \o Delivered to the widget after its enabled state has + changed. + \row \o StyleChange + \o Delivered to the widget after its style has changed. + \row \o LayoutDirectionChange + \o Delivered to the widget after its layout direction has + changed. + \row \o ContentsRectChange + \o Delivered to the widget after its contents margins/ + contents rect has changed. + \endtable +*/ +bool QGraphicsWidget::event(QEvent *event) +{ + Q_D(QGraphicsWidget); + // Forward the event to the layout first. + if (d->layout) + d->layout->widgetEvent(event); + + // Handle the event itself. + switch (event->type()) { + case QEvent::GraphicsSceneMove: + moveEvent(static_cast<QGraphicsSceneMoveEvent *>(event)); + break; + case QEvent::GraphicsSceneResize: + resizeEvent(static_cast<QGraphicsSceneResizeEvent *>(event)); + break; + case QEvent::Show: + showEvent(static_cast<QShowEvent *>(event)); + break; + case QEvent::Hide: + hideEvent(static_cast<QHideEvent *>(event)); + break; + case QEvent::Polish: + polishEvent(); + break; + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + update(); + foreach (QGraphicsItem *child, childItems()) { + if (child->isWidget()) + QApplication::sendEvent(static_cast<QGraphicsWidget *>(child), event); + } + break; + // Taken from QWidget::event + case QEvent::ActivationChange: + case QEvent::EnabledChange: + case QEvent::FontChange: + case QEvent::StyleChange: + case QEvent::PaletteChange: + case QEvent::ParentChange: + case QEvent::ContentsRectChange: + case QEvent::LayoutDirectionChange: + changeEvent(event); + break; + case QEvent::Close: + closeEvent((QCloseEvent *)event); + break; + case QEvent::GrabMouse: + grabMouseEvent(event); + break; + case QEvent::UngrabMouse: + ungrabMouseEvent(event); + break; + case QEvent::GrabKeyboard: + grabKeyboardEvent(event); + break; + case QEvent::UngrabKeyboard: + ungrabKeyboardEvent(event); + break; + case QEvent::GraphicsSceneMousePress: + if (d->hasDecoration() && windowFrameEvent(event)) + return true; + case QEvent::GraphicsSceneMouseMove: + case QEvent::GraphicsSceneMouseRelease: + case QEvent::GraphicsSceneMouseDoubleClick: + if (d->hasDecoration() && d->grabbedSection != Qt::NoSection) + return windowFrameEvent(event); + break; + case QEvent::GraphicsSceneHoverEnter: + case QEvent::GraphicsSceneHoverMove: + case QEvent::GraphicsSceneHoverLeave: + if (d->hasDecoration()) { + windowFrameEvent(event); + // Filter out hover events if they were sent to us only because of the + // decoration (special case in QGraphicsScenePrivate::dispatchHoverEvent). + if (!acceptsHoverEvents()) + return true; + } + break; + default: + break; + } + return QObject::event(event); +} + +/*! + This event handler can be reimplemented to handle state changes. + + The state being changed in this event can be retrieved through \a event. + + Change events include: QEvent::ActivationChange, QEvent::EnabledChange, + QEvent::FontChange, QEvent::StyleChange, QEvent::PaletteChange, + QEvent::ParentChange, QEvent::LayoutDirectionChange, and + QEvent::ContentsRectChange. +*/ +void QGraphicsWidget::changeEvent(QEvent *event) +{ + Q_D(QGraphicsWidget); + switch (event->type()) { + case QEvent::StyleChange: + // ### Don't unset if the margins are explicitly set. + unsetWindowFrameMargins(); + case QEvent::FontChange: + update(); + updateGeometry(); + break; + case QEvent::PaletteChange: + update(); + break; + case QEvent::ParentChange: + d->resolveFont(d->inheritedFontResolveMask); + d->resolvePalette(d->inheritedPaletteResolveMask); + break; + default: + break; + } +} + +/*! + This event handler, for \a event, can be reimplemented in a subclass to + receive widget close events. The default implementation accepts the + event. + + \sa close(), QCloseEvent +*/ +void QGraphicsWidget::closeEvent(QCloseEvent *event) +{ + event->accept(); +} + +/*! + \reimp +*/ +void QGraphicsWidget::focusInEvent(QFocusEvent *event) +{ + Q_UNUSED(event); + if (focusPolicy() != Qt::NoFocus) + update(); +} + +/*! + Finds a new widget to give the keyboard focus to, as appropriate for Tab + and Shift+Tab, and returns true if it can find a new widget; returns false + otherwise. If \a next is true, this function searches forward; if \a next + is false, it searches backward. + + Sometimes, you will want to reimplement this function to provide special + focus handling for your widget and its subwidgets. For example, a web + browser might reimplement it to move its current active link forward or + backward, and call the base implementation only when it reaches the last + or first link on the page. + + Child widgets call focusNextPrevChild() on their parent widgets, but only + the window that contains the child widgets decides where to redirect + focus. By reimplementing this function for an object, you gain control of + focus traversal for all child widgets. + + \sa focusPolicy() +*/ +bool QGraphicsWidget::focusNextPrevChild(bool next) +{ + Q_D(QGraphicsWidget); + // Let the parent's focusNextPrevChild implementation decide what to do. + QGraphicsWidget *parent = 0; + if (!isWindow() && (parent = parentWidget())) + return parent->focusNextPrevChild(next); + if (!d->scene) + return false; + if (d->scene->focusNextPrevChild(next)) + return true; + if (isWindow()) { + setFocus(next ? Qt::TabFocusReason : Qt::BacktabFocusReason); + if (hasFocus()) + return true; + } + return false; +} + +/*! + \reimp +*/ +void QGraphicsWidget::focusOutEvent(QFocusEvent *event) +{ + Q_UNUSED(event); + if (focusPolicy() != Qt::NoFocus) + update(); +} + +/*! + This event handler, for \l{QEvent::Hide}{Hide} events, is delivered after + the widget has been hidden, for example, setVisible(false) has been called + for the widget or one of its ancestors when the widget was previously + shown. + + You can reimplement this event handler to detect when your widget is + hidden. Calling QEvent::accept() or QEvent::ignore() on \a event has no + effect. + + \sa showEvent(), QWidget::hideEvent(), ItemVisibleChange +*/ +void QGraphicsWidget::hideEvent(QHideEvent *event) +{ + ///### focusNextPrevChild(true), don't lose focus when the focus widget + // is hidden. + Q_UNUSED(event); +} + +/*! + This event handler, for \l{QEvent::GraphicsSceneMove}{GraphicsSceneMove} + events, is delivered after the widget has moved (e.g., its local position + has changed). + + This event is only delivered when the item is moved locally. Calling + setTransform() or moving any of the item's ancestors does not affect the + item's local position. + + You can reimplement this event handler to detect when your widget has + moved. Calling QEvent::accept() or QEvent::ignore() on \a event has no + effect. + + \sa ItemPositionChange, ItemPositionHasChanged +*/ +void QGraphicsWidget::moveEvent(QGraphicsSceneMoveEvent *event) +{ + // ### Last position is always == current position + Q_UNUSED(event); +} + +/*! + This event is delivered to the item by the scene at some point after it + has been constructed, but before it is shown or otherwise accessed through + the scene. You can use this event handler to do last-minute initializations + of the widget which require the item to be fully constructed. + + The base implementation does nothing. +*/ +void QGraphicsWidget::polishEvent() +{ +} + +/*! + This event handler, for + \l{QEvent::GraphicsSceneResize}{GraphicsSceneResize} events, is + delivered after the widget has been resized (i.e., its local size has + changed). \a event contains both the old and the new size. + + This event is only delivered when the widget is resized locally; calling + setTransform() on the widget or any of its ancestors or view, does not + affect the widget's local size. + + You can reimplement this event handler to detect when your widget has been + resized. Calling QEvent::accept() or QEvent::ignore() on \a event has no + effect. + + \sa geometry(), setGeometry() +*/ +void QGraphicsWidget::resizeEvent(QGraphicsSceneResizeEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for \l{QEvent::Show}{Show} events, is delivered before + the widget has been shown, for example, setVisible(true) has been called + for the widget or one of its ancestors when the widget was previously + hidden. + + You can reimplement this event handler to detect when your widget is + shown. Calling QEvent::accept() or QEvent::ignore() on \a event has no + effect. + + \sa hideEvent(), QWidget::showEvent(), ItemVisibleChange +*/ +void QGraphicsWidget::showEvent(QShowEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QGraphicsWidget::hoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); +} + +/*! + \reimp +*/ +void QGraphicsWidget::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for \a event, can be reimplemented in a subclass to + receive notifications for Qt::GrabMouse events. + + \sa grabMouse(), grabKeyboard() +*/ +void QGraphicsWidget::grabMouseEvent(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for \a event, can be reimplemented in a subclass to + receive notifications for Qt::UngrabMouse events. + + \sa ungrabMouse(), ungrabKeyboard() +*/ +void QGraphicsWidget::ungrabMouseEvent(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for \a event, can be reimplemented in a subclass to + receive notifications for Qt::GrabKeyboard events. + + \sa grabKeyboard(), grabMouse() +*/ +void QGraphicsWidget::grabKeyboardEvent(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + This event handler, for \a event, can be reimplemented in a subclass to + receive notifications for Qt::UngrabKeyboard events. + + \sa ungrabKeyboard(), ungrabMouse() +*/ +void QGraphicsWidget::ungrabKeyboardEvent(QEvent *event) +{ + Q_UNUSED(event); +} + +/*! + Returns the widgets window type. +*/ +Qt::WindowType QGraphicsWidget::windowType() const +{ + return Qt::WindowType(int(windowFlags()) & Qt::WindowType_Mask); +} + +/*! + \property QGraphicsWidget::windowFlags + \brief the widget's window flags + + Window flags are a combination of a window type (e.g., Qt::Dialog) and + several flags giving hints on the behavior of the window. The behavior + is platform-dependent. + + By default, this property contains no window flags. +*/ +Qt::WindowFlags QGraphicsWidget::windowFlags() const +{ + Q_D(const QGraphicsWidget); + return d->windowFlags; +} +void QGraphicsWidget::setWindowFlags(Qt::WindowFlags wFlags) +{ + Q_D(QGraphicsWidget); + if (d->windowFlags == wFlags) + return; + bool wasPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup; + + d->windowFlags = wFlags; + if (!d->setWindowFrameMargins) + unsetWindowFrameMargins(); + + bool isPopup = (d->windowFlags & Qt::WindowType_Mask) == Qt::Popup; + if (d->scene && isVisible() && wasPopup != isPopup) { + // Popup state changed; update implicit mouse grab. + if (!isPopup) + d->scene->d_func()->removePopup(this); + else + d->scene->d_func()->addPopup(this); + } +} + +/*! + Returns true if this widget's window is in the active window, or if the + widget does not have a window but is in an active scene (i.e., a scene + that currently has focus). + + The active window is the window that either contains a child widget that + currently has input focus, or that itself has input focus. + + \sa QGraphicsScene::activeWindow(), QGraphicsScene::setActiveWindow() +*/ +bool QGraphicsWidget::isActiveWindow() const +{ + Q_D(const QGraphicsWidget); + if (!d->scene) + return false; + const QGraphicsWidget *w = window(); + return (!w && d->scene->d_func()->activationRefCount) || (w && d->scene->activeWindow() == w); +} + +/*! + \property QGraphicsWidget::windowTitle + \brief This property holds the window title (caption). + + This property is only used for windows. + + By default, if no title has been set, this property contains an empty string. +*/ +void QGraphicsWidget::setWindowTitle(const QString &title) +{ + Q_D(QGraphicsWidget); + d->windowTitle = title; +} +QString QGraphicsWidget::windowTitle() const +{ + Q_D(const QGraphicsWidget); + return d->windowTitle; +} + +/*! + \property QGraphicsWidget::focusPolicy + \brief the way the widget accepts keyboard focus + + The focus policy is Qt::TabFocus if the widget accepts keyboard focus by + tabbing, Qt::ClickFocus if the widget accepts focus by clicking, + Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if it + does not accept focus at all. + + You must enable keyboard focus for a widget if it processes keyboard + events. This is normally done from the widget's constructor. For instance, + the QLineEdit constructor calls setFocusPolicy(Qt::StrongFocus). + + If you enable a focus policy (i.e., not Qt::NoFocus), QGraphicsWidget will + automatically enable the ItemIsFocusable flag. Setting Qt::NoFocus on a + widget will clear the ItemIsFocusable flag. If the widget currently has + keyboard focus, the widget will automatically lose focus. + + \sa focusInEvent(), focusOutEvent(), keyPressEvent(), keyReleaseEvent(), enabled +*/ +Qt::FocusPolicy QGraphicsWidget::focusPolicy() const +{ + Q_D(const QGraphicsWidget); + return d->focusPolicy; +} +void QGraphicsWidget::setFocusPolicy(Qt::FocusPolicy policy) +{ + Q_D(QGraphicsWidget); + if (d->focusPolicy == policy) + return; + d->focusPolicy = policy; + if (hasFocus() && policy == Qt::NoFocus) + clearFocus(); + setFlag(ItemIsFocusable, policy != Qt::NoFocus); +} + +/*! + If this widget, a child or descendant of this widget currently has input + focus, this function will return a pointer to that widget. If + no descendant has input focus, 0 is returned. + + \sa QWidget::focusWidget() +*/ +QGraphicsWidget *QGraphicsWidget::focusWidget() const +{ + Q_D(const QGraphicsWidget); + return d->focusChild; +} + + +#ifndef QT_NO_SHORTCUT +/*! + \since 4.5 + + Adds a shortcut to Qt's shortcut system that watches for the given key \a + sequence in the given \a context. If the \a context is + Qt::ApplicationShortcut, the shortcut applies to the application as a + whole. Otherwise, it is either local to this widget, Qt::WidgetShortcut, + or to the window itself, Qt::WindowShortcut. For widgets that are not part + of a window (i.e., top-level widgets and their children), + Qt::WindowShortcut shortcuts apply to the scene. + + If the same key \a sequence has been grabbed by several widgets, + when the key \a sequence occurs a QEvent::Shortcut event is sent + to all the widgets to which it applies in a non-deterministic + order, but with the ``ambiguous'' flag set to true. + + \warning You should not normally need to use this function; + instead create \l{QAction}s with the shortcut key sequences you + require (if you also want equivalent menu options and toolbar + buttons), or create \l{QShortcut}s if you just need key sequences. + Both QAction and QShortcut handle all the event filtering for you, + and provide signals which are triggered when the user triggers the + key sequence, so are much easier to use than this low-level + function. + + \sa releaseShortcut() setShortcutEnabled() QWidget::grabShortcut() +*/ +int QGraphicsWidget::grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context) +{ + Q_ASSERT(qApp); + if (sequence.isEmpty()) + return 0; + // ### setAttribute(Qt::WA_GrabbedShortcut); + return qApp->d_func()->shortcutMap.addShortcut(this, sequence, context); +} + +/*! + \since 4.5 + + Removes the shortcut with the given \a id from Qt's shortcut + system. The widget will no longer receive QEvent::Shortcut events + for the shortcut's key sequence (unless it has other shortcuts + with the same key sequence). + + \warning You should not normally need to use this function since + Qt's shortcut system removes shortcuts automatically when their + parent widget is destroyed. It is best to use QAction or + QShortcut to handle shortcuts, since they are easier to use than + this low-level function. Note also that this is an expensive + operation. + + \sa grabShortcut() setShortcutEnabled() , QWidget::releaseShortcut() +*/ +void QGraphicsWidget::releaseShortcut(int id) +{ + Q_ASSERT(qApp); + if (id) + qApp->d_func()->shortcutMap.removeShortcut(id, this, 0); +} + +/*! + \since 4.5 + + If \a enabled is true, the shortcut with the given \a id is + enabled; otherwise the shortcut is disabled. + + \warning You should not normally need to use this function since + Qt's shortcut system enables/disables shortcuts automatically as + widgets become hidden/visible and gain or lose focus. It is best + to use QAction or QShortcut to handle shortcuts, since they are + easier to use than this low-level function. + + \sa grabShortcut() releaseShortcut(), QWidget::setShortcutEnabled() +*/ +void QGraphicsWidget::setShortcutEnabled(int id, bool enabled) +{ + Q_ASSERT(qApp); + if (id) + qApp->d_func()->shortcutMap.setShortcutEnabled(enabled, id, this, 0); +} + +/*! + \since 4.5 + + If \a enabled is true, auto repeat of the shortcut with the + given \a id is enabled; otherwise it is disabled. + + \sa grabShortcut() releaseShortcut() QWidget::setShortcutAutoRepeat() +*/ +void QGraphicsWidget::setShortcutAutoRepeat(int id, bool enabled) +{ + Q_ASSERT(qApp); + if (id) + qApp->d_func()->shortcutMap.setShortcutAutoRepeat(enabled, id, this, 0); +} +#endif + +#ifndef QT_NO_ACTION +/*! + \since 4.5 + + Appends the action \a action to this widget's list of actions. + + All QGraphicsWidgets have a list of \l{QAction}s, however they can be + represented graphically in many different ways. The default use of the + QAction list (as returned by actions()) is to create a context QMenu. + + A QGraphicsWidget should only have one of each action and adding an action + it already has will not cause the same action to be in the widget twice. + + \sa removeAction(), insertAction(), actions(), QWidget::addAction() +*/ +void QGraphicsWidget::addAction(QAction *action) +{ + insertAction(0, action); +} + +/*! + \since 4.5 + + Appends the actions \a actions to this widget's list of actions. + + \sa removeAction(), QMenu, addAction(), QWidget::addActions() +*/ +void QGraphicsWidget::addActions(QList<QAction *> actions) +{ + for (int i = 0; i < actions.count(); ++i) + insertAction(0, actions.at(i)); +} + +/*! + \since 4.5 + + Inserts the action \a action to this widget's list of actions, + before the action \a before. It appends the action if \a before is 0 or + \a before is not a valid action for this widget. + + A QGraphicsWidget should only have one of each action. + + \sa removeAction(), addAction(), QMenu, actions(), + QWidget::insertActions() +*/ +void QGraphicsWidget::insertAction(QAction *before, QAction *action) +{ + if (!action) { + qWarning("QWidget::insertAction: Attempt to insert null action"); + return; + } + + Q_D(QGraphicsWidget); + int index = d->actions.indexOf(action); + if (index != -1) + d->actions.removeAt(index); + + int pos = d->actions.indexOf(before); + if (pos < 0) { + before = 0; + pos = d->actions.size(); + } + d->actions.insert(pos, action); + + QActionPrivate *apriv = action->d_func(); + apriv->graphicsWidgets.append(this); + + QActionEvent e(QEvent::ActionAdded, action, before); + QApplication::sendEvent(this, &e); +} + +/*! + \since 4.5 + + Inserts the actions \a actions to this widget's list of actions, + before the action \a before. It appends the action if \a before is 0 or + \a before is not a valid action for this widget. + + A QGraphicsWidget can have at most one of each action. + + \sa removeAction(), QMenu, insertAction(), QWidget::insertActions() +*/ +void QGraphicsWidget::insertActions(QAction *before, QList<QAction *> actions) +{ + for (int i = 0; i < actions.count(); ++i) + insertAction(before, actions.at(i)); +} + +/*! + \since 4.5 + + Removes the action \a action from this widget's list of actions. + + \sa insertAction(), actions(), insertAction(), QWidget::removeAction() +*/ +void QGraphicsWidget::removeAction(QAction *action) +{ + if (!action) + return; + + Q_D(QGraphicsWidget); + + QActionPrivate *apriv = action->d_func(); + apriv->graphicsWidgets.removeAll(this); + + if (d->actions.removeAll(action)) { + QActionEvent e(QEvent::ActionRemoved, action); + QApplication::sendEvent(this, &e); + } +} + +/*! + \since 4.5 + + Returns the (possibly empty) list of this widget's actions. + + \sa insertAction(), removeAction(), QWidget::actions(), + QAction::associatedWidgets(), QAction::associatedGraphicsWidgets() +*/ +QList<QAction *> QGraphicsWidget::actions() const +{ + Q_D(const QGraphicsWidget); + return d->actions; +} +#endif + +/*! + Moves the \a second widget around the ring of focus widgets so that + keyboard focus moves from the \a first widget to the \a second widget when + the Tab key is pressed. + + Note that since the tab order of the \a second widget is changed, you + should order a chain like this: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 1 + + \e not like this: + + \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicswidget.cpp 2 + + If \a first is 0, this indicates that \a second should be the first widget + to receive input focus should the scene gain Tab focus (i.e., the user + hits Tab so that focus passes into the scene). If \a second is 0, this + indicates that \a first should be the first widget to gain focus if the + scene gained BackTab focus. + + By default, tab order is defined implicitly using widget creation order. + + \sa focusPolicy, {Keyboard Focus} +*/ +void QGraphicsWidget::setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second) +{ + if (!first && !second) { + qWarning("QGraphicsWidget::setTabOrder(0, 0) is undefined"); + return; + } + if ((first && second) && first->scene() != second->scene()) { + qWarning("QGraphicsWidget::setTabOrder: scenes %p and %p are different", + first->scene(), second->scene()); + return; + } + QGraphicsScene *scene = first ? first->scene() : second->scene(); + if (!scene && (!first || !second)) { + qWarning("QGraphicsWidget::setTabOrder: assigning tab order from/to the" + " scene requires the item to be in a scene."); + return; + } + + // If either first or second are 0, the scene's tabFocusFirst is updated + // to point to the first item in the scene's focus chain. Then first or + // second are set to point to tabFocusFirst. + QGraphicsScenePrivate *sceneD = scene->d_func(); + if (!first) { + sceneD->tabFocusFirst = second; + return; + } + if (!second) { + sceneD->tabFocusFirst = first->d_func()->focusNext; + return; + } + + // Both first and second are != 0. + QGraphicsWidget *firstFocusNext = first->d_func()->focusNext; + if (firstFocusNext == second) { + // Nothing to do. + return; + } + + // Update the focus chain. + QGraphicsWidget *secondFocusPrev = second->d_func()->focusPrev; + QGraphicsWidget *secondFocusNext = second->d_func()->focusNext; + firstFocusNext->d_func()->focusPrev = second; + first->d_func()->focusNext = second; + second->d_func()->focusNext = firstFocusNext; + second->d_func()->focusPrev = first; + secondFocusPrev->d_func()->focusNext = secondFocusNext; + secondFocusNext->d_func()->focusPrev = secondFocusPrev; + + Q_ASSERT(first->d_func()->focusNext->d_func()->focusPrev == first); + Q_ASSERT(first->d_func()->focusPrev->d_func()->focusNext == first); + + Q_ASSERT(second->d_func()->focusNext->d_func()->focusPrev == second); + Q_ASSERT(second->d_func()->focusPrev->d_func()->focusNext == second); + +} + +/*! + If \a on is true, this function enables \a attribute; otherwise + \a attribute is disabled. + + See the class documentation for QGraphicsWidget for a complete list of + which attributes are supported, and what they are for. + + \sa testAttribute(), QWidget::setAttribute() +*/ +void QGraphicsWidget::setAttribute(Qt::WidgetAttribute attribute, bool on) +{ + Q_D(QGraphicsWidget); + // ### most flags require some immediate action + // ### we might want to qWarn use of unsupported attributes + // ### we might want to not use Qt::WidgetAttribute, but roll our own instead + d->setAttribute(attribute, on); +} + +/*! + Returns true if \a attribute is enabled for this widget; otherwise, + returns false. + + \sa setAttribute() +*/ +bool QGraphicsWidget::testAttribute(Qt::WidgetAttribute attribute) const +{ + Q_D(const QGraphicsWidget); + return d->testAttribute(attribute); +} + +/*! + \reimp +*/ +int QGraphicsWidget::type() const +{ + return Type; +} + +/*! + \reimp +*/ +void QGraphicsWidget::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(painter); + Q_UNUSED(option); + Q_UNUSED(widget); +} + +/*! + This virtual function is called by QGraphicsScene to draw the window frame + for windows using \a painter, \a option, and \a widget, in local + coordinates. The base implementation uses the current style to render the + frame and title bar. + + You can reimplement this function in a subclass of QGraphicsWidget to + provide custom rendering of the widget's window frame. + + \sa QGraphicsItem::paint() +*/ +void QGraphicsWidget::paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + const bool fillBackground = !testAttribute(Qt::WA_OpaquePaintEvent) + && !testAttribute(Qt::WA_NoSystemBackground); + QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(this); + const bool embeddedWidgetFillsOwnBackground = proxy && proxy->widget(); + + if (rect().contains(option->exposedRect)) { + if (fillBackground && !embeddedWidgetFillsOwnBackground) + painter->fillRect(option->exposedRect, palette().window()); + return; + } + + Q_D(QGraphicsWidget); + + QRect windowFrameRect = QRect(QPoint(), windowFrameGeometry().size().toSize()); + QStyleOptionTitleBar bar; + bar.QStyleOption::operator=(*option); + d->initStyleOptionTitleBar(&bar); // this clear flags in bar.state + if (d->buttonMouseOver) + bar.state |= QStyle::State_MouseOver; + else + bar.state &= ~QStyle::State_MouseOver; + if (d->buttonSunken) + bar.state |= QStyle::State_Sunken; + else + bar.state &= ~QStyle::State_Sunken; + + bar.rect = windowFrameRect; + + // translate painter to make the style happy + const QPointF styleOrigin = this->windowFrameRect().topLeft(); + painter->translate(styleOrigin); + +#ifdef Q_WS_MAC + const QSize pixmapSize = windowFrameRect.size(); + if (pixmapSize.width() <= 0 || pixmapSize.height() <= 0) + return; + QPainter *realPainter = painter; + QPixmap pm(pixmapSize); + painter = new QPainter(&pm); +#endif + + // Fill background + QStyleHintReturnMask mask; + bool setMask = style()->styleHint(QStyle::SH_WindowFrame_Mask, &bar, widget, &mask) && !mask.region.isEmpty(); + bool hasBorder = !style()->styleHint(QStyle::SH_TitleBar_NoBorder, &bar, widget); + int frameWidth = style()->pixelMetric(QStyle::PM_MDIFrameWidth, &bar, widget); + if (setMask) { + painter->save(); + painter->setClipRegion(mask.region, Qt::IntersectClip); + } + if (fillBackground) { + if (embeddedWidgetFillsOwnBackground) { + // Don't fill the background twice. + QPainterPath windowFrameBackground; + windowFrameBackground.addRect(windowFrameRect); + // Adjust with 0.5 to avoid border artifacts between + // widget background and frame background. + windowFrameBackground.addRect(rect().translated(-styleOrigin).adjusted(0.5, 0.5, -0.5, -0.5)); + painter->fillPath(windowFrameBackground, palette().window()); + } else { + painter->fillRect(windowFrameRect, palette().window()); + } + } + painter->setRenderHint(QPainter::NonCosmeticDefaultPen); + + // Draw title + int height = (int)d->titleBarHeight(bar); + bar.rect.setHeight(height); + if (hasBorder) // Frame is painted by PE_FrameWindow + bar.rect.adjust(frameWidth, frameWidth, -frameWidth, 0); + + painter->save(); + painter->setFont(QApplication::font("QWorkspaceTitleBar")); + style()->drawComplexControl(QStyle::CC_TitleBar, &bar, painter, widget); + painter->restore(); + if (setMask) + painter->restore(); + // Draw window frame + QStyleOptionFrame frameOptions; + frameOptions.QStyleOption::operator=(*option); + initStyleOption(&frameOptions); + if (!hasBorder) + painter->setClipRect(windowFrameRect.adjusted(0, +height, 0, 0), Qt::IntersectClip); + if (hasFocus()) { + frameOptions.state |= QStyle::State_HasFocus; + } else { + frameOptions.state &= ~QStyle::State_HasFocus; + } + bool isActive = isActiveWindow(); + if (isActive) { + frameOptions.state |= QStyle::State_Active; + } else { + frameOptions.state &= ~QStyle::State_Active; + } + + frameOptions.palette.setCurrentColorGroup(isActive ? QPalette::Active : QPalette::Normal); + frameOptions.rect = windowFrameRect; + frameOptions.lineWidth = style()->pixelMetric(QStyle::PM_MdiSubWindowFrameWidth, 0, widget); + frameOptions.midLineWidth = 1; + style()->drawPrimitive(QStyle::PE_FrameWindow, &frameOptions, painter, widget); + +#ifdef Q_WS_MAC + realPainter->drawPixmap(QPoint(), pm); + delete painter; +#endif +} + +/*! + \reimp +*/ +QRectF QGraphicsWidget::boundingRect() const +{ + return windowFrameRect(); +} + +/*! + \reimp +*/ +QPainterPath QGraphicsWidget::shape() const +{ + QPainterPath path; + path.addRect(rect()); + return path; +} + +/*! + Call this function to close the widget. + + Returns true if the widget was closed; otherwise returns false. + This slot will first send a QCloseEvent to the widget, which may or may + not accept the event. If the event was ignored, nothing happens. If the + event was accepted, it will hide() the widget. + + If the widget has the Qt::WA_DeleteOnClose attribute set it will be + deleted. +*/ +bool QGraphicsWidget::close() +{ + QCloseEvent closeEvent; + QApplication::sendEvent(this, &closeEvent); + if (!closeEvent.isAccepted()) { + return false; + } + // hide + if (isVisible()) { + hide(); + } + if (testAttribute(Qt::WA_DeleteOnClose)) { + deleteLater(); + } + return true; +} + +#ifdef Q_NO_USING_KEYWORD +/*! + \fn const QObjectList &QGraphicsWidget::children() const + + This function returns the same value as QObject::children(). It's + provided to differentiate between the obsolete member + QGraphicsItem::children() and QObject::children(). QGraphicsItem now + provides childItems() instead. +*/ +#endif + +#if 0 +void QGraphicsWidget::dumpFocusChain() +{ + qDebug() << "=========== Dumping focus chain =============="; + int i = 0; + QGraphicsWidget *next = this; + QSet<QGraphicsWidget*> visited; + do { + if (!next) { + qWarning("Found a focus chain that is not circular, (next == 0)"); + break; + } + qDebug() << i++ << QString::number(uint(next), 16) << next->className() << next->data(0) << QString::fromAscii("focusItem:%1").arg(next->hasFocus() ? "1" : "0") << QLatin1String("next:") << next->d_func()->focusNext->data(0) << QLatin1String("prev:") << next->d_func()->focusPrev->data(0); + if (visited.contains(next)) { + qWarning("Already visited this node. However, I expected to dump until I found myself."); + break; + } + visited << next; + next = next->d_func()->focusNext; + } while (next != this); +} +#endif + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgraphicswidget.h b/src/gui/graphicsview/qgraphicswidget.h new file mode 100644 index 0000000..34f1c5f --- /dev/null +++ b/src/gui/graphicsview/qgraphicswidget.h @@ -0,0 +1,249 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSWIDGET_H +#define QGRAPHICSWIDGET_H + +#include <QtGui/qfont.h> +#include <QtGui/qgraphicslayoutitem.h> +#include <QtGui/qgraphicsitem.h> +#include <QtGui/qpalette.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Gui) + +class QFont; +class QFontMetrics; +class QGraphicsLayout; +class QGraphicsSceneMoveEvent; +class QGraphicsWidgetPrivate; +class QGraphicsSceneResizeEvent; +class QStyle; +class QStyleOption; + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class QGraphicsWidgetPrivate; + +class Q_GUI_EXPORT QGraphicsWidget : public QObject, public QGraphicsItem, public QGraphicsLayoutItem +{ + Q_OBJECT + Q_PROPERTY(QPalette palette READ palette WRITE setPalette) + Q_PROPERTY(QFont font READ font WRITE setFont) + Q_PROPERTY(Qt::LayoutDirection layoutDirection READ layoutDirection WRITE setLayoutDirection RESET unsetLayoutDirection) + Q_PROPERTY(QSizeF size READ size WRITE resize) + Q_PROPERTY(Qt::FocusPolicy focusPolicy READ focusPolicy WRITE setFocusPolicy) + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled) + Q_PROPERTY(bool visible READ isVisible WRITE setVisible) + Q_PROPERTY(Qt::WindowFlags windowFlags READ windowFlags WRITE setWindowFlags) + Q_PROPERTY(QString windowTitle READ windowTitle WRITE setWindowTitle) + Q_PROPERTY(qreal opacity READ opacity WRITE setOpacity) + Q_PROPERTY(QPointF pos READ pos WRITE setPos) + Q_PROPERTY(QRectF geometry READ geometry WRITE setGeometry) + +public: + QGraphicsWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + ~QGraphicsWidget(); + + QGraphicsLayout *layout() const; + void setLayout(QGraphicsLayout *layout); + void adjustSize(); + + Qt::LayoutDirection layoutDirection() const; + void setLayoutDirection(Qt::LayoutDirection direction); + void unsetLayoutDirection(); + + QStyle *style() const; + void setStyle(QStyle *style); + + QFont font() const; + void setFont(const QFont &font); + + QPalette palette() const; + void setPalette(const QPalette &palette); + + void resize(const QSizeF &size); + inline void resize(qreal w, qreal h) { resize(QSizeF(w, h)); } + QSizeF size() const; + + void setGeometry(const QRectF &rect); + inline void setGeometry(qreal x, qreal y, qreal w, qreal h); + inline QRectF rect() const { return QRectF(QPointF(), size()); } + + void setContentsMargins(qreal left, qreal top, qreal right, qreal bottom); + void getContentsMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; + + void setWindowFrameMargins(qreal left, qreal top, qreal right, qreal bottom); + void getWindowFrameMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; + void unsetWindowFrameMargins(); + QRectF windowFrameGeometry() const; + QRectF windowFrameRect() const; + + // Window handling + Qt::WindowFlags windowFlags() const; + Qt::WindowType windowType() const; + void setWindowFlags(Qt::WindowFlags wFlags); + bool isActiveWindow() const; + void setWindowTitle(const QString &title); + QString windowTitle() const; + + // Focus handling + Qt::FocusPolicy focusPolicy() const; + void setFocusPolicy(Qt::FocusPolicy policy); + static void setTabOrder(QGraphicsWidget *first, QGraphicsWidget *second); + QGraphicsWidget *focusWidget() const; + +#ifndef QT_NO_SHORTCUT + int grabShortcut(const QKeySequence &sequence, Qt::ShortcutContext context = Qt::WindowShortcut); + void releaseShortcut(int id); + void setShortcutEnabled(int id, bool enabled = true); + void setShortcutAutoRepeat(int id, bool enabled = true); +#endif + +#ifndef QT_NO_ACTION + //actions + void addAction(QAction *action); + void addActions(QList<QAction*> actions); + void insertAction(QAction *before, QAction *action); + void insertActions(QAction *before, QList<QAction*> actions); + void removeAction(QAction *action); + QList<QAction*> actions() const; +#endif + + void setAttribute(Qt::WidgetAttribute attribute, bool on = true); + bool testAttribute(Qt::WidgetAttribute attribute) const; + + enum { + Type = 11 + }; + int type() const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + virtual void paintWindowFrame(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = 0); + QRectF boundingRect() const; + QPainterPath shape() const; + +#if 0 + void dumpFocusChain(); +#endif + + // ### Qt 5: Disambiguate +#ifdef Q_NO_USING_KEYWORD + const QObjectList &children() const { return QObject::children(); } +#else + using QObject::children; +#endif + +public Q_SLOTS: + bool close(); + +protected: + virtual void initStyleOption(QStyleOption *option) const; + + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + void updateGeometry(); + + // Notification + QVariant itemChange(GraphicsItemChange change, const QVariant &value); + virtual QVariant propertyChange(const QString &propertyName, const QVariant &value); + + // Scene events + bool sceneEvent(QEvent *event); + virtual bool windowFrameEvent(QEvent *e); + virtual Qt::WindowFrameSection windowFrameSectionAt(const QPointF& pos) const; + + // Base event handlers + bool event(QEvent *event); + //virtual void actionEvent(QActionEvent *event); + virtual void changeEvent(QEvent *event); + virtual void closeEvent(QCloseEvent *event); + //void create(WId window = 0, bool initializeWindow = true, bool destroyOldWindow = true); + //void destroy(bool destroyWindow = true, bool destroySubWindows = true); + void focusInEvent(QFocusEvent *event); + virtual bool focusNextPrevChild(bool next); + void focusOutEvent(QFocusEvent *event); + virtual void hideEvent(QHideEvent *event); + //virtual bool macEvent(EventHandlerCallRef caller, EventRef event); + //virtual int metric(PaintDeviceMetric m ) const; + virtual void moveEvent(QGraphicsSceneMoveEvent *event); + virtual void polishEvent(); + //virtual bool qwsEvent(QWSEvent *event); + //void resetInputContext (); + virtual void resizeEvent(QGraphicsSceneResizeEvent *event); + virtual void showEvent(QShowEvent *event); + //virtual void tabletEvent(QTabletEvent *event); + //virtual bool winEvent(MSG *message, long *result); + //virtual bool x11Event(XEvent *event); + virtual void hoverMoveEvent(QGraphicsSceneHoverEvent *event); + virtual void hoverLeaveEvent(QGraphicsSceneHoverEvent *event); + virtual void grabMouseEvent(QEvent *event); + virtual void ungrabMouseEvent(QEvent *event); + virtual void grabKeyboardEvent(QEvent *event); + virtual void ungrabKeyboardEvent(QEvent *event); + QGraphicsWidget(QGraphicsWidgetPrivate &, QGraphicsItem *parent, QGraphicsScene *, Qt::WindowFlags wFlags = 0); + +private: + Q_DISABLE_COPY(QGraphicsWidget) + Q_DECLARE_PRIVATE_D(QGraphicsItem::d_ptr, QGraphicsWidget) + friend class QGraphicsScene; + friend class QGraphicsScenePrivate; + friend class QGraphicsView; + friend class QGraphicsItem; + friend class QGraphicsItemPrivate; + friend class QGraphicsLayout; + friend class QWidget; + friend class QApplication; +}; + +inline void QGraphicsWidget::setGeometry(qreal ax, qreal ay, qreal aw, qreal ah) +{ setGeometry(QRectF(ax, ay, aw, ah)); } + +#endif + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + diff --git a/src/gui/graphicsview/qgraphicswidget_p.cpp b/src/gui/graphicsview/qgraphicswidget_p.cpp new file mode 100644 index 0000000..641409d --- /dev/null +++ b/src/gui/graphicsview/qgraphicswidget_p.cpp @@ -0,0 +1,740 @@ +/**************************************************************************** +** +** 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 <QtCore/qdebug.h> +#include "qgraphicswidget_p.h" +#include "qgraphicslayout.h" +#include "qgraphicsscene_p.h" +#include <QtGui/qapplication.h> +#include <QtGui/qgraphicsscene.h> +#include <QtGui/qstyleoption.h> +#include <QtGui/QStyleOptionTitleBar> +#include <QtGui/QGraphicsSceneMouseEvent> +#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC) +# include <QMacStyle> +#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; + q->setParentItem(parentItem); + q->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred, QSizePolicy::DefaultType)); + q->setGraphicsItem(q); + + resolveLayoutDirection(); + + if (!parentItem) + adjustWindowFlags(&wFlags); + windowFlags = wFlags; + 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<QMacStyle*>(q->style())) { + height -=4; + } +#endif + return (qreal)height; +} + +void QGraphicsWidgetPrivate::getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const +{ + if (left) + *left = leftLayoutItemMargin; + if (top) + *top = topLayoutItemMargin; + if (right) + *right = rightLayoutItemMargin; + if (bottom) + *bottom = bottomLayoutItemMargin; +} + +void QGraphicsWidgetPrivate::setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom) +{ + if (leftLayoutItemMargin == left + && topLayoutItemMargin == top + && rightLayoutItemMargin == right + && bottomLayoutItemMargin == bottom) + return; + + Q_Q(QGraphicsWidget); + leftLayoutItemMargin = left; + topLayoutItemMargin = top; + rightLayoutItemMargin = right; + bottomLayoutItemMargin = bottom; + q->updateGeometry(); +} + +void QGraphicsWidgetPrivate::setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt) +{ + Q_Q(QGraphicsWidget); + QStyleOption myOpt; + if (!opt) { + q->initStyleOption(&myOpt); + myOpt.rect.setRect(0, 0, 32768, 32768); // arbitrary + opt = &myOpt; + } + + QRect liRect = q->style()->subElementRect(element, opt, /* q */ 0); + if (liRect.isValid()) { + leftLayoutItemMargin = (opt->rect.left() - liRect.left()); + topLayoutItemMargin = (opt->rect.top() - liRect.top()); + rightLayoutItemMargin = (liRect.right() - opt->rect.right()); + bottomLayoutItemMargin = (liRect.bottom() - opt->rect.bottom()); + } else { + leftLayoutItemMargin = 0; + topLayoutItemMargin = 0; + rightLayoutItemMargin = 0; + bottomLayoutItemMargin = 0; + } +} + +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<QGraphicsWidget *>(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<QGraphicsWidget *>(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<QGraphicsWidget *>(item); + if (!w->isWindow() || w->testAttribute(Qt::WA_WindowPropagation)) + w->d_func()->resolveFont(mask); + } else { + item->d_ptr->resolveFont(mask); + } + } + + // 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); + q->initStyleOption(option); + option->rect.setHeight(titleBarHeight(*option)); + option->titleBarFlags = windowFlags; + option->subControls = QStyle::SC_TitleBarCloseButton | QStyle::SC_TitleBarLabel | QStyle::SC_TitleBarSysMenu; + option->activeSubControls = 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(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); + if (grabbedSection != Qt::NoSection) { + if (grabbedSection == Qt::TitleBarArea) { + 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(); + pos.rx() += leftWindowFrameMargin; + pos.ry() += topWindowFrameMargin; + 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<QGraphicsSceneMouseEvent *>(event)->buttons())) + grabbedSection = Qt::NoSection; + event->accept(); + } +} + +void QGraphicsWidgetPrivate::windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event) +{ + Q_Q(QGraphicsWidget); + if (event->button() != Qt::LeftButton) + return; + + startGeometry = q->geometry(); + grabbedSection = q->windowFrameSectionAt(event->pos()); + switch (grabbedSection) { + case Qt::LeftSection: + case Qt::TopLeftSection: + mouseDelta = event->pos() - q->rect().topLeft(); + break; + case Qt::TopSection: + case Qt::TopRightSection: + mouseDelta = event->pos() - q->rect().topRight(); + break; + case Qt::RightSection: + case Qt::BottomRightSection: + mouseDelta = event->pos() - q->rect().bottomRight(); + break; + case Qt::BottomSection: + case Qt::BottomLeftSection: + mouseDelta = event->pos() - q->rect().bottomLeft(); + break; + case Qt::TitleBarArea: + if (hoveredSubControl == QStyle::SC_TitleBarCloseButton) { + buttonSunken = true; + q->update(); + } + break; + case Qt::NoSection: + break; + } + event->setAccepted(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); + if (!(event->buttons() & Qt::LeftButton) || 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 (grabbedSection) { + case Qt::LeftSection: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), + startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + break; + case Qt::TopLeftSection: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), + startGeometry.size() - QSizeF(delta.dx(), delta.dy())); + break; + case Qt::TopSection: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), + startGeometry.size() - QSizeF(0, delta.dy())); + break; + case Qt::TopRightSection: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentYDelta.dx(), parentYDelta.dy()), + startGeometry.size() - QSizeF(-delta.dx(), delta.dy())); + break; + case Qt::RightSection: + newGeometry = QRectF(startGeometry.topLeft(), + startGeometry.size() + QSizeF(delta.dx(), 0)); + break; + case Qt::BottomRightSection: + newGeometry = QRectF(startGeometry.topLeft(), + startGeometry.size() + QSizeF(delta.dx(), delta.dy())); + break; + case Qt::BottomSection: + newGeometry = QRectF(startGeometry.topLeft(), + startGeometry.size() + QSizeF(0, delta.dy())); + break; + case Qt::BottomLeftSection: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentXDelta.dx(), parentXDelta.dy()), + startGeometry.size() - QSizeF(delta.dx(), -delta.dy())); + break; + case Qt::TitleBarArea: + newGeometry = QRectF(startGeometry.topLeft() + QPointF(parentDelta.dx(), parentDelta.dy()), + startGeometry.size()); + break; + case Qt::NoSection: + break; + } + + if (grabbedSection != Qt::NoSection) { + _q_boundGeometryToSizeConstraints(startGeometry, &newGeometry, grabbedSection, + q->effectiveSizeHint(Qt::MinimumSize), + q->effectiveSizeHint(Qt::MaximumSize)); + q->setGeometry(newGeometry); + } +} + +void QGraphicsWidgetPrivate::windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event) +{ + Q_Q(QGraphicsWidget); + if (!hasDecoration()) + return; + + if (q->rect().contains(event->pos())) { + if (buttonMouseOver || hoveredSubControl != QStyle::SC_None) + windowFrameHoverLeaveEvent(event); + return; + } + + bool wasMouseOver = buttonMouseOver; + QRect oldButtonRect = buttonRect; + buttonRect = QRect(); + buttonMouseOver = false; + QPointF pos = event->pos(); + QStyleOptionTitleBar bar; + // make sure that the coordinates (rect and pos) we send to the style are positive. + pos.rx() += leftWindowFrameMargin; + pos.ry() += topWindowFrameMargin; + 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: + 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 + buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMinButton, 0); + buttonRect |= q->style()->subControlRect(QStyle::CC_TitleBar, &bar, QStyle::SC_TitleBarMaxButton, 0); +#endif + if (buttonRect.contains(pos.toPoint())) + 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 + hoveredSubControl = q->style()->hitTestComplexControl(QStyle::CC_TitleBar, &bar, pos.toPoint(), 0); + if (hoveredSubControl != QStyle::SC_TitleBarCloseButton) + hoveredSubControl = QStyle::SC_TitleBarLabel; + + if (buttonMouseOver != wasMouseOver) { + if (!oldButtonRect.isNull()) + q->update(QRectF(oldButtonRect).translated(q->windowFrameRect().topLeft())); + if (!buttonRect.isNull()) + q->update(QRectF(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 + + bool needsUpdate = false; + if (hoveredSubControl == QStyle::SC_TitleBarCloseButton || buttonMouseOver) + needsUpdate = true; + + // update the hover state (of buttons etc...) + hoveredSubControl = QStyle::SC_None; + buttonMouseOver = false; + buttonRect = QRect(); + if (needsUpdate) + q->update(buttonRect); + } +} + +bool QGraphicsWidgetPrivate::hasDecoration() const +{ + return (windowFlags & Qt::Window) && (windowFlags & Qt::WindowTitleHint); +} + +/*! + \internal +*/ +void QGraphicsWidgetPrivate::setFocusWidget() +{ + // Update focus child chain. + QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(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<QGraphicsWidget *>(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<QGraphicsWidget *>(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 diff --git a/src/gui/graphicsview/qgraphicswidget_p.h b/src/gui/graphicsview/qgraphicswidget_p.h new file mode 100644 index 0000000..afa6812 --- /dev/null +++ b/src/gui/graphicsview/qgraphicswidget_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRAPHICSWIDGET_P_H +#define QGRAPHICSWIDGET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qapplication_*.cpp, qwidget*.cpp and qfiledialog.cpp. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include <private/qobject_p.h> +#include "qgraphicsitem_p.h" +#include "qgraphicswidget.h" +#include <QtGui/qfont.h> +#include <QtGui/qpalette.h> +#include <QtGui/qsizepolicy.h> +#include <QtGui/qstyle.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsLayout; +class QStyleOptionTitleBar; + +#if !defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +class Q_GUI_EXPORT QGraphicsWidgetPrivate : public QGraphicsItemPrivate +{ + Q_DECLARE_PUBLIC(QGraphicsWidget) +public: + QGraphicsWidgetPrivate() + : leftMargin(0), + topMargin(0), + rightMargin(0), + bottomMargin(0), + leftLayoutItemMargin(0), + topLayoutItemMargin(0), + rightLayoutItemMargin(0), + bottomLayoutItemMargin(0), + layout(0), + inheritedPaletteResolveMask(0), + inheritedFontResolveMask(0), + inSetGeometry(0), + focusPolicy(Qt::NoFocus), + focusNext(0), + focusPrev(0), + focusChild(0), + windowFlags(0), + hoveredSubControl(QStyle::SC_None), + grabbedSection(Qt::NoSection), + buttonMouseOver(false), + buttonSunken(false), + setWindowFrameMargins(false), + leftWindowFrameMargin(0), + topWindowFrameMargin(0), + rightWindowFrameMargin(0), + bottomWindowFrameMargin(0) + { } + + void init(QGraphicsItem *parentItem, Qt::WindowFlags wFlags); + qreal titleBarHeight(const QStyleOptionTitleBar &options) const; + + // Margins + qreal leftMargin; + qreal topMargin; + qreal rightMargin; + qreal bottomMargin; + QRectF contentsRect; + + // Layout item margins + void getLayoutItemMargins(qreal *left, qreal *top, qreal *right, qreal *bottom) const; + void setLayoutItemMargins(qreal left, qreal top, qreal right, qreal bottom); + void setLayoutItemMargins(QStyle::SubElement element, const QStyleOption *opt = 0); + + void fixFocusChainBeforeReparenting(QGraphicsWidget *newParent, QGraphicsScene *newScene = 0); + void setLayout_helper(QGraphicsLayout *l); + + qreal leftLayoutItemMargin; + qreal topLayoutItemMargin; + qreal rightLayoutItemMargin; + qreal bottomLayoutItemMargin; + + // Layouts + QGraphicsLayout *layout; + void setLayoutDirection_helper(Qt::LayoutDirection direction); + void resolveLayoutDirection(); + + // Style + QPalette palette; + uint inheritedPaletteResolveMask; + void setPalette_helper(const QPalette &palette); + void resolvePalette(uint inheritedMask); + void updatePalette(const QPalette &palette); + QPalette naturalWidgetPalette() const; + QFont font; + uint inheritedFontResolveMask; + void setFont_helper(const QFont &font); + void resolveFont(uint inheritedMask); + void updateFont(const QFont &font); + QFont naturalWidgetFont() const; + + // Window specific + void initStyleOptionTitleBar(QStyleOptionTitleBar *option); + void adjustWindowFlags(Qt::WindowFlags *wFlags); + void windowFrameMouseReleaseEvent(QGraphicsSceneMouseEvent *event); + void windowFrameMousePressEvent(QGraphicsSceneMouseEvent *event); + void windowFrameMouseMoveEvent(QGraphicsSceneMouseEvent *event); + void windowFrameHoverMoveEvent(QGraphicsSceneHoverEvent *event); + void windowFrameHoverLeaveEvent(QGraphicsSceneHoverEvent *event); + bool hasDecoration() const; + + // State + inline int attributeToBitIndex(Qt::WidgetAttribute att) const + { + int bit = -1; + switch (att) { + case Qt::WA_SetLayoutDirection: bit = 0; break; + case Qt::WA_RightToLeft: bit = 1; break; + case Qt::WA_SetStyle: bit = 2; break; + case Qt::WA_Resized: bit = 3; break; + case Qt::WA_DeleteOnClose: bit = 4; break; + case Qt::WA_NoSystemBackground: bit = 5; break; + case Qt::WA_OpaquePaintEvent: bit = 6; break; + case Qt::WA_SetPalette: bit = 7; break; + case Qt::WA_SetFont: bit = 8; break; + case Qt::WA_WindowPropagation: bit = 9; break; + default: break; + } + return bit; + } + inline void setAttribute(Qt::WidgetAttribute att, bool value) + { + int bit = attributeToBitIndex(att); + if (bit == -1) { + qWarning("QGraphicsWidget::setAttribute: unsupported attribute %d", int(att)); + return; + } + if (value) + attributes |= (1 << bit); + else + attributes &= ~(1 << bit); + } + inline bool testAttribute(Qt::WidgetAttribute att) const + { + int bit = attributeToBitIndex(att); + if (bit == -1) + return false; + return (attributes & (1 << bit)) != 0; + } + quint32 attributes : 10; + quint32 inSetGeometry : 1; + + // Focus + Qt::FocusPolicy focusPolicy; + QGraphicsWidget *focusNext; + QGraphicsWidget *focusPrev; + QGraphicsWidget *focusChild; + void setFocusWidget(); + void clearFocusWidget(); + + // Windows + Qt::WindowFlags windowFlags; + QString windowTitle; + QStyle::SubControl hoveredSubControl; + Qt::WindowFrameSection grabbedSection; + uint buttonMouseOver : 1; + uint buttonSunken : 1; + QPointF mouseDelta; // to compensate for small error when interactively resizing + QRectF startGeometry; + QRect buttonRect; + + bool setWindowFrameMargins; + qreal leftWindowFrameMargin; + qreal topWindowFrameMargin; + qreal rightWindowFrameMargin; + qreal bottomWindowFrameMargin; + +#ifndef QT_NO_ACTION + QList<QAction *> actions; +#endif +}; + +#endif //!defined(QT_NO_GRAPHICSVIEW) || (QT_EDITION & QT_MODULE_GRAPHICSVIEW) != QT_MODULE_GRAPHICSVIEW + +QT_END_NAMESPACE + +#endif //QGRAPHICSWIDGET_P_H + diff --git a/src/gui/graphicsview/qgridlayoutengine.cpp b/src/gui/graphicsview/qgridlayoutengine.cpp new file mode 100644 index 0000000..dc5ca21 --- /dev/null +++ b/src/gui/graphicsview/qgridlayoutengine.cpp @@ -0,0 +1,1542 @@ +/**************************************************************************** +** +** 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 <math.h> + +#include "qgraphicslayoutitem.h" +#include "qgridlayoutengine_p.h" +#include "qstyleoption.h" +#include "qvarlengtharray.h" + +#include <QtDebug> + +QT_BEGIN_NAMESPACE + +template <typename T> +static void insertOrRemoveItems(QVector<T> &items, int index, int delta) +{ + int count = items.count(); + if (index < count) { + if (delta > 0) { + items.insert(index, delta, T()); + } else if (delta < 0) { + items.remove(index, qMin(-delta, count - index)); + } + } +} + +static qreal growthFactorBelowPreferredSize(qreal desired, qreal sumAvailable, qreal sumDesired) +{ + Q_ASSERT(sumDesired != 0.0); + return desired * ::pow(sumAvailable / sumDesired, desired / sumDesired); +} + +static qreal fixedDescent(qreal descent, qreal ascent, qreal targetSize) +{ + if (descent < 0.0) + return -1.0; + + Q_ASSERT(descent >= 0.0); + Q_ASSERT(ascent >= 0.0); + Q_ASSERT(targetSize >= ascent + descent); + + qreal extra = targetSize - (ascent + descent); + return descent + (extra / 2.0); +} + +static qreal compare(const QGridLayoutBox &box1, const QGridLayoutBox &box2, int which) +{ + qreal size1 = box1.q_sizes(which); + qreal size2 = box2.q_sizes(which); + + if (which == MaximumSize) { + return size2 - size1; + } else { + return size1 - size2; + } +} + +void QGridLayoutBox::add(const QGridLayoutBox &other, int stretch, qreal spacing) +{ + Q_ASSERT(q_minimumDescent < 0.0); + + q_minimumSize += other.q_minimumSize + spacing; + q_preferredSize += other.q_preferredSize + spacing; + q_maximumSize += ((stretch == 0) ? other.q_preferredSize : other.q_maximumSize) + spacing; +} + +void QGridLayoutBox::combine(const QGridLayoutBox &other) +{ + q_minimumDescent = qMax(q_minimumDescent, other.q_minimumDescent); + q_minimumAscent = qMax(q_minimumAscent, other.q_minimumAscent); + + q_minimumSize = qMax(q_minimumAscent + q_minimumDescent, + qMax(q_minimumSize, other.q_minimumSize)); + qreal maxMax; + if (q_maximumSize == FLT_MAX && other.q_maximumSize != FLT_MAX) + maxMax = other.q_maximumSize; + else if (other.q_maximumSize == FLT_MAX && q_maximumSize != FLT_MAX) + maxMax = q_maximumSize; + else + maxMax = qMax(q_maximumSize, other.q_maximumSize); + + q_maximumSize = qMax(q_minimumSize, maxMax); + q_preferredSize = qBound(q_minimumSize, qMax(q_preferredSize, other.q_preferredSize), + q_maximumSize); +} + +void QGridLayoutBox::normalize() +{ + q_maximumSize = qMax(qreal(0.0), q_maximumSize); + q_minimumSize = qBound(qreal(0.0), q_minimumSize, q_maximumSize); + q_preferredSize = qBound(q_minimumSize, q_preferredSize, q_maximumSize); + q_minimumDescent = qMin(q_minimumDescent, q_minimumSize); + + Q_ASSERT((q_minimumDescent < 0.0) == (q_minimumAscent < 0.0)); +} + +#ifdef QT_DEBUG +void QGridLayoutBox::dump(int indent) const +{ + qDebug("%*sBox (%g <= %g <= %g [%g/%g])", indent, "", q_minimumSize, q_preferredSize, + q_maximumSize, q_minimumAscent, q_minimumDescent); +} +#endif + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2) +{ + for (int i = 0; i < NSizes; ++i) { + if (box1.q_sizes(i) != box2.q_sizes(i)) + return false; + } + return box1.q_minimumDescent == box2.q_minimumDescent + && box1.q_minimumAscent == box2.q_minimumAscent; +} + +void QGridLayoutRowData::reset(int count) +{ + ignore.fill(false, count); + boxes.fill(QGridLayoutBox(), count); + multiCellMap.clear(); + stretches.fill(0, count); + spacings.fill(0.0, count); + hasIgnoreFlag = false; +} + +void QGridLayoutRowData::distributeMultiCells() +{ + MultiCellMap::const_iterator i = multiCellMap.constBegin(); + for (; i != multiCellMap.constEnd(); ++i) { + int start = i.key().first; + int span = i.key().second; + int end = start + span; + const QGridLayoutBox &box = i.value().q_box; + int stretch = i.value().q_stretch; + + QGridLayoutBox totalBox = this->totalBox(start, end); + QVarLengthArray<QGridLayoutBox> extras(span); + QVarLengthArray<qreal> dummy(span); + QVarLengthArray<qreal> newSizes(span); + + for (int j = 0; j < NSizes; ++j) { + qreal extra = compare(totalBox, box, j); + if (extra > 0.0) { + calculateGeometries(start, end, totalBox.q_sizes(j), dummy.data(), newSizes.data(), + 0, totalBox); + + for (int k = 0; k < span; ++k) + extras[k].q_sizes(j) = newSizes[k]; + } + } + + for (int k = 0; k < span; ++k) { + boxes[start + k].combine(extras[k]); + stretches[start + k] = qMax(stretches[start + k], stretch); + } + } + multiCellMap.clear(); +} + +void QGridLayoutRowData::calculateGeometries(int start, int end, qreal targetSize, qreal *positions, + qreal *sizes, qreal *descents, + const QGridLayoutBox &totalBox) +{ + Q_ASSERT(end > start); + + targetSize = qBound(totalBox.q_minimumSize, targetSize, totalBox.q_maximumSize); + + int n = end - start; + QVarLengthArray<qreal> newSizes(n); + QVarLengthArray<qreal> factors(n); + qreal sumFactors = 0.0; + int sumStretches = 0; + qreal sumAvailable; + + for (int i = 0; i < n; ++i) { + if (stretches[start + i] > 0) + sumStretches += stretches[start + i]; + } + + if (targetSize < totalBox.q_preferredSize) { + stealBox(start, end, MinimumSize, positions, sizes); + + sumAvailable = targetSize - totalBox.q_minimumSize; + if (sumAvailable > 0.0) { + qreal sumDesired = totalBox.q_preferredSize - totalBox.q_minimumSize; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal desired = box.q_preferredSize - box.q_minimumSize; + factors[i] = growthFactorBelowPreferredSize(desired, sumAvailable, sumDesired); + sumFactors += factors[i]; + } + + for (int i = 0; i < n; ++i) { + Q_ASSERT(sumFactors > 0.0); + qreal delta = sumAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } else { + stealBox(start, end, PreferredSize, positions, sizes); + + sumAvailable = targetSize - totalBox.q_preferredSize; + if (sumAvailable > 0.0) { + bool somethingHasAMaximumSize = false; + + qreal sumPreferredSizes = 0.0; + for (int i = 0; i < n; ++i) + sumPreferredSizes += sizes[i]; + + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) { + newSizes[i] = 0.0; + factors[i] = 0.0; + continue; + } + + const QGridLayoutBox &box = boxes.at(start + i); + qreal desired = box.q_maximumSize - box.q_preferredSize; + if (desired == 0.0) { + newSizes[i] = sizes[i]; + factors[i] = 0.0; + } else { + Q_ASSERT(desired > 0.0); + + int stretch = stretches[start + i]; + if (sumStretches == 0) { + if (hasIgnoreFlag) { + factors[i] = (stretch < 0) ? 1.0 : 0.0; + } else { + factors[i] = (stretch < 0) ? sizes[i] : 0.0; + } + } else if (stretch == sumStretches) { + factors[i] = 1.0; + } else if (stretch <= 0) { + factors[i] = 0.0; + } else { + qreal ultimatePreferredSize; + qreal ultimateSumPreferredSizes; + qreal x = ((stretch * sumPreferredSizes) + - (sumStretches * box.q_preferredSize)) + / (sumStretches - stretch); + if (x >= 0.0) { + ultimatePreferredSize = box.q_preferredSize + x; + ultimateSumPreferredSizes = sumPreferredSizes + x; + } else { + ultimatePreferredSize = box.q_preferredSize; + ultimateSumPreferredSizes = (sumStretches * box.q_preferredSize) + / stretch; + } + + /* + We multiply these by 1.5 to give some space for a smooth transition + (at the expense of the stretch factors, which are not fully respected + during the transition). + */ + ultimatePreferredSize = ultimatePreferredSize * 3 / 2; + ultimateSumPreferredSizes = ultimateSumPreferredSizes * 3 / 2; + + qreal ultimateFactor = (stretch * ultimateSumPreferredSizes + / sumStretches) + - (box.q_preferredSize); + qreal transitionalFactor = sumAvailable + * (ultimatePreferredSize - box.q_preferredSize) + / (ultimateSumPreferredSizes + - sumPreferredSizes); + + qreal alpha = qMin(sumAvailable, + ultimateSumPreferredSizes - sumPreferredSizes); + qreal beta = ultimateSumPreferredSizes - sumPreferredSizes; + + factors[i] = ((alpha * ultimateFactor) + + ((beta - alpha) * transitionalFactor)) / beta; + } + sumFactors += factors[i]; + if (desired < sumAvailable) + somethingHasAMaximumSize = true; + + newSizes[i] = -1.0; + } + } + + bool keepGoing = somethingHasAMaximumSize; + while (keepGoing) { + keepGoing = false; + + for (int i = 0; i < n; ++i) { + if (newSizes[i] >= 0.0) + continue; + + const QGridLayoutBox &box = boxes.at(start + i); + qreal avail = sumAvailable * factors[i] / sumFactors; + if (sizes[i] + avail >= box.q_maximumSize) { + newSizes[i] = box.q_maximumSize; + sumAvailable -= box.q_maximumSize - sizes[i]; + sumFactors -= factors[i]; + keepGoing = (sumAvailable > 0.0); + if (!keepGoing) + break; + } + } + } + + for (int i = 0; i < n; ++i) { + if (newSizes[i] < 0.0) { + qreal delta = (sumFactors == 0.0) ? 0.0 + : sumAvailable * factors[i] / sumFactors; + newSizes[i] = sizes[i] + delta; + } + } + } + } + + if (sumAvailable > 0) { + qreal offset = 0; + for (int i = 0; i < n; ++i) { + qreal delta = newSizes[i] - sizes[i]; + positions[i] += offset; + sizes[i] += delta; + offset += delta; + } + +#if 0 // some "pixel allocation" + int surplus = targetSize - (positions[n - 1] + sizes[n - 1]); + Q_ASSERT(surplus >= 0 && surplus <= n); + + int prevSurplus = -1; + while (surplus > 0 && surplus != prevSurplus) { + prevSurplus = surplus; + + int offset = 0; + for (int i = 0; i < n; ++i) { + const QGridLayoutBox &box = boxes.at(start + i); + int delta = (!ignore.testBit(start + i) && surplus > 0 + && factors[i] > 0 && sizes[i] < box.q_maximumSize) + ? 1 : 0; + + positions[i] += offset; + sizes[i] += delta; + offset += delta; + surplus -= delta; + } + } + Q_ASSERT(surplus == 0); +#endif + } + + if (descents) { + for (int i = 0; i < n; ++i) { + if (ignore.testBit(start + i)) + continue; + const QGridLayoutBox &box = boxes.at(start + i); + descents[i] = fixedDescent(box.q_minimumDescent, box.q_minimumAscent, sizes[i]); + } + } +} + +QGridLayoutBox QGridLayoutRowData::totalBox(int start, int end) const +{ + QGridLayoutBox result; + if (start < end) { + result.q_maximumSize = 0.0; + qreal nextSpacing = 0.0; + for (int i = start; i < end; ++i) { + result.add(boxes.at(i), stretches.at(i), nextSpacing); + nextSpacing = spacings.at(i); + } + } + return result; +} + +void QGridLayoutRowData::stealBox(int start, int end, int which, qreal *positions, qreal *sizes) +{ + qreal offset = 0.0; + qreal nextSpacing = 0.0; + + for (int i = start; i < end; ++i) { + qreal avail = 0.0; + + if (!ignore.testBit(i)) { + const QGridLayoutBox &box = boxes.at(i); + avail = box.q_sizes(which); + offset += nextSpacing; + nextSpacing = spacings.at(i); + } + + *positions++ = offset; + *sizes++ = avail; + offset += avail; + } +} + +#ifdef QT_DEBUG +void QGridLayoutRowData::dump(int indent) const +{ + qDebug("%*sData", indent, ""); + + for (int i = 0; i < ignore.count(); ++i) { + qDebug("%*s Row %d (stretch %d, spacing %g)", indent, "", i, stretches.at(i), + spacings.at(i)); + if (ignore.testBit(i)) + qDebug("%*s Ignored", indent, ""); + boxes.at(i).dump(indent + 2); + } + + MultiCellMap::const_iterator it = multiCellMap.constBegin(); + while (it != multiCellMap.constEnd()) { + qDebug("%*s Multi-cell entry <%d, %d> (stretch %d)", indent, "", it.key().first, + it.key().second, it.value().q_stretch); + it.value().q_box.dump(indent + 2); + } +} +#endif + +QGridLayoutItem::QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, + int row, int column, int rowSpan, int columnSpan, + Qt::Alignment alignment) + : q_engine(engine), q_layoutItem(layoutItem), q_alignment(alignment) +{ + q_firstRows[Hor] = column; + q_firstRows[Ver] = row; + q_rowSpans[Hor] = columnSpan; + q_rowSpans[Ver] = rowSpan; + q_stretches[Hor] = -1; + q_stretches[Ver] = -1; + + q_engine->addItem(this); +} + +int QGridLayoutItem::firstRow(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::firstColumn(Qt::Orientation orientation) const +{ + return q_firstRows[orientation == Qt::Horizontal]; +} + +int QGridLayoutItem::lastRow(Qt::Orientation orientation) const +{ + return firstRow(orientation) + rowSpan(orientation) - 1; +} + +int QGridLayoutItem::lastColumn(Qt::Orientation orientation) const +{ + return firstColumn(orientation) + columnSpan(orientation) - 1; +} + +int QGridLayoutItem::rowSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Vertical]; +} + +int QGridLayoutItem::columnSpan(Qt::Orientation orientation) const +{ + return q_rowSpans[orientation == Qt::Horizontal]; +} + +void QGridLayoutItem::setFirstRow(int row, Qt::Orientation orientation) +{ + q_firstRows[orientation == Qt::Vertical] = row; +} + +void QGridLayoutItem::setRowSpan(int rowSpan, Qt::Orientation orientation) +{ + q_rowSpans[orientation == Qt::Vertical] = rowSpan; +} + +int QGridLayoutItem::stretchFactor(Qt::Orientation orientation) const +{ + int stretch = q_stretches[orientation == Qt::Vertical]; + if (stretch >= 0) + return stretch; + + QSizePolicy::Policy policy = sizePolicy(orientation); + + if (policy & QSizePolicy::ExpandFlag) { + return 1; + } else if (policy & QSizePolicy::GrowFlag) { + return -1; // because we max it up + } else { + return 0; + } +} + +void QGridLayoutItem::setStretchFactor(int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(stretch >= 0); // ### deal with too big stretches + q_stretches[orientation == Qt::Vertical] = stretch; +} + +QSizePolicy::Policy QGridLayoutItem::sizePolicy(Qt::Orientation orientation) const +{ + QSizePolicy sizePolicy(q_layoutItem->sizePolicy()); + return (orientation == Qt::Horizontal) ? sizePolicy.horizontalPolicy() + : sizePolicy.verticalPolicy(); +} + +QSizePolicy::ControlTypes QGridLayoutItem::controlTypes(LayoutSide /* side */) const +{ + return q_layoutItem->sizePolicy().controlType(); +} + +QSizeF QGridLayoutItem::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const +{ + return q_layoutItem->effectiveSizeHint(which, constraint); +} + +QGridLayoutBox QGridLayoutItem::box(Qt::Orientation orientation, qreal constraint) const +{ + QGridLayoutBox result; + QSizePolicy::Policy policy = sizePolicy(orientation); + + if (orientation == Qt::Horizontal) { + QSizeF constraintSize(-1.0, constraint); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).width(); + + if (policy & QSizePolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).width(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).width(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + } else { + QSizeF constraintSize(constraint, -1.0); + + result.q_preferredSize = sizeHint(Qt::PreferredSize, constraintSize).height(); + + if (policy & QSizePolicy::ShrinkFlag) { + result.q_minimumSize = sizeHint(Qt::MinimumSize, constraintSize).height(); + } else { + result.q_minimumSize = result.q_preferredSize; + } + + if (policy & (QSizePolicy::GrowFlag | QSizePolicy::ExpandFlag)) { + result.q_maximumSize = sizeHint(Qt::MaximumSize, constraintSize).height(); + } else { + result.q_maximumSize = result.q_preferredSize; + } + + result.q_minimumDescent = sizeHint(Qt::MinimumDescent, constraintSize).height(); + if (result.q_minimumDescent >= 0.0) + result.q_minimumAscent = result.q_minimumSize - result.q_minimumDescent; + } + if (policy & QSizePolicy::IgnoreFlag) + result.q_preferredSize = result.q_minimumSize; + + return result; +} + +QRectF QGridLayoutItem::geometryWithin(qreal x, qreal y, qreal width, qreal height, + qreal rowDescent) const +{ + rowDescent = -1.0; // ### This disables the descent + + QGridLayoutBox vBox = box(Qt::Vertical); + if (vBox.q_minimumDescent < 0.0 || rowDescent < 0.0) { + qreal cellWidth = width; + qreal cellHeight = height; + + QSizeF size = effectiveMaxSize().boundedTo(QSizeF(cellWidth, cellHeight)); + width = size.width(); + height = size.height(); + + Qt::Alignment align = q_engine->effectiveAlignment(this); + switch (align & Qt::AlignHorizontal_Mask) { + case Qt::AlignHCenter: + x += (cellWidth - width)/2; + break; + case Qt::AlignRight: + x += cellWidth - width; + break; + default: + break; + } + switch (align & Qt::AlignVertical_Mask) { + case Qt::AlignVCenter: + y += (cellHeight - height)/2; + break; + case Qt::AlignBottom: + y += cellHeight - height; + break; + default: + break; + } + return QRectF(x, y, width, height); + } else { + qreal descent = vBox.q_minimumDescent; + qreal ascent = vBox.q_minimumSize - descent; + return QRectF(x, y + height - rowDescent - ascent, width, ascent + descent); + } +} + +void QGridLayoutItem::setGeometry(const QRectF &rect) +{ + q_layoutItem->setGeometry(rect); +} + +void QGridLayoutItem::transpose() +{ + qSwap(q_firstRows[Hor], q_firstRows[Ver]); + qSwap(q_rowSpans[Hor], q_rowSpans[Ver]); + qSwap(q_stretches[Hor], q_stretches[Ver]); +} + +void QGridLayoutItem::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldFirstRow = firstRow(orientation); + if (oldFirstRow >= row) { + setFirstRow(oldFirstRow + delta, orientation); + } else if (lastRow(orientation) >= row) { + setRowSpan(rowSpan(orientation) + delta, orientation); + } +} +/*! + \internal + returns the effective maximumSize, will take the sizepolicy into + consideration. (i.e. if sizepolicy does not have QSizePolicy::Grow, then + maxSizeHint will be the preferredSize) + Note that effectiveSizeHint does not take sizePolicy into consideration, + (since it only evaluates the hints, as the name implies) +*/ +QSizeF QGridLayoutItem::effectiveMaxSize() const +{ + QSizeF size; + bool vGrow = (sizePolicy(Qt::Vertical) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag; + bool hGrow = (sizePolicy(Qt::Horizontal) & QSizePolicy::GrowFlag) == QSizePolicy::GrowFlag; + if (!vGrow || !hGrow) { + QSizeF pref = layoutItem()->effectiveSizeHint(Qt::PreferredSize); + if (!vGrow) + size.setHeight(pref.height()); + if (!hGrow) + size.setWidth(pref.width()); + } + + if (!size.isValid()) { + QSizeF maxSize = layoutItem()->effectiveSizeHint(Qt::MaximumSize); + if (size.width() == -1) + size.setWidth(maxSize.width()); + if (size.height() == -1) + size.setHeight(maxSize.height()); + } + return size; +} + +#ifdef QT_DEBUG +void QGridLayoutItem::dump(int indent) const +{ + qDebug("%*s%p (%d, %d) %d x %d", indent, "", q_layoutItem, firstRow(), firstColumn(), + rowSpan(), columnSpan()); + + if (q_stretches[Hor] >= 0) + qDebug("%*s Horizontal stretch: %d", indent, "", q_stretches[Hor]); + if (q_stretches[Ver] >= 0) + qDebug("%*s Vertical stretch: %d", indent, "", q_stretches[Ver]); + if (q_alignment != 0) + qDebug("%*s Alignment: %x", indent, "", uint(q_alignment)); + qDebug("%*s Horizontal size policy: %x Vertical size policy: %x", + indent, "", sizePolicy(Qt::Horizontal), sizePolicy(Qt::Vertical)); +} +#endif + +void QGridLayoutRowInfo::insertOrRemoveRows(int row, int delta) +{ + count += delta; + + insertOrRemoveItems(stretches, row, delta); + insertOrRemoveItems(spacings, row, delta); + insertOrRemoveItems(alignments, row, delta); + insertOrRemoveItems(boxes, row, delta); +} + +#ifdef QT_DEBUG +void QGridLayoutRowInfo::dump(int indent) const +{ + qDebug("%*sInfo (count: %d)", indent, "", count); + for (int i = 0; i < count; ++i) { + QString message; + + if (stretches.value(i).value() >= 0) + message += QString::fromAscii(" stretch %1").arg(stretches.value(i).value()); + if (spacings.value(i).value() >= 0.0) + message += QString::fromAscii(" spacing %1").arg(spacings.value(i).value()); + if (alignments.value(i) != 0) + message += QString::fromAscii(" alignment %1").arg(int(alignments.value(i)), 16); + + if (!message.isEmpty() || boxes.value(i) != QGridLayoutBox()) { + qDebug("%*s Row %d:%s", indent, "", i, qPrintable(message)); + if (boxes.value(i) != QGridLayoutBox()) + boxes.value(i).dump(indent + 1); + } + } +} +#endif + +QGridLayoutEngine::QGridLayoutEngine() +{ + m_visualDirection = Qt::LeftToRight; + invalidate(); +} + +int QGridLayoutEngine::rowCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].count; +} + +int QGridLayoutEngine::columnCount(Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Horizontal].count; +} + +int QGridLayoutEngine::itemCount() const +{ + return q_items.count(); +} + +QGridLayoutItem *QGridLayoutEngine::itemAt(int index) const +{ + Q_ASSERT(index >= 0 && index < itemCount()); + return q_items.at(index); +} + +int QGridLayoutEngine::effectiveFirstRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveFirstRows[orientation == Qt::Vertical]; +} + +int QGridLayoutEngine::effectiveLastRow(Qt::Orientation orientation) const +{ + ensureEffectiveFirstAndLastRows(); + return q_cachedEffectiveLastRows[orientation == Qt::Vertical]; +} + +void QGridLayoutEngine::setSpacing(qreal spacing, Qt::Orientations orientations) +{ + Q_ASSERT(spacing >= 0.0); + if (orientations & Qt::Horizontal) + q_defaultSpacings[Hor].setUserValue(spacing); + if (orientations & Qt::Vertical) + q_defaultSpacings[Ver].setUserValue(spacing); + + invalidate(); +} + +qreal QGridLayoutEngine::spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const +{ + if (q_defaultSpacings[orientation == Qt::Vertical].isDefault()) { + QStyle *style = styleInfo.style(); + QStyleOption option; + option.initFrom(styleInfo.widget()); + qreal defaultSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing + : QStyle::PM_LayoutHorizontalSpacing, &option, styleInfo.widget()); + q_defaultSpacings[orientation == Qt::Vertical].setCachedValue(defaultSpacing); + } + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowSpacing(int row, qreal spacing, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.spacings.count()) + rowInfo.spacings.resize(row + 1); + if (spacing >= 0) + rowInfo.spacings[row].setUserValue(spacing); + else + rowInfo.spacings[row] = QLayoutParameter<qreal>(); + invalidate(); +} + +qreal QGridLayoutEngine::rowSpacing(int row, Qt::Orientation orientation) const +{ + QLayoutParameter<qreal> spacing = q_infos[orientation == Qt::Vertical].spacings.value(row); + if (!spacing.isDefault()) + return spacing.value(); + return q_defaultSpacings[orientation == Qt::Vertical].value(); +} + +void QGridLayoutEngine::setRowStretchFactor(int row, int stretch, Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(stretch >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.stretches.count()) + rowInfo.stretches.resize(row + 1); + rowInfo.stretches[row].setUserValue(stretch); +} + +int QGridLayoutEngine::rowStretchFactor(int row, Qt::Orientation orientation) const +{ + QStretchParameter stretch = q_infos[orientation == Qt::Vertical].stretches.value(row); + if (!stretch.isDefault()) + return stretch.value(); + return 0; +} + +void QGridLayoutEngine::setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch, + Qt::Orientation orientation) +{ + Q_ASSERT(stretch >= 0); + + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + item->setStretchFactor(stretch, orientation); +} + +int QGridLayoutEngine::stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const +{ + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + return item->stretchFactor(orientation); + return 0; +} + +void QGridLayoutEngine::setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + Q_ASSERT(size >= 0.0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.boxes.count()) + rowInfo.boxes.resize(row + 1); + rowInfo.boxes[row].q_sizes(which) = size; +} + +qreal QGridLayoutEngine::rowSizeHint(Qt::SizeHint which, int row, Qt::Orientation orientation) const +{ + return q_infos[orientation == Qt::Vertical].boxes.value(row).q_sizes(which); +} + +void QGridLayoutEngine::setRowAlignment(int row, Qt::Alignment alignment, + Qt::Orientation orientation) +{ + Q_ASSERT(row >= 0); + + maybeExpandGrid(row, -1, orientation); + + QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + if (row >= rowInfo.alignments.count()) + rowInfo.alignments.resize(row + 1); + rowInfo.alignments[row] = alignment; +} + +Qt::Alignment QGridLayoutEngine::rowAlignment(int row, Qt::Orientation orientation) const +{ + Q_ASSERT(row >= 0); + return q_infos[orientation == Qt::Vertical].alignments.value(row); +} + +void QGridLayoutEngine::setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment) +{ + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + item->setAlignment(alignment); + invalidate(); +} + +Qt::Alignment QGridLayoutEngine::alignment(QGraphicsLayoutItem *layoutItem) const +{ + if (QGridLayoutItem *item = findLayoutItem(layoutItem)) + return item->alignment(); + return 0; +} + +Qt::Alignment QGridLayoutEngine::effectiveAlignment(const QGridLayoutItem *layoutItem) const +{ + Qt::Alignment align = layoutItem->alignment(); + if (!(align & Qt::AlignVertical_Mask)) { + // no vertical alignment, respect the row alignment + int y = layoutItem->firstRow(); + align |= (rowAlignment(y, Qt::Vertical) & Qt::AlignVertical_Mask); + } + if (!(align & Qt::AlignHorizontal_Mask)) { + // no horizontal alignment, respect the column alignment + int x = layoutItem->firstColumn(); + align |= (rowAlignment(x, Qt::Horizontal) & Qt::AlignHorizontal_Mask); + } + return align; +} + +void QGridLayoutEngine::addItem(QGridLayoutItem *item) +{ + maybeExpandGrid(item->lastRow(), item->lastColumn()); + + q_items.append(item); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j)) + qWarning("QGridLayoutEngine::addItem: Cell (%d, %d) already taken", i, j); + setItemAt(i, j, item); + } + } +} + +void QGridLayoutEngine::removeItem(QGridLayoutItem *item) +{ + Q_ASSERT(q_items.contains(item)); + + invalidate(); + + for (int i = item->firstRow(); i <= item->lastRow(); ++i) { + for (int j = item->firstColumn(); j <= item->lastColumn(); ++j) { + if (itemAt(i, j) == item) + setItemAt(i, j, 0); + } + } + + q_items.removeAll(item); +} + +QGridLayoutItem *QGridLayoutEngine::findLayoutItem(QGraphicsLayoutItem *layoutItem) const +{ + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + if (item->layoutItem() == layoutItem) + return item; + } + return 0; +} + +QGridLayoutItem *QGridLayoutEngine::itemAt(int row, int column, Qt::Orientation orientation) const +{ + if (orientation == Qt::Horizontal) + qSwap(row, column); + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount())) + return 0; + return q_grid.at((row * internalGridColumnCount()) + column); +} + +void QGridLayoutEngine::invalidate() +{ + q_cachedEffectiveFirstRows[Hor] = -1; + q_cachedEffectiveFirstRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + q_cachedEffectiveLastRows[Ver] = -1; + q_cachedDataForStyleInfo.invalidate(); + q_cachedSize = QSizeF(); +} + +static void visualRect(QRectF *geom, Qt::LayoutDirection dir, const QRectF &contentsRect) +{ + if (dir == Qt::RightToLeft) + geom->moveRight(contentsRect.right() - (geom->left() - contentsRect.left())); +} + +void QGridLayoutEngine::setGeometries(const QLayoutStyleInfo &styleInfo, + const QRectF &contentsGeometry) +{ + if (rowCount() < 1 || columnCount() < 1) + return; + + ensureGeometries(styleInfo, contentsGeometry.size()); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + qreal x = q_xx[item->firstColumn()]; + qreal y = q_yy[item->firstRow()]; + qreal width = q_widths[item->lastColumn()]; + qreal height = q_heights[item->lastRow()]; + + if (item->columnSpan() != 1) + width += q_xx[item->lastColumn()] - x; + if (item->rowSpan() != 1) + height += q_yy[item->lastRow()] - y; + + QRectF geom = item->geometryWithin(contentsGeometry.x() + x, contentsGeometry.y() + y, + width, height, q_descents[item->lastRow()]); + visualRect(&geom, visualDirection(), contentsGeometry); + item->setGeometry(geom); + } +} + +// ### candidate for deletion +QRectF QGridLayoutEngine::cellRect(const QLayoutStyleInfo &styleInfo, + const QRectF &contentsGeometry, int row, int column, int rowSpan, + int columnSpan) const +{ + if (uint(row) >= uint(rowCount()) || uint(column) >= uint(columnCount()) + || rowSpan < 1 || columnSpan < 1) + return QRectF(); + + ensureGeometries(styleInfo, contentsGeometry.size()); + + int lastColumn = qMax(column + columnSpan, columnCount()) - 1; + int lastRow = qMax(row + rowSpan, rowCount()) - 1; + + qreal x = q_xx[column]; + qreal y = q_yy[row]; + qreal width = q_widths[lastColumn]; + qreal height = q_heights[lastRow]; + + if (columnSpan != 1) + width += q_xx[lastColumn] - x; + if (rowSpan != 1) + height += q_yy[lastRow] - y; + + return QRectF(contentsGeometry.x() + x, contentsGeometry.y() + y, width, height); +} + +QSizeF QGridLayoutEngine::sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which, + const QSizeF & /* constraint */) const +{ + ensureColumnAndRowData(styleInfo); + + switch (which) { + case Qt::MinimumSize: + return QSizeF(q_totalBoxes[Hor].q_minimumSize, q_totalBoxes[Ver].q_minimumSize); + case Qt::PreferredSize: + return QSizeF(q_totalBoxes[Hor].q_preferredSize, q_totalBoxes[Ver].q_preferredSize); + case Qt::MaximumSize: + return QSizeF(q_totalBoxes[Hor].q_maximumSize, q_totalBoxes[Ver].q_maximumSize); + case Qt::MinimumDescent: + return QSizeF(-1.0, q_totalBoxes[Hor].q_minimumDescent); // ### doesn't work + default: + break; + } + return QSizeF(); +} + +QSizePolicy::ControlTypes QGridLayoutEngine::controlTypes(LayoutSide side) const +{ + Qt::Orientation orientation = (side == Top || side == Bottom) ? Qt::Vertical : Qt::Horizontal; + int row = (side == Top || side == Left) ? effectiveFirstRow(orientation) + : effectiveLastRow(orientation); + QSizePolicy::ControlTypes result = 0; + + for (int column = columnCount(orientation) - 1; column >= 0; --column) { + if (QGridLayoutItem *item = itemAt(row, column, orientation)) + result |= item->controlTypes(side); + } + return result; +} + +void QGridLayoutEngine::transpose() +{ + invalidate(); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->transpose(); + + qSwap(q_defaultSpacings[Hor], q_defaultSpacings[Ver]); + qSwap(q_infos[Hor], q_infos[Ver]); + + regenerateGrid(); +} + +void QGridLayoutEngine::setVisualDirection(Qt::LayoutDirection direction) +{ + m_visualDirection = direction; +} + +Qt::LayoutDirection QGridLayoutEngine::visualDirection() const +{ + return m_visualDirection; +} + +#ifdef QT_DEBUG +void QGridLayoutEngine::dump(int indent) const +{ + qDebug("%*sEngine", indent, ""); + + qDebug("%*s Items (%d)", indent, "", q_items.count()); + int i; + for (i = 0; i < q_items.count(); ++i) + q_items.at(i)->dump(indent + 2); + + qDebug("%*s Grid (%d x %d)", indent, "", internalGridRowCount(), + internalGridColumnCount()); + for (int row = 0; row < internalGridRowCount(); ++row) { + QString message = QLatin1String("[ "); + for (int column = 0; column < internalGridColumnCount(); ++column) { + message += QString::number(q_items.indexOf(itemAt(row, column))).rightJustified(3); + message += QLatin1String(" "); + } + message += QLatin1String("]"); + qDebug("%*s %s", indent, "", qPrintable(message)); + } + + if (q_defaultSpacings[Hor].value() >= 0.0 || q_defaultSpacings[Ver].value() >= 0.0) + qDebug("%*s Default spacings: %g %g", indent, "", q_defaultSpacings[Hor].value(), + q_defaultSpacings[Ver].value()); + + qDebug("%*s Column and row info", indent, ""); + q_infos[Hor].dump(indent + 2); + q_infos[Ver].dump(indent + 2); + + qDebug("%*s Column and row data", indent, ""); + q_columnData.dump(indent + 2); + q_rowData.dump(indent + 2); + + qDebug("%*s Geometries output", indent, ""); + for (int pass = 0; pass < 2; ++pass) { + QVector<qreal> &cellPos = q_yy; + QString message; + for (i = 0; i < cellPos.count(); ++i) { + message += QLatin1String((message.isEmpty() ? "[" : ", ")); + message += QString::number(cellPos.at(i)); + } + message += QLatin1String("]"); + qDebug("%*s %s %s", indent, "", (pass == 0 ? "rows:" : "columns:"), qPrintable(message)); + cellPos = q_xx; + } +} +#endif + +void QGridLayoutEngine::maybeExpandGrid(int row, int column, Qt::Orientation orientation) +{ + invalidate(); // ### move out of here? + + if (orientation == Qt::Horizontal) + qSwap(row, column); + + if (row < rowCount() && column < columnCount()) + return; + + int oldGridRowCount = internalGridRowCount(); + int oldGridColumnCount = internalGridColumnCount(); + + q_infos[Ver].count = qMax(row + 1, rowCount()); + q_infos[Hor].count = qMax(column + 1, columnCount()); + + int newGridRowCount = internalGridRowCount(); + int newGridColumnCount = internalGridColumnCount(); + + int newGridSize = newGridRowCount * newGridColumnCount; + if (newGridSize != q_grid.count()) { + q_grid.resize(newGridSize); + + if (newGridColumnCount != oldGridColumnCount) { + for (int i = oldGridRowCount - 1; i >= 1; --i) { + for (int j = oldGridColumnCount - 1; j >= 0; --j) { + int oldIndex = (i * oldGridColumnCount) + j; + int newIndex = (i * newGridColumnCount) + j; + + Q_ASSERT(newIndex > oldIndex); + q_grid[newIndex] = q_grid[oldIndex]; + q_grid[oldIndex] = 0; + } + } + } + } +} + +void QGridLayoutEngine::regenerateGrid() +{ + q_grid.fill(0); + + for (int i = q_items.count() - 1; i >= 0; --i) { + QGridLayoutItem *item = q_items.at(i); + + for (int j = item->firstRow(); j <= item->lastRow(); ++j) { + for (int k = item->firstColumn(); k <= item->lastColumn(); ++k) { + setItemAt(j, k, item); + } + } + } +} + +void QGridLayoutEngine::setItemAt(int row, int column, QGridLayoutItem *item) +{ + Q_ASSERT(row >= 0 && row < rowCount()); + Q_ASSERT(column >= 0 && column < columnCount()); + q_grid[(row * internalGridColumnCount()) + column] = item; +} + +void QGridLayoutEngine::insertOrRemoveRows(int row, int delta, Qt::Orientation orientation) +{ + int oldRowCount = rowCount(orientation); + Q_ASSERT(uint(row) <= uint(oldRowCount)); + + invalidate(); + + // appending rows (or columns) is easy + if (row == oldRowCount && delta > 0) { + maybeExpandGrid(oldRowCount + delta - 1, -1, orientation); + return; + } + + q_infos[orientation == Qt::Vertical].insertOrRemoveRows(row, delta); + + for (int i = q_items.count() - 1; i >= 0; --i) + q_items.at(i)->insertOrRemoveRows(row, delta, orientation); + + q_grid.resize(internalGridRowCount() * internalGridColumnCount()); + regenerateGrid(); +} + +void QGridLayoutEngine::fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo, + Qt::Orientation orientation) const +{ + const int ButtonMask = QSizePolicy::ButtonBox | QSizePolicy::PushButton; + const QGridLayoutRowInfo &rowInfo = q_infos[orientation == Qt::Vertical]; + const QGridLayoutRowInfo &columnInfo = q_infos[orientation == Qt::Horizontal]; + LayoutSide top = (orientation == Qt::Vertical) ? Top : Left; + LayoutSide bottom = (orientation == Qt::Vertical) ? Bottom : Right; + + QStyle *style = styleInfo.style(); + QStyleOption option; + option.initFrom(styleInfo.widget()); + + const QLayoutParameter<qreal> &defaultSpacing = q_defaultSpacings[orientation == Qt::Vertical]; + qreal innerSpacing = 0.0; + if (style) + innerSpacing = (qreal)style->pixelMetric(orientation == Qt::Vertical ? QStyle::PM_LayoutVerticalSpacing + : QStyle::PM_LayoutHorizontalSpacing, + &option, styleInfo.widget()); + if (innerSpacing >= 0.0) + defaultSpacing.setCachedValue(innerSpacing); + + for (int row = 0; row < rowInfo.count; ++row) { + bool rowIsEmpty = true; + bool rowIsIdenticalToPrevious = (row > 0); + + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + + if (rowIsIdenticalToPrevious && item != itemAt(row - 1, column, orientation)) + rowIsIdenticalToPrevious = false; + + if (item) + rowIsEmpty = false; + } + + if ((rowIsEmpty || rowIsIdenticalToPrevious) + && rowInfo.spacings.value(row).isDefault() + && rowInfo.stretches.value(row).isDefault() + && rowInfo.boxes.value(row) == QGridLayoutBox()) + rowData->ignore.setBit(row, true); + + if (rowInfo.spacings.value(row).isUser()) { + rowData->spacings[row] = rowInfo.spacings.at(row).value(); + } else if (!defaultSpacing.isDefault()) { + rowData->spacings[row] = defaultSpacing.value(); + } + + rowData->stretches[row] = rowInfo.stretches.value(row).value(); + } + + struct RowAdHocData { + int q_row; + unsigned int q_hasButtons : 8; + unsigned int q_hasNonButtons : 8; + + inline RowAdHocData() : q_row(-1), q_hasButtons(false), q_hasNonButtons(false) {} + inline void init(int row) { + this->q_row = row; + q_hasButtons = false; + q_hasNonButtons = false; + } + inline bool hasOnlyButtons() const { return q_hasButtons && !q_hasNonButtons; } + inline bool hasOnlyNonButtons() const { return q_hasNonButtons && !q_hasButtons; } + }; + RowAdHocData lastRowAdHocData; + RowAdHocData nextToLastRowAdHocData; + RowAdHocData nextToNextToLastRowAdHocData; + + rowData->hasIgnoreFlag = false; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + QGridLayoutBox &rowBox = rowData->boxes[row]; + if (option.state & QStyle::State_Window) { + nextToNextToLastRowAdHocData = nextToLastRowAdHocData; + nextToLastRowAdHocData = lastRowAdHocData; + lastRowAdHocData.init(row); + } + + bool userRowStretch = rowInfo.stretches.value(row).isUser(); + int &rowStretch = rowData->stretches[row]; + + bool hasIgnoreFlag = true; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item = itemAt(row, column, orientation); + if (item) { + int itemRow = item->firstRow(orientation); + int itemColumn = item->firstColumn(orientation); + + if (itemRow == row && itemColumn == column) { + int itemStretch = item->stretchFactor(orientation); + if (!(item->sizePolicy(orientation) & QSizePolicy::IgnoreFlag)) + hasIgnoreFlag = false; + int itemRowSpan = item->rowSpan(orientation); + + int effectiveRowSpan = 1; + for (int i = 1; i < itemRowSpan; ++i) { + if (!rowData->ignore.testBit(i)) + ++effectiveRowSpan; + } + + QGridLayoutBox *box; + if (effectiveRowSpan == 1) { + box = &rowBox; + if (!userRowStretch) + rowStretch = qMax(rowStretch, itemStretch); + } else { + QGridLayoutMultiCellData &multiCell = + rowData->multiCellMap[qMakePair(row, effectiveRowSpan)]; + box = &multiCell.q_box; + multiCell.q_stretch = itemStretch; + } + box->combine(item->box(orientation)); + + if (effectiveRowSpan == 1) { + QSizePolicy::ControlTypes controls = item->controlTypes(top); + if (controls & ButtonMask) + lastRowAdHocData.q_hasButtons = true; + if (controls & ~ButtonMask) + lastRowAdHocData.q_hasNonButtons = true; + } + } + } + } + if (row < rowInfo.boxes.count()) { + QGridLayoutBox rowBoxInfo = rowInfo.boxes.at(row); + rowBoxInfo.normalize(); + rowBox.q_minimumSize = qMax(rowBox.q_minimumSize, rowBoxInfo.q_minimumSize); + rowBox.q_maximumSize = qMax(rowBox.q_minimumSize, + (rowBoxInfo.q_maximumSize != FLT_MAX ? + rowBoxInfo.q_maximumSize : rowBox.q_maximumSize)); + rowBox.q_preferredSize = qBound(rowBox.q_minimumSize, + qMax(rowBox.q_preferredSize, rowBoxInfo.q_preferredSize), + rowBox.q_maximumSize); + } + if (hasIgnoreFlag) + rowData->hasIgnoreFlag = true; + } + + /* + Heuristic: Detect button boxes that don't use QSizePolicy::ButtonBox. + This is somewhat ad hoc but it usually does the trick. + */ + bool lastRowIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyNonButtons()); + bool lastTwoRowsIsButtonBox = (lastRowAdHocData.hasOnlyButtons() + && nextToLastRowAdHocData.hasOnlyButtons() + && nextToNextToLastRowAdHocData.hasOnlyNonButtons() + && orientation == Qt::Vertical); + + if (defaultSpacing.isDefault()) { + int prevRow = -1; + for (int row = 0; row < rowInfo.count; ++row) { + if (rowData->ignore.testBit(row)) + continue; + + if (prevRow != -1 && !rowInfo.spacings.value(prevRow).isUser()) { + qreal &rowSpacing = rowData->spacings[prevRow]; + for (int column = 0; column < columnInfo.count; ++column) { + QGridLayoutItem *item1 = itemAt(prevRow, column, orientation); + QGridLayoutItem *item2 = itemAt(row, column, orientation); + + if (item1 && item2 && item1 != item2) { + QSizePolicy::ControlTypes controls1 = item1->controlTypes(bottom); + QSizePolicy::ControlTypes controls2 = item2->controlTypes(top); + + if (controls2 & QSizePolicy::PushButton) { + if ((row == nextToLastRowAdHocData.q_row && lastTwoRowsIsButtonBox) + || (row == lastRowAdHocData.q_row && lastRowIsButtonBox)) { + controls2 &= ~QSizePolicy::PushButton; + controls2 |= QSizePolicy::ButtonBox; + } + } + + qreal spacing = style->combinedLayoutSpacing(controls1, controls2, + orientation, &option, + styleInfo.widget()); + if (orientation == Qt::Horizontal) { + qreal width1 = rowData->boxes.at(prevRow).q_minimumSize; + qreal width2 = rowData->boxes.at(row).q_minimumSize; + QRectF rect1 = item1->geometryWithin(0.0, 0.0, width1, FLT_MAX, -1.0); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, width2, FLT_MAX, -1.0); + spacing -= (width1 - (rect1.x() + rect1.width())) + rect2.x(); + } else { + const QGridLayoutBox &box1 = rowData->boxes.at(prevRow); + const QGridLayoutBox &box2 = rowData->boxes.at(row); + qreal height1 = box1.q_minimumSize; + qreal height2 = box2.q_minimumSize; + qreal rowDescent1 = fixedDescent(box1.q_minimumDescent, + box1.q_minimumAscent, height1); + qreal rowDescent2 = fixedDescent(box2.q_minimumDescent, + box2.q_minimumAscent, height2); + QRectF rect1 = item1->geometryWithin(0.0, 0.0, FLT_MAX, height1, + rowDescent1); + QRectF rect2 = item2->geometryWithin(0.0, 0.0, FLT_MAX, height2, + rowDescent2); + spacing -= (height1 - (rect1.y() + rect1.height())) + rect2.y(); + } + rowSpacing = qMax(spacing, rowSpacing); + } + } + } + prevRow = row; + } + } else if (lastRowIsButtonBox || lastTwoRowsIsButtonBox) { + /* + Even for styles that define a uniform spacing, we cheat a + bit and use the window margin as the spacing. This + significantly improves the look of dialogs. + */ + int prevRow = lastRowIsButtonBox ? nextToLastRowAdHocData.q_row + : nextToNextToLastRowAdHocData.q_row; + if (!defaultSpacing.isUser() && !rowInfo.spacings.value(prevRow).isUser()) { + qreal windowMargin = style->pixelMetric(orientation == Qt::Vertical + ? QStyle::PM_LayoutBottomMargin + : QStyle::PM_LayoutRightMargin, + &option, styleInfo.widget()); + + qreal &rowSpacing = rowData->spacings[prevRow]; + rowSpacing = qMax(windowMargin, rowSpacing); + } + } +} + +void QGridLayoutEngine::ensureEffectiveFirstAndLastRows() const +{ + if (q_cachedEffectiveFirstRows[Hor] == -1 && !q_items.isEmpty()) { + int rowCount = this->rowCount(); + int columnCount = this->columnCount(); + + q_cachedEffectiveFirstRows[Ver] = rowCount; + q_cachedEffectiveFirstRows[Hor] = columnCount; + q_cachedEffectiveLastRows[Ver] = -1; + q_cachedEffectiveLastRows[Hor] = -1; + + for (int i = q_items.count() - 1; i >= 0; --i) { + const QGridLayoutItem *item = q_items.at(i); + + for (int j = 0; j < NOrientations; ++j) { + Qt::Orientation orientation = (j == Hor) ? Qt::Horizontal : Qt::Vertical; + if (item->firstRow(orientation) < q_cachedEffectiveFirstRows[j]) + q_cachedEffectiveFirstRows[j] = item->firstRow(orientation); + if (item->lastRow(orientation) > q_cachedEffectiveLastRows[j]) + q_cachedEffectiveLastRows[j] = item->lastRow(orientation); + } + } + } +} + +void QGridLayoutEngine::ensureColumnAndRowData(const QLayoutStyleInfo &styleInfo) const +{ + if (q_cachedDataForStyleInfo == styleInfo) + return; + + q_columnData.reset(columnCount()); + q_rowData.reset(rowCount()); + + fillRowData(&q_columnData, styleInfo, Qt::Horizontal); + fillRowData(&q_rowData, styleInfo, Qt::Vertical); + + q_columnData.distributeMultiCells(); + q_rowData.distributeMultiCells(); + + q_totalBoxes[Hor] = q_columnData.totalBox(0, columnCount()); + q_totalBoxes[Ver] = q_rowData.totalBox(0, rowCount()); + + q_cachedDataForStyleInfo = styleInfo; +} + +void QGridLayoutEngine::ensureGeometries(const QLayoutStyleInfo &styleInfo, + const QSizeF &size) const +{ + ensureColumnAndRowData(styleInfo); + if (q_cachedSize == size) + return; + + q_xx.resize(columnCount()); + q_yy.resize(rowCount()); + q_widths.resize(columnCount()); + q_heights.resize(rowCount()); + q_descents.resize(rowCount()); + q_columnData.calculateGeometries(0, columnCount(), size.width(), q_xx.data(), q_widths.data(), + 0, q_totalBoxes[Hor]); + q_rowData.calculateGeometries(0, rowCount(), size.height(), q_yy.data(), q_heights.data(), + q_descents.data(), q_totalBoxes[Ver]); + + q_cachedSize = size; +} + +QT_END_NAMESPACE + +#endif //QT_NO_GRAPHICSVIEW diff --git a/src/gui/graphicsview/qgridlayoutengine_p.h b/src/gui/graphicsview/qgridlayoutengine_p.h new file mode 100644 index 0000000..acc96de --- /dev/null +++ b/src/gui/graphicsview/qgridlayoutengine_p.h @@ -0,0 +1,449 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#ifndef QGRIDLAYOUTENGINE_P_H +#define QGRIDLAYOUTENGINE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the graphics view layout classes. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "qalgorithms.h" +#include "qbitarray.h" +#include "qlist.h" +#include "qmap.h" +#include "qpair.h" +#include "qvector.h" + +#include <float.h> + +QT_BEGIN_NAMESPACE + +class QGraphicsLayoutItem; +class QStyle; +class QWidget; + +// ### deal with Descent in a similar way +enum { + MinimumSize = Qt::MinimumSize, + PreferredSize = Qt::PreferredSize, + MaximumSize = Qt::MaximumSize, + NSizes +}; + +// do not reorder +enum { + Hor, + Ver, + NOrientations +}; + +// do not reorder +enum LayoutSide { + Left, + Top, + Right, + Bottom +}; + +template <typename T> +class QLayoutParameter +{ +public: + enum State { Default, User, Cached }; + + inline QLayoutParameter() : q_value(T()), q_state(Default) {} + inline QLayoutParameter(T value, State state = Default) : q_value(value), q_state(state) {} + + inline void setUserValue(T value) { + q_value = value; + q_state = User; + } + inline void setCachedValue(T value) const { + if (q_state != User) { + q_value = value; + q_state = Cached; + } + } + inline T value() const { return q_value; } + inline T value(T defaultValue) const { return isUser() ? q_value : defaultValue; } + inline bool isDefault() const { return q_state == Default; } + inline bool isUser() const { return q_state == User; } + inline bool isCached() const { return q_state == Cached; } + +private: + mutable T q_value; + mutable State q_state; +}; + +class QStretchParameter : public QLayoutParameter<int> +{ +public: + QStretchParameter() : QLayoutParameter<int>(-1) {} + +}; + +class QLayoutStyleInfo +{ +public: + inline QLayoutStyleInfo() { invalidate(); } + inline QLayoutStyleInfo(QStyle *style, QWidget *widget) + : q_valid(true), q_style(style), q_widget(widget) {} + + inline void invalidate() { q_valid = false; q_style = 0; q_widget = 0; } + + inline QStyle *style() const { return q_style; } + inline QWidget *widget() const { return q_widget; } + + inline bool operator==(const QLayoutStyleInfo &other) + { return q_style == other.q_style && q_widget == other.q_widget; } + inline bool operator!=(const QLayoutStyleInfo &other) + { return !(*this == other); } + +private: + bool q_valid; + QStyle *q_style; + QWidget *q_widget; +}; + +class QGridLayoutBox +{ +public: + inline QGridLayoutBox() + : q_minimumSize(0), q_preferredSize(0), q_maximumSize(FLT_MAX), + q_minimumDescent(-1), q_minimumAscent(-1) {} + + void add(const QGridLayoutBox &other, int stretch, qreal spacing); + void combine(const QGridLayoutBox &other); + void normalize(); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + // This code could use the union-struct-array trick, but a compiler + // bug prevents this from working. + qreal q_minimumSize; + qreal q_preferredSize; + qreal q_maximumSize; + qreal q_minimumDescent; + qreal q_minimumAscent; + inline qreal &q_sizes(int which) + { + qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } + inline const qreal &q_sizes(int which) const + { + const qreal *t; + switch (which) { + case Qt::MinimumSize: + t = &q_minimumSize; + break; + case Qt::PreferredSize: + t = &q_preferredSize; + break; + case Qt::MaximumSize: + t = &q_maximumSize; + break; + case Qt::MinimumDescent: + t = &q_minimumDescent; + break; + case (Qt::MinimumDescent + 1): + t = &q_minimumAscent; + break; + default: + t = 0; + break; + } + return *t; + } +}; + +bool operator==(const QGridLayoutBox &box1, const QGridLayoutBox &box2); +inline bool operator!=(const QGridLayoutBox &box1, const QGridLayoutBox &box2) + { return !operator==(box1, box2); } + +class QGridLayoutMultiCellData +{ +public: + inline QGridLayoutMultiCellData() : q_stretch(-1) {} + + QGridLayoutBox q_box; + int q_stretch; +}; + +typedef QMap<QPair<int, int>, QGridLayoutMultiCellData> MultiCellMap; + +class QGridLayoutRowData +{ +public: + void reset(int count); + void distributeMultiCells(); + void calculateGeometries(int start, int end, qreal targetSize, qreal *positions, qreal *sizes, + qreal *descents, const QGridLayoutBox &totalBox); + QGridLayoutBox totalBox(int start, int end) const; + void stealBox(int start, int end, int which, qreal *positions, qreal *sizes); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + + QBitArray ignore; // ### rename q_ + QVector<QGridLayoutBox> boxes; + MultiCellMap multiCellMap; + QVector<int> stretches; + QVector<qreal> spacings; + bool hasIgnoreFlag; +}; + +class QGridLayoutEngine; + +class QGridLayoutItem +{ +public: + QGridLayoutItem(QGridLayoutEngine *engine, QGraphicsLayoutItem *layoutItem, int row, int column, + int rowSpan = 1, int columnSpan = 1, Qt::Alignment alignment = 0); + + inline int firstRow() const { return q_firstRows[Ver]; } + inline int firstColumn() const { return q_firstRows[Hor]; } + inline int rowSpan() const { return q_rowSpans[Ver]; } + inline int columnSpan() const { return q_rowSpans[Hor]; } + inline int lastRow() const { return firstRow() + rowSpan() - 1; } + inline int lastColumn() const { return firstColumn() + columnSpan() - 1; } + + int firstRow(Qt::Orientation orientation) const; + int firstColumn(Qt::Orientation orientation) const; + int lastRow(Qt::Orientation orientation) const; + int lastColumn(Qt::Orientation orientation) const; + int rowSpan(Qt::Orientation orientation) const; + int columnSpan(Qt::Orientation orientation) const; + void setFirstRow(int row, Qt::Orientation orientation = Qt::Vertical); + void setRowSpan(int rowSpan, Qt::Orientation orientation = Qt::Vertical); + + int stretchFactor(Qt::Orientation orientation) const; + void setStretchFactor(int stretch, Qt::Orientation orientation); + + inline Qt::Alignment alignment() const { return q_alignment; } + inline void setAlignment(Qt::Alignment alignment) { q_alignment = alignment; } + + QSizePolicy::Policy sizePolicy(Qt::Orientation orientation) const; + QSizePolicy::ControlTypes controlTypes(LayoutSide side) const; + QSizeF sizeHint(Qt::SizeHint which, const QSizeF &constraint = QSizeF()) const; + QGridLayoutBox box(Qt::Orientation orientation, qreal constraint = -1.0) const; + QRectF geometryWithin(qreal x, qreal y, qreal width, qreal height, qreal rowDescent) const; + + QGraphicsLayoutItem *layoutItem() const { return q_layoutItem; } + + void setGeometry(const QRectF &rect); + void transpose(); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + QSizeF effectiveMaxSize() const; + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + +private: + QGridLayoutEngine *q_engine; // ### needed? + QGraphicsLayoutItem *q_layoutItem; + int q_firstRows[NOrientations]; + int q_rowSpans[NOrientations]; + int q_stretches[NOrientations]; + Qt::Alignment q_alignment; +}; + +class QGridLayoutRowInfo +{ +public: + inline QGridLayoutRowInfo() : count(0) {} + + void insertOrRemoveRows(int row, int delta); + +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + + int count; + QVector<QStretchParameter> stretches; + QVector<QLayoutParameter<qreal> > spacings; + QVector<Qt::Alignment> alignments; + QVector<QGridLayoutBox> boxes; +}; + +class QGridLayoutEngine +{ +public: + QGridLayoutEngine(); + inline ~QGridLayoutEngine() { qDeleteAll(q_items); } + + int rowCount(Qt::Orientation orientation) const; + int columnCount(Qt::Orientation orientation) const; + inline int rowCount() const { return q_infos[Ver].count; } + inline int columnCount() const { return q_infos[Hor].count; } + // returns the number of items inserted, which may be less than (rowCount * columnCount) + int itemCount() const; + QGridLayoutItem *itemAt(int index) const; + + int effectiveFirstRow(Qt::Orientation orientation = Qt::Vertical) const; + int effectiveLastRow(Qt::Orientation orientation = Qt::Vertical) const; + + void setSpacing(qreal spacing, Qt::Orientations orientations); + qreal spacing(const QLayoutStyleInfo &styleInfo, Qt::Orientation orientation) const; + // ### setSpacingAfterRow(), spacingAfterRow() + void setRowSpacing(int row, qreal spacing, Qt::Orientation orientation = Qt::Vertical); + qreal rowSpacing(int row, Qt::Orientation orientation = Qt::Vertical) const; + + void setRowStretchFactor(int row, int stretch, Qt::Orientation orientation = Qt::Vertical); + int rowStretchFactor(int row, Qt::Orientation orientation = Qt::Vertical) const; + + void setStretchFactor(QGraphicsLayoutItem *layoutItem, int stretch, + Qt::Orientation orientation); + int stretchFactor(QGraphicsLayoutItem *layoutItem, Qt::Orientation orientation) const; + + void setRowSizeHint(Qt::SizeHint which, int row, qreal size, + Qt::Orientation orientation = Qt::Vertical); + qreal rowSizeHint(Qt::SizeHint which, int row, + Qt::Orientation orientation = Qt::Vertical) const; + + void setRowAlignment(int row, Qt::Alignment alignment, Qt::Orientation orientation); + Qt::Alignment rowAlignment(int row, Qt::Orientation orientation) const; + + void setAlignment(QGraphicsLayoutItem *layoutItem, Qt::Alignment alignment); + Qt::Alignment alignment(QGraphicsLayoutItem *layoutItem) const; + Qt::Alignment effectiveAlignment(const QGridLayoutItem *layoutItem) const; + + + void addItem(QGridLayoutItem *item); + void removeItem(QGridLayoutItem *item); + QGridLayoutItem *findLayoutItem(QGraphicsLayoutItem *layoutItem) const; + QGridLayoutItem *itemAt(int row, int column, Qt::Orientation orientation = Qt::Vertical) const; + inline void insertRow(int row, Qt::Orientation orientation = Qt::Vertical) + { insertOrRemoveRows(row, +1, orientation); } + inline void removeRow(int row, Qt::Orientation orientation = Qt::Vertical) + { insertOrRemoveRows(row, -1, orientation); } + + void invalidate(); + void setGeometries(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry); + QRectF cellRect(const QLayoutStyleInfo &styleInfo, const QRectF &contentsGeometry, int row, + int column, int rowSpan, int columnSpan) const; + QSizeF sizeHint(const QLayoutStyleInfo &styleInfo, Qt::SizeHint which, + const QSizeF &constraint) const; + QSizePolicy::ControlTypes controlTypes(LayoutSide side) const; + void transpose(); + void setVisualDirection(Qt::LayoutDirection direction); + Qt::LayoutDirection visualDirection() const; +#ifdef QT_DEBUG + void dump(int indent = 0) const; +#endif + +private: + static int grossRoundUp(int n) { return ((n + 2) | 0x3) - 2; } + + void maybeExpandGrid(int row, int column, Qt::Orientation orientation = Qt::Vertical); + void regenerateGrid(); + inline int internalGridRowCount() const { return grossRoundUp(rowCount()); } + inline int internalGridColumnCount() const { return grossRoundUp(columnCount()); } + void setItemAt(int row, int column, QGridLayoutItem *item); + void insertOrRemoveRows(int row, int delta, Qt::Orientation orientation = Qt::Vertical); + void fillRowData(QGridLayoutRowData *rowData, const QLayoutStyleInfo &styleInfo, + Qt::Orientation orientation = Qt::Vertical) const; + void ensureEffectiveFirstAndLastRows() const; + void ensureColumnAndRowData(const QLayoutStyleInfo &styleInfo) const; + void ensureGeometries(const QLayoutStyleInfo &styleInfo, const QSizeF &size) const; + + // User input + QVector<QGridLayoutItem *> q_grid; + QList<QGridLayoutItem *> q_items; + QLayoutParameter<qreal> q_defaultSpacings[NOrientations]; + QGridLayoutRowInfo q_infos[NOrientations]; + Qt::LayoutDirection m_visualDirection; + + // Lazily computed from the above user input + mutable int q_cachedEffectiveFirstRows[NOrientations]; + mutable int q_cachedEffectiveLastRows[NOrientations]; + + // Layout item input + mutable QLayoutStyleInfo q_cachedDataForStyleInfo; + mutable QGridLayoutRowData q_columnData; + mutable QGridLayoutRowData q_rowData; + mutable QGridLayoutBox q_totalBoxes[NOrientations]; + + // Output + mutable QSizeF q_cachedSize; + mutable QVector<qreal> q_xx; + mutable QVector<qreal> q_yy; + mutable QVector<qreal> q_widths; + mutable QVector<qreal> q_heights; + mutable QVector<qreal> q_descents; + + friend class QGridLayoutItem; +}; + +QT_END_NAMESPACE + +#endif |