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