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

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

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

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