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