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

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

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

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