1 /************************************************************************
2 **
3 ** @file vmaingraphicsview.cpp
4 ** @author Roman Telezhynskyi <dismine(at)gmail.com>
5 ** @date November 15, 2013
6 **
7 ** @brief
8 ** @copyright
9 ** This source code is part of the Valentina project, a pattern making
10 ** program, whose allow create and modeling patterns of clothing.
11 ** Copyright (C) 2013-2015 Valentina project
12 ** <https://gitlab.com/smart-pattern/valentina> All Rights Reserved.
13 **
14 ** Valentina is free software: you can redistribute it and/or modify
15 ** it under the terms of the GNU General Public License as published by
16 ** the Free Software Foundation, either version 3 of the License, or
17 ** (at your option) any later version.
18 **
19 ** Valentina is distributed in the hope that it will be useful,
20 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ** GNU General Public License for more details.
23 **
24 ** You should have received a copy of the GNU General Public License
25 ** along with Valentina. If not, see <http://www.gnu.org/licenses/>.
26 **
27 *************************************************************************/
28
29 #include "vmaingraphicsview.h"
30
31 #include <QApplication>
32 #include <QCursor>
33 #include <QEvent>
34 #include <QFlags>
35 #include <QGraphicsItem>
36 #include <QGraphicsScene>
37 #include <QLineF>
38 #include <QList>
39 #include <QMessageLogger>
40 #include <QMouseEvent>
41 #include <QPainter>
42 #include <QPoint>
43 #include <QScrollBar>
44 #include <QTimeLine>
45 #include <QTransform>
46 #include <QWheelEvent>
47 #include <QWidget>
48 #include <QThread>
49 #include <QGestureEvent>
50 #include <QScreen>
51 #include <QOpenGLWidget>
52
53 #include "../vmisc/def.h"
54 #include "../vmisc/vmath.h"
55 #include "vmaingraphicsscene.h"
56 #include "vsimplecurve.h"
57 #include "vcontrolpointspline.h"
58 #include "../vmisc/vabstractapplication.h"
59 #include "../vmisc/vsettings.h"
60 #include "vabstractmainwindow.h"
61 #include "global.h"
62
63 const qreal maxSceneSize = ((20.0 * 1000.0) / 25.4) * PrintDPI; // 20 meters in pixels
64
65 namespace
66 {
ScrollingSteps(QWheelEvent * wheel_event)67 qreal ScrollingSteps(QWheelEvent* wheel_event)
68 {
69 SCASSERT(wheel_event != nullptr)
70
71 const QPoint numPixels = wheel_event->pixelDelta();
72 const QPoint numDegrees = wheel_event->angleDelta() / 8;
73 qreal numSteps = 0;
74 VSettings *settings = qobject_cast<VSettings *>(VAbstractApplication::VApp()->Settings());
75
76 if (not numPixels.isNull())
77 {
78 const qreal mouseScale = settings->GetSensorMouseScale();
79 numSteps = (numPixels.x() == 0 ? numPixels.y() : numPixels.x()) / mouseScale;
80 }
81 else if (not numDegrees.isNull())
82 {
83 const qreal mouseScale = settings->GetWheelMouseScale();
84 numSteps = (numPixels.x() == 0 ? numDegrees.y() : numDegrees.x()) / 15. * mouseScale;
85 }
86
87 return numSteps;
88 }
89
90 //---------------------------------------------------------------------------------------------------------------------
PrepareScrolling(qreal scheduledScrollings,QWheelEvent * wheel_event)91 qreal PrepareScrolling(qreal scheduledScrollings, QWheelEvent *wheel_event)
92 {
93 const qreal numSteps = ScrollingSteps(wheel_event);
94
95 if (qFuzzyIsNull(numSteps))
96 {
97 return scheduledScrollings;//Just ignore
98 }
99
100 if (std::signbit(scheduledScrollings) != std::signbit(numSteps))
101 { // if user moved the wheel in another direction, we reset previously scheduled scalings
102 scheduledScrollings = numSteps;
103 }
104 else
105 {
106 scheduledScrollings += numSteps;
107 }
108
109 scheduledScrollings *=
110 qobject_cast<VSettings *>(VAbstractApplication::VApp()->Settings())->GetScrollingAcceleration();
111
112 return scheduledScrollings;
113 }
114
115 //---------------------------------------------------------------------------------------------------------------------
116 /**
117 * @brief PrioritizeItems helps prioritize some items over others.
118 *
119 * In some cases we want items like curve handle points to be selected over other items on scene.
120 * @param list list of scene items under a mouse pointer
121 * @return prioritized list where prioritized items goes first
122 */
PrioritizeItems(const QList<QGraphicsItem * > & list)123 QList<QGraphicsItem *> PrioritizeItems(const QList<QGraphicsItem *> &list)
124 {
125 QList<QGraphicsItem *> prioritized;
126 QList<QGraphicsItem *> nonPrioritized;
127 for (auto item : list)
128 {
129 item && item->type() == VControlPointSpline::Type ? prioritized.append(item) : nonPrioritized.append(item);
130 }
131
132 return prioritized + nonPrioritized;
133 }
134 }
135
136 //---------------------------------------------------------------------------------------------------------------------
GraphicsViewZoom(QGraphicsView * view)137 GraphicsViewZoom::GraphicsViewZoom(QGraphicsView* view)
138 : QObject(view),
139 _view(view),
140 _modifiers(Qt::ControlModifier),
141 _zoom_factor_base(1.0015),
142 target_scene_pos(),
143 target_viewport_pos(),
144 verticalScrollAnim(),
145 _numScheduledVerticalScrollings(0),
146 horizontalScrollAnim(),
147 _numScheduledHorizontalScrollings(0)
148 {
149 _view->viewport()->installEventFilter(this);
150 _view->viewport()->grabGesture(Qt::PinchGesture);
151 _view->setMouseTracking(true);
152
153 InitScrollingAnimation();
154 }
155
156 //---------------------------------------------------------------------------------------------------------------------
gentle_zoom(double factor)157 void GraphicsViewZoom::gentle_zoom(double factor)
158 {
159 // We need to check current scale factor because in Windows we have an error when we zoom in or zoom out to much.
160 // See issue #532: Unexpected error occurs when zoom out image.
161 // factor > 1 for zoomIn and factor < 1 for zoomOut.
162 const qreal m11 = _view->transform().m11();
163
164 if ((factor > 1 && m11 <= VMainGraphicsView::MaxScale()) || (factor < 1 && m11 >= VMainGraphicsView::MinScale()))
165 {
166 _view->scale(factor, factor);
167 if (factor < 1)
168 {
169 // Because QGraphicsView centers the picture when it's smaller than the view. And QGraphicsView's scrolls
170 // boundaries don't allow to put any picture point at any viewport position we will provide fictive scene
171 // size. Temporary and bigger than view, scene size will help position an image under cursor.
172 FictiveSceneRect(_view->scene(), _view);
173 }
174 _view->centerOn(target_scene_pos);
175 QPointF delta_viewport_pos = target_viewport_pos - QPointF(_view->viewport()->width() / 2.0,
176 _view->viewport()->height() / 2.0);
177 QPointF viewport_center = _view->mapFromScene(target_scene_pos) - delta_viewport_pos;
178 _view->centerOn(_view->mapToScene(viewport_center.toPoint()));
179 // In the end we just set correct scene size
180 VMainGraphicsView::NewSceneRect(_view->scene(), _view);
181 emit zoomed();
182 }
183 }
184
185 //---------------------------------------------------------------------------------------------------------------------
186 // cppcheck-suppress unusedFunction
set_modifiers(Qt::KeyboardModifiers modifiers)187 void GraphicsViewZoom::set_modifiers(Qt::KeyboardModifiers modifiers)
188 {
189 _modifiers = modifiers;
190 }
191
192 //---------------------------------------------------------------------------------------------------------------------
193 // cppcheck-suppress unusedFunction
set_zoom_factor_base(double value)194 void GraphicsViewZoom::set_zoom_factor_base(double value)
195 {
196 _zoom_factor_base = value;
197 }
198
199 //---------------------------------------------------------------------------------------------------------------------
InitScrollingAnimation()200 void GraphicsViewZoom::InitScrollingAnimation()
201 {
202 VSettings *settings = qobject_cast<VSettings *>(VAbstractApplication::VApp()->Settings());
203
204 if (not verticalScrollAnim.isNull())
205 {
206 delete verticalScrollAnim;
207 }
208
209 verticalScrollAnim = new QTimeLine(settings->GetScrollingDuration(), this);
210 verticalScrollAnim->setUpdateInterval(settings->GetScrollingUpdateInterval());
211
212 connect(verticalScrollAnim.data(), &QTimeLine::valueChanged, this, &GraphicsViewZoom::VerticalScrollingTime);
213 connect(verticalScrollAnim.data(), &QTimeLine::finished, this, &GraphicsViewZoom::animFinished);
214
215 if (not horizontalScrollAnim.isNull())
216 {
217 delete horizontalScrollAnim;
218 }
219
220 horizontalScrollAnim = new QTimeLine(settings->GetScrollingDuration(), this);
221 horizontalScrollAnim->setUpdateInterval(settings->GetScrollingUpdateInterval());
222
223 connect(horizontalScrollAnim.data(), &QTimeLine::valueChanged, this, &GraphicsViewZoom::HorizontalScrollingTime);
224 connect(horizontalScrollAnim.data(), &QTimeLine::finished, this, &GraphicsViewZoom::animFinished);
225 }
226
227 //---------------------------------------------------------------------------------------------------------------------
VerticalScrollingTime(qreal x)228 void GraphicsViewZoom::VerticalScrollingTime(qreal x)
229 {
230 const qreal scroll = _numScheduledVerticalScrollings * x;
231 _numScheduledVerticalScrollings -= scroll;
232 _view->verticalScrollBar()->setValue(qRound(_view->verticalScrollBar()->value() - scroll));
233 }
234
235 //---------------------------------------------------------------------------------------------------------------------
HorizontalScrollingTime(qreal x)236 void GraphicsViewZoom::HorizontalScrollingTime(qreal x)
237 {
238 const qreal scroll = _numScheduledHorizontalScrollings * x;
239 _numScheduledHorizontalScrollings -= scroll;
240 _view->horizontalScrollBar()->setValue(qRound(_view->horizontalScrollBar()->value() - scroll));
241 }
242
243 //---------------------------------------------------------------------------------------------------------------------
animFinished()244 void GraphicsViewZoom::animFinished()
245 {
246 _numScheduledVerticalScrollings = 0;
247 _numScheduledHorizontalScrollings = 0;
248
249 /*
250 * In moust cases cursor position on view doesn't change, but for scene after scrolling position will be different.
251 * We are goint to check changes and save new value.
252 * If don't do that we will zoom using old value cursor position on scene. It is not what we expect.
253 * Almoust the same we do in method GraphicsViewZoom::eventFilter.
254 */
255 const QPoint pos = _view->mapFromGlobal(QCursor::pos());
256 const QPointF delta = target_scene_pos - _view->mapToScene(pos);
257 if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5)
258 {
259 target_viewport_pos = pos;
260 target_scene_pos = _view->mapToScene(pos);
261 }
262 }
263
264 //---------------------------------------------------------------------------------------------------------------------
eventFilter(QObject * object,QEvent * event)265 bool GraphicsViewZoom::eventFilter(QObject *object, QEvent *event)
266 {
267 if (event->type() == QEvent::MouseMove)
268 {
269 /*
270 * Here we are saving cursor position on view and scene.
271 * This data need for gentle_zoom().
272 * Almoust the same we do in method GraphicsViewZoom::animFinished.
273 */
274 QMouseEvent* mouse_event = static_cast<QMouseEvent*>(event);
275 QPointF delta = target_viewport_pos - mouse_event->pos();
276 if (qAbs(delta.x()) > 5 || qAbs(delta.y()) > 5)
277 {
278 target_viewport_pos = mouse_event->pos();
279 target_scene_pos = _view->mapToScene(mouse_event->pos());
280 }
281 return false;
282 }
283 else if (event->type() == QEvent::Wheel)
284 {
285 if (QWheelEvent* wheel_event = static_cast<QWheelEvent*>(event))
286 {
287 const QPoint numDegrees = wheel_event->angleDelta();
288 if (numDegrees.x() == 0)
289 {
290 if (QGuiApplication::keyboardModifiers() == _modifiers)
291 {
292 gentle_zoom(qPow(_zoom_factor_base, numDegrees.y()));
293 return true;
294 }
295 else if (QGuiApplication::keyboardModifiers() == Qt::ShiftModifier)
296 {
297 StartHorizontalScrollings(wheel_event);
298 return true;
299 }
300 else
301 {
302 StartVerticalScrollings(wheel_event);
303 return true;
304 }
305 }
306 else
307 {
308 if (QGuiApplication::keyboardModifiers() == _modifiers)
309 {
310 return true; //ignore
311 }
312
313 StartHorizontalScrollings(wheel_event);
314 return true;
315 }
316 }
317 }
318 else if (event->type() == QEvent::Gesture)
319 {
320 return GestureEvent(static_cast<QGestureEvent*>(event));
321 }
322
323 return QObject::eventFilter(object, event);
324 }
325
326 //---------------------------------------------------------------------------------------------------------------------
FictiveSceneRect(QGraphicsScene * sc,QGraphicsView * view)327 void GraphicsViewZoom::FictiveSceneRect(QGraphicsScene *sc, QGraphicsView *view)
328 {
329 SCASSERT(sc != nullptr)
330 SCASSERT(view != nullptr)
331
332 //Calculate view rect
333 //to receive the currently visible area, map the widgets bounds to the scene
334 const QPointF a = view->mapToScene(0, 0 );
335 const QPointF b = view->mapToScene(view->viewport()->width(), view->viewport()->height());
336 QRectF viewRect = QRectF( a, b );
337
338 //Scale view
339 QLineF topLeftRay(viewRect.center(), viewRect.topLeft());
340 topLeftRay.setLength(topLeftRay.length()*2);
341
342 QLineF bottomRightRay(viewRect.center(), viewRect.bottomRight());
343 bottomRightRay.setLength(bottomRightRay.length()*2);
344
345 viewRect = QRectF(topLeftRay.p2(), bottomRightRay.p2());
346
347 //Calculate scene rect
348 const QRectF sceneRect = sc->sceneRect();
349
350 //Unite two rects
351 const QRectF newRect = sceneRect.united(viewRect);
352
353 sc->setSceneRect(newRect);
354 }
355
356 //---------------------------------------------------------------------------------------------------------------------
StartVerticalScrollings(QWheelEvent * wheel_event)357 void GraphicsViewZoom::StartVerticalScrollings(QWheelEvent *wheel_event)
358 {
359 if (not wheel_event->pixelDelta().isNull())
360 { // Native scrolling animation
361 _view->verticalScrollBar()->setValue(qCeil(_view->verticalScrollBar()->value() - ScrollingSteps(wheel_event)));
362 animFinished();
363 }
364 else
365 {
366 _numScheduledVerticalScrollings = PrepareScrolling(_numScheduledVerticalScrollings, wheel_event);
367
368 if (verticalScrollAnim->state() != QTimeLine::Running)
369 {
370 verticalScrollAnim->start();
371 }
372 }
373 }
374
375 //---------------------------------------------------------------------------------------------------------------------
StartHorizontalScrollings(QWheelEvent * wheel_event)376 void GraphicsViewZoom::StartHorizontalScrollings(QWheelEvent *wheel_event)
377 {
378 if (not wheel_event->pixelDelta().isNull())
379 { // Native scrolling animation
380 _view->horizontalScrollBar()->setValue(qCeil(_view->horizontalScrollBar()->value() -
381 ScrollingSteps(wheel_event)));
382 animFinished();
383 }
384 else
385 {
386 _numScheduledHorizontalScrollings = PrepareScrolling(_numScheduledHorizontalScrollings, wheel_event);
387
388 if (horizontalScrollAnim->state() != QTimeLine::Running)
389 {
390 horizontalScrollAnim->start();
391 }
392 }
393 }
394
395 //---------------------------------------------------------------------------------------------------------------------
GestureEvent(QGestureEvent * event)396 bool GraphicsViewZoom::GestureEvent(QGestureEvent *event)
397 {
398 if (QGesture *pinch = event->gesture(Qt::PinchGesture))
399 {
400 PinchTriggered(static_cast<QPinchGesture *>(pinch));
401 return true;
402 }
403 return false;
404 }
405
406 //---------------------------------------------------------------------------------------------------------------------
PinchTriggered(QPinchGesture * gesture)407 void GraphicsViewZoom::PinchTriggered(QPinchGesture *gesture)
408 {
409 QPinchGesture::ChangeFlags changeFlags = gesture->changeFlags();
410 if (changeFlags & QPinchGesture::ScaleFactorChanged)
411 {
412 qreal currentStepScaleFactor = gesture->lastScaleFactor();
413 gentle_zoom(currentStepScaleFactor);
414 }
415 }
416
417 const unsigned long VMainGraphicsView::scrollDelay = 160;
418
419 //---------------------------------------------------------------------------------------------------------------------
420 /**
421 * @brief VMainGraphicsView constructor.
422 * @param parent parent object.
423 */
VMainGraphicsView(QWidget * parent)424 VMainGraphicsView::VMainGraphicsView(QWidget *parent)
425 : QGraphicsView(parent),
426 zoom(nullptr),
427 showToolOptions(true),
428 isAllowRubberBand(true),
429 m_ptStartPos(),
430 m_oldCursor(),
431 m_currentCursor(Qt::ArrowCursor)
432 {
433 VSettings *settings = qobject_cast<VSettings *>(VAbstractApplication::VApp()->Settings());
434 if (settings && settings->IsOpenGLRender())
435 {
436 QOpenGLWidget *viewport = new QOpenGLWidget();
437 QSurfaceFormat fmt;
438 fmt.setSamples(settings->GetGraphicalOutput() ? 10 : 0);
439 fmt.setStencilBufferSize(8);
440 viewport->setFormat(fmt);
441
442 setViewport(viewport);
443 }
444
445 zoom = new GraphicsViewZoom(this);
446
447 this->setResizeAnchor(QGraphicsView::AnchorUnderMouse);
448 this->setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
449 this->setInteractive(true);
450 SetAntialiasing(true);
451
452 connect(zoom, &GraphicsViewZoom::zoomed, this, [this](){emit ScaleChanged(transform().m11());});
453 }
454
455 //---------------------------------------------------------------------------------------------------------------------
Zoom(qreal scale)456 void VMainGraphicsView::Zoom(qreal scale)
457 {
458 qreal factor = qBound(MinScale(), scale, MaxScale());
459 QTransform transform = this->transform();
460 transform.setMatrix(factor, transform.m12(), transform.m13(), transform.m21(), factor, transform.m23(),
461 transform.m31(), transform.m32(), transform.m33());
462 this->setTransform(transform);
463 VMainGraphicsView::NewSceneRect(this->scene(), this);
464 emit ScaleChanged(this->transform().m11());
465 }
466
467 //---------------------------------------------------------------------------------------------------------------------
ZoomIn()468 void VMainGraphicsView::ZoomIn()
469 {
470 // We need to check current scale factor because in Windows we have an error when we zoom in or zoom out to much.
471 // See issue #532: Unexpected error occurs when zoom out image.
472 if (this->transform().m11() <= MaxScale())
473 {
474 scale(1.1, 1.1);
475 VMainGraphicsView::NewSceneRect(this->scene(), this);
476 emit ScaleChanged(transform().m11());
477 }
478 }
479
480 //---------------------------------------------------------------------------------------------------------------------
ZoomOut()481 void VMainGraphicsView::ZoomOut()
482 {
483 // We need to check current scale factor because in Windows we have an error when we zoom in or zoom out to much.
484 // See issue #532: Unexpected error occurs when zoom out image.
485 if (this->transform().m11() >= MinScale())
486 {
487 scale(1.0/1.1, 1.0/1.1);
488 VMainGraphicsView::NewSceneRect(this->scene(), this);
489 emit ScaleChanged(transform().m11());
490 }
491 }
492
493 //---------------------------------------------------------------------------------------------------------------------
ZoomOriginal()494 void VMainGraphicsView::ZoomOriginal()
495 {
496 QTransform trans = this->transform();
497 trans.setMatrix(1.0, trans.m12(), trans.m13(), trans.m21(), 1.0, trans.m23(), trans.m31(), trans.m32(),
498 trans.m33());
499 this->setTransform(trans);
500 VMainGraphicsView::NewSceneRect(this->scene(), this);
501 emit ScaleChanged(transform().m11());
502 }
503
504 //---------------------------------------------------------------------------------------------------------------------
ZoomFitBest()505 void VMainGraphicsView::ZoomFitBest()
506 {
507 VMainGraphicsScene *currentScene = qobject_cast<VMainGraphicsScene *>(scene());
508 SCASSERT(currentScene)
509 currentScene->SetOriginsVisible(false);
510 const QRectF rect = currentScene->VisibleItemsBoundingRect();
511 currentScene->SetOriginsVisible(true);
512 if (rect.isEmpty())
513 {
514 return;
515 }
516
517 VMainGraphicsView::NewSceneRect(scene(), this);
518
519 this->fitInView(rect, Qt::KeepAspectRatio);
520 QTransform transform = this->transform();
521
522 const qreal factor = qBound(MinScale(), transform.m11(), MaxScale());
523 transform.setMatrix(factor, transform.m12(), transform.m13(), transform.m21(), factor, transform.m23(),
524 transform.m31(), transform.m32(), transform.m33());
525 this->setTransform(transform);
526 emit ScaleChanged(this->transform().m11());
527 }
528
529 //---------------------------------------------------------------------------------------------------------------------
ResetScrollingAnimation()530 void VMainGraphicsView::ResetScrollingAnimation()
531 {
532 zoom->InitScrollingAnimation();
533 }
534
535 //---------------------------------------------------------------------------------------------------------------------
536 /**
537 * @brief mousePressEvent handle mouse press events.
538 * @param event mouse press event.
539 */
mousePressEvent(QMouseEvent * event)540 void VMainGraphicsView::mousePressEvent(QMouseEvent *event)
541 {
542 switch (event->button())
543 {
544 case Qt::LeftButton:
545 {
546 if (isAllowRubberBand)
547 {
548 QGraphicsView::setDragMode(QGraphicsView::RubberBandDrag);
549 }
550
551 if (showToolOptions)
552 {
553 bool success = false;
554 const QList<QGraphicsItem *> list = PrioritizeItems(items(event->pos()));
555 for (auto item : list)
556 {
557 if (item && item->type() > QGraphicsItem::UserType && item->type() <= VSimpleCurve::Type)
558 {
559 emit itemClicked(item);
560 success = true;
561 break;
562 }
563 }
564
565 if (not success)
566 {
567 emit itemClicked(nullptr);
568 }
569 }
570 break;
571 }
572 case Qt::MiddleButton:
573 {
574 auto scene = qobject_cast<VMainGraphicsScene*>(this->scene());
575 const QList<QGraphicsItem *> list = items(event->pos());
576 if (list.isEmpty() || (scene && scene->IsNonInteractive()))
577 {// Only when the user clicks on the scene background or non interactive scene
578 m_ptStartPos = event->pos();
579 m_oldCursor = viewport()->cursor();
580 QGraphicsView::setDragMode(QGraphicsView::ScrollHandDrag);
581 event->accept();
582 viewport()->setCursor(Qt::ClosedHandCursor);
583 }
584 break;
585 }
586 default:
587 break;
588 }
589 QGraphicsView::mousePressEvent(event);
590 }
591
592 //---------------------------------------------------------------------------------------------------------------------
mouseMoveEvent(QMouseEvent * event)593 void VMainGraphicsView::mouseMoveEvent(QMouseEvent *event)
594 {
595 // Hack to fix problem with mouse cursor. Looks like after we switch cursor back it is rewrited back by a dialog.
596 // Because no real way to catch this call we will check state for each move and compare to excpected state.
597 if (dragMode() != QGraphicsView::ScrollHandDrag)
598 {
599 QCursor cur = viewport()->cursor();
600 // No way to restore bitmap from shape and we really don't need this for now.
601 if (m_currentCursor != Qt::BitmapCursor
602 && cur.shape() == Qt::BitmapCursor
603 && cur.pixmap().cacheKey() != QPixmapFromCache(cursorArrowOpenHand).cacheKey()
604 && cur.pixmap().cacheKey() != QPixmapFromCache(cursorArrowCloseHand).cacheKey())
605 {
606 viewport()->setCursor(m_currentCursor);
607 }
608 }
609
610 if (dragMode() == QGraphicsView::ScrollHandDrag)
611 {
612 QScrollBar *hBar = horizontalScrollBar();
613 QScrollBar *vBar = verticalScrollBar();
614 const QPoint delta = event->pos() - m_ptStartPos;
615 hBar->setValue(hBar->value() + (isRightToLeft() ? delta.x() : -delta.x()));
616 vBar->setValue(vBar->value() - delta.y());
617 m_ptStartPos = event->pos();
618 }
619 else
620 {
621 QGraphicsView::mouseMoveEvent(event);
622 }
623 }
624
625 //---------------------------------------------------------------------------------------------------------------------
626 /**
627 * @brief mouseReleaseEvent handle mouse release events.
628 * @param event mouse release event.
629 */
mouseReleaseEvent(QMouseEvent * event)630 void VMainGraphicsView::mouseReleaseEvent(QMouseEvent *event)
631 {
632 QGraphicsView::mouseReleaseEvent ( event ); // First because need to hide a rubber band
633 QGraphicsView::setDragMode( QGraphicsView::NoDrag );
634 if (event->button() == Qt::MiddleButton)
635 {
636 viewport()->setCursor(m_oldCursor);
637 }
638 if (event->button() == Qt::LeftButton)
639 {
640 emit MouseRelease();
641 }
642 }
643
644 //---------------------------------------------------------------------------------------------------------------------
mouseDoubleClickEvent(QMouseEvent * event)645 void VMainGraphicsView::mouseDoubleClickEvent(QMouseEvent *event)
646 {
647 if (event->button() == Qt::LeftButton &&
648 VAbstractApplication::VApp()->Settings()->IsDoubleClickZoomFitBestCurrentPP())
649 {
650 emit ZoomFitBestCurrent();
651 }
652
653 QGraphicsView::mouseDoubleClickEvent(event);
654 }
655
656 //---------------------------------------------------------------------------------------------------------------------
MinScale()657 qreal VMainGraphicsView::MinScale()
658 {
659 const QRect screenRect = QGuiApplication::primaryScreen()->availableGeometry();
660 const qreal screenSize = qMin(screenRect.width(), screenRect.height());
661
662 return screenSize / maxSceneSize;
663 }
664
665 //---------------------------------------------------------------------------------------------------------------------
MaxScale()666 qreal VMainGraphicsView::MaxScale()
667 {
668 const QRect screenRect = QGuiApplication::primaryScreen()->availableGeometry();
669 const qreal screenSize = qMin(screenRect.width(), screenRect.height());
670
671 return maxSceneSize / screenSize;
672 }
673
674 //---------------------------------------------------------------------------------------------------------------------
EnsureItemVisibleWithDelay(const QGraphicsItem * item,unsigned long msecs,int xmargin,int ymargin)675 void VMainGraphicsView::EnsureItemVisibleWithDelay(const QGraphicsItem *item, unsigned long msecs, int xmargin,
676 int ymargin)
677 {
678 SCASSERT(item != nullptr)
679 const qreal scale = SceneScale(item->scene());
680
681 const QRectF viewRect = VMainGraphicsView::SceneVisibleArea(this);
682 const QRectF itemRect = item->mapToScene(item->boundingRect()).boundingRect();
683
684 // If item's rect is bigger than view's rect ensureVisible works very unstable.
685 if (itemRect.height() + 2*ymargin < viewRect.height() &&
686 itemRect.width() + 2*xmargin < viewRect.width())
687 {
688 EnsureVisibleWithDelay(item, msecs, xmargin, ymargin);
689 }
690 else
691 {
692 // Ensure visible only small rect around a cursor
693 VMainGraphicsScene *currentScene = qobject_cast<VMainGraphicsScene *>(item->scene());
694 SCASSERT(currentScene);
695 const QPointF cursorPosition = currentScene->getScenePos();
696 EnsureVisibleWithDelay(QRectF(cursorPosition.x()-5/scale, cursorPosition.y()-5/scale, 10/scale, 10/scale),
697 msecs);
698 }
699 }
700
701 //---------------------------------------------------------------------------------------------------------------------
EnsureVisibleWithDelay(const QRectF & rect,unsigned long msecs,int xmargin,int ymargin)702 void VMainGraphicsView::EnsureVisibleWithDelay(const QRectF &rect, unsigned long msecs, int xmargin, int ymargin)
703 {
704 const int hbar = horizontalScrollBar()->value();
705 const int vbar = verticalScrollBar()->value();
706
707 ensureVisible(rect, xmargin, ymargin);
708
709 if (hbar != horizontalScrollBar()->value() || vbar != verticalScrollBar()->value())
710 {
711 QThread::msleep(msecs);
712 }
713 }
714
715 //---------------------------------------------------------------------------------------------------------------------
EnsureVisibleWithDelay(const QGraphicsItem * item,unsigned long msecs,int xmargin,int ymargin)716 void VMainGraphicsView::EnsureVisibleWithDelay(const QGraphicsItem *item, unsigned long msecs, int xmargin, int ymargin)
717 {
718 const int hbar = horizontalScrollBar()->value();
719 const int vbar = verticalScrollBar()->value();
720
721 ensureVisible(item, xmargin, ymargin);
722
723 if (hbar != horizontalScrollBar()->value() || vbar != verticalScrollBar()->value())
724 {
725 QThread::msleep(msecs);
726 }
727 }
728
729 //---------------------------------------------------------------------------------------------------------------------
setCurrentCursorShape()730 void VMainGraphicsView::setCurrentCursorShape()
731 {
732 m_currentCursor = viewport()->cursor().shape();
733 }
734
735 //---------------------------------------------------------------------------------------------------------------------
SetAntialiasing(bool value)736 void VMainGraphicsView::SetAntialiasing(bool value)
737 {
738 setRenderHint(QPainter::Antialiasing, value);
739 setRenderHint(QPainter::SmoothPixmapTransform, value);
740 }
741
742 //---------------------------------------------------------------------------------------------------------------------
IsOpenGLRender() const743 bool VMainGraphicsView::IsOpenGLRender() const
744 {
745 QOpenGLWidget *viewport = qobject_cast<QOpenGLWidget *>(this->viewport());
746 if (viewport)
747 {
748 return true;
749 }
750 else
751 {
752 return false;
753 }
754 }
755
756 //---------------------------------------------------------------------------------------------------------------------
setShowToolOptions(bool value)757 void VMainGraphicsView::setShowToolOptions(bool value)
758 {
759 showToolOptions = value;
760 }
761
762 //---------------------------------------------------------------------------------------------------------------------
AllowRubberBand(bool value)763 void VMainGraphicsView::AllowRubberBand(bool value)
764 {
765 isAllowRubberBand = value;
766 }
767
768 //---------------------------------------------------------------------------------------------------------------------
769 /**
770 * @brief NewSceneRect calculate scene rect what contains all items and doesn't less that size of scene view.
771 * @param sc scene.
772 * @param view view.
773 */
NewSceneRect(QGraphicsScene * sc,QGraphicsView * view,QGraphicsItem * item)774 void VMainGraphicsView::NewSceneRect(QGraphicsScene *sc, QGraphicsView *view, QGraphicsItem *item)
775 {
776 SCASSERT(sc != nullptr)
777 SCASSERT(view != nullptr)
778
779 if (item == nullptr)
780 {
781 //Calculate view rect
782 const QRectF viewRect = SceneVisibleArea(view);
783
784 //Calculate scene rect
785 VMainGraphicsScene *currentScene = qobject_cast<VMainGraphicsScene *>(sc);
786 SCASSERT(currentScene)
787 const QRectF itemsRect = currentScene->VisibleItemsBoundingRect();
788
789 //Unite two rects
790 sc->setSceneRect(itemsRect.united(viewRect));
791 }
792 else
793 {
794 QRectF rect = item->sceneBoundingRect();
795 const QList<QGraphicsItem *> children = item->childItems();
796 for (auto child : children)
797 {
798 if(child->isVisible())
799 {
800 rect = rect.united(child->sceneBoundingRect());
801 }
802 }
803
804 if (not sc->sceneRect().contains(rect))
805 {
806 sc->setSceneRect(sc->sceneRect().united(rect));
807 }
808 }
809 }
810
811 //---------------------------------------------------------------------------------------------------------------------
SceneVisibleArea(QGraphicsView * view)812 QRectF VMainGraphicsView::SceneVisibleArea(QGraphicsView *view)
813 {
814 SCASSERT(view != nullptr)
815 //to receive the currently visible area, map the widgets bounds to the scene
816 return QRectF(view->mapToScene(0, 0), view->mapToScene(view->width(), view->height()));
817 }
818