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