worldscene.cc 24.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* This file is part of Step.
   Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>

   Step is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   Step is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with Step; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

19
20
#include "worldscene.h"

Patrick Spendrin's avatar
Patrick Spendrin committed
21
22
23
24
#ifdef _WIN32
#include <windows.h>
#endif

25
26
#include "settings.h"

27
#include "arrow.h"
28
29
#include "worldmodel.h"
#include "worldfactory.h"
30
#include "stepgraphicsitem.h"
31
32
33
#include "worldgraphics.h"

#include <stepcore/world.h>
34
35
#include <stepcore/particle.h>
#include <stepcore/rigidbody.h>
36

Yuri Chornoivan's avatar
Yuri Chornoivan committed
37
38
#include <QCoreApplication>
#include <QGLWidget>
39
40
41
42
#include <QGraphicsItem>
#include <QGraphicsSceneMouseEvent>
#include <QItemSelectionModel>
#include <QPainter>
43
#include <QScrollBar>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
44
45
#include <QTimer>
#include <QToolTip>
Inge Wallin's avatar
Inge Wallin committed
46
#include <QUrl>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
47
#include <QWheelEvent>
Inge Wallin's avatar
Inge Wallin committed
48

Laurent Montel's avatar
Laurent Montel committed
49
#include <KLocalizedString>
50

51
52
53
54
class WorldSceneAxes: public QGraphicsItem
{
public:
    WorldSceneAxes(QGraphicsItem* parent = 0, QGraphicsScene* scene = 0);
Laurent Montel's avatar
Laurent Montel committed
55
56
57
    QRectF boundingRect() const Q_DECL_OVERRIDE;
    QPainterPath shape() const Q_DECL_OVERRIDE;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) Q_DECL_OVERRIDE;
58
59
    void viewScaleChanged();

60
61
62
63
protected:
    QRectF _boundingRect;
    double _viewScale;
    static const int LENGTH = 100;
64
65
    static const int LENGTHT = 100;
    static const int LENGTH1 = 10;
66
    static const int ARROW_STROKE = 6;
67
68
69
};

WorldSceneAxes::WorldSceneAxes(QGraphicsItem* parent, QGraphicsScene* scene)
Pepe Osca's avatar
Pepe Osca committed
70
    : QGraphicsItem(parent),
71
    _boundingRect(-LENGTH, -LENGTH, LENGTH+LENGTH, LENGTH+LENGTH)
72
{
73
    _viewScale = 1;
74
    setZValue(-100);
Pepe Osca's avatar
Pepe Osca committed
75
76
    if (scene)
      scene->addItem(this);
77
78
79
80
}

QRectF WorldSceneAxes::boundingRect() const
{
81
    return _boundingRect;
82
83
84
85
86
87
88
89
90
}

QPainterPath WorldSceneAxes::shape() const
{
    return QPainterPath();
}

void WorldSceneAxes::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
{
91
92
93
94
95
96
97
98
99
    QPen pen(Qt::gray, 2);
    pen.setCosmetic(true);
    painter->setPen(pen);//, Qt::DotLine, Qt::SquareCap, Qt::RoundJoin));
    //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH));
    //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0));
    Arrow arrow(0, LENGTH, 0, -LENGTH, 8);
    arrow.draw(painter);
    Arrow arrow2(-LENGTH, 0, LENGTH, 0, 8);
    arrow2.draw(painter);
100
101
102
103
    //painter->drawLine(QLineF(0, -LENGTH, 0, LENGTH));
    //painter->drawLine(QLineF(-LENGTH, 0, LENGTH, 0));
    //painter->drawLine(QLineF(-2, -LENGTHT, 2, -LENGTHT));
    //painter->drawLine(QLineF(LENGTHT, -2, LENGTHT, 2));
104

105
106
107
108
    //painter->drawLine(QLineF(0, -LENGTH, -0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE ));
    //painter->drawLine(QLineF(0, -LENGTH, +0.5*ARROW_STROKE, -LENGTH+0.866*ARROW_STROKE ));
    //painter->drawLine(QLineF(LENGTH, 0, LENGTH-0.866*ARROW_STROKE, -0.5*ARROW_STROKE ));
    //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,
Laurent Montel's avatar
Laurent Montel committed
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
119
120
121

    //painter->drawText(QRectF(ARROW_STROKE, -LENGTHT-50, LENGTHT, 100), Qt::AlignLeft | Qt::AlignVCenter,
    //                        QString::number( pos().y() + LENGTHT/_viewScale, 'g', 3 ));
    //painter->drawText(QRectF(LENGTHT-50, -LENGTHT, 100, LENGTHT), Qt::AlignHCenter | Qt::AlignBottom,
    //                        QString::number( pos().x() + LENGTHT/_viewScale, 'g', 3 ));
122
123
}

