source: trunk/src/gui/graphicsview/qgraphicsview.cpp

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

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

File size: 129.6 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation (qt-info@nokia.com)
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42static const int QGRAPHICSVIEW_REGION_RECT_THRESHOLD = 50;
43
44static const int QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS = 503; // largest prime < 2^9
45
46/*!
47 \class QGraphicsView
48 \brief The QGraphicsView class provides a widget for displaying the
49 contents of a QGraphicsScene.
50 \since 4.2
51 \ingroup graphicsview-api
52
53
54 QGraphicsView visualizes the contents of a QGraphicsScene in a scrollable
55 viewport. To create a scene with geometrical items, see QGraphicsScene's
56 documentation. QGraphicsView is part of the \l{Graphics View Framework}.
57
58 To visualize a scene, you start by constructing a QGraphicsView object,
59 passing the address of the scene you want to visualize to QGraphicsView's
60 constructor. Alternatively, you can call setScene() to set the scene at a
61 later point. After you call show(), the view will by default scroll to the
62 center of the scene and display any items that are visible at this
63 point. For example:
64
65 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 0
66
67 You can explicitly scroll to any position on the scene by using the
68 scroll bars, or by calling centerOn(). By passing a point to centerOn(),
69 QGraphicsView will scroll its viewport to ensure that the point is
70 centered in the view. An overload is provided for scrolling to a
71 QGraphicsItem, in which case QGraphicsView will see to that the center of
72 the item is centered in the view. If all you want is to ensure that a
73 certain area is visible, (but not necessarily centered,) you can call
74 ensureVisible() instead.
75
76 QGraphicsView can be used to visualize a whole scene, or only parts of it.
77 The visualized area is by default detected automatically when the view is
78 displayed for the first time (by calling
79 QGraphicsScene::itemsBoundingRect()). To set the visualized area rectangle
80 yourself, you can call setSceneRect(). This will adjust the scroll bars'
81 ranges appropriately. Note that although the scene supports a virtually
82 unlimited size, the range of the scroll bars will never exceed the range of
83 an integer (INT_MIN, INT_MAX). When the scene is larger than the scroll
84 bars' values, you can choose to use translate() to navigate the scene
85 instead.
86
87 QGraphicsView visualizes the scene by calling render(). By default, the
88 items are drawn onto the viewport by using a regular QPainter, and using
89 default render hints. To change the default render hints that
90 QGraphicsView passes to QPainter when painting items, you can call
91 setRenderHints().
92
93 By default, QGraphicsView provides a regular QWidget for the viewport
94 widget. You can access this widget by calling viewport(), or you can
95 replace it by calling setViewport(). To render using OpenGL, simply call
96 setViewport(new QGLWidget). QGraphicsView takes ownership of the viewport
97 widget.
98
99 QGraphicsView supports affine transformations, using QTransform. You can
100 either pass a matrix to setTransform(), or you can call one of the
101 convenience functions rotate(), scale(), translate() or shear(). The most
102 two common transformations are scaling, which is used to implement
103 zooming, and rotation. QGraphicsView keeps the center of the view fixed
104 during a transformation.
105
106 You can interact with the items on the scene by using the mouse and
107 keyboard. QGraphicsView translates the mouse and key events into \e scene
108 events, (events that inherit QGraphicsSceneEvent,), and forward them to
109 the visualized scene. In the end, it's the individual item that handles
110 the events and reacts to them. For example, if you click on a selectable
111 item, the item will typically let the scene know that it has been
112 selected, and it will also redraw itself to display a selection
113 rectangle. Similiary, if you click and drag the mouse to move a movable
114 item, it's the item that handles the mouse moves and moves itself. Item
115 interaction is enabled by default, and you can toggle it by calling
116 setInteractive().
117
118 You can also provide your own custom scene interaction, by creating a
119 subclass of QGraphicsView, and reimplementing the mouse and key event
120 handlers. To simplify how you programmatically interact with items in the
121 view, QGraphicsView provides the mapping functions mapToScene() and
122 mapFromScene(), and the item accessors items() and itemAt(). These
123 functions allow you to map points, rectangles, polygons and paths between
124 view coordinates and scene coordinates, and to find items on the scene
125 using view coordinates.
126
127 \img graphicsview-view.png
128
129 \sa QGraphicsScene, QGraphicsItem, QGraphicsSceneEvent
130*/
131
132/*!
133 \enum QGraphicsView::ViewportAnchor
134
135 This enums describe the possible anchors that QGraphicsView can
136 use when the user resizes the view or when the view is
137 transformed.
138
139 \value NoAnchor No anchor, i.e. the view leaves the scene's
140 position unchanged.
141 \value AnchorViewCenter The scene point at the center of the view
142 is used as the anchor.
143 \value AnchorUnderMouse The point under the mouse is used as the anchor.
144
145 \sa resizeAnchor, transformationAnchor
146*/
147
148/*!
149 \enum QGraphicsView::ViewportUpdateMode
150
151 \since 4.3
152
153 This enum describes how QGraphicsView updates its viewport when the scene
154 contents change or are exposed.
155
156 \value FullViewportUpdate When any visible part of the scene changes or is
157 reexposed, QGraphicsView will update the entire viewport. This approach is
158 fastest when QGraphicsView spends more time figuring out what to draw than
159 it would spend drawing (e.g., when very many small items are repeatedly
160 updated). This is the preferred update mode for viewports that do not
161 support partial updates, such as QGLWidget, and for viewports that need to
162 disable scroll optimization.
163
164 \value MinimalViewportUpdate QGraphicsView will determine the minimal
165 viewport region that requires a redraw, minimizing the time spent drawing
166 by avoiding a redraw of areas that have not changed. This is
167 QGraphicsView's default mode. Although this approach provides the best
168 performance in general, if there are many small visible changes on the
169 scene, QGraphicsView might end up spending more time finding the minimal
170 approach than it will spend drawing.
171
172 \value SmartViewportUpdate QGraphicsView will attempt to find an optimal
173 update mode by analyzing the areas that require a redraw.
174
175 \value BoundingRectViewportUpdate The bounding rectangle of all changes in
176 the viewport will be redrawn. This mode has the advantage that
177 QGraphicsView searches only one region for changes, minimizing time spent
178 determining what needs redrawing. The disadvantage is that areas that have
179 not changed also need to be redrawn.
180
181 \value NoViewportUpdate QGraphicsView will never update its viewport when
182 the scene changes; the user is expected to control all updates. This mode
183 disables all (potentially slow) item visibility testing in QGraphicsView,
184 and is suitable for scenes that either require a fixed frame rate, or where
185 the viewport is otherwise updated externally.
186
187 \sa viewportUpdateMode
188*/
189
190/*!
191 \enum QGraphicsView::OptimizationFlag
192
193 \since 4.3
194
195 This enum describes flags that you can enable to improve rendering
196 performance in QGraphicsView. By default, none of these flags are set.
197 Note that setting a flag usually imposes a side effect, and this effect
198 can vary between paint devices and platforms.
199
200 \value DontClipPainter This value is obsolete and has no effect.
201
202 \value DontSavePainterState When rendering, QGraphicsView protects the
203 painter state (see QPainter::save()) when rendering the background or
204 foreground, and when rendering each item. This allows you to leave the
205 painter in an altered state (i.e., you can call QPainter::setPen() or
206 QPainter::setBrush() without restoring the state after painting). However,
207 if the items consistently do restore the state, you should enable this
208 flag to prevent QGraphicsView from doing the same.
209
210 \value DontAdjustForAntialiasing Disables QGraphicsView's antialiasing
211 auto-adjustment of exposed areas. Items that render antialiased lines on
212 the boundaries of their QGraphicsItem::boundingRect() can end up rendering
213 parts of the line outside. To prevent rendering artifacts, QGraphicsView
214 expands all exposed regions by 2 pixels in all directions. If you enable
215 this flag, QGraphicsView will no longer perform these adjustments,
216 minimizing the areas that require redrawing, which improves performance. A
217 common side effect is that items that do draw with antialiasing can leave
218 painting traces behind on the scene as they are moved.
219
220 \value IndirectPainting Since Qt 4.6, restore the old painting algorithm
221 that calls QGraphicsView::drawItems() and QGraphicsScene::drawItems().
222 To be used only for compatibility with old code.
223*/
224
225/*!
226 \enum QGraphicsView::CacheModeFlag
227
228 This enum describes the flags that you can set for a QGraphicsView's cache
229 mode.
230
231 \value CacheNone All painting is done directly onto the viewport.
232
233 \value CacheBackground The background is cached. This affects both custom
234 backgrounds, and backgrounds based on the backgroundBrush property. When
235 this flag is enabled, QGraphicsView will allocate one pixmap with the full
236 size of the viewport.
237
238 \sa cacheMode
239*/
240
241/*!
242 \enum QGraphicsView::DragMode
243
244 This enum describes the default action for the view when pressing and
245 dragging the mouse over the viewport.
246
247 \value NoDrag Nothing happens; the mouse event is ignored.
248
249 \value ScrollHandDrag The cursor changes into a pointing hand, and
250 dragging the mouse around will scroll the scrolbars. This mode works both
251 in \l{QGraphicsView::interactive}{interactive} and non-interactive mode.
252
253 \value RubberBandDrag A rubber band will appear. Dragging the mouse will
254 set the rubber band geometry, and all items covered by the rubber band are
255 selected. This mode is disabled for non-interactive views.
256
257 \sa dragMode, QGraphicsScene::setSelectionArea()
258*/
259
260#include "qgraphicsview.h"
261#include "qgraphicsview_p.h"
262
263#ifndef QT_NO_GRAPHICSVIEW
264
265#include "qgraphicsitem.h"
266#include "qgraphicsitem_p.h"
267#include "qgraphicsscene.h"
268#include "qgraphicsscene_p.h"
269#include "qgraphicssceneevent.h"
270#include "qgraphicswidget.h"
271
272#include <QtCore/qdatetime.h>
273#include <QtCore/qdebug.h>
274#include <QtCore/qmath.h>
275#include <QtGui/qapplication.h>
276#include <QtGui/qdesktopwidget.h>
277#include <QtGui/qevent.h>
278#include <QtGui/qlayout.h>
279#include <QtGui/qtransform.h>
280#include <QtGui/qmatrix.h>
281#include <QtGui/qpainter.h>
282#include <QtGui/qscrollbar.h>
283#include <QtGui/qstyleoption.h>
284#include <QtGui/qinputcontext.h>
285#ifdef Q_WS_X11
286#include <QtGui/qpaintengine.h>
287#include <private/qt_x11_p.h>
288#endif
289
290#include <private/qevent_p.h>
291
292QT_BEGIN_NAMESPACE
293
294bool qt_sendSpontaneousEvent(QObject *receiver, QEvent *event);
295
296inline int q_round_bound(qreal d) //### (int)(qreal) INT_MAX != INT_MAX for single precision
297{
298 if (d <= (qreal) INT_MIN)
299 return INT_MIN;
300 else if (d >= (qreal) INT_MAX)
301 return INT_MAX;
302 return d >= 0.0 ? int(d + 0.5) : int(d - int(d-1) + 0.5) + int(d-1);
303}
304
305void QGraphicsViewPrivate::translateTouchEvent(QGraphicsViewPrivate *d, QTouchEvent *touchEvent)
306{
307 QList<QTouchEvent::TouchPoint> touchPoints = touchEvent->touchPoints();
308 for (int i = 0; i < touchPoints.count(); ++i) {
309 QTouchEvent::TouchPoint &touchPoint = touchPoints[i];
310 // the scene will set the item local pos, startPos, lastPos, and rect before delivering to
311 // an item, but for now those functions are returning the view's local coordinates
312 touchPoint.setSceneRect(d->mapToScene(touchPoint.rect()));
313 touchPoint.setStartScenePos(d->mapToScene(touchPoint.startPos()));
314 touchPoint.setLastScenePos(d->mapToScene(touchPoint.lastPos()));
315
316 // screenPos, startScreenPos, lastScreenPos, and screenRect are already set
317 }
318
319 touchEvent->setTouchPoints(touchPoints);
320}
321
322/*!
323 \internal
324*/
325QGraphicsViewPrivate::QGraphicsViewPrivate()
326 : renderHints(QPainter::TextAntialiasing),
327 dragMode(QGraphicsView::NoDrag),
328 sceneInteractionAllowed(true), hasSceneRect(false),
329 connectedToScene(false),
330 useLastMouseEvent(false),
331 identityMatrix(true),
332 dirtyScroll(true),
333 accelerateScrolling(true),
334 keepLastCenterPoint(true),
335 transforming(false),
336 handScrolling(false),
337 mustAllocateStyleOptions(false),
338 mustResizeBackgroundPixmap(true),
339 fullUpdatePending(true),
340 hasUpdateClip(false),
341 mousePressButton(Qt::NoButton),
342 leftIndent(0), topIndent(0),
343 lastMouseEvent(QEvent::None, QPoint(), Qt::NoButton, 0, 0),
344 alignment(Qt::AlignCenter),
345 transformationAnchor(QGraphicsView::AnchorViewCenter), resizeAnchor(QGraphicsView::NoAnchor),
346 viewportUpdateMode(QGraphicsView::MinimalViewportUpdate),
347 optimizationFlags(0),
348 scene(0),
349#ifndef QT_NO_RUBBERBAND
350 rubberBanding(false),
351 rubberBandSelectionMode(Qt::IntersectsItemShape),
352#endif
353 handScrollMotions(0), cacheMode(0),
354#ifndef QT_NO_CURSOR
355 hasStoredOriginalCursor(false),
356#endif
357 lastDragDropEvent(0),
358 updateSceneSlotReimplementedChecked(false)
359{
360 styleOptions.reserve(QGRAPHICSVIEW_PREALLOC_STYLE_OPTIONS);
361}
362
363/*!
364 \internal
365*/
366void QGraphicsViewPrivate::recalculateContentSize()
367{
368 Q_Q(QGraphicsView);
369
370 QSize maxSize = q->maximumViewportSize();
371 int width = maxSize.width();
372 int height = maxSize.height();
373 QRectF viewRect = matrix.mapRect(q->sceneRect());
374
375 bool frameOnlyAround = (q->style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents, 0, q));
376 if (frameOnlyAround) {
377 if (hbarpolicy == Qt::ScrollBarAlwaysOn)
378 height -= frameWidth * 2;
379 if (vbarpolicy == Qt::ScrollBarAlwaysOn)
380 width -= frameWidth * 2;
381 }
382
383 // Adjust the maximum width and height of the viewport based on the width
384 // of visible scroll bars.
385 int scrollBarExtent = q->style()->pixelMetric(QStyle::PM_ScrollBarExtent, 0, q);
386 if (frameOnlyAround)
387 scrollBarExtent += frameWidth * 2;
388
389 bool useHorizontalScrollBar = (viewRect.width() > width) && hbarpolicy != Qt::ScrollBarAlwaysOff;
390 bool useVerticalScrollBar = (viewRect.height() > height) && vbarpolicy != Qt::ScrollBarAlwaysOff;
391 if (useHorizontalScrollBar && !useVerticalScrollBar) {
392 if (viewRect.height() > height - scrollBarExtent)
393 useVerticalScrollBar = true;
394 }
395 if (useVerticalScrollBar && !useHorizontalScrollBar) {
396 if (viewRect.width() > width - scrollBarExtent)
397 useHorizontalScrollBar = true;
398 }
399 if (useHorizontalScrollBar && hbarpolicy != Qt::ScrollBarAlwaysOn)
400 height -= scrollBarExtent;
401 if (useVerticalScrollBar && vbarpolicy != Qt::ScrollBarAlwaysOn)
402 width -= scrollBarExtent;
403
404 // Setting the ranges of these scroll bars can/will cause the values to
405 // change, and scrollContentsBy() will be called correspondingly. This
406 // will reset the last center point.
407 QPointF savedLastCenterPoint = lastCenterPoint;
408
409 // Remember the former indent settings
410 qreal oldLeftIndent = leftIndent;
411 qreal oldTopIndent = topIndent;
412
413 // If the whole scene fits horizontally, we center the scene horizontally,
414 // and ignore the horizontal scroll bars.
415 int left = q_round_bound(viewRect.left());
416 int right = q_round_bound(viewRect.right() - width);
417 if (left >= right) {
418 hbar->setRange(0, 0);
419
420 switch (alignment & Qt::AlignHorizontal_Mask) {
421 case Qt::AlignLeft:
422 leftIndent = -viewRect.left();
423 break;
424 case Qt::AlignRight:
425 leftIndent = width - viewRect.width() - viewRect.left() - 1;
426 break;
427 case Qt::AlignHCenter:
428 default:
429 leftIndent = width / 2 - (viewRect.left() + viewRect.right()) / 2;
430 break;
431 }
432 } else {
433 hbar->setRange(left, right);
434 hbar->setPageStep(width);
435 hbar->setSingleStep(width / 20);
436 leftIndent = 0;
437 }
438
439 // If the whole scene fits vertically, we center the scene vertically, and
440 // ignore the vertical scroll bars.
441 int top = q_round_bound(viewRect.top());
442 int bottom = q_round_bound(viewRect.bottom() - height);
443 if (top >= bottom) {
444 vbar->setRange(0, 0);
445
446 switch (alignment & Qt::AlignVertical_Mask) {
447 case Qt::AlignTop:
448 topIndent = -viewRect.top();
449 break;
450 case Qt::AlignBottom:
451 topIndent = height - viewRect.height() - viewRect.top() - 1;
452 break;
453 case Qt::AlignVCenter:
454 default:
455 topIndent = height / 2 - (viewRect.top() + viewRect.bottom()) / 2;
456 break;
457 }
458 } else {
459 vbar->setRange(top, bottom);
460 vbar->setPageStep(height);
461 vbar->setSingleStep(height / 20);
462 topIndent = 0;
463 }
464
465 // Restorethe center point from before the ranges changed.
466 lastCenterPoint = savedLastCenterPoint;
467
468 // Issue a full update if the indents change.
469 // ### If the transform is still the same, we can get away with just a
470 // scroll instead.
471 if (oldLeftIndent != leftIndent || oldTopIndent != topIndent) {
472 dirtyScroll = true;
473 updateAll();
474 } else if (q->isRightToLeft() && !leftIndent) {
475 // In reverse mode, the horizontal scroll always changes after the content
476 // size has changed, as the scroll is calculated by summing the min and
477 // max values of the range and subtracting the current value. In normal
478 // mode the scroll remains unchanged unless the indent has changed.
479 dirtyScroll = true;
480 }
481
482 if (cacheMode & QGraphicsView::CacheBackground) {
483 // Invalidate the background pixmap
484 mustResizeBackgroundPixmap = true;
485 }
486}
487
488/*!
489 \internal
490*/
491void QGraphicsViewPrivate::centerView(QGraphicsView::ViewportAnchor anchor)
492{
493 Q_Q(QGraphicsView);
494 switch (anchor) {
495 case QGraphicsView::AnchorUnderMouse: {
496 if (q->underMouse()) {
497 // Last scene pos: lastMouseMoveScenePoint
498 // Current mouse pos:
499 QPointF transformationDiff = q->mapToScene(viewport->rect().center())
500 - q->mapToScene(viewport->mapFromGlobal(QCursor::pos()));
501 q->centerOn(lastMouseMoveScenePoint + transformationDiff);
502 } else {
503 q->centerOn(lastCenterPoint);
504 }
505 break;
506 }
507 case QGraphicsView::AnchorViewCenter:
508 q->centerOn(lastCenterPoint);
509 break;
510 case QGraphicsView::NoAnchor:
511 break;
512 }
513}
514
515/*!
516 \internal
517*/
518void QGraphicsViewPrivate::updateLastCenterPoint()
519{
520 Q_Q(QGraphicsView);
521 lastCenterPoint = q->mapToScene(viewport->rect().center());
522}
523
524/*!
525 \internal
526
527 Returns the horizontal scroll value (the X value of the left edge of the
528 viewport).
529*/
530qint64 QGraphicsViewPrivate::horizontalScroll() const
531{
532 if (dirtyScroll)
533 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
534 return scrollX;
535}
536
537/*!
538 \internal
539
540 Returns the vertical scroll value (the X value of the top edge of the
541 viewport).
542*/
543qint64 QGraphicsViewPrivate::verticalScroll() const
544{
545 if (dirtyScroll)
546 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
547 return scrollY;
548}
549
550/*!
551 \internal
552
553 Maps the given rectangle to the scene using QTransform::mapRect()
554*/
555QRectF QGraphicsViewPrivate::mapRectToScene(const QRect &rect) const
556{
557 if (dirtyScroll)
558 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
559 QRectF scrolled = QRectF(rect.translated(scrollX, scrollY));
560 return identityMatrix ? scrolled : matrix.inverted().mapRect(scrolled);
561}
562
563
564/*!
565 \internal
566
567 Maps the given rectangle from the scene using QTransform::mapRect()
568*/
569QRectF QGraphicsViewPrivate::mapRectFromScene(const QRectF &rect) const
570{
571 if (dirtyScroll)
572 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
573 return (identityMatrix ? rect : matrix.mapRect(rect)).translated(-scrollX, -scrollY);
574}
575
576/*!
577 \internal
578*/
579void QGraphicsViewPrivate::updateScroll()
580{
581 Q_Q(QGraphicsView);
582 scrollX = qint64(-leftIndent);
583 if (q->isRightToLeft()) {
584 if (!leftIndent) {
585 scrollX += hbar->minimum();
586 scrollX += hbar->maximum();
587 scrollX -= hbar->value();
588 }
589 } else {
590 scrollX += hbar->value();
591 }
592
593 scrollY = qint64(vbar->value() - topIndent);
594
595 dirtyScroll = false;
596}
597
598/*!
599 \internal
600*/
601void QGraphicsViewPrivate::replayLastMouseEvent()
602{
603 if (!useLastMouseEvent || !scene)
604 return;
605 mouseMoveEventHandler(&lastMouseEvent);
606}
607
608/*!
609 \internal
610*/
611void QGraphicsViewPrivate::storeMouseEvent(QMouseEvent *event)
612{
613 useLastMouseEvent = true;
614 lastMouseEvent = QMouseEvent(QEvent::MouseMove, event->pos(), event->globalPos(),
615 event->button(), event->buttons(), event->modifiers());
616}
617
618void QGraphicsViewPrivate::mouseMoveEventHandler(QMouseEvent *event)
619{
620 Q_Q(QGraphicsView);
621
622 storeMouseEvent(event);
623 lastMouseEvent.setAccepted(false);
624
625 if (!sceneInteractionAllowed)
626 return;
627 if (handScrolling)
628 return;
629 if (!scene)
630 return;
631
632 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
633 mouseEvent.setWidget(viewport);
634 mouseEvent.setButtonDownScenePos(mousePressButton, mousePressScenePoint);
635 mouseEvent.setButtonDownScreenPos(mousePressButton, mousePressScreenPoint);
636 mouseEvent.setScenePos(q->mapToScene(event->pos()));
637 mouseEvent.setScreenPos(event->globalPos());
638 mouseEvent.setLastScenePos(lastMouseMoveScenePoint);
639 mouseEvent.setLastScreenPos(lastMouseMoveScreenPoint);
640 mouseEvent.setButtons(event->buttons());
641 mouseEvent.setButton(event->button());
642 mouseEvent.setModifiers(event->modifiers());
643 lastMouseMoveScenePoint = mouseEvent.scenePos();
644 lastMouseMoveScreenPoint = mouseEvent.screenPos();
645 mouseEvent.setAccepted(false);
646 if (event->spontaneous())
647 qt_sendSpontaneousEvent(scene, &mouseEvent);
648 else
649 QApplication::sendEvent(scene, &mouseEvent);
650
651 // Remember whether the last event was accepted or not.
652 lastMouseEvent.setAccepted(mouseEvent.isAccepted());
653
654 if (mouseEvent.isAccepted() && mouseEvent.buttons() != 0) {
655 // The event was delivered to a mouse grabber; the press is likely to
656 // have set a cursor, and we must not change it.
657 return;
658 }
659
660#ifndef QT_NO_CURSOR
661 // If all the items ignore hover events, we don't look-up any items
662 // in QGraphicsScenePrivate::dispatchHoverEvent, hence the
663 // cachedItemsUnderMouse list will be empty. We therefore do the look-up
664 // for cursor items here if not all items use the default cursor.
665 if (scene->d_func()->allItemsIgnoreHoverEvents && !scene->d_func()->allItemsUseDefaultCursor
666 && scene->d_func()->cachedItemsUnderMouse.isEmpty()) {
667 scene->d_func()->cachedItemsUnderMouse = scene->d_func()->itemsAtPosition(mouseEvent.screenPos(),
668 mouseEvent.scenePos(),
669 mouseEvent.widget());
670 }
671 // Find the topmost item under the mouse with a cursor.
672 foreach (QGraphicsItem *item, scene->d_func()->cachedItemsUnderMouse) {
673 if (item->hasCursor()) {
674 _q_setViewportCursor(item->cursor());
675 return;
676 }
677 }
678
679 // No items with cursors found; revert to the view cursor.
680 if (hasStoredOriginalCursor) {
681 // Restore the original viewport cursor.
682 hasStoredOriginalCursor = false;
683 viewport->setCursor(originalCursor);
684 }
685#endif
686}
687
688/*!
689 \internal
690*/
691#ifndef QT_NO_RUBBERBAND
692QRegion QGraphicsViewPrivate::rubberBandRegion(const QWidget *widget, const QRect &rect) const
693{
694 QStyleHintReturnMask mask;
695 QStyleOptionRubberBand option;
696 option.initFrom(widget);
697 option.rect = rect;
698 option.opaque = false;
699 option.shape = QRubberBand::Rectangle;
700
701 QRegion tmp;
702 tmp += rect;
703 if (widget->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, widget, &mask))
704 tmp &= mask.region;
705 return tmp;
706}
707#endif
708
709/*!
710 \internal
711*/
712#ifndef QT_NO_CURSOR
713void QGraphicsViewPrivate::_q_setViewportCursor(const QCursor &cursor)
714{
715 if (!hasStoredOriginalCursor) {
716 hasStoredOriginalCursor = true;
717 originalCursor = viewport->cursor();
718 }
719 viewport->setCursor(cursor);
720}
721#endif
722
723/*!
724 \internal
725*/
726#ifndef QT_NO_CURSOR
727void QGraphicsViewPrivate::_q_unsetViewportCursor()
728{
729 Q_Q(QGraphicsView);
730 foreach (QGraphicsItem *item, q->items(lastMouseEvent.pos())) {
731 if (item->hasCursor()) {
732 _q_setViewportCursor(item->cursor());
733 return;
734 }
735 }
736
737 // Restore the original viewport cursor.
738 if (hasStoredOriginalCursor) {
739 hasStoredOriginalCursor = false;
740 if (dragMode == QGraphicsView::ScrollHandDrag)
741 viewport->setCursor(Qt::OpenHandCursor);
742 else
743 viewport->setCursor(originalCursor);
744 }
745}
746#endif
747
748/*!
749 \internal
750*/
751void QGraphicsViewPrivate::storeDragDropEvent(const QGraphicsSceneDragDropEvent *event)
752{
753 delete lastDragDropEvent;
754 lastDragDropEvent = new QGraphicsSceneDragDropEvent(event->type());
755 lastDragDropEvent->setScenePos(event->scenePos());
756 lastDragDropEvent->setScreenPos(event->screenPos());
757 lastDragDropEvent->setButtons(event->buttons());
758 lastDragDropEvent->setModifiers(event->modifiers());
759 lastDragDropEvent->setPossibleActions(event->possibleActions());
760 lastDragDropEvent->setProposedAction(event->proposedAction());
761 lastDragDropEvent->setDropAction(event->dropAction());
762 lastDragDropEvent->setMimeData(event->mimeData());
763 lastDragDropEvent->setWidget(event->widget());
764 lastDragDropEvent->setSource(event->source());
765}
766
767/*!
768 \internal
769*/
770void QGraphicsViewPrivate::populateSceneDragDropEvent(QGraphicsSceneDragDropEvent *dest,
771 QDropEvent *source)
772{
773#ifndef QT_NO_DRAGANDDROP
774 Q_Q(QGraphicsView);
775 dest->setScenePos(q->mapToScene(source->pos()));
776 dest->setScreenPos(q->mapToGlobal(source->pos()));
777 dest->setButtons(source->mouseButtons());
778 dest->setModifiers(source->keyboardModifiers());
779 dest->setPossibleActions(source->possibleActions());
780 dest->setProposedAction(source->proposedAction());
781 dest->setDropAction(source->dropAction());
782 dest->setMimeData(source->mimeData());
783 dest->setWidget(viewport);
784 dest->setSource(source->source());
785#else
786 Q_UNUSED(dest)
787 Q_UNUSED(source)
788#endif
789}
790
791/*!
792 \internal
793*/
794QRect QGraphicsViewPrivate::mapToViewRect(const QGraphicsItem *item, const QRectF &rect) const
795{
796 Q_Q(const QGraphicsView);
797 if (dirtyScroll)
798 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
799
800 if (item->d_ptr->itemIsUntransformable()) {
801 QTransform itv = item->deviceTransform(q->viewportTransform());
802 return itv.mapRect(rect).toAlignedRect();
803 }
804
805 // Translate-only
806 // COMBINE
807 QPointF offset;
808 const QGraphicsItem *parentItem = item;
809 const QGraphicsItemPrivate *itemd;
810 do {
811 itemd = parentItem->d_ptr.data();
812 if (itemd->transformData)
813 break;
814 offset += itemd->pos;
815 } while ((parentItem = itemd->parent));
816
817 QRectF baseRect = rect.translated(offset.x(), offset.y());
818 if (!parentItem) {
819 if (identityMatrix) {
820 baseRect.translate(-scrollX, -scrollY);
821 return baseRect.toAlignedRect();
822 }
823 return matrix.mapRect(baseRect).translated(-scrollX, -scrollY).toAlignedRect();
824 }
825
826 QTransform tr = parentItem->sceneTransform();
827 if (!identityMatrix)
828 tr *= matrix;
829 QRectF r = tr.mapRect(baseRect);
830 r.translate(-scrollX, -scrollY);
831 return r.toAlignedRect();
832}
833
834/*!
835 \internal
836*/
837QRegion QGraphicsViewPrivate::mapToViewRegion(const QGraphicsItem *item, const QRectF &rect) const
838{
839 Q_Q(const QGraphicsView);
840 if (dirtyScroll)
841 const_cast<QGraphicsViewPrivate *>(this)->updateScroll();
842
843 // Accurate bounding region
844 QTransform itv = item->deviceTransform(q->viewportTransform());
845 return item->boundingRegion(itv) & itv.mapRect(rect).toAlignedRect();
846}
847
848/*!
849 \internal
850*/
851void QGraphicsViewPrivate::processPendingUpdates()
852{
853 if (!scene)
854 return;
855
856 if (fullUpdatePending) {
857 viewport->update();
858 } else if (viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate) {
859 viewport->update(dirtyBoundingRect);
860 } else {
861 viewport->update(dirtyRegion); // Already adjusted in updateRect/Region.
862 }
863
864 dirtyBoundingRect = QRect();
865 dirtyRegion = QRegion();
866}
867
868static inline bool intersectsViewport(const QRect &r, int width, int height)
869{ return !(r.left() > width) && !(r.right() < 0) && !(r.top() >= height) && !(r.bottom() < 0); }
870
871static inline bool containsViewport(const QRect &r, int width, int height)
872{ return r.left() <= 0 && r.top() <= 0 && r.right() >= width - 1 && r.bottom() >= height - 1; }
873
874static inline void QRect_unite(QRect *rect, const QRect &other)
875{
876 if (rect->isEmpty()) {
877 *rect = other;
878 } else {
879 rect->setCoords(qMin(rect->left(), other.left()), qMin(rect->top(), other.top()),
880 qMax(rect->right(), other.right()), qMax(rect->bottom(), other.bottom()));
881 }
882}
883
884/*
885 Calling this function results in update rects being clipped to the item's
886 bounding rect. Note that updates prior to this function call is not clipped.
887 The clip is removed by passing 0.
888*/
889void QGraphicsViewPrivate::setUpdateClip(QGraphicsItem *item)
890{
891 Q_Q(QGraphicsView);
892 // We simply ignore the request if the update mode is either FullViewportUpdate
893 // or NoViewportUpdate; in that case there's no point in clipping anything.
894 if (!item || viewportUpdateMode == QGraphicsView::NoViewportUpdate
895 || viewportUpdateMode == QGraphicsView::FullViewportUpdate) {
896 hasUpdateClip = false;
897 return;
898 }
899
900 // Calculate the clip (item's bounding rect in view coordinates).
901 // Optimized version of:
902 // QRect clip = item->deviceTransform(q->viewportTransform())
903 // .mapRect(item->boundingRect()).toAlignedRect();
904 QRect clip;
905 if (item->d_ptr->itemIsUntransformable()) {
906 QTransform xform = item->deviceTransform(q->viewportTransform());
907 clip = xform.mapRect(item->boundingRect()).toAlignedRect();
908 } else if (item->d_ptr->sceneTransformTranslateOnly && identityMatrix) {
909 QRectF r(item->boundingRect());
910 r.translate(item->d_ptr->sceneTransform.dx() - horizontalScroll(),
911 item->d_ptr->sceneTransform.dy() - verticalScroll());
912 clip = r.toAlignedRect();
913 } else if (!q->isTransformed()) {
914 clip = item->d_ptr->sceneTransform.mapRect(item->boundingRect()).toAlignedRect();
915 } else {
916 QTransform xform = item->d_ptr->sceneTransform;
917 xform *= q->viewportTransform();
918 clip = xform.mapRect(item->boundingRect()).toAlignedRect();
919 }
920
921 if (hasUpdateClip) {
922 // Intersect with old clip.
923 updateClip &= clip;
924 } else {
925 updateClip = clip;
926 hasUpdateClip = true;
927 }
928}
929
930bool QGraphicsViewPrivate::updateRegion(const QRectF &rect, const QTransform &xform)
931{
932 if (rect.isEmpty())
933 return false;
934
935 if (viewportUpdateMode != QGraphicsView::MinimalViewportUpdate
936 && viewportUpdateMode != QGraphicsView::SmartViewportUpdate) {
937 // No point in updating with QRegion granularity; use the rect instead.
938 return updateRectF(xform.mapRect(rect));
939 }
940
941 // Update mode is either Minimal or Smart, so we have to do a potentially slow operation,
942 // which is clearly documented here: QGraphicsItem::setBoundingRegionGranularity.
943 const QRegion region = xform.map(QRegion(rect.toAlignedRect()));
944 QRect viewRect = region.boundingRect();
945 const bool dontAdjustForAntialiasing = optimizationFlags & QGraphicsView::DontAdjustForAntialiasing;
946 if (dontAdjustForAntialiasing)
947 viewRect.adjust(-1, -1, 1, 1);
948 else
949 viewRect.adjust(-2, -2, 2, 2);
950 if (!intersectsViewport(viewRect, viewport->width(), viewport->height()))
951 return false; // Update region for sure outside viewport.
952
953 const QVector<QRect> &rects = region.rects();
954 for (int i = 0; i < rects.size(); ++i) {
955 viewRect = rects.at(i);
956 if (dontAdjustForAntialiasing)
957 viewRect.adjust(-1, -1, 1, 1);
958 else
959 viewRect.adjust(-2, -2, 2, 2);
960 if (hasUpdateClip)
961 viewRect &= updateClip;
962 dirtyRegion += viewRect;
963 }
964
965 return true;
966}
967
968// NB! Assumes the rect 'r' is already aligned and adjusted for antialiasing.
969// For QRectF use updateRectF(const QRectF &) to ensure proper adjustments.
970bool QGraphicsViewPrivate::updateRect(const QRect &r)
971{
972 if (fullUpdatePending || viewportUpdateMode == QGraphicsView::NoViewportUpdate
973 || !intersectsViewport(r, viewport->width(), viewport->height())) {
974 return false;
975 }
976
977 switch (viewportUpdateMode) {
978 case QGraphicsView::FullViewportUpdate:
979 fullUpdatePending = true;
980 viewport->update();
981 break;
982 case QGraphicsView::BoundingRectViewportUpdate:
983 if (hasUpdateClip)
984 QRect_unite(&dirtyBoundingRect, r & updateClip);
985 else
986 QRect_unite(&dirtyBoundingRect, r);
987 if (containsViewport(dirtyBoundingRect, viewport->width(), viewport->height())) {
988 fullUpdatePending = true;
989 viewport->update();
990 }
991 break;
992 case QGraphicsView::SmartViewportUpdate: // ### DEPRECATE
993 case QGraphicsView::MinimalViewportUpdate:
994 if (hasUpdateClip)
995 dirtyRegion += r & updateClip;
996 else
997 dirtyRegion += r;
998 break;
999 default:
1000 break;
1001 }
1002
1003 return true;
1004}
1005
1006QStyleOptionGraphicsItem *QGraphicsViewPrivate::allocStyleOptionsArray(int numItems)
1007{
1008 if (mustAllocateStyleOptions || (numItems > styleOptions.capacity()))
1009 // too many items, let's allocate on-the-fly
1010 return new QStyleOptionGraphicsItem[numItems];
1011
1012 // expand only whenever necessary
1013 if (numItems > styleOptions.size())
1014 styleOptions.resize(numItems);
1015
1016 mustAllocateStyleOptions = true;
1017 return styleOptions.data();
1018}
1019
1020void QGraphicsViewPrivate::freeStyleOptionsArray(QStyleOptionGraphicsItem *array)
1021{
1022 mustAllocateStyleOptions = false;
1023 if (array != styleOptions.data())
1024 delete [] array;
1025}
1026
1027extern QPainterPath qt_regionToPath(const QRegion &region);
1028
1029/*!
1030 ### Adjustments in findItems: mapToScene(QRect) forces us to adjust the
1031 input rectangle by (0, 0, 1, 1), because it uses QRect::bottomRight()
1032 (etc) when mapping the rectangle to a polygon (which is _wrong_). In
1033 addition, as QGraphicsItem::boundingRect() is defined in logical space,
1034 but the default pen for QPainter is cosmetic with a width of 0, QPainter
1035 is at risk of painting 1 pixel outside the bounding rect. Therefore we
1036 must search for items with an adjustment of (-1, -1, 1, 1).
1037*/
1038QList<QGraphicsItem *> QGraphicsViewPrivate::findItems(const QRegion &exposedRegion, bool *allItems,
1039 const QTransform &viewTransform) const
1040{
1041 Q_Q(const QGraphicsView);
1042
1043 // Step 1) If all items are contained within the expose region, then
1044 // return a list of all visible items. ### the scene's growing bounding
1045 // rect does not take into account untransformable items.
1046 const QRectF exposedRegionSceneBounds = q->mapToScene(exposedRegion.boundingRect().adjusted(-1, -1, 1, 1))
1047 .boundingRect();
1048 if (exposedRegionSceneBounds.contains(scene->sceneRect())) {
1049 Q_ASSERT(allItems);
1050 *allItems = true;
1051
1052 // All items are guaranteed within the exposed region.
1053 return scene->items(Qt::AscendingOrder);
1054 }
1055
1056 // Step 2) If the expose region is a simple rect and the view is only
1057 // translated or scaled, search for items using
1058 // QGraphicsScene::items(QRectF).
1059 bool simpleRectLookup = exposedRegion.rectCount() == 1 && matrix.type() <= QTransform::TxScale;
1060 if (simpleRectLookup) {
1061 return scene->items(exposedRegionSceneBounds,
1062 Qt::IntersectsItemBoundingRect,
1063 Qt::AscendingOrder, viewTransform);
1064 }
1065
1066 // If the region is complex or the view has a complex transform, adjust
1067 // the expose region, convert it to a path, and then search for items
1068 // using QGraphicsScene::items(QPainterPath);
1069 QRegion adjustedRegion;
1070 foreach (const QRect &r, exposedRegion.rects())
1071 adjustedRegion += r.adjusted(-1, -1, 1, 1);
1072
1073 const QPainterPath exposedScenePath(q->mapToScene(qt_regionToPath(adjustedRegion)));
1074 return scene->items(exposedScenePath, Qt::IntersectsItemBoundingRect,
1075 Qt::AscendingOrder, viewTransform);
1076}
1077
1078/*!
1079 \internal
1080
1081 Enables input methods for the view if and only if the current focus item of
1082 the scene accepts input methods. Call function whenever that condition has
1083 potentially changed.
1084*/
1085void QGraphicsViewPrivate::updateInputMethodSensitivity()
1086{
1087 Q_Q(QGraphicsView);
1088 QGraphicsItem *focusItem = 0;
1089 bool enabled = scene && (focusItem = scene->focusItem())
1090 && (focusItem->d_ptr->flags & QGraphicsItem::ItemAcceptsInputMethod);
1091 q->setAttribute(Qt::WA_InputMethodEnabled, enabled);
1092 q->viewport()->setAttribute(Qt::WA_InputMethodEnabled, enabled);
1093
1094 if (!enabled) {
1095 q->setInputMethodHints(0);
1096 return;
1097 }
1098
1099 QGraphicsProxyWidget *proxy = focusItem->d_ptr->isWidget && focusItem->d_ptr->isProxyWidget()
1100 ? static_cast<QGraphicsProxyWidget *>(focusItem) : 0;
1101 if (!proxy) {
1102 q->setInputMethodHints(focusItem->inputMethodHints());
1103 } else if (QWidget *widget = proxy->widget()) {
1104 if (QWidget *fw = widget->focusWidget())
1105 widget = fw;
1106 q->setInputMethodHints(widget->inputMethodHints());
1107 } else {
1108 q->setInputMethodHints(0);
1109 }
1110}
1111
1112/*!
1113 Constructs a QGraphicsView. \a parent is passed to QWidget's constructor.
1114*/
1115QGraphicsView::QGraphicsView(QWidget *parent)
1116 : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
1117{
1118 setViewport(0);
1119 setAcceptDrops(true);
1120 setBackgroundRole(QPalette::Base);
1121 // Investigate leaving these disabled by default.
1122 setAttribute(Qt::WA_InputMethodEnabled);
1123 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1124}
1125
1126/*!
1127 Constructs a QGraphicsView and sets the visualized scene to \a
1128 scene. \a parent is passed to QWidget's constructor.
1129*/
1130QGraphicsView::QGraphicsView(QGraphicsScene *scene, QWidget *parent)
1131 : QAbstractScrollArea(*new QGraphicsViewPrivate, parent)
1132{
1133 setScene(scene);
1134 setViewport(0);
1135 setAcceptDrops(true);
1136 setBackgroundRole(QPalette::Base);
1137 // Investigate leaving these disabled by default.
1138 setAttribute(Qt::WA_InputMethodEnabled);
1139 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1140}
1141
1142/*!
1143 \internal
1144 */
1145QGraphicsView::QGraphicsView(QGraphicsViewPrivate &dd, QWidget *parent)
1146 : QAbstractScrollArea(dd, parent)
1147{
1148 setViewport(0);
1149 setAcceptDrops(true);
1150 setBackgroundRole(QPalette::Base);
1151 // Investigate leaving these disabled by default.
1152 setAttribute(Qt::WA_InputMethodEnabled);
1153 viewport()->setAttribute(Qt::WA_InputMethodEnabled);
1154}
1155
1156/*!
1157 Destructs the QGraphicsView object.
1158*/
1159QGraphicsView::~QGraphicsView()
1160{
1161 Q_D(QGraphicsView);
1162 if (d->scene)
1163 d->scene->d_func()->views.removeAll(this);
1164 delete d->lastDragDropEvent;
1165}
1166
1167/*!
1168 \reimp
1169*/
1170QSize QGraphicsView::sizeHint() const
1171{
1172 Q_D(const QGraphicsView);
1173 if (d->scene) {
1174 QSizeF baseSize = d->matrix.mapRect(sceneRect()).size();
1175 baseSize += QSizeF(d->frameWidth * 2, d->frameWidth * 2);
1176 return baseSize.boundedTo((3 * QApplication::desktop()->size()) / 4).toSize();
1177 }
1178 return QAbstractScrollArea::sizeHint();
1179}
1180
1181/*!
1182 \property QGraphicsView::renderHints
1183 \brief the default render hints for the view
1184
1185 These hints are
1186 used to initialize QPainter before each visible item is drawn. QPainter
1187 uses render hints to toggle rendering features such as antialiasing and
1188 smooth pixmap transformation.
1189
1190 QPainter::TextAntialiasing is enabled by default.
1191
1192 Example:
1193
1194 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 1
1195*/
1196QPainter::RenderHints QGraphicsView::renderHints() const
1197{
1198 Q_D(const QGraphicsView);
1199 return d->renderHints;
1200}
1201void QGraphicsView::setRenderHints(QPainter::RenderHints hints)
1202{
1203 Q_D(QGraphicsView);
1204 if (hints == d->renderHints)
1205 return;
1206 d->renderHints = hints;
1207 d->updateAll();
1208}
1209
1210/*!
1211 If \a enabled is true, the render hint \a hint is enabled; otherwise it
1212 is disabled.
1213
1214 \sa renderHints
1215*/
1216void QGraphicsView::setRenderHint(QPainter::RenderHint hint, bool enabled)
1217{
1218 Q_D(QGraphicsView);
1219 QPainter::RenderHints oldHints = d->renderHints;
1220 if (enabled)
1221 d->renderHints |= hint;
1222 else
1223 d->renderHints &= ~hint;
1224 if (oldHints != d->renderHints)
1225 d->updateAll();
1226}
1227
1228/*!
1229 \property QGraphicsView::alignment
1230 \brief the alignment of the scene in the view when the whole
1231 scene is visible.
1232
1233 If the whole scene is visible in the view, (i.e., there are no visible
1234 scroll bars,) the view's alignment will decide where the scene will be
1235 rendered in the view. For example, if the alignment is Qt::AlignCenter,
1236 which is default, the scene will be centered in the view, and if the
1237 alignment is (Qt::AlignLeft | Qt::AlignTop), the scene will be rendered in
1238 the top-left corner of the view.
1239*/
1240Qt::Alignment QGraphicsView::alignment() const
1241{
1242 Q_D(const QGraphicsView);
1243 return d->alignment;
1244}
1245void QGraphicsView::setAlignment(Qt::Alignment alignment)
1246{
1247 Q_D(QGraphicsView);
1248 if (d->alignment != alignment) {
1249 d->alignment = alignment;
1250 d->recalculateContentSize();
1251 }
1252}
1253
1254/*!
1255 \property QGraphicsView::transformationAnchor
1256 \brief how the view should position the scene during transformations.
1257
1258 QGraphicsView uses this property to decide how to position the scene in
1259 the viewport when the transformation matrix changes, and the coordinate
1260 system of the view is transformed. The default behavior, AnchorViewCenter,
1261 ensures that the scene point at the center of the view remains unchanged
1262 during transformations (e.g., when rotating, the scene will appear to
1263 rotate around the center of the view).
1264
1265 Note that the effect of this property is noticeable when only a part of the
1266 scene is visible (i.e., when there are scroll bars). Otherwise, if the
1267 whole scene fits in the view, QGraphicsScene uses the view \l alignment to
1268 position the scene in the view.
1269
1270 \sa alignment, resizeAnchor
1271*/
1272QGraphicsView::ViewportAnchor QGraphicsView::transformationAnchor() const
1273{
1274 Q_D(const QGraphicsView);
1275 return d->transformationAnchor;
1276}
1277void QGraphicsView::setTransformationAnchor(ViewportAnchor anchor)
1278{
1279 Q_D(QGraphicsView);
1280 d->transformationAnchor = anchor;
1281
1282 // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
1283 // in order to have up-to-date information for centering the view.
1284 if (d->transformationAnchor == AnchorUnderMouse)
1285 d->viewport->setMouseTracking(true);
1286}
1287
1288/*!
1289 \property QGraphicsView::resizeAnchor
1290 \brief how the view should position the scene when the view is resized.
1291
1292 QGraphicsView uses this property to decide how to position the scene in
1293 the viewport when the viewport widget's size changes. The default
1294 behavior, NoAnchor, leaves the scene's position unchanged during a resize;
1295 the top-left corner of the view will appear to be anchored while resizing.
1296
1297 Note that the effect of this property is noticeable when only a part of the
1298 scene is visible (i.e., when there are scroll bars). Otherwise, if the
1299 whole scene fits in the view, QGraphicsScene uses the view \l alignment to
1300 position the scene in the view.
1301
1302 \sa alignment, transformationAnchor, Qt::WNorthWestGravity
1303*/
1304QGraphicsView::ViewportAnchor QGraphicsView::resizeAnchor() const
1305{
1306 Q_D(const QGraphicsView);
1307 return d->resizeAnchor;
1308}
1309void QGraphicsView::setResizeAnchor(ViewportAnchor anchor)
1310{
1311 Q_D(QGraphicsView);
1312 d->resizeAnchor = anchor;
1313
1314 // Ensure mouse tracking is enabled in the case we are using AnchorUnderMouse
1315 // in order to have up-to-date information for centering the view.
1316 if (d->resizeAnchor == AnchorUnderMouse)
1317 d->viewport->setMouseTracking(true);
1318}
1319
1320/*!
1321 \property QGraphicsView::viewportUpdateMode
1322 \brief how the viewport should update its contents.
1323
1324 \since 4.3
1325
1326 QGraphicsView uses this property to decide how to update areas of the
1327 scene that have been reexposed or changed. Usually you do not need to
1328 modify this property, but there are some cases where doing so can improve
1329 rendering performance. See the ViewportUpdateMode documentation for
1330 specific details.
1331
1332 The default value is MinimalViewportUpdate, where QGraphicsView will
1333 update as small an area of the viewport as possible when the contents
1334 change.
1335
1336 \sa ViewportUpdateMode, cacheMode
1337*/
1338QGraphicsView::ViewportUpdateMode QGraphicsView::viewportUpdateMode() const
1339{
1340 Q_D(const QGraphicsView);
1341 return d->viewportUpdateMode;
1342}
1343void QGraphicsView::setViewportUpdateMode(ViewportUpdateMode mode)
1344{
1345 Q_D(QGraphicsView);
1346 d->viewportUpdateMode = mode;
1347}
1348
1349/*!
1350 \property QGraphicsView::optimizationFlags
1351 \brief flags that can be used to tune QGraphicsView's performance.
1352
1353 \since 4.3
1354
1355 QGraphicsView uses clipping, extra bounding rect adjustments, and certain
1356 other aids to improve rendering quality and performance for the common
1357 case graphics scene. However, depending on the target platform, the scene,
1358 and the viewport in use, some of these operations can degrade performance.
1359
1360 The effect varies from flag to flag; see the OptimizationFlags
1361 documentation for details.
1362
1363 By default, no optimization flags are enabled.
1364
1365 \sa setOptimizationFlag()
1366*/
1367QGraphicsView::OptimizationFlags QGraphicsView::optimizationFlags() const
1368{
1369 Q_D(const QGraphicsView);
1370 return d->optimizationFlags;
1371}
1372void QGraphicsView::setOptimizationFlags(OptimizationFlags flags)
1373{
1374 Q_D(QGraphicsView);
1375 d->optimizationFlags = flags;
1376}
1377
1378/*!
1379 Enables \a flag if \a enabled is true; otherwise disables \a flag.
1380
1381 \sa optimizationFlags
1382*/
1383void QGraphicsView::setOptimizationFlag(OptimizationFlag flag, bool enabled)
1384{
1385 Q_D(QGraphicsView);
1386 if (enabled)
1387 d->optimizationFlags |= flag;
1388 else
1389 d->optimizationFlags &= ~flag;
1390}
1391
1392/*!
1393 \property QGraphicsView::dragMode
1394 \brief the behavior for dragging the mouse over the scene while
1395 the left mouse button is pressed.
1396
1397 This property defines what should happen when the user clicks on the scene
1398 background and drags the mouse (e.g., scrolling the viewport contents
1399 using a pointing hand cursor, or selecting multiple items with a rubber
1400 band). The default value, NoDrag, does nothing.
1401
1402 This behavior only affects mouse clicks that are not handled by any item.
1403 You can define a custom behavior by creating a subclass of QGraphicsView
1404 and reimplementing mouseMoveEvent().
1405*/
1406QGraphicsView::DragMode QGraphicsView::dragMode() const
1407{
1408 Q_D(const QGraphicsView);
1409 return d->dragMode;
1410}
1411void QGraphicsView::setDragMode(DragMode mode)
1412{
1413 Q_D(QGraphicsView);
1414 if (d->dragMode == mode)
1415 return;
1416
1417#ifndef QT_NO_CURSOR
1418 if (d->dragMode == ScrollHandDrag)
1419 viewport()->unsetCursor();
1420#endif
1421
1422 // If dragMode is unset while dragging, e.g. via a keyEvent, we
1423 // don't unset the handScrolling state. When enabling scrolling
1424 // again the mouseMoveEvent will automatically start scrolling,
1425 // without a mousePress
1426 if (d->dragMode == ScrollHandDrag && mode == NoDrag && d->handScrolling)
1427 d->handScrolling = false;
1428
1429 d->dragMode = mode;
1430
1431#ifndef QT_NO_CURSOR
1432 if (d->dragMode == ScrollHandDrag) {
1433 // Forget the stored viewport cursor when we enter scroll hand drag mode.
1434 d->hasStoredOriginalCursor = false;
1435 viewport()->setCursor(Qt::OpenHandCursor);
1436 }
1437#endif
1438}
1439
1440#ifndef QT_NO_RUBBERBAND
1441/*!
1442 \property QGraphicsView::rubberBandSelectionMode
1443 \brief the behavior for selecting items with a rubber band selection rectangle.
1444 \since 4.3
1445
1446 This property defines how items are selected when using the RubberBandDrag
1447 drag mode.
1448
1449 The default value is Qt::IntersectsItemShape; all items whose shape
1450 intersects with or is contained by the rubber band are selected.
1451
1452 \sa dragMode, items()
1453*/
1454Qt::ItemSelectionMode QGraphicsView::rubberBandSelectionMode() const
1455{
1456 Q_D(const QGraphicsView);
1457 return d->rubberBandSelectionMode;
1458}
1459void QGraphicsView::setRubberBandSelectionMode(Qt::ItemSelectionMode mode)
1460{
1461 Q_D(QGraphicsView);
1462 d->rubberBandSelectionMode = mode;
1463}
1464#endif
1465
1466/*!
1467 \property QGraphicsView::cacheMode
1468 \brief which parts of the view are cached
1469
1470 QGraphicsView can cache pre-rendered content in a QPixmap, which is then
1471 drawn onto the viewport. The purpose of such caching is to speed up the
1472 total rendering time for areas that are slow to render. Texture, gradient
1473 and alpha blended backgrounds, for example, can be notibly slow to render;
1474 especially with a transformed view. The CacheBackground flag enables
1475 caching of the view's background. For example:
1476
1477 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 2
1478
1479 The cache is invalidated every time the view is transformed. However, when
1480 scrolling, only partial invalidation is required.
1481
1482 By default, nothing is cached.
1483
1484 \sa resetCachedContent(), QPixmapCache
1485*/
1486QGraphicsView::CacheMode QGraphicsView::cacheMode() const
1487{
1488 Q_D(const QGraphicsView);
1489 return d->cacheMode;
1490}
1491void QGraphicsView::setCacheMode(CacheMode mode)
1492{
1493 Q_D(QGraphicsView);
1494 if (mode == d->cacheMode)
1495 return;
1496 d->cacheMode = mode;
1497 resetCachedContent();
1498}
1499
1500/*!
1501 Resets any cached content. Calling this function will clear
1502 QGraphicsView's cache. If the current cache mode is \l CacheNone, this
1503 function does nothing.
1504
1505 This function is called automatically for you when the backgroundBrush or
1506 QGraphicsScene::backgroundBrush properties change; you only need to call
1507 this function if you have reimplemented QGraphicsScene::drawBackground()
1508 or QGraphicsView::drawBackground() to draw a custom background, and need
1509 to trigger a full redraw.
1510
1511 \sa cacheMode()
1512*/
1513void QGraphicsView::resetCachedContent()
1514{
1515 Q_D(QGraphicsView);
1516 if (d->cacheMode == CacheNone)
1517 return;
1518
1519 if (d->cacheMode & CacheBackground) {
1520 // Background caching is enabled.
1521 d->mustResizeBackgroundPixmap = true;
1522 d->updateAll();
1523 } else if (d->mustResizeBackgroundPixmap) {
1524 // Background caching is disabled.
1525 // Cleanup, free some resources.
1526 d->mustResizeBackgroundPixmap = false;
1527 d->backgroundPixmap = QPixmap();
1528 d->backgroundPixmapExposed = QRegion();
1529 }
1530}
1531
1532/*!
1533 Invalidates and schedules a redraw of \a layers inside \a rect. \a rect is
1534 in scene coordinates. Any cached content for \a layers inside \a rect is
1535 unconditionally invalidated and redrawn.
1536
1537 You can call this function to notify QGraphicsView of changes to the
1538 background or the foreground of the scene. It is commonly used for scenes
1539 with tile-based backgrounds to notify changes when QGraphicsView has
1540 enabled background caching.
1541
1542 Note that QGraphicsView currently supports background caching only (see
1543 QGraphicsView::CacheBackground). This function is equivalent to calling update() if any
1544 layer but QGraphicsScene::BackgroundLayer is passed.
1545
1546 \sa QGraphicsScene::invalidate(), update()
1547*/
1548void QGraphicsView::invalidateScene(const QRectF &rect, QGraphicsScene::SceneLayers layers)
1549{
1550 Q_D(QGraphicsView);
1551 if ((layers & QGraphicsScene::BackgroundLayer) && !d->mustResizeBackgroundPixmap) {
1552 QRect viewRect = mapFromScene(rect).boundingRect();
1553 if (viewport()->rect().intersects(viewRect)) {
1554 // The updated background area is exposed; schedule this area for
1555 // redrawing.
1556 d->backgroundPixmapExposed += viewRect;
1557 if (d->scene)
1558 d->scene->update(rect);
1559 }
1560 }
1561}
1562
1563/*!
1564 \property QGraphicsView::interactive
1565 \brief whether the view allowed scene interaction.
1566
1567 If enabled, this view is set to allow scene interaction. Otherwise, this
1568 view will not allow interaction, and any mouse or key events are ignored
1569 (i.e., it will act as a read-only view).
1570
1571 By default, this property is true.
1572*/
1573bool QGraphicsView::isInteractive() const
1574{
1575 Q_D(const QGraphicsView);
1576 return d->sceneInteractionAllowed;
1577}
1578void QGraphicsView::setInteractive(bool allowed)
1579{
1580 Q_D(QGraphicsView);
1581 d->sceneInteractionAllowed = allowed;
1582}
1583
1584/*!
1585 Returns a pointer to the scene that is currently visualized in the
1586 view. If no scene is currently visualized, 0 is returned.
1587
1588 \sa setScene()
1589*/
1590QGraphicsScene *QGraphicsView::scene() const
1591{
1592 Q_D(const QGraphicsView);
1593 return d->scene;
1594}
1595
1596/*!
1597 Sets the current scene to \a scene. If \a scene is already being
1598 viewed, this function does nothing.
1599
1600 When a scene is set on a view, the QGraphicsScene::changed() signal
1601 is automatically connected to this view's updateScene() slot, and the
1602 view's scroll bars are adjusted to fit the size of the scene.
1603*/
1604void QGraphicsView::setScene(QGraphicsScene *scene)
1605{
1606 Q_D(QGraphicsView);
1607 if (d->scene == scene)
1608 return;
1609
1610 // Always update the viewport when the scene changes.
1611 d->updateAll();
1612
1613 // Remove the previously assigned scene.
1614 if (d->scene) {
1615 disconnect(d->scene, SIGNAL(changed(QList<QRectF>)),
1616 this, SLOT(updateScene(QList<QRectF>)));
1617 disconnect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
1618 this, SLOT(updateSceneRect(QRectF)));
1619 d->scene->d_func()->removeView(this);
1620 d->connectedToScene = false;
1621
1622 if (isActiveWindow() && isVisible()) {
1623 QEvent windowDeactivate(QEvent::WindowDeactivate);
1624 QApplication::sendEvent(d->scene, &windowDeactivate);
1625 }
1626 if(hasFocus())
1627 d->scene->clearFocus();
1628 }
1629
1630 // Assign the new scene and update the contents (scrollbars, etc.)).
1631 if ((d->scene = scene)) {
1632 connect(d->scene, SIGNAL(sceneRectChanged(QRectF)),
1633 this, SLOT(updateSceneRect(QRectF)));
1634 d->updateSceneSlotReimplementedChecked = false;
1635 d->scene->d_func()->addView(this);
1636 d->recalculateContentSize();
1637 d->lastCenterPoint = sceneRect().center();
1638 d->keepLastCenterPoint = true;
1639 // We are only interested in mouse tracking if items accept
1640 // hover events or use non-default cursors.
1641 if (!d->scene->d_func()->allItemsIgnoreHoverEvents
1642 || !d->scene->d_func()->allItemsUseDefaultCursor) {
1643 d->viewport->setMouseTracking(true);
1644 }
1645
1646 // enable touch events if any items is interested in them
1647 if (!d->scene->d_func()->allItemsIgnoreTouchEvents)
1648 d->viewport->setAttribute(Qt::WA_AcceptTouchEvents);
1649
1650 if (isActiveWindow() && isVisible()) {
1651 QEvent windowActivate(QEvent::WindowActivate);
1652 QApplication::sendEvent(d->scene, &windowActivate);
1653 }
1654 } else {
1655 d->recalculateContentSize();
1656 }
1657
1658 d->updateInputMethodSensitivity();
1659
1660 if (d->scene && hasFocus())
1661 d->scene->setFocus();
1662}
1663
1664/*!
1665 \property QGraphicsView::sceneRect
1666 \brief the area of the scene visualized by this view.
1667
1668 The scene rectangle defines the extent of the scene, and in the view's case,
1669 this means the area of the scene that you can navigate using the scroll
1670 bars.
1671
1672 If unset, or if a null QRectF is set, this property has the same value as
1673 QGraphicsScene::sceneRect, and it changes with
1674 QGraphicsScene::sceneRect. Otherwise, the view's scene rect is unaffected
1675 by the scene.
1676
1677 Note that, although the scene supports a virtually unlimited size, the
1678 range of the scroll bars will never exceed the range of an integer
1679 (INT_MIN, INT_MAX). When the scene is larger than the scroll bars' values,
1680 you can choose to use translate() to navigate the scene instead.
1681
1682 By default, this property contains a rectangle at the origin with zero
1683 width and height.
1684
1685 \sa QGraphicsScene::sceneRect
1686*/
1687QRectF QGraphicsView::sceneRect() const
1688{
1689 Q_D(const QGraphicsView);
1690 if (d->hasSceneRect)
1691 return d->sceneRect;
1692 if (d->scene)
1693 return d->scene->sceneRect();
1694 return QRectF();
1695}
1696void QGraphicsView::setSceneRect(const QRectF &rect)
1697{
1698 Q_D(QGraphicsView);
1699 d->hasSceneRect = !rect.isNull();
1700 d->sceneRect = rect;
1701 d->recalculateContentSize();
1702}
1703
1704/*!
1705 Returns the current transformation matrix for the view. If no current
1706 transformation is set, the identity matrix is returned.
1707
1708 \sa setMatrix(), transform(), rotate(), scale(), shear(), translate()
1709*/
1710QMatrix QGraphicsView::matrix() const
1711{
1712 Q_D(const QGraphicsView);
1713 return d->matrix.toAffine();
1714}
1715
1716/*!
1717 Sets the view's current transformation matrix to \a matrix.
1718
1719 If \a combine is true, then \a matrix is combined with the current matrix;
1720 otherwise, \a matrix \e replaces the current matrix. \a combine is false
1721 by default.
1722
1723 The transformation matrix tranforms the scene into view coordinates. Using
1724 the default transformation, provided by the identity matrix, one pixel in
1725 the view represents one unit in the scene (e.g., a 10x10 rectangular item
1726 is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
1727 applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
1728 then drawn using 20x20 pixels in the view).
1729
1730 Example:
1731
1732 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 3
1733
1734 To simplify interation with items using a transformed view, QGraphicsView
1735 provides mapTo... and mapFrom... functions that can translate between
1736 scene and view coordinates. For example, you can call mapToScene() to map
1737 a view coordinate to a floating point scene coordinate, or mapFromScene()
1738 to map from floating point scene coordinates to view coordinates.
1739
1740 \sa matrix(), setTransform(), rotate(), scale(), shear(), translate()
1741*/
1742void QGraphicsView::setMatrix(const QMatrix &matrix, bool combine)
1743{
1744 setTransform(QTransform(matrix), combine);
1745}
1746
1747/*!
1748 Resets the view transformation matrix to the identity matrix.
1749
1750 \sa resetTransform()
1751*/
1752void QGraphicsView::resetMatrix()
1753{
1754 resetTransform();
1755}
1756
1757/*!
1758 Rotates the current view transformation \a angle degrees clockwise.
1759
1760 \sa setTransform(), transform(), scale(), shear(), translate()
1761*/
1762void QGraphicsView::rotate(qreal angle)
1763{
1764 Q_D(QGraphicsView);
1765 QTransform matrix = d->matrix;
1766 matrix.rotate(angle);
1767 setTransform(matrix);
1768}
1769
1770/*!
1771 Scales the current view transformation by (\a sx, \a sy).
1772
1773 \sa setTransform(), transform(), rotate(), shear(), translate()
1774*/
1775void QGraphicsView::scale(qreal sx, qreal sy)
1776{
1777 Q_D(QGraphicsView);
1778 QTransform matrix = d->matrix;
1779 matrix.scale(sx, sy);
1780 setTransform(matrix);
1781}
1782
1783/*!
1784 Shears the current view transformation by (\a sh, \a sv).
1785
1786 \sa setTransform(), transform(), rotate(), scale(), translate()
1787*/
1788void QGraphicsView::shear(qreal sh, qreal sv)
1789{
1790 Q_D(QGraphicsView);
1791 QTransform matrix = d->matrix;
1792 matrix.shear(sh, sv);
1793 setTransform(matrix);
1794}
1795
1796/*!
1797 Translates the current view transformation by (\a dx, \a dy).
1798
1799 \sa setTransform(), transform(), rotate(), shear()
1800*/
1801void QGraphicsView::translate(qreal dx, qreal dy)
1802{
1803 Q_D(QGraphicsView);
1804 QTransform matrix = d->matrix;
1805 matrix.translate(dx, dy);
1806 setTransform(matrix);
1807}
1808
1809/*!
1810 Scrolls the contents of the viewport to ensure that the scene
1811 coordinate \a pos, is centered in the view.
1812
1813 Because \a pos is a floating point coordinate, and the scroll bars operate
1814 on integer coordinates, the centering is only an approximation.
1815
1816 \note If the item is close to or outside the border, it will be visible
1817 in the view, but not centered.
1818
1819 \sa ensureVisible()
1820*/
1821void QGraphicsView::centerOn(const QPointF &pos)
1822{
1823 Q_D(QGraphicsView);
1824 qreal width = viewport()->width();
1825 qreal height = viewport()->height();
1826 QPointF viewPoint = d->matrix.map(pos);
1827 QPointF oldCenterPoint = pos;
1828
1829 if (!d->leftIndent) {
1830 if (isRightToLeft()) {
1831 qint64 horizontal = 0;
1832 horizontal += horizontalScrollBar()->minimum();
1833 horizontal += horizontalScrollBar()->maximum();
1834 horizontal -= int(viewPoint.x() - width / 2.0);
1835 horizontalScrollBar()->setValue(horizontal);
1836 } else {
1837 horizontalScrollBar()->setValue(int(viewPoint.x() - width / 2.0));
1838 }
1839 }
1840 if (!d->topIndent)
1841 verticalScrollBar()->setValue(int(viewPoint.y() - height / 2.0));
1842 d->lastCenterPoint = oldCenterPoint;
1843}
1844
1845/*!
1846 \fn QGraphicsView::centerOn(qreal x, qreal y)
1847 \overload
1848
1849 This function is provided for convenience. It's equivalent to calling
1850 centerOn(QPointF(\a x, \a y)).
1851*/
1852
1853/*!
1854 \overload
1855
1856 Scrolls the contents of the viewport to ensure that \a item
1857 is centered in the view.
1858
1859 \sa ensureVisible()
1860*/
1861void QGraphicsView::centerOn(const QGraphicsItem *item)
1862{
1863 centerOn(item->sceneBoundingRect().center());
1864}
1865
1866/*!
1867 Scrolls the contents of the viewport so that the scene rectangle \a rect
1868 is visible, with margins specified in pixels by \a xmargin and \a
1869 ymargin. If the specified rect cannot be reached, the contents are
1870 scrolled to the nearest valid position. The default value for both margins
1871 is 50 pixels.
1872
1873 \sa centerOn()
1874*/
1875void QGraphicsView::ensureVisible(const QRectF &rect, int xmargin, int ymargin)
1876{
1877 Q_D(QGraphicsView);
1878 qreal width = viewport()->width();
1879 qreal height = viewport()->height();
1880 QRectF viewRect = d->matrix.mapRect(rect);
1881
1882 qreal left = d->horizontalScroll();
1883 qreal right = left + width;
1884 qreal top = d->verticalScroll();
1885 qreal bottom = top + height;
1886
1887 if (viewRect.left() <= left + xmargin) {
1888 // need to scroll from the left
1889 if (!d->leftIndent)
1890 horizontalScrollBar()->setValue(int(viewRect.left() - xmargin - 0.5));
1891 }
1892 if (viewRect.right() >= right - xmargin) {
1893 // need to scroll from the right
1894 if (!d->leftIndent)
1895 horizontalScrollBar()->setValue(int(viewRect.right() - width + xmargin + 0.5));
1896 }
1897 if (viewRect.top() <= top + ymargin) {
1898 // need to scroll from the top
1899 if (!d->topIndent)
1900 verticalScrollBar()->setValue(int(viewRect.top() - ymargin - 0.5));
1901 }
1902 if (viewRect.bottom() >= bottom - ymargin) {
1903 // need to scroll from the bottom
1904 if (!d->topIndent)
1905 verticalScrollBar()->setValue(int(viewRect.bottom() - height + ymargin + 0.5));
1906 }
1907}
1908
1909/*!
1910 \fn QGraphicsView::ensureVisible(qreal x, qreal y, qreal w, qreal h,
1911 int xmargin, int ymargin)
1912 \overload
1913
1914 This function is provided for convenience. It's equivalent to calling
1915 ensureVisible(QRectF(\a x, \a y, \a w, \a h), \a xmargin, \a ymargin).
1916*/
1917
1918/*!
1919 \overload
1920
1921 Scrolls the contents of the viewport so that the center of item \a item is
1922 visible, with margins specified in pixels by \a xmargin and \a ymargin. If
1923 the specified point cannot be reached, the contents are scrolled to the
1924 nearest valid position. The default value for both margins is 50 pixels.
1925
1926 \sa centerOn()
1927*/
1928void QGraphicsView::ensureVisible(const QGraphicsItem *item, int xmargin, int ymargin)
1929{
1930 ensureVisible(item->sceneBoundingRect(), xmargin, ymargin);
1931}
1932
1933/*!
1934 Scales the view matrix and scrolls the scroll bars to ensure that the
1935 scene rectangle \a rect fits inside the viewport. \a rect must be inside
1936 the scene rect; otherwise, fitInView() cannot guarantee that the whole
1937 rect is visible.
1938
1939 This function keeps the view's rotation, translation, or shear. The view
1940 is scaled according to \a aspectRatioMode. \a rect will be centered in the
1941 view if it does not fit tightly.
1942
1943 It's common to call fitInView() from inside a reimplementation of
1944 resizeEvent(), to ensure that the whole scene, or parts of the scene,
1945 scales automatically to fit the new size of the viewport as the view is
1946 resized. Note though, that calling fitInView() from inside resizeEvent()
1947 can lead to unwanted resize recursion, if the new transformation toggles
1948 the automatic state of the scrollbars. You can toggle the scrollbar
1949 policies to always on or always off to prevent this (see
1950 horizontalScrollBarPolicy() and verticalScrollBarPolicy()).
1951
1952 If \a rect is empty, or if the viewport is too small, this
1953 function will do nothing.
1954
1955 \sa setTransform(), ensureVisible(), centerOn()
1956*/
1957void QGraphicsView::fitInView(const QRectF &rect, Qt::AspectRatioMode aspectRatioMode)
1958{
1959 Q_D(QGraphicsView);
1960 if (!d->scene || rect.isNull())
1961 return;
1962
1963 // Reset the view scale to 1:1.
1964 QRectF unity = d->matrix.mapRect(QRectF(0, 0, 1, 1));
1965 if (unity.isEmpty())
1966 return;
1967 scale(1 / unity.width(), 1 / unity.height());
1968
1969 // Find the ideal x / y scaling ratio to fit \a rect in the view.
1970 int margin = 2;
1971 QRectF viewRect = viewport()->rect().adjusted(margin, margin, -margin, -margin);
1972 if (viewRect.isEmpty())
1973 return;
1974 QRectF sceneRect = d->matrix.mapRect(rect);
1975 if (sceneRect.isEmpty())
1976 return;
1977 qreal xratio = viewRect.width() / sceneRect.width();
1978 qreal yratio = viewRect.height() / sceneRect.height();
1979
1980 // Respect the aspect ratio mode.
1981 switch (aspectRatioMode) {
1982 case Qt::KeepAspectRatio:
1983 xratio = yratio = qMin(xratio, yratio);
1984 break;
1985 case Qt::KeepAspectRatioByExpanding:
1986 xratio = yratio = qMax(xratio, yratio);
1987 break;
1988 case Qt::IgnoreAspectRatio:
1989 break;
1990 }
1991
1992 // Scale and center on the center of \a rect.
1993 scale(xratio, yratio);
1994 centerOn(rect.center());
1995}
1996
1997/*!
1998 \fn void QGraphicsView::fitInView(qreal x, qreal y, qreal w, qreal h,
1999 Qt::AspectRatioMode aspectRatioMode = Qt::IgnoreAspectRatio)
2000
2001 \overload
2002
2003 This convenience function is equivalent to calling
2004 fitInView(QRectF(\a x, \a y, \a w, \a h), \a aspectRatioMode).
2005
2006 \sa ensureVisible(), centerOn()
2007*/
2008
2009/*!
2010 \overload
2011
2012 Ensures that \a item fits tightly inside the view, scaling the view
2013 according to \a aspectRatioMode.
2014
2015 \sa ensureVisible(), centerOn()
2016*/
2017void QGraphicsView::fitInView(const QGraphicsItem *item, Qt::AspectRatioMode aspectRatioMode)
2018{
2019 QPainterPath path = item->isClipped() ? item->clipPath() : item->shape();
2020 if (item->d_ptr->hasTranslateOnlySceneTransform()) {
2021 path.translate(item->d_ptr->sceneTransform.dx(), item->d_ptr->sceneTransform.dy());
2022 fitInView(path.boundingRect(), aspectRatioMode);
2023 } else {
2024 fitInView(item->d_ptr->sceneTransform.map(path).boundingRect(), aspectRatioMode);
2025 }
2026}
2027
2028/*!
2029 Renders the \a source rect, which is in view coordinates, from the scene
2030 into \a target, which is in paint device coordinates, using \a
2031 painter. This function is useful for capturing the contents of the view
2032 onto a paint device, such as a QImage (e.g., to take a screenshot), or for
2033 printing to QPrinter. For example:
2034
2035 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 4
2036
2037 If \a source is a null rect, this function will use viewport()->rect() to
2038 determine what to draw. If \a target is a null rect, the full dimensions
2039 of \a painter's paint device (e.g., for a QPrinter, the page size) will be
2040 used.
2041
2042 The source rect contents will be transformed according to \a
2043 aspectRatioMode to fit into the target rect. By default, the aspect ratio
2044 is kept, and \a source is scaled to fit in \a target.
2045
2046 \sa QGraphicsScene::render()
2047*/
2048void QGraphicsView::render(QPainter *painter, const QRectF &target, const QRect &source,
2049 Qt::AspectRatioMode aspectRatioMode)
2050{
2051 // ### Switch to using the recursive rendering algorithm instead.
2052
2053 Q_D(QGraphicsView);
2054 if (!d->scene || !(painter && painter->isActive()))
2055 return;
2056
2057 // Default source rect = viewport rect
2058 QRect sourceRect = source;
2059 if (source.isNull())
2060 sourceRect = viewport()->rect();
2061
2062 // Default target rect = device rect
2063 QRectF targetRect = target;
2064 if (target.isNull()) {
2065 if (painter->device()->devType() == QInternal::Picture)
2066 targetRect = sourceRect;
2067 else
2068 targetRect.setRect(0, 0, painter->device()->width(), painter->device()->height());
2069 }
2070
2071 // Find the ideal x / y scaling ratio to fit \a source into \a target.
2072 qreal xratio = targetRect.width() / sourceRect.width();
2073 qreal yratio = targetRect.height() / sourceRect.height();
2074
2075 // Scale according to the aspect ratio mode.
2076 switch (aspectRatioMode) {
2077 case Qt::KeepAspectRatio:
2078 xratio = yratio = qMin(xratio, yratio);
2079 break;
2080 case Qt::KeepAspectRatioByExpanding:
2081 xratio = yratio = qMax(xratio, yratio);
2082 break;
2083 case Qt::IgnoreAspectRatio:
2084 break;
2085 }
2086
2087 // Find all items to draw, and reverse the list (we want to draw
2088 // in reverse order).
2089 QPolygonF sourceScenePoly = mapToScene(sourceRect.adjusted(-1, -1, 1, 1));
2090 QList<QGraphicsItem *> itemList = d->scene->items(sourceScenePoly,
2091 Qt::IntersectsItemBoundingRect);
2092 QGraphicsItem **itemArray = new QGraphicsItem *[itemList.size()];
2093 int numItems = itemList.size();
2094 for (int i = 0; i < numItems; ++i)
2095 itemArray[numItems - i - 1] = itemList.at(i);
2096 itemList.clear();
2097
2098 // Setup painter matrix.
2099 QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
2100 QTransform painterMatrix = d->matrix * moveMatrix;
2101 painterMatrix *= QTransform()
2102 .translate(targetRect.left(), targetRect.top())
2103 .scale(xratio, yratio)
2104 .translate(-sourceRect.left(), -sourceRect.top());
2105
2106 // Generate the style options
2107 QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
2108 for (int i = 0; i < numItems; ++i)
2109 itemArray[i]->d_ptr->initStyleOption(&styleOptionArray[i], painterMatrix, targetRect.toRect());
2110
2111 painter->save();
2112
2113 // Clip in device coordinates to avoid QRegion transformations.
2114 painter->setClipRect(targetRect);
2115 QPainterPath path;
2116 path.addPolygon(sourceScenePoly);
2117 path.closeSubpath();
2118 painter->setClipPath(painterMatrix.map(path), Qt::IntersectClip);
2119
2120 // Transform the painter.
2121 painter->setTransform(painterMatrix, true);
2122
2123 // Render the scene.
2124 QRectF sourceSceneRect = sourceScenePoly.boundingRect();
2125 drawBackground(painter, sourceSceneRect);
2126 drawItems(painter, numItems, itemArray, styleOptionArray);
2127 drawForeground(painter, sourceSceneRect);
2128
2129 delete [] itemArray;
2130 d->freeStyleOptionsArray(styleOptionArray);
2131
2132 painter->restore();
2133}
2134
2135/*!
2136 Returns a list of all the items in the associated scene, in descending
2137 stacking order (i.e., the first item in the returned list is the uppermost
2138 item).
2139
2140 \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
2141*/
2142QList<QGraphicsItem *> QGraphicsView::items() const
2143{
2144 Q_D(const QGraphicsView);
2145 if (!d->scene)
2146 return QList<QGraphicsItem *>();
2147 return d->scene->items();
2148}
2149
2150/*!
2151 Returns a list of all the items at the position \a pos in the view. The
2152 items are listed in descending stacking order (i.e., the first item in the
2153 list is the uppermost item, and the last item is the lowermost item). \a
2154 pos is in viewport coordinates.
2155
2156 This function is most commonly called from within mouse event handlers in
2157 a subclass in QGraphicsView. \a pos is in untransformed viewport
2158 coordinates, just like QMouseEvent::pos().
2159
2160 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 5
2161
2162 \sa QGraphicsScene::items(), {QGraphicsItem#Sorting}{Sorting}
2163*/
2164QList<QGraphicsItem *> QGraphicsView::items(const QPoint &pos) const
2165{
2166 Q_D(const QGraphicsView);
2167 if (!d->scene)
2168 return QList<QGraphicsItem *>();
2169 // ### Unify these two, and use the items(QPointF) version in
2170 // QGraphicsScene instead. The scene items function could use the viewport
2171 // transform to map the point to a rect/polygon.
2172 if ((d->identityMatrix || d->matrix.type() <= QTransform::TxScale)) {
2173 // Use the rect version
2174 QTransform xinv = viewportTransform().inverted();
2175 return d->scene->items(xinv.mapRect(QRectF(pos.x(), pos.y(), 1, 1)),
2176 Qt::IntersectsItemShape,
2177 Qt::DescendingOrder,
2178 viewportTransform());
2179 }
2180 // Use the polygon version
2181 return d->scene->items(mapToScene(pos.x(), pos.y(), 1, 1),
2182 Qt::IntersectsItemShape,
2183 Qt::DescendingOrder,
2184 viewportTransform());
2185}
2186
2187/*!
2188 \fn QGraphicsView::items(int x, int y) const
2189
2190 This function is provided for convenience. It's equivalent to calling
2191 items(QPoint(\a x, \a y)).
2192*/
2193
2194/*!
2195 \overload
2196
2197 Returns a list of all the items that, depending on \a mode, are either
2198 contained by or intersect with \a rect. \a rect is in viewport
2199 coordinates.
2200
2201 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2202 exact shape intersects with or is contained by \a rect are returned.
2203
2204 The items are sorted in descending stacking order (i.e., the first item in
2205 the returned list is the uppermost item).
2206
2207 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2208*/
2209QList<QGraphicsItem *> QGraphicsView::items(const QRect &rect, Qt::ItemSelectionMode mode) const
2210{
2211 Q_D(const QGraphicsView);
2212 if (!d->scene)
2213 return QList<QGraphicsItem *>();
2214 return d->scene->items(mapToScene(rect), mode, Qt::DescendingOrder, viewportTransform());
2215}
2216
2217/*!
2218 \fn QList<QGraphicsItem *> QGraphicsView::items(int x, int y, int w, int h, Qt::ItemSelectionMode mode) const
2219 \since 4.3
2220
2221 This convenience function is equivalent to calling items(QRectF(\a x, \a
2222 y, \a w, \a h), \a mode).
2223*/
2224
2225/*!
2226 \overload
2227
2228 Returns a list of all the items that, depending on \a mode, are either
2229 contained by or intersect with \a polygon. \a polygon is in viewport
2230 coordinates.
2231
2232 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2233 exact shape intersects with or is contained by \a polygon are returned.
2234
2235 The items are sorted by descending stacking order (i.e., the first item in
2236 the returned list is the uppermost item).
2237
2238 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2239*/
2240QList<QGraphicsItem *> QGraphicsView::items(const QPolygon &polygon, Qt::ItemSelectionMode mode) const
2241{
2242 Q_D(const QGraphicsView);
2243 if (!d->scene)
2244 return QList<QGraphicsItem *>();
2245 return d->scene->items(mapToScene(polygon), mode, Qt::DescendingOrder, viewportTransform());
2246}
2247
2248/*!
2249 \overload
2250
2251 Returns a list of all the items that, depending on \a mode, are either
2252 contained by or intersect with \a path. \a path is in viewport
2253 coordinates.
2254
2255 The default value for \a mode is Qt::IntersectsItemShape; all items whose
2256 exact shape intersects with or is contained by \a path are returned.
2257
2258 \sa itemAt(), items(), mapToScene(), {QGraphicsItem#Sorting}{Sorting}
2259*/
2260QList<QGraphicsItem *> QGraphicsView::items(const QPainterPath &path, Qt::ItemSelectionMode mode) const
2261{
2262 Q_D(const QGraphicsView);
2263 if (!d->scene)
2264 return QList<QGraphicsItem *>();
2265 return d->scene->items(mapToScene(path), mode, Qt::DescendingOrder, viewportTransform());
2266}
2267
2268/*!
2269 Returns the item at position \a pos, which is in viewport coordinates.
2270 If there are several items at this position, this function returns
2271 the topmost item.
2272
2273 Example:
2274
2275 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 6
2276
2277 \sa items(), {QGraphicsItem#Sorting}{Sorting}
2278*/
2279QGraphicsItem *QGraphicsView::itemAt(const QPoint &pos) const
2280{
2281 Q_D(const QGraphicsView);
2282 if (!d->scene)
2283 return 0;
2284 QList<QGraphicsItem *> itemsAtPos = items(pos);
2285 return itemsAtPos.isEmpty() ? 0 : itemsAtPos.first();
2286}
2287
2288/*!
2289 \overload
2290 \fn QGraphicsItem *QGraphicsView::itemAt(int x, int y) const
2291
2292 This function is provided for convenience. It's equivalent to
2293 calling itemAt(QPoint(\a x, \a y)).
2294*/
2295
2296/*!
2297 Returns the viewport coordinate \a point mapped to scene coordinates.
2298
2299 Note: It can be useful to map the whole rectangle covered by the pixel at
2300 \a point instead of the point itself. To do this, you can call
2301 mapToScene(QRect(\a point, QSize(2, 2))).
2302
2303 \sa mapFromScene()
2304*/
2305QPointF QGraphicsView::mapToScene(const QPoint &point) const
2306{
2307 Q_D(const QGraphicsView);
2308 QPointF p = point;
2309 p.rx() += d->horizontalScroll();
2310 p.ry() += d->verticalScroll();
2311 return d->identityMatrix ? p : d->matrix.inverted().map(p);
2312}
2313
2314/*!
2315 \fn QGraphicsView::mapToScene(int x, int y) const
2316
2317 This function is provided for convenience. It's equivalent to calling
2318 mapToScene(QPoint(\a x, \a y)).
2319*/
2320
2321/*!
2322 Returns the viewport rectangle \a rect mapped to a scene coordinate
2323 polygon.
2324
2325 \sa mapFromScene()
2326*/
2327QPolygonF QGraphicsView::mapToScene(const QRect &rect) const
2328{
2329 Q_D(const QGraphicsView);
2330 if (!rect.isValid())
2331 return QPolygonF();
2332
2333 QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
2334 QRect r = rect.adjusted(0, 0, 1, 1);
2335 QPointF tl = scrollOffset + r.topLeft();
2336 QPointF tr = scrollOffset + r.topRight();
2337 QPointF br = scrollOffset + r.bottomRight();
2338 QPointF bl = scrollOffset + r.bottomLeft();
2339
2340 QPolygonF poly(4);
2341 if (!d->identityMatrix) {
2342 QTransform x = d->matrix.inverted();
2343 poly[0] = x.map(tl);
2344 poly[1] = x.map(tr);
2345 poly[2] = x.map(br);
2346 poly[3] = x.map(bl);
2347 } else {
2348 poly[0] = tl;
2349 poly[1] = tr;
2350 poly[2] = br;
2351 poly[3] = bl;
2352 }
2353 return poly;
2354}
2355
2356/*!
2357 \fn QGraphicsView::mapToScene(int x, int y, int w, int h) const
2358
2359 This function is provided for convenience. It's equivalent to calling
2360 mapToScene(QRect(\a x, \a y, \a w, \a h)).
2361*/
2362
2363/*!
2364 Returns the viewport polygon \a polygon mapped to a scene coordinate
2365 polygon.
2366
2367 \sa mapFromScene()
2368*/
2369QPolygonF QGraphicsView::mapToScene(const QPolygon &polygon) const
2370{
2371 QPolygonF poly;
2372 foreach (const QPoint &point, polygon)
2373 poly << mapToScene(point);
2374 return poly;
2375}
2376
2377/*!
2378 Returns the viewport painter path \a path mapped to a scene coordinate
2379 painter path.
2380
2381 \sa mapFromScene()
2382*/
2383QPainterPath QGraphicsView::mapToScene(const QPainterPath &path) const
2384{
2385 Q_D(const QGraphicsView);
2386 QTransform matrix = QTransform::fromTranslate(d->horizontalScroll(), d->verticalScroll());
2387 matrix *= d->matrix.inverted();
2388 return matrix.map(path);
2389}
2390
2391/*!
2392 Returns the scene coordinate \a point to viewport coordinates.
2393
2394 \sa mapToScene()
2395*/
2396QPoint QGraphicsView::mapFromScene(const QPointF &point) const
2397{
2398 Q_D(const QGraphicsView);
2399 QPointF p = d->identityMatrix ? point : d->matrix.map(point);
2400 p.rx() -= d->horizontalScroll();
2401 p.ry() -= d->verticalScroll();
2402 return p.toPoint();
2403}
2404
2405/*!
2406 \fn QGraphicsView::mapFromScene(qreal x, qreal y) const
2407
2408 This function is provided for convenience. It's equivalent to
2409 calling mapFromScene(QPointF(\a x, \a y)).
2410*/
2411
2412/*!
2413 Returns the scene rectangle \a rect to a viewport coordinate
2414 polygon.
2415
2416 \sa mapToScene()
2417*/
2418QPolygon QGraphicsView::mapFromScene(const QRectF &rect) const
2419{
2420 Q_D(const QGraphicsView);
2421 QPointF tl;
2422 QPointF tr;
2423 QPointF br;
2424 QPointF bl;
2425 if (!d->identityMatrix) {
2426 const QTransform &x = d->matrix;
2427 tl = x.map(rect.topLeft());
2428 tr = x.map(rect.topRight());
2429 br = x.map(rect.bottomRight());
2430 bl = x.map(rect.bottomLeft());
2431 } else {
2432 tl = rect.topLeft();
2433 tr = rect.topRight();
2434 br = rect.bottomRight();
2435 bl = rect.bottomLeft();
2436 }
2437 QPointF scrollOffset(d->horizontalScroll(), d->verticalScroll());
2438 tl -= scrollOffset;
2439 tr -= scrollOffset;
2440 br -= scrollOffset;
2441 bl -= scrollOffset;
2442
2443 QPolygon poly(4);
2444 poly[0] = tl.toPoint();
2445 poly[1] = tr.toPoint();
2446 poly[2] = br.toPoint();
2447 poly[3] = bl.toPoint();
2448 return poly;
2449}
2450
2451/*!
2452 \fn QGraphicsView::mapFromScene(qreal x, qreal y, qreal w, qreal h) const
2453
2454 This function is provided for convenience. It's equivalent to
2455 calling mapFromScene(QRectF(\a x, \a y, \a w, \a h)).
2456*/
2457
2458/*!
2459 Returns the scene coordinate polygon \a polygon to a viewport coordinate
2460 polygon.
2461
2462 \sa mapToScene()
2463*/
2464QPolygon QGraphicsView::mapFromScene(const QPolygonF &polygon) const
2465{
2466 QPolygon poly;
2467 foreach (const QPointF &point, polygon)
2468 poly << mapFromScene(point);
2469 return poly;
2470}
2471
2472/*!
2473 Returns the scene coordinate painter path \a path to a viewport coordinate
2474 painter path.
2475
2476 \sa mapToScene()
2477*/
2478QPainterPath QGraphicsView::mapFromScene(const QPainterPath &path) const
2479{
2480 Q_D(const QGraphicsView);
2481 QTransform matrix = d->matrix;
2482 matrix *= QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
2483 return matrix.map(path);
2484}
2485
2486/*!
2487 \reimp
2488*/
2489QVariant QGraphicsView::inputMethodQuery(Qt::InputMethodQuery query) const
2490{
2491 Q_D(const QGraphicsView);
2492 if (!d->scene)
2493 return QVariant();
2494
2495 QVariant value = d->scene->inputMethodQuery(query);
2496 if (value.type() == QVariant::RectF)
2497 value = d->mapRectFromScene(value.toRectF());
2498 else if (value.type() == QVariant::PointF)
2499 value = mapFromScene(value.toPointF());
2500 else if (value.type() == QVariant::Rect)
2501 value = mapFromScene(value.toRect()).boundingRect();
2502 else if (value.type() == QVariant::Point)
2503 value = mapFromScene(value.toPoint());
2504 return value;
2505}
2506
2507/*!
2508 \property QGraphicsView::backgroundBrush
2509 \brief the background brush of the scene.
2510
2511 This property sets the background brush for the scene in this view. It is
2512 used to override the scene's own background, and defines the behavior of
2513 drawBackground(). To provide custom background drawing for this view, you
2514 can reimplement drawBackground() instead.
2515
2516 By default, this property contains a brush with the Qt::NoBrush pattern.
2517
2518 \sa QGraphicsScene::backgroundBrush, foregroundBrush
2519*/
2520QBrush QGraphicsView::backgroundBrush() const
2521{
2522 Q_D(const QGraphicsView);
2523 return d->backgroundBrush;
2524}
2525void QGraphicsView::setBackgroundBrush(const QBrush &brush)
2526{
2527 Q_D(QGraphicsView);
2528 d->backgroundBrush = brush;
2529 d->updateAll();
2530
2531 if (d->cacheMode & CacheBackground) {
2532 // Invalidate the background pixmap
2533 d->mustResizeBackgroundPixmap = true;
2534 }
2535}
2536
2537/*!
2538 \property QGraphicsView::foregroundBrush
2539 \brief the foreground brush of the scene.
2540
2541 This property sets the foreground brush for the scene in this view. It is
2542 used to override the scene's own foreground, and defines the behavior of
2543 drawForeground(). To provide custom foreground drawing for this view, you
2544 can reimplement drawForeground() instead.
2545
2546 By default, this property contains a brush with the Qt::NoBrush pattern.
2547
2548 \sa QGraphicsScene::foregroundBrush, backgroundBrush
2549*/
2550QBrush QGraphicsView::foregroundBrush() const
2551{
2552 Q_D(const QGraphicsView);
2553 return d->foregroundBrush;
2554}
2555void QGraphicsView::setForegroundBrush(const QBrush &brush)
2556{
2557 Q_D(QGraphicsView);
2558 d->foregroundBrush = brush;
2559 d->updateAll();
2560}
2561
2562/*!
2563 Schedules an update of the scene rectangles \a rects.
2564
2565 \sa QGraphicsScene::changed()
2566*/
2567void QGraphicsView::updateScene(const QList<QRectF> &rects)
2568{
2569 // ### Note: Since 4.5, this slot is only called if the user explicitly
2570 // establishes a connection between the scene and the view, as the scene
2571 // and view are no longer connected. We need to keep it working (basically
2572 // leave it as it is), but the new delivery path is through
2573 // QGraphicsScenePrivate::itemUpdate().
2574 Q_D(QGraphicsView);
2575 if (d->fullUpdatePending || d->viewportUpdateMode == QGraphicsView::NoViewportUpdate)
2576 return;
2577
2578 // Extract and reset dirty scene rect info.
2579 QVector<QRect> dirtyViewportRects;
2580 const QVector<QRect> &dirtyRects = d->dirtyRegion.rects();
2581 for (int i = 0; i < dirtyRects.size(); ++i)
2582 dirtyViewportRects += dirtyRects.at(i);
2583 d->dirtyRegion = QRegion();
2584 d->dirtyBoundingRect = QRect();
2585
2586 bool fullUpdate = !d->accelerateScrolling || d->viewportUpdateMode == QGraphicsView::FullViewportUpdate;
2587 bool boundingRectUpdate = (d->viewportUpdateMode == QGraphicsView::BoundingRectViewportUpdate)
2588 || (d->viewportUpdateMode == QGraphicsView::SmartViewportUpdate
2589 && ((dirtyViewportRects.size() + rects.size()) >= QGRAPHICSVIEW_REGION_RECT_THRESHOLD));
2590
2591 QRegion updateRegion;
2592 QRect boundingRect;
2593 QRect viewportRect = viewport()->rect();
2594 bool redraw = false;
2595 QTransform transform = viewportTransform();
2596
2597 // Convert scene rects to viewport rects.
2598 foreach (const QRectF &rect, rects) {
2599 QRect xrect = transform.mapRect(rect).toAlignedRect();
2600 if (!(d->optimizationFlags & DontAdjustForAntialiasing))
2601 xrect.adjust(-2, -2, 2, 2);
2602 else
2603 xrect.adjust(-1, -1, 1, 1);
2604 if (!viewportRect.intersects(xrect))
2605 continue;
2606 dirtyViewportRects << xrect;
2607 }
2608
2609 foreach (const QRect &rect, dirtyViewportRects) {
2610 // Add the exposed rect to the update region. In rect update
2611 // mode, we only count the bounding rect of items.
2612 if (!boundingRectUpdate) {
2613 updateRegion += rect;
2614 } else {
2615 boundingRect |= rect;
2616 }
2617 redraw = true;
2618 if (fullUpdate) {
2619 // If fullUpdate is true and we found a visible dirty rect,
2620 // we're done.
2621 break;
2622 }
2623 }
2624
2625 if (!redraw)
2626 return;
2627
2628 if (fullUpdate)
2629 viewport()->update();
2630 else if (boundingRectUpdate)
2631 viewport()->update(boundingRect);
2632 else
2633 viewport()->update(updateRegion);
2634}
2635
2636/*!
2637 Notifies QGraphicsView that the scene's scene rect has changed. \a rect
2638 is the new scene rect. If the view already has an explicitly set scene
2639 rect, this function does nothing.
2640
2641 \sa sceneRect, QGraphicsScene::sceneRectChanged()
2642*/
2643void QGraphicsView::updateSceneRect(const QRectF &rect)
2644{
2645 Q_D(QGraphicsView);
2646 if (!d->hasSceneRect) {
2647 d->sceneRect = rect;
2648 d->recalculateContentSize();
2649 }
2650}
2651
2652/*!
2653 This slot is called by QAbstractScrollArea after setViewport() has been
2654 called. Reimplement this function in a subclass of QGraphicsView to
2655 initialize the new viewport \a widget before it is used.
2656
2657 \sa setViewport()
2658*/
2659void QGraphicsView::setupViewport(QWidget *widget)
2660{
2661 Q_D(QGraphicsView);
2662
2663 if (!widget) {
2664 qWarning("QGraphicsView::setupViewport: cannot initialize null widget");
2665 return;
2666 }
2667
2668 const bool isGLWidget = widget->inherits("QGLWidget");
2669
2670 d->accelerateScrolling = !(isGLWidget);
2671
2672 widget->setFocusPolicy(Qt::StrongFocus);
2673
2674 if (!isGLWidget) {
2675 // autoFillBackground enables scroll acceleration.
2676 widget->setAutoFillBackground(true);
2677 }
2678
2679 // We are only interested in mouse tracking if items
2680 // accept hover events or use non-default cursors or if
2681 // AnchorUnderMouse is used as transformation or resize anchor.
2682 if ((d->scene && (!d->scene->d_func()->allItemsIgnoreHoverEvents
2683 || !d->scene->d_func()->allItemsUseDefaultCursor))
2684 || d->transformationAnchor == AnchorUnderMouse
2685 || d->resizeAnchor == AnchorUnderMouse) {
2686 widget->setMouseTracking(true);
2687 }
2688
2689 // enable touch events if any items is interested in them
2690 if (d->scene && !d->scene->d_func()->allItemsIgnoreTouchEvents)
2691 widget->setAttribute(Qt::WA_AcceptTouchEvents);
2692
2693#ifndef QT_NO_GESTURES
2694 if (d->scene) {
2695 foreach (Qt::GestureType gesture, d->scene->d_func()->grabbedGestures.keys())
2696 widget->grabGesture(gesture);
2697 }
2698#endif
2699
2700 widget->setAcceptDrops(acceptDrops());
2701}
2702
2703/*!
2704 \reimp
2705*/
2706bool QGraphicsView::event(QEvent *event)
2707{
2708 Q_D(QGraphicsView);
2709
2710 if (d->sceneInteractionAllowed) {
2711 switch (event->type()) {
2712 case QEvent::ShortcutOverride:
2713 if (d->scene)
2714 return QApplication::sendEvent(d->scene, event);
2715 break;
2716 case QEvent::KeyPress:
2717 if (d->scene) {
2718 QKeyEvent *k = static_cast<QKeyEvent *>(event);
2719 if (k->key() == Qt::Key_Tab || k->key() == Qt::Key_Backtab) {
2720 // Send the key events to the scene. This will invoke the
2721 // scene's tab focus handling, and if the event is
2722 // accepted, we return (prevent further event delivery),
2723 // and the base implementation will call QGraphicsView's
2724 // focusNextPrevChild() function. If the event is ignored,
2725 // we fall back to standard tab focus handling.
2726 QApplication::sendEvent(d->scene, event);
2727 if (event->isAccepted())
2728 return true;
2729 // Ensure the event doesn't propagate just because the
2730 // scene ignored it. If the event propagates, then tab
2731 // handling will be called twice (this and parent).
2732 event->accept();
2733 }
2734 }
2735 break;
2736 default:
2737 break;
2738 }
2739 }
2740
2741 return QAbstractScrollArea::event(event);
2742}
2743
2744/*!
2745 \reimp
2746*/
2747bool QGraphicsView::viewportEvent(QEvent *event)
2748{
2749 Q_D(QGraphicsView);
2750 if (!d->scene)
2751 return QAbstractScrollArea::viewportEvent(event);
2752
2753 switch (event->type()) {
2754 case QEvent::Enter:
2755 QApplication::sendEvent(d->scene, event);
2756 break;
2757 case QEvent::WindowActivate:
2758 QApplication::sendEvent(d->scene, event);
2759 break;
2760 case QEvent::WindowDeactivate:
2761 // ### This is a temporary fix for until we get proper mouse
2762 // grab events. mouseGrabberItem should be set to 0 if we lose
2763 // the mouse grab.
2764 // Remove all popups when the scene loses focus.
2765 if (!d->scene->d_func()->popupWidgets.isEmpty())
2766 d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
2767 QApplication::sendEvent(d->scene, event);
2768 break;
2769 case QEvent::Show:
2770 if (d->scene && isActiveWindow()) {
2771 QEvent windowActivate(QEvent::WindowActivate);
2772 QApplication::sendEvent(d->scene, &windowActivate);
2773 }
2774 break;
2775 case QEvent::Hide:
2776 // spontaneous event will generate a WindowDeactivate.
2777 if (!event->spontaneous() && d->scene && isActiveWindow()) {
2778 QEvent windowDeactivate(QEvent::WindowDeactivate);
2779 QApplication::sendEvent(d->scene, &windowDeactivate);
2780 }
2781 break;
2782 case QEvent::Leave:
2783 // ### This is a temporary fix for until we get proper mouse grab
2784 // events. activeMouseGrabberItem should be set to 0 if we lose the
2785 // mouse grab.
2786 if ((QApplication::activePopupWidget() && QApplication::activePopupWidget() != window())
2787 || (QApplication::activeModalWidget() && QApplication::activeModalWidget() != window())
2788 || (QApplication::activeWindow() != window())) {
2789 if (!d->scene->d_func()->popupWidgets.isEmpty())
2790 d->scene->d_func()->removePopup(d->scene->d_func()->popupWidgets.first());
2791 }
2792 d->useLastMouseEvent = false;
2793 QApplication::sendEvent(d->scene, event);
2794 break;
2795#ifndef QT_NO_TOOLTIP
2796 case QEvent::ToolTip: {
2797 QHelpEvent *toolTip = static_cast<QHelpEvent *>(event);
2798 QGraphicsSceneHelpEvent helpEvent(QEvent::GraphicsSceneHelp);
2799 helpEvent.setWidget(viewport());
2800 helpEvent.setScreenPos(toolTip->globalPos());
2801 helpEvent.setScenePos(mapToScene(toolTip->pos()));
2802 QApplication::sendEvent(d->scene, &helpEvent);
2803 toolTip->setAccepted(helpEvent.isAccepted());
2804 return true;
2805 }
2806#endif
2807 case QEvent::Paint:
2808 // Reset full update
2809 d->fullUpdatePending = false;
2810 d->dirtyScrollOffset = QPoint();
2811 if (d->scene) {
2812 // Check if this view reimplements the updateScene slot; if it
2813 // does, we can't do direct update delivery and have to fall back
2814 // to connecting the changed signal.
2815 if (!d->updateSceneSlotReimplementedChecked) {
2816 d->updateSceneSlotReimplementedChecked = true;
2817 const QMetaObject *mo = metaObject();
2818 if (mo != &QGraphicsView::staticMetaObject) {
2819 if (mo->indexOfSlot("updateScene(QList<QRectF>)")
2820 != QGraphicsView::staticMetaObject.indexOfSlot("updateScene(QList<QRectF>)")) {
2821 connect(d->scene, SIGNAL(changed(QList<QRectF>)),
2822 this, SLOT(updateScene(QList<QRectF>)));
2823 }
2824 }
2825 }
2826 }
2827 break;
2828 case QEvent::TouchBegin:
2829 case QEvent::TouchUpdate:
2830 case QEvent::TouchEnd:
2831 {
2832 if (!isEnabled())
2833 return false;
2834
2835 if (d->scene && d->sceneInteractionAllowed) {
2836 // Convert and deliver the touch event to the scene.
2837 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
2838 touchEvent->setWidget(viewport());
2839 QGraphicsViewPrivate::translateTouchEvent(d, touchEvent);
2840 (void) QApplication::sendEvent(d->scene, touchEvent);
2841 }
2842
2843 return true;
2844 }
2845#ifndef QT_NO_GESTURES
2846 case QEvent::Gesture:
2847 case QEvent::GestureOverride:
2848 {
2849 if (!isEnabled())
2850 return false;
2851
2852 if (d->scene && d->sceneInteractionAllowed) {
2853 QGestureEvent *gestureEvent = static_cast<QGestureEvent *>(event);
2854 gestureEvent->setWidget(viewport());
2855 (void) QApplication::sendEvent(d->scene, gestureEvent);
2856 }
2857 return true;
2858 }
2859#endif // QT_NO_GESTURES
2860 default:
2861 break;
2862 }
2863
2864 return QAbstractScrollArea::viewportEvent(event);
2865}
2866
2867#ifndef QT_NO_CONTEXTMENU
2868/*!
2869 \reimp
2870*/
2871void QGraphicsView::contextMenuEvent(QContextMenuEvent *event)
2872{
2873 Q_D(QGraphicsView);
2874 if (!d->scene || !d->sceneInteractionAllowed)
2875 return;
2876
2877 d->mousePressViewPoint = event->pos();
2878 d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
2879 d->mousePressScreenPoint = event->globalPos();
2880 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
2881 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
2882
2883 QGraphicsSceneContextMenuEvent contextEvent(QEvent::GraphicsSceneContextMenu);
2884 contextEvent.setWidget(viewport());
2885 contextEvent.setScenePos(d->mousePressScenePoint);
2886 contextEvent.setScreenPos(d->mousePressScreenPoint);
2887 contextEvent.setModifiers(event->modifiers());
2888 contextEvent.setReason((QGraphicsSceneContextMenuEvent::Reason)(event->reason()));
2889 contextEvent.setAccepted(event->isAccepted());
2890 QApplication::sendEvent(d->scene, &contextEvent);
2891 event->setAccepted(contextEvent.isAccepted());
2892}
2893#endif // QT_NO_CONTEXTMENU
2894
2895/*!
2896 \reimp
2897*/
2898void QGraphicsView::dropEvent(QDropEvent *event)
2899{
2900#ifndef QT_NO_DRAGANDDROP
2901 Q_D(QGraphicsView);
2902 if (!d->scene || !d->sceneInteractionAllowed)
2903 return;
2904
2905 // Generate a scene event.
2906 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDrop);
2907 d->populateSceneDragDropEvent(&sceneEvent, event);
2908
2909 // Send it to the scene.
2910 QApplication::sendEvent(d->scene, &sceneEvent);
2911
2912 // Accept the originating event if the scene accepted the scene event.
2913 event->setAccepted(sceneEvent.isAccepted());
2914 if (sceneEvent.isAccepted())
2915 event->setDropAction(sceneEvent.dropAction());
2916
2917 delete d->lastDragDropEvent;
2918 d->lastDragDropEvent = 0;
2919
2920#else
2921 Q_UNUSED(event)
2922#endif
2923}
2924
2925/*!
2926 \reimp
2927*/
2928void QGraphicsView::dragEnterEvent(QDragEnterEvent *event)
2929{
2930#ifndef QT_NO_DRAGANDDROP
2931 Q_D(QGraphicsView);
2932 if (!d->scene || !d->sceneInteractionAllowed)
2933 return;
2934
2935 // Disable replaying of mouse move events.
2936 d->useLastMouseEvent = false;
2937
2938 // Generate a scene event.
2939 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragEnter);
2940 d->populateSceneDragDropEvent(&sceneEvent, event);
2941
2942 // Store it for later use.
2943 d->storeDragDropEvent(&sceneEvent);
2944
2945 // Send it to the scene.
2946 QApplication::sendEvent(d->scene, &sceneEvent);
2947
2948 // Accept the originating event if the scene accepted the scene event.
2949 if (sceneEvent.isAccepted()) {
2950 event->setAccepted(true);
2951 event->setDropAction(sceneEvent.dropAction());
2952 }
2953#else
2954 Q_UNUSED(event)
2955#endif
2956}
2957
2958/*!
2959 \reimp
2960*/
2961void QGraphicsView::dragLeaveEvent(QDragLeaveEvent *event)
2962{
2963#ifndef QT_NO_DRAGANDDROP
2964 Q_D(QGraphicsView);
2965 if (!d->scene || !d->sceneInteractionAllowed)
2966 return;
2967 if (!d->lastDragDropEvent) {
2968 qWarning("QGraphicsView::dragLeaveEvent: drag leave received before drag enter");
2969 return;
2970 }
2971
2972 // Generate a scene event.
2973 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragLeave);
2974 sceneEvent.setScenePos(d->lastDragDropEvent->scenePos());
2975 sceneEvent.setScreenPos(d->lastDragDropEvent->screenPos());
2976 sceneEvent.setButtons(d->lastDragDropEvent->buttons());
2977 sceneEvent.setModifiers(d->lastDragDropEvent->modifiers());
2978 sceneEvent.setPossibleActions(d->lastDragDropEvent->possibleActions());
2979 sceneEvent.setProposedAction(d->lastDragDropEvent->proposedAction());
2980 sceneEvent.setDropAction(d->lastDragDropEvent->dropAction());
2981 sceneEvent.setMimeData(d->lastDragDropEvent->mimeData());
2982 sceneEvent.setWidget(d->lastDragDropEvent->widget());
2983 sceneEvent.setSource(d->lastDragDropEvent->source());
2984 delete d->lastDragDropEvent;
2985 d->lastDragDropEvent = 0;
2986
2987 // Send it to the scene.
2988 QApplication::sendEvent(d->scene, &sceneEvent);
2989
2990 // Accept the originating event if the scene accepted the scene event.
2991 if (sceneEvent.isAccepted())
2992 event->setAccepted(true);
2993#else
2994 Q_UNUSED(event)
2995#endif
2996}
2997
2998/*!
2999 \reimp
3000*/
3001void QGraphicsView::dragMoveEvent(QDragMoveEvent *event)
3002{
3003#ifndef QT_NO_DRAGANDDROP
3004 Q_D(QGraphicsView);
3005 if (!d->scene || !d->sceneInteractionAllowed)
3006 return;
3007
3008 // Generate a scene event.
3009 QGraphicsSceneDragDropEvent sceneEvent(QEvent::GraphicsSceneDragMove);
3010 d->populateSceneDragDropEvent(&sceneEvent, event);
3011
3012 // Store it for later use.
3013 d->storeDragDropEvent(&sceneEvent);
3014
3015 // Send it to the scene.
3016 QApplication::sendEvent(d->scene, &sceneEvent);
3017
3018 // Ignore the originating event if the scene ignored the scene event.
3019 event->setAccepted(sceneEvent.isAccepted());
3020 if (sceneEvent.isAccepted())
3021 event->setDropAction(sceneEvent.dropAction());
3022#else
3023 Q_UNUSED(event)
3024#endif
3025}
3026
3027/*!
3028 \reimp
3029*/
3030void QGraphicsView::focusInEvent(QFocusEvent *event)
3031{
3032 Q_D(QGraphicsView);
3033 d->updateInputMethodSensitivity();
3034 QAbstractScrollArea::focusInEvent(event);
3035 if (d->scene)
3036 QApplication::sendEvent(d->scene, event);
3037 // Pass focus on if the scene cannot accept focus.
3038 if (!d->scene || !event->isAccepted())
3039 QAbstractScrollArea::focusInEvent(event);
3040}
3041
3042/*!
3043 \reimp
3044*/
3045bool QGraphicsView::focusNextPrevChild(bool next)
3046{
3047 return QAbstractScrollArea::focusNextPrevChild(next);
3048}
3049
3050/*!
3051 \reimp
3052*/
3053void QGraphicsView::focusOutEvent(QFocusEvent *event)
3054{
3055 Q_D(QGraphicsView);
3056 QAbstractScrollArea::focusOutEvent(event);
3057 if (d->scene)
3058 QApplication::sendEvent(d->scene, event);
3059}
3060
3061/*!
3062 \reimp
3063*/
3064void QGraphicsView::keyPressEvent(QKeyEvent *event)
3065{
3066 Q_D(QGraphicsView);
3067 if (!d->scene || !d->sceneInteractionAllowed) {
3068 QAbstractScrollArea::keyPressEvent(event);
3069 return;
3070 }
3071 QApplication::sendEvent(d->scene, event);
3072 if (!event->isAccepted())
3073 QAbstractScrollArea::keyPressEvent(event);
3074}
3075
3076/*!
3077 \reimp
3078*/
3079void QGraphicsView::keyReleaseEvent(QKeyEvent *event)
3080{
3081 Q_D(QGraphicsView);
3082 if (!d->scene || !d->sceneInteractionAllowed)
3083 return;
3084 QApplication::sendEvent(d->scene, event);
3085 if (!event->isAccepted())
3086 QAbstractScrollArea::keyReleaseEvent(event);
3087}
3088
3089/*!
3090 \reimp
3091*/
3092void QGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
3093{
3094 Q_D(QGraphicsView);
3095 if (!d->scene || !d->sceneInteractionAllowed)
3096 return;
3097
3098 d->storeMouseEvent(event);
3099 d->mousePressViewPoint = event->pos();
3100 d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
3101 d->mousePressScreenPoint = event->globalPos();
3102 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
3103 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
3104 d->mousePressButton = event->button();
3105
3106 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseDoubleClick);
3107 mouseEvent.setWidget(viewport());
3108 mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
3109 mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
3110 mouseEvent.setScenePos(mapToScene(d->mousePressViewPoint));
3111 mouseEvent.setScreenPos(d->mousePressScreenPoint);
3112 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3113 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3114 mouseEvent.setButtons(event->buttons());
3115 mouseEvent.setButtons(event->buttons());
3116 mouseEvent.setAccepted(false);
3117 mouseEvent.setButton(event->button());
3118 mouseEvent.setModifiers(event->modifiers());
3119 if (event->spontaneous())
3120 qt_sendSpontaneousEvent(d->scene, &mouseEvent);
3121 else
3122 QApplication::sendEvent(d->scene, &mouseEvent);
3123}
3124
3125/*!
3126 \reimp
3127*/
3128void QGraphicsView::mousePressEvent(QMouseEvent *event)
3129{
3130 Q_D(QGraphicsView);
3131
3132 // Store this event for replaying, finding deltas, and for
3133 // scroll-dragging; even in non-interactive mode, scroll hand dragging is
3134 // allowed, so we store the event at the very top of this function.
3135 d->storeMouseEvent(event);
3136 d->lastMouseEvent.setAccepted(false);
3137
3138 if (d->sceneInteractionAllowed) {
3139 // Store some of the event's button-down data.
3140 d->mousePressViewPoint = event->pos();
3141 d->mousePressScenePoint = mapToScene(d->mousePressViewPoint);
3142 d->mousePressScreenPoint = event->globalPos();
3143 d->lastMouseMoveScenePoint = d->mousePressScenePoint;
3144 d->lastMouseMoveScreenPoint = d->mousePressScreenPoint;
3145 d->mousePressButton = event->button();
3146
3147 if (d->scene) {
3148 // Convert and deliver the mouse event to the scene.
3149 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMousePress);
3150 mouseEvent.setWidget(viewport());
3151 mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
3152 mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
3153 mouseEvent.setScenePos(d->mousePressScenePoint);
3154 mouseEvent.setScreenPos(d->mousePressScreenPoint);
3155 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3156 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3157 mouseEvent.setButtons(event->buttons());
3158 mouseEvent.setButton(event->button());
3159 mouseEvent.setModifiers(event->modifiers());
3160 mouseEvent.setAccepted(false);
3161 if (event->spontaneous())
3162 qt_sendSpontaneousEvent(d->scene, &mouseEvent);
3163 else
3164 QApplication::sendEvent(d->scene, &mouseEvent);
3165
3166 // Update the original mouse event accepted state.
3167 bool isAccepted = mouseEvent.isAccepted();
3168 event->setAccepted(isAccepted);
3169
3170 // Update the last mouse event accepted state.
3171 d->lastMouseEvent.setAccepted(isAccepted);
3172
3173 if (isAccepted)
3174 return;
3175 }
3176 }
3177
3178#ifndef QT_NO_RUBBERBAND
3179 if (d->dragMode == QGraphicsView::RubberBandDrag && !d->rubberBanding) {
3180 if (d->sceneInteractionAllowed) {
3181 // Rubberbanding is only allowed in interactive mode.
3182 event->accept();
3183 d->rubberBanding = true;
3184 d->rubberBandRect = QRect();
3185 if (d->scene) {
3186 // Initiating a rubber band always clears the selection.
3187 d->scene->clearSelection();
3188 }
3189 }
3190 } else
3191#endif
3192 if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
3193 // Left-button press in scroll hand mode initiates hand scrolling.
3194 event->accept();
3195 d->handScrolling = true;
3196 d->handScrollMotions = 0;
3197#ifndef QT_NO_CURSOR
3198 viewport()->setCursor(Qt::ClosedHandCursor);
3199#endif
3200 }
3201}
3202
3203/*!
3204 \reimp
3205*/
3206void QGraphicsView::mouseMoveEvent(QMouseEvent *event)
3207{
3208 Q_D(QGraphicsView);
3209
3210#ifndef QT_NO_RUBBERBAND
3211 if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed) {
3212 d->storeMouseEvent(event);
3213 if (d->rubberBanding) {
3214 // Check for enough drag distance
3215 if ((d->mousePressViewPoint - event->pos()).manhattanLength()
3216 < QApplication::startDragDistance()) {
3217 return;
3218 }
3219
3220 // Update old rubberband
3221 if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate && !d->rubberBandRect.isEmpty()) {
3222 if (d->viewportUpdateMode != FullViewportUpdate)
3223 viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
3224 else
3225 d->updateAll();
3226 }
3227
3228 // Stop rubber banding if the user has let go of all buttons (even
3229 // if we didn't get the release events).
3230 if (!event->buttons()) {
3231 d->rubberBanding = false;
3232 d->rubberBandRect = QRect();
3233 return;
3234 }
3235
3236 // Update rubberband position
3237 const QPoint &mp = d->mousePressViewPoint;
3238 QPoint ep = event->pos();
3239 d->rubberBandRect = QRect(qMin(mp.x(), ep.x()), qMin(mp.y(), ep.y()),
3240 qAbs(mp.x() - ep.x()) + 1, qAbs(mp.y() - ep.y()) + 1);
3241
3242 // Update new rubberband
3243 if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
3244 if (d->viewportUpdateMode != FullViewportUpdate)
3245 viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
3246 else
3247 d->updateAll();
3248 }
3249 // Set the new selection area
3250 QPainterPath selectionArea;
3251 selectionArea.addPolygon(mapToScene(d->rubberBandRect));
3252 selectionArea.closeSubpath();
3253 if (d->scene)
3254 d->scene->setSelectionArea(selectionArea, d->rubberBandSelectionMode,
3255 viewportTransform());
3256 return;
3257 }
3258 } else
3259#endif // QT_NO_RUBBERBAND
3260 if (d->dragMode == QGraphicsView::ScrollHandDrag) {
3261 if (d->handScrolling) {
3262 QScrollBar *hBar = horizontalScrollBar();
3263 QScrollBar *vBar = verticalScrollBar();
3264 QPoint delta = event->pos() - d->lastMouseEvent.pos();
3265 hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
3266 vBar->setValue(vBar->value() - delta.y());
3267
3268 // Detect how much we've scrolled to disambiguate scrolling from
3269 // clicking.
3270 ++d->handScrollMotions;
3271 }
3272 }
3273
3274 d->mouseMoveEventHandler(event);
3275}
3276
3277/*!
3278 \reimp
3279*/
3280void QGraphicsView::mouseReleaseEvent(QMouseEvent *event)
3281{
3282 Q_D(QGraphicsView);
3283
3284#ifndef QT_NO_RUBBERBAND
3285 if (d->dragMode == QGraphicsView::RubberBandDrag && d->sceneInteractionAllowed && !event->buttons()) {
3286 if (d->rubberBanding) {
3287 if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate){
3288 if (d->viewportUpdateMode != FullViewportUpdate)
3289 viewport()->update(d->rubberBandRegion(viewport(), d->rubberBandRect));
3290 else
3291 d->updateAll();
3292 }
3293 d->rubberBanding = false;
3294 d->rubberBandRect = QRect();
3295 }
3296 } else
3297#endif
3298 if (d->dragMode == QGraphicsView::ScrollHandDrag && event->button() == Qt::LeftButton) {
3299#ifndef QT_NO_CURSOR
3300 // Restore the open hand cursor. ### There might be items
3301 // under the mouse that have a valid cursor at this time, so
3302 // we could repeat the steps from mouseMoveEvent().
3303 viewport()->setCursor(Qt::OpenHandCursor);
3304#endif
3305 d->handScrolling = false;
3306
3307 if (d->scene && d->sceneInteractionAllowed && !d->lastMouseEvent.isAccepted() && d->handScrollMotions <= 6) {
3308 // If we've detected very little motion during the hand drag, and
3309 // no item accepted the last event, we'll interpret that as a
3310 // click to the scene, and reset the selection.
3311 d->scene->clearSelection();
3312 }
3313 }
3314
3315 d->storeMouseEvent(event);
3316
3317 if (!d->sceneInteractionAllowed)
3318 return;
3319
3320 if (!d->scene)
3321 return;
3322
3323 QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseRelease);
3324 mouseEvent.setWidget(viewport());
3325 mouseEvent.setButtonDownScenePos(d->mousePressButton, d->mousePressScenePoint);
3326 mouseEvent.setButtonDownScreenPos(d->mousePressButton, d->mousePressScreenPoint);
3327 mouseEvent.setScenePos(mapToScene(event->pos()));
3328 mouseEvent.setScreenPos(event->globalPos());
3329 mouseEvent.setLastScenePos(d->lastMouseMoveScenePoint);
3330 mouseEvent.setLastScreenPos(d->lastMouseMoveScreenPoint);
3331 mouseEvent.setButtons(event->buttons());
3332 mouseEvent.setButton(event->button());
3333 mouseEvent.setModifiers(event->modifiers());
3334 mouseEvent.setAccepted(false);
3335 if (event->spontaneous())
3336 qt_sendSpontaneousEvent(d->scene, &mouseEvent);
3337 else
3338 QApplication::sendEvent(d->scene, &mouseEvent);
3339
3340 // Update the last mouse event selected state.
3341 d->lastMouseEvent.setAccepted(mouseEvent.isAccepted());
3342
3343#ifndef QT_NO_CURSOR
3344 if (mouseEvent.isAccepted() && mouseEvent.buttons() == 0 && viewport()->testAttribute(Qt::WA_SetCursor)) {
3345 // The last mouse release on the viewport will trigger clearing the cursor.
3346 d->_q_unsetViewportCursor();
3347 }
3348#endif
3349}
3350
3351#ifndef QT_NO_WHEELEVENT
3352/*!
3353 \reimp
3354*/
3355void QGraphicsView::wheelEvent(QWheelEvent *event)
3356{
3357 Q_D(QGraphicsView);
3358 if (!d->scene || !d->sceneInteractionAllowed) {
3359 QAbstractScrollArea::wheelEvent(event);
3360 return;
3361 }
3362
3363 event->ignore();
3364
3365 QGraphicsSceneWheelEvent wheelEvent(QEvent::GraphicsSceneWheel);
3366 wheelEvent.setWidget(viewport());
3367 wheelEvent.setScenePos(mapToScene(event->pos()));
3368 wheelEvent.setScreenPos(event->globalPos());
3369 wheelEvent.setButtons(event->buttons());
3370 wheelEvent.setModifiers(event->modifiers());
3371 wheelEvent.setDelta(event->delta());
3372 wheelEvent.setOrientation(event->orientation());
3373 wheelEvent.setAccepted(false);
3374 QApplication::sendEvent(d->scene, &wheelEvent);
3375 event->setAccepted(wheelEvent.isAccepted());
3376 if (!event->isAccepted())
3377 QAbstractScrollArea::wheelEvent(event);
3378}
3379#endif // QT_NO_WHEELEVENT
3380
3381/*!
3382 \reimp
3383*/
3384void QGraphicsView::paintEvent(QPaintEvent *event)
3385{
3386 Q_D(QGraphicsView);
3387 if (!d->scene) {
3388 QAbstractScrollArea::paintEvent(event);
3389 return;
3390 }
3391
3392 // Set up painter state protection.
3393 d->scene->d_func()->painterStateProtection = !(d->optimizationFlags & DontSavePainterState);
3394
3395 // Determine the exposed region
3396 d->exposedRegion = event->region();
3397 QRectF exposedSceneRect = mapToScene(d->exposedRegion.boundingRect()).boundingRect();
3398
3399 // Set up the painter
3400 QPainter painter(viewport());
3401#ifndef QT_NO_RUBBERBAND
3402 if (d->rubberBanding && !d->rubberBandRect.isEmpty())
3403 painter.save();
3404#endif
3405 // Set up render hints
3406 painter.setRenderHints(painter.renderHints(), false);
3407 painter.setRenderHints(d->renderHints, true);
3408
3409 // Set up viewport transform
3410 const bool viewTransformed = isTransformed();
3411 if (viewTransformed)
3412 painter.setWorldTransform(viewportTransform());
3413 const QTransform viewTransform = painter.worldTransform();
3414
3415 // Draw background
3416 if ((d->cacheMode & CacheBackground)
3417#ifdef Q_WS_X11
3418 && X11->use_xrender
3419#endif
3420 ) {
3421 // Recreate the background pixmap, and flag the whole background as
3422 // exposed.
3423 if (d->mustResizeBackgroundPixmap) {
3424 d->backgroundPixmap = QPixmap(viewport()->size());
3425 QBrush bgBrush = viewport()->palette().brush(viewport()->backgroundRole());
3426 if (!bgBrush.isOpaque())
3427 d->backgroundPixmap.fill(Qt::transparent);
3428 QPainter p(&d->backgroundPixmap);
3429 p.fillRect(0, 0, d->backgroundPixmap.width(), d->backgroundPixmap.height(), bgBrush);
3430 d->backgroundPixmapExposed = QRegion(viewport()->rect());
3431 d->mustResizeBackgroundPixmap = false;
3432 }
3433
3434 // Redraw exposed areas
3435 if (!d->backgroundPixmapExposed.isEmpty()) {
3436 QPainter backgroundPainter(&d->backgroundPixmap);
3437 backgroundPainter.setClipRegion(d->backgroundPixmapExposed, Qt::ReplaceClip);
3438 if (viewTransformed)
3439 backgroundPainter.setTransform(viewTransform);
3440 QRectF backgroundExposedSceneRect = mapToScene(d->backgroundPixmapExposed.boundingRect()).boundingRect();
3441 drawBackground(&backgroundPainter, backgroundExposedSceneRect);
3442 d->backgroundPixmapExposed = QRegion();
3443 }
3444
3445 // Blit the background from the background pixmap
3446 if (viewTransformed) {
3447 painter.setWorldTransform(QTransform());
3448 painter.drawPixmap(QPoint(), d->backgroundPixmap);
3449 painter.setWorldTransform(viewTransform);
3450 } else {
3451 painter.drawPixmap(QPoint(), d->backgroundPixmap);
3452 }
3453 } else {
3454 if (!(d->optimizationFlags & DontSavePainterState))
3455 painter.save();
3456 drawBackground(&painter, exposedSceneRect);
3457 if (!(d->optimizationFlags & DontSavePainterState))
3458 painter.restore();
3459 }
3460
3461 // Items
3462 if (!(d->optimizationFlags & IndirectPainting)) {
3463 const quint32 oldRectAdjust = d->scene->d_func()->rectAdjust;
3464 if (d->optimizationFlags & QGraphicsView::DontAdjustForAntialiasing)
3465 d->scene->d_func()->rectAdjust = 1;
3466 else
3467 d->scene->d_func()->rectAdjust = 2;
3468 d->scene->d_func()->drawItems(&painter, viewTransformed ? &viewTransform : 0,
3469 &d->exposedRegion, viewport());
3470 d->scene->d_func()->rectAdjust = oldRectAdjust;
3471 // Make sure the painter's world transform is restored correctly when
3472 // drawing without painter state protection (DontSavePainterState).
3473 // We only change the worldTransform() so there's no need to do a full-blown
3474 // save() and restore(). Also note that we don't have to do this in case of
3475 // IndirectPainting (the else branch), because in that case we always save()
3476 // and restore() in QGraphicsScene::drawItems().
3477 if (!d->scene->d_func()->painterStateProtection)
3478 painter.setOpacity(1.0);
3479 painter.setWorldTransform(viewTransform);
3480 } else {
3481 // Make sure we don't have unpolished items before we draw
3482 if (!d->scene->d_func()->unpolishedItems.isEmpty())
3483 d->scene->d_func()->_q_polishItems();
3484 // We reset updateAll here (after we've issued polish events)
3485 // so that we can discard update requests coming from polishEvent().
3486 d->scene->d_func()->updateAll = false;
3487
3488 // Find all exposed items
3489 bool allItems = false;
3490 QList<QGraphicsItem *> itemList = d->findItems(d->exposedRegion, &allItems, viewTransform);
3491 if (!itemList.isEmpty()) {
3492 // Generate the style options.
3493 const int numItems = itemList.size();
3494 QGraphicsItem **itemArray = &itemList[0]; // Relies on QList internals, but is perfectly valid.
3495 QStyleOptionGraphicsItem *styleOptionArray = d->allocStyleOptionsArray(numItems);
3496 QTransform transform(Qt::Uninitialized);
3497 for (int i = 0; i < numItems; ++i) {
3498 QGraphicsItem *item = itemArray[i];
3499 QGraphicsItemPrivate *itemd = item->d_ptr.data();
3500 itemd->initStyleOption(&styleOptionArray[i], viewTransform, d->exposedRegion, allItems);
3501 // Cache the item's area in view coordinates.
3502 // Note that we have to do this here in case the base class implementation
3503 // (QGraphicsScene::drawItems) is not called. If it is, we'll do this
3504 // operation twice, but that's the price one has to pay for using indirect
3505 // painting :-/.
3506 const QRectF brect = adjustedItemEffectiveBoundingRect(item);
3507 if (!itemd->itemIsUntransformable()) {
3508 transform = item->sceneTransform();
3509 if (viewTransformed)
3510 transform *= viewTransform;
3511 } else {
3512 transform = item->deviceTransform(viewTransform);
3513 }
3514 itemd->paintedViewBoundingRects.insert(d->viewport, transform.mapRect(brect).toRect());
3515 }
3516 // Draw the items.
3517 drawItems(&painter, numItems, itemArray, styleOptionArray);
3518 d->freeStyleOptionsArray(styleOptionArray);
3519 }
3520 }
3521
3522 // Foreground
3523 drawForeground(&painter, exposedSceneRect);
3524
3525#ifndef QT_NO_RUBBERBAND
3526 // Rubberband
3527 if (d->rubberBanding && !d->rubberBandRect.isEmpty()) {
3528 painter.restore();
3529 QStyleOptionRubberBand option;
3530 option.initFrom(viewport());
3531 option.rect = d->rubberBandRect;
3532 option.shape = QRubberBand::Rectangle;
3533
3534 QStyleHintReturnMask mask;
3535 if (viewport()->style()->styleHint(QStyle::SH_RubberBand_Mask, &option, viewport(), &mask)) {
3536 // painter clipping for masked rubberbands
3537 painter.setClipRegion(mask.region, Qt::IntersectClip);
3538 }
3539
3540 viewport()->style()->drawControl(QStyle::CE_RubberBand, &option, &painter, viewport());
3541 }
3542#endif
3543
3544 painter.end();
3545
3546 // Restore painter state protection.
3547 d->scene->d_func()->painterStateProtection = true;
3548}
3549
3550/*!
3551 \reimp
3552*/
3553void QGraphicsView::resizeEvent(QResizeEvent *event)
3554{
3555 Q_D(QGraphicsView);
3556 // Save the last center point - the resize may scroll the view, which
3557 // changes the center point.
3558 QPointF oldLastCenterPoint = d->lastCenterPoint;
3559
3560 QAbstractScrollArea::resizeEvent(event);
3561 d->recalculateContentSize();
3562
3563 // Restore the center point again.
3564 if (d->resizeAnchor == NoAnchor && !d->keepLastCenterPoint) {
3565 d->updateLastCenterPoint();
3566 } else {
3567 d->lastCenterPoint = oldLastCenterPoint;
3568 }
3569 d->centerView(d->resizeAnchor);
3570 d->keepLastCenterPoint = false;
3571
3572 if (d->cacheMode & CacheBackground) {
3573 // Invalidate the background pixmap
3574 d->mustResizeBackgroundPixmap = true;
3575 }
3576}
3577
3578/*!
3579 \reimp
3580*/
3581void QGraphicsView::scrollContentsBy(int dx, int dy)
3582{
3583 Q_D(QGraphicsView);
3584 d->dirtyScroll = true;
3585 if (d->transforming)
3586 return;
3587 if (isRightToLeft())
3588 dx = -dx;
3589
3590 if (d->viewportUpdateMode != QGraphicsView::NoViewportUpdate) {
3591 if (d->viewportUpdateMode != QGraphicsView::FullViewportUpdate) {
3592 if (d->accelerateScrolling) {
3593#ifndef QT_NO_RUBBERBAND
3594 // Update new and old rubberband regions
3595 if (!d->rubberBandRect.isEmpty()) {
3596 QRegion rubberBandRegion(d->rubberBandRegion(viewport(), d->rubberBandRect));
3597 rubberBandRegion += rubberBandRegion.translated(-dx, -dy);
3598 viewport()->update(rubberBandRegion);
3599 }
3600#endif
3601 d->dirtyScrollOffset.rx() += dx;
3602 d->dirtyScrollOffset.ry() += dy;
3603 d->dirtyRegion.translate(dx, dy);
3604 viewport()->scroll(dx, dy);
3605 } else {
3606 d->updateAll();
3607 }
3608 } else {
3609 d->updateAll();
3610 }
3611 }
3612
3613 d->updateLastCenterPoint();
3614
3615 if ((d->cacheMode & CacheBackground)
3616#ifdef Q_WS_X11
3617 && X11->use_xrender
3618#endif
3619 ) {
3620 // Scroll the background pixmap
3621 QRegion exposed;
3622 if (!d->backgroundPixmap.isNull())
3623 d->backgroundPixmap.scroll(dx, dy, d->backgroundPixmap.rect(), &exposed);
3624
3625 // Invalidate the background pixmap
3626 d->backgroundPixmapExposed.translate(dx, dy);
3627 d->backgroundPixmapExposed += exposed;
3628 }
3629
3630 // Always replay on scroll.
3631 if (d->sceneInteractionAllowed)
3632 d->replayLastMouseEvent();
3633}
3634
3635/*!
3636 \reimp
3637*/
3638void QGraphicsView::showEvent(QShowEvent *event)
3639{
3640 Q_D(QGraphicsView);
3641 d->recalculateContentSize();
3642 d->centerView(d->transformationAnchor);
3643 QAbstractScrollArea::showEvent(event);
3644}
3645
3646/*!
3647 \reimp
3648*/
3649void QGraphicsView::inputMethodEvent(QInputMethodEvent *event)
3650{
3651 Q_D(QGraphicsView);
3652 if (d->scene)
3653 QApplication::sendEvent(d->scene, event);
3654}
3655
3656/*!
3657 Draws the background of the scene using \a painter, before any items and
3658 the foreground are drawn. Reimplement this function to provide a custom
3659 background for this view.
3660
3661 If all you want is to define a color, texture or gradient for the
3662 background, you can call setBackgroundBrush() instead.
3663
3664 All painting is done in \e scene coordinates. \a rect is the exposed
3665 rectangle.
3666
3667 The default implementation fills \a rect using the view's backgroundBrush.
3668 If no such brush is defined (the default), the scene's drawBackground()
3669 function is called instead.
3670
3671 \sa drawForeground(), QGraphicsScene::drawBackground()
3672*/
3673void QGraphicsView::drawBackground(QPainter *painter, const QRectF &rect)
3674{
3675 Q_D(QGraphicsView);
3676 if (d->scene && d->backgroundBrush.style() == Qt::NoBrush) {
3677 d->scene->drawBackground(painter, rect);
3678 return;
3679 }
3680
3681 painter->fillRect(rect, d->backgroundBrush);
3682}
3683
3684/*!
3685 Draws the foreground of the scene using \a painter, after the background
3686 and all items are drawn. Reimplement this function to provide a custom
3687 foreground for this view.
3688
3689 If all you want is to define a color, texture or gradient for the
3690 foreground, you can call setForegroundBrush() instead.
3691
3692 All painting is done in \e scene coordinates. \a rect is the exposed
3693 rectangle.
3694
3695 The default implementation fills \a rect using the view's foregroundBrush.
3696 If no such brush is defined (the default), the scene's drawForeground()
3697 function is called instead.
3698
3699 \sa drawBackground(), QGraphicsScene::drawForeground()
3700*/
3701void QGraphicsView::drawForeground(QPainter *painter, const QRectF &rect)
3702{
3703 Q_D(QGraphicsView);
3704 if (d->scene && d->foregroundBrush.style() == Qt::NoBrush) {
3705 d->scene->drawForeground(painter, rect);
3706 return;
3707 }
3708
3709 painter->fillRect(rect, d->foregroundBrush);
3710}
3711
3712/*!
3713 \obsolete
3714
3715 Draws the items \a items in the scene using \a painter, after the
3716 background and before the foreground are drawn. \a numItems is the number
3717 of items in \a items and options in \a options. \a options is a list of
3718 styleoptions; one for each item. Reimplement this function to provide
3719 custom item drawing for this view.
3720
3721 The default implementation calls the scene's drawItems() function.
3722
3723 Since Qt 4.6, this function is not called anymore unless
3724 the QGraphicsView::IndirectPainting flag is given as an Optimization
3725 flag.
3726
3727 \sa drawForeground(), drawBackground(), QGraphicsScene::drawItems()
3728*/
3729void QGraphicsView::drawItems(QPainter *painter, int numItems,
3730 QGraphicsItem *items[],
3731 const QStyleOptionGraphicsItem options[])
3732{
3733 Q_D(QGraphicsView);
3734 if (d->scene) {
3735 QWidget *widget = painter->device() == viewport() ? viewport() : 0;
3736 d->scene->drawItems(painter, numItems, items, options, widget);
3737 }
3738}
3739
3740/*!
3741 Returns the current transformation matrix for the view. If no current
3742 transformation is set, the identity matrix is returned.
3743
3744 \sa setTransform(), rotate(), scale(), shear(), translate()
3745*/
3746QTransform QGraphicsView::transform() const
3747{
3748 Q_D(const QGraphicsView);
3749 return d->matrix;
3750}
3751
3752/*!
3753 Returns a matrix that maps viewport coordinates to scene coordinates.
3754
3755 \sa mapToScene(), mapFromScene()
3756*/
3757QTransform QGraphicsView::viewportTransform() const
3758{
3759 Q_D(const QGraphicsView);
3760 QTransform moveMatrix = QTransform::fromTranslate(-d->horizontalScroll(), -d->verticalScroll());
3761 return d->identityMatrix ? moveMatrix : d->matrix * moveMatrix;
3762}
3763
3764/*!
3765 \since 4.6
3766
3767 Returns true if the view is transformed (i.e., a non-identity transform
3768 has been assigned, or the scrollbars are adjusted).
3769
3770 \sa setTransform(), horizontalScrollBar(), verticalScrollBar()
3771*/
3772bool QGraphicsView::isTransformed() const
3773{
3774 Q_D(const QGraphicsView);
3775 return !d->identityMatrix || d->horizontalScroll() || d->verticalScroll();
3776}
3777
3778/*!
3779 Sets the view's current transformation matrix to \a matrix.
3780
3781 If \a combine is true, then \a matrix is combined with the current matrix;
3782 otherwise, \a matrix \e replaces the current matrix. \a combine is false
3783 by default.
3784
3785 The transformation matrix tranforms the scene into view coordinates. Using
3786 the default transformation, provided by the identity matrix, one pixel in
3787 the view represents one unit in the scene (e.g., a 10x10 rectangular item
3788 is drawn using 10x10 pixels in the view). If a 2x2 scaling matrix is
3789 applied, the scene will be drawn in 1:2 (e.g., a 10x10 rectangular item is
3790 then drawn using 20x20 pixels in the view).
3791
3792 Example:
3793
3794 \snippet doc/src/snippets/code/src_gui_graphicsview_qgraphicsview.cpp 7
3795
3796 To simplify interation with items using a transformed view, QGraphicsView
3797 provides mapTo... and mapFrom... functions that can translate between
3798 scene and view coordinates. For example, you can call mapToScene() to map
3799 a view coordiate to a floating point scene coordinate, or mapFromScene()
3800 to map from floating point scene coordinates to view coordinates.
3801
3802 \sa transform(), rotate(), scale(), shear(), translate()
3803*/
3804void QGraphicsView::setTransform(const QTransform &matrix, bool combine )
3805{
3806 Q_D(QGraphicsView);
3807 QTransform oldMatrix = d->matrix;
3808 if (!combine)
3809 d->matrix = matrix;
3810 else
3811 d->matrix = matrix * d->matrix;
3812 if (oldMatrix == d->matrix)
3813 return;
3814
3815 d->identityMatrix = d->matrix.isIdentity();
3816 d->transforming = true;
3817 if (d->scene) {
3818 d->recalculateContentSize();
3819 d->centerView(d->transformationAnchor);
3820 } else {
3821 d->updateLastCenterPoint();
3822 }
3823
3824 if (d->sceneInteractionAllowed)
3825 d->replayLastMouseEvent();
3826 d->transforming = false;
3827
3828 // Any matrix operation requires a full update.
3829 d->updateAll();
3830}
3831
3832/*!
3833 Resets the view transformation to the identity matrix.
3834
3835 \sa transform(), setTransform()
3836*/
3837void QGraphicsView::resetTransform()
3838{
3839 setTransform(QTransform());
3840}
3841
3842QPointF QGraphicsViewPrivate::mapToScene(const QPointF &point) const
3843{
3844 QPointF p = point;
3845 p.rx() += horizontalScroll();
3846 p.ry() += verticalScroll();
3847 return identityMatrix ? p : matrix.inverted().map(p);
3848}
3849
3850QRectF QGraphicsViewPrivate::mapToScene(const QRectF &rect) const
3851{
3852 QPointF scrollOffset(horizontalScroll(), verticalScroll());
3853 QPointF tl = scrollOffset + rect.topLeft();
3854 QPointF tr = scrollOffset + rect.topRight();
3855 QPointF br = scrollOffset + rect.bottomRight();
3856 QPointF bl = scrollOffset + rect.bottomLeft();
3857
3858 QPolygonF poly(4);
3859 if (!identityMatrix) {
3860 QTransform x = matrix.inverted();
3861 poly[0] = x.map(tl);
3862 poly[1] = x.map(tr);
3863 poly[2] = x.map(br);
3864 poly[3] = x.map(bl);
3865 } else {
3866 poly[0] = tl;
3867 poly[1] = tr;
3868 poly[2] = br;
3869 poly[3] = bl;
3870 }
3871 return poly.boundingRect();
3872}
3873
3874QT_END_NAMESPACE
3875
3876#include "moc_qgraphicsview.cpp"
3877
3878#endif // QT_NO_GRAPHICSVIEW
Note: See TracBrowser for help on using the repository browser.