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

Last change on this file since 135 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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