124
void WorldSceneAxes::viewScaleChanged()
125
{
126
127
    prepareGeometryChange();
    _viewScale = static_cast<WorldScene*>(scene())->currentViewScale();
128
    setTransform(QTransform::fromScale(1/_viewScale, -1/_viewScale), false);
129
130
131
}

WorldScene::WorldScene(WorldModel* worldModel, QObject* parent)
132
133
134
    : QGraphicsScene(parent), _worldModel(worldModel), _worldView(0),
        _currentViewScale(1), _itemCreator(0), _bgColor(0),
        _sceneAxes(0), _snapItem(0)
135
{
Pino Toscano's avatar
Pino Toscano committed
136
    #ifdef __GNUC__
137
    #warning TODO: measure what index method is faster
Pino Toscano's avatar
Pino Toscano committed
138
    #endif
139
140

    // Alternative index method is BspTreeIndex
141
142
    setItemIndexMethod(NoIndex);
    //XXX
143
144
    //setct(-200,-200,400,400);
    setSceneRect(-20, -20, 40, 40);
145

146
    _messageFrame = new MessageFrame();
147
148
149
    _snapTimer = new QTimer(this);
    _snapTimer->setInterval(0);
    _snapTimer->setSingleShot(true);
150

151
152
    worldModelReset();

Laurent Montel's avatar
Laurent Montel committed
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    connect(_worldModel, &QAbstractItemModel::modelReset, this, &WorldScene::worldModelReset);
    connect(_worldModel, &WorldModel::worldDataChanged, this, &WorldScene::worldDataChanged);
    connect(_worldModel->selectionModel(), &QItemSelectionModel::currentChanged,
                                  this, &WorldScene::worldCurrentChanged);
    connect(_worldModel->selectionModel(), &QItemSelectionModel::selectionChanged,
                                  this, &WorldScene::worldSelectionChanged);
    connect(_worldModel, &QAbstractItemModel::rowsInserted,
                this, &WorldScene::worldRowsInserted);
    connect(_worldModel, &QAbstractItemModel::rowsAboutToBeRemoved,
                         this, &WorldScene::worldRowsAboutToBeRemoved);

    connect(_messageFrame, &MessageFrame::linkActivated,
                this, &WorldScene::messageLinkActivated);
    connect(_snapTimer, &QTimer::timeout, this, &WorldScene::snapUpdateToolTip);
167
168
169
170
171
172
}

WorldScene::~WorldScene()
{
}

173
StepCore::Item* WorldScene::itemFromGraphics(const QGraphicsItem* graphicsItem) const
174
{
175
176
    const StepGraphicsItem* worldGraphicsItem =
            dynamic_cast<const StepGraphicsItem*>(graphicsItem);
177
178
179
180
    if(worldGraphicsItem != NULL) return worldGraphicsItem->item();
    else return NULL;
}

181
StepGraphicsItem* WorldScene::graphicsFromItem(const StepCore::Item* item) const
182
183
184
185
186
187
{
    return _itemsHash.value(item, NULL);
}

void WorldScene::beginAddItem(const QString& name)
{
188
189
190
191
    //_currentCreator = name;
    if(_itemCreator) {
        _itemCreator->abort();
        emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL);
192
193
        delete _itemCreator;
    }
