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