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 ®ion);
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