Laurent Montel's avatar
Laurent Montel committed
194
    if(name == QLatin1String("Pointer")) {
195
196
197
198
        _itemCreator = NULL;
    } else {
        _itemCreator = _worldModel->worldFactory()->newItemCreator(name, _worldModel, this);
        Q_ASSERT(_itemCreator != NULL);
199
        _itemCreator->start();
200
    }
201
202
203
204
}

bool WorldScene::event(QEvent* event)
{
205
    //qDebug("event, _currentCreator = %s", _currentCreator.toAscii().constData());
206
    /*if(!_currentCreator.isEmpty()) {
207
208
209
210
211
212
        if(_worldModel->worldFactory()->graphicsCreateItem(_currentCreator, _worldModel,
                            this, event)) {
            emit endAddItem(_currentCreator, true);
            _currentCreator.clear();
        }
        if(event->isAccepted()) return true;
213
    }*/
214
    if(_itemCreator) {
215
216
        bool handled = _itemCreator->sceneEvent(event);
        if(_itemCreator->finished()) {
217
            emit endAddItem(_itemCreator->className(), _itemCreator->item() != NULL);
218
219
220
221
222
            // ~ItemCreator() will indirectly call this method, thus we must set
            // the pointer to NULL before deleting the ItemCreator.
            ItemCreator* itemCreator = _itemCreator;
            _itemCreator = NULL;
            delete itemCreator;
223
        }
224
225
226
227
        if(handled) {
            event->accept();
            return true;
        }
228
    }
229
230
231
232
233
    return QGraphicsScene::event(event);
}

void WorldScene::mousePressEvent(QGraphicsSceneMouseEvent* mouseEvent)
{
234
    if(itemAt(mouseEvent->scenePos(), QTransform()) == NULL) {
235
236
237
238
239
240
241
242
        // XXX: how to easily select World ?
        //_worldModel->selectionModel()->clearSelection();
        _worldModel->selectionModel()->setCurrentIndex(_worldModel->worldIndex(), QItemSelectionModel::Clear);
    }

    QGraphicsScene::mousePressEvent(mouseEvent);
}

243
244
void WorldScene::helpEvent(QGraphicsSceneHelpEvent *helpEvent)
{
245
246
247
248
    helpEvent->accept();
    
    if(_snapItem || _snapTimer->isActive()) return;

249
250
    QString text;

251
    QList<StepCore::Item*> activeItems;
252
253
254
    foreach(QGraphicsItem* it, items(helpEvent->scenePos())) {
        if(it->parentItem()) continue;
        StepCore::Item* item = itemFromGraphics(it);
255
256
257
258
259
        if(item) activeItems << item;
    }

    int count = activeItems.count();
    if(count > 1) { //XXX
Laurent Montel's avatar
Laurent Montel committed
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)
Laurent Montel's avatar
Laurent Montel committed
262
            text += QStringLiteral("<br /><nobr>%1</nobr>")
263
264
                        .arg(_worldModel->objectIndex(activeItems[i]).data(Qt::DisplayRole).toString());
        if(count > 10)
Laurent Montel's avatar
Laurent Montel committed
265
            text += QStringLiteral("<br /><nobr>%1</nobr>").arg(i18np("... (1 more item)", "... (%1 more items)", count - 10));
266
267
268
    } else {
        for(int i=0; i<count; ++i)
            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
}

275
276
277
278
279
280
281
282
283
void WorldScene::worldModelReset()
{
    /* Clear */
    while(!items().isEmpty()) {
        QGraphicsItem* item = items()[0];
        removeItem(item);
        delete item;
    }
    _itemsHash.clear();
284
    _sceneAxes = 0;
285

286
287
288
    /* Background */
    if(_bgColor != _worldModel->world()->color()) {
        _bgColor = _worldModel->world()->color();
289
        if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush);
290
291
292
        else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor)));
    }

293
    /* Axes */
