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