source: trunk/src/gui/graphicsview/qgraphicsscene.cpp@ 1147

Last change on this file since 1147 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 235.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*!
43 \class QGraphicsScene
44 \brief The QGraphicsScene class provides a surface for managing a large
45 number of 2D graphical items.
46 \since 4.2
47 \ingroup graphicsview-api
48
49
50 The class serves as a container for QGraphicsItems. It is used together
51 with QGraphicsView for visualizing graphical items, such as lines,
52 rectangles, text, or even custom items, on a 2D surface. QGraphicsScene is
53 part of the \l{Graphics View Framework}.
54
55 QGraphicsScene also provides functionality that lets you efficiently
56 determine both the location of items, and for determining what items are
57 visible within an arbitrary area on the scene. With the QGraphicsView
58 widget, you can either visualize the whole scene, or zoom in and view only
59 parts of the scene.
60
61 Example:
62
63 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 0
64
65 Note that QGraphicsScene has no visual appearance of its own; it only
66 manages the items. You need to create a QGraphicsView widget to visualize
67 the scene.
68
69 To add items to a scene, you start off by constructing a QGraphicsScene
70 object. Then, you have two options: either add your existing QGraphicsItem
71 objects by calling addItem(), or you can call one of the convenience
72 functions addEllipse(), addLine(), addPath(), addPixmap(), addPolygon(),
73 addRect(), or addText(), which all return a pointer to the newly added item.
74 The dimensions of the items added with these functions are relative to the
75 item's coordinate system, and the items position is initialized to (0,
76 0) in the scene.
77
78 You can then visualize the scene using QGraphicsView. When the scene
79 changes, (e.g., when an item moves or is transformed) QGraphicsScene
80 emits the changed() signal. To remove an item, call removeItem().
81
82 QGraphicsScene uses an indexing algorithm to manage the location of items
83 efficiently. By default, a BSP (Binary Space Partitioning) tree is used; an
84 algorithm suitable for large scenes where most items remain static (i.e.,
85 do not move around). You can choose to disable this index by calling
86 setItemIndexMethod(). For more information about the available indexing
87 algorithms, see the itemIndexMethod property.
88
89 The scene's bounding rect is set by calling setSceneRect(). Items can be
90 placed at any position on the scene, and the size of the scene is by
91 default unlimited. The scene rect is used only for internal bookkeeping,
92 maintaining the scene's item index. If the scene rect is unset,
93 QGraphicsScene will use the bounding area of all items, as returned by
94 itemsBoundingRect(), as the scene rect. However, itemsBoundingRect() is a
95 relatively time consuming function, as it operates by collecting
96 positional information for every item on the scene. Because of this, you
97 should always set the scene rect when operating on large scenes.
98
99 One of QGraphicsScene's greatest strengths is its ability to efficiently
100 determine the location of items. Even with millions of items on the scene,
101 the items() functions can determine the location of an item within few
102 milliseconds. There are several overloads to items(): one that finds items
103 at a certain position, one that finds items inside or intersecting with a
104 polygon or a rectangle, and more. The list of returned items is sorted by
105 stacking order, with the topmost item being the first item in the list.
106 For convenience, there is also an itemAt() function that returns the
107 topmost item at a given position.
108
109 QGraphicsScene maintains selection information for the scene. To select
110 items, call setSelectionArea(), and to clear the current selection, call
111 clearSelection(). Call selectedItems() to get the list of all selected
112 items.
113
114 \section1 Event Handling and Propagation
115
116 Another responsibility that QGraphicsScene has, is to propagate events
117 from QGraphicsView. To send an event to a scene, you construct an event
118 that inherits QEvent, and then send it using, for example,
119 QApplication::sendEvent(). event() is responsible for dispatching
120 the event to the individual items. Some common events are handled by
121 convenience event handlers. For example, key press events are handled by
122 keyPressEvent(), and mouse press events are handled by mousePressEvent().
123
124 Key events are delivered to the \e {focus item}. To set the focus item,
125 you can either call setFocusItem(), passing an item that accepts focus, or
126 the item itself can call QGraphicsItem::setFocus(). Call focusItem() to
127 get the current focus item. For compatibility with widgets, the scene also
128 maintains its own focus information. By default, the scene does not have
129 focus, and all key events are discarded. If setFocus() is called, or if an
130 item on the scene gains focus, the scene automatically gains focus. If the
131 scene has focus, hasFocus() will return true, and key events will be
132 forwarded to the focus item, if any. If the scene loses focus, (i.e.,
133 someone calls clearFocus(),) while an item has focus, the scene will
134 maintain its item focus information, and once the scene regains focus, it
135 will make sure the last focus item regains focus.
136
137 For mouse-over effects, QGraphicsScene dispatches \e {hover
138 events}. If an item accepts hover events (see
139 QGraphicsItem::acceptHoverEvents()), it will receive a \l
140 {QEvent::}{GraphicsSceneHoverEnter} event when the mouse enters
141 its area. As the mouse continues moving inside the item's area,
142 QGraphicsScene will send it \l {QEvent::}{GraphicsSceneHoverMove}
143 events. When the mouse leaves the item's area, the item will
144 receive a \l {QEvent::}{GraphicsSceneHoverLeave} event.
145
146 All mouse events are delivered to the current \e {mouse grabber}
147 item. An item becomes the scene's mouse grabber if it accepts
148 mouse events (see QGraphicsItem::acceptedMouseButtons()) and it
149 receives a mouse press. It stays the mouse grabber until it
150 receives a mouse release when no other mouse buttons are
151 pressed. You can call mouseGrabberItem() to determine what item is
152 currently grabbing the mouse.
153
154 \sa QGraphicsItem, QGraphicsView
155*/
156
157/*!
158 \enum QGraphicsScene::SceneLayer
159 \since 4.3
160
161 This enum describes the rendering layers in a QGraphicsScene. When
162 QGraphicsScene draws the scene contents, it renders each of these layers
163 separately, in order.
164
165 Each layer represents a flag that can be OR'ed together when calling
166 functions such as invalidate() or QGraphicsView::invalidateScene().
167
168 \value ItemLayer The item layer. QGraphicsScene renders all items are in
169 this layer by calling the virtual function drawItems(). The item layer is
170 drawn after the background layer, but before the foreground layer.
171
172 \value BackgroundLayer The background layer. QGraphicsScene renders the
173 scene's background in this layer by calling the virtual function
174 drawBackground(). The background layer is drawn first of all layers.
175
176 \value ForegroundLayer The foreground layer. QGraphicsScene renders the
177 scene's foreground in this layer by calling the virtual function
178 drawForeground(). The foreground layer is drawn last of all layers.
179
180 \value AllLayers All layers; this value represents a combination of all
181 three layers.
182
183 \sa invalidate(), QGraphicsView::invalidateScene()
184*/
185
186/*!
187 \enum QGraphicsScene::ItemIndexMethod
188
189 This enum describes the indexing algorithms QGraphicsScene provides for
190 managing positional information about items on the scene.
191
192 \value BspTreeIndex A Binary Space Partitioning tree is applied. All
193 QGraphicsScene's item location algorithms are of an order close to
194 logarithmic complexity, by making use of binary search. Adding, moving and
195 removing items is logarithmic. This approach is best for static scenes
196 (i.e., scenes where most items do not move).
197
198 \value NoIndex No index is applied. Item location is of linear complexity,
199 as all items on the scene are searched. Adding, moving and removing items,
200 however, is done in constant time. This approach is ideal for dynamic
201 scenes, where many items are added, moved or removed continuously.
202
203 \sa setItemIndexMethod(), bspTreeDepth
204*/
205
206#include "qgraphicsscene.h"
207
208#ifndef QT_NO_GRAPHICSVIEW
209
210#include "qgraphicsitem.h"
211#include "qgraphicsitem_p.h"
212#include "qgraphicslayout.h"
213#include "qgraphicsscene_p.h"
214#include "qgraphicssceneevent.h"
215#include "qgraphicsview.h"
216#include "qgraphicsview_p.h"
217#include "qgraphicswidget.h"
218#include "qgraphicswidget_p.h"
219#include "qgraphicssceneindex_p.h"
220#include "qgraphicsscenebsptreeindex_p.h"
221#include "qgraphicsscenelinearindex_p.h"
222
223#include <QtCore/qdebug.h>
224#include <QtCore/qlist.h>
225#include <QtCore/qmath.h>
226#include <QtCore/qrect.h>
227#include <QtCore/qset.h>
228#include <QtCore/qstack.h>
229#include <QtCore/qtimer.h>
230#include <QtCore/qvarlengtharray.h>
231#include <QtCore/QMetaMethod>
232#include <QtGui/qapplication.h>
233#include <QtGui/qdesktopwidget.h>
234#include <QtGui/qevent.h>
235#include <QtGui/qgraphicslayout.h>
236#include <QtGui/qgraphicsproxywidget.h>
237#include <QtGui/qgraphicswidget.h>
238#include <QtGui/qmatrix.h>
239#include <QtGui/qpaintengine.h>
240#include <QtGui/qpainter.h>
241#include <QtGui/qpixmapcache.h>
242#include <QtGui/qpolygon.h>
243#include <QtGui/qstyleoption.h>
244#include <QtGui/qtooltip.h>
245#include <QtGui/qtransform.h>
246#include <QtGui/qinputcontext.h>
247#include <QtGui/qgraphicseffect.h>
248#include <private/qapplication_p.h>
249#include <private/qobject_p.h>
250#ifdef Q_WS_X11
251#include <private/qt_x11_p.h>
252#endif
253#include <private/qgraphicseffect_p.h>
254#include <private/qgesturemanager_p.h>
255#include <private/qpathclipper_p.h>
256
257// #define GESTURE_DEBUG
258#ifndef GESTURE_DEBUG
259# define DEBUG if (0) qDebug
260#else
261# define DEBUG qDebug
262#endif
263
264QT_BEGIN_NAMESPACE
265
266bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
267
268static void _q_hoverFromMouseEvent(QGraphicsSceneHoverEvent *hover, const QGraphicsSceneMouseEvent *mouseEvent)
269{
270 hover->setWidget(mouseEvent->widget());
271 hover->setPos(mouseEvent->pos());
272 hover->setScenePos(mouseEvent->scenePos());
273 hover->setScreenPos(mouseEvent->screenPos());
274 hover->setLastPos(mouseEvent->lastPos());
275 hover->setLastScenePos(mouseEvent->lastScenePos());
276 hover->setLastScreenPos(mouseEvent->lastScreenPos());
277 hover->setModifiers(mouseEvent->modifiers());
278 hover->setAccepted(mouseEvent->isAccepted());
279}
280
281/*!
282 \internal
283*/
284QGraphicsScenePrivate::QGraphicsScenePrivate()
285 : indexMethod(QGraphicsScene::BspTreeIndex),
286 index(0),
287 lastItemCount(0),
288 hasSceneRect(false),
289 dirtyGrowingItemsBoundingRect(true),
290 updateAll(false),
291 calledEmitUpdated(false),
292 processDirtyItemsEmitted(false),
293 needSortTopLevelItems(true),
294 holesInTopLevelSiblingIndex(false),
295 topLevelSequentialOrdering(true),
296 scenePosDescendantsUpdatePending(false),
297 stickyFocus(false),
298 hasFocus(false),
299 lastMouseGrabberItemHasImplicitMouseGrab(false),
300 allItemsIgnoreHoverEvents(true),
301 allItemsUseDefaultCursor(true),
302 painterStateProtection(true),
303 sortCacheEnabled(false),
304 allItemsIgnoreTouchEvents(true),
305 selectionChanging(0),
306 rectAdjust(2),
307 focusItem(0),
308 lastFocusItem(0),
309 tabFocusFirst(0),
310 activePanel(0),
311 lastActivePanel(0),
312 activationRefCount(0),
313 childExplicitActivation(0),
314 lastMouseGrabberItem(0),
315 dragDropItem(0),
316 enterWidget(0),
317 lastDropAction(Qt::IgnoreAction),
318 style(0)
319{
320}
321
322/*!
323 \internal
324*/
325void QGraphicsScenePrivate::init()
326{
327 Q_Q(QGraphicsScene);
328
329 index = new QGraphicsSceneBspTreeIndex(q);
330
331 // Keep this index so we can check for connected slots later on.
332 changedSignalIndex = signalIndex("changed(QList<QRectF>)");
333 processDirtyItemsIndex = q->metaObject()->indexOfSlot("_q_processDirtyItems()");
334 polishItemsIndex = q->metaObject()->indexOfSlot("_q_polishItems()");
335
336 qApp->d_func()->scene_list.append(q);
337 q->update();
338}
339
340/*!
341 \internal
342*/
343QGraphicsScenePrivate *QGraphicsScenePrivate::get(QGraphicsScene *q)
344{
345 return q->d_func();
346}
347
348void QGraphicsScenePrivate::_q_emitUpdated()
349{
350 Q_Q(QGraphicsScene);
351 calledEmitUpdated = false;
352
353 if (dirtyGrowingItemsBoundingRect) {
354 if (!hasSceneRect) {
355 const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
356 growingItemsBoundingRect |= q->itemsBoundingRect();
357 if (oldGrowingItemsBoundingRect != growingItemsBoundingRect)
358 emit q->sceneRectChanged(growingItemsBoundingRect);
359 }
360 dirtyGrowingItemsBoundingRect = false;
361 }
362
363 // Ensure all views are connected if anything is connected. This disables
364 // the optimization that items send updates directly to the views, but it
365 // needs to happen in order to keep compatibility with the behavior from
366 // Qt 4.4 and backward.
367 if (isSignalConnected(changedSignalIndex)) {
368 for (int i = 0; i < views.size(); ++i) {
369 QGraphicsView *view = views.at(i);
370 if (!view->d_func()->connectedToScene) {
371 view->d_func()->connectedToScene = true;
372 q->connect(q, SIGNAL(changed(QList<QRectF>)),
373 views.at(i), SLOT(updateScene(QList<QRectF>)));
374 }
375 }
376 } else {
377 if (views.isEmpty()) {
378 updateAll = false;
379 return;
380 }
381 for (int i = 0; i < views.size(); ++i)
382 views.at(i)->d_func()->processPendingUpdates();
383 // It's important that we update all views before we dispatch, hence two for-loops.
384 for (int i = 0; i < views.size(); ++i)
385 views.at(i)->d_func()->dispatchPendingUpdateRequests();
386 return;
387 }
388
389 // Notify the changes to anybody interested.
390 QList<QRectF> oldUpdatedRects;
391 oldUpdatedRects = updateAll ? (QList<QRectF>() << q->sceneRect()) : updatedRects;
392 updateAll = false;
393 updatedRects.clear();
394 emit q->changed(oldUpdatedRects);
395}
396
397/*!
398 \internal
399
400 ### This function is almost identical to QGraphicsItemPrivate::addChild().
401*/
402void QGraphicsScenePrivate::registerTopLevelItem(QGraphicsItem *item)
403{
404 item->d_ptr->ensureSequentialSiblingIndex();
405 needSortTopLevelItems = true; // ### maybe false
406 item->d_ptr->siblingIndex = topLevelItems.size();
407 topLevelItems.append(item);
408}
409
410/*!
411 \internal
412
413 ### This function is almost identical to QGraphicsItemPrivate::removeChild().
414*/
415void QGraphicsScenePrivate::unregisterTopLevelItem(QGraphicsItem *item)
416{
417 if (!holesInTopLevelSiblingIndex)
418 holesInTopLevelSiblingIndex = item->d_ptr->siblingIndex != topLevelItems.size() - 1;
419 if (topLevelSequentialOrdering && !holesInTopLevelSiblingIndex)
420 topLevelItems.removeAt(item->d_ptr->siblingIndex);
421 else
422 topLevelItems.removeOne(item);
423 // NB! Do not use topLevelItems.removeAt(item->d_ptr->siblingIndex) because
424 // the item is not guaranteed to be at the index after the list is sorted
425 // (see ensureSortedTopLevelItems()).
426 item->d_ptr->siblingIndex = -1;
427 if (topLevelSequentialOrdering)
428 topLevelSequentialOrdering = !holesInTopLevelSiblingIndex;
429}
430
431/*!
432 \internal
433*/
434void QGraphicsScenePrivate::_q_polishItems()
435{
436 if (unpolishedItems.isEmpty())
437 return;
438
439 const QVariant booleanTrueVariant(true);
440 QGraphicsItem *item = 0;
441 QGraphicsItemPrivate *itemd = 0;
442 const int oldUnpolishedCount = unpolishedItems.count();
443
444 for (int i = 0; i < oldUnpolishedCount; ++i) {
445 item = unpolishedItems.at(i);
446 if (!item)
447 continue;
448 itemd = item->d_ptr.data();
449 itemd->pendingPolish = false;
450 if (!itemd->explicitlyHidden) {
451 item->itemChange(QGraphicsItem::ItemVisibleChange, booleanTrueVariant);
452 item->itemChange(QGraphicsItem::ItemVisibleHasChanged, booleanTrueVariant);
453 }
454 if (itemd->isWidget) {
455 QEvent event(QEvent::Polish);
456 QApplication::sendEvent((QGraphicsWidget *)item, &event);
457 }
458 }
459
460 if (unpolishedItems.count() == oldUnpolishedCount) {
461 // No new items were added to the vector.
462 unpolishedItems.clear();
463 } else {
464 // New items were appended; keep them and remove the old ones.
465 unpolishedItems.remove(0, oldUnpolishedCount);
466 unpolishedItems.squeeze();
467 QMetaObject::invokeMethod(q_ptr, "_q_polishItems", Qt::QueuedConnection);
468 }
469}
470
471void QGraphicsScenePrivate::_q_processDirtyItems()
472{
473 processDirtyItemsEmitted = false;
474
475 if (updateAll) {
476 Q_ASSERT(calledEmitUpdated);
477 // No need for further processing (except resetting the dirty states).
478 // The growingItemsBoundingRect is updated in _q_emitUpdated.
479 for (int i = 0; i < topLevelItems.size(); ++i)
480 resetDirtyItem(topLevelItems.at(i), /*recursive=*/true);
481 return;
482 }
483
484 const bool wasPendingSceneUpdate = calledEmitUpdated;
485 const QRectF oldGrowingItemsBoundingRect = growingItemsBoundingRect;
486
487 // Process items recursively.
488 for (int i = 0; i < topLevelItems.size(); ++i)
489 processDirtyItemsRecursive(topLevelItems.at(i));
490
491 dirtyGrowingItemsBoundingRect = false;
492 if (!hasSceneRect && oldGrowingItemsBoundingRect != growingItemsBoundingRect)
493 emit q_func()->sceneRectChanged(growingItemsBoundingRect);
494
495 if (wasPendingSceneUpdate)
496 return;
497
498 for (int i = 0; i < views.size(); ++i)
499 views.at(i)->d_func()->processPendingUpdates();
500
501 if (calledEmitUpdated) {
502 // We did a compatibility QGraphicsScene::update in processDirtyItemsRecursive
503 // and we cannot wait for the control to reach the eventloop before the
504 // changed signal is emitted, so we emit it now.
505 _q_emitUpdated();
506 }
507
508 // Immediately dispatch all pending update requests on the views.
509 for (int i = 0; i < views.size(); ++i)
510 views.at(i)->d_func()->dispatchPendingUpdateRequests();
511}
512
513/*!
514 \internal
515*/
516void QGraphicsScenePrivate::setScenePosItemEnabled(QGraphicsItem *item, bool enabled)
517{
518 QGraphicsItem *p = item->d_ptr->parent;
519 while (p) {
520 p->d_ptr->scenePosDescendants = enabled;
521 p = p->d_ptr->parent;
522 }
523 if (!enabled && !scenePosDescendantsUpdatePending) {
524 scenePosDescendantsUpdatePending = true;
525 QMetaObject::invokeMethod(q_func(), "_q_updateScenePosDescendants", Qt::QueuedConnection);
526 }
527}
528
529/*!
530 \internal
531*/
532void QGraphicsScenePrivate::registerScenePosItem(QGraphicsItem *item)
533{
534 scenePosItems.insert(item);
535 setScenePosItemEnabled(item, true);
536}
537
538/*!
539 \internal
540*/
541void QGraphicsScenePrivate::unregisterScenePosItem(QGraphicsItem *item)
542{
543 scenePosItems.remove(item);
544 setScenePosItemEnabled(item, false);
545}
546
547/*!
548 \internal
549*/
550void QGraphicsScenePrivate::_q_updateScenePosDescendants()
551{
552 foreach (QGraphicsItem *item, scenePosItems) {
553 QGraphicsItem *p = item->d_ptr->parent;
554 while (p) {
555 p->d_ptr->scenePosDescendants = 1;
556 p = p->d_ptr->parent;
557 }
558 }
559 scenePosDescendantsUpdatePending = false;
560}
561
562/*!
563 \internal
564
565 Schedules an item for removal. This function leaves some stale indexes
566 around in the BSP tree if called from the item's destructor; these will
567 be cleaned up the next time someone triggers purgeRemovedItems().
568
569 Note: This function might get called from QGraphicsItem's destructor. \a item is
570 being destroyed, so we cannot call any pure virtual functions on it (such
571 as boundingRect()). Also, it is unnecessary to update the item's own state
572 in any way.
573*/
574void QGraphicsScenePrivate::removeItemHelper(QGraphicsItem *item)
575{
576 Q_Q(QGraphicsScene);
577
578 // Clear focus on the item to remove any reference in the focusWidget chain.
579 item->clearFocus();
580
581 markDirty(item, QRectF(), /*invalidateChildren=*/false, /*force=*/false,
582 /*ignoreOpacity=*/false, /*removingItemFromScene=*/true);
583
584 if (item->d_ptr->inDestructor) {
585 // The item is actually in its destructor, we call the special method in the index.
586 index->deleteItem(item);
587 } else {
588 // Can potentially call item->boundingRect() (virtual function), that's why
589 // we only can call this function if the item is not in its destructor.
590 index->removeItem(item);
591 }
592
593 item->d_ptr->clearSubFocus();
594
595 if (item->flags() & QGraphicsItem::ItemSendsScenePositionChanges)
596 unregisterScenePosItem(item);
597
598 QGraphicsScene *oldScene = item->d_func()->scene;
599 item->d_func()->scene = 0;
600
601 //We need to remove all children first because they might use their parent
602 //attributes (e.g. sceneTransform).
603 if (!item->d_ptr->inDestructor) {
604 // Remove all children recursively
605 for (int i = 0; i < item->d_ptr->children.size(); ++i)
606 q->removeItem(item->d_ptr->children.at(i));
607 }
608
609 if (!item->d_ptr->inDestructor && item == tabFocusFirst) {
610 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
611 widget->d_func()->fixFocusChainBeforeReparenting(0, oldScene, 0);
612 }
613
614 // Unregister focus proxy.
615 item->d_ptr->resetFocusProxy();
616
617 // Remove from parent, or unregister from toplevels.
618 if (QGraphicsItem *parentItem = item->parentItem()) {
619 if (parentItem->scene()) {
620 Q_ASSERT_X(parentItem->scene() == q, "QGraphicsScene::removeItem",
621 "Parent item's scene is different from this item's scene");
622 item->setParentItem(0);
623 }
624 } else {
625 unregisterTopLevelItem(item);
626 }
627
628 // Reset the mouse grabber and focus item data.
629 if (item == focusItem)
630 focusItem = 0;
631 if (item == lastFocusItem)
632 lastFocusItem = 0;
633 if (item == activePanel) {
634 // ### deactivate...
635 activePanel = 0;
636 }
637 if (item == lastActivePanel)
638 lastActivePanel = 0;
639
640 // Cancel active touches
641 {
642 QMap<int, QGraphicsItem *>::iterator it = itemForTouchPointId.begin();
643 while (it != itemForTouchPointId.end()) {
644 if (it.value() == item) {
645 sceneCurrentTouchPoints.remove(it.key());
646 it = itemForTouchPointId.erase(it);
647 } else {
648 ++it;
649 }
650 }
651 }
652
653 // Disable selectionChanged() for individual items
654 ++selectionChanging;
655 int oldSelectedItemsSize = selectedItems.size();
656
657 // Update selected & hovered item bookkeeping
658 selectedItems.remove(item);
659 hoverItems.removeAll(item);
660 cachedItemsUnderMouse.removeAll(item);
661 if (item->d_ptr->pendingPolish) {
662 const int unpolishedIndex = unpolishedItems.indexOf(item);
663 if (unpolishedIndex != -1)
664 unpolishedItems[unpolishedIndex] = 0;
665 item->d_ptr->pendingPolish = false;
666 }
667 resetDirtyItem(item);
668
669 //We remove all references of item from the sceneEventFilter arrays
670 QMultiMap<QGraphicsItem*, QGraphicsItem*>::iterator iterator = sceneEventFilters.begin();
671 while (iterator != sceneEventFilters.end()) {
672 if (iterator.value() == item || iterator.key() == item)
673 iterator = sceneEventFilters.erase(iterator);
674 else
675 ++iterator;
676 }
677
678 if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
679 leaveModal(item);
680
681 // Reset the mouse grabber and focus item data.
682 if (mouseGrabberItems.contains(item))
683 ungrabMouse(item, /* item is dying */ item->d_ptr->inDestructor);
684
685 // Reset the keyboard grabber
686 if (keyboardGrabberItems.contains(item))
687 ungrabKeyboard(item, /* item is dying */ item->d_ptr->inDestructor);
688
689 // Reset the last mouse grabber item
690 if (item == lastMouseGrabberItem)
691 lastMouseGrabberItem = 0;
692
693 // Reset the current drop item
694 if (item == dragDropItem)
695 dragDropItem = 0;
696
697 // Reenable selectionChanged() for individual items
698 --selectionChanging;
699 if (!selectionChanging && selectedItems.size() != oldSelectedItemsSize)
700 emit q->selectionChanged();
701
702#ifndef QT_NO_GESTURES
703 QHash<QGesture *, QGraphicsObject *>::iterator it;
704 for (it = gestureTargets.begin(); it != gestureTargets.end();) {
705 if (it.value() == item)
706 it = gestureTargets.erase(it);
707 else
708 ++it;
709 }
710
711 QGraphicsObject *dummy = static_cast<QGraphicsObject *>(item);
712 cachedTargetItems.removeOne(dummy);
713 cachedItemGestures.remove(dummy);
714 cachedAlreadyDeliveredGestures.remove(dummy);
715
716 foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
717 ungrabGesture(item, gesture);
718#endif // QT_NO_GESTURES
719}
720
721/*!
722 \internal
723*/
724void QGraphicsScenePrivate::setActivePanelHelper(QGraphicsItem *item, bool duringActivationEvent)
725{
726 Q_Q(QGraphicsScene);
727 if (item && item->scene() != q) {
728 qWarning("QGraphicsScene::setActivePanel: item %p must be part of this scene",
729 item);
730 return;
731 }
732
733 // Ensure the scene has focus when we change panel activation.
734 q->setFocus(Qt::ActiveWindowFocusReason);
735
736 // Find the item's panel.
737 QGraphicsItem *panel = item ? item->panel() : 0;
738 lastActivePanel = panel ? activePanel : 0;
739 if (panel == activePanel || (!q->isActive() && !duringActivationEvent))
740 return;
741
742 // Deactivate the last active panel.
743 if (activePanel) {
744 if (QGraphicsItem *fi = activePanel->focusItem()) {
745 // Remove focus from the current focus item.
746 if (fi == q->focusItem())
747 q->setFocusItem(0, Qt::ActiveWindowFocusReason);
748 }
749
750 QEvent event(QEvent::WindowDeactivate);
751 q->sendEvent(activePanel, &event);
752 } else if (panel && !duringActivationEvent) {
753 // Deactivate the scene if changing activation to a panel.
754 QEvent event(QEvent::WindowDeactivate);
755 foreach (QGraphicsItem *item, q->items()) {
756 if (item->isVisible() && !item->isPanel() && !item->parentItem())
757 q->sendEvent(item, &event);
758 }
759 }
760
761 // Update activate state.
762 activePanel = panel;
763 QEvent event(QEvent::ActivationChange);
764 QApplication::sendEvent(q, &event);
765
766 // Activate
767 if (panel) {
768 QEvent event(QEvent::WindowActivate);
769 q->sendEvent(panel, &event);
770
771 // Set focus on the panel's focus item.
772 if (QGraphicsItem *focusItem = panel->focusItem())
773 focusItem->setFocus(Qt::ActiveWindowFocusReason);
774 } else if (q->isActive()) {
775 // Activate the scene
776 QEvent event(QEvent::WindowActivate);
777 foreach (QGraphicsItem *item, q->items()) {
778 if (item->isVisible() && !item->isPanel() && !item->parentItem())
779 q->sendEvent(item, &event);
780 }
781 }
782}
783
784/*!
785 \internal
786*/
787void QGraphicsScenePrivate::setFocusItemHelper(QGraphicsItem *item,
788 Qt::FocusReason focusReason)
789{
790 Q_Q(QGraphicsScene);
791 if (item == focusItem)
792 return;
793
794 // Clear focus if asked to set focus on something that can't
795 // accept input focus.
796 if (item && (!(item->flags() & QGraphicsItem::ItemIsFocusable)
797 || !item->isVisible() || !item->isEnabled())) {
798 item = 0;
799 }
800
801 // Set focus on the scene if an item requests focus.
802 if (item) {
803 q->setFocus(focusReason);
804 if (item == focusItem)
805 return;
806 }
807
808 if (focusItem) {
809 QFocusEvent event(QEvent::FocusOut, focusReason);
810 lastFocusItem = focusItem;
811 focusItem = 0;
812 sendEvent(lastFocusItem, &event);
813
814#ifndef QT_NO_IM
815 if (lastFocusItem
816 && (lastFocusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod)) {
817 // Reset any visible preedit text
818 QInputMethodEvent imEvent;
819 sendEvent(lastFocusItem, &imEvent);
820
821 // Close any external input method panel. This happens
822 // automatically by removing WA_InputMethodEnabled on
823 // the views, but if we are changing focus, we have to
824 // do it ourselves.
825 if (item) {
826 for (int i = 0; i < views.size(); ++i)
827 if (views.at(i)->inputContext())
828 views.at(i)->inputContext()->reset();
829 }
830 }
831#endif //QT_NO_IM
832 }
833
834 // This handles the case that the item has been removed from the
835 // scene in response to the FocusOut event.
836 if (item && item->scene() != q)
837 item = 0;
838
839 if (item)
840 focusItem = item;
841 updateInputMethodSensitivityInViews();
842 if (item) {
843 QFocusEvent event(QEvent::FocusIn, focusReason);
844 sendEvent(item, &event);
845 }
846}
847
848/*!
849 \internal
850*/
851void QGraphicsScenePrivate::addPopup(QGraphicsWidget *widget)
852{
853 Q_ASSERT(widget);
854 Q_ASSERT(!popupWidgets.contains(widget));
855 popupWidgets << widget;
856 if (QGraphicsWidget *focusWidget = widget->focusWidget()) {
857 focusWidget->setFocus(Qt::PopupFocusReason);
858 } else {
859 grabKeyboard((QGraphicsItem *)widget);
860 if (focusItem && popupWidgets.size() == 1) {
861 QFocusEvent event(QEvent::FocusOut, Qt::PopupFocusReason);
862 sendEvent(focusItem, &event);
863 }
864 }
865 grabMouse((QGraphicsItem *)widget);
866}
867
868/*!
869 \internal
870
871 Remove \a widget from the popup list. Important notes:
872
873 \a widget is guaranteed to be in the list of popups, but it might not be
874 the last entry; you can hide any item in the pop list before the others,
875 and this must cause all later mouse grabbers to lose the grab.
876*/
877void QGraphicsScenePrivate::removePopup(QGraphicsWidget *widget, bool itemIsDying)
878{
879 Q_ASSERT(widget);
880 int index = popupWidgets.indexOf(widget);
881 Q_ASSERT(index != -1);
882
883 for (int i = popupWidgets.size() - 1; i >= index; --i) {
884 QGraphicsWidget *widget = popupWidgets.takeLast();
885 ungrabMouse(widget, itemIsDying);
886 if (focusItem && popupWidgets.isEmpty()) {
887 QFocusEvent event(QEvent::FocusIn, Qt::PopupFocusReason);
888 sendEvent(focusItem, &event);
889 } else if (keyboardGrabberItems.contains(static_cast<QGraphicsItem *>(widget))) {
890 ungrabKeyboard(static_cast<QGraphicsItem *>(widget), itemIsDying);
891 }
892 if (!itemIsDying && widget->isVisible()) {
893 widget->QGraphicsItem::d_ptr->setVisibleHelper(false, /* explicit = */ false);
894 }
895 }
896}
897
898/*!
899 \internal
900*/
901void QGraphicsScenePrivate::grabMouse(QGraphicsItem *item, bool implicit)
902{
903 // Append to list of mouse grabber items, and send a mouse grab event.
904 if (mouseGrabberItems.contains(item)) {
905 if (mouseGrabberItems.last() == item) {
906 Q_ASSERT(!implicit);
907 if (!lastMouseGrabberItemHasImplicitMouseGrab) {
908 qWarning("QGraphicsItem::grabMouse: already a mouse grabber");
909 } else {
910 // Upgrade to an explicit mouse grab
911 lastMouseGrabberItemHasImplicitMouseGrab = false;
912 }
913 } else {
914 qWarning("QGraphicsItem::grabMouse: already blocked by mouse grabber: %p",
915 mouseGrabberItems.last());
916 }
917 return;
918 }
919
920 // Send ungrab event to the last grabber.
921 if (!mouseGrabberItems.isEmpty()) {
922 QGraphicsItem *last = mouseGrabberItems.last();
923 if (lastMouseGrabberItemHasImplicitMouseGrab) {
924 // Implicit mouse grab is immediately lost.
925 last->ungrabMouse();
926 } else {
927 // Just send ungrab event to current grabber.
928 QEvent ungrabEvent(QEvent::UngrabMouse);
929 sendEvent(last, &ungrabEvent);
930 }
931 }
932
933 mouseGrabberItems << item;
934 lastMouseGrabberItemHasImplicitMouseGrab = implicit;
935
936 // Send grab event to current grabber.
937 QEvent grabEvent(QEvent::GrabMouse);
938 sendEvent(item, &grabEvent);
939}
940
941/*!
942 \internal
943*/
944void QGraphicsScenePrivate::ungrabMouse(QGraphicsItem *item, bool itemIsDying)
945{
946 int index = mouseGrabberItems.indexOf(item);
947 if (index == -1) {
948 qWarning("QGraphicsItem::ungrabMouse: not a mouse grabber");
949 return;
950 }
951
952 if (item != mouseGrabberItems.last()) {
953 // Recursively ungrab the next mouse grabber until we reach this item
954 // to ensure state consistency.
955 ungrabMouse(mouseGrabberItems.at(index + 1), itemIsDying);
956 }
957 if (!popupWidgets.isEmpty() && item == popupWidgets.last()) {
958 // If the item is a popup, go via removePopup to ensure state
959 // consistency and that it gets hidden correctly - beware that
960 // removePopup() reenters this function to continue removing the grab.
961 removePopup((QGraphicsWidget *)item, itemIsDying);
962 return;
963 }
964
965 // Send notification about mouse ungrab.
966 if (!itemIsDying) {
967 QEvent event(QEvent::UngrabMouse);
968 sendEvent(item, &event);
969 }
970
971 // Remove the item from the list of grabbers. Whenever this happens, we
972 // reset the implicitGrab (there can be only ever be one implicit grabber
973 // in a scene, and it is always the latest grabber; if the implicit grab
974 // is lost, it is not automatically regained.
975 mouseGrabberItems.takeLast();
976 lastMouseGrabberItemHasImplicitMouseGrab = false;
977
978 // Send notification about mouse regrab. ### It's unfortunate that all the
979 // items get a GrabMouse event, but this is a rare case with a simple
980 // implementation and it does ensure a consistent state.
981 if (!itemIsDying && !mouseGrabberItems.isEmpty()) {
982 QGraphicsItem *last = mouseGrabberItems.last();
983 QEvent event(QEvent::GrabMouse);
984 sendEvent(last, &event);
985 }
986}
987
988/*!
989 \internal
990*/
991void QGraphicsScenePrivate::clearMouseGrabber()
992{
993 if (!mouseGrabberItems.isEmpty())
994 mouseGrabberItems.first()->ungrabMouse();
995 lastMouseGrabberItem = 0;
996}
997
998/*!
999 \internal
1000*/
1001void QGraphicsScenePrivate::grabKeyboard(QGraphicsItem *item)
1002{
1003 if (keyboardGrabberItems.contains(item)) {
1004 if (keyboardGrabberItems.last() == item)
1005 qWarning("QGraphicsItem::grabKeyboard: already a keyboard grabber");
1006 else
1007 qWarning("QGraphicsItem::grabKeyboard: already blocked by keyboard grabber: %p",
1008 keyboardGrabberItems.last());
1009 return;
1010 }
1011
1012 // Send ungrab event to the last grabber.
1013 if (!keyboardGrabberItems.isEmpty()) {
1014 // Just send ungrab event to current grabber.
1015 QEvent ungrabEvent(QEvent::UngrabKeyboard);
1016 sendEvent(keyboardGrabberItems.last(), &ungrabEvent);
1017 }
1018
1019 keyboardGrabberItems << item;
1020
1021 // Send grab event to current grabber.
1022 QEvent grabEvent(QEvent::GrabKeyboard);
1023 sendEvent(item, &grabEvent);
1024}
1025
1026/*!
1027 \internal
1028*/
1029void QGraphicsScenePrivate::ungrabKeyboard(QGraphicsItem *item, bool itemIsDying)
1030{
1031 int index = keyboardGrabberItems.lastIndexOf(item);
1032 if (index == -1) {
1033 qWarning("QGraphicsItem::ungrabKeyboard: not a keyboard grabber");
1034 return;
1035 }
1036 if (item != keyboardGrabberItems.last()) {
1037 // Recursively ungrab the topmost keyboard grabber until we reach this
1038 // item to ensure state consistency.
1039 ungrabKeyboard(keyboardGrabberItems.at(index + 1), itemIsDying);
1040 }
1041
1042 // Send notification about keyboard ungrab.
1043 if (!itemIsDying) {
1044 QEvent event(QEvent::UngrabKeyboard);
1045 sendEvent(item, &event);
1046 }
1047
1048 // Remove the item from the list of grabbers.
1049 keyboardGrabberItems.takeLast();
1050
1051 // Send notification about mouse regrab.
1052 if (!itemIsDying && !keyboardGrabberItems.isEmpty()) {
1053 QGraphicsItem *last = keyboardGrabberItems.last();
1054 QEvent event(QEvent::GrabKeyboard);
1055 sendEvent(last, &event);
1056 }
1057}
1058
1059/*!
1060 \internal
1061*/
1062void QGraphicsScenePrivate::clearKeyboardGrabber()
1063{
1064 if (!keyboardGrabberItems.isEmpty())
1065 ungrabKeyboard(keyboardGrabberItems.first());
1066}
1067
1068void QGraphicsScenePrivate::enableMouseTrackingOnViews()
1069{
1070 foreach (QGraphicsView *view, views)
1071 view->viewport()->setMouseTracking(true);
1072}
1073
1074/*!
1075 Returns all items for the screen position in \a event.
1076*/
1077QList<QGraphicsItem *> QGraphicsScenePrivate::itemsAtPosition(const QPoint &/*screenPos*/,
1078 const QPointF &scenePos,
1079 QWidget *widget) const
1080{
1081 Q_Q(const QGraphicsScene);
1082 QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
1083 if (!view)
1084 return q->items(scenePos, Qt::IntersectsItemShape, Qt::DescendingOrder, QTransform());
1085
1086 const QRectF pointRect(scenePos, QSizeF(1, 1));
1087 if (!view->isTransformed())
1088 return q->items(pointRect, Qt::IntersectsItemShape, Qt::DescendingOrder);
1089
1090 const QTransform viewTransform = view->viewportTransform();
1091 return q->items(pointRect, Qt::IntersectsItemShape,
1092 Qt::DescendingOrder, viewTransform);
1093}
1094
1095/*!
1096 \internal
1097*/
1098void QGraphicsScenePrivate::storeMouseButtonsForMouseGrabber(QGraphicsSceneMouseEvent *event)
1099{
1100 for (int i = 0x1; i <= 0x10; i <<= 1) {
1101 if (event->buttons() & i) {
1102 mouseGrabberButtonDownPos.insert(Qt::MouseButton(i),
1103 mouseGrabberItems.last()->d_ptr->genericMapFromScene(event->scenePos(),
1104 event->widget()));
1105 mouseGrabberButtonDownScenePos.insert(Qt::MouseButton(i), event->scenePos());
1106 mouseGrabberButtonDownScreenPos.insert(Qt::MouseButton(i), event->screenPos());
1107 }
1108 }
1109}
1110
1111/*!
1112 \internal
1113*/
1114void QGraphicsScenePrivate::installSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
1115{
1116 sceneEventFilters.insert(watched, filter);
1117}
1118
1119/*!
1120 \internal
1121*/
1122void QGraphicsScenePrivate::removeSceneEventFilter(QGraphicsItem *watched, QGraphicsItem *filter)
1123{
1124 if (!sceneEventFilters.contains(watched))
1125 return;
1126
1127 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(watched);
1128 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(watched);
1129 do {
1130 if (it.value() == filter)
1131 it = sceneEventFilters.erase(it);
1132 else
1133 ++it;
1134 } while (it != end);
1135}
1136
1137/*!
1138 \internal
1139*/
1140bool QGraphicsScenePrivate::filterDescendantEvent(QGraphicsItem *item, QEvent *event)
1141{
1142 if (item && (item->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents)) {
1143 QGraphicsItem *parent = item->parentItem();
1144 while (parent) {
1145 if (parent->d_ptr->filtersDescendantEvents && parent->sceneEventFilter(item, event))
1146 return true;
1147 if (!(parent->d_ptr->ancestorFlags & QGraphicsItemPrivate::AncestorFiltersChildEvents))
1148 return false;
1149 parent = parent->parentItem();
1150 }
1151 }
1152 return false;
1153}
1154
1155/*!
1156 \internal
1157*/
1158bool QGraphicsScenePrivate::filterEvent(QGraphicsItem *item, QEvent *event)
1159{
1160 if (item && !sceneEventFilters.contains(item))
1161 return false;
1162
1163 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator it = sceneEventFilters.lowerBound(item);
1164 QMultiMap<QGraphicsItem *, QGraphicsItem *>::Iterator end = sceneEventFilters.upperBound(item);
1165 while (it != end) {
1166 // ### The filterer and filteree might both be deleted.
1167 if (it.value()->sceneEventFilter(it.key(), event))
1168 return true;
1169 ++it;
1170 }
1171 return false;
1172}
1173
1174/*!
1175 \internal
1176
1177 This is the final dispatch point for any events from the scene to the
1178 item. It filters the event first - if the filter returns true, the event
1179 is considered to have been eaten by the filter, and is therefore stopped
1180 (the default filter returns false). Then/otherwise, if the item is
1181 enabled, the event is sent; otherwise it is stopped.
1182*/
1183bool QGraphicsScenePrivate::sendEvent(QGraphicsItem *item, QEvent *event)
1184{
1185 if (QGraphicsObject *object = item->toGraphicsObject()) {
1186#ifndef QT_NO_GESTURES
1187 QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
1188 if (gestureManager) {
1189 if (gestureManager->filterEvent(object, event))
1190 return true;
1191 }
1192#endif // QT_NO_GESTURES
1193 }
1194
1195 if (filterEvent(item, event))
1196 return false;
1197 if (filterDescendantEvent(item, event))
1198 return false;
1199 if (!item || !item->isEnabled())
1200 return false;
1201 if (QGraphicsObject *o = item->toGraphicsObject()) {
1202 bool spont = event->spontaneous();
1203 if (spont ? qt_sendSpontaneousEvent(o, event) : QApplication::sendEvent(o, event))
1204 return true;
1205 event->spont = spont;
1206 }
1207 return item->sceneEvent(event);
1208}
1209
1210/*!
1211 \internal
1212*/
1213void QGraphicsScenePrivate::cloneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
1214 QGraphicsSceneDragDropEvent *source)
1215{
1216 dest->setWidget(source->widget());
1217 dest->setPos(source->pos());
1218 dest->setScenePos(source->scenePos());
1219 dest->setScreenPos(source->screenPos());
1220 dest->setButtons(source->buttons());
1221 dest->setModifiers(source->modifiers());
1222 dest->setPossibleActions(source->possibleActions());
1223 dest->setProposedAction(source->proposedAction());
1224 dest->setDropAction(source->dropAction());
1225 dest->setSource(source->source());
1226 dest->setMimeData(source->mimeData());
1227}
1228
1229/*!
1230 \internal
1231*/
1232void QGraphicsScenePrivate::sendDragDropEvent(QGraphicsItem *item,
1233 QGraphicsSceneDragDropEvent *dragDropEvent)
1234{
1235 dragDropEvent->setPos(item->d_ptr->genericMapFromScene(dragDropEvent->scenePos(), dragDropEvent->widget()));
1236 sendEvent(item, dragDropEvent);
1237}
1238
1239/*!
1240 \internal
1241*/
1242void QGraphicsScenePrivate::sendHoverEvent(QEvent::Type type, QGraphicsItem *item,
1243 QGraphicsSceneHoverEvent *hoverEvent)
1244{
1245 QGraphicsSceneHoverEvent event(type);
1246 event.setWidget(hoverEvent->widget());
1247 event.setPos(item->d_ptr->genericMapFromScene(hoverEvent->scenePos(), hoverEvent->widget()));
1248 event.setScenePos(hoverEvent->scenePos());
1249 event.setScreenPos(hoverEvent->screenPos());
1250 event.setLastPos(item->d_ptr->genericMapFromScene(hoverEvent->lastScenePos(), hoverEvent->widget()));
1251 event.setLastScenePos(hoverEvent->lastScenePos());
1252 event.setLastScreenPos(hoverEvent->lastScreenPos());
1253 event.setModifiers(hoverEvent->modifiers());
1254 sendEvent(item, &event);
1255}
1256
1257/*!
1258 \internal
1259*/
1260void QGraphicsScenePrivate::sendMouseEvent(QGraphicsSceneMouseEvent *mouseEvent)
1261{
1262 if (mouseEvent->button() == 0 && mouseEvent->buttons() == 0 && lastMouseGrabberItemHasImplicitMouseGrab) {
1263 // ### This is a temporary fix for until we get proper mouse
1264 // grab events.
1265 clearMouseGrabber();
1266 return;
1267 }
1268
1269 QGraphicsItem *item = mouseGrabberItems.last();
1270 if (item->isBlockedByModalPanel())
1271 return;
1272
1273 for (int i = 0x1; i <= 0x10; i <<= 1) {
1274 Qt::MouseButton button = Qt::MouseButton(i);
1275 mouseEvent->setButtonDownPos(button, mouseGrabberButtonDownPos.value(button, item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget())));
1276 mouseEvent->setButtonDownScenePos(button, mouseGrabberButtonDownScenePos.value(button, mouseEvent->scenePos()));
1277 mouseEvent->setButtonDownScreenPos(button, mouseGrabberButtonDownScreenPos.value(button, mouseEvent->screenPos()));
1278 }
1279 mouseEvent->setPos(item->d_ptr->genericMapFromScene(mouseEvent->scenePos(), mouseEvent->widget()));
1280 mouseEvent->setLastPos(item->d_ptr->genericMapFromScene(mouseEvent->lastScenePos(), mouseEvent->widget()));
1281 sendEvent(item, mouseEvent);
1282}
1283
1284/*!
1285 \internal
1286*/
1287void QGraphicsScenePrivate::mousePressEventHandler(QGraphicsSceneMouseEvent *mouseEvent)
1288{
1289 Q_Q(QGraphicsScene);
1290
1291 // Ignore by default, unless we find a mouse grabber that accepts it.
1292 mouseEvent->ignore();
1293
1294 // Deliver to any existing mouse grabber.
1295 if (!mouseGrabberItems.isEmpty()) {
1296 if (mouseGrabberItems.last()->isBlockedByModalPanel())
1297 return;
1298 // The event is ignored by default, but we disregard the event's
1299 // accepted state after delivery; the mouse is grabbed, after all.
1300 sendMouseEvent(mouseEvent);
1301 return;
1302 }
1303
1304 // Start by determining the number of items at the current position.
1305 // Reuse value from earlier calculations if possible.
1306 if (cachedItemsUnderMouse.isEmpty()) {
1307 cachedItemsUnderMouse = itemsAtPosition(mouseEvent->screenPos(),
1308 mouseEvent->scenePos(),
1309 mouseEvent->widget());
1310 }
1311
1312 // Update window activation.
1313 QGraphicsItem *topItem = cachedItemsUnderMouse.value(0);
1314 QGraphicsWidget *newActiveWindow = topItem ? topItem->window() : 0;
1315 if (newActiveWindow && newActiveWindow->isBlockedByModalPanel(&topItem)) {
1316 // pass activation to the blocking modal window
1317 newActiveWindow = topItem ? topItem->window() : 0;
1318 }
1319
1320 if (newActiveWindow != q->activeWindow())
1321 q->setActiveWindow(newActiveWindow);
1322
1323 // Set focus on the topmost enabled item that can take focus.
1324 bool setFocus = false;
1325 foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
1326 if (item->isBlockedByModalPanel()) {
1327 // Make sure we don't clear focus.
1328 setFocus = true;
1329 break;
1330 }
1331 if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable))) {
1332 if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
1333 setFocus = true;
1334 if (item != q->focusItem() && item->d_ptr->mouseSetsFocus)
1335 q->setFocusItem(item, Qt::MouseFocusReason);
1336 break;
1337 }
1338 }
1339 if (item->d_ptr->flags & QGraphicsItem::ItemStopsClickFocusPropagation)
1340 break;
1341 if (item->isPanel())
1342 break;
1343 }
1344
1345 // Check for scene modality.
1346 bool sceneModality = false;
1347 for (int i = 0; i < modalPanels.size(); ++i) {
1348 if (modalPanels.at(i)->panelModality() == QGraphicsItem::SceneModal) {
1349 sceneModality = true;
1350 break;
1351 }
1352 }
1353
1354 // If nobody could take focus, clear it.
1355 if (!stickyFocus && !setFocus && !sceneModality)
1356 q->setFocusItem(0, Qt::MouseFocusReason);
1357
1358 // Any item will do.
1359 if (sceneModality && cachedItemsUnderMouse.isEmpty())
1360 cachedItemsUnderMouse << modalPanels.first();
1361
1362 // Find a mouse grabber by sending mouse press events to all mouse grabber
1363 // candidates one at a time, until the event is accepted. It's accepted by
1364 // default, so the receiver has to explicitly ignore it for it to pass
1365 // through.
1366 foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
1367 if (!(item->acceptedMouseButtons() & mouseEvent->button())) {
1368 // Skip items that don't accept the event's mouse button.
1369 continue;
1370 }
1371
1372 // Check if this item is blocked by a modal panel and deliver the mouse event to the
1373 // blocking panel instead of this item if blocked.
1374 (void) item->isBlockedByModalPanel(&item);
1375
1376 grabMouse(item, /* implicit = */ true);
1377 mouseEvent->accept();
1378
1379 // check if the item we are sending to are disabled (before we send the event)
1380 bool disabled = !item->isEnabled();
1381 bool isPanel = item->isPanel();
1382 if (mouseEvent->type() == QEvent::GraphicsSceneMouseDoubleClick
1383 && item != lastMouseGrabberItem && lastMouseGrabberItem) {
1384 // If this item is different from the item that received the last
1385 // mouse event, and mouseEvent is a doubleclick event, then the
1386 // event is converted to a press. Known limitation:
1387 // Triple-clicking will not generate a doubleclick, though.
1388 QGraphicsSceneMouseEvent mousePress(QEvent::GraphicsSceneMousePress);
1389 mousePress.spont = mouseEvent->spont;
1390 mousePress.accept();
1391 mousePress.setButton(mouseEvent->button());
1392 mousePress.setButtons(mouseEvent->buttons());
1393 mousePress.setScreenPos(mouseEvent->screenPos());
1394 mousePress.setScenePos(mouseEvent->scenePos());
1395 mousePress.setModifiers(mouseEvent->modifiers());
1396 mousePress.setWidget(mouseEvent->widget());
1397 mousePress.setButtonDownPos(mouseEvent->button(),
1398 mouseEvent->buttonDownPos(mouseEvent->button()));
1399 mousePress.setButtonDownScenePos(mouseEvent->button(),
1400 mouseEvent->buttonDownScenePos(mouseEvent->button()));
1401 mousePress.setButtonDownScreenPos(mouseEvent->button(),
1402 mouseEvent->buttonDownScreenPos(mouseEvent->button()));
1403 sendMouseEvent(&mousePress);
1404 mouseEvent->setAccepted(mousePress.isAccepted());
1405 } else {
1406 sendMouseEvent(mouseEvent);
1407 }
1408
1409 bool dontSendUngrabEvents = mouseGrabberItems.isEmpty() || mouseGrabberItems.last() != item;
1410 if (disabled) {
1411 ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
1412 break;
1413 }
1414 if (mouseEvent->isAccepted()) {
1415 if (!mouseGrabberItems.isEmpty())
1416 storeMouseButtonsForMouseGrabber(mouseEvent);
1417 lastMouseGrabberItem = item;
1418 return;
1419 }
1420 ungrabMouse(item, /* itemIsDying = */ dontSendUngrabEvents);
1421
1422 // Don't propagate through panels.
1423 if (isPanel)
1424 break;
1425 }
1426
1427 // Is the event still ignored? Then the mouse press goes to the scene.
1428 // Reset the mouse grabber, clear the selection, clear focus, and leave
1429 // the event ignored so that it can propagate through the originating
1430 // view.
1431 if (!mouseEvent->isAccepted()) {
1432 clearMouseGrabber();
1433
1434 QGraphicsView *view = mouseEvent->widget() ? qobject_cast<QGraphicsView *>(mouseEvent->widget()->parentWidget()) : 0;
1435 bool dontClearSelection = view && view->dragMode() == QGraphicsView::ScrollHandDrag;
1436 if (!dontClearSelection) {
1437 // Clear the selection if the originating view isn't in scroll
1438 // hand drag mode. The view will clear the selection if no drag
1439 // happened.
1440 q->clearSelection();
1441 }
1442 }
1443}
1444
1445/*!
1446 \internal
1447
1448 Ensures that the list of toplevels is sorted by insertion order, and that
1449 the siblingIndexes are packed (no gaps), and start at 0.
1450
1451 ### This function is almost identical to
1452 QGraphicsItemPrivate::ensureSequentialSiblingIndex().
1453*/
1454void QGraphicsScenePrivate::ensureSequentialTopLevelSiblingIndexes()
1455{
1456 if (!topLevelSequentialOrdering) {
1457 qSort(topLevelItems.begin(), topLevelItems.end(), QGraphicsItemPrivate::insertionOrder);
1458 topLevelSequentialOrdering = true;
1459 needSortTopLevelItems = 1;
1460 }
1461 if (holesInTopLevelSiblingIndex) {
1462 holesInTopLevelSiblingIndex = 0;
1463 for (int i = 0; i < topLevelItems.size(); ++i)
1464 topLevelItems[i]->d_ptr->siblingIndex = i;
1465 }
1466}
1467
1468/*!
1469 \internal
1470
1471 Set the font and propagate the changes if the font is different from the
1472 current font.
1473*/
1474void QGraphicsScenePrivate::setFont_helper(const QFont &font)
1475{
1476 if (this->font == font && this->font.resolve() == font.resolve())
1477 return;
1478 updateFont(font);
1479}
1480
1481/*!
1482 \internal
1483
1484 Resolve the scene's font against the application font, and propagate the
1485 changes too all items in the scene.
1486*/
1487void QGraphicsScenePrivate::resolveFont()
1488{
1489 QFont naturalFont = QApplication::font();
1490 naturalFont.resolve(0);
1491 QFont resolvedFont = font.resolve(naturalFont);
1492 updateFont(resolvedFont);
1493}
1494
1495/*!
1496 \internal
1497
1498 Update the font, and whether or not it has changed, reresolve all fonts in
1499 the scene.
1500*/
1501void QGraphicsScenePrivate::updateFont(const QFont &font)
1502{
1503 Q_Q(QGraphicsScene);
1504
1505 // Update local font setting.
1506 this->font = font;
1507
1508 // Resolve the fonts of all top-level widget items, or widget items
1509 // whose parent is not a widget.
1510 foreach (QGraphicsItem *item, q->items()) {
1511 if (!item->parentItem()) {
1512 // Resolvefont for an item is a noop operation, but
1513 // every item can be a widget, or can have a widget
1514 // childre.
1515 item->d_ptr->resolveFont(font.resolve());
1516 }
1517 }
1518
1519 // Send the scene a FontChange event.
1520 QEvent event(QEvent::FontChange);
1521 QApplication::sendEvent(q, &event);
1522}
1523
1524/*!
1525 \internal
1526
1527 Set the palette and propagate the changes if the palette is different from
1528 the current palette.
1529*/
1530void QGraphicsScenePrivate::setPalette_helper(const QPalette &palette)
1531{
1532 if (this->palette == palette && this->palette.resolve() == palette.resolve())
1533 return;
1534 updatePalette(palette);
1535}
1536
1537/*!
1538 \internal
1539
1540 Resolve the scene's palette against the application palette, and propagate
1541 the changes too all items in the scene.
1542*/
1543void QGraphicsScenePrivate::resolvePalette()
1544{
1545 QPalette naturalPalette = QApplication::palette();
1546 naturalPalette.resolve(0);
1547 QPalette resolvedPalette = palette.resolve(naturalPalette);
1548 updatePalette(resolvedPalette);
1549}
1550
1551/*!
1552 \internal
1553
1554 Update the palette, and whether or not it has changed, reresolve all
1555 palettes in the scene.
1556*/
1557void QGraphicsScenePrivate::updatePalette(const QPalette &palette)
1558{
1559 Q_Q(QGraphicsScene);
1560
1561 // Update local palette setting.
1562 this->palette = palette;
1563
1564 // Resolve the palettes of all top-level widget items, or widget items
1565 // whose parent is not a widget.
1566 foreach (QGraphicsItem *item, q->items()) {
1567 if (!item->parentItem()) {
1568 // Resolvefont for an item is a noop operation, but
1569 // every item can be a widget, or can have a widget
1570 // childre.
1571 item->d_ptr->resolvePalette(palette.resolve());
1572 }
1573 }
1574
1575 // Send the scene a PaletteChange event.
1576 QEvent event(QEvent::PaletteChange);
1577 QApplication::sendEvent(q, &event);
1578}
1579
1580/*!
1581 Constructs a QGraphicsScene object. The \a parent parameter is
1582 passed to QObject's constructor.
1583*/
1584QGraphicsScene::QGraphicsScene(QObject *parent)
1585 : QObject(*new QGraphicsScenePrivate, parent)
1586{
1587 d_func()->init();
1588}
1589
1590/*!
1591 Constructs a QGraphicsScene object, using \a sceneRect for its
1592 scene rectangle. The \a parent parameter is passed to QObject's
1593 constructor.
1594
1595 \sa sceneRect
1596*/
1597QGraphicsScene::QGraphicsScene(const QRectF &sceneRect, QObject *parent)
1598 : QObject(*new QGraphicsScenePrivate, parent)
1599{
1600 d_func()->init();
1601 setSceneRect(sceneRect);
1602}
1603
1604/*!
1605 Constructs a QGraphicsScene object, using the rectangle specified
1606 by (\a x, \a y), and the given \a width and \a height for its
1607 scene rectangle. The \a parent parameter is passed to QObject's
1608 constructor.
1609
1610 \sa sceneRect
1611*/
1612QGraphicsScene::QGraphicsScene(qreal x, qreal y, qreal width, qreal height, QObject *parent)
1613 : QObject(*new QGraphicsScenePrivate, parent)
1614{
1615 d_func()->init();
1616 setSceneRect(x, y, width, height);
1617}
1618
1619/*!
1620 Removes and deletes all items from the scene object
1621 before destroying the scene object. The scene object
1622 is removed from the application's global scene list,
1623 and it is removed from all associated views.
1624*/
1625QGraphicsScene::~QGraphicsScene()
1626{
1627 Q_D(QGraphicsScene);
1628
1629 // Remove this scene from qApp's global scene list.
1630 qApp->d_func()->scene_list.removeAll(this);
1631
1632 clear();
1633
1634 // Remove this scene from all associated views.
1635 for (int j = 0; j < d->views.size(); ++j)
1636 d->views.at(j)->setScene(0);
1637}
1638
1639/*!
1640 \property QGraphicsScene::sceneRect
1641 \brief the scene rectangle; the bounding rectangle of the scene
1642
1643 The scene rectangle defines the extent of the scene. It is
1644 primarily used by QGraphicsView to determine the view's default
1645 scrollable area, and by QGraphicsScene to manage item indexing.
1646
1647 If unset, or if set to a null QRectF, sceneRect() will return the largest
1648 bounding rect of all items on the scene since the scene was created (i.e.,
1649 a rectangle that grows when items are added to or moved in the scene, but
1650 never shrinks).
1651
1652 \sa width(), height(), QGraphicsView::sceneRect
1653*/
1654QRectF QGraphicsScene::sceneRect() const
1655{
1656 Q_D(const QGraphicsScene);
1657 if (d->hasSceneRect)
1658 return d->sceneRect;
1659
1660 if (d->dirtyGrowingItemsBoundingRect) {
1661 // Lazily update the growing items bounding rect
1662 QGraphicsScenePrivate *thatd = const_cast<QGraphicsScenePrivate *>(d);
1663 QRectF oldGrowingBoundingRect = thatd->growingItemsBoundingRect;
1664 thatd->growingItemsBoundingRect |= itemsBoundingRect();
1665 thatd->dirtyGrowingItemsBoundingRect = false;
1666 if (oldGrowingBoundingRect != thatd->growingItemsBoundingRect)
1667 emit const_cast<QGraphicsScene *>(this)->sceneRectChanged(thatd->growingItemsBoundingRect);
1668 }
1669 return d->growingItemsBoundingRect;
1670}
1671void QGraphicsScene::setSceneRect(const QRectF &rect)
1672{
1673 Q_D(QGraphicsScene);
1674 if (rect != d->sceneRect) {
1675 d->hasSceneRect = !rect.isNull();
1676 d->sceneRect = rect;
1677 emit sceneRectChanged(d->hasSceneRect ? rect : d->growingItemsBoundingRect);
1678 }
1679}
1680
1681/*!
1682 \fn qreal QGraphicsScene::width() const
1683
1684 This convenience function is equivalent to calling sceneRect().width().
1685
1686 \sa height()
1687*/
1688
1689/*!
1690 \fn qreal QGraphicsScene::height() const
1691
1692 This convenience function is equivalent to calling \c sceneRect().height().
1693
1694 \sa width()
1695*/
1696
1697/*!
1698 Renders the \a source rect from scene into \a target, using \a painter. This
1699 function is useful for capturing the contents of the scene onto a paint
1700 device, such as a QImage (e.g., to take a screenshot), or for printing
1701 with QPrinter. For example:
1702
1703 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 1
1704
1705 If \a source is a null rect, this function will use sceneRect() to
1706 determine what to render. If \a target is a null rect, the dimensions of \a
1707 painter's paint device will be used.
1708
1709 The source rect contents will be transformed according to \a
1710 aspectRatioMode to fit into the target rect. By default, the aspect ratio
1711 is kept, and \a source is scaled to fit in \a target.
1712
1713 \sa QGraphicsView::render()
1714*/
1715void QGraphicsScene::render(QPainter *painter, const QRectF &target, const QRectF &source,
1716 Qt::AspectRatioMode aspectRatioMode)
1717{
1718 // ### Switch to using the recursive rendering algorithm instead.
1719
1720 // Default source rect = scene rect
1721 QRectF sourceRect = source;
1722 if (sourceRect.isNull())
1723 sourceRect = sceneRect();
1724
1725 // Default target rect = device rect
1726 QRectF targetRect = target;
1727 if (targetRect.isNull()) {
1728 if (painter->device()->devType() == QInternal::Picture)
1729 targetRect = sourceRect;
1730 else
1731 targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
1732 }
1733
1734 // Find the ideal x / y scaling ratio to fit \a source into \a target.
1735 qreal xratio = targetRect.width() / sourceRect.width();
1736 qreal yratio = targetRect.height() / sourceRect.height();
1737
1738 // Scale according to the aspect ratio mode.
1739 switch (aspectRatioMode) {
1740 case Qt::KeepAspectRatio:
1741 xratio = yratio = qMin(xratio, yratio);
1742 break;
1743 case Qt::KeepAspectRatioByExpanding:
1744 xratio = yratio = qMax(xratio, yratio);
1745 break;
1746 case Qt::IgnoreAspectRatio:
1747 break;
1748 }
1749
1750 // Find all items to draw, and reverse the list (we want to draw
1751 // in reverse order).
1752 QList<QGraphicsItem *> itemList = items(sourceRect, Qt::IntersectsItemBoundingRect);
1753 QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
1754 int numItems = itemList.size();
1755 for (int i = 0; i < numItems; ++i)
1756 itemArray[numItems - i - 1] = itemList.at(i);
1757 itemList.clear();
1758
1759 painter->save();
1760
1761 // Transform the painter.
1762 painter->setClipRect(targetRect, Qt::IntersectClip);
1763 QTransform painterTransform;
1764 painterTransform *= QTransform()
1765 .translate(targetRect.left(), targetRect.top())
1766 .scale(xratio, yratio)
1767 .translate(-sourceRect.left(), -sourceRect.top());
1768 painter->setWorldTransform(painterTransform, true);
1769
1770 // Two unit vectors.
1771 QLineF v1(0, 0, 1, 0);
1772 QLineF v2(0, 0, 0, 1);
1773
1774 // Generate the style options
1775 QStyleOptionGraphicsItem *styleOptionArray = new QStyleOptionGraphicsItem[numItems];
1776 for (int i = 0; i < numItems; ++i)
1777 itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterTransform, targetRect.toRect());
1778
1779 // Render the scene.
1780 drawBackground(painter, sourceRect);
1781 drawItems(painter, numItems, itemArray, styleOptionArray);
1782 drawForeground(painter, sourceRect);
1783
1784 delete [] itemArray;
1785 delete [] styleOptionArray;
1786
1787 painter->restore();
1788}
1789
1790/*!
1791 \property QGraphicsScene::itemIndexMethod
1792 \brief the item indexing method.
1793
1794 QGraphicsScene applies an indexing algorithm to the scene, to speed up
1795 item discovery functions like items() and itemAt(). Indexing is most
1796 efficient for static scenes (i.e., where items don't move around). For
1797 dynamic scenes, or scenes with many animated items, the index bookkeeping
1798 can outweight the fast lookup speeds.
1799
1800 For the common case, the default index method BspTreeIndex works fine. If
1801 your scene uses many animations and you are experiencing slowness, you can
1802 disable indexing by calling \c setItemIndexMethod(NoIndex).
1803
1804 \sa bspTreeDepth
1805*/
1806QGraphicsScene::ItemIndexMethod QGraphicsScene::itemIndexMethod() const
1807{
1808 Q_D(const QGraphicsScene);
1809 return d->indexMethod;
1810}
1811void QGraphicsScene::setItemIndexMethod(ItemIndexMethod method)
1812{
1813 Q_D(QGraphicsScene);
1814 if (d->indexMethod == method)
1815 return;
1816
1817 d->indexMethod = method;
1818
1819 QList<QGraphicsItem *> oldItems = d->index->items(Qt::DescendingOrder);
1820 delete d->index;
1821 if (method == BspTreeIndex)
1822 d->index = new QGraphicsSceneBspTreeIndex(this);
1823 else
1824 d->index = new QGraphicsSceneLinearIndex(this);
1825 for (int i = oldItems.size() - 1; i >= 0; --i)
1826 d->index->addItem(oldItems.at(i));
1827}
1828
1829/*!
1830 \property QGraphicsScene::bspTreeDepth
1831 \brief the depth of QGraphicsScene's BSP index tree
1832 \since 4.3
1833
1834 This property has no effect when NoIndex is used.
1835
1836 This value determines the depth of QGraphicsScene's BSP tree. The depth
1837 directly affects QGraphicsScene's performance and memory usage; the latter
1838 growing exponentially with the depth of the tree. With an optimal tree
1839 depth, QGraphicsScene can instantly determine the locality of items, even
1840 for scenes with thousands or millions of items. This also greatly improves
1841 rendering performance.
1842
1843 By default, the value is 0, in which case Qt will guess a reasonable
1844 default depth based on the size, location and number of items in the
1845 scene. If these parameters change frequently, however, you may experience
1846 slowdowns as QGraphicsScene retunes the depth internally. You can avoid
1847 potential slowdowns by fixating the tree depth through setting this
1848 property.
1849
1850 The depth of the tree and the size of the scene rectangle decide the
1851 granularity of the scene's partitioning. The size of each scene segment is
1852 determined by the following algorithm:
1853
1854 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 2
1855
1856 The BSP tree has an optimal size when each segment contains between 0 and
1857 10 items.
1858
1859 \sa itemIndexMethod
1860*/
1861int QGraphicsScene::bspTreeDepth() const
1862{
1863 Q_D(const QGraphicsScene);
1864 QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
1865 return bspTree ? bspTree->bspTreeDepth() : 0;
1866}
1867void QGraphicsScene::setBspTreeDepth(int depth)
1868{
1869 Q_D(QGraphicsScene);
1870 if (depth < 0) {
1871 qWarning("QGraphicsScene::setBspTreeDepth: invalid depth %d ignored; must be >= 0", depth);
1872 return;
1873 }
1874
1875 QGraphicsSceneBspTreeIndex *bspTree = qobject_cast<QGraphicsSceneBspTreeIndex *>(d->index);
1876 if (!bspTree) {
1877 qWarning("QGraphicsScene::setBspTreeDepth: can not apply if indexing method is not BSP");
1878 return;
1879 }
1880 bspTree->setBspTreeDepth(depth);
1881}
1882
1883/*!
1884 \property QGraphicsScene::sortCacheEnabled
1885 \brief whether sort caching is enabled
1886 \since 4.5
1887 \obsolete
1888
1889 Since Qt 4.6, this property has no effect.
1890*/
1891bool QGraphicsScene::isSortCacheEnabled() const
1892{
1893 Q_D(const QGraphicsScene);
1894 return d->sortCacheEnabled;
1895}
1896void QGraphicsScene::setSortCacheEnabled(bool enabled)
1897{
1898 Q_D(QGraphicsScene);
1899 if (d->sortCacheEnabled == enabled)
1900 return;
1901 d->sortCacheEnabled = enabled;
1902}
1903
1904/*!
1905 Calculates and returns the bounding rect of all items on the scene. This
1906 function works by iterating over all items, and because if this, it can
1907 be slow for large scenes.
1908
1909 \sa sceneRect()
1910*/
1911QRectF QGraphicsScene::itemsBoundingRect() const
1912{
1913 // Does not take untransformable items into account.
1914 QRectF boundingRect;
1915 foreach (QGraphicsItem *item, items())
1916 boundingRect |= item->sceneBoundingRect();
1917 return boundingRect;
1918}
1919
1920/*!
1921 Returns a list of all items in the scene in descending stacking order.
1922
1923 \sa addItem(), removeItem(), {QGraphicsItem#Sorting}{Sorting}
1924*/
1925QList<QGraphicsItem *> QGraphicsScene::items() const
1926{
1927 Q_D(const QGraphicsScene);
1928 return d->index->items(Qt::DescendingOrder);
1929}
1930
1931/*!
1932 Returns an ordered list of all items on the scene. \a order decides the
1933 stacking order.
1934
1935 \sa addItem(), removeItem(), {QGraphicsItem#Sorting}{Sorting}
1936*/
1937QList<QGraphicsItem *> QGraphicsScene::items(Qt::SortOrder order) const
1938{
1939 Q_D(const QGraphicsScene);
1940 return d->index->items(order);
1941}
1942
1943/*!
1944 \obsolete
1945
1946 Returns all visible items at position \a pos in the scene. The items are
1947 listed in descending stacking order (i.e., the first item in the list is the
1948 top-most item, and the last item is the bottom-most item).
1949
1950 This function is deprecated and returns incorrect results if the scene
1951 contains items that ignore transformations. Use the overload that takes
1952 a QTransform instead.
1953
1954 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
1955*/
1956QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos) const
1957{
1958 Q_D(const QGraphicsScene);
1959 return d->index->items(pos, Qt::IntersectsItemShape, Qt::DescendingOrder);
1960}
1961
1962/*!
1963 \overload
1964 \obsolete
1965
1966 Returns all visible items that, depending on \a mode, are either inside or
1967 intersect with the specified \a rectangle.
1968
1969 The default value for \a mode is Qt::IntersectsItemShape; all items whose
1970 exact shape intersects with or is contained by \a rectangle are returned.
1971
1972 This function is deprecated and returns incorrect results if the scene
1973 contains items that ignore transformations. Use the overload that takes
1974 a QTransform instead.
1975
1976 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
1977*/
1978QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rectangle, Qt::ItemSelectionMode mode) const
1979{
1980 Q_D(const QGraphicsScene);
1981 return d->index->items(rectangle, mode, Qt::DescendingOrder);
1982}
1983
1984/*!
1985 \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode) const
1986 \obsolete
1987 \since 4.3
1988
1989 This convenience function is equivalent to calling items(QRectF(\a x, \a y, \a w, \a h), \a mode).
1990
1991 This function is deprecated and returns incorrect results if the scene
1992 contains items that ignore transformations. Use the overload that takes
1993 a QTransform instead.
1994*/
1995
1996/*!
1997 \fn QList<QGraphicsItem *> QGraphicsScene::items(qreal x, qreal y, qreal w, qreal h, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
1998 \overload
1999 \since 4.6
2000
2001 \brief Returns all visible items that, depending on \a mode, are
2002 either inside or intersect with the rectangle defined by \a x, \a y,
2003 \a w and \a h, in a list sorted using \a order.
2004
2005 \a deviceTransform is the transformation that applies to the view, and needs to
2006 be provided if the scene contains items that ignore transformations.
2007*/
2008
2009/*!
2010 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const
2011 \overload
2012 \obsolete
2013
2014 Returns all visible items that, depending on \a mode, are either inside or
2015 intersect with the polygon \a polygon.
2016
2017 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2018 exact shape intersects with or is contained by \a polygon are returned.
2019
2020 This function is deprecated and returns incorrect results if the scene
2021 contains items that ignore transformations. Use the overload that takes
2022 a QTransform instead.
2023
2024 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2025*/
2026QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode) const
2027{
2028 Q_D(const QGraphicsScene);
2029 return d->index->items(polygon, mode, Qt::DescendingOrder);
2030}
2031
2032/*!
2033 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
2034 \overload
2035 \obsolete
2036
2037 Returns all visible items that, depending on \a path, are either inside or
2038 intersect with the path \a path.
2039
2040 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2041 exact shape intersects with or is contained by \a path are returned.
2042
2043 This function is deprecated and returns incorrect results if the scene
2044 contains items that ignore transformations. Use the overload that takes
2045 a QTransform instead.
2046
2047 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2048*/
2049QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
2050{
2051 Q_D(const QGraphicsScene);
2052 return d->index->items(path, mode, Qt::DescendingOrder);
2053}
2054
2055/*!
2056 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2057 \since 4.6
2058
2059 \brief Returns all visible items that, depending on \a mode, are at
2060 the specified \a pos in a list sorted using \a order.
2061
2062 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2063 exact shape intersects with \a pos are returned.
2064
2065 \a deviceTransform is the transformation that applies to the view, and needs to
2066 be provided if the scene contains items that ignore transformations.
2067
2068 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2069*/
2070QList<QGraphicsItem *> QGraphicsScene::items(const QPointF &pos, Qt::ItemSelectionMode mode,
2071 Qt::SortOrder order, const QTransform &deviceTransform) const
2072{
2073 Q_D(const QGraphicsScene);
2074 return d->index->items(pos, mode, order, deviceTransform);
2075}
2076
2077/*!
2078 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2079 \overload
2080 \since 4.6
2081
2082 \brief Returns all visible items that, depending on \a mode, are
2083 either inside or intersect with the specified \a rect and return a
2084 list sorted using \a order.
2085
2086 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2087 exact shape intersects with or is contained by \a rect are returned.
2088
2089 \a deviceTransform is the transformation that applies to the view, and needs to
2090 be provided if the scene contains items that ignore transformations.
2091
2092 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2093*/
2094QList<QGraphicsItem *> QGraphicsScene::items(const QRectF &rect, Qt::ItemSelectionMode mode,
2095 Qt::SortOrder order, const QTransform &deviceTransform) const
2096{
2097 Q_D(const QGraphicsScene);
2098 return d->index->items(rect, mode, order, deviceTransform);
2099}
2100
2101/*!
2102 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2103 \overload
2104 \since 4.6
2105
2106 \brief Returns all visible items that, depending on \a mode, are
2107 either inside or intersect with the specified \a polygon and return
2108 a list sorted using \a order.
2109
2110 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2111 exact shape intersects with or is contained by \a polygon are returned.
2112
2113 \a deviceTransform is the transformation that applies to the view, and needs to
2114 be provided if the scene contains items that ignore transformations.
2115
2116 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2117*/
2118QList<QGraphicsItem *> QGraphicsScene::items(const QPolygonF &polygon, Qt::ItemSelectionMode mode,
2119 Qt::SortOrder order, const QTransform &deviceTransform) const
2120{
2121 Q_D(const QGraphicsScene);
2122 return d->index->items(polygon, mode, order, deviceTransform);
2123}
2124
2125/*!
2126 \fn QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode, Qt::SortOrder order, const QTransform &deviceTransform) const
2127 \overload
2128 \since 4.6
2129
2130 \brief Returns all visible items that, depending on \a mode, are
2131 either inside or intersect with the specified \a path and return a
2132 list sorted using \a order.
2133
2134 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2135 exact shape intersects with or is contained by \a path are returned.
2136
2137 \a deviceTransform is the transformation that applies to the view, and needs to
2138 be provided if the scene contains items that ignore transformations.
2139
2140 \sa itemAt(), {QGraphicsItem#Sorting}{Sorting}
2141*/
2142QList<QGraphicsItem *> QGraphicsScene::items(const QPainterPath &path, Qt::ItemSelectionMode mode,
2143 Qt::SortOrder order, const QTransform &deviceTransform) const
2144{
2145 Q_D(const QGraphicsScene);
2146 return d->index->items(path, mode, order, deviceTransform);
2147}
2148
2149/*!
2150 Returns a list of all items that collide with \a item. Collisions are
2151 determined by calling QGraphicsItem::collidesWithItem(); the collision
2152 detection is determined by \a mode. By default, all items whose shape
2153 intersects \a item or is contained inside \a item's shape are returned.
2154
2155 The items are returned in descending stacking order (i.e., the first item
2156 in the list is the uppermost item, and the last item is the lowermost
2157 item).
2158
2159 \sa items(), itemAt(), QGraphicsItem::collidesWithItem(), {QGraphicsItem#Sorting}{Sorting}
2160*/
2161QList<QGraphicsItem *> QGraphicsScene::collidingItems(const QGraphicsItem *item,
2162 Qt::ItemSelectionMode mode) const
2163{
2164 Q_D(const QGraphicsScene);
2165 if (!item) {
2166 qWarning("QGraphicsScene::collidingItems: cannot find collisions for null item");
2167 return QList<QGraphicsItem *>();
2168 }
2169
2170 // Does not support ItemIgnoresTransformations.
2171 QList<QGraphicsItem *> tmp;
2172 foreach (QGraphicsItem *itemInVicinity, d->index->estimateItems(item->sceneBoundingRect(), Qt::DescendingOrder)) {
2173 if (item != itemInVicinity && item->collidesWithItem(itemInVicinity, mode))
2174 tmp << itemInVicinity;
2175 }
2176 return tmp;
2177}
2178
2179/*!
2180 \overload
2181 \obsolete
2182
2183 Returns the topmost visible item at the specified \a position, or 0 if
2184 there are no items at this position.
2185
2186 This function is deprecated and returns incorrect results if the scene
2187 contains items that ignore transformations. Use the overload that takes
2188 a QTransform instead.
2189
2190 \sa items(), collidingItems(), {QGraphicsItem#Sorting}{Sorting}
2191*/
2192QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position) const
2193{
2194 QList<QGraphicsItem *> itemsAtPoint = items(position);
2195 return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first();
2196}
2197
2198/*!
2199 \since 4.6
2200
2201 Returns the topmost visible item at the specified \a position, or 0
2202 if there are no items at this position.
2203
2204 \a deviceTransform is the transformation that applies to the view, and needs to
2205 be provided if the scene contains items that ignore transformations.
2206
2207 \sa items(), collidingItems(), {QGraphicsItem#Sorting}{Sorting}
2208*/
2209QGraphicsItem *QGraphicsScene::itemAt(const QPointF &position, const QTransform &deviceTransform) const
2210{
2211 QList<QGraphicsItem *> itemsAtPoint = items(position, Qt::IntersectsItemShape,
2212 Qt::DescendingOrder, deviceTransform);
2213 return itemsAtPoint.isEmpty() ? 0 : itemsAtPoint.first();
2214}
2215
2216/*!
2217 \fn QGraphicsScene::itemAt(qreal x, qreal y, const QTransform &deviceTransform) const
2218 \overload
2219 \since 4.6
2220
2221 Returns the topmost item at the position specified by (\a x, \a
2222 y), or 0 if there are no items at this position.
2223
2224 \a deviceTransform is the transformation that applies to the view, and needs to
2225 be provided if the scene contains items that ignore transformations.
2226
2227 This convenience function is equivalent to calling \c
2228 {itemAt(QPointF(x, y), deviceTransform)}.
2229*/
2230
2231/*!
2232 \fn QGraphicsScene::itemAt(qreal x, qreal y) const
2233 \overload
2234 \obsolete
2235
2236 Returns the topmost item at the position specified by (\a x, \a
2237 y), or 0 if there are no items at this position.
2238
2239 This convenience function is equivalent to calling \c
2240 {itemAt(QPointF(x, y))}.
2241
2242 This function is deprecated and returns incorrect results if the scene
2243 contains items that ignore transformations. Use the overload that takes
2244 a QTransform instead.
2245*/
2246
2247/*!
2248 Returns a list of all currently selected items. The items are
2249 returned in no particular order.
2250
2251 \sa setSelectionArea()
2252*/
2253QList<QGraphicsItem *> QGraphicsScene::selectedItems() const
2254{
2255 Q_D(const QGraphicsScene);
2256
2257 // Optimization: Lazily removes items that are not selected.
2258 QGraphicsScene *that = const_cast<QGraphicsScene *>(this);
2259 QSet<QGraphicsItem *> actuallySelectedSet;
2260 foreach (QGraphicsItem *item, that->d_func()->selectedItems) {
2261 if (item->isSelected())
2262 actuallySelectedSet << item;
2263 }
2264
2265 that->d_func()->selectedItems = actuallySelectedSet;
2266
2267 return d->selectedItems.values();
2268}
2269
2270/*!
2271 Returns the selection area that was previously set with
2272 setSelectionArea(), or an empty QPainterPath if no selection area has been
2273 set.
2274
2275 \sa setSelectionArea()
2276*/
2277QPainterPath QGraphicsScene::selectionArea() const
2278{
2279 Q_D(const QGraphicsScene);
2280 return d->selectionArea;
2281}
2282
2283/*!
2284 \since 4.6
2285
2286 Sets the selection area to \a path. All items within this area are
2287 immediately selected, and all items outside are unselected. You can get
2288 the list of all selected items by calling selectedItems().
2289
2290 \a deviceTransform is the transformation that applies to the view, and needs to
2291 be provided if the scene contains items that ignore transformations.
2292
2293 For an item to be selected, it must be marked as \e selectable
2294 (QGraphicsItem::ItemIsSelectable).
2295
2296 \sa clearSelection(), selectionArea()
2297*/
2298void QGraphicsScene::setSelectionArea(const QPainterPath &path, const QTransform &deviceTransform)
2299{
2300 setSelectionArea(path, Qt::IntersectsItemShape, deviceTransform);
2301}
2302
2303/*!
2304 \obsolete
2305 \overload
2306
2307 Sets the selection area to \a path.
2308
2309 This function is deprecated and leads to incorrect results if the scene
2310 contains items that ignore transformations. Use the overload that takes
2311 a QTransform instead.
2312*/
2313void QGraphicsScene::setSelectionArea(const QPainterPath &path)
2314{
2315 setSelectionArea(path, Qt::IntersectsItemShape, QTransform());
2316}
2317
2318/*!
2319 \obsolete
2320 \overload
2321 \since 4.3
2322
2323 Sets the selection area to \a path using \a mode to determine if items are
2324 included in the selection area.
2325
2326 \sa clearSelection(), selectionArea()
2327*/
2328void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode)
2329{
2330 setSelectionArea(path, mode, QTransform());
2331}
2332
2333/*!
2334 \overload
2335 \since 4.6
2336
2337 Sets the selection area to \a path using \a mode to determine if items are
2338 included in the selection area.
2339
2340 \a deviceTransform is the transformation that applies to the view, and needs to
2341 be provided if the scene contains items that ignore transformations.
2342
2343 \sa clearSelection(), selectionArea()
2344*/
2345void QGraphicsScene::setSelectionArea(const QPainterPath &path, Qt::ItemSelectionMode mode,
2346 const QTransform &deviceTransform)
2347{
2348 Q_D(QGraphicsScene);
2349
2350 // Note: with boolean path operations, we can improve performance here
2351 // quite a lot by "growing" the old path instead of replacing it. That
2352 // allows us to only check the intersect area for changes, instead of
2353 // reevaluating the whole path over again.
2354 d->selectionArea = path;
2355
2356 QSet<QGraphicsItem *> unselectItems = d->selectedItems;
2357
2358 // Disable emitting selectionChanged() for individual items.
2359 ++d->selectionChanging;
2360 bool changed = false;
2361
2362 // Set all items in path to selected.
2363 foreach (QGraphicsItem *item, items(path, mode, Qt::DescendingOrder, deviceTransform)) {
2364 if (item->flags() & QGraphicsItem::ItemIsSelectable) {
2365 if (!item->isSelected())
2366 changed = true;
2367 unselectItems.remove(item);
2368 item->setSelected(true);
2369 }
2370 }
2371
2372 // Unselect all items outside path.
2373 foreach (QGraphicsItem *item, unselectItems) {
2374 item->setSelected(false);
2375 changed = true;
2376 }
2377
2378 // Reenable emitting selectionChanged() for individual items.
2379 --d->selectionChanging;
2380
2381 if (!d->selectionChanging && changed)
2382 emit selectionChanged();
2383}
2384
2385/*!
2386 Clears the current selection.
2387
2388 \sa setSelectionArea(), selectedItems()
2389*/
2390void QGraphicsScene::clearSelection()
2391{
2392 Q_D(QGraphicsScene);
2393
2394 // Disable emitting selectionChanged
2395 ++d->selectionChanging;
2396 bool changed = !d->selectedItems.isEmpty();
2397
2398 foreach (QGraphicsItem *item, d->selectedItems)
2399 item->setSelected(false);
2400 d->selectedItems.clear();
2401
2402 // Reenable emitting selectionChanged() for individual items.
2403 --d->selectionChanging;
2404
2405 if (!d->selectionChanging && changed)
2406 emit selectionChanged();
2407}
2408
2409/*!
2410 \since 4.4
2411
2412 Removes and deletes all items from the scene, but otherwise leaves the
2413 state of the scene unchanged.
2414
2415 \sa addItem()
2416*/
2417void QGraphicsScene::clear()
2418{
2419 Q_D(QGraphicsScene);
2420 // NB! We have to clear the index before deleting items; otherwise the
2421 // index might try to access dangling item pointers.
2422 d->index->clear();
2423 // NB! QGraphicsScenePrivate::unregisterTopLevelItem() removes items
2424 while (!d->topLevelItems.isEmpty())
2425 delete d->topLevelItems.first();
2426 Q_ASSERT(d->topLevelItems.isEmpty());
2427 d->lastItemCount = 0;
2428 d->allItemsIgnoreHoverEvents = true;
2429 d->allItemsUseDefaultCursor = true;
2430 d->allItemsIgnoreTouchEvents = true;
2431}
2432
2433/*!
2434 Groups all items in \a items into a new QGraphicsItemGroup, and returns a
2435 pointer to the group. The group is created with the common ancestor of \a
2436 items as its parent, and with position (0, 0). The items are all
2437 reparented to the group, and their positions and transformations are
2438 mapped to the group. If \a items is empty, this function will return an
2439 empty top-level QGraphicsItemGroup.
2440
2441 QGraphicsScene has ownership of the group item; you do not need to delete
2442 it. To dismantle (ungroup) a group, call destroyItemGroup().
2443
2444 \sa destroyItemGroup(), QGraphicsItemGroup::addToGroup()
2445*/
2446QGraphicsItemGroup *QGraphicsScene::createItemGroup(const QList<QGraphicsItem *> &items)
2447{
2448 // Build a list of the first item's ancestors
2449 QList<QGraphicsItem *> ancestors;
2450 int n = 0;
2451 if (!items.isEmpty()) {
2452 QGraphicsItem *parent = items.at(n++);
2453 while ((parent = parent->parentItem()))
2454 ancestors.append(parent);
2455 }
2456
2457 // Find the common ancestor for all items
2458 QGraphicsItem *commonAncestor = 0;
2459 if (!ancestors.isEmpty()) {
2460 while (n < items.size()) {
2461 int commonIndex = -1;
2462 QGraphicsItem *parent = items.at(n++);
2463 do {
2464 int index = ancestors.indexOf(parent, qMax(0, commonIndex));
2465 if (index != -1) {
2466 commonIndex = index;
2467 break;
2468 }
2469 } while ((parent = parent->parentItem()));
2470
2471 if (commonIndex == -1) {
2472 commonAncestor = 0;
2473 break;
2474 }
2475
2476 commonAncestor = ancestors.at(commonIndex);
2477 }
2478 }
2479
2480 // Create a new group at that level
2481 QGraphicsItemGroup *group = new QGraphicsItemGroup(commonAncestor);
2482 if (!commonAncestor)
2483 addItem(group);
2484 foreach (QGraphicsItem *item, items)
2485 group->addToGroup(item);
2486 return group;
2487}
2488
2489/*!
2490 Reparents all items in \a group to \a group's parent item, then removes \a
2491 group from the scene, and finally deletes it. The items' positions and
2492 transformations are mapped from the group to the group's parent.
2493
2494 \sa createItemGroup(), QGraphicsItemGroup::removeFromGroup()
2495*/
2496void QGraphicsScene::destroyItemGroup(QGraphicsItemGroup *group)
2497{
2498 foreach (QGraphicsItem *item, group->children())
2499 group->removeFromGroup(item);
2500 removeItem(group);
2501 delete group;
2502}
2503
2504/*!
2505 Adds or moves the \a item and all its childen to this scene.
2506 This scene takes ownership of the \a item.
2507
2508 If the item is visible (i.e., QGraphicsItem::isVisible() returns
2509 true), QGraphicsScene will emit changed() once control goes back
2510 to the event loop.
2511
2512 If the item is already in a different scene, it will first be
2513 removed from its old scene, and then added to this scene as a
2514 top-level.
2515
2516 QGraphicsScene will send ItemSceneChange notifications to \a item
2517 while it is added to the scene. If item does not currently belong
2518 to a scene, only one notification is sent. If it does belong to
2519 scene already (i.e., it is moved to this scene), QGraphicsScene
2520 will send an addition notification as the item is removed from its
2521 previous scene.
2522
2523 If the item is a panel, the scene is active, and there is no
2524 active panel in the scene, then the item will be activated.
2525
2526 \sa removeItem(), addEllipse(), addLine(), addPath(), addPixmap(),
2527 addRect(), addText(), addWidget(), {QGraphicsItem#Sorting}{Sorting}
2528*/
2529void QGraphicsScene::addItem(QGraphicsItem *item)
2530{
2531 Q_D(QGraphicsScene);
2532 if (!item) {
2533 qWarning("QGraphicsScene::addItem: cannot add null item");
2534 return;
2535 }
2536 if (item->d_ptr->scene == this) {
2537 qWarning("QGraphicsScene::addItem: item has already been added to this scene");
2538 return;
2539 }
2540 // Remove this item from its existing scene
2541 if (QGraphicsScene *oldScene = item->d_ptr->scene)
2542 oldScene->removeItem(item);
2543
2544 // Notify the item that its scene is changing, and allow the item to
2545 // react.
2546 const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
2547 qVariantFromValue<QGraphicsScene *>(this)));
2548 QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant);
2549 if (targetScene != this) {
2550 if (targetScene && item->d_ptr->scene != targetScene)
2551 targetScene->addItem(item);
2552 return;
2553 }
2554
2555 // QDeclarativeItems do not rely on initial itemChanged message, as the componentComplete
2556 // function allows far more opportunity for delayed-construction optimization.
2557 if (!item->d_ptr->isDeclarativeItem) {
2558 if (d->unpolishedItems.isEmpty()) {
2559 QMetaMethod method = metaObject()->method(d->polishItemsIndex);
2560 method.invoke(this, Qt::QueuedConnection);
2561 }
2562 d->unpolishedItems.append(item);
2563 item->d_ptr->pendingPolish = true;
2564 }
2565
2566 // Detach this item from its parent if the parent's scene is different
2567 // from this scene.
2568 if (QGraphicsItem *itemParent = item->d_ptr->parent) {
2569 if (itemParent->d_ptr->scene != this)
2570 item->setParentItem(0);
2571 }
2572
2573 // Add the item to this scene
2574 item->d_func()->scene = targetScene;
2575
2576 // Add the item in the index
2577 d->index->addItem(item);
2578
2579 // Add to list of toplevels if this item is a toplevel.
2580 if (!item->d_ptr->parent)
2581 d->registerTopLevelItem(item);
2582
2583 // Add to list of items that require an update. We cannot assume that the
2584 // item is fully constructed, so calling item->update() can lead to a pure
2585 // virtual function call to boundingRect().
2586 d->markDirty(item);
2587 d->dirtyGrowingItemsBoundingRect = true;
2588
2589 // Disable selectionChanged() for individual items
2590 ++d->selectionChanging;
2591 int oldSelectedItemSize = d->selectedItems.size();
2592
2593 // Enable mouse tracking if the item accepts hover events or has a cursor set.
2594 if (d->allItemsIgnoreHoverEvents && d->itemAcceptsHoverEvents_helper(item)) {
2595 d->allItemsIgnoreHoverEvents = false;
2596 d->enableMouseTrackingOnViews();
2597 }
2598#ifndef QT_NO_CURSOR
2599 if (d->allItemsUseDefaultCursor && item->d_ptr->hasCursor) {
2600 d->allItemsUseDefaultCursor = false;
2601 if (d->allItemsIgnoreHoverEvents) // already enabled otherwise
2602 d->enableMouseTrackingOnViews();
2603 }
2604#endif //QT_NO_CURSOR
2605
2606 // Enable touch events if the item accepts touch events.
2607 if (d->allItemsIgnoreTouchEvents && item->d_ptr->acceptTouchEvents) {
2608 d->allItemsIgnoreTouchEvents = false;
2609 d->enableTouchEventsOnViews();
2610 }
2611
2612#ifndef QT_NO_GESTURES
2613 foreach (Qt::GestureType gesture, item->d_ptr->gestureContext.keys())
2614 d->grabGesture(item, gesture);
2615#endif
2616
2617 // Update selection lists
2618 if (item->isSelected())
2619 d->selectedItems << item;
2620 if (item->isWidget() && item->isVisible() && static_cast<QGraphicsWidget *>(item)->windowType() == Qt::Popup)
2621 d->addPopup(static_cast<QGraphicsWidget *>(item));
2622 if (item->isPanel() && item->isVisible() && item->panelModality() != QGraphicsItem::NonModal)
2623 d->enterModal(item);
2624
2625 // Update creation order focus chain. Make sure to leave the widget's
2626 // internal tab order intact.
2627 if (item->isWidget()) {
2628 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
2629 if (!d->tabFocusFirst) {
2630 // No first tab focus widget - make this the first tab focus
2631 // widget.
2632 d->tabFocusFirst = widget;
2633 } else if (!widget->parentWidget()) {
2634 // Adding a widget that is not part of a tab focus chain.
2635 QGraphicsWidget *last = d->tabFocusFirst->d_func()->focusPrev;
2636 QGraphicsWidget *lastNew = widget->d_func()->focusPrev;
2637 last->d_func()->focusNext = widget;
2638 widget->d_func()->focusPrev = last;
2639 d->tabFocusFirst->d_func()->focusPrev = lastNew;
2640 lastNew->d_func()->focusNext = d->tabFocusFirst;
2641 }
2642 }
2643
2644 // Add all children recursively
2645 item->d_ptr->ensureSortedChildren();
2646 for (int i = 0; i < item->d_ptr->children.size(); ++i)
2647 addItem(item->d_ptr->children.at(i));
2648
2649 // Resolve font and palette.
2650 item->d_ptr->resolveFont(d->font.resolve());
2651 item->d_ptr->resolvePalette(d->palette.resolve());
2652
2653
2654 // Reenable selectionChanged() for individual items
2655 --d->selectionChanging;
2656 if (!d->selectionChanging && d->selectedItems.size() != oldSelectedItemSize)
2657 emit selectionChanged();
2658
2659 // Deliver post-change notification
2660 item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
2661
2662 // Update explicit activation
2663 bool autoActivate = true;
2664 if (!d->childExplicitActivation && item->d_ptr->explicitActivate)
2665 d->childExplicitActivation = item->d_ptr->wantsActive ? 1 : 2;
2666 if (d->childExplicitActivation && item->isPanel()) {
2667 if (d->childExplicitActivation == 1)
2668 setActivePanel(item);
2669 else
2670 autoActivate = false;
2671 d->childExplicitActivation = 0;
2672 } else if (!item->d_ptr->parent) {
2673 d->childExplicitActivation = 0;
2674 }
2675
2676 // Auto-activate this item's panel if nothing else has been activated
2677 if (autoActivate) {
2678 if (!d->lastActivePanel && !d->activePanel && item->isPanel()) {
2679 if (isActive())
2680 setActivePanel(item);
2681 else
2682 d->lastActivePanel = item;
2683 }
2684 }
2685
2686 if (item->d_ptr->flags & QGraphicsItem::ItemSendsScenePositionChanges)
2687 d->registerScenePosItem(item);
2688
2689 // Ensure that newly added items that have subfocus set, gain
2690 // focus automatically if there isn't a focus item already.
2691 if (!d->focusItem && item != d->lastFocusItem && item->focusItem() == item)
2692 item->focusItem()->setFocus();
2693
2694 d->updateInputMethodSensitivityInViews();
2695}
2696
2697/*!
2698 Creates and adds an ellipse item to the scene, and returns the item
2699 pointer. The geometry of the ellipse is defined by \a rect, and its pen
2700 and brush are initialized to \a pen and \a brush.
2701
2702 Note that the item's geometry is provided in item coordinates, and its
2703 position is initialized to (0, 0).
2704
2705 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2706 QGraphicsScene will emit changed() once control goes back to the event
2707 loop.
2708
2709 \sa addLine(), addPath(), addPixmap(), addRect(), addText(), addItem(),
2710 addWidget()
2711*/
2712QGraphicsEllipseItem *QGraphicsScene::addEllipse(const QRectF &rect, const QPen &pen, const QBrush &brush)
2713{
2714 QGraphicsEllipseItem *item = new QGraphicsEllipseItem(rect);
2715 item->setPen(pen);
2716 item->setBrush(brush);
2717 addItem(item);
2718 return item;
2719}
2720
2721/*!
2722 \fn QGraphicsEllipseItem *QGraphicsScene::addEllipse(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
2723 \since 4.3
2724
2725 This convenience function is equivalent to calling addEllipse(QRectF(\a x,
2726 \a y, \a w, \a h), \a pen, \a brush).
2727*/
2728
2729/*!
2730 Creates and adds a line item to the scene, and returns the item
2731 pointer. The geometry of the line is defined by \a line, and its pen
2732 is initialized to \a pen.
2733
2734 Note that the item's geometry is provided in item coordinates, and its
2735 position is initialized to (0, 0).
2736
2737 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2738 QGraphicsScene will emit changed() once control goes back to the event
2739 loop.
2740
2741 \sa addEllipse(), addPath(), addPixmap(), addRect(), addText(), addItem(),
2742 addWidget()
2743*/
2744QGraphicsLineItem *QGraphicsScene::addLine(const QLineF &line, const QPen &pen)
2745{
2746 QGraphicsLineItem *item = new QGraphicsLineItem(line);
2747 item->setPen(pen);
2748 addItem(item);
2749 return item;
2750}
2751
2752/*!
2753 \fn QGraphicsLineItem *QGraphicsScene::addLine(qreal x1, qreal y1, qreal x2, qreal y2, const QPen &pen)
2754 \since 4.3
2755
2756 This convenience function is equivalent to calling addLine(QLineF(\a x1,
2757 \a y1, \a x2, \a y2), \a pen).
2758*/
2759
2760/*!
2761 Creates and adds a path item to the scene, and returns the item
2762 pointer. The geometry of the path is defined by \a path, and its pen and
2763 brush are initialized to \a pen and \a brush.
2764
2765 Note that the item's geometry is provided in item coordinates, and its
2766 position is initialized to (0, 0).
2767
2768 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2769 QGraphicsScene will emit changed() once control goes back to the event
2770 loop.
2771
2772 \sa addEllipse(), addLine(), addPixmap(), addRect(), addText(), addItem(),
2773 addWidget()
2774*/
2775QGraphicsPathItem *QGraphicsScene::addPath(const QPainterPath &path, const QPen &pen, const QBrush &brush)
2776{
2777 QGraphicsPathItem *item = new QGraphicsPathItem(path);
2778 item->setPen(pen);
2779 item->setBrush(brush);
2780 addItem(item);
2781 return item;
2782}
2783
2784/*!
2785 Creates and adds a pixmap item to the scene, and returns the item
2786 pointer. The pixmap is defined by \a pixmap.
2787
2788 Note that the item's geometry is provided in item coordinates, and its
2789 position is initialized to (0, 0).
2790
2791 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2792 QGraphicsScene will emit changed() once control goes back to the event
2793 loop.
2794
2795 \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
2796 addWidget()
2797*/
2798QGraphicsPixmapItem *QGraphicsScene::addPixmap(const QPixmap &pixmap)
2799{
2800 QGraphicsPixmapItem *item = new QGraphicsPixmapItem(pixmap);
2801 addItem(item);
2802 return item;
2803}
2804
2805/*!
2806 Creates and adds a polygon item to the scene, and returns the item
2807 pointer. The polygon is defined by \a polygon, and its pen and
2808 brush are initialized to \a pen and \a brush.
2809
2810 Note that the item's geometry is provided in item coordinates, and its
2811 position is initialized to (0, 0).
2812
2813 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2814 QGraphicsScene will emit changed() once control goes back to the event
2815 loop.
2816
2817 \sa addEllipse(), addLine(), addPath(), addRect(), addText(), addItem(),
2818 addWidget()
2819*/
2820QGraphicsPolygonItem *QGraphicsScene::addPolygon(const QPolygonF &polygon,
2821 const QPen &pen, const QBrush &brush)
2822{
2823 QGraphicsPolygonItem *item = new QGraphicsPolygonItem(polygon);
2824 item->setPen(pen);
2825 item->setBrush(brush);
2826 addItem(item);
2827 return item;
2828}
2829
2830/*!
2831 Creates and adds a rectangle item to the scene, and returns the item
2832 pointer. The geometry of the rectangle is defined by \a rect, and its pen
2833 and brush are initialized to \a pen and \a brush.
2834
2835 Note that the item's geometry is provided in item coordinates, and its
2836 position is initialized to (0, 0). For example, if a QRect(50, 50, 100,
2837 100) is added, its top-left corner will be at (50, 50) relative to the
2838 origin in the items coordinate system.
2839
2840 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2841 QGraphicsScene will emit changed() once control goes back to the event
2842 loop.
2843
2844 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addText(),
2845 addItem(), addWidget()
2846*/
2847QGraphicsRectItem *QGraphicsScene::addRect(const QRectF &rect, const QPen &pen, const QBrush &brush)
2848{
2849 QGraphicsRectItem *item = new QGraphicsRectItem(rect);
2850 item->setPen(pen);
2851 item->setBrush(brush);
2852 addItem(item);
2853 return item;
2854}
2855
2856/*!
2857 \fn QGraphicsRectItem *QGraphicsScene::addRect(qreal x, qreal y, qreal w, qreal h, const QPen &pen, const QBrush &brush)
2858 \since 4.3
2859
2860 This convenience function is equivalent to calling addRect(QRectF(\a x,
2861 \a y, \a w, \a h), \a pen, \a brush).
2862*/
2863
2864/*!
2865 Creates and adds a text item to the scene, and returns the item
2866 pointer. The text string is initialized to \a text, and its font
2867 is initialized to \a font.
2868
2869 The item's position is initialized to (0, 0).
2870
2871 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2872 QGraphicsScene will emit changed() once control goes back to the event
2873 loop.
2874
2875 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2876 addItem(), addWidget()
2877*/
2878QGraphicsTextItem *QGraphicsScene::addText(const QString &text, const QFont &font)
2879{
2880 QGraphicsTextItem *item = new QGraphicsTextItem(text);
2881 item->setFont(font);
2882 addItem(item);
2883 return item;
2884}
2885
2886/*!
2887 Creates and adds a QGraphicsSimpleTextItem to the scene, and returns the
2888 item pointer. The text string is initialized to \a text, and its font is
2889 initialized to \a font.
2890
2891 The item's position is initialized to (0, 0).
2892
2893 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2894 QGraphicsScene will emit changed() once control goes back to the event
2895 loop.
2896
2897 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2898 addItem(), addWidget()
2899*/
2900QGraphicsSimpleTextItem *QGraphicsScene::addSimpleText(const QString &text, const QFont &font)
2901{
2902 QGraphicsSimpleTextItem *item = new QGraphicsSimpleTextItem(text);
2903 item->setFont(font);
2904 addItem(item);
2905 return item;
2906}
2907
2908/*!
2909 Creates a new QGraphicsProxyWidget for \a widget, adds it to the scene,
2910 and returns a pointer to the proxy. \a wFlags set the default window flags
2911 for the embedding proxy widget.
2912
2913 The item's position is initialized to (0, 0).
2914
2915 If the item is visible (i.e., QGraphicsItem::isVisible() returns true),
2916 QGraphicsScene will emit changed() once control goes back to the event
2917 loop.
2918
2919 Note that widgets with the Qt::WA_PaintOnScreen widget attribute
2920 set and widgets that wrap an external application or controller
2921 are not supported. Examples are QGLWidget and QAxWidget.
2922
2923 \sa addEllipse(), addLine(), addPixmap(), addPixmap(), addRect(),
2924 addText(), addSimpleText(), addItem()
2925*/
2926QGraphicsProxyWidget *QGraphicsScene::addWidget(QWidget *widget, Qt::WindowFlags wFlags)
2927{
2928 QGraphicsProxyWidget *proxy = new QGraphicsProxyWidget(0, wFlags);
2929 proxy->setWidget(widget);
2930 addItem(proxy);
2931 return proxy;
2932}
2933
2934/*!
2935 Removes the item \a item and all its children from the scene. The
2936 ownership of \a item is passed on to the caller (i.e.,
2937 QGraphicsScene will no longer delete \a item when destroyed).
2938
2939 \sa addItem()
2940*/
2941void QGraphicsScene::removeItem(QGraphicsItem *item)
2942{
2943 // ### Refactoring: This function shares much functionality with _q_removeItemLater()
2944 Q_D(QGraphicsScene);
2945 if (!item) {
2946 qWarning("QGraphicsScene::removeItem: cannot remove 0-item");
2947 return;
2948 }
2949 if (item->scene() != this) {
2950 qWarning("QGraphicsScene::removeItem: item %p's scene (%p)"
2951 " is different from this scene (%p)",
2952 item, item->scene(), this);
2953 return;
2954 }
2955
2956 // Notify the item that it's scene is changing to 0, allowing the item to
2957 // react.
2958 const QVariant newSceneVariant(item->itemChange(QGraphicsItem::ItemSceneChange,
2959 qVariantFromValue<QGraphicsScene *>(0)));
2960 QGraphicsScene *targetScene = qVariantValue<QGraphicsScene *>(newSceneVariant);
2961 if (targetScene != 0 && targetScene != this) {
2962 targetScene->addItem(item);
2963 return;
2964 }
2965
2966 d->removeItemHelper(item);
2967
2968 // Deliver post-change notification
2969 item->itemChange(QGraphicsItem::ItemSceneHasChanged, newSceneVariant);
2970
2971 d->updateInputMethodSensitivityInViews();
2972}
2973
2974/*!
2975 When the scene is active, this functions returns the scene's current focus
2976 item, or 0 if no item currently has focus. When the scene is inactive, this
2977 functions returns the item that will gain input focus when the scene becomes
2978 active.
2979
2980 The focus item receives keyboard input when the scene receives a
2981 key event.
2982
2983 \sa setFocusItem(), QGraphicsItem::hasFocus(), isActive()
2984*/
2985QGraphicsItem *QGraphicsScene::focusItem() const
2986{
2987 Q_D(const QGraphicsScene);
2988 return isActive() ? d->focusItem : d->lastFocusItem;
2989}
2990
2991/*!
2992 Sets the scene's focus item to \a item, with the focus reason \a
2993 focusReason, after removing focus from any previous item that may have had
2994 focus.
2995
2996 If \a item is 0, or if it either does not accept focus (i.e., it does not
2997 have the QGraphicsItem::ItemIsFocusable flag enabled), or is not visible
2998 or not enabled, this function only removes focus from any previous
2999 focusitem.
3000
3001 If item is not 0, and the scene does not currently have focus (i.e.,
3002 hasFocus() returns false), this function will call setFocus()
3003 automatically.
3004
3005 \sa focusItem(), hasFocus(), setFocus()
3006*/
3007void QGraphicsScene::setFocusItem(QGraphicsItem *item, Qt::FocusReason focusReason)
3008{
3009 Q_D(QGraphicsScene);
3010 if (item)
3011 item->setFocus(focusReason);
3012 else
3013 d->setFocusItemHelper(item, focusReason);
3014}
3015
3016/*!
3017 Returns true if the scene has focus; otherwise returns false. If the scene
3018 has focus, it will will forward key events from QKeyEvent to any item that
3019 has focus.
3020
3021 \sa setFocus(), setFocusItem()
3022*/
3023bool QGraphicsScene::hasFocus() const
3024{
3025 Q_D(const QGraphicsScene);
3026 return d->hasFocus;
3027}
3028
3029/*!
3030 Sets focus on the scene by sending a QFocusEvent to the scene, passing \a
3031 focusReason as the reason. If the scene regains focus after having
3032 previously lost it while an item had focus, the last focus item will
3033 receive focus with \a focusReason as the reason.
3034
3035 If the scene already has focus, this function does nothing.
3036
3037 \sa hasFocus(), clearFocus(), setFocusItem()
3038*/
3039void QGraphicsScene::setFocus(Qt::FocusReason focusReason)
3040{
3041 Q_D(QGraphicsScene);
3042 if (d->hasFocus || !isActive())
3043 return;
3044 QFocusEvent event(QEvent::FocusIn, focusReason);
3045 QCoreApplication::sendEvent(this, &event);
3046}
3047
3048/*!
3049 Clears focus from the scene. If any item has focus when this function is
3050 called, it will lose focus, and regain focus again once the scene regains
3051 focus.
3052
3053 A scene that does not have focus ignores key events.
3054
3055 \sa hasFocus(), setFocus(), setFocusItem()
3056*/
3057void QGraphicsScene::clearFocus()
3058{
3059 Q_D(QGraphicsScene);
3060 if (d->hasFocus) {
3061 d->hasFocus = false;
3062 setFocusItem(0, Qt::OtherFocusReason);
3063 }
3064}
3065
3066/*!
3067 \property QGraphicsScene::stickyFocus
3068 \brief whether clicking into the scene background will clear focus
3069
3070 \since 4.6
3071
3072 In a QGraphicsScene with stickyFocus set to true, focus will remain
3073 unchanged when the user clicks into the scene background or on an item
3074 that does not accept focus. Otherwise, focus will be cleared.
3075
3076 By default, this property is false.
3077
3078 Focus changes in response to a mouse press. You can reimplement
3079 mousePressEvent() in a subclass of QGraphicsScene to toggle this property
3080 based on where the user has clicked.
3081
3082 \sa clearFocus(), setFocusItem()
3083*/
3084void QGraphicsScene::setStickyFocus(bool enabled)
3085{
3086 Q_D(QGraphicsScene);
3087 d->stickyFocus = enabled;
3088}
3089bool QGraphicsScene::stickyFocus() const
3090{
3091 Q_D(const QGraphicsScene);
3092 return d->stickyFocus;
3093}
3094
3095/*!
3096 Returns the current mouse grabber item, or 0 if no item is currently
3097 grabbing the mouse. The mouse grabber item is the item that receives all
3098 mouse events sent to the scene.
3099
3100 An item becomes a mouse grabber when it receives and accepts a
3101 mouse press event, and it stays the mouse grabber until either of
3102 the following events occur:
3103
3104 \list
3105 \o If the item receives a mouse release event when there are no other
3106 buttons pressed, it loses the mouse grab.
3107 \o If the item becomes invisible (i.e., someone calls \c {item->setVisible(false))},
3108 or if it becomes disabled (i.e., someone calls \c {item->setEnabled(false))},
3109 it loses the mouse grab.
3110 \o If the item is removed from the scene, it loses the mouse grab.
3111 \endlist
3112
3113 If the item loses its mouse grab, the scene will ignore all mouse events
3114 until a new item grabs the mouse (i.e., until a new item receives a mouse
3115 press event).
3116*/
3117QGraphicsItem *QGraphicsScene::mouseGrabberItem() const
3118{
3119 Q_D(const QGraphicsScene);
3120 return !d->mouseGrabberItems.isEmpty() ? d->mouseGrabberItems.last() : 0;
3121}
3122
3123/*!
3124 \property QGraphicsScene::backgroundBrush
3125 \brief the background brush of the scene.
3126
3127 Set this property to changes the scene's background to a different color,
3128 gradient or texture. The default background brush is Qt::NoBrush. The
3129 background is drawn before (behind) the items.
3130
3131 Example:
3132
3133 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 3
3134
3135 QGraphicsScene::render() calls drawBackground() to draw the scene
3136 background. For more detailed control over how the background is drawn,
3137 you can reimplement drawBackground() in a subclass of QGraphicsScene.
3138*/
3139QBrush QGraphicsScene::backgroundBrush() const
3140{
3141 Q_D(const QGraphicsScene);
3142 return d->backgroundBrush;
3143}
3144void QGraphicsScene::setBackgroundBrush(const QBrush &brush)
3145{
3146 Q_D(QGraphicsScene);
3147 d->backgroundBrush = brush;
3148 foreach (QGraphicsView *view, d->views) {
3149 view->resetCachedContent();
3150 view->viewport()->update();
3151 }
3152 update();
3153}
3154
3155/*!
3156 \property QGraphicsScene::foregroundBrush
3157 \brief the foreground brush of the scene.
3158
3159 Change this property to set the scene's foreground to a different
3160 color, gradient or texture.
3161
3162 The foreground is drawn after (on top of) the items. The default
3163 foreground brush is Qt::NoBrush ( i.e. the foreground is not
3164 drawn).
3165
3166 Example:
3167
3168 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 4
3169
3170 QGraphicsScene::render() calls drawForeground() to draw the scene
3171 foreground. For more detailed control over how the foreground is
3172 drawn, you can reimplement the drawForeground() function in a
3173 QGraphicsScene subclass.
3174*/
3175QBrush QGraphicsScene::foregroundBrush() const
3176{
3177 Q_D(const QGraphicsScene);
3178 return d->foregroundBrush;
3179}
3180void QGraphicsScene::setForegroundBrush(const QBrush &brush)
3181{
3182 Q_D(QGraphicsScene);
3183 d->foregroundBrush = brush;
3184 foreach (QGraphicsView *view, views())
3185 view->viewport()->update();
3186 update();
3187}
3188
3189/*!
3190 This method is used by input methods to query a set of properties of
3191 the scene to be able to support complex input method operations as support
3192 for surrounding text and reconversions.
3193
3194 The \a query parameter specifies which property is queried.
3195
3196 \sa QWidget::inputMethodQuery()
3197*/
3198QVariant QGraphicsScene::inputMethodQuery(Qt::InputMethodQuery query) const
3199{
3200 Q_D(const QGraphicsScene);
3201 if (!d->focusItem || !(d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
3202 return QVariant();
3203 const QTransform matrix = d->focusItem->sceneTransform();
3204 QVariant value = d->focusItem->inputMethodQuery(query);
3205 if (value.type() == QVariant::RectF)
3206 value = matrix.mapRect(value.toRectF());
3207 else if (value.type() == QVariant::PointF)
3208 value = matrix.map(value.toPointF());
3209 else if (value.type() == QVariant::Rect)
3210 value = matrix.mapRect(value.toRect());
3211 else if (value.type() == QVariant::Point)
3212 value = matrix.map(value.toPoint());
3213 return value;
3214}
3215
3216/*!
3217 \fn void QGraphicsScene::update(const QRectF &rect)
3218 Schedules a redraw of the area \a rect on the scene.
3219
3220 \sa sceneRect(), changed()
3221*/
3222void QGraphicsScene::update(const QRectF &rect)
3223{
3224 Q_D(QGraphicsScene);
3225 if (d->updateAll || (rect.isEmpty() && !rect.isNull()))
3226 return;
3227
3228 // Check if anyone's connected; if not, we can send updates directly to
3229 // the views. Otherwise or if there are no views, use old behavior.
3230 bool directUpdates = !(d->isSignalConnected(d->changedSignalIndex)) && !d->views.isEmpty();
3231 if (rect.isNull()) {
3232 d->updateAll = true;
3233 d->updatedRects.clear();
3234 if (directUpdates) {
3235 // Update all views.
3236 for (int i = 0; i < d->views.size(); ++i)
3237 d->views.at(i)->d_func()->fullUpdatePending = true;
3238 }
3239 } else {
3240 if (directUpdates) {
3241 // Update all views.
3242 for (int i = 0; i < d->views.size(); ++i) {
3243 QGraphicsView *view = d->views.at(i);
3244 if (view->isTransformed())
3245 view->d_func()->updateRectF(view->viewportTransform().mapRect(rect));
3246 else
3247 view->d_func()->updateRectF(rect);
3248 }
3249 } else {
3250 d->updatedRects << rect;
3251 }
3252 }
3253
3254 if (!d->calledEmitUpdated) {
3255 d->calledEmitUpdated = true;
3256 QMetaObject::invokeMethod(this, "_q_emitUpdated", Qt::QueuedConnection);
3257 }
3258}
3259
3260/*!
3261 \fn void QGraphicsScene::update(qreal x, qreal y, qreal w, qreal h)
3262 \overload
3263 \since 4.3
3264
3265 This function is equivalent to calling update(QRectF(\a x, \a y, \a w,
3266 \a h));
3267*/
3268
3269/*!
3270 Invalidates and schedules a redraw of the \a layers in \a rect on the
3271 scene. Any cached content in \a layers is unconditionally invalidated and
3272 redrawn.
3273
3274 You can use this function overload to notify QGraphicsScene of changes to
3275 the background or the foreground of the scene. This function is commonly
3276 used for scenes with tile-based backgrounds to notify changes when
3277 QGraphicsView has enabled
3278 \l{QGraphicsView::CacheBackground}{CacheBackground}.
3279
3280 Example:
3281
3282 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsscene.cpp 5
3283
3284 Note that QGraphicsView currently supports background caching only (see
3285 QGraphicsView::CacheBackground). This function is equivalent to calling
3286 update() if any layer but BackgroundLayer is passed.
3287
3288 \sa QGraphicsView::resetCachedContent()
3289*/
3290void QGraphicsScene::invalidate(const QRectF &rect, SceneLayers layers)
3291{
3292 foreach (QGraphicsView *view, views())
3293 view->invalidateScene(rect, layers);
3294 update(rect);
3295}
3296
3297/*!
3298 \fn void QGraphicsScene::invalidate(qreal x, qreal y, qreal w, qreal h, SceneLayers layers)
3299 \overload
3300 \since 4.3
3301
3302 This convenience function is equivalent to calling invalidate(QRectF(\a x, \a
3303 y, \a w, \a h), \a layers);
3304*/
3305
3306/*!
3307 Returns a list of all the views that display this scene.
3308
3309 \sa QGraphicsView::scene()
3310*/
3311QList <QGraphicsView *> QGraphicsScene::views() const
3312{
3313 Q_D(const QGraphicsScene);
3314 return d->views;
3315}
3316
3317/*!
3318 This slot \e advances the scene by one step, by calling
3319 QGraphicsItem::advance() for all items on the scene. This is done in two
3320 phases: in the first phase, all items are notified that the scene is about
3321 to change, and in the second phase all items are notified that they can
3322 move. In the first phase, QGraphicsItem::advance() is called passing a
3323 value of 0 as an argument, and 1 is passed in the second phase.
3324
3325 \sa QGraphicsItem::advance(), QGraphicsItemAnimation, QTimeLine
3326*/
3327void QGraphicsScene::advance()
3328{
3329 for (int i = 0; i < 2; ++i) {
3330 foreach (QGraphicsItem *item, items())
3331 item->advance(i);
3332 }
3333}
3334
3335/*!
3336 Processes the event \a event, and dispatches it to the respective
3337 event handlers.
3338
3339 In addition to calling the convenience event handlers, this
3340 function is responsible for converting mouse move events to hover
3341 events for when there is no mouse grabber item. Hover events are
3342 delivered directly to items; there is no convenience function for
3343 them.
3344
3345 Unlike QWidget, QGraphicsScene does not have the convenience functions
3346 \l{QWidget::}{enterEvent()} and \l{QWidget::}{leaveEvent()}. Use this
3347 function to obtain those events instead.
3348
3349 \sa contextMenuEvent(), keyPressEvent(), keyReleaseEvent(),
3350 mousePressEvent(), mouseMoveEvent(), mouseReleaseEvent(),
3351 mouseDoubleClickEvent(), focusInEvent(), focusOutEvent()
3352*/
3353bool QGraphicsScene::event(QEvent *event)
3354{
3355 Q_D(QGraphicsScene);
3356
3357 switch (event->type()) {
3358 case QEvent::GraphicsSceneMousePress:
3359 case QEvent::GraphicsSceneMouseMove:
3360 case QEvent::GraphicsSceneMouseRelease:
3361 case QEvent::GraphicsSceneMouseDoubleClick:
3362 case QEvent::GraphicsSceneHoverEnter:
3363 case QEvent::GraphicsSceneHoverLeave:
3364 case QEvent::GraphicsSceneHoverMove:
3365 case QEvent::TouchBegin:
3366 case QEvent::TouchUpdate:
3367 case QEvent::TouchEnd:
3368 // Reset the under-mouse list to ensure that this event gets fresh
3369 // item-under-mouse data. Be careful about this list; if people delete
3370 // items from inside event handlers, this list can quickly end up
3371 // having stale pointers in it. We need to clear it before dispatching
3372 // events that use it.
3373 // ### this should only be cleared if we received a new mouse move event,
3374 // which relies on us fixing the replay mechanism in QGraphicsView.
3375 d->cachedItemsUnderMouse.clear();
3376 default:
3377 break;
3378 }
3379
3380 switch (event->type()) {
3381 case QEvent::GraphicsSceneDragEnter:
3382 dragEnterEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3383 break;
3384 case QEvent::GraphicsSceneDragMove:
3385 dragMoveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3386 break;
3387 case QEvent::GraphicsSceneDragLeave:
3388 dragLeaveEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3389 break;
3390 case QEvent::GraphicsSceneDrop:
3391 dropEvent(static_cast<QGraphicsSceneDragDropEvent *>(event));
3392 break;
3393 case QEvent::GraphicsSceneContextMenu:
3394 contextMenuEvent(static_cast<QGraphicsSceneContextMenuEvent *>(event));
3395 break;
3396 case QEvent::KeyPress:
3397 if (!d->focusItem) {
3398 QKeyEvent *k = static_cast<QKeyEvent *>(event);
3399 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
3400 if (!(k->modifiers() & (Qt::ControlModifier | Qt::AltModifier))) { //### Add MetaModifier?
3401 bool res = false;
3402 if (k->key() == Qt::Key_Backtab
3403 || (k->key() == Qt::Key_Tab && (k->modifiers() & Qt::ShiftModifier))) {
3404 res = focusNextPrevChild(false);
3405 } else if (k->key() == Qt::Key_Tab) {
3406 res = focusNextPrevChild(true);
3407 }
3408 if (!res)
3409 event->ignore();
3410 return true;
3411 }
3412 }
3413 }
3414 keyPressEvent(static_cast<QKeyEvent *>(event));
3415 break;
3416 case QEvent::KeyRelease:
3417 keyReleaseEvent(static_cast<QKeyEvent *>(event));
3418 break;
3419 case QEvent::ShortcutOverride: {
3420 QGraphicsItem *parent = focusItem();
3421 while (parent) {
3422 d->sendEvent(parent, event);
3423 if (event->isAccepted())
3424 return true;
3425 parent = parent->parentItem();
3426 }
3427 }
3428 return false;
3429 case QEvent::GraphicsSceneMouseMove:
3430 {
3431 QGraphicsSceneMouseEvent *mouseEvent = static_cast<QGraphicsSceneMouseEvent *>(event);
3432 d->lastSceneMousePos = mouseEvent->scenePos();
3433 mouseMoveEvent(mouseEvent);
3434 break;
3435 }
3436 case QEvent::GraphicsSceneMousePress:
3437 mousePressEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3438 break;
3439 case QEvent::GraphicsSceneMouseRelease:
3440 mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3441 break;
3442 case QEvent::GraphicsSceneMouseDoubleClick:
3443 mouseDoubleClickEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
3444 break;
3445 case QEvent::GraphicsSceneWheel:
3446 wheelEvent(static_cast<QGraphicsSceneWheelEvent *>(event));
3447 break;
3448 case QEvent::FocusIn:
3449 focusInEvent(static_cast<QFocusEvent *>(event));
3450 break;
3451 case QEvent::FocusOut:
3452 focusOutEvent(static_cast<QFocusEvent *>(event));
3453 break;
3454 case QEvent::GraphicsSceneHoverEnter:
3455 case QEvent::GraphicsSceneHoverLeave:
3456 case QEvent::GraphicsSceneHoverMove:
3457 {
3458 QGraphicsSceneHoverEvent *hoverEvent = static_cast<QGraphicsSceneHoverEvent *>(event);
3459 d->lastSceneMousePos = hoverEvent->scenePos();
3460 d->dispatchHoverEvent(hoverEvent);
3461 break;
3462 }
3463 case QEvent::Leave:
3464 d->leaveScene();
3465 break;
3466 case QEvent::GraphicsSceneHelp:
3467 helpEvent(static_cast<QGraphicsSceneHelpEvent *>(event));
3468 break;
3469 case QEvent::InputMethod:
3470 inputMethodEvent(static_cast<QInputMethodEvent *>(event));
3471 break;
3472 case QEvent::WindowActivate:
3473 if (!d->activationRefCount++) {
3474 if (d->lastActivePanel) {
3475 // Activate the last panel.
3476 d->setActivePanelHelper(d->lastActivePanel, true);
3477 } else if (d->tabFocusFirst && d->tabFocusFirst->isPanel()) {
3478 // Activate the panel of the first item in the tab focus
3479 // chain.
3480 d->setActivePanelHelper(d->tabFocusFirst, true);
3481 } else {
3482 // Activate all toplevel items.
3483 QEvent event(QEvent::WindowActivate);
3484 foreach (QGraphicsItem *item, items()) {
3485 if (item->isVisible() && !item->isPanel() && !item->parentItem())
3486 sendEvent(item, &event);
3487 }
3488 }
3489 }
3490 break;
3491 case QEvent::WindowDeactivate:
3492 if (!--d->activationRefCount) {
3493 if (d->activePanel) {
3494 // Deactivate the active panel (but keep it so we can
3495 // reactivate it later).
3496 QGraphicsItem *lastActivePanel = d->activePanel;
3497 d->setActivePanelHelper(0, true);
3498 d->lastActivePanel = lastActivePanel;
3499 } else {
3500 // Activate all toplevel items.
3501 QEvent event(QEvent::WindowDeactivate);
3502 foreach (QGraphicsItem *item, items()) {
3503 if (item->isVisible() && !item->isPanel() && !item->parentItem())
3504 sendEvent(item, &event);
3505 }
3506 }
3507 }
3508 break;
3509 case QEvent::ApplicationFontChange: {
3510 // Resolve the existing scene font.
3511 d->resolveFont();
3512 break;
3513 }
3514 case QEvent::FontChange:
3515 // Update the entire scene when the font changes.
3516 update();
3517 break;
3518 case QEvent::ApplicationPaletteChange: {
3519 // Resolve the existing scene palette.
3520 d->resolvePalette();
3521 break;
3522 }
3523 case QEvent::PaletteChange:
3524 // Update the entire scene when the palette changes.
3525 update();
3526 break;
3527 case QEvent::StyleChange:
3528 // Reresolve all widgets' styles. Update all top-level widgets'
3529 // geometries that do not have an explicit style set.
3530 update();
3531 break;
3532 case QEvent::TouchBegin:
3533 case QEvent::TouchUpdate:
3534 case QEvent::TouchEnd:
3535 d->touchEventHandler(static_cast<QTouchEvent *>(event));
3536 break;
3537#ifndef QT_NO_GESTURES
3538 case QEvent::Gesture:
3539 case QEvent::GestureOverride:
3540 d->gestureEventHandler(static_cast<QGestureEvent *>(event));
3541 break;
3542#endif // QT_NO_GESTURES
3543 default:
3544 return QObject::event(event);
3545 }
3546 return true;
3547}
3548
3549/*!
3550 \reimp
3551
3552 QGraphicsScene filters QApplication's events to detect palette and font
3553 changes.
3554*/
3555bool QGraphicsScene::eventFilter(QObject *watched, QEvent *event)
3556{
3557 if (watched != qApp)
3558 return false;
3559
3560 switch (event->type()) {
3561 case QEvent::ApplicationPaletteChange:
3562 QApplication::postEvent(this, new QEvent(QEvent::ApplicationPaletteChange));
3563 break;
3564 case QEvent::ApplicationFontChange:
3565 QApplication::postEvent(this, new QEvent(QEvent::ApplicationFontChange));
3566 break;
3567 default:
3568 break;
3569 }
3570 return false;
3571}
3572
3573/*!
3574 This event handler, for event \a contextMenuEvent, can be reimplemented in
3575 a subclass to receive context menu events. The default implementation
3576 forwards the event to the topmost item that accepts context menu events at
3577 the position of the event. If no items accept context menu events at this
3578 position, the event is ignored.
3579
3580 \sa QGraphicsItem::contextMenuEvent()
3581*/
3582void QGraphicsScene::contextMenuEvent(QGraphicsSceneContextMenuEvent *contextMenuEvent)
3583{
3584 Q_D(QGraphicsScene);
3585 // Ignore by default.
3586 contextMenuEvent->ignore();
3587
3588 // Send the event to all items at this position until one item accepts the
3589 // event.
3590 foreach (QGraphicsItem *item, d->itemsAtPosition(contextMenuEvent->screenPos(),
3591 contextMenuEvent->scenePos(),
3592 contextMenuEvent->widget())) {
3593 contextMenuEvent->setPos(item->d_ptr->genericMapFromScene(contextMenuEvent->scenePos(),
3594 contextMenuEvent->widget()));
3595 contextMenuEvent->accept();
3596 if (!d->sendEvent(item, contextMenuEvent))
3597 break;
3598
3599 if (contextMenuEvent->isAccepted())
3600 break;
3601 }
3602}
3603
3604/*!
3605 This event handler, for event \a event, can be reimplemented in a subclass
3606 to receive drag enter events for the scene.
3607
3608 The default implementation accepts the event and prepares the scene to
3609 accept drag move events.
3610
3611 \sa QGraphicsItem::dragEnterEvent(), dragMoveEvent(), dragLeaveEvent(),
3612 dropEvent()
3613*/
3614void QGraphicsScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
3615{
3616 Q_D(QGraphicsScene);
3617 d->dragDropItem = 0;
3618 d->lastDropAction = Qt::IgnoreAction;
3619 event->accept();
3620}
3621
3622/*!
3623 This event handler, for event \a event, can be reimplemented in a subclass
3624 to receive drag move events for the scene.
3625
3626 \sa QGraphicsItem::dragMoveEvent(), dragEnterEvent(), dragLeaveEvent(),
3627 dropEvent()
3628*/
3629void QGraphicsScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
3630{
3631 Q_D(QGraphicsScene);
3632 event->ignore();
3633
3634 if (!d->mouseGrabberItems.isEmpty()) {
3635 // Mouse grabbers that start drag events lose the mouse grab.
3636 d->clearMouseGrabber();
3637 d->mouseGrabberButtonDownPos.clear();
3638 d->mouseGrabberButtonDownScenePos.clear();
3639 d->mouseGrabberButtonDownScreenPos.clear();
3640 }
3641
3642 bool eventDelivered = false;
3643
3644 // Find the topmost enabled items under the cursor. They are all
3645 // candidates for accepting drag & drop events.
3646 foreach (QGraphicsItem *item, d->itemsAtPosition(event->screenPos(),
3647 event->scenePos(),
3648 event->widget())) {
3649 if (!item->isEnabled() || !item->acceptDrops())
3650 continue;
3651
3652 if (item != d->dragDropItem) {
3653 // Enter the new drag drop item. If it accepts the event, we send
3654 // the leave to the parent item.
3655 QGraphicsSceneDragDropEvent dragEnter(QEvent::GraphicsSceneDragEnter);
3656 d->cloneDragDropEvent(&dragEnter, event);
3657 dragEnter.setDropAction(event->proposedAction());
3658 d->sendDragDropEvent(item, &dragEnter);
3659 event->setAccepted(dragEnter.isAccepted());
3660 event->setDropAction(dragEnter.dropAction());
3661 if (!event->isAccepted()) {
3662 // Propagate to the item under
3663 continue;
3664 }
3665
3666 d->lastDropAction = event->dropAction();
3667
3668 if (d->dragDropItem) {
3669 // Leave the last drag drop item. A perfect implementation
3670 // would set the position of this event to the point where
3671 // this event and the last event intersect with the item's
3672 // shape, but that's not easy to do. :-)
3673 QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
3674 d->cloneDragDropEvent(&dragLeave, event);
3675 d->sendDragDropEvent(d->dragDropItem, &dragLeave);
3676 }
3677
3678 // We've got a new drag & drop item
3679 d->dragDropItem = item;
3680 }
3681
3682 // Send the move event.
3683 event->setDropAction(d->lastDropAction);
3684 event->accept();
3685 d->sendDragDropEvent(item, event);
3686 if (event->isAccepted())
3687 d->lastDropAction = event->dropAction();
3688 eventDelivered = true;
3689 break;
3690 }
3691
3692 if (!eventDelivered) {
3693 if (d->dragDropItem) {
3694 // Leave the last drag drop item
3695 QGraphicsSceneDragDropEvent dragLeave(QEvent::GraphicsSceneDragLeave);
3696 d->cloneDragDropEvent(&dragLeave, event);
3697 d->sendDragDropEvent(d->dragDropItem, &dragLeave);
3698 d->dragDropItem = 0;
3699 }
3700 // Propagate
3701 event->setDropAction(Qt::IgnoreAction);
3702 }
3703}
3704
3705/*!
3706 This event handler, for event \a event, can be reimplemented in a subclass
3707 to receive drag leave events for the scene.
3708
3709 \sa QGraphicsItem::dragLeaveEvent(), dragEnterEvent(), dragMoveEvent(),
3710 dropEvent()
3711*/
3712void QGraphicsScene::dragLeaveEvent(QGraphicsSceneDragDropEvent *event)
3713{
3714 Q_D(QGraphicsScene);
3715 if (d->dragDropItem) {
3716 // Leave the last drag drop item
3717 d->sendDragDropEvent(d->dragDropItem, event);
3718 d->dragDropItem = 0;
3719 }
3720}
3721
3722/*!
3723 This event handler, for event \a event, can be reimplemented in a subclass
3724 to receive drop events for the scene.
3725
3726 \sa QGraphicsItem::dropEvent(), dragEnterEvent(), dragMoveEvent(),
3727 dragLeaveEvent()
3728*/
3729void QGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event)
3730{
3731 Q_UNUSED(event);
3732 Q_D(QGraphicsScene);
3733 if (d->dragDropItem) {
3734 // Drop on the last drag drop item
3735 d->sendDragDropEvent(d->dragDropItem, event);
3736 d->dragDropItem = 0;
3737 }
3738}
3739
3740/*!
3741 This event handler, for event \a focusEvent, can be reimplemented in a
3742 subclass to receive focus in events.
3743
3744 The default implementation sets focus on the scene, and then on the last
3745 focus item.
3746
3747 \sa QGraphicsItem::focusOutEvent()
3748*/
3749void QGraphicsScene::focusInEvent(QFocusEvent *focusEvent)
3750{
3751 Q_D(QGraphicsScene);
3752
3753 d->hasFocus = true;
3754 switch (focusEvent->reason()) {
3755 case Qt::TabFocusReason:
3756 if (!focusNextPrevChild(true))
3757 focusEvent->ignore();
3758 break;
3759 case Qt::BacktabFocusReason:
3760 if (!focusNextPrevChild(false))
3761 focusEvent->ignore();
3762 break;
3763 default:
3764 if (d->lastFocusItem) {
3765 // Set focus on the last focus item
3766 setFocusItem(d->lastFocusItem, focusEvent->reason());
3767 }
3768 break;
3769 }
3770}
3771
3772/*!
3773 This event handler, for event \a focusEvent, can be reimplemented in a
3774 subclass to receive focus out events.
3775
3776 The default implementation removes focus from any focus item, then removes
3777 focus from the scene.
3778
3779 \sa QGraphicsItem::focusInEvent()
3780*/
3781void QGraphicsScene::focusOutEvent(QFocusEvent *focusEvent)
3782{
3783 Q_D(QGraphicsScene);
3784 d->hasFocus = false;
3785 setFocusItem(0, focusEvent->reason());
3786
3787 // Remove all popups when the scene loses focus.
3788 if (!d->popupWidgets.isEmpty())
3789 d->removePopup(d->popupWidgets.first());
3790}
3791
3792/*!
3793 This event handler, for event \a helpEvent, can be
3794 reimplemented in a subclass to receive help events. The events
3795 are of type QEvent::ToolTip, which are created when a tooltip is
3796 requested.
3797
3798 The default implementation shows the tooltip of the topmost
3799 item, i.e., the item with the highest z-value, at the mouse
3800 cursor position. If no item has a tooltip set, this function
3801 does nothing.
3802
3803 \sa QGraphicsItem::toolTip(), QGraphicsSceneHelpEvent
3804*/
3805void QGraphicsScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
3806{
3807#ifdef QT_NO_TOOLTIP
3808 Q_UNUSED(helpEvent);
3809#else
3810 // Find the first item that does tooltips
3811 Q_D(QGraphicsScene);
3812 QList<QGraphicsItem *> itemsAtPos = d->itemsAtPosition(helpEvent->screenPos(),
3813 helpEvent->scenePos(),
3814 helpEvent->widget());
3815 QGraphicsItem *toolTipItem = 0;
3816 for (int i = 0; i < itemsAtPos.size(); ++i) {
3817 QGraphicsItem *tmp = itemsAtPos.at(i);
3818 if (tmp->d_func()->isProxyWidget()) {
3819 // if the item is a proxy widget, the event is forwarded to it
3820 sendEvent(tmp, helpEvent);
3821 if (helpEvent->isAccepted())
3822 return;
3823 }
3824 if (!tmp->toolTip().isEmpty()) {
3825 toolTipItem = tmp;
3826 break;
3827 }
3828 }
3829
3830 // Show or hide the tooltip
3831 QString text;
3832 QPoint point;
3833 if (toolTipItem && !toolTipItem->toolTip().isEmpty()) {
3834 text = toolTipItem->toolTip();
3835 point = helpEvent->screenPos();
3836 }
3837 QToolTip::showText(point, text, helpEvent->widget());
3838 helpEvent->setAccepted(!text.isEmpty());
3839#endif
3840}
3841
3842bool QGraphicsScenePrivate::itemAcceptsHoverEvents_helper(const QGraphicsItem *item) const
3843{
3844 return (item->d_ptr->acceptsHover
3845 || (item->d_ptr->isWidget
3846 && static_cast<const QGraphicsWidget *>(item)->d_func()->hasDecoration()))
3847 && !item->isBlockedByModalPanel();
3848}
3849
3850/*!
3851 This event handler, for event \a hoverEvent, can be reimplemented in a
3852 subclass to receive hover enter events. The default implementation
3853 forwards the event to the topmost item that accepts hover events at the
3854 scene position from the event.
3855
3856 \sa QGraphicsItem::hoverEvent(), QGraphicsItem::setAcceptHoverEvents()
3857*/
3858bool QGraphicsScenePrivate::dispatchHoverEvent(QGraphicsSceneHoverEvent *hoverEvent)
3859{
3860 if (allItemsIgnoreHoverEvents)
3861 return false;
3862
3863 // Find the first item that accepts hover events, reusing earlier
3864 // calculated data is possible.
3865 if (cachedItemsUnderMouse.isEmpty()) {
3866 cachedItemsUnderMouse = itemsAtPosition(hoverEvent->screenPos(),
3867 hoverEvent->scenePos(),
3868 hoverEvent->widget());
3869 }
3870
3871 QGraphicsItem *item = 0;
3872 for (int i = 0; i < cachedItemsUnderMouse.size(); ++i) {
3873 QGraphicsItem *tmp = cachedItemsUnderMouse.at(i);
3874 if (itemAcceptsHoverEvents_helper(tmp)) {
3875 item = tmp;
3876 break;
3877 }
3878 }
3879
3880 // Find the common ancestor item for the new topmost hoverItem and the
3881 // last item in the hoverItem list.
3882 QGraphicsItem *commonAncestorItem = (item && !hoverItems.isEmpty()) ? item->commonAncestorItem(hoverItems.last()) : 0;
3883 while (commonAncestorItem && !itemAcceptsHoverEvents_helper(commonAncestorItem))
3884 commonAncestorItem = commonAncestorItem->parentItem();
3885 if (commonAncestorItem && commonAncestorItem->panel() != item->panel()) {
3886 // The common ancestor isn't in the same panel as the two hovered
3887 // items.
3888 commonAncestorItem = 0;
3889 }
3890
3891 // Check if the common ancestor item is known.
3892 int index = commonAncestorItem ? hoverItems.indexOf(commonAncestorItem) : -1;
3893 // Send hover leaves to any existing hovered children of the common
3894 // ancestor item.
3895 for (int i = hoverItems.size() - 1; i > index; --i) {
3896 QGraphicsItem *lastItem = hoverItems.takeLast();
3897 if (itemAcceptsHoverEvents_helper(lastItem))
3898 sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, hoverEvent);
3899 }
3900
3901 // Item is a child of a known item. Generate enter events for the
3902 // missing links.
3903 QList<QGraphicsItem *> parents;
3904 QGraphicsItem *parent = item;
3905 while (parent && parent != commonAncestorItem) {
3906 parents.prepend(parent);
3907 if (parent->isPanel()) {
3908 // Stop at the panel - we don't deliver beyond this point.
3909 break;
3910 }
3911 parent = parent->parentItem();
3912 }
3913 for (int i = 0; i < parents.size(); ++i) {
3914 parent = parents.at(i);
3915 hoverItems << parent;
3916 if (itemAcceptsHoverEvents_helper(parent))
3917 sendHoverEvent(QEvent::GraphicsSceneHoverEnter, parent, hoverEvent);
3918 }
3919
3920 // Generate a move event for the item itself
3921 if (item
3922 && !hoverItems.isEmpty()
3923 && item == hoverItems.last()) {
3924 sendHoverEvent(QEvent::GraphicsSceneHoverMove, item, hoverEvent);
3925 return true;
3926 }
3927 return false;
3928}
3929
3930/*!
3931 \internal
3932
3933 Handles all actions necessary to clean up the scene when the mouse leaves
3934 the view.
3935*/
3936void QGraphicsScenePrivate::leaveScene()
3937{
3938 Q_Q(QGraphicsScene);
3939#ifndef QT_NO_TOOLTIP
3940 QToolTip::hideText();
3941#endif
3942 // Send HoverLeave events to all existing hover items, topmost first.
3943 QGraphicsView *senderWidget = qobject_cast<QGraphicsView *>(q->sender());
3944 QGraphicsSceneHoverEvent hoverEvent;
3945 hoverEvent.setWidget(senderWidget);
3946
3947 if (senderWidget) {
3948 QPoint cursorPos = QCursor::pos();
3949 hoverEvent.setScenePos(senderWidget->mapToScene(senderWidget->mapFromGlobal(cursorPos)));
3950 hoverEvent.setLastScenePos(hoverEvent.scenePos());
3951 hoverEvent.setScreenPos(cursorPos);
3952 hoverEvent.setLastScreenPos(hoverEvent.screenPos());
3953 }
3954
3955 while (!hoverItems.isEmpty()) {
3956 QGraphicsItem *lastItem = hoverItems.takeLast();
3957 if (itemAcceptsHoverEvents_helper(lastItem))
3958 sendHoverEvent(QEvent::GraphicsSceneHoverLeave, lastItem, &hoverEvent);
3959 }
3960}
3961
3962/*!
3963 This event handler, for event \a keyEvent, can be reimplemented in a
3964 subclass to receive keypress events. The default implementation forwards
3965 the event to current focus item.
3966
3967 \sa QGraphicsItem::keyPressEvent(), focusItem()
3968*/
3969void QGraphicsScene::keyPressEvent(QKeyEvent *keyEvent)
3970{
3971 // ### Merge this function with keyReleaseEvent; they are identical
3972 // ### (except this comment).
3973 Q_D(QGraphicsScene);
3974 QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
3975 if (!item)
3976 item = focusItem();
3977 if (item) {
3978 QGraphicsItem *p = item;
3979 do {
3980 // Accept the event by default
3981 keyEvent->accept();
3982 // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
3983 // is filtered out, stop propagating it.
3984 if (p->isBlockedByModalPanel())
3985 break;
3986 if (!d->sendEvent(p, keyEvent))
3987 break;
3988 } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
3989 } else {
3990 keyEvent->ignore();
3991 }
3992}
3993
3994/*!
3995 This event handler, for event \a keyEvent, can be reimplemented in a
3996 subclass to receive key release events. The default implementation
3997 forwards the event to current focus item.
3998
3999 \sa QGraphicsItem::keyReleaseEvent(), focusItem()
4000*/
4001void QGraphicsScene::keyReleaseEvent(QKeyEvent *keyEvent)
4002{
4003 // ### Merge this function with keyPressEvent; they are identical (except
4004 // ### this comment).
4005 Q_D(QGraphicsScene);
4006 QGraphicsItem *item = !d->keyboardGrabberItems.isEmpty() ? d->keyboardGrabberItems.last() : 0;
4007 if (!item)
4008 item = focusItem();
4009 if (item) {
4010 QGraphicsItem *p = item;
4011 do {
4012 // Accept the event by default
4013 keyEvent->accept();
4014 // Send it; QGraphicsItem::keyPressEvent ignores it. If the event
4015 // is filtered out, stop propagating it.
4016 if (p->isBlockedByModalPanel())
4017 break;
4018 if (!d->sendEvent(p, keyEvent))
4019 break;
4020 } while (!keyEvent->isAccepted() && !p->isPanel() && (p = p->parentItem()));
4021 } else {
4022 keyEvent->ignore();
4023 }
4024}
4025
4026/*!
4027 This event handler, for event \a mouseEvent, can be reimplemented
4028 in a subclass to receive mouse press events for the scene.
4029
4030 The default implementation depends on the state of the scene. If
4031 there is a mouse grabber item, then the event is sent to the mouse
4032 grabber. Otherwise, it is forwarded to the topmost item that
4033 accepts mouse events at the scene position from the event, and
4034 that item promptly becomes the mouse grabber item.
4035
4036 If there is no item at the given position on the scene, the
4037 selection area is reset, any focus item loses its input focus, and
4038 the event is then ignored.
4039
4040 \sa QGraphicsItem::mousePressEvent(),
4041 QGraphicsItem::setAcceptedMouseButtons()
4042*/
4043void QGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
4044{
4045 Q_D(QGraphicsScene);
4046 if (d->mouseGrabberItems.isEmpty()) {
4047 // Dispatch hover events
4048 QGraphicsSceneHoverEvent hover;
4049 _q_hoverFromMouseEvent(&hover, mouseEvent);
4050 d->dispatchHoverEvent(&hover);
4051 }
4052
4053 d->mousePressEventHandler(mouseEvent);
4054}
4055
4056/*!
4057 This event handler, for event \a mouseEvent, can be reimplemented
4058 in a subclass to receive mouse move events for the scene.
4059
4060 The default implementation depends on the mouse grabber state. If there is
4061 a mouse grabber item, the event is sent to the mouse grabber. If there
4062 are any items that accept hover events at the current position, the event
4063 is translated into a hover event and accepted; otherwise it's ignored.
4064
4065 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseReleaseEvent(),
4066 QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
4067*/
4068void QGraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
4069{
4070 Q_D(QGraphicsScene);
4071 if (d->mouseGrabberItems.isEmpty()) {
4072 if (mouseEvent->buttons())
4073 return;
4074 QGraphicsSceneHoverEvent hover;
4075 _q_hoverFromMouseEvent(&hover, mouseEvent);
4076 mouseEvent->setAccepted(d->dispatchHoverEvent(&hover));
4077 return;
4078 }
4079
4080 // Forward the event to the mouse grabber
4081 d->sendMouseEvent(mouseEvent);
4082 mouseEvent->accept();
4083}
4084
4085/*!
4086 This event handler, for event \a mouseEvent, can be reimplemented
4087 in a subclass to receive mouse release events for the scene.
4088
4089 The default implementation depends on the mouse grabber state. If
4090 there is no mouse grabber, the event is ignored. Otherwise, if
4091 there is a mouse grabber item, the event is sent to the mouse
4092 grabber. If this mouse release represents the last pressed button
4093 on the mouse, the mouse grabber item then loses the mouse grab.
4094
4095 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
4096 QGraphicsItem::mouseDoubleClickEvent(), QGraphicsItem::setAcceptedMouseButtons()
4097*/
4098void QGraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
4099{
4100 Q_D(QGraphicsScene);
4101 if (d->mouseGrabberItems.isEmpty()) {
4102 mouseEvent->ignore();
4103 return;
4104 }
4105
4106 // Forward the event to the mouse grabber
4107 d->sendMouseEvent(mouseEvent);
4108 mouseEvent->accept();
4109
4110 // Reset the mouse grabber when the last mouse button has been released.
4111 if (!mouseEvent->buttons()) {
4112 if (!d->mouseGrabberItems.isEmpty()) {
4113 d->lastMouseGrabberItem = d->mouseGrabberItems.last();
4114 if (d->lastMouseGrabberItemHasImplicitMouseGrab)
4115 d->mouseGrabberItems.last()->ungrabMouse();
4116 } else {
4117 d->lastMouseGrabberItem = 0;
4118 }
4119
4120 // Generate a hoverevent
4121 QGraphicsSceneHoverEvent hoverEvent;
4122 _q_hoverFromMouseEvent(&hoverEvent, mouseEvent);
4123 d->dispatchHoverEvent(&hoverEvent);
4124 }
4125}
4126
4127/*!
4128 This event handler, for event \a mouseEvent, can be reimplemented
4129 in a subclass to receive mouse doubleclick events for the scene.
4130
4131 If someone doubleclicks on the scene, the scene will first receive
4132 a mouse press event, followed by a release event (i.e., a click),
4133 then a doubleclick event, and finally a release event. If the
4134 doubleclick event is delivered to a different item than the one
4135 that received the first press and release, it will be delivered as
4136 a press event. However, tripleclick events are not delivered as
4137 doubleclick events in this case.
4138
4139 The default implementation is similar to mousePressEvent().
4140
4141 \sa QGraphicsItem::mousePressEvent(), QGraphicsItem::mouseMoveEvent(),
4142 QGraphicsItem::mouseReleaseEvent(), QGraphicsItem::setAcceptedMouseButtons()
4143*/
4144void QGraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *mouseEvent)
4145{
4146 Q_D(QGraphicsScene);
4147 d->mousePressEventHandler(mouseEvent);
4148}
4149
4150/*!
4151 This event handler, for event \a wheelEvent, can be reimplemented in a
4152 subclass to receive mouse wheel events for the scene.
4153
4154 By default, the event is delivered to the topmost visible item under the
4155 cursor. If ignored, the event propagates to the item beneath, and again
4156 until the event is accepted, or it reaches the scene. If no items accept
4157 the event, it is ignored.
4158
4159 \sa QGraphicsItem::wheelEvent()
4160*/
4161void QGraphicsScene::wheelEvent(QGraphicsSceneWheelEvent *wheelEvent)
4162{
4163 Q_D(QGraphicsScene);
4164 QList<QGraphicsItem *> wheelCandidates = d->itemsAtPosition(wheelEvent->screenPos(),
4165 wheelEvent->scenePos(),
4166 wheelEvent->widget());
4167
4168#ifdef Q_WS_MAC
4169 // On Mac, ignore the event if the first item under the mouse is not the last opened
4170 // popup (or one of its descendant)
4171 if (!d->popupWidgets.isEmpty() && !wheelCandidates.isEmpty() && wheelCandidates.first() != d->popupWidgets.back() && !d->popupWidgets.back()->isAncestorOf(wheelCandidates.first())) {
4172 wheelEvent->accept();
4173 return;
4174 }
4175#else
4176 // Find the first popup under the mouse (including the popup's descendants) starting from the last.
4177 // Remove all popups after the one found, or all or them if no popup is under the mouse.
4178 // Then continue with the event.
4179 QList<QGraphicsWidget *>::const_iterator iter = d->popupWidgets.end();
4180 while (--iter >= d->popupWidgets.begin() && !wheelCandidates.isEmpty()) {
4181 if (wheelCandidates.first() == *iter || (*iter)->isAncestorOf(wheelCandidates.first()))
4182 break;
4183 d->removePopup(*iter);
4184 }
4185#endif
4186
4187 bool hasSetFocus = false;
4188 foreach (QGraphicsItem *item, wheelCandidates) {
4189 if (!hasSetFocus && item->isEnabled()
4190 && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
4191 if (item->isWidget() && static_cast<QGraphicsWidget *>(item)->focusPolicy() == Qt::WheelFocus) {
4192 hasSetFocus = true;
4193 if (item != focusItem())
4194 setFocusItem(item, Qt::MouseFocusReason);
4195 }
4196 }
4197
4198 wheelEvent->setPos(item->d_ptr->genericMapFromScene(wheelEvent->scenePos(),
4199 wheelEvent->widget()));
4200 wheelEvent->accept();
4201 bool isPanel = item->isPanel();
4202 d->sendEvent(item, wheelEvent);
4203 if (isPanel || wheelEvent->isAccepted())
4204 break;
4205 }
4206}
4207
4208/*!
4209 This event handler, for event \a event, can be reimplemented in a
4210 subclass to receive input method events for the scene.
4211
4212 The default implementation forwards the event to the focusItem().
4213 If no item currently has focus or the current focus item does not
4214 accept input methods, this function does nothing.
4215
4216 \sa QGraphicsItem::inputMethodEvent()
4217*/
4218void QGraphicsScene::inputMethodEvent(QInputMethodEvent *event)
4219{
4220 Q_D(QGraphicsScene);
4221 if (d->focusItem && (d->focusItem->flags() & QGraphicsItem::ItemAcceptsInputMethod))
4222 d->sendEvent(d->focusItem, event);
4223}
4224
4225/*!
4226 Draws the background of the scene using \a painter, before any items and
4227 the foreground are drawn. Reimplement this function to provide a custom
4228 background for the scene.
4229
4230 All painting is done in \e scene coordinates. The \a rect
4231 parameter is the exposed rectangle.
4232
4233 If all you want is to define a color, texture, or gradient for the
4234 background, you can call setBackgroundBrush() instead.
4235
4236 \sa drawForeground(), drawItems()
4237*/
4238void QGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
4239{
4240 Q_D(QGraphicsScene);
4241
4242 if (d->backgroundBrush.style() != Qt::NoBrush) {
4243 if (d->painterStateProtection)
4244 painter->save();
4245 painter->setBrushOrigin(0, 0);
4246 painter->fillRect(rect, backgroundBrush());
4247 if (d->painterStateProtection)
4248 painter->restore();
4249 }
4250}
4251
4252/*!
4253 Draws the foreground of the scene using \a painter, after the background
4254 and all items have been drawn. Reimplement this function to provide a
4255 custom foreground for the scene.
4256
4257 All painting is done in \e scene coordinates. The \a rect
4258 parameter is the exposed rectangle.
4259
4260 If all you want is to define a color, texture or gradient for the
4261 foreground, you can call setForegroundBrush() instead.
4262
4263 \sa drawBackground(), drawItems()
4264*/
4265void QGraphicsScene::drawForeground(QPainter *painter, const QRectF &rect)
4266{
4267 Q_D(QGraphicsScene);
4268
4269 if (d->foregroundBrush.style() != Qt::NoBrush) {
4270 if (d->painterStateProtection)
4271 painter->save();
4272 painter->setBrushOrigin(0, 0);
4273 painter->fillRect(rect, foregroundBrush());
4274 if (d->painterStateProtection)
4275 painter->restore();
4276 }
4277}
4278
4279static void _q_paintItem(QGraphicsItem *item, QPainter *painter,
4280 const QStyleOptionGraphicsItem *option, QWidget *widget,
4281 bool useWindowOpacity, bool painterStateProtection)
4282{
4283 if (!item->isWidget()) {
4284 item->paint(painter, option, widget);
4285 return;
4286 }
4287 QGraphicsWidget *widgetItem = static_cast<QGraphicsWidget *>(item);
4288 QGraphicsProxyWidget *proxy = qobject_cast<QGraphicsProxyWidget *>(widgetItem);
4289 const qreal windowOpacity = (proxy && proxy->widget() && useWindowOpacity)
4290 ? proxy->widget()->windowOpacity() : 1.0;
4291 const qreal oldPainterOpacity = painter->opacity();
4292
4293 if (qFuzzyIsNull(windowOpacity))
4294 return;
4295 // Set new painter opacity.
4296 if (windowOpacity < 1.0)
4297 painter->setOpacity(oldPainterOpacity * windowOpacity);
4298
4299 // set layoutdirection on the painter
4300 Qt::LayoutDirection oldLayoutDirection = painter->layoutDirection();
4301 painter->setLayoutDirection(widgetItem->layoutDirection());
4302
4303 if (widgetItem->isWindow() && widgetItem->windowType() != Qt::Popup && widgetItem->windowType() != Qt::ToolTip
4304 && !(widgetItem->windowFlags() & Qt::FramelessWindowHint)) {
4305 if (painterStateProtection)
4306 painter->save();
4307 widgetItem->paintWindowFrame(painter, option, widget);
4308 if (painterStateProtection)
4309 painter->restore();
4310 } else if (widgetItem->autoFillBackground()) {
4311 painter->fillRect(option->exposedRect, widgetItem->palette().window());
4312 }
4313
4314 widgetItem->paint(painter, option, widget);
4315
4316 // Restore layoutdirection on the painter.
4317 painter->setLayoutDirection(oldLayoutDirection);
4318 // Restore painter opacity.
4319 if (windowOpacity < 1.0)
4320 painter->setOpacity(oldPainterOpacity);
4321}
4322
4323static void _q_paintIntoCache(QPixmap *pix, QGraphicsItem *item, const QRegion &pixmapExposed,
4324 const QTransform &itemToPixmap, QPainter::RenderHints renderHints,
4325 const QStyleOptionGraphicsItem *option, bool painterStateProtection)
4326{
4327 QPixmap subPix;
4328 QPainter pixmapPainter;
4329 QRect br = pixmapExposed.boundingRect();
4330
4331 // Don't use subpixmap if we get a full update.
4332 if (pixmapExposed.isEmpty() || (pixmapExposed.rectCount() == 1 && br.contains(pix->rect()))) {
4333 pix->fill(Qt::transparent);
4334 pixmapPainter.begin(pix);
4335 } else {
4336 subPix = QPixmap(br.size());
4337 subPix.fill(Qt::transparent);
4338 pixmapPainter.begin(&subPix);
4339 pixmapPainter.translate(-br.topLeft());
4340 if (!pixmapExposed.isEmpty()) {
4341 // Applied to subPix; paint is adjusted to the coordinate space is
4342 // correct.
4343 pixmapPainter.setClipRegion(pixmapExposed);
4344 }
4345 }
4346
4347 pixmapPainter.setRenderHints(pixmapPainter.renderHints(), false);
4348 pixmapPainter.setRenderHints(renderHints, true);
4349 pixmapPainter.setWorldTransform(itemToPixmap, true);
4350
4351 // Render.
4352 _q_paintItem(item, &pixmapPainter, option, 0, false, painterStateProtection);
4353 pixmapPainter.end();
4354
4355 if (!subPix.isNull()) {
4356 // Blit the subpixmap into the main pixmap.
4357 pixmapPainter.begin(pix);
4358 pixmapPainter.setCompositionMode(QPainter::CompositionMode_Source);
4359 pixmapPainter.setClipRegion(pixmapExposed);
4360 pixmapPainter.drawPixmap(br.topLeft(), subPix);
4361 pixmapPainter.end();
4362 }
4363}
4364
4365// Copied from qpaintengine_vg.cpp
4366// Returns true for 90, 180, and 270 degree rotations.
4367static inline bool transformIsSimple(const QTransform& transform)
4368{
4369 QTransform::TransformationType type = transform.type();
4370 if (type == QTransform::TxNone || type == QTransform::TxTranslate) {
4371 return true;
4372 } else if (type == QTransform::TxScale) {
4373 // Check for 0 and 180 degree rotations.
4374 // (0 might happen after 4 rotations of 90 degrees).
4375 qreal m11 = transform.m11();
4376 qreal m12 = transform.m12();
4377 qreal m21 = transform.m21();
4378 qreal m22 = transform.m22();
4379 if (m12 == 0.0f && m21 == 0.0f) {
4380 if (m11 == 1.0f && m22 == 1.0f)
4381 return true; // 0 degrees
4382 else if (m11 == -1.0f && m22 == -1.0f)
4383 return true; // 180 degrees.
4384 if(m11 == 1.0f && m22 == -1.0f)
4385 return true; // 0 degrees inverted y.
4386 else if(m11 == -1.0f && m22 == 1.0f)
4387 return true; // 180 degrees inverted y.
4388 }
4389 } else if (type == QTransform::TxRotate) {
4390 // Check for 90, and 270 degree rotations.
4391 qreal m11 = transform.m11();
4392 qreal m12 = transform.m12();
4393 qreal m21 = transform.m21();
4394 qreal m22 = transform.m22();
4395 if (m11 == 0.0f && m22 == 0.0f) {
4396 if (m12 == 1.0f && m21 == -1.0f)
4397 return true; // 90 degrees.
4398 else if (m12 == -1.0f && m21 == 1.0f)
4399 return true; // 270 degrees.
4400 else if (m12 == -1.0f && m21 == -1.0f)
4401 return true; // 90 degrees inverted y.
4402 else if (m12 == 1.0f && m21 == 1.0f)
4403 return true; // 270 degrees inverted y.
4404 }
4405 }
4406 return false;
4407}
4408
4409/*!
4410 \internal
4411
4412 Draws items directly, or using cache.
4413*/
4414void QGraphicsScenePrivate::drawItemHelper(QGraphicsItem *item, QPainter *painter,
4415 const QStyleOptionGraphicsItem *option, QWidget *widget,
4416 bool painterStateProtection)
4417{
4418 QGraphicsItemPrivate *itemd = item->d_ptr.data();
4419 QGraphicsItem::CacheMode cacheMode = QGraphicsItem::CacheMode(itemd->cacheMode);
4420
4421 // Render directly, using no cache.
4422 if (cacheMode == QGraphicsItem::NoCache
4423#ifdef Q_WS_X11
4424 || !X11->use_xrender
4425#endif
4426 ) {
4427 _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget, true, painterStateProtection);
4428 return;
4429 }
4430
4431 const qreal oldPainterOpacity = painter->opacity();
4432 qreal newPainterOpacity = oldPainterOpacity;
4433 QGraphicsProxyWidget *proxy = item->isWidget() ? qobject_cast<QGraphicsProxyWidget *>(static_cast<QGraphicsWidget *>(item)) : 0;
4434 if (proxy && proxy->widget()) {
4435 const qreal windowOpacity = proxy->widget()->windowOpacity();
4436 if (windowOpacity < 1.0)
4437 newPainterOpacity *= windowOpacity;
4438 }
4439
4440 // Item's (local) bounding rect
4441 QRectF brect = item->boundingRect();
4442 QRectF adjustedBrect(brect);
4443 _q_adjustRect(&adjustedBrect);
4444 if (adjustedBrect.isEmpty())
4445 return;
4446
4447 // Fetch the off-screen transparent buffer and exposed area info.
4448 QPixmapCache::Key pixmapKey;
4449 QPixmap pix;
4450 bool pixmapFound;
4451 QGraphicsItemCache *itemCache = itemd->extraItemCache();
4452 if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
4453 pixmapKey = itemCache->key;
4454 } else {
4455 pixmapKey = itemCache->deviceData.value(widget).key;
4456 }
4457
4458 // Find pixmap in cache.
4459 pixmapFound = QPixmapCache::find(pixmapKey, &pix);
4460
4461 // Render using item coordinate cache mode.
4462 if (cacheMode == QGraphicsItem::ItemCoordinateCache) {
4463 QSize pixmapSize;
4464 bool fixedCacheSize = false;
4465 QRect br = brect.toAlignedRect();
4466 if ((fixedCacheSize = itemCache->fixedSize.isValid())) {
4467 pixmapSize = itemCache->fixedSize;
4468 } else {
4469 pixmapSize = br.size();
4470 }
4471
4472 // Create or recreate the pixmap.
4473 int adjust = itemCache->fixedSize.isValid() ? 0 : 2;
4474 QSize adjustSize(adjust*2, adjust*2);
4475 br.adjust(-adjust, -adjust, adjust, adjust);
4476 if (pix.isNull() || (!fixedCacheSize && (pixmapSize + adjustSize) != pix.size())) {
4477 pix = QPixmap(pixmapSize + adjustSize);
4478 itemCache->boundingRect = br;
4479 itemCache->exposed.clear();
4480 itemCache->allExposed = true;
4481 } else if (itemCache->boundingRect != br) {
4482 itemCache->boundingRect = br;
4483 itemCache->exposed.clear();
4484 itemCache->allExposed = true;
4485 }
4486
4487 // Redraw any newly exposed areas.
4488 if (itemCache->allExposed || !itemCache->exposed.isEmpty()) {
4489
4490 //We know that we will modify the pixmap, removing it from the cache
4491 //will detach the one we have and avoid a deep copy
4492 if (pixmapFound)
4493 QPixmapCache::remove(pixmapKey);
4494
4495 // Fit the item's bounding rect into the pixmap's coordinates.
4496 QTransform itemToPixmap;
4497 if (fixedCacheSize) {
4498 const QPointF scale(pixmapSize.width() / brect.width(), pixmapSize.height() / brect.height());
4499 itemToPixmap.scale(scale.x(), scale.y());
4500 }
4501 itemToPixmap.translate(-br.x(), -br.y());
4502
4503 // Generate the item's exposedRect and map its list of expose
4504 // rects to device coordinates.
4505 styleOptionTmp = *option;
4506 QRegion pixmapExposed;
4507 QRectF exposedRect;
4508 if (!itemCache->allExposed) {
4509 for (int i = 0; i < itemCache->exposed.size(); ++i) {
4510 QRectF r = itemCache->exposed.at(i);
4511 exposedRect |= r;
4512 pixmapExposed += itemToPixmap.mapRect(r).toAlignedRect();
4513 }
4514 } else {
4515 exposedRect = brect;
4516 }
4517 styleOptionTmp.exposedRect = exposedRect;
4518
4519 // Render.
4520 _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
4521 &styleOptionTmp, painterStateProtection);
4522
4523 // insert this pixmap into the cache.
4524 itemCache->key = QPixmapCache::insert(pix);
4525
4526 // Reset expose data.
4527 itemCache->allExposed = false;
4528 itemCache->exposed.clear();
4529 }
4530
4531 // Redraw the exposed area using the transformed painter. Depending on
4532 // the hardware, this may be a server-side operation, or an expensive
4533 // qpixmap-image-transform-pixmap roundtrip.
4534 if (newPainterOpacity != oldPainterOpacity) {
4535 painter->setOpacity(newPainterOpacity);
4536 painter->drawPixmap(br.topLeft(), pix);
4537 painter->setOpacity(oldPainterOpacity);
4538 } else {
4539 painter->drawPixmap(br.topLeft(), pix);
4540 }
4541 return;
4542 }
4543
4544 // Render using device coordinate cache mode.
4545 if (cacheMode == QGraphicsItem::DeviceCoordinateCache) {
4546 // Find the item's bounds in device coordinates.
4547 QRectF deviceBounds = painter->worldTransform().mapRect(brect);
4548 QRect deviceRect = deviceBounds.toRect().adjusted(-1, -1, 1, 1);
4549 if (deviceRect.isEmpty())
4550 return;
4551 QRect viewRect = widget ? widget->rect() : QRect();
4552 if (widget && !viewRect.intersects(deviceRect))
4553 return;
4554
4555 // Resort to direct rendering if the device rect exceeds the
4556 // (optional) maximum bounds. (QGraphicsSvgItem uses this).
4557 QSize maximumCacheSize =
4558 itemd->extra(QGraphicsItemPrivate::ExtraMaxDeviceCoordCacheSize).toSize();
4559 if (!maximumCacheSize.isEmpty()
4560 && (deviceRect.width() > maximumCacheSize.width()
4561 || deviceRect.height() > maximumCacheSize.height())) {
4562 _q_paintItem(static_cast<QGraphicsWidget *>(item), painter, option, widget,
4563 oldPainterOpacity != newPainterOpacity, painterStateProtection);
4564 return;
4565 }
4566
4567 // Create or reuse offscreen pixmap, possibly scroll/blit from the old one.
4568 // If the world transform is rotated we always recreate the cache to avoid
4569 // wrong blending.
4570 bool pixModified = false;
4571 QGraphicsItemCache::DeviceData *deviceData = &itemCache->deviceData[widget];
4572 bool invertable = true;
4573 QTransform diff = deviceData->lastTransform.inverted(&invertable);
4574 if (invertable)
4575 diff *= painter->worldTransform();
4576 deviceData->lastTransform = painter->worldTransform();
4577 bool allowPartialCacheExposure = false;
4578 bool simpleTransform = invertable && diff.type() <= QTransform::TxTranslate
4579 && transformIsSimple(painter->worldTransform());
4580 if (!simpleTransform) {
4581 pixModified = true;
4582 itemCache->allExposed = true;
4583 itemCache->exposed.clear();
4584 deviceData->cacheIndent = QPoint();
4585 pix = QPixmap();
4586 } else if (!viewRect.isNull()) {
4587 allowPartialCacheExposure = deviceData->cacheIndent != QPoint();
4588 }
4589
4590 // Allow partial cache exposure if the device rect isn't fully contained and
4591 // deviceRect is 20% taller or wider than the viewRect.
4592 if (!allowPartialCacheExposure && !viewRect.isNull() && !viewRect.contains(deviceRect)) {
4593 allowPartialCacheExposure = (viewRect.width() * 1.2 < deviceRect.width())
4594 || (viewRect.height() * 1.2 < deviceRect.height());
4595 }
4596
4597 QRegion scrollExposure;
4598 if (allowPartialCacheExposure) {
4599 // Part of pixmap is drawn. Either device contains viewrect (big
4600 // item covers whole screen) or parts of device are outside the
4601 // viewport. In either case the device rect must be the intersect
4602 // between the two.
4603 int dx = deviceRect.left() < viewRect.left() ? viewRect.left() - deviceRect.left() : 0;
4604 int dy = deviceRect.top() < viewRect.top() ? viewRect.top() - deviceRect.top() : 0;
4605 QPoint newCacheIndent(dx, dy);
4606 deviceRect &= viewRect;
4607
4608 if (pix.isNull()) {
4609 deviceData->cacheIndent = QPoint();
4610 itemCache->allExposed = true;
4611 itemCache->exposed.clear();
4612 pixModified = true;
4613 }
4614
4615 // Copy / "scroll" the old pixmap onto the new ole and calculate
4616 // scrolled exposure.
4617 if (newCacheIndent != deviceData->cacheIndent || deviceRect.size() != pix.size()) {
4618 QPoint diff = newCacheIndent - deviceData->cacheIndent;
4619 QPixmap newPix(deviceRect.size());
4620 // ### Investigate removing this fill (test with Plasma and
4621 // graphicssystem raster).
4622 newPix.fill(Qt::transparent);
4623 if (!pix.isNull()) {
4624 QPainter newPixPainter(&newPix);
4625 newPixPainter.drawPixmap(-diff, pix);
4626 newPixPainter.end();
4627 }
4628 QRegion exposed;
4629 exposed += newPix.rect();
4630 if (!pix.isNull())
4631 exposed -= QRect(-diff, pix.size());
4632 scrollExposure = exposed;
4633
4634 pix = newPix;
4635 pixModified = true;
4636 }
4637 deviceData->cacheIndent = newCacheIndent;
4638 } else {
4639 // Full pixmap is drawn.
4640 deviceData->cacheIndent = QPoint();
4641
4642 // Auto-adjust the pixmap size.
4643 if (deviceRect.size() != pix.size()) {
4644 // exposed needs to cover the whole pixmap
4645 pix = QPixmap(deviceRect.size());
4646 pixModified = true;
4647 itemCache->allExposed = true;
4648 itemCache->exposed.clear();
4649 }
4650 }
4651
4652 // Check for newly invalidated areas.
4653 if (itemCache->allExposed || !itemCache->exposed.isEmpty() || !scrollExposure.isEmpty()) {
4654 //We know that we will modify the pixmap, removing it from the cache
4655 //will detach the one we have and avoid a deep copy
4656 if (pixmapFound)
4657 QPixmapCache::remove(pixmapKey);
4658
4659 // Construct an item-to-pixmap transform.
4660 QPointF p = deviceRect.topLeft();
4661 QTransform itemToPixmap = painter->worldTransform();
4662 if (!p.isNull())
4663 itemToPixmap *= QTransform::fromTranslate(-p.x(), -p.y());
4664
4665 // Map the item's logical expose to pixmap coordinates.
4666 QRegion pixmapExposed = scrollExposure;
4667 if (!itemCache->allExposed) {
4668 const QVector<QRectF> &exposed = itemCache->exposed;
4669 for (int i = 0; i < exposed.size(); ++i)
4670 pixmapExposed += itemToPixmap.mapRect(exposed.at(i)).toRect().adjusted(-1, -1, 1, 1);
4671 }
4672
4673 // Calculate the style option's exposedRect.
4674 QRectF br;
4675 if (itemCache->allExposed) {
4676 br = item->boundingRect();
4677 } else {
4678 const QVector<QRectF> &exposed = itemCache->exposed;
4679 for (int i = 0; i < exposed.size(); ++i)
4680 br |= exposed.at(i);
4681 QTransform pixmapToItem = itemToPixmap.inverted();
4682 foreach (QRect r, scrollExposure.rects())
4683 br |= pixmapToItem.mapRect(r);
4684 }
4685 styleOptionTmp = *option;
4686 styleOptionTmp.exposedRect = br.adjusted(-1, -1, 1, 1);
4687
4688 // Render the exposed areas.
4689 _q_paintIntoCache(&pix, item, pixmapExposed, itemToPixmap, painter->renderHints(),
4690 &styleOptionTmp, painterStateProtection);
4691
4692 // Reset expose data.
4693 pixModified = true;
4694 itemCache->allExposed = false;
4695 itemCache->exposed.clear();
4696 }
4697
4698 if (pixModified) {
4699 // Insert this pixmap into the cache.
4700 deviceData->key = QPixmapCache::insert(pix);
4701 }
4702
4703 // Redraw the exposed area using an untransformed painter. This
4704 // effectively becomes a bitblit that does not transform the cache.
4705 QTransform restoreTransform = painter->worldTransform();
4706 painter->setWorldTransform(QTransform());
4707 if (newPainterOpacity != oldPainterOpacity) {
4708 painter->setOpacity(newPainterOpacity);
4709 painter->drawPixmap(deviceRect.topLeft(), pix);
4710 painter->setOpacity(oldPainterOpacity);
4711 } else {
4712 painter->drawPixmap(deviceRect.topLeft(), pix);
4713 }
4714 painter->setWorldTransform(restoreTransform);
4715 return;
4716 }
4717}
4718
4719void QGraphicsScenePrivate::drawItems(QPainter *painter, const QTransform *const viewTransform,
4720 QRegion *exposedRegion, QWidget *widget)
4721{
4722 // Make sure we don't have unpolished items before we draw.
4723 if (!unpolishedItems.isEmpty())
4724 _q_polishItems();
4725
4726 updateAll = false;
4727 QRectF exposedSceneRect;
4728 if (exposedRegion && indexMethod != QGraphicsScene::NoIndex) {
4729 exposedSceneRect = exposedRegion->boundingRect().adjusted(-1, -1, 1, 1);
4730 if (viewTransform)
4731 exposedSceneRect = viewTransform->inverted().mapRect(exposedSceneRect);
4732 }
4733 const QList<QGraphicsItem *> tli = index->estimateTopLevelItems(exposedSceneRect, Qt::AscendingOrder);
4734 for (int i = 0; i < tli.size(); ++i)
4735 drawSubtreeRecursive(tli.at(i), painter, viewTransform, exposedRegion, widget);
4736}
4737
4738void QGraphicsScenePrivate::drawSubtreeRecursive(QGraphicsItem *item, QPainter *painter,
4739 const QTransform *const viewTransform,
4740 QRegion *exposedRegion, QWidget *widget,
4741 qreal parentOpacity, const QTransform *const effectTransform)
4742{
4743 Q_ASSERT(item);
4744
4745 if (!item->d_ptr->visible)
4746 return;
4747
4748 const bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
4749 const bool itemHasChildren = !item->d_ptr->children.isEmpty();
4750 if (!itemHasContents && !itemHasChildren)
4751 return; // Item has neither contents nor children!(?)
4752
4753 const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
4754 const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
4755 if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity()))
4756 return;
4757
4758 QTransform transform(Qt::Uninitialized);
4759 QTransform *transformPtr = 0;
4760 bool translateOnlyTransform = false;
4761#define ENSURE_TRANSFORM_PTR \
4762 if (!transformPtr) { \
4763 Q_ASSERT(!itemIsUntransformable); \
4764 if (viewTransform) { \
4765 transform = item->d_ptr->sceneTransform; \
4766 transform *= *viewTransform; \
4767 transformPtr = &transform; \
4768 } else { \
4769 transformPtr = &item->d_ptr->sceneTransform; \
4770 translateOnlyTransform = item->d_ptr->sceneTransformTranslateOnly; \
4771 } \
4772 }
4773
4774 // Update the item's scene transform if the item is transformable;
4775 // otherwise calculate the full transform,
4776 bool wasDirtyParentSceneTransform = false;
4777 const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
4778 if (itemIsUntransformable) {
4779 transform = item->deviceTransform(viewTransform ? *viewTransform : QTransform());
4780 transformPtr = &transform;
4781 } else if (item->d_ptr->dirtySceneTransform) {
4782 item->d_ptr->updateSceneTransformFromParent();
4783 Q_ASSERT(!item->d_ptr->dirtySceneTransform);
4784 wasDirtyParentSceneTransform = true;
4785 }
4786
4787 const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
4788 bool drawItem = itemHasContents && !itemIsFullyTransparent;
4789 if (drawItem) {
4790 const QRectF brect = adjustedItemEffectiveBoundingRect(item);
4791 ENSURE_TRANSFORM_PTR
4792 QRect viewBoundingRect = translateOnlyTransform ? brect.translated(transformPtr->dx(), transformPtr->dy()).toAlignedRect()
4793 : transformPtr->mapRect(brect).toAlignedRect();
4794 viewBoundingRect.adjust(-int(rectAdjust), -int(rectAdjust), rectAdjust, rectAdjust);
4795 if (widget)
4796 item->d_ptr->paintedViewBoundingRects.insert(widget, viewBoundingRect);
4797 drawItem = exposedRegion ? exposedRegion->intersects(viewBoundingRect)
4798 : !viewBoundingRect.normalized().isEmpty();
4799 if (!drawItem) {
4800 if (!itemHasChildren)
4801 return;
4802 if (itemClipsChildrenToShape) {
4803 if (wasDirtyParentSceneTransform)
4804 item->d_ptr->invalidateChildrenSceneTransform();
4805 return;
4806 }
4807 }
4808 } // else we know for sure this item has children we must process.
4809
4810 if (itemHasChildren && itemClipsChildrenToShape)
4811 ENSURE_TRANSFORM_PTR;
4812
4813#ifndef QT_NO_GRAPHICSEFFECT
4814 if (item->d_ptr->graphicsEffect && item->d_ptr->graphicsEffect->isEnabled()) {
4815 ENSURE_TRANSFORM_PTR;
4816 QGraphicsItemPaintInfo info(viewTransform, transformPtr, effectTransform, exposedRegion, widget, &styleOptionTmp,
4817 painter, opacity, wasDirtyParentSceneTransform, itemHasContents && !itemIsFullyTransparent);
4818 QGraphicsEffectSource *source = item->d_ptr->graphicsEffect->d_func()->source;
4819 QGraphicsItemEffectSourcePrivate *sourced = static_cast<QGraphicsItemEffectSourcePrivate *>
4820 (source->d_func());
4821 sourced->info = &info;
4822 const QTransform restoreTransform = painter->worldTransform();
4823 if (effectTransform)
4824 painter->setWorldTransform(*transformPtr * *effectTransform);
4825 else
4826 painter->setWorldTransform(*transformPtr);
4827 painter->setOpacity(opacity);
4828
4829 if (sourced->currentCachedSystem() != Qt::LogicalCoordinates
4830 && sourced->lastEffectTransform != painter->worldTransform())
4831 {
4832 if (sourced->lastEffectTransform.type() <= QTransform::TxTranslate
4833 && painter->worldTransform().type() <= QTransform::TxTranslate)
4834 {
4835 QRectF sourceRect = sourced->boundingRect(Qt::DeviceCoordinates);
4836 QRect effectRect = sourced->paddedEffectRect(Qt::DeviceCoordinates, sourced->currentCachedMode(), sourceRect);
4837
4838 sourced->setCachedOffset(effectRect.topLeft());
4839 } else {
4840 sourced->invalidateCache(QGraphicsEffectSourcePrivate::TransformChanged);
4841 }
4842
4843 sourced->lastEffectTransform = painter->worldTransform();
4844 }
4845
4846 item->d_ptr->graphicsEffect->draw(painter);
4847 painter->setWorldTransform(restoreTransform);
4848 sourced->info = 0;
4849 } else
4850#endif //QT_NO_GRAPHICSEFFECT
4851 {
4852 draw(item, painter, viewTransform, transformPtr, exposedRegion, widget, opacity,
4853 effectTransform, wasDirtyParentSceneTransform, drawItem);
4854 }
4855}
4856
4857static inline void setClip(QPainter *painter, QGraphicsItem *item)
4858{
4859 painter->save();
4860 QRectF clipRect;
4861 const QPainterPath clipPath(item->shape());
4862 if (QPathClipper::pathToRect(clipPath, &clipRect))
4863 painter->setClipRect(clipRect, Qt::IntersectClip);
4864 else
4865 painter->setClipPath(clipPath, Qt::IntersectClip);
4866}
4867
4868static inline void setWorldTransform(QPainter *painter, const QTransform *const transformPtr,
4869 const QTransform *effectTransform)
4870{
4871 Q_ASSERT(transformPtr);
4872 if (effectTransform)
4873 painter->setWorldTransform(*transformPtr * *effectTransform);
4874 else
4875 painter->setWorldTransform(*transformPtr);
4876}
4877
4878void QGraphicsScenePrivate::draw(QGraphicsItem *item, QPainter *painter, const QTransform *const viewTransform,
4879 const QTransform *const transformPtr, QRegion *exposedRegion, QWidget *widget,
4880 qreal opacity, const QTransform *effectTransform,
4881 bool wasDirtyParentSceneTransform, bool drawItem)
4882{
4883 const bool itemIsFullyTransparent = QGraphicsItemPrivate::isOpacityNull(opacity);
4884 const bool itemClipsChildrenToShape = (item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape);
4885 const bool itemHasChildren = !item->d_ptr->children.isEmpty();
4886 bool setChildClip = itemClipsChildrenToShape;
4887 bool itemHasChildrenStackedBehind = false;
4888
4889 int i = 0;
4890 if (itemHasChildren) {
4891 if (itemClipsChildrenToShape)
4892 setWorldTransform(painter, transformPtr, effectTransform);
4893
4894 item->d_ptr->ensureSortedChildren();
4895 // Items with the 'ItemStacksBehindParent' flag are put in front of the list
4896 // so all we have to do is to check the first item.
4897 itemHasChildrenStackedBehind = (item->d_ptr->children.at(0)->d_ptr->flags
4898 & QGraphicsItem::ItemStacksBehindParent);
4899
4900 if (itemHasChildrenStackedBehind) {
4901 if (itemClipsChildrenToShape) {
4902 setClip(painter, item);
4903 setChildClip = false;
4904 }
4905
4906 // Draw children behind
4907 for (i = 0; i < item->d_ptr->children.size(); ++i) {
4908 QGraphicsItem *child = item->d_ptr->children.at(i);
4909 if (wasDirtyParentSceneTransform)
4910 child->d_ptr->dirtySceneTransform = 1;
4911 if (!(child->d_ptr->flags & QGraphicsItem::ItemStacksBehindParent))
4912 break;
4913 if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
4914 continue;
4915 drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
4916 }
4917 }
4918 }
4919
4920 // Draw item
4921 if (drawItem) {
4922 Q_ASSERT(!itemIsFullyTransparent);
4923 Q_ASSERT(!(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents));
4924 Q_ASSERT(transformPtr);
4925 item->d_ptr->initStyleOption(&styleOptionTmp, *transformPtr, exposedRegion
4926 ? *exposedRegion : QRegion(), exposedRegion == 0);
4927
4928 const bool itemClipsToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsToShape;
4929 bool restorePainterClip = false;
4930
4931 if (!itemHasChildren || !itemClipsChildrenToShape) {
4932 // Item does not have children or clip children to shape.
4933 setWorldTransform(painter, transformPtr, effectTransform);
4934 if ((restorePainterClip = itemClipsToShape))
4935 setClip(painter, item);
4936 } else if (itemHasChildrenStackedBehind){
4937 // Item clips children to shape and has children stacked behind, which means
4938 // the painter is already clipped to the item's shape.
4939 if (itemClipsToShape) {
4940 // The clip is already correct. Ensure correct world transform.
4941 setWorldTransform(painter, transformPtr, effectTransform);
4942 } else {
4943 // Remove clip (this also ensures correct world transform).
4944 painter->restore();
4945 setChildClip = true;
4946 }
4947 } else if (itemClipsToShape) {
4948 // Item clips children and itself to shape. It does not have hildren stacked
4949 // behind, which means the clip has not yet been set. We set it now and re-use it
4950 // for the children.
4951 setClip(painter, item);
4952 setChildClip = false;
4953 }
4954
4955 if (painterStateProtection && !restorePainterClip)
4956 painter->save();
4957
4958 painter->setOpacity(opacity);
4959 if (!item->d_ptr->cacheMode && !item->d_ptr->isWidget)
4960 item->paint(painter, &styleOptionTmp, widget);
4961 else
4962 drawItemHelper(item, painter, &styleOptionTmp, widget, painterStateProtection);
4963
4964 if (painterStateProtection || restorePainterClip)
4965 painter->restore();
4966 }
4967
4968 // Draw children in front
4969 if (itemHasChildren) {
4970 if (setChildClip)
4971 setClip(painter, item);
4972
4973 for (; i < item->d_ptr->children.size(); ++i) {
4974 QGraphicsItem *child = item->d_ptr->children.at(i);
4975 if (wasDirtyParentSceneTransform)
4976 child->d_ptr->dirtySceneTransform = 1;
4977 if (itemIsFullyTransparent && !(child->d_ptr->flags & QGraphicsItem::ItemIgnoresParentOpacity))
4978 continue;
4979 drawSubtreeRecursive(child, painter, viewTransform, exposedRegion, widget, opacity, effectTransform);
4980 }
4981
4982 // Restore child clip
4983 if (itemClipsChildrenToShape)
4984 painter->restore();
4985 }
4986}
4987
4988void QGraphicsScenePrivate::markDirty(QGraphicsItem *item, const QRectF &rect, bool invalidateChildren,
4989 bool force, bool ignoreOpacity, bool removingItemFromScene,
4990 bool updateBoundingRect)
4991{
4992 Q_ASSERT(item);
4993 if (updateAll)
4994 return;
4995
4996 if (removingItemFromScene && !ignoreOpacity && !item->d_ptr->ignoreOpacity) {
4997 // If any of the item's ancestors ignore opacity, it means that the opacity
4998 // was set to 0 (and the update request has not yet been processed). That
4999 // also means that we have to ignore the opacity for the item itself; otherwise
5000 // things like: parent->setOpacity(0); scene->removeItem(child) won't work.
5001 // Note that we only do this when removing items from the scene. In all other
5002 // cases the ignoreOpacity bit propagates properly in processDirtyItems, but
5003 // since the item is removed immediately it won't be processed there.
5004 QGraphicsItem *p = item->d_ptr->parent;
5005 while (p) {
5006 if (p->d_ptr->ignoreOpacity) {
5007 item->d_ptr->ignoreOpacity = true;
5008 break;
5009 }
5010 p = p->d_ptr->parent;
5011 }
5012 }
5013
5014 if (item->d_ptr->discardUpdateRequest(/*ignoreVisibleBit=*/force,
5015 /*ignoreDirtyBit=*/removingItemFromScene || invalidateChildren,
5016 /*ignoreOpacity=*/ignoreOpacity)) {
5017 if (item->d_ptr->dirty) {
5018 // The item is already marked as dirty and will be processed later. However,
5019 // we have to make sure ignoreVisible and ignoreOpacity are set properly;
5020 // otherwise things like: item->update(); item->hide() (force is now true)
5021 // won't work as expected.
5022 if (force)
5023 item->d_ptr->ignoreVisible = 1;
5024 if (ignoreOpacity)
5025 item->d_ptr->ignoreOpacity = 1;
5026 }
5027 return;
5028 }
5029
5030 const bool fullItemUpdate = rect.isNull();
5031 if (!fullItemUpdate && rect.isEmpty())
5032 return;
5033
5034 if (!processDirtyItemsEmitted) {
5035 QMetaMethod method = q_ptr->metaObject()->method(processDirtyItemsIndex);
5036 method.invoke(q_ptr, Qt::QueuedConnection);
5037// QMetaObject::invokeMethod(q_ptr, "_q_processDirtyItems", Qt::QueuedConnection);
5038 processDirtyItemsEmitted = true;
5039 }
5040
5041 if (removingItemFromScene) {
5042 // Note that this function can be called from the item's destructor, so
5043 // do NOT call any virtual functions on it within this block.
5044 if (isSignalConnected(changedSignalIndex) || views.isEmpty()) {
5045 // This block of code is kept for compatibility. Since 4.5, by default
5046 // QGraphicsView does not connect the signal and we use the below
5047 // method of delivering updates.
5048 q_func()->update();
5049 return;
5050 }
5051
5052 for (int i = 0; i < views.size(); ++i) {
5053 QGraphicsViewPrivate *viewPrivate = views.at(i)->d_func();
5054 QRect rect = item->d_ptr->paintedViewBoundingRects.value(viewPrivate->viewport);
5055 rect.translate(viewPrivate->dirtyScrollOffset);
5056 viewPrivate->updateRect(rect);
5057 }
5058 return;
5059 }
5060
5061 bool hasNoContents = item->d_ptr->flags & QGraphicsItem::ItemHasNoContents;
5062 if (!hasNoContents) {
5063 item->d_ptr->dirty = 1;
5064 if (fullItemUpdate)
5065 item->d_ptr->fullUpdatePending = 1;
5066 else if (!item->d_ptr->fullUpdatePending)
5067 item->d_ptr->needsRepaint |= rect;
5068 } else if (item->d_ptr->graphicsEffect) {
5069 invalidateChildren = true;
5070 }
5071
5072 if (invalidateChildren) {
5073 item->d_ptr->allChildrenDirty = 1;
5074 item->d_ptr->dirtyChildren = 1;
5075 }
5076
5077 if (force)
5078 item->d_ptr->ignoreVisible = 1;
5079 if (ignoreOpacity)
5080 item->d_ptr->ignoreOpacity = 1;
5081
5082 if (!updateBoundingRect)
5083 item->d_ptr->markParentDirty();
5084}
5085
5086static inline bool updateHelper(QGraphicsViewPrivate *view, QGraphicsItemPrivate *item,
5087 const QRectF &rect, bool itemIsUntransformable)
5088{
5089 Q_ASSERT(view);
5090 Q_ASSERT(item);
5091
5092 QGraphicsItem *itemq = static_cast<QGraphicsItem *>(item->q_ptr);
5093 QGraphicsView *viewq = static_cast<QGraphicsView *>(view->q_ptr);
5094
5095 if (itemIsUntransformable) {
5096 const QTransform xform = itemq->deviceTransform(viewq->viewportTransform());
5097 if (!item->hasBoundingRegionGranularity)
5098 return view->updateRectF(xform.mapRect(rect));
5099 return view->updateRegion(rect, xform);
5100 }
5101
5102 if (item->sceneTransformTranslateOnly && view->identityMatrix) {
5103 const qreal dx = item->sceneTransform.dx();
5104 const qreal dy = item->sceneTransform.dy();
5105 QRectF r(rect);
5106 r.translate(dx - view->horizontalScroll(), dy - view->verticalScroll());
5107 return view->updateRectF(r);
5108 }
5109
5110 if (!viewq->isTransformed()) {
5111 if (!item->hasBoundingRegionGranularity)
5112 return view->updateRectF(item->sceneTransform.mapRect(rect));
5113 return view->updateRegion(rect, item->sceneTransform);
5114 }
5115
5116 QTransform xform = item->sceneTransform;
5117 xform *= viewq->viewportTransform();
5118 if (!item->hasBoundingRegionGranularity)
5119 return view->updateRectF(xform.mapRect(rect));
5120 return view->updateRegion(rect, xform);
5121}
5122
5123void QGraphicsScenePrivate::processDirtyItemsRecursive(QGraphicsItem *item, bool dirtyAncestorContainsChildren,
5124 qreal parentOpacity)
5125{
5126 Q_Q(QGraphicsScene);
5127 Q_ASSERT(item);
5128 Q_ASSERT(!updateAll);
5129
5130 if (!item->d_ptr->dirty && !item->d_ptr->dirtyChildren) {
5131 resetDirtyItem(item);
5132 return;
5133 }
5134
5135 const bool itemIsHidden = !item->d_ptr->ignoreVisible && !item->d_ptr->visible;
5136 if (itemIsHidden) {
5137 resetDirtyItem(item, /*recursive=*/true);
5138 return;
5139 }
5140
5141 bool itemHasContents = !(item->d_ptr->flags & QGraphicsItem::ItemHasNoContents);
5142 const bool itemHasChildren = !item->d_ptr->children.isEmpty();
5143 if (!itemHasContents) {
5144 if (!itemHasChildren) {
5145 resetDirtyItem(item);
5146 return; // Item has neither contents nor children!(?)
5147 }
5148 if (item->d_ptr->graphicsEffect)
5149 itemHasContents = true;
5150 }
5151
5152 const qreal opacity = item->d_ptr->combineOpacityFromParent(parentOpacity);
5153 const bool itemIsFullyTransparent = !item->d_ptr->ignoreOpacity
5154 && QGraphicsItemPrivate::isOpacityNull(opacity);
5155 if (itemIsFullyTransparent && (!itemHasChildren || item->d_ptr->childrenCombineOpacity())) {
5156 resetDirtyItem(item, /*recursive=*/itemHasChildren);
5157 return;
5158 }
5159
5160 bool wasDirtyParentSceneTransform = item->d_ptr->dirtySceneTransform;
5161 const bool itemIsUntransformable = item->d_ptr->itemIsUntransformable();
5162 if (wasDirtyParentSceneTransform && !itemIsUntransformable) {
5163 item->d_ptr->updateSceneTransformFromParent();
5164 Q_ASSERT(!item->d_ptr->dirtySceneTransform);
5165 }
5166
5167 const bool wasDirtyParentViewBoundingRects = item->d_ptr->paintedViewBoundingRectsNeedRepaint;
5168 if (itemIsFullyTransparent || !itemHasContents || dirtyAncestorContainsChildren) {
5169 // Make sure we don't process invisible items or items with no content.
5170 item->d_ptr->dirty = 0;
5171 item->d_ptr->fullUpdatePending = 0;
5172 // Might have a dirty view bounding rect otherwise.
5173 if (itemIsFullyTransparent || !itemHasContents)
5174 item->d_ptr->paintedViewBoundingRectsNeedRepaint = 0;
5175 }
5176
5177 if (!hasSceneRect && item->d_ptr->geometryChanged && item->d_ptr->visible) {
5178 // Update growingItemsBoundingRect.
5179 if (item->d_ptr->sceneTransformTranslateOnly) {
5180 growingItemsBoundingRect |= item->boundingRect().translated(item->d_ptr->sceneTransform.dx(),
5181 item->d_ptr->sceneTransform.dy());
5182 } else {
5183 growingItemsBoundingRect |= item->d_ptr->sceneTransform.mapRect(item->boundingRect());
5184 }
5185 }
5186
5187 // Process item.
5188 if (item->d_ptr->dirty || item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5189 const bool useCompatUpdate = views.isEmpty() || isSignalConnected(changedSignalIndex);
5190 const QRectF itemBoundingRect = adjustedItemEffectiveBoundingRect(item);
5191
5192 if (useCompatUpdate && !itemIsUntransformable && qFuzzyIsNull(item->boundingRegionGranularity())) {
5193 // This block of code is kept for compatibility. Since 4.5, by default
5194 // QGraphicsView does not connect the signal and we use the below
5195 // method of delivering updates.
5196 if (item->d_ptr->sceneTransformTranslateOnly) {
5197 q->update(itemBoundingRect.translated(item->d_ptr->sceneTransform.dx(),
5198 item->d_ptr->sceneTransform.dy()));
5199 } else {
5200 QRectF rect = item->d_ptr->sceneTransform.mapRect(itemBoundingRect);
5201 if (!rect.isEmpty())
5202 q->update(rect);
5203 }
5204 } else {
5205 QRectF dirtyRect;
5206 bool uninitializedDirtyRect = true;
5207
5208 for (int j = 0; j < views.size(); ++j) {
5209 QGraphicsView *view = views.at(j);
5210 QGraphicsViewPrivate *viewPrivate = view->d_func();
5211 QRect &paintedViewBoundingRect = item->d_ptr->paintedViewBoundingRects[viewPrivate->viewport];
5212 if (viewPrivate->fullUpdatePending
5213 || viewPrivate->viewportUpdateMode == QGraphicsView::NoViewportUpdate) {
5214 // Okay, if we have a full update pending or no viewport update, this item's
5215 // paintedViewBoundingRect will be updated correctly in the next paintEvent if
5216 // it is inside the viewport, but for now we can pretend that it is outside.
5217 paintedViewBoundingRect = QRect(-1, -1, -1, -1);
5218 continue;
5219 }
5220
5221 if (item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5222 paintedViewBoundingRect.translate(viewPrivate->dirtyScrollOffset);
5223 if (!viewPrivate->updateRect(paintedViewBoundingRect))
5224 paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
5225 }
5226
5227 if (!item->d_ptr->dirty)
5228 continue;
5229
5230 if (!item->d_ptr->paintedViewBoundingRectsNeedRepaint
5231 && paintedViewBoundingRect.x() == -1 && paintedViewBoundingRect.y() == -1
5232 && paintedViewBoundingRect.width() == -1 && paintedViewBoundingRect.height() == -1) {
5233 continue; // Outside viewport.
5234 }
5235
5236 if (uninitializedDirtyRect) {
5237 dirtyRect = itemBoundingRect;
5238 if (!item->d_ptr->fullUpdatePending) {
5239 _q_adjustRect(&item->d_ptr->needsRepaint);
5240 dirtyRect &= item->d_ptr->needsRepaint;
5241 }
5242 uninitializedDirtyRect = false;
5243 }
5244
5245 if (dirtyRect.isEmpty())
5246 continue; // Discard updates outside the bounding rect.
5247
5248 if (!updateHelper(viewPrivate, item->d_ptr.data(), dirtyRect, itemIsUntransformable)
5249 && item->d_ptr->paintedViewBoundingRectsNeedRepaint) {
5250 paintedViewBoundingRect = QRect(-1, -1, -1, -1); // Outside viewport.
5251 }
5252 }
5253 }
5254 }
5255
5256 // Process children.
5257 if (itemHasChildren && item->d_ptr->dirtyChildren) {
5258 const bool itemClipsChildrenToShape = item->d_ptr->flags & QGraphicsItem::ItemClipsChildrenToShape;
5259 // Items with no content are threated as 'dummy' items which means they are never drawn and
5260 // 'processed', so the painted view bounding rect is never up-to-date. This means that whenever
5261 // such an item changes geometry, its children have to take care of the update regardless
5262 // of whether the item clips children to shape or not.
5263 const bool bypassUpdateClip = !itemHasContents && wasDirtyParentViewBoundingRects;
5264 if (itemClipsChildrenToShape && !bypassUpdateClip) {
5265 // Make sure child updates are clipped to the item's bounding rect.
5266 for (int i = 0; i < views.size(); ++i)
5267 views.at(i)->d_func()->setUpdateClip(item);
5268 }
5269 if (!dirtyAncestorContainsChildren) {
5270 dirtyAncestorContainsChildren = item->d_ptr->fullUpdatePending
5271 && itemClipsChildrenToShape;
5272 }
5273 const bool allChildrenDirty = item->d_ptr->allChildrenDirty;
5274 const bool parentIgnoresVisible = item->d_ptr->ignoreVisible;
5275 const bool parentIgnoresOpacity = item->d_ptr->ignoreOpacity;
5276 for (int i = 0; i < item->d_ptr->children.size(); ++i) {
5277 QGraphicsItem *child = item->d_ptr->children.at(i);
5278 if (wasDirtyParentSceneTransform)
5279 child->d_ptr->dirtySceneTransform = 1;
5280 if (wasDirtyParentViewBoundingRects)
5281 child->d_ptr->paintedViewBoundingRectsNeedRepaint = 1;
5282 if (parentIgnoresVisible)
5283 child->d_ptr->ignoreVisible = 1;
5284 if (parentIgnoresOpacity)
5285 child->d_ptr->ignoreOpacity = 1;
5286 if (allChildrenDirty) {
5287 child->d_ptr->dirty = 1;
5288 child->d_ptr->fullUpdatePending = 1;
5289 child->d_ptr->dirtyChildren = 1;
5290 child->d_ptr->allChildrenDirty = 1;
5291 }
5292 processDirtyItemsRecursive(child, dirtyAncestorContainsChildren, opacity);
5293 }
5294
5295 if (itemClipsChildrenToShape) {
5296 // Reset updateClip.
5297 for (int i = 0; i < views.size(); ++i)
5298 views.at(i)->d_func()->setUpdateClip(0);
5299 }
5300 } else if (wasDirtyParentSceneTransform) {
5301 item->d_ptr->invalidateChildrenSceneTransform();
5302 }
5303
5304 resetDirtyItem(item);
5305}
5306
5307/*!
5308 \obsolete
5309
5310 Paints the given \a items using the provided \a painter, after the
5311 background has been drawn, and before the foreground has been
5312 drawn. All painting is done in \e scene coordinates. Before
5313 drawing each item, the painter must be transformed using
5314 QGraphicsItem::sceneTransform().
5315
5316 The \a options parameter is the list of style option objects for
5317 each item in \a items. The \a numItems parameter is the number of
5318 items in \a items and options in \a options. The \a widget
5319 parameter is optional; if specified, it should point to the widget
5320 that is being painted on.
5321
5322 The default implementation prepares the painter matrix, and calls
5323 QGraphicsItem::paint() on all items. Reimplement this function to
5324 provide custom painting of all items for the scene; gaining
5325 complete control over how each item is drawn. In some cases this
5326 can increase drawing performance significantly.
5327
5328 Example:
5329
5330 \snippet doc/src/snippets/graphicssceneadditemsnippet.cpp 0
5331
5332 Since Qt 4.6, this function is not called anymore unless
5333 the QGraphicsView::IndirectPainting flag is given as an Optimization
5334 flag.
5335
5336 \sa drawBackground(), drawForeground()
5337*/
5338void QGraphicsScene::drawItems(QPainter *painter,
5339 int numItems,
5340 QGraphicsItem *items[],
5341 const QStyleOptionGraphicsItem options[], QWidget *widget)
5342{
5343 Q_D(QGraphicsScene);
5344 // Make sure we don't have unpolished items before we draw.
5345 if (!d->unpolishedItems.isEmpty())
5346 d->_q_polishItems();
5347
5348 const qreal opacity = painter->opacity();
5349 QTransform viewTransform = painter->worldTransform();
5350 Q_UNUSED(options);
5351
5352 // Determine view, expose and flags.
5353 QGraphicsView *view = widget ? qobject_cast<QGraphicsView *>(widget->parentWidget()) : 0;
5354 QRegion *expose = 0;
5355 const quint32 oldRectAdjust = d->rectAdjust;
5356 if (view) {
5357 d->updateAll = false;
5358 expose = &view->d_func()->exposedRegion;
5359 if (view->d_func()->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
5360 d->rectAdjust = 1;
5361 else
5362 d->rectAdjust = 2;
5363 }
5364
5365 // Find all toplevels, they are already sorted.
5366 QList<QGraphicsItem *> topLevelItems;
5367 for (int i = 0; i < numItems; ++i) {
5368 QGraphicsItem *item = items[i]->topLevelItem();
5369 if (!item->d_ptr->itemDiscovered) {
5370 topLevelItems << item;
5371 item->d_ptr->itemDiscovered = 1;
5372 d->drawSubtreeRecursive(item, painter, &viewTransform, expose, widget);
5373 }
5374 }
5375
5376 d->rectAdjust = oldRectAdjust;
5377 // Reset discovery bits.
5378 for (int i = 0; i < topLevelItems.size(); ++i)
5379 topLevelItems.at(i)->d_ptr->itemDiscovered = 0;
5380
5381 painter->setWorldTransform(viewTransform);
5382 painter->setOpacity(opacity);
5383}
5384
5385/*!
5386 \since 4.4
5387
5388 Finds a new widget to give the keyboard focus to, as appropriate for Tab
5389 and Shift+Tab, and returns true if it can find a new widget, or false if
5390 it cannot. If \a next is true, this function searches forward; if \a next
5391 is false, it searches backward.
5392
5393 You can reimplement this function in a subclass of QGraphicsScene to
5394 provide fine-grained control over how tab focus passes inside your
5395 scene. The default implementation is based on the tab focus chain defined
5396 by QGraphicsWidget::setTabOrder().
5397*/
5398bool QGraphicsScene::focusNextPrevChild(bool next)
5399{
5400 Q_D(QGraphicsScene);
5401
5402 QGraphicsItem *item = focusItem();
5403 if (item && !item->isWidget()) {
5404 // Tab out of the scene.
5405 return false;
5406 }
5407 if (!item) {
5408 if (d->lastFocusItem && !d->lastFocusItem->isWidget()) {
5409 // Restore focus to the last focusable non-widget item that had
5410 // focus.
5411 setFocusItem(d->lastFocusItem, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5412 return true;
5413 }
5414 }
5415 if (!d->tabFocusFirst) {
5416 // No widgets...
5417 return false;
5418 }
5419
5420 // The item must be a widget.
5421 QGraphicsWidget *widget = 0;
5422 if (!item) {
5423 widget = next ? d->tabFocusFirst : d->tabFocusFirst->d_func()->focusPrev;
5424 } else {
5425 QGraphicsWidget *test = static_cast<QGraphicsWidget *>(item);
5426 widget = next ? test->d_func()->focusNext : test->d_func()->focusPrev;
5427 if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
5428 return false;
5429 }
5430 QGraphicsWidget *widgetThatHadFocus = widget;
5431
5432 // Run around the focus chain until we find a widget that can take tab focus.
5433 do {
5434 if (widget->flags() & QGraphicsItem::ItemIsFocusable
5435 && widget->isEnabled() && widget->isVisibleTo(0)
5436 && (widget->focusPolicy() & Qt::TabFocus)
5437 && (!item || !item->isPanel() || item->isAncestorOf(widget))
5438 ) {
5439 setFocusItem(widget, next ? Qt::TabFocusReason : Qt::BacktabFocusReason);
5440 return true;
5441 }
5442 widget = next ? widget->d_func()->focusNext : widget->d_func()->focusPrev;
5443 if ((next && widget == d->tabFocusFirst) || (!next && widget == d->tabFocusFirst->d_func()->focusPrev))
5444 return false;
5445 } while (widget != widgetThatHadFocus);
5446
5447 return false;
5448}
5449
5450/*!
5451 \fn QGraphicsScene::changed(const QList<QRectF> &region)
5452
5453 This signal is emitted by QGraphicsScene when control reaches the
5454 event loop, if the scene content changes. The \a region parameter
5455 contains a list of scene rectangles that indicate the area that
5456 has been changed.
5457
5458 \sa QGraphicsView::updateScene()
5459*/
5460
5461/*!
5462 \fn QGraphicsScene::sceneRectChanged(const QRectF &rect)
5463
5464 This signal is emitted by QGraphicsScene whenever the scene rect changes.
5465 The \a rect parameter is the new scene rectangle.
5466
5467 \sa QGraphicsView::updateSceneRect()
5468*/
5469
5470/*!
5471 \fn QGraphicsScene::selectionChanged()
5472 \since 4.3
5473
5474 This signal is emitted by QGraphicsScene whenever the selection
5475 changes. You can call selectedItems() to get the new list of selected
5476 items.
5477
5478 The selection changes whenever an item is selected or unselected, a
5479 selection area is set, cleared or otherwise changed, if a preselected item
5480 is added to the scene, or if a selected item is removed from the scene.
5481
5482 QGraphicsScene emits this signal only once for group selection operations.
5483 For example, if you set a selection area, select or unselect a
5484 QGraphicsItemGroup, or if you add or remove from the scene a parent item
5485 that contains several selected items, selectionChanged() is emitted only
5486 once after the operation has completed (instead of once for each item).
5487
5488 \sa setSelectionArea(), selectedItems(), QGraphicsItem::setSelected()
5489*/
5490
5491/*!
5492 \since 4.4
5493
5494 Returns the scene's style, or the same as QApplication::style() if the
5495 scene has not been explicitly assigned a style.
5496
5497 \sa setStyle()
5498*/
5499QStyle *QGraphicsScene::style() const
5500{
5501 Q_D(const QGraphicsScene);
5502 // ### This function, and the use of styles in general, is non-reentrant.
5503 return d->style ? d->style : QApplication::style();
5504}
5505
5506/*!
5507 \since 4.4
5508
5509 Sets or replaces the style of the scene to \a style, and reparents the
5510 style to this scene. Any previously assigned style is deleted. The scene's
5511 style defaults to QApplication::style(), and serves as the default for all
5512 QGraphicsWidget items in the scene.
5513
5514 Changing the style, either directly by calling this function, or
5515 indirectly by calling QApplication::setStyle(), will automatically update
5516 the style for all widgets in the scene that do not have a style explicitly
5517 assigned to them.
5518
5519 If \a style is 0, QGraphicsScene will revert to QApplication::style().
5520
5521 \sa style()
5522*/
5523void QGraphicsScene::setStyle(QStyle *style)
5524{
5525 Q_D(QGraphicsScene);
5526 // ### This function, and the use of styles in general, is non-reentrant.
5527 if (style == d->style)
5528 return;
5529
5530 // Delete the old style,
5531 delete d->style;
5532 if ((d->style = style))
5533 d->style->setParent(this);
5534
5535 // Notify the scene.
5536 QEvent event(QEvent::StyleChange);
5537 QApplication::sendEvent(this, &event);
5538
5539 // Notify all widgets that don't have a style explicitly set.
5540 foreach (QGraphicsItem *item, items()) {
5541 if (item->isWidget()) {
5542 QGraphicsWidget *widget = static_cast<QGraphicsWidget *>(item);
5543 if (!widget->testAttribute(Qt::WA_SetStyle))
5544 QApplication::sendEvent(widget, &event);
5545 }
5546 }
5547}
5548
5549/*!
5550 \property QGraphicsScene::font
5551 \since 4.4
5552 \brief the scene's default font
5553
5554 This property provides the scene's font. The scene font defaults to,
5555 and resolves all its entries from, QApplication::font.
5556
5557 If the scene's font changes, either directly through setFont() or
5558 indirectly when the application font changes, QGraphicsScene first
5559 sends itself a \l{QEvent::FontChange}{FontChange} event, and it then
5560 sends \l{QEvent::FontChange}{FontChange} events to all top-level
5561 widget items in the scene. These items respond by resolving their own
5562 fonts to the scene, and they then notify their children, who again
5563 notify their children, and so on, until all widget items have updated
5564 their fonts.
5565
5566 Changing the scene font, (directly or indirectly through
5567 QApplication::setFont(),) automatically schedules a redraw the entire
5568 scene.
5569
5570 \sa QWidget::font, QApplication::setFont(), palette, style()
5571*/
5572QFont QGraphicsScene::font() const
5573{
5574 Q_D(const QGraphicsScene);
5575 return d->font;
5576}
5577void QGraphicsScene::setFont(const QFont &font)
5578{
5579 Q_D(QGraphicsScene);
5580 QFont naturalFont = QApplication::font();
5581 naturalFont.resolve(0);
5582 QFont resolvedFont = font.resolve(naturalFont);
5583 d->setFont_helper(resolvedFont);
5584}
5585
5586/*!
5587 \property QGraphicsScene::palette
5588 \since 4.4
5589 \brief the scene's default palette
5590
5591 This property provides the scene's palette. The scene palette defaults to,
5592 and resolves all its entries from, QApplication::palette.
5593
5594 If the scene's palette changes, either directly through setPalette() or
5595 indirectly when the application palette changes, QGraphicsScene first
5596 sends itself a \l{QEvent::PaletteChange}{PaletteChange} event, and it then
5597 sends \l{QEvent::PaletteChange}{PaletteChange} events to all top-level
5598 widget items in the scene. These items respond by resolving their own
5599 palettes to the scene, and they then notify their children, who again
5600 notify their children, and so on, until all widget items have updated
5601 their palettes.
5602
5603 Changing the scene palette, (directly or indirectly through
5604 QApplication::setPalette(),) automatically schedules a redraw the entire
5605 scene.
5606
5607 \sa QWidget::palette, QApplication::setPalette(), font, style()
5608*/
5609QPalette QGraphicsScene::palette() const
5610{
5611 Q_D(const QGraphicsScene);
5612 return d->palette;
5613}
5614void QGraphicsScene::setPalette(const QPalette &palette)
5615{
5616 Q_D(QGraphicsScene);
5617 QPalette naturalPalette = QApplication::palette();
5618 naturalPalette.resolve(0);
5619 QPalette resolvedPalette = palette.resolve(naturalPalette);
5620 d->setPalette_helper(resolvedPalette);
5621}
5622
5623/*!
5624 \since 4.6
5625
5626 Returns true if the scene is active (e.g., it's viewed by
5627 at least one QGraphicsView that is active); otherwise returns false.
5628
5629 \sa QGraphicsItem::isActive(), QWidget::isActiveWindow()
5630*/
5631bool QGraphicsScene::isActive() const
5632{
5633 Q_D(const QGraphicsScene);
5634 return d->activationRefCount > 0;
5635}
5636
5637/*!
5638 \since 4.6
5639 Returns the current active panel, or 0 if no panel is currently active.
5640
5641 \sa QGraphicsScene::setActivePanel()
5642*/
5643QGraphicsItem *QGraphicsScene::activePanel() const
5644{
5645 Q_D(const QGraphicsScene);
5646 return d->activePanel;
5647}
5648
5649/*!
5650 \since 4.6
5651 Activates \a item, which must be an item in this scene. You
5652 can also pass 0 for \a item, in which case QGraphicsScene will
5653 deactivate any currently active panel.
5654
5655 If the scene is currently inactive, \a item remains inactive until the
5656 scene becomes active (or, ir \a item is 0, no item will be activated).
5657
5658 \sa activePanel(), isActive(), QGraphicsItem::isActive()
5659*/
5660void QGraphicsScene::setActivePanel(QGraphicsItem *item)
5661{
5662 Q_D(QGraphicsScene);
5663 d->setActivePanelHelper(item, false);
5664}
5665
5666/*!
5667 \since 4.4
5668
5669 Returns the current active window, or 0 if no window is currently
5670 active.
5671
5672 \sa QGraphicsScene::setActiveWindow()
5673*/
5674QGraphicsWidget *QGraphicsScene::activeWindow() const
5675{
5676 Q_D(const QGraphicsScene);
5677 if (d->activePanel && d->activePanel->isWindow())
5678 return static_cast<QGraphicsWidget *>(d->activePanel);
5679 return 0;
5680}
5681
5682/*!
5683 \since 4.4
5684 Activates \a widget, which must be a widget in this scene. You can also
5685 pass 0 for \a widget, in which case QGraphicsScene will deactivate any
5686 currently active window.
5687
5688 \sa activeWindow(), QGraphicsWidget::isActiveWindow()
5689*/
5690void QGraphicsScene::setActiveWindow(QGraphicsWidget *widget)
5691{
5692 if (widget && widget->scene() != this) {
5693 qWarning("QGraphicsScene::setActiveWindow: widget %p must be part of this scene",
5694 widget);
5695 return;
5696 }
5697
5698 // Activate the widget's panel (all windows are panels).
5699 QGraphicsItem *panel = widget ? widget->panel() : 0;
5700 setActivePanel(panel);
5701
5702 // Raise
5703 if (panel) {
5704 QList<QGraphicsItem *> siblingWindows;
5705 QGraphicsItem *parent = panel->parentItem();
5706 // Raise ### inefficient for toplevels
5707 foreach (QGraphicsItem *sibling, parent ? parent->children() : items()) {
5708 if (sibling != panel && sibling->isWindow())
5709 siblingWindows << sibling;
5710 }
5711
5712 // Find the highest z value.
5713 qreal z = panel->zValue();
5714 for (int i = 0; i < siblingWindows.size(); ++i)
5715 z = qMax(z, siblingWindows.at(i)->zValue());
5716
5717 // This will probably never overflow.
5718 const qreal litt = qreal(0.001);
5719 panel->setZValue(z + litt);
5720 }
5721}
5722
5723/*!
5724 \since 4.6
5725
5726 Sends event \a event to item \a item through possible event filters.
5727
5728 The event is sent only if the item is enabled.
5729
5730 Returns \c false if the event was filtered or if the item is disabled.
5731 Otherwise returns the value that was returned from the event handler.
5732
5733 \sa QGraphicsItem::sceneEvent(), QGraphicsItem::sceneEventFilter()
5734*/
5735bool QGraphicsScene::sendEvent(QGraphicsItem *item, QEvent *event)
5736{
5737 Q_D(QGraphicsScene);
5738 if (!item) {
5739 qWarning("QGraphicsScene::sendEvent: cannot send event to a null item");
5740 return false;
5741 }
5742 if (item->scene() != this) {
5743 qWarning("QGraphicsScene::sendEvent: item %p's scene (%p)"
5744 " is different from this scene (%p)",
5745 item, item->scene(), this);
5746 return false;
5747 }
5748 return d->sendEvent(item, event);
5749}
5750
5751void QGraphicsScenePrivate::addView(QGraphicsView *view)
5752{
5753 views << view;
5754#ifndef QT_NO_GESTURES
5755 foreach (Qt::GestureType gesture, grabbedGestures.keys())
5756 view->viewport()->grabGesture(gesture);
5757#endif
5758}
5759
5760void QGraphicsScenePrivate::removeView(QGraphicsView *view)
5761{
5762 views.removeAll(view);
5763}
5764
5765void QGraphicsScenePrivate::updateTouchPointsForItem(QGraphicsItem *item, QTouchEvent *touchEvent)
5766{
5767 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
5768 for (int i = 0; i < touchPoints.count(); ++i) {
5769 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
5770 touchPoint.setRect(item->mapFromScene(touchPoint.sceneRect()).boundingRect());
5771 touchPoint.setStartPos(item->d_ptr->genericMapFromScene(touchPoint.startScenePos(), touchEvent->widget()));
5772 touchPoint.setLastPos(item->d_ptr->genericMapFromScene(touchPoint.lastScenePos(), touchEvent->widget()));
5773 }
5774 touchEvent->setTouchPoints(touchPoints);
5775}
5776
5777int QGraphicsScenePrivate::findClosestTouchPointId(const QPointF &scenePos)
5778{
5779 int closestTouchPointId = -1;
5780 qreal closestDistance = qreal(0.);
5781 foreach (const QTouchEvent::TouchPoint &touchPoint, sceneCurrentTouchPoints) {
5782 qreal distance = QLineF(scenePos, touchPoint.scenePos()).length();
5783 if (closestTouchPointId == -1|| distance < closestDistance) {
5784 closestTouchPointId = touchPoint.id();
5785 closestDistance = distance;
5786 }
5787 }
5788 return closestTouchPointId;
5789}
5790
5791void QGraphicsScenePrivate::touchEventHandler(QTouchEvent *sceneTouchEvent)
5792{
5793 typedef QPair<Qt::TouchPointStates, QList<QTouchEvent::TouchPoint> > StatesAndTouchPoints;
5794 QHash<QGraphicsItem *, StatesAndTouchPoints> itemsNeedingEvents;
5795
5796 for (int i = 0; i < sceneTouchEvent->touchPoints().count(); ++i) {
5797 const QTouchEvent::TouchPoint &touchPoint = sceneTouchEvent->touchPoints().at(i);
5798
5799 // update state
5800 QGraphicsItem *item = 0;
5801 if (touchPoint.state() == Qt::TouchPointPressed) {
5802 if (sceneTouchEvent->deviceType() == QTouchEvent::TouchPad) {
5803 // on touch-pad devices, send all touch points to the same item
5804 item = itemForTouchPointId.isEmpty()
5805 ? 0
5806 : itemForTouchPointId.constBegin().value();
5807 }
5808
5809 if (!item) {
5810 // determine which item this touch point will go to
5811 cachedItemsUnderMouse = itemsAtPosition(touchPoint.screenPos().toPoint(),
5812 touchPoint.scenePos(),
5813 sceneTouchEvent->widget());
5814 item = cachedItemsUnderMouse.isEmpty() ? 0 : cachedItemsUnderMouse.first();
5815 }
5816
5817 if (sceneTouchEvent->deviceType() == QTouchEvent::TouchScreen) {
5818 // on touch-screens, combine this touch point with the closest one we find
5819 int closestTouchPointId = findClosestTouchPointId(touchPoint.scenePos());
5820 QGraphicsItem *closestItem = itemForTouchPointId.value(closestTouchPointId);
5821 if (!item || (closestItem && cachedItemsUnderMouse.contains(closestItem)))
5822 item = closestItem;
5823 }
5824 if (!item)
5825 continue;
5826
5827 itemForTouchPointId.insert(touchPoint.id(), item);
5828 sceneCurrentTouchPoints.insert(touchPoint.id(), touchPoint);
5829 } else if (touchPoint.state() == Qt::TouchPointReleased) {
5830 item = itemForTouchPointId.take(touchPoint.id());
5831 if (!item)
5832 continue;
5833
5834 sceneCurrentTouchPoints.remove(touchPoint.id());
5835 } else {
5836 item = itemForTouchPointId.value(touchPoint.id());
5837 if (!item)
5838 continue;
5839 Q_ASSERT(sceneCurrentTouchPoints.contains(touchPoint.id()));
5840 sceneCurrentTouchPoints[touchPoint.id()] = touchPoint;
5841 }
5842
5843 StatesAndTouchPoints &statesAndTouchPoints = itemsNeedingEvents[item];
5844 statesAndTouchPoints.first |= touchPoint.state();
5845 statesAndTouchPoints.second.append(touchPoint);
5846 }
5847
5848 if (itemsNeedingEvents.isEmpty()) {
5849 sceneTouchEvent->accept();
5850 return;
5851 }
5852
5853 bool ignoreSceneTouchEvent = true;
5854 QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator it = itemsNeedingEvents.constBegin();
5855 const QHash<QGraphicsItem *, StatesAndTouchPoints>::ConstIterator end = itemsNeedingEvents.constEnd();
5856 for (; it != end; ++it) {
5857 QGraphicsItem *item = it.key();
5858
5859 (void) item->isBlockedByModalPanel(&item);
5860
5861 // determine event type from the state mask
5862 QEvent::Type eventType;
5863 switch (it.value().first) {
5864 case Qt::TouchPointPressed:
5865 // all touch points have pressed state
5866 eventType = QEvent::TouchBegin;
5867 break;
5868 case Qt::TouchPointReleased:
5869 // all touch points have released state
5870 eventType = QEvent::TouchEnd;
5871 break;
5872 case Qt::TouchPointStationary:
5873 // don't send the event if nothing changed
5874 continue;
5875 default:
5876 // all other combinations
5877 eventType = QEvent::TouchUpdate;
5878 break;
5879 }
5880
5881 QTouchEvent touchEvent(eventType);
5882 touchEvent.setWidget(sceneTouchEvent->widget());
5883 touchEvent.setDeviceType(sceneTouchEvent->deviceType());
5884 touchEvent.setModifiers(sceneTouchEvent->modifiers());
5885 touchEvent.setTouchPointStates(it.value().first);
5886 touchEvent.setTouchPoints(it.value().second);
5887
5888 switch (touchEvent.type()) {
5889 case QEvent::TouchBegin:
5890 {
5891 // if the TouchBegin handler recurses, we assume that means the event
5892 // has been implicitly accepted and continue to send touch events
5893 item->d_ptr->acceptedTouchBeginEvent = true;
5894 bool res = sendTouchBeginEvent(item, &touchEvent)
5895 && touchEvent.isAccepted();
5896 if (!res) {
5897 // forget about these touch points, we didn't handle them
5898 for (int i = 0; i < touchEvent.touchPoints().count(); ++i) {
5899 const QTouchEvent::TouchPoint &touchPoint = touchEvent.touchPoints().at(i);
5900 itemForTouchPointId.remove(touchPoint.id());
5901 sceneCurrentTouchPoints.remove(touchPoint.id());
5902 }
5903 ignoreSceneTouchEvent = false;
5904 }
5905 break;
5906 }
5907 default:
5908 if (item->d_ptr->acceptedTouchBeginEvent) {
5909 updateTouchPointsForItem(item, &touchEvent);
5910 (void) sendEvent(item, &touchEvent);
5911 ignoreSceneTouchEvent = false;
5912 }
5913 break;
5914 }
5915 }
5916 sceneTouchEvent->setAccepted(ignoreSceneTouchEvent);
5917}
5918
5919bool QGraphicsScenePrivate::sendTouchBeginEvent(QGraphicsItem *origin, QTouchEvent *touchEvent)
5920{
5921 Q_Q(QGraphicsScene);
5922
5923 if (cachedItemsUnderMouse.isEmpty() || cachedItemsUnderMouse.first() != origin) {
5924 const QTouchEvent::TouchPoint &firstTouchPoint = touchEvent->touchPoints().first();
5925 cachedItemsUnderMouse = itemsAtPosition(firstTouchPoint.screenPos().toPoint(),
5926 firstTouchPoint.scenePos(),
5927 touchEvent->widget());
5928 }
5929 Q_ASSERT(cachedItemsUnderMouse.first() == origin);
5930
5931 // Set focus on the topmost enabled item that can take focus.
5932 bool setFocus = false;
5933 foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
5934 if (item->isEnabled() && ((item->flags() & QGraphicsItem::ItemIsFocusable) && item->d_ptr->mouseSetsFocus)) {
5935 if (!item->isWidget() || ((QGraphicsWidget *)item)->focusPolicy() & Qt::ClickFocus) {
5936 setFocus = true;
5937 if (item != q->focusItem())
5938 q->setFocusItem(item, Qt::MouseFocusReason);
5939 break;
5940 }
5941 }
5942 if (item->isPanel())
5943 break;
5944 }
5945
5946 // If nobody could take focus, clear it.
5947 if (!stickyFocus && !setFocus)
5948 q->setFocusItem(0, Qt::MouseFocusReason);
5949
5950 bool res = false;
5951 bool eventAccepted = touchEvent->isAccepted();
5952 foreach (QGraphicsItem *item, cachedItemsUnderMouse) {
5953 // first, try to deliver the touch event
5954 updateTouchPointsForItem(item, touchEvent);
5955 bool acceptTouchEvents = item->acceptTouchEvents();
5956 touchEvent->setAccepted(acceptTouchEvents);
5957 res = acceptTouchEvents && sendEvent(item, touchEvent);
5958 eventAccepted = touchEvent->isAccepted();
5959 if (itemForTouchPointId.value(touchEvent->touchPoints().first().id()) == 0) {
5960 // item was deleted
5961 item = 0;
5962 } else {
5963 item->d_ptr->acceptedTouchBeginEvent = (res && eventAccepted);
5964 }
5965 touchEvent->spont = false;
5966 if (res && eventAccepted) {
5967 // the first item to accept the TouchBegin gets an implicit grab.
5968 for (int i = 0; i < touchEvent->touchPoints().count(); ++i) {
5969 const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().at(i);
5970 itemForTouchPointId[touchPoint.id()] = item; // can be zero
5971 }
5972 break;
5973 }
5974 if (item && item->isPanel())
5975 break;
5976 }
5977
5978 touchEvent->setAccepted(eventAccepted);
5979 return res;
5980}
5981
5982void QGraphicsScenePrivate::enableTouchEventsOnViews()
5983{
5984 foreach (QGraphicsView *view, views)
5985 view->viewport()->setAttribute(Qt::WA_AcceptTouchEvents, true);
5986}
5987
5988void QGraphicsScenePrivate::updateInputMethodSensitivityInViews()
5989{
5990 for (int i = 0; i < views.size(); ++i)
5991 views.at(i)->d_func()->updateInputMethodSensitivity();
5992}
5993
5994void QGraphicsScenePrivate::enterModal(QGraphicsItem *panel, QGraphicsItem::PanelModality previousModality)
5995{
5996 Q_Q(QGraphicsScene);
5997 Q_ASSERT(panel && panel->isPanel());
5998
5999 QGraphicsItem::PanelModality panelModality = panel->d_ptr->panelModality;
6000 if (previousModality != QGraphicsItem::NonModal) {
6001 // the panel is changing from one modality type to another... temporarily set it back so
6002 // that blockedPanels is populated correctly
6003 panel->d_ptr->panelModality = previousModality;
6004 }
6005
6006 QSet<QGraphicsItem *> blockedPanels;
6007 QList<QGraphicsItem *> items = q->items(); // ### store panels separately
6008 for (int i = 0; i < items.count(); ++i) {
6009 QGraphicsItem *item = items.at(i);
6010 if (item->isPanel() && item->isBlockedByModalPanel())
6011 blockedPanels.insert(item);
6012 }
6013 // blockedPanels contains all currently blocked panels
6014
6015 if (previousModality != QGraphicsItem::NonModal) {
6016 // reset the modality to the proper value, since we changed it above
6017 panel->d_ptr->panelModality = panelModality;
6018 // remove this panel so that it will be reinserted at the front of the stack
6019 modalPanels.removeAll(panel);
6020 }
6021
6022 modalPanels.prepend(panel);
6023
6024 if (!hoverItems.isEmpty()) {
6025 // send GraphicsSceneHoverLeave events to newly blocked hoverItems
6026 QGraphicsSceneHoverEvent hoverEvent;
6027 hoverEvent.setScenePos(lastSceneMousePos);
6028 dispatchHoverEvent(&hoverEvent);
6029 }
6030
6031 if (!mouseGrabberItems.isEmpty() && lastMouseGrabberItemHasImplicitMouseGrab) {
6032 QGraphicsItem *item = mouseGrabberItems.last();
6033 if (item->isBlockedByModalPanel())
6034 ungrabMouse(item, /*itemIsDying =*/ false);
6035 }
6036
6037 QEvent windowBlockedEvent(QEvent::WindowBlocked);
6038 QEvent windowUnblockedEvent(QEvent::WindowUnblocked);
6039 for (int i = 0; i < items.count(); ++i) {
6040 QGraphicsItem *item = items.at(i);
6041 if (item->isPanel()) {
6042 if (!blockedPanels.contains(item) && item->isBlockedByModalPanel()) {
6043 // send QEvent::WindowBlocked to newly blocked panels
6044 sendEvent(item, &windowBlockedEvent);
6045 } else if (blockedPanels.contains(item) && !item->isBlockedByModalPanel()) {
6046 // send QEvent::WindowUnblocked to unblocked panels when downgrading
6047 // a panel from SceneModal to PanelModal
6048 sendEvent(item, &windowUnblockedEvent);
6049 }
6050 }
6051 }
6052}
6053
6054void QGraphicsScenePrivate::leaveModal(QGraphicsItem *panel)
6055{
6056 Q_Q(QGraphicsScene);
6057 Q_ASSERT(panel && panel->isPanel());
6058
6059 QSet<QGraphicsItem *> blockedPanels;
6060 QList<QGraphicsItem *> items = q->items(); // ### same as above
6061 for (int i = 0; i < items.count(); ++i) {
6062 QGraphicsItem *item = items.at(i);
6063 if (item->isPanel() && item->isBlockedByModalPanel())
6064 blockedPanels.insert(item);
6065 }
6066
6067 modalPanels.removeAll(panel);
6068
6069 QEvent e(QEvent::WindowUnblocked);
6070 for (int i = 0; i < items.count(); ++i) {
6071 QGraphicsItem *item = items.at(i);
6072 if (item->isPanel() && blockedPanels.contains(item) && !item->isBlockedByModalPanel())
6073 sendEvent(item, &e);
6074 }
6075
6076 // send GraphicsSceneHoverEnter events to newly unblocked items
6077 QGraphicsSceneHoverEvent hoverEvent;
6078 hoverEvent.setScenePos(lastSceneMousePos);
6079 dispatchHoverEvent(&hoverEvent);
6080}
6081
6082#ifndef QT_NO_GESTURES
6083void QGraphicsScenePrivate::gestureTargetsAtHotSpots(const QSet<QGesture *> &gestures,
6084 Qt::GestureFlag flag,
6085 QHash<QGraphicsObject *, QSet<QGesture *> > *targets,
6086 QSet<QGraphicsObject *> *itemsSet,
6087 QSet<QGesture *> *normal,
6088 QSet<QGesture *> *conflicts)
6089{
6090 QSet<QGesture *> normalGestures; // that are not in conflicted state.
6091 foreach (QGesture *gesture, gestures) {
6092 if (!gesture->hasHotSpot())
6093 continue;
6094 const Qt::GestureType gestureType = gesture->gestureType();
6095 QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), gesture->d_func()->sceneHotSpot, 0);
6096 for (int j = 0; j < items.size(); ++j) {
6097 QGraphicsItem *item = items.at(j);
6098
6099 // Check if the item is blocked by a modal panel and use it as
6100 // a target instead of this item.
6101 (void) item->isBlockedByModalPanel(&item);
6102
6103 if (QGraphicsObject *itemobj = item->toGraphicsObject()) {
6104 QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
6105 QMap<Qt::GestureType, Qt::GestureFlags>::const_iterator it =
6106 d->gestureContext.find(gestureType);
6107 if (it != d->gestureContext.end() && (!flag || (it.value() & flag))) {
6108 if (normalGestures.contains(gesture)) {
6109 normalGestures.remove(gesture);
6110 if (conflicts)
6111 conflicts->insert(gesture);
6112 } else {
6113 normalGestures.insert(gesture);
6114 }
6115 if (targets)
6116 (*targets)[itemobj].insert(gesture);
6117 if (itemsSet)
6118 (*itemsSet).insert(itemobj);
6119 }
6120 }
6121 // Don't propagate through panels.
6122 if (item->isPanel())
6123 break;
6124 }
6125 }
6126 if (normal)
6127 *normal = normalGestures;
6128}
6129
6130void QGraphicsScenePrivate::gestureEventHandler(QGestureEvent *event)
6131{
6132 QWidget *viewport = event->widget();
6133 if (!viewport)
6134 return;
6135 QGraphicsView *graphicsView = qobject_cast<QGraphicsView *>(viewport->parent());
6136 if (!graphicsView)
6137 return;
6138
6139 QList<QGesture *> allGestures = event->gestures();
6140 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6141 << "Gestures:" << allGestures;
6142
6143 QSet<QGesture *> startedGestures;
6144 QPoint delta = viewport->mapFromGlobal(QPoint());
6145 QTransform toScene = QTransform::fromTranslate(delta.x(), delta.y())
6146 * graphicsView->viewportTransform().inverted();
6147 foreach (QGesture *gesture, allGestures) {
6148 // cache scene coordinates of the hot spot
6149 if (gesture->hasHotSpot()) {
6150 gesture->d_func()->sceneHotSpot = toScene.map(gesture->hotSpot());
6151 } else {
6152 gesture->d_func()->sceneHotSpot = QPointF();
6153 }
6154
6155 QGraphicsObject *target = gestureTargets.value(gesture, 0);
6156 if (!target) {
6157 // when we are not in started mode but don't have a target
6158 // then the only one interested in gesture is the view/scene
6159 if (gesture->state() == Qt::GestureStarted)
6160 startedGestures.insert(gesture);
6161 }
6162 }
6163
6164 if (!startedGestures.isEmpty()) {
6165 QSet<QGesture *> normalGestures; // that have just one target
6166 QSet<QGesture *> conflictedGestures; // that have multiple possible targets
6167 gestureTargetsAtHotSpots(startedGestures, Qt::GestureFlag(0), &cachedItemGestures, 0,
6168 &normalGestures, &conflictedGestures);
6169 cachedTargetItems = cachedItemGestures.keys();
6170 qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6171 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6172 << "Normal gestures:" << normalGestures
6173 << "Conflicting gestures:" << conflictedGestures;
6174
6175 // deliver conflicted gestures as override events AND remember
6176 // initial gesture targets
6177 if (!conflictedGestures.isEmpty()) {
6178 for (int i = 0; i < cachedTargetItems.size(); ++i) {
6179 QWeakPointer<QGraphicsObject> item = cachedTargetItems.at(i);
6180
6181 // get gestures to deliver to the current item
6182 QSet<QGesture *> gestures = conflictedGestures & cachedItemGestures.value(item.data());
6183 if (gestures.isEmpty())
6184 continue;
6185
6186 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6187 << "delivering override to"
6188 << item.data() << gestures;
6189 // send gesture override
6190 QGestureEvent ev(gestures.toList());
6191 ev.t = QEvent::GestureOverride;
6192 ev.setWidget(event->widget());
6193 // mark event and individual gestures as ignored
6194 ev.ignore();
6195 foreach(QGesture *g, gestures)
6196 ev.setAccepted(g, false);
6197 sendEvent(item.data(), &ev);
6198 // mark all accepted gestures to deliver them as normal gesture events
6199 foreach (QGesture *g, gestures) {
6200 if (ev.isAccepted() || ev.isAccepted(g)) {
6201 conflictedGestures.remove(g);
6202 // mark the item as a gesture target
6203 if (item) {
6204 gestureTargets.insert(g, item.data());
6205 QHash<QGraphicsObject *, QSet<QGesture *> >::iterator it, e;
6206 it = cachedItemGestures.begin();
6207 e = cachedItemGestures.end();
6208 for(; it != e; ++it)
6209 it.value().remove(g);
6210 cachedItemGestures[item.data()].insert(g);
6211 }
6212 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6213 << "override was accepted:"
6214 << g << item.data();
6215 }
6216 // remember the first item that received the override event
6217 // as it most likely become a target if no one else accepts
6218 // the override event
6219 if (!gestureTargets.contains(g) && item)
6220 gestureTargets.insert(g, item.data());
6221
6222 }
6223 if (conflictedGestures.isEmpty())
6224 break;
6225 }
6226 }
6227 // remember the initial target item for each gesture that was not in
6228 // the conflicted state.
6229 if (!normalGestures.isEmpty()) {
6230 for (int i = 0; i < cachedTargetItems.size() && !normalGestures.isEmpty(); ++i) {
6231 QGraphicsObject *item = cachedTargetItems.at(i);
6232
6233 // get gestures to deliver to the current item
6234 foreach (QGesture *g, cachedItemGestures.value(item)) {
6235 if (!gestureTargets.contains(g)) {
6236 gestureTargets.insert(g, item);
6237 normalGestures.remove(g);
6238 }
6239 }
6240 }
6241 }
6242 }
6243
6244
6245 // deliver all gesture events
6246 QSet<QGesture *> undeliveredGestures;
6247 QSet<QGesture *> parentPropagatedGestures;
6248 foreach (QGesture *gesture, allGestures) {
6249 if (QGraphicsObject *target = gestureTargets.value(gesture, 0)) {
6250 cachedItemGestures[target].insert(gesture);
6251 cachedTargetItems.append(target);
6252 undeliveredGestures.insert(gesture);
6253 QGraphicsItemPrivate *d = target->QGraphicsItem::d_func();
6254 const Qt::GestureFlags flags = d->gestureContext.value(gesture->gestureType());
6255 if (flags & Qt::IgnoredGesturesPropagateToParent)
6256 parentPropagatedGestures.insert(gesture);
6257 } else {
6258 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6259 << "no target for" << gesture << "at"
6260 << gesture->hotSpot() << gesture->d_func()->sceneHotSpot;
6261 }
6262 }
6263 qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6264 for (int i = 0; i < cachedTargetItems.size(); ++i) {
6265 QWeakPointer<QGraphicsObject> receiver = cachedTargetItems.at(i);
6266 QSet<QGesture *> gestures =
6267 undeliveredGestures & cachedItemGestures.value(receiver.data());
6268 gestures -= cachedAlreadyDeliveredGestures.value(receiver.data());
6269
6270 if (gestures.isEmpty())
6271 continue;
6272
6273 cachedAlreadyDeliveredGestures[receiver.data()] += gestures;
6274 const bool isPanel = receiver.data()->isPanel();
6275
6276 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6277 << "delivering to"
6278 << receiver.data() << gestures;
6279 QGestureEvent ev(gestures.toList());
6280 ev.setWidget(event->widget());
6281 sendEvent(receiver.data(), &ev);
6282 QSet<QGesture *> ignoredGestures;
6283 foreach (QGesture *g, gestures) {
6284 if (!ev.isAccepted() && !ev.isAccepted(g)) {
6285 // if the gesture was ignored by its target, we will update the
6286 // targetItems list with a possible target items (items that
6287 // want to receive partial gestures).
6288 // ### wont' work if the target was destroyed in the event
6289 // we will just stop delivering it.
6290 if (receiver && receiver.data() == gestureTargets.value(g, 0))
6291 ignoredGestures.insert(g);
6292 } else {
6293 if (receiver && g->state() == Qt::GestureStarted) {
6294 // someone accepted the propagated initial GestureStarted
6295 // event, let it be the new target for all following events.
6296 gestureTargets[g] = receiver.data();
6297 }
6298 undeliveredGestures.remove(g);
6299 }
6300 }
6301 if (undeliveredGestures.isEmpty())
6302 break;
6303
6304 // ignoredGestures list is only filled when delivering to the gesture
6305 // target item, so it is safe to assume item == target.
6306 if (!ignoredGestures.isEmpty() && !isPanel) {
6307 // look for new potential targets for gestures that were ignored
6308 // and should be propagated.
6309
6310 QSet<QGraphicsObject *> targetsSet = cachedTargetItems.toSet();
6311
6312 if (receiver) {
6313 // first if the gesture should be propagated to parents only
6314 for (QSet<QGesture *>::iterator it = ignoredGestures.begin();
6315 it != ignoredGestures.end();) {
6316 if (parentPropagatedGestures.contains(*it)) {
6317 QGesture *gesture = *it;
6318 const Qt::GestureType gestureType = gesture->gestureType();
6319 QGraphicsItem *item = receiver.data();
6320 while (item) {
6321 if (QGraphicsObject *obj = item->toGraphicsObject()) {
6322 if (item->d_func()->gestureContext.contains(gestureType)) {
6323 targetsSet.insert(obj);
6324 cachedItemGestures[obj].insert(gesture);
6325 }
6326 }
6327 if (item->isPanel())
6328 break;
6329 item = item->parentItem();
6330 }
6331
6332 it = ignoredGestures.erase(it);
6333 continue;
6334 }
6335 ++it;
6336 }
6337 }
6338
6339 gestureTargetsAtHotSpots(ignoredGestures, Qt::ReceivePartialGestures,
6340 &cachedItemGestures, &targetsSet, 0, 0);
6341
6342 cachedTargetItems = targetsSet.toList();
6343 qSort(cachedTargetItems.begin(), cachedTargetItems.end(), qt_closestItemFirst);
6344 DEBUG() << "QGraphicsScenePrivate::gestureEventHandler:"
6345 << "new targets:" << cachedTargetItems;
6346 i = -1; // start delivery again
6347 continue;
6348 }
6349 }
6350
6351 foreach (QGesture *g, startedGestures) {
6352 if (g->gestureCancelPolicy() == QGesture::CancelAllInContext) {
6353 DEBUG() << "lets try to cancel some";
6354 // find gestures in context in Qt::GestureStarted or Qt::GestureUpdated state and cancel them
6355 cancelGesturesForChildren(g);
6356 }
6357 }
6358
6359 // forget about targets for gestures that have ended
6360 foreach (QGesture *g, allGestures) {
6361 switch (g->state()) {
6362 case Qt::GestureFinished:
6363 case Qt::GestureCanceled:
6364 gestureTargets.remove(g);
6365 break;
6366 default:
6367 break;
6368 }
6369 }
6370
6371 cachedTargetItems.clear();
6372 cachedItemGestures.clear();
6373 cachedAlreadyDeliveredGestures.clear();
6374}
6375
6376void QGraphicsScenePrivate::cancelGesturesForChildren(QGesture *original)
6377{
6378 Q_ASSERT(original);
6379 QGraphicsItem *originalItem = gestureTargets.value(original);
6380 if (originalItem == 0) // we only act on accepted gestures, which implies it has a target.
6381 return;
6382
6383 // iterate over all active gestures and for each find the owner
6384 // if the owner is part of our sub-hierarchy, cancel it.
6385
6386 QSet<QGesture *> canceledGestures;
6387 QHash<QGesture *, QGraphicsObject *>::Iterator iter = gestureTargets.begin();
6388 while (iter != gestureTargets.end()) {
6389 QGraphicsObject *item = iter.value();
6390 // note that we don't touch the gestures for our originalItem
6391 if (item != originalItem && originalItem->isAncestorOf(item)) {
6392 DEBUG() << " found a gesture to cancel" << iter.key();
6393 iter.key()->d_func()->state = Qt::GestureCanceled;
6394 canceledGestures << iter.key();
6395 }
6396 ++iter;
6397 }
6398
6399 // sort them per target item by cherry picking from almostCanceledGestures and delivering
6400 QSet<QGesture *> almostCanceledGestures = canceledGestures;
6401 QSet<QGesture *>::Iterator setIter;
6402 while (!almostCanceledGestures.isEmpty()) {
6403 QGraphicsObject *target = 0;
6404 QSet<QGesture*> gestures;
6405 setIter = almostCanceledGestures.begin();
6406 // sort per target item
6407 while (setIter != almostCanceledGestures.end()) {
6408 QGraphicsObject *item = gestureTargets.value(*setIter);
6409 if (target == 0)
6410 target = item;
6411 if (target == item) {
6412 gestures << *setIter;
6413 setIter = almostCanceledGestures.erase(setIter);
6414 } else {
6415 ++setIter;
6416 }
6417 }
6418 Q_ASSERT(target);
6419
6420 QList<QGesture *> list = gestures.toList();
6421 QGestureEvent ev(list);
6422 sendEvent(target, &ev);
6423
6424 foreach (QGesture *g, list) {
6425 if (ev.isAccepted() || ev.isAccepted(g))
6426 gestures.remove(g);
6427 }
6428
6429 foreach (QGesture *g, gestures) {
6430 if (!g->hasHotSpot())
6431 continue;
6432
6433 QList<QGraphicsItem *> items = itemsAtPosition(QPoint(), g->d_func()->sceneHotSpot, 0);
6434 for (int j = 0; j < items.size(); ++j) {
6435 QGraphicsObject *item = items.at(j)->toGraphicsObject();
6436 if (!item)
6437 continue;
6438 QGraphicsItemPrivate *d = item->QGraphicsItem::d_func();
6439 if (d->gestureContext.contains(g->gestureType())) {
6440 QList<QGesture *> list;
6441 list << g;
6442 QGestureEvent ev(list);
6443 sendEvent(item, &ev);
6444 if (ev.isAccepted() || ev.isAccepted(g))
6445 break; // successfully delivered
6446 }
6447 }
6448 }
6449 }
6450
6451 QGestureManager *gestureManager = QApplicationPrivate::instance()->gestureManager;
6452 Q_ASSERT(gestureManager); // it would be very odd if we got called without a manager.
6453 for (setIter = canceledGestures.begin(); setIter != canceledGestures.end(); ++setIter) {
6454 gestureManager->recycle(*setIter);
6455 gestureTargets.remove(*setIter);
6456 }
6457}
6458
6459void QGraphicsScenePrivate::grabGesture(QGraphicsItem *, Qt::GestureType gesture)
6460{
6461 (void)QGestureManager::instance(); // create a gesture manager
6462 if (!grabbedGestures[gesture]++) {
6463 foreach (QGraphicsView *view, views)
6464 view->viewport()->grabGesture(gesture);
6465 }
6466}
6467
6468void QGraphicsScenePrivate::ungrabGesture(QGraphicsItem *item, Qt::GestureType gesture)
6469{
6470 // we know this can only be an object
6471 Q_ASSERT(item->d_ptr->isObject);
6472 QGraphicsObject *obj = static_cast<QGraphicsObject *>(item);
6473 QGestureManager::instance()->cleanupCachedGestures(obj, gesture);
6474 if (!--grabbedGestures[gesture]) {
6475 foreach (QGraphicsView *view, views)
6476 view->viewport()->ungrabGesture(gesture);
6477 }
6478}
6479#endif // QT_NO_GESTURES
6480
6481QT_END_NAMESPACE
6482
6483#include "moc_qgraphicsscene.cpp"
6484
6485#endif // QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.