294
    if(Settings::showAxes()) {
295
296
297
        _sceneAxes = new WorldSceneAxes();
        addItem(_sceneAxes);
        _sceneAxes->viewScaleChanged();
298
    }
299
300

    /* Check for new items */
301
    worldGetItemsRecursive(_worldModel->worldIndex());
302
303

    update();
304
305
306
307
308
309
310
}

void WorldScene::worldGetItemsRecursive(const QModelIndex& parent)
{
    for(int i=0; i<_worldModel->rowCount(parent); ++i) {
        worldRowsInserted(parent, i, i);
        worldGetItemsRecursive(_worldModel->index(i, 0, parent));
311
312
313
314
315
316
317
    }
}

void WorldScene::worldRowsInserted(const QModelIndex& parent, int start, int end)
{
    for(int i=start; i<=end; ++i) {
        QModelIndex index = _worldModel->index(i, 0, parent);
318
319
320

        StepCore::Item* item = _worldModel->item(index);
        if(!item) continue;
321
        StepGraphicsItem* graphicsItem =
322
            _worldModel->worldFactory()->newGraphicsItem(item, _worldModel);
323
324
325
326
327
328
329
330
        if(!graphicsItem) continue;

        _itemsHash.insert(item, graphicsItem);
        addItem(graphicsItem);
        graphicsItem->viewScaleChanged();
        graphicsItem->worldDataChanged(false);
        foreach(QGraphicsItem *item, items()) {
            if(graphicsItem->isAncestorOf(item)) {
331
                StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
332
                if(gItem) gItem->viewScaleChanged();
333
            }
334
        }
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
335
336

        worldGetItemsRecursive(index);
337
338
339
340
341
342
343
    }
}

void WorldScene::worldRowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
{
    for(int i=start; i<=end; ++i) {
        QModelIndex index = _worldModel->index(i, 0, parent);
344
345
346
347
        
        int childCount = _worldModel->rowCount(index);
        if(childCount > 0) worldRowsAboutToBeRemoved(index, 0, childCount-1);

348
        QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(index));
349
350
351
352
353
354
355
356
357
        if(graphicsItem) {
            removeItem(graphicsItem);
            delete graphicsItem;
        }
    }
}

void WorldScene::worldCurrentChanged(const QModelIndex& current, const QModelIndex& /*previous*/)
{
358
    if(!_worldView || _worldView->viewport()->hasFocus()) return;
359
    QGraphicsItem* graphicsItem = graphicsFromItem(_worldModel->item(current));
360
    if(graphicsItem) graphicsItem->ensureVisible(QRectF(), 5, 5);
361
362
363
364
}

void WorldScene::worldSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
365
    foreach(const QModelIndex &index, selected.indexes()) {
366
        QGraphicsItem* item = _itemsHash.value(_worldModel->item(index));
367
368
        if(item) item->setSelected(true);
    }
369
    foreach(const QModelIndex &index, deselected.indexes()) {
370
        QGraphicsItem* item = _itemsHash.value(_worldModel->item(index));
371
372
373
374
        if(item) item->setSelected(false);
    }
}

375
void WorldScene::worldDataChanged(bool dynamicOnly)
376
{
377
    //if(dynamicOnly) return;
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
378
    _worldModel->simulationPause();
379
380
381
382
383

    if(!dynamicOnly) {
        /* Background */
        if(_bgColor != _worldModel->world()->color()) {
            _bgColor = _worldModel->world()->color();
384
            if(_bgColor == 0) setBackgroundBrush(Qt::NoBrush);
385
386
387
388
            else setBackgroundBrush(QBrush(QColor::fromRgba(_bgColor)));
        }
    }

389
    foreach (QGraphicsItem *item, items()) {
390
        StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
391
392
393
        if(gItem) {
            gItem->worldDataChanged(dynamicOnly);
        }
394
    }
395
396
397
    
    QRectF boundingRect = itemsBoundingRect();
    setSceneRect(boundingRect.united(QRectF(-20, -20, 40, 40)));
398
399
}

