1 /* This file is part of Step.
2    Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
3 
4    Step is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2 of the License, or
7    (at your option) any later version.
8 
9    Step is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with Step; if not, write to the Free Software
16    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 #include "worldscene.h"
20 
21 #ifdef _WIN32
22 #include <windows.h>
23 #endif
24 
25 #include "settings.h"
26 
27 #include "arrow.h"
28 #include "worldmodel.h"
29 #include "worldfactory.h"
30 #include "stepgraphicsitem.h"
31 #include "worldgraphics.h"
32 
33 #include <stepcore/world.h>
34 #include <stepcore/particle.h>
35 #include <stepcore/rigidbody.h>
36 
37 #include <QCoreApplication>
38 #include <QGLWidget>
39 #include <QGraphicsItem>
40 #include <QGraphicsSceneMouseEvent>
41 #include <QItemSelectionModel>
42 #include <QPainter>
43 #include <QScrollBar>
44 #include <QTimer>
45 #include <QToolTip>
46 #include <QUrl>
47 #include <QWheelEvent>
48 
49 #include <KLocalizedString>
50 
51 class WorldSceneAxes: public QGraphicsItem
52 {
53 public:
54     WorldSceneAxes(QGraphicsItem* parent = 0, QGraphicsScene* scene = 0);
55     QRectF boundingRect() const override;
56     QPainterPath shape() const override;
57     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
58     void viewScaleChanged();
59 
60 protected:
61     QRectF _boundingRect;
62     double _viewScale;
63     static const int LENGTH = 100;
64     static const int LENGTHT = 100;
65     static const int LENGTH1 = 10;
66     static const int ARROW_STROKE = 6;
67 };
68 
WorldSceneAxes(QGraphicsItem * parent,QGraphicsScene * scene)69 WorldSceneAxes::WorldSceneAxes(QGraphicsItem* parent, QGraphicsScene* scene)
70     : QGraphicsItem(parent),
71     _boundingRect(-LENGTH, -LENGTH, LENGTH+LENGTH, LENGTH+LENGTH)
72 {
73     _viewScale = 1;
74     setZValue(-100);
75     if (scene)
76       scene->addItem(this);
77 }
78 
boundingRect() const79 QRectF WorldSceneAxes::boundingRect() const
80 {
81     return _boundingRect;
82 }
83 
shape() const84 QPainterPath WorldSceneAxes::shape() const
85 {
86     return QPainterPath();
87 }
88 
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)89 void WorldSceneAxes::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
90 {
91     QPen pen(Qt::gray, 2);
92     pen.setCosmetic(true);
93     painter->setPen(pen);//, Qt::DotLine, Qt::SquareCap, Qt::RoundJoin));
94     //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH));
95     //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0));
96     Arrow arrow(0, LENGTH, 0, -LENGTH, 8);
97     arrow.draw(painter);
98     Arrow arrow2(-LENGTH, 0, LENGTH, 0, 8);
99     arrow2.draw(painter);
100     //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH));
101     //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0));
102     //painter->drawLine(QLineF(-2, -LENGTHT, 2, -LENGTHT));
103     //painter->drawLine(QLineF(LENGTHT, -2, LENGTHT, 2));
104 
105     //painter->drawLine(QLineF(0, -LENGTH, -0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE ));
106     //painter->drawLine(QLineF(0, -LENGTH, +0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE ));
107     //painter->drawLine(QLineF(LENGTH, 0, LENGTH-0.866*ARROW_STROKE, -0.5*ARROW_STROKE ));
108     //painter->drawLine(QLineF(LENGTH, 0, LENGTH-0.866*ARROW_STROKE, +0.5*ARROW_STROKE ));
109 
110     painter->drawText(QRectF(-LENGTH - 6, 6, LENGTH, LENGTH),
111                         Qt::AlignRight | Qt::AlignTop,
112                         QStringLiteral("%1,%2").arg( pos().x(), 0, 'g', 3 ).arg( pos().y(), 0, 'g', 3 ));
113     painter->drawText(QRectF(6, -LENGTH, LENGTH - 6, LENGTH - 6),
114             Qt::AlignLeft | Qt::AlignTop, QString::number( pos().y() + LENGTH/_viewScale, 'g', 3 ));
115     painter->drawText(QRectF(6, -LENGTH, LENGTH - 6, LENGTH - 6),
116             Qt::AlignRight | Qt::AlignBottom, QString::number( pos().x() + LENGTH/_viewScale, 'g', 3 ));
117 
118     //painter->drawText(QRectF(ARROW_STROKE, -LENGTHT-50, LENGTHT, 100), Qt::AlignLeft | Qt::AlignVCenter,
119     //                        QString::number( pos().y() + LENGTHT/_viewScale, 'g', 3 ));
120     //painter->drawText(QRectF(LENGTHT-50, -LENGTHT, 100, LENGTHT), Qt::AlignHCenter | Qt::AlignBottom,
121     //                        QString::number( pos().x() + LENGTHT/_viewScale, 'g', 3 ));
122 }
123 
viewScaleChanged()124 void WorldSceneAxes::viewScaleChanged()
125 {
126     prepareGeometryChange();
127     _viewScale = static_cast<WorldScene*>(scene())->currentViewScale();
128     setTransform(QTransform::fromScale(1/_viewScale, -1/_viewScale), false);
129 }
130 
WorldScene(WorldModel * worldModel,QObject * parent)131 WorldScene::WorldScene(WorldModel* worldModel, QObject* parent)
132     : QGraphicsScene(parent), _worldModel(worldModel), _worldView(0),
133         _currentViewScale(1), _itemCreator(0), _bgColor(0),
134         _sceneAxes(0), _snapItem(0)
135 {
136     #ifdef __GNUC__
137     #warning TODO: measure what index method is faster
138     #endif
139 
140     // Alternative index method is BspTreeIndex
141     setItemIndexMethod(NoIndex);
142     //XXX
143     //setct(-200,-200,400,400);
144     setSceneRect(-20, -20, 40, 40);
145 
146     _messageFrame = new MessageFrame();
147     _snapTimer = new QTimer(this);
148     _snapTimer->setInterval(0);
149     _snapTimer->setSingleShot(true);
150 
151     worldModelReset();
152 
153     connect(_worldModel, &QAbstractItemModel::modelReset, this, &WorldScene::worldModelReset);
154     connect(_worldModel, &WorldModel::worldDataChanged, this, &WorldScene::worldDataChanged);
155     connect(_worldModel->selectionModel(), &QItemSelectionModel::currentChanged,
156                                   this, &WorldScene::worldCurrentChanged);
157     connect(_worldModel->selectionModel(), &QItemSelectionModel::selectionChanged,
158                                   this, &WorldScene::worldSelectionChanged);
159     connect(_worldModel, &QAbstractItemModel::rowsInserted,
160                 this, &WorldScene::worldRowsInserted);
161     connect(_worldModel, &QAbstractItemModel::rowsAboutToBeRemoved,
162                          this, &WorldScene::worldRowsAboutToBeRemoved);
163 
164     connect(_messageFrame, &MessageFrame::linkActivated,
165                 this, &WorldScene::messageLinkActivated);
166     connect(_snapTimer, &QTimer::timeout, this, &WorldScene::snapUpdateToolTip);
167 }
168 
~WorldScene()169 WorldScene::~WorldScene()
170 {
171 }
172 
itemFromGraphics(const QGraphicsItem * graphicsItem) const173 StepCore::Item* WorldScene::itemFromGraphics(const QGraphicsItem* graphicsItem) const
174 {
175     const StepGraphicsItem* worldGraphicsItem =
176             dynamic_cast<const StepGraphicsItem*>(graphicsItem);
177     if(worldGraphicsItem != NULL) return worldGraphicsItem->item();
178     else return NULL;
179 }
180 
graphicsFromItem(const StepCore::Item * item) const181 StepGraphicsItem* WorldScene::graphicsFromItem(const StepCore::Item* item) const
182 {
183     return _itemsHash.value(item, NULL);
184 }
185 
beginAddItem(const QString & name)186 void WorldScene::beginAddItem(const QString& name)
187 {
188     //_currentCreator = name;
189     if(_itemCreator) {
190         _itemCreator->abort();
191         emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL);
192         delete _itemCreator;
193     }
194     if(name == QLatin1String("Pointer")) {
195         _itemCreator = NULL;
196     } else {
197         _itemCreator = _worldModel->worldFactory()->newItemCreator(name, _worldModel, this);
198         Q_ASSERT(_itemCreator != NULL);
199         _itemCreator->start();
200     }
201 }
202 
event(QEvent * event)203 bool WorldScene::event(QEvent* event)
204 {
205     //qDebug("event, _currentCreator = %s", _currentCreator.toAscii().constData());
206     /*if(!_currentCreator.isEmpty()) {
207         if(_worldModel->worldFactory()->graphicsCreateItem(_currentCreator, _worldModel,
208                             this, event)) {
209             emit endAddItem(_currentCreator, true);
210             _currentCreator.clear();
211         }
212         if(event->isAccepted()) return true;
213     }*/
214     if(_itemCreator) {
215         bool handled = _itemCreator->sceneEvent(event);
216         if(_itemCreator->finished()) {
217             emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL);
218             // ~ItemCreator() will indirectly call this method, thus we must set
219             // the pointer to NULL before deleting the ItemCreator.
220             ItemCreator* itemCreator = _itemCreator;
221             _itemCreator = NULL;
222             delete itemCreator;
223         }
224         if(handled) {
225             event->accept();
226             return true;
227         }
228     }
229     return QGraphicsScene::event(event);
230 }
231 
mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)232 void WorldScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent)
233 {
234     if(itemAt(mouseEvent->scenePos(), QTransform()) == NULL) {
235         // XXX: how to easily select World ?
236         //_worldModel->selectionModel()->clearSelection();
237         _worldModel->selectionModel()->setCurrentIndex(_worldModel->worldIndex(), QItemSelectionModel::Clear);
238     }
239 
240     QGraphicsScene::mousePressEvent(mouseEvent);
241 }
242 
helpEvent(QGraphicsSceneHelpEvent * helpEvent)243 void WorldScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
244 {
245     helpEvent->accept();
246 
247     if(_snapItem || _snapTimer->isActive()) return;
248 
249     QString text;
250 
251     QList<StepCore::Item*> activeItems;
252     foreach(QGraphicsItem* it, items(helpEvent->scenePos())) {
253         if(it->parentItem()) continue;
254         StepCore::Item* item = itemFromGraphics(it);
255         if(item) activeItems << item;
256     }
257 
258     int count = activeItems.count();
259     if(count > 1) { //XXX
260         text = QStringLiteral("<nobr><h4><u>%1</u></h4></nobr>").arg(i18n("Objects under mouse:"));
261         for(int i=0; i<qMin<int>(count,10); ++i)
262             text += QStringLiteral("<br /><nobr>%1</nobr>")
263                         .arg(_worldModel->objectIndex(activeItems[i]).data(Qt::DisplayRole).toString());
264         if(count > 10)
265             text += QStringLiteral("<br /><nobr>%1</nobr>").arg(i18np("... (1 more item)", "... (%1 more items)", count - 10));
266     } else {
267         for(int i=0; i<count; ++i)
268             text += _worldModel->objectIndex(activeItems[i]).data(Qt::ToolTipRole).toString();
269     }
270 
271     Q_ASSERT(helpEvent->widget());
272     QToolTip::showText(helpEvent->screenPos(), text, helpEvent->widget(), QRect());
273 }
274 
worldModelReset()275 void WorldScene::worldModelReset()
276 {
277     /* Clear */
278     while(!items().isEmpty()) {
279         QGraphicsItem* item = items()[0];
280         removeItem(item);
281         delete item;
282     }
283     _itemsHash.clear();
284     _sceneAxes = 0;
285 
286     /* Background */
287     if(_bgColor != _worldModel->world()->color()) {
288         _bgColor = _worldModel->world()->color();
289         if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush);
290         else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor)));
291     }
292 
293     /* Axes */
294     if(Settings::showAxes()) {
295         _sceneAxes = new WorldSceneAxes();
296         addItem(_sceneAxes);
297         _sceneAxes->viewScaleChanged();
298     }
299 
300     /* Check for new items */
301     worldGetItemsRecursive(_worldModel->worldIndex());
302 
303     update();
304 }
305 
worldGetItemsRecursive(const QModelIndex & parent)306 void WorldScene::worldGetItemsRecursive(const QModelIndex& parent)
307 {
308     for(int i=0; i<_worldModel->rowCount(parent); ++i) {
309         worldRowsInserted(parent, i, i);
310         worldGetItemsRecursive(_worldModel->index(i, 0, parent));
311     }
312 }
313 
worldRowsInserted(const QModelIndex & parent,int start,int end)314 void WorldScene::worldRowsInserted(const QModelIndex& parent, int start, int end)
315 {
316     for(int i=start; i<=end; ++i) {
317         QModelIndex index = _worldModel->index(i, 0, parent);
318 
319         StepCore::Item* item = _worldModel->item(index);
320         if(!item) continue;
321         StepGraphicsItem* graphicsItem =
322             _worldModel->worldFactory()->newGraphicsItem(item, _worldModel);
323         if(!graphicsItem) continue;
324 
325         _itemsHash.insert(item, graphicsItem);
326         addItem(graphicsItem);
327         graphicsItem->viewScaleChanged();
328         graphicsItem->worldDataChanged(false);
329         foreach(QGraphicsItem *item, items()) {
330             if(graphicsItem->isAncestorOf(item)) {
331                 StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
332                 if(gItem) gItem->viewScaleChanged();
333             }
334         }
335 
336         worldGetItemsRecursive(index);
337     }
338 }
339 
worldRowsAboutToBeRemoved(const QModelIndex & parent,int start,int end)340 void WorldScene::worldRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
341 {
342     for(int i=start; i<=end; ++i) {
343         QModelIndex index = _worldModel->index(i, 0, parent);
344 
345         int childCount = _worldModel->rowCount(index);
346         if(childCount > 0) worldRowsAboutToBeRemoved(index, 0, childCount-1);
347 
348         QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(index));
349         if(graphicsItem) {
350             removeItem(graphicsItem);
351             delete graphicsItem;
352         }
353     }
354 }
355 
worldCurrentChanged(const QModelIndex & current,const QModelIndex &)356 void WorldScene::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
357 {
358     if(!_worldView || _worldView->viewport()->hasFocus()) return;
359     QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(current));
360     if(graphicsItem) graphicsItem->ensureVisible(QRectF(), 5, 5);
361 }
362 
worldSelectionChanged(const QItemSelection & selected,const QItemSelection & deselected)363 void WorldScene::worldSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
364 {
365     foreach(const QModelIndex &index, selected.indexes()) {
366         QGraphicsItem* item = _itemsHash.value(_worldModel->item(index));
367         if(item) item->setSelected(true);
368     }
369     foreach(const QModelIndex &index, deselected.indexes()) {
370         QGraphicsItem* item = _itemsHash.value(_worldModel->item(index));
371         if(item) item->setSelected(false);
372     }
373 }
374 
worldDataChanged(bool dynamicOnly)375 void WorldScene::worldDataChanged(bool dynamicOnly)
376 {
377     //if(dynamicOnly) return;
378     _worldModel->simulationPause();
379 
380     if(!dynamicOnly) {
381         /* Background */
382         if(_bgColor != _worldModel->world()->color()) {
383             _bgColor = _worldModel->world()->color();
384             if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush);
385             else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor)));
386         }
387     }
388 
389     foreach (QGraphicsItem *item, items()) {
390         StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
391         if(gItem) {
392             gItem->worldDataChanged(dynamicOnly);
393         }
394     }
395 
396     QRectF boundingRect = itemsBoundingRect();
397     setSceneRect(boundingRect.united(QRectF(-20, -20, 40, 40)));
398 }
399 
updateViewScale()400 void WorldScene::updateViewScale()
401 {
402     if(_worldView) {
403         _currentViewScale = _worldView->matrix().m11();
404         _worldModel->simulationPause();
405         foreach (QGraphicsItem *item, items()) {
406             StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
407             if(gItem) gItem->viewScaleChanged();
408         }
409         if(_sceneAxes) _sceneAxes->viewScaleChanged();
410     }
411 }
412 
calcItemsBoundingRect()413 QRectF WorldScene::calcItemsBoundingRect()
414 {
415     QRectF boundingRect;
416     foreach(QGraphicsItem* item, items()) {
417         StepGraphicsItem* wItem = dynamic_cast<StepGraphicsItem*>(item);
418         if(wItem) {
419             boundingRect |= wItem->sceneBoundingRect();
420             //qDebug() << itemFromGraphics(wItem)->name() << ": " << wItem->sceneBoundingRect() << endl;
421         }
422     }
423     return boundingRect;
424 }
425 
messageLinkActivated(const QString & link)426 void WorldScene::messageLinkActivated(const QString& link)
427 {
428     emit linkActivated(QUrl::fromUserInput(link));
429 }
430 
settingsChanged()431 void WorldScene::settingsChanged()
432 {
433     worldModelReset();
434     _messageFrame->raise();
435 }
436 
snapClear()437 void WorldScene::snapClear()
438 {
439     if(_snapItem) {
440         _snapItem->setItemHighlighted(false);
441         _snapItem = 0;
442         _snapToolTip.clear();
443         _snapTimer->start();
444     }
445 }
446 
snapHighlight(QPointF pos,SnapFlags flags,const SnapList * moreTypes)447 StepCore::Item* WorldScene::snapHighlight(QPointF pos, SnapFlags flags, const SnapList* moreTypes)
448 {
449     SnapList types;
450     if(flags.testFlag(SnapParticle)) types << StepCore::Particle::staticMetaObject();
451     if(flags.testFlag(SnapRigidBody)) types << StepCore::RigidBody::staticMetaObject();
452     if(moreTypes) types << *moreTypes;
453 
454     StepCore::Item* item = 0;
455     QGraphicsItem* gItem = 0;
456     foreach(gItem, items(pos)) {
457         item = itemFromGraphics(gItem); if(!item) continue;
458         if(flags.testFlag(SnapParticle) && item->metaObject()->inherits<StepCore::Particle>()) break;
459         if(flags.testFlag(SnapRigidBody) && item->metaObject()->inherits<StepCore::RigidBody>()) break;
460 
461         if(moreTypes) {
462             bool found = false;
463             foreach(const StepCore::MetaObject* type, *moreTypes)
464                 if(item->metaObject()->inherits(type)) { found = true; break; }
465             if(found) break;
466         }
467         item = NULL;
468     }
469 
470     if(item) {
471         if(_snapItem != gItem) {
472             snapClear();
473             _snapItem = static_cast<StepGraphicsItem*>(gItem);
474             _snapItem->setItemHighlighted(true);
475         }
476         _snapPos = pos;
477         _snapToolTip = _worldModel->formatNameFull(item);
478         _snapTimer->start();
479         return item;
480 
481     } else {
482         snapClear();
483         return 0;
484     }
485 }
486 
snapItem(QPointF pos,SnapFlags flags,const SnapList * moreTypes,int movingState,StepCore::Item * item,int num)487 StepCore::Item* WorldScene::snapItem(QPointF pos, SnapFlags flags, const SnapList* moreTypes,
488                                             int movingState, StepCore::Item* item, int num)
489 {
490     QString n;
491     if(num >= 0) n = QString::number(num);
492 
493     _worldModel->simulationPause();
494     StepCore::Item* sItem = snapHighlight(pos, flags, moreTypes);
495 
496     if(movingState == StepGraphicsItem::Started || movingState == StepGraphicsItem::Moving) {
497         if(movingState == StepGraphicsItem::Started)
498             _worldModel->setProperty(item, "body"+n,
499                     QVariant::fromValue<StepCore::Object*>(NULL), WorldModel::UndoNoMerge);
500 
501         if(flags.testFlag(SnapSetPosition))
502             _worldModel->setProperty(item, "position"+n,
503                                 QVariant::fromValue(StepGraphicsItem::pointToVector(pos)));
504 
505         if(flags.testFlag(SnapSetLocalPosition))
506             _worldModel->setProperty(item, "localPosition"+n,
507                                 QVariant::fromValue(StepGraphicsItem::pointToVector(pos)));
508 
509         if(flags.testFlag(SnapSetAngle) && movingState == StepGraphicsItem::Started)
510             _worldModel->setProperty(item, "angle"+n, QVariant::fromValue(0.0));
511 
512     } else if(movingState == StepGraphicsItem::Finished) {
513         StepCore::Vector2d wPos(StepGraphicsItem::pointToVector(pos));
514         StepCore::Vector2d lPos(0,0);
515         double angle = 0.0;
516 
517         if(sItem) {
518             if(sItem->metaObject()->inherits<StepCore::Particle>()) {
519                 wPos = static_cast<StepCore::Particle*>(sItem)->position();
520             } else if(sItem->metaObject()->inherits<StepCore::RigidBody>()) {
521                 if(flags.testFlag(SnapOnCenter))
522                     wPos = static_cast<StepCore::RigidBody*>(sItem)->position();
523                 else
524                     lPos = static_cast<StepCore::RigidBody*>(sItem)->pointWorldToLocal(wPos);
525                 angle = static_cast<StepCore::RigidBody*>(sItem)->angle();
526             }
527 
528         } else {
529             lPos = wPos;
530         }
531 
532         _worldModel->setProperty(item, "body"+n,
533                     QVariant::fromValue<StepCore::Object*>(sItem), WorldModel::UndoNoMerge);
534 
535         if(flags.testFlag(SnapSetPosition))
536             _worldModel->setProperty(item, "position"+n, QVariant::fromValue(wPos));
537         if(flags.testFlag(SnapSetLocalPosition))
538             _worldModel->setProperty(item, "localPosition"+n, QVariant::fromValue(lPos));
539         if(flags.testFlag(SnapSetAngle))
540             _worldModel->setProperty(item, "angle"+n, angle);
541 
542         snapClear();
543     }
544 
545     return sItem;
546 }
547 
snapUpdateToolTip()548 void WorldScene::snapUpdateToolTip()
549 {
550     if(!_snapToolTip.isEmpty()) {
551         QGraphicsView* view = views()[0];
552         QPoint pos = view->viewport()->mapToGlobal(view->mapFromScene(_snapPos));
553         QPoint size(1,1);
554         QToolTip::showText(pos, _snapToolTip, view->viewport(), QRect(pos-size,pos+size));
555     } else {
556         QToolTip::hideText();
557         // Hack to hide tooltip immediately
558         QWheelEvent fakeEvent(QPoint(0,0), 0, Qt::NoButton, Qt::NoModifier);
559         QCoreApplication::sendEvent(_messageFrame, &fakeEvent);
560     }
561 }
562 
hasItemCreator() const563 bool WorldScene::hasItemCreator() const
564 {
565     return _itemCreator && !_itemCreator->finished();
566 }
567 
WorldGraphicsView(WorldScene * worldScene,QWidget * parent)568 WorldGraphicsView::WorldGraphicsView(WorldScene* worldScene, QWidget* parent)
569     : QGraphicsView(worldScene, parent)
570 {
571     worldScene->_worldView = this;
572     worldScene->_messageFrame->setParent(this);
573     worldScene->_messageFrame->move(15,15);
574 
575     _sceneRect = worldScene->sceneRect();
576     updateSceneRect();
577     connect(worldScene, &QGraphicsScene::sceneRectChanged,
578             this, &WorldGraphicsView::sceneRectChanged);
579 
580     //worldGraphicsView->setRenderHints(QPainter::Antialiasing);
581     setDragMode(QGraphicsView::RubberBandDrag);
582     //setDragMode(QGraphicsView::ScrollHandDrag);
583     setResizeAnchor(QGraphicsView::AnchorViewCenter);
584     #ifdef __GNUC__
585     #warning Check paint() for all items to preserve painter state
586     #warning Use NoViewportUpdate and manual updating here !
587     #endif
588     setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
589     actualSize();
590     settingsChanged();
591 }
592 
zoomIn()593 void WorldGraphicsView::zoomIn()
594 {
595     scale(1.25, 1.25);
596     updateSceneRect();
597 
598     static_cast<WorldScene*>(scene())->updateViewScale();
599 }
600 
zoomOut()601 void WorldGraphicsView::zoomOut()
602 {
603     scale(1 / 1.25, 1 / 1.25);
604     updateSceneRect();
605 
606     static_cast<WorldScene*>(scene())->updateViewScale();
607 }
608 
fitToPage()609 void WorldGraphicsView::fitToPage()
610 {
611     QRectF br = static_cast<WorldScene*>(scene())->calcItemsBoundingRect();
612     if(br.isNull())
613         return;
614     //qDebug() << br << " " << (br | QRectF(0,0,0,0)) << endl;
615     QRect  ws = viewport()->rect();
616 
617     double currentViewScale = matrix().m11();
618     double s = qMin( ws.width()/br.width(), ws.height()/br.height() );
619 
620     // XXX: use QSize::scale !
621 
622     if(s < currentViewScale || s*0.8 > currentViewScale) {
623         s *= 0.9;
624         resetMatrix();
625         scale(s, -s);
626         updateSceneRect();
627     } else {
628         s = currentViewScale;
629     }
630 
631     centerOn(br.center());
632 
633     if(s != currentViewScale)
634         static_cast<WorldScene*>(scene())->updateViewScale();
635 }
636 
actualSize()637 void WorldGraphicsView::actualSize()
638 {
639     resetMatrix();
640     scale(100, -100);
641     updateSceneRect();
642 
643     centerOn(0, 0);
644 
645     static_cast<WorldScene*>(scene())->updateViewScale();
646 }
647 
settingsChanged()648 void WorldGraphicsView::settingsChanged()
649 {
650     if(qobject_cast<QGLWidget*>(viewport())) {
651         if(!Settings::enableOpenGL()) setViewport(new QWidget(this));
652     } else {
653         if(Settings::enableOpenGL() && QGLFormat::hasOpenGL()) {
654             //qDebug() << "enable OpenGL" << endl;
655             setViewport(new QGLWidget(QGLFormat(QGL::SampleBuffers), this));
656             if(!qobject_cast<QGLWidget*>(viewport())) {
657                 qDebug() << "can't create QGLWidget!" << endl;
658             }
659         }
660     }
661     if(scene()) static_cast<WorldScene*>(scene())->settingsChanged();
662 }
663 
mousePressEvent(QMouseEvent * e)664 void WorldGraphicsView::mousePressEvent(QMouseEvent* e)
665 {
666     if(e->button() == Qt::MiddleButton) {
667         setDragMode(QGraphicsView::ScrollHandDrag);
668         QMouseEvent e1(e->type(), e->pos(), e->globalPos(), Qt::LeftButton,
669                         e->buttons(), e->modifiers());
670         QGraphicsView::mousePressEvent(&e1);
671     } else {
672         QGraphicsView::mousePressEvent(e);
673     }
674 }
675 
mouseReleaseEvent(QMouseEvent * e)676 void WorldGraphicsView::mouseReleaseEvent(QMouseEvent* e)
677 {
678     QGraphicsView::mouseReleaseEvent(e);
679     if(e->button() == Qt::MiddleButton) {
680         setDragMode(QGraphicsView::RubberBandDrag);
681     }
682 }
683 
wheelEvent(QWheelEvent * e)684 void WorldGraphicsView::wheelEvent(QWheelEvent* e)
685 {
686     if (e->modifiers() == Qt::ControlModifier) {
687         if (e->angleDelta().y() == 0) {
688             e->ignore();
689             return;
690         }
691 
692         QGraphicsView::ViewportAnchor anchor = transformationAnchor();
693         setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
694         if (e->angleDelta().y() > 0) {
695             zoomIn();
696         }
697         else {
698             zoomOut();
699         }
700         setTransformationAnchor(anchor);
701 
702         e->accept();
703         return;
704     }
705 
706     QGraphicsView::wheelEvent(e);
707 }
708 
scrollContentsBy(int dx,int dy)709 void WorldGraphicsView::scrollContentsBy(int dx, int dy)
710 {
711     QGraphicsView::scrollContentsBy(dx, dy);
712     WorldSceneAxes* axes = static_cast<WorldScene*>(scene())->_sceneAxes;
713     if(axes)
714         axes->setPos(mapToScene(viewport()->width()/2, viewport()->height()/2));
715         //axes->setPos(mapToScene(20, maximumViewportSize().height()-horizontalScrollBar()->height()-23));
716 }
717 
sceneRectChanged(const QRectF & rect)718 void WorldGraphicsView::sceneRectChanged(const QRectF& rect)
719 {
720     _sceneRect = rect;
721     updateSceneRect();
722 }
723 
updateSceneRect()724 void WorldGraphicsView::updateSceneRect()
725 {
726     QPointF topleft = mapToScene(0, 0);
727     QPointF bottomright = mapToScene(viewport()->width(), viewport()->height());
728 
729     QRectF viewportRect(topleft, bottomright);
730     QRectF sceneRect = _sceneRect.adjusted(-viewportRect.width() / 4,
731                                            viewportRect.height() / 4,
732                                            viewportRect.width() / 4,
733                                            -viewportRect.height() / 4);
734     setSceneRect(sceneRect.united(viewportRect));
735 }
736 
737