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