400
401
void WorldScene::updateViewScale()
{
402
403
    if(_worldView) {
        _currentViewScale = _worldView->matrix().m11();
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
404
        _worldModel->simulationPause();
405
        foreach (QGraphicsItem *item, items()) {
406
            StepGraphicsItem* gItem = dynamic_cast<StepGraphicsItem*>(item);
407
408
            if(gItem) gItem->viewScaleChanged();
        }
409
        if(_sceneAxes) _sceneAxes->viewScaleChanged();
410
411
412
    }
}

413
414
415
416
QRectF WorldScene::calcItemsBoundingRect()
{
    QRectF boundingRect;
    foreach(QGraphicsItem* item, items()) {
417
        StepGraphicsItem* wItem = dynamic_cast<StepGraphicsItem*>(item);
418
419
        if(wItem) {
            boundingRect |= wItem->sceneBoundingRect();
420
            //qDebug() << itemFromGraphics(wItem)->name() << ": " << wItem->sceneBoundingRect() << endl;
421
        }
422
423
424
425
    }
    return boundingRect;
}

426
427
void WorldScene::messageLinkActivated(const QString& link)
{
Inge Wallin's avatar
Inge Wallin committed
428
    emit linkActivated(QUrl::fromUserInput(link));
429
430
}

431
432
433
void WorldScene::settingsChanged()
{
    worldModelReset();
434
    _messageFrame->raise();
435
436
}

437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
void WorldScene::snapClear()
{
    if(_snapItem) {
        _snapItem->setItemHighlighted(false);
        _snapItem = 0;
        _snapToolTip.clear();
        _snapTimer->start();
    }
}

StepCore::Item* WorldScene::snapHighlight(QPointF pos, SnapFlags flags, const SnapList* moreTypes)
{
    SnapList types;
    if(flags.testFlag(SnapParticle)) types << StepCore::Particle::staticMetaObject();
    if(flags.testFlag(SnapRigidBody)) types << StepCore::RigidBody::staticMetaObject();
    if(moreTypes) types << *moreTypes;

454
455
456
457
458
459
460
461
462
463
464
465
466
467
    StepCore::Item* item = 0;
    QGraphicsItem* gItem = 0;
    foreach(gItem, items(pos)) {
        item = itemFromGraphics(gItem); if(!item) continue;
        if(flags.testFlag(SnapParticle) && item->metaObject()->inherits<StepCore::Particle>()) break;
        if(flags.testFlag(SnapRigidBody) && item->metaObject()->inherits<StepCore::RigidBody>()) break;

        if(moreTypes) {
            bool found = false;
            foreach(const StepCore::MetaObject* type, *moreTypes)
                if(item->metaObject()->inherits(type)) { found = true; break; }
            if(found) break;
        }
        item = NULL;
468
469
    }

470
471
472
    if(item) {
        if(_snapItem != gItem) {
            snapClear();
473
            _snapItem = static_cast<StepGraphicsItem*>(gItem);
474
475
476
477
478
479
480
481
482
483
484
            _snapItem->setItemHighlighted(true);
        }
        _snapPos = pos;
        _snapToolTip = _worldModel->formatNameFull(item);
        _snapTimer->start();
        return item;

    } else {
        snapClear();
        return 0;
    }
485
486
}

487
488
StepCore::Item* WorldScene::snapItem(QPointF pos, SnapFlags flags, const SnapList* moreTypes,
                                            int movingState, StepCore::Item* item, int num)
