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

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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