489
490
491
492
{
    QString n;
    if(num >= 0) n = QString::number(num);

493
    _worldModel->simulationPause();
494
495
    StepCore::Item* sItem = snapHighlight(pos, flags, moreTypes);

496
497
    if(movingState == StepGraphicsItem::Started || movingState == StepGraphicsItem::Moving) {
        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
513
    } else if(movingState == StepGraphicsItem::Finished) {
        StepCore::Vector2d wPos(StepGraphicsItem::pointToVector(pos));
514
515
        StepCore::Vector2d lPos(0,0);
        double angle = 0.0;
516

517
518
519
520
521
522
523
524
525
526
        if(sItem) {
            if(sItem->metaObject()->inherits<StepCore::Particle>()) {
                wPos = static_cast<StepCore::Particle*>(sItem)->position();
            } else if(sItem->metaObject()->inherits<StepCore::RigidBody>()) {
                if(flags.testFlag(SnapOnCenter))
                    wPos = static_cast<StepCore::RigidBody*>(sItem)->position();
                else
                    lPos = static_cast<StepCore::RigidBody*>(sItem)->pointWorldToLocal(wPos);
                angle = static_cast<StepCore::RigidBody*>(sItem)->angle();
            }
527
528

        } else {
529
            lPos = wPos;
530
        }
531

532
        _worldModel->setProperty(item, "body"+n,
533
534
535
                    QVariant::fromValue<StepCore::Object*>(sItem), WorldModel::UndoNoMerge);

        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
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
    }

    return sItem;
}

void WorldScene::snapUpdateToolTip()
{
    if(!_snapToolTip.isEmpty()) {
        QGraphicsView* view = views()[0];
        QPoint pos = view->viewport()->mapToGlobal(view->mapFromScene(_snapPos));
        QPoint size(1,1);
        QToolTip::showText(pos, _snapToolTip, view->viewport(), QRect(pos-size,pos+size));
    } else {
        QToolTip::hideText();
        // Hack to hide tooltip immediately
        QWheelEvent fakeEvent(QPoint(0,0), 0, Qt::NoButton, Qt::NoModifier);
        QCoreApplication::sendEvent(_messageFrame, &fakeEvent);
    }
}

563
564
565
566
567
bool WorldScene::hasItemCreator() const
{
    return _itemCreator && !_itemCreator->finished();
}

568
569
570
WorldGraphicsView::WorldGraphicsView(WorldScene* worldScene, QWidget* parent)
    : QGraphicsView(worldScene, parent)
{
571
    worldScene->_worldView = this;
572
573
    worldScene->_messageFrame->setParent(this);
    worldScene->_messageFrame->move(15,15);
574
575
576
    
    _sceneRect = worldScene->sceneRect();
    updateSceneRect();
Laurent Montel's avatar
Laurent Montel committed
577
578
    connect(worldScene, &QGraphicsScene::sceneRectChanged,
            this, &WorldGraphicsView::sceneRectChanged);
579
    
580
581
    //worldGraphicsView->setRenderHints(QPainter::Antialiasing);
    setDragMode(QGraphicsView::RubberBandDrag);
582
    //setDragMode(QGraphicsView::ScrollHandDrag);
583
    setResizeAnchor(QGraphicsView::AnchorViewCenter);
Pino Toscano's avatar
Pino Toscano committed
584
    #ifdef __GNUC__
585
    #warning Check paint() for all items to preserve painter state
586
    #warning Use NoViewportUpdate and manual updating here !
Pino Toscano's avatar
Pino Toscano committed
587
    #endif
588
    setViewportUpdateMode(QGraphicsView::SmartViewportUpdate);
589
    actualSize();
590
    settingsChanged();
591
592
593
594
595
}

void WorldGraphicsView::zoomIn()
{
    scale(1.25, 1.25);
596
597
    updateSceneRect();
    
598
599
600
601
602
    static_cast<WorldScene*>(scene())->updateViewScale();
}

void WorldGraphicsView::zoomOut()
{
603
604
605
    scale(1 / 1.25, 1 / 1.25);
    updateSceneRect();
    
606
607
    static_cast<WorldScene*>(scene())->updateViewScale();
}
608

609
void WorldGraphicsView::fitToPage()
610
{
611
    QRectF br = static_cast<WorldScene*>(scene())->calcItemsBoundingRect();
612
613
    if(br.isNull())
        return;
614
    //qDebug() << br << " " << (br | QRectF(0,0,0,0)) << endl;
615
    QRect  ws = viewport()->rect();
616

617
618
    double currentViewScale = matrix().m11();
    double s = qMin( ws.width()/br.width(), ws.height()/br.height() );
619

620
621
    // XXX: use QSize::scale !

622
623
624
625
    if(s < currentViewScale || s*0.8 > currentViewScale) {
        s *= 0.9;
        resetMatrix();
        scale(s, -s);
626
        updateSceneRect();
627
628
629
630
    } else {
        s = currentViewScale;
    }

631
    centerOn(br.center());
632
633
634

    if(s != currentViewScale)
        static_cast<WorldScene*>(scene())->updateViewScale();
635
636
}

637
638
639
void WorldGraphicsView::actualSize()
{
    resetMatrix();
640
    scale(100, -100);
641
642
    updateSceneRect();
    
643
    centerOn(0, 0);
644
    
645
646
647
    static_cast<WorldScene*>(scene())->updateViewScale();
}

648
649
void WorldGraphicsView::settingsChanged()
{
650
651
652
653
    if(qobject_cast<QGLWidget*>(viewport())) {
        if(!Settings::enableOpenGL()) setViewport(new QWidget(this));
    } else {
        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
}

664
665
void WorldGraphicsView::mousePressEvent(QMouseEvent* e)
{
666
    if(e->button() == Qt::MidButton) {
667
        setDragMode(QGraphicsView::ScrollHandDrag);
668
669
670
671
672
673
        QMouseEvent e1(e->type(), e->pos(), e->globalPos(), Qt::LeftButton,
                        e->buttons(), e->modifiers());
        QGraphicsView::mousePressEvent(&e1);
    } else {
        QGraphicsView::mousePressEvent(e);
    }
674
675
676
677
678
679
680
681
682
683
}

void WorldGraphicsView::mouseReleaseEvent(QMouseEvent* e)
{
    QGraphicsView::mouseReleaseEvent(e);
    if(e->button() == Qt::MidButton) {
        setDragMode(QGraphicsView::RubberBandDrag);
    }
}

684
685
686
void WorldGraphicsView::wheelEvent(QWheelEvent* e)
{
    if (e->modifiers() == Qt::ControlModifier) {
Laurent Montel's avatar
Laurent Montel committed
687
        if (e->angleDelta().y() == 0) {
688
689
690
691
692
693
            e->ignore();
            return;
        }
        
        QGraphicsView::ViewportAnchor anchor = transformationAnchor();
        setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
Laurent Montel's avatar
Laurent Montel committed
694
        if (e->angleDelta().y() > 0) {
695
696
697
698
699
700
701
702
703
704
705
706
707
708
            zoomIn();
        }
        else {
            zoomOut();
        }
        setTransformationAnchor(anchor);
        
        e->accept();
        return;
    }
    
    QGraphicsView::wheelEvent(e);
}

709
710
711
712
713
714
715
716
717
void WorldGraphicsView::scrollContentsBy(int dx, int dy)
{
    QGraphicsView::scrollContentsBy(dx, dy);
    WorldSceneAxes* axes = static_cast<WorldScene*>(scene())->_sceneAxes;
    if(axes)
        axes->setPos(mapToScene(viewport()->width()/2, viewport()->height()/2));
        //axes->setPos(mapToScene(20, maximumViewportSize().height()-horizontalScrollBar()->height()-23));
}

718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
void WorldGraphicsView::sceneRectChanged(const QRectF& rect)
{
    _sceneRect = rect;
    updateSceneRect();
}

void WorldGraphicsView::updateSceneRect()
{
    QPointF topleft = mapToScene(0, 0);
    QPointF bottomright = mapToScene(viewport()->width(), viewport()->height());
    
    QRectF viewportRect(topleft, bottomright);
    QRectF sceneRect = _sceneRect.adjusted(-viewportRect.width() / 4,
                                           viewportRect.height() / 4,
                                           viewportRect.width() / 4,
                                           -viewportRect.height() / 4);
    setSceneRect(sceneRect.united(viewportRect));
}