toolgraphics.cc 68.3 KB
Newer Older
1
2
/* This file is part of Step.
   Copyright (C) 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com>
3
   Copyright (C) 2014 Inge Wallin        <inge@lysator.liu.se>
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

   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
*/

#include "toolgraphics.h"

22
#include "ui_configure_graph.h"
23
#include "ui_configure_meter.h"
24
#include "ui_configure_controller.h"
25

Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
26
27
28
#include <stepcore/tool.h>
#include <stepcore/particle.h>
#include <stepcore/rigidbody.h>
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
29
30
31
#include <stepcore/solver.h>
#include <stepcore/collisionsolver.h>

32
#include "worldmodel.h"
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
33
#include "worldscene.h"
34
#include "worldfactory.h"
35
#include "latexformula.h"
Yuri Chornoivan's avatar
Yuri Chornoivan committed
36

Yuri Chornoivan's avatar
Yuri Chornoivan committed
37
#include <QAbstractButton>
38
#include <QAbstractTextDocumentLayout>
Inge Wallin's avatar
Inge Wallin committed
39
#include <QAction>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
40
41
#include <QApplication>
#include <QColorDialog>
42
43
#include <QDialog>
#include <QDialogButtonBox>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
44
45
46
47
48
49
50
#include <QEvent>
#include <QFileDialog>
#include <QFocusEvent>
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QGraphicsView>
#include <QGridLayout>
51
#include <QInputDialog>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
52
53
54
55
56
57
#include <QItemSelectionModel>
#include <QLCDNumber>
#include <QLabel>
#include <QPainter>
#include <QPushButton>
#include <QStyleOptionGraphicsItem>
58
#include <QTemporaryFile>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
59
60
61
#include <QTextDocument>
#include <QUrl>
#include <QVBoxLayout>
Inge Wallin's avatar
Inge Wallin committed
62

63
64
#include <KFontAction>
#include <KFontSizeAction>
65
66
67
#include <KIO/CopyJob>
#include <KIO/Job>
#include <KJobWidgets>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
68
69
70
71
72
73
74
75
#include <KLocalizedString>
#include <KMessageBox>
#include <KPlotAxis>
#include <KPlotObject>
#include <KPlotPoint>
#include <KPlotWidget>
#include <KToggleAction>
#include <KToolBar>
76

77
78
#include <float.h>

79

80
81
82
StepCore::Vector2d WidgetVertexHandlerGraphicsItem::value()
{
    double s = currentViewScale();
Laurent Montel's avatar
Laurent Montel committed
83
    StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))->
84
                            readVariant(_item).value<StepCore::Vector2d>()/s;
Anton Gladky's avatar
Anton Gladky committed
85
    return (size.array()* corners[_vertexNum].array()).matrix();
86
87
88
89
90
91
92
}

void WidgetVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value)
{
    double s = currentViewScale();

    QGraphicsView* activeView = scene()->views().first();
93
    QTransform viewportTransform = activeView->viewportTransform();
94

Laurent Montel's avatar
Laurent Montel committed
95
    StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))->
96
                        readVariant(_item).value<StepCore::Vector2d>()/s;
Laurent Montel's avatar
Laurent Montel committed
97
    StepCore::Vector2d position = _item->metaObject()->property(QStringLiteral("position"))->
98
99
                            readVariant(_item).value<StepCore::Vector2d>();

Anton Gladky's avatar
Anton Gladky committed
100
    StepCore::Vector2d oCorner = position - (size.array()*((corners[_vertexNum]).array())).matrix();
101
102
103

    oCorner = pointToVector( viewportTransform.inverted().map(
                QPointF(viewportTransform.map(vectorToPoint(oCorner)).toPoint()) ));
104
105
106

    StepCore::Vector2d delta = (value + position - oCorner)/2.0;
    StepCore::Vector2d newPos = oCorner + delta;
107
108
    newPos = pointToVector( viewportTransform.inverted().map(
                QPointF(viewportTransform.map(vectorToPoint(newPos)).toPoint()) ));
109
110
    StepCore::Vector2d newSize = (newPos - oCorner)*2.0;

Anton Gladky's avatar
Anton Gladky committed
111
    StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum].array())).matrix();
112
113
114
115
116
117
118
119
120
121
    double d = -0.1/s;
    if(sign[0] < d || sign[1] < d) {
        if(sign[0] < d) {
            newPos[0] = oCorner[0]; newSize[0] = 0;
            _vertexNum ^= 1;
        }
        if(sign[1] < d) {
            newPos[1] = oCorner[1]; newSize[1] = 0;
            _vertexNum ^= 2;
        }
Laurent Montel's avatar
Laurent Montel committed
122
123
        _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos));
        _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue((newSize*s).eval()));
124
125
126
127
        setValue(value);
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
128
129
    _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos));
    _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue((newSize*s).eval()));
130
131
}

132
WidgetGraphicsItem::WidgetGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
133
    : StepGraphicsItem(item, worldModel), _centralWidget(0)
134
135
136
{
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsMovable);
137
    setAcceptHoverEvents(true);
138
139
140
141
142
143
144
145
146
147
148
149
150
151

    _backgroundBrush = Qt::NoBrush;

    _boundingRect = QRectF(0, 0, 0, 0);
}

WidgetGraphicsItem::~WidgetGraphicsItem()
{
    if(_centralWidget) {
        _centralWidget->hide();
        _centralWidget->deleteLater();
    }
}

152
153
154
OnHoverHandlerGraphicsItem* WidgetGraphicsItem::createOnHoverHandler(const QPointF& pos)
{
    double s = currentViewScale();
Laurent Montel's avatar
Laurent Montel committed
155
    StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))->
156
                            readVariant(_item).value<StepCore::Vector2d>()/s;
Laurent Montel's avatar
Laurent Montel committed
157
    StepCore::Vector2d position = _item->metaObject()->property(QStringLiteral("position"))->
158
159
160
161
162
                            readVariant(_item).value<StepCore::Vector2d>();
    StepCore::Vector2d l = pointToVector(pos) - position;

    int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s;
    for(unsigned int i=0; i<4; ++i) {
Anton Gladky's avatar
Anton Gladky committed
163
        double dist2 = (l - (size.array()*(WidgetVertexHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm();
164
165
166
        if(dist2 < minDist2) { num = i; minDist2 = dist2; }
    }

167
    if(_onHoverHandler && _onHoverHandler->vertexNum() == num)
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
        return _onHoverHandler;

    if(num >= 0)
        return new WidgetVertexHandlerGraphicsItem(_item, _worldModel, this, num);

    return 0;
}

// XXX: ???
void WidgetGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState)
{
    QGraphicsView* activeView = scene()->views().first();
    QTransform itemTransform = activeView->transform() * deviceTransform(activeView->viewportTransform());
    StepCore::Vector2d newPos = pointToVector( itemTransform.inverted().map(
                QPointF(itemTransform.map(pos/*/50.0*/).toPoint()) ))/**50.0*/;
Laurent Montel's avatar
Laurent Montel committed
183
    _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos));
184
185
}

186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
void WidgetGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
{
    painter->setRenderHint(QPainter::Antialiasing, true);
    if(_isSelected) painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine));
    else painter->setPen(QPen(Qt::NoPen));
    painter->setBrush(_backgroundBrush);
    painter->drawRect(_boundingRect);
}

void WidgetGraphicsItem::setCenteralWidget(QWidget* widget)
{
    if(_centralWidget) {
        _centralWidget->hide();
        _centralWidget->deleteLater();
    }
    _centralWidget = widget;
    viewScaleChanged();
}

void WidgetGraphicsItem::viewScaleChanged()
{
207
208
    if(!scene() || scene()->views().isEmpty()) return;
    QGraphicsView* activeView = scene()->views().first();
209

Laurent Montel's avatar
Laurent Montel committed
210
    QPointF position = vectorToPoint(_item->metaObject()->property(QStringLiteral("position"))->
211
                    readVariant(_item).value<StepCore::Vector2d>());
212
213
214
215
    
    // Move item to the closest pixel position
    QPoint viewPosition = activeView->mapFromScene(position);
    setPos(activeView->mapToScene(viewPosition));
216

Laurent Montel's avatar
Laurent Montel committed
217
    StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))->
218
219
                    readVariant(_item).value<StepCore::Vector2d>();

220
221
222
223
224
225
226
227
228
229
230
231
232
    QSize viewSize(qRound(size[0]), qRound(size[1]));
    QPoint viewTopLeft =
        viewPosition - QPoint(viewSize.width() / 2, viewSize.height() / 2);
    QRect viewRect(viewTopLeft, viewSize);
    
    QRectF sceneRect =
        activeView->mapToScene(viewRect.adjusted(0, 0, 1, 1)).boundingRect();
    QRectF boundingRect = mapRectFromScene(sceneRect);
    double s = currentViewScale();
    boundingRect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s,
                        SELECTION_MARGIN/s, SELECTION_MARGIN/s);
    
    if(boundingRect != _boundingRect) {
233
        prepareGeometryChange();
234
        _boundingRect = boundingRect;
235
236
        update();
    }
237
    
238
239
240
241
242
    // Reparent the widget if necessary.
    if(_centralWidget->parentWidget() != activeView->viewport()) {
       _centralWidget->setParent(activeView->viewport());
       _centralWidget->show();
    }
243

244
    _centralWidget->setGeometry(viewRect.adjusted(0, 0, 1, 1));
245
246
247
248
249
250
251
252
253
}

void WidgetGraphicsItem::stateChanged()
{
    update();
}

void WidgetGraphicsItem::worldDataChanged(bool dynamicOnly)
{
254
    if(!dynamicOnly) {
255
        /*QPointF position = vectorToPoint(_item->metaObject()->property("position")->
256
                    readVariant(_item).value<StepCore::Vector2d>());
257
        setPos(position);*/
258
        viewScaleChanged();
259
        update();
260
    }
261
262
263
264
}

/////////////////////////////////////////////////////////////////////////////////////////

265
QString NoteTextEdit::emptyNotice() const
266
{
Andrew Coles's avatar
Andrew Coles committed
267
    return i18n("Click to enter text");
268
269
}

270
271
/*
void NoteTextEdit::focusInEvent(QFocusEvent *event)
272
{
273
    if(_noteItem->note()->text().isEmpty()) {
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
274
        ++_noteItem->_updating;
275
        setPlainText("");
276
        _noteItem->worldDataChanged(false);
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
277
        --_noteItem->_updating;
278
    }
279
280
281
282
283
    _noteItem->_hasFocus = true;
    _noteItem->_toolBar->show();
    _noteItem->setSelected(true);
    _noteItem->viewScaleChanged();
    KTextEdit::focusInEvent(event);
284
285
}

286
void NoteTextEdit::focusOutEvent(QFocusEvent *event)
287
{
288
289
    qDebug() << event->reason() << endl;
    qDebug() << QApplication::focusWidget()->metaObject()->className() << endl;
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304

    QObject* f = QApplication::focusWidget();
    if(f == this) f = NULL;
    while(f && f != _noteItem->_widget) f = f->parent();

    if(!f && event->reason() != Qt::PopupFocusReason) {
        if(_noteItem->note()->text().isEmpty()) {
            ++_noteItem->_updating;
            setPlainText(emptyNotice());
            _noteItem->worldDataChanged(false);
            --_noteItem->_updating;
        }
        _noteItem->_hasFocus = false;
        _noteItem->_toolBar->hide();
        _noteItem->viewScaleChanged();
305
    }
306
    KTextEdit::focusOutEvent(event);
307
}
308
*/
309

310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
StepCore::NoteFormula* NoteTextEdit::formulaAt(const QPoint& pos)
{
    int p = document()->documentLayout()->hitTest(pos, Qt::ExactHit);
    if(p < 0) return NULL;

    QTextCursor cursor(document());
    cursor.setPosition(p);
    if(cursor.atEnd()) return NULL;
    cursor.setPosition(p+1);

    QTextFormat format = cursor.charFormat();
    if(!format.isImageFormat()) return NULL;
    QString image = format.toImageFormat().name();

    StepCore::Item* item = _noteItem->note()->childItem(image);
    if(!item) {
        foreach(StepCore::Item* it, _noteItem->_newItems)
            if(it->name() == image) { item = it; break; }
    }

    if(!item || !item->metaObject()->inherits<StepCore::NoteFormula>())
        return NULL;

    return static_cast<StepCore::NoteFormula*>(item);
}

void NoteTextEdit::mousePressEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton) _mousePressPoint = e->pos();
    KTextEdit::mousePressEvent(e);
}

void NoteTextEdit::mouseMoveEvent(QMouseEvent *e)
{
    if(formulaAt(e->pos()) != NULL) {
        viewport()->setCursor(Qt::PointingHandCursor);
    } else {
        viewport()->setCursor(Qt::IBeamCursor);
    }
    _mousePressPoint.setX(-1);
    KTextEdit::mouseMoveEvent(e);
}

void NoteTextEdit::mouseReleaseEvent(QMouseEvent *e)
{
    if(e->button() == Qt::LeftButton && e->pos() == _mousePressPoint) {
        StepCore::NoteFormula* formula = formulaAt(e->pos());
        if(formula) {
            e->accept();
            _noteItem->editFormula(formula);
            setDocument(document());
            return;
        }
    }
    KTextEdit::mouseReleaseEvent(e);
}

367
NoteGraphicsItem::NoteGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
368
    : WidgetGraphicsItem(item, worldModel)
369
370
371
372
{
    Q_ASSERT(dynamic_cast<StepCore::Note*>(_item) != NULL);
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsMovable);
373
    setAcceptHoverEvents(true);
374

375
    _widget = new QWidget();
376
    _widget->setPalette(QPalette(Qt::lightGray));
377
378
379

    _textEdit = new NoteTextEdit(this, _widget);
    _textEdit->setFrameShape(QFrame::NoFrame);
380
381
382
383
    QPalette p = _textEdit->palette();
    p.setColor(QPalette::Base, Qt::transparent);
    _textEdit->setPalette(p);
    //_textEdit->setStyleSheet(".NoteTextEdit {background-color: rgba(0,0,0,0%);}");
384
385
386
387

    _toolBar = new KToolBar(_widget);
    _toolBar->setIconDimensions(16);
    _toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly);
388
389
390
    _toolBar->setContentsMargins(0,0,0,0);
    if(_toolBar->layout()) _toolBar->layout()->setSpacing(0);
    //_toolBar->setStyleSheet(".KToolBar {margin: 0px; border-width: 0px; padding: 0px; }");
391

Inge Wallin's avatar
Inge Wallin committed
392
    _actionColor = new QAction(QIcon(), i18n("&Color"), _toolBar);
393

Laurent Montel's avatar
Laurent Montel committed
394
    _actionBold = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-bold")), i18n("&Bold"), _toolBar);
395
    _actionBold->setShortcut(Qt::CTRL + Qt::Key_B);
Laurent Montel's avatar
Laurent Montel committed
396
    _actionItalic = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-italic")), i18n("&Italic"), _toolBar);
397
    _actionItalic->setShortcut(Qt::CTRL + Qt::Key_I);
Laurent Montel's avatar
Laurent Montel committed
398
    _actionUnderline = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-underline")), i18n("&Underline"), _toolBar);
399
400
    _actionUnderline->setShortcut(Qt::CTRL + Qt::Key_U);

Laurent Montel's avatar
Laurent Montel committed
401
    _actionAlignLeft = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-left")), i18n("Align &Left"), _toolBar);
402
    _actionAlignLeft->setShortcut(Qt::CTRL + Qt::Key_L);
Laurent Montel's avatar
Laurent Montel committed
403
    _actionAlignCenter = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-center")), i18n("Align C&enter"), _toolBar);
404
    _actionAlignCenter->setShortcut(Qt::CTRL + Qt::Key_E);
Laurent Montel's avatar
Laurent Montel committed
405
    _actionAlignRight = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-right")), i18n("Align &Right"), _toolBar);
406
    _actionAlignRight->setShortcut(Qt::CTRL + Qt::Key_R);
Laurent Montel's avatar
Laurent Montel committed
407
    _actionAlignJustify = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-fill")), i18n("Align &Justify"), _toolBar);
408
409
    _actionAlignJustify->setShortcut(Qt::CTRL + Qt::Key_J);

410
411
412
413
414
415
416
    _actionAlign = new KSelectAction(i18n("&Align"), _toolBar);
    _actionAlign->setToolBarMode(KSelectAction::MenuMode);
    _actionAlign->setToolButtonPopupMode(QToolButton::InstantPopup);
    _actionAlign->addAction(_actionAlignLeft);
    _actionAlign->addAction(_actionAlignCenter);
    _actionAlign->addAction(_actionAlignRight);
    _actionAlign->addAction(_actionAlignJustify);
417
418
    _actionAlignLeft->setChecked(true);

419
420
    _actionFont = new KFontAction(i18n("&Font"), _toolBar);
    _actionFontSize = new KFontSizeAction(i18n("Font &Size"), _toolBar);
421

Laurent Montel's avatar
Laurent Montel committed
422
    _actionInsertImage = new QAction(QIcon::fromTheme(QStringLiteral("insert-image")), i18n("Insert &Image"), _toolBar);
423
424
425
#ifdef __GNUC__
#warning Select right icon here
#endif
Laurent Montel's avatar
Laurent Montel committed
426
    _actionInsertFormula = new QAction(QIcon::fromTheme(QStringLiteral("application-vnd.oasis.opendocument.formula")),
427
                                    i18n("Insert &Formula"), _toolBar);
428

Laurent Montel's avatar
Laurent Montel committed
429
430
431
432
    connect(_actionColor, &QAction::triggered, this, &NoteGraphicsItem::formatColor);
    connect(_actionBold, &QAction::triggered, this, &NoteGraphicsItem::formatBold);
    connect(_actionItalic, &QAction::triggered, _textEdit, &QTextEdit::setFontItalic);
    connect(_actionUnderline, &QAction::triggered, _textEdit, &QTextEdit::setFontUnderline);
433
    connect(_actionAlign, SIGNAL(triggered(QAction*)), this, SLOT(formatAlign(QAction*)));
434
    connect(_actionFont, SIGNAL(triggered(QString)), this, SLOT(formatFontFamily(QString)));
Laurent Montel's avatar
Laurent Montel committed
435
436
437
    connect(_actionFontSize, &KFontSizeAction::fontSizeChanged, this, &NoteGraphicsItem::formatFontSize);
    connect(_actionInsertImage, &QAction::triggered, this, &NoteGraphicsItem::insertImage);
    connect(_actionInsertFormula, &QAction::triggered, this, &NoteGraphicsItem::insertFormula);
438
    
Laurent Montel's avatar
Laurent Montel committed
439
440
441
    connect(_textEdit, &QTextEdit::currentCharFormatChanged,
                            this, &NoteGraphicsItem::currentCharFormatChanged);
    connect(_textEdit, &QTextEdit::cursorPositionChanged, this, &NoteGraphicsItem::cursorPositionChanged);
442
443
444
445


    connect(_toolBar, SIGNAL(actionTriggered(QAction*)), _textEdit, SLOT(setFocus()));

446
    _toolBar->addAction(_actionAlign);
447
448
449
    _toolBar->addAction(_actionBold);
    _toolBar->addAction(_actionItalic);
    _toolBar->addAction(_actionUnderline);
450
    _toolBar->addAction(_actionColor);
451
    //_toolBar->addSeparator();
452

453
    //_toolBar->addSeparator();
454

455
    _toolBar->addAction(_actionInsertImage);
456
    _toolBar->addAction(_actionInsertFormula);
457

458
    _toolBar->addAction(_actionFontSize);
459
    _toolBar->addAction(_actionFont);
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476

    QVBoxLayout* layout = new QVBoxLayout(_widget);
    layout->setContentsMargins(0,0,0,0);
    layout->setSpacing(0);
    layout->addWidget(_textEdit);
    layout->addWidget(_toolBar);

    // without it focus is passed to QGrahicsView
    _toolBar->setFocusPolicy(Qt::ClickFocus);
    _toolBar->setFocusProxy(_textEdit);
    _widget->setFocusProxy(_textEdit);

    _hasFocus = false;
    _toolBar->hide();

    _textEdit->installEventFilter(this);

477
478
479
480
481
482
483
484
485
486
487
488
489
490
    QComboBox* font = qobject_cast<QComboBox*>(_toolBar->widgetForAction(_actionFont));
    if(font) {
        font->setMinimumContentsLength(5);
        font->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
        font->installEventFilter(this);
        font->setToolTip(_actionFont->toolTip());
    }

    QComboBox* fontSize = qobject_cast<QComboBox*>(_toolBar->widgetForAction(_actionFontSize));
    if(fontSize) {
        fontSize->setMinimumContentsLength(2);
        fontSize->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
        fontSize->installEventFilter(this);
        fontSize->setToolTip(_actionFontSize->toolTip());
491
492
    }

493
    setCenteralWidget(_widget);
494
495
496
497
    setOnHoverHandlerEnabled(true);
    _widget->setMouseTracking(true);
    _textEdit->setMouseTracking(true);
    _toolBar->setMouseTracking(true);
498
499
500
501
502
503
504
}

inline StepCore::Note* NoteGraphicsItem::note() const
{
    return static_cast<StepCore::Note*>(_item);
}

505
506
507
508
bool NoteGraphicsItem::eventFilter(QObject* obj, QEvent* event)
{
    if(event->type() == QEvent::FocusIn) {
        if(!_hasFocus) {
509
            _hasFocus = true;
510
            if(note()->text().isEmpty()) {
511
                //++_updating;
Laurent Montel's avatar
Laurent Montel committed
512
                _textEdit->setPlainText(QLatin1String(""));
513
                worldDataChanged(false);
514
                //--_updating;
515
516
            }
            _toolBar->show();
517
            bool multiSelect = (QApplication::keyboardModifiers() & Qt::ControlModifier) != 0;
518
            if(!multiSelect/* && !isSelected()*/) {
519
520
521
                if(scene()) scene()->clearSelection();
                _worldModel->selectionModel()->clearSelection();
            }
522
523
524
525
526
527
528
529
530
531
532
            setSelected(true);
            viewScaleChanged();
        }
    } else if(event->type() == QEvent::FocusOut &&
            static_cast<QFocusEvent*>(event)->reason() != Qt::PopupFocusReason) {

        QObject* f = QApplication::focusWidget();
        if(f == obj) f = NULL;
        while(f && f != _widget) f = f->parent();

        if(!f) {
533
534
535
536
            _worldModel->simulationPause();

            QString newText = _textEdit->toHtml();
            if(newText != note()->text()) {
537
                //++_updating;
538
539
                _worldModel->beginMacro(i18n("Edit %1", _item->name()));

540
541
                foreach(StepCore::Item* item, note()->items())
                    if(!newText.contains(item->name())) _worldModel->deleteItem(item);
542

543
544
545
546
547
548
549
                foreach(StepCore::Item* item, _newItems)
                    if(!newText.contains(item->name())) _newItems.removeAll(item);

                foreach(StepCore::Item* item, _newItems)
                    _worldModel->addItem(item, note());

                _newItems.clear();
550

Laurent Montel's avatar
Laurent Montel committed
551
                _worldModel->setProperty(_item, QStringLiteral("text"), newText);
552
553

                _worldModel->endMacro();
554
                //--_updating;
555
556
            }

557
558
            _hasFocus = false;
            _toolBar->hide();
559
            _textEdit->clear();
560
561

            viewScaleChanged();
562
            worldDataChanged(false);
563
564
565
566
567
        }
    }
    return QObject::eventFilter(obj, event);
}

568
569
570
void NoteGraphicsItem::formatColor()
{
    QColor color = _textEdit->textColor();
571
572
    color = QColorDialog::getColor(color, _widget);
    if(QColorDialog::Accepted && color.isValid()) {
573
574
575
576
        _textEdit->setTextColor(color);
    }
}

577
void NoteGraphicsItem::formatBold(bool checked)
578
{
579
580
581
582
583
584
585
586
587
588
589
590
591
    _textEdit->setFontWeight(checked ? QFont::Bold : QFont::Normal);
}

void NoteGraphicsItem::formatAlign(QAction* action)
{
    if(action == _actionAlignLeft)
        _textEdit->setAlignment(Qt::AlignLeft);
    else if(action == _actionAlignCenter)
        _textEdit->setAlignment(Qt::AlignHCenter);
    else if(action == _actionAlignRight)
        _textEdit->setAlignment(Qt::AlignRight);
    else if(action == _actionAlignJustify)
        _textEdit->setAlignment(Qt::AlignJustify);
592
593

    _actionAlign->setIcon(action->icon());
594
595
596
597
598
}

void NoteGraphicsItem::formatFontFamily(const QString& family)
{
    _textEdit->setFontFamily(family);
599
    _textEdit->setFocus();
600
601
602
603
604
605
}

void NoteGraphicsItem::formatFontSize(int size)
{
    if(size > 0) _textEdit->setFontPointSize(size);
    else currentCharFormatChanged(_textEdit->currentCharFormat());
606
    _textEdit->setFocus();
607
608
609
610
611
612
613
}

void NoteGraphicsItem::currentCharFormatChanged(const QTextCharFormat& f)
{
    _actionBold->setChecked(f.fontWeight() >= QFont::Bold);
    _actionItalic->setChecked(f.fontItalic());
    _actionUnderline->setChecked(f.fontUnderline());
614
615
616
617
618

    QPixmap pix(16,16);
    pix.fill(_textEdit->textColor());
    _actionColor->setIcon(pix);

619
    QFontInfo ff(f.font());
Pino Toscano's avatar
Pino Toscano committed
620
#ifdef __GNUC__
Pino Toscano's avatar
Pino Toscano committed
621
#warning Strange, the following line does nothing !
Pino Toscano's avatar
Pino Toscano committed
622
#endif
623
624
    _actionFont->setFont(ff.family());
    _actionFontSize->setFontSize(ff.pointSize());
625
626
627
628
629
630
631
632
633
634
635
636
}

void NoteGraphicsItem::cursorPositionChanged()
{
    if(_textEdit->alignment() & Qt::AlignLeft)
        _actionAlignLeft->setChecked(true);
    else if(_textEdit->alignment() & Qt::AlignHCenter)
        _actionAlignCenter->setChecked(true);
    else if(_textEdit->alignment() & Qt::AlignRight)
        _actionAlignRight->setChecked(true);
    else if(_textEdit->alignment() & Qt::AlignJustify)
        _actionAlignJustify->setChecked(true);
637
638

    _actionAlign->setIcon(_actionAlign->currentAction()->icon());
639
640
}

641
642
void NoteGraphicsItem::insertImage()
{
643
    QUrl url = QFileDialog::getOpenFileUrl(_widget, i18n("Open Image File"), QUrl(), i18n("Images (*.png *.jpg *.jpeg)"));
644
645
    if(url.isEmpty()) return;

646
647
648
649
650
651
652
    QTemporaryFile tempFile;
    tempFile.open();
    KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite);
    KJobWidgets::setWindow(job, _widget);
    job->exec();
    if (job->error()) {
        KMessageBox::error(_widget, job->errorString());
653
654
655
        return;
    }

656
657
    QByteArray data = tempFile.readAll();
    tempFile.close();
658
659
660

    QPixmap pixmap;
    if(!pixmap.loadFromData(data)) {
661
        KMessageBox::error(_widget, i18n("Cannot parse file '%1'", tempFile.fileName()));
662
663
664
        return;
    }

665
666
    QString imgName;
    for(int n=0;; ++n) {
Laurent Montel's avatar
Laurent Montel committed
667
        imgName = QStringLiteral("img:%1").arg(n);
668
669
670
671
672
673
674
675
        if(note()->childItem(imgName) != NULL) continue;
        bool found = false;
        foreach(StepCore::Item* item, _newItems)
            if(item->name() == imgName) { found = true; break; }
        if(!found) break;
    }
    
    _newItems << new StepCore::NoteImage(imgName, data);
676
    //_textEdit->document()->addResource(QTextDocument::ImageResource, imgName, pixmap);
Laurent Montel's avatar
Laurent Montel committed
677
    _textEdit->insertHtml(QStringLiteral("<img src=\"%1\" />").arg(imgName));
678
679
680
}

void NoteGraphicsItem::insertFormula()
681
682
683
{
    QString imgName;
    for(int n=0;; ++n) {
Laurent Montel's avatar
Laurent Montel committed
684
        imgName = QStringLiteral("fml:%1").arg(n);
685
686
687
688
689
690
691
692
693
694
695
696
697
698
        if(note()->childItem(imgName) != NULL) continue;
        bool found = false;
        foreach(StepCore::Item* item, _newItems)
            if(item->name() == imgName) { found = true; break; }
        if(!found) break;
    }

    StepCore::NoteFormula* formula = new StepCore::NoteFormula(imgName);
    if(!editFormula(formula)) {
        delete formula;
        return;
    }

    _newItems << formula;
Laurent Montel's avatar
Laurent Montel committed
699
    _textEdit->insertHtml(QStringLiteral("<img src=\"%1\" />").arg(imgName));
700
701
702
}

bool NoteGraphicsItem::editFormula(StepCore::NoteFormula* formula)
703
704
{
    if(!LatexFormula::isLatexInstalled()) {
705
        KMessageBox::sorry(_widget, i18n("Cannot find latex installation. "
706
                    "You need 'latex', 'dvips' and 'gs' executables installed and accessible from $PATH"));
707
        return false;
708
709
710
    }

    bool ok;
711
712
    QString code = QInputDialog::getMultiLineText(_widget, i18n("LaTex Formula - Step"),
                i18n("Enter LaTeX formula string"), QString(formula->code()), &ok);
713
    if(!ok) return false;
714

715
    QByteArray image;
716
717
    QString error;

718
    bool result = LatexFormula::compileFormula(code, &image, &error);
719
720
721

    if(!result) {
        KMessageBox::error(_widget, i18n("Cannot compile LaTeX formula: %1", error));
722
        return false;
723
724
725
    }

    QPixmap pixmap;
726
727
728
    if(!pixmap.loadFromData(image)) {
        KMessageBox::error(_widget, i18n("Cannot parse result image"));
        return false;
729
730
    }

731
732
    formula->setCode(code);
    formula->setImage(image);
733

734
    _textEdit->document()->addResource(QTextDocument::ImageResource,
735
                                            QUrl(formula->name()), pixmap);
736
    return true;
737
738
}

739
740
741
void NoteGraphicsItem::worldDataChanged(bool dynamicOnly)
{
    if(!dynamicOnly) {
742
743
        if(!_hasFocus && _textEdit->toHtml() != note()->text()) {
            //++_updating;
744

745
746
747
748
749
            const StepCore::ItemList::const_iterator end = note()->items().end();
            for(StepCore::ItemList::const_iterator it = note()->items().begin(); it != end; ++it) {
                if((*it)->metaObject()->inherits<StepCore::NoteImage>()) {
                    QPixmap pix;
                    pix.loadFromData(static_cast<StepCore::NoteImage*>(*it)->image());
750
                    _textEdit->document()->addResource(QTextDocument::ImageResource, QUrl((*it)->name()), pix);
751
752
753
754
                }
            }

            /*
755
756
757
758
759
760
761
            const StepCore::NoteDataMap::const_iterator end = note()->dataMap().constEnd();
            for(StepCore::NoteDataMap::const_iterator it = note()->dataMap().constBegin();
                                                                            it != end; ++it) {
                QPixmap pix;
                pix.loadFromData(it.value());
                _textEdit->document()->addResource(QTextDocument::ImageResource, it.key(), pix);
            }
762
            */
763

764
765
            if(!_textEdit->hasFocus() && note()->text().isEmpty()) {
                _textEdit->setPlainText(_textEdit->emptyNotice());
766
            } else {
767
                _textEdit->setHtml(note()->text());
768
            }
769

770
771
            currentCharFormatChanged(_textEdit->currentCharFormat());
            cursorPositionChanged();
772
            //--_updating;
773
        }
774
        WidgetGraphicsItem::worldDataChanged(dynamicOnly);
775
    }
776
777
}

778
////////////////////////////////////////////////////
779
780
781
DataSourceWidget::DataSourceWidget(QWidget* parent)
    : QWidget(parent), _worldModel(0)
{
782
783
    _skipReadOnly = false;
    
784
785
786
    QHBoxLayout *layout = new QHBoxLayout(this);
    layout->setContentsMargins(0,0,0,0);

787
    _object = new KComboBox(this);
Laurent Montel's avatar
Laurent Montel committed
788
    _object->setToolTip(i18n("Object name"));
789
    _object->setMinimumContentsLength(10);
790
791
    layout->addWidget(_object, 1);

792
    _property = new KComboBox(this);
Laurent Montel's avatar
Laurent Montel committed
793
    _property->setToolTip(i18n("Property name"));
794
    _property->setEnabled(false);
795
    _property->setMinimumContentsLength(10);
796
797
    layout->addWidget(_property, 1);

798
    _index = new KComboBox(this);
Laurent Montel's avatar
Laurent Montel committed
799
    _index->setToolTip(i18n("Vector index"));
800
801
802
803
    _index->setMinimumContentsLength(1);
    _index->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
    layout->addWidget(_index, 0);

804
805
806
807
    connect(_object, SIGNAL(activated(int)),
            this, SLOT(objectSelected(int)));
    connect(_property, SIGNAL(activated(int)),
            this, SLOT(propertySelected(int)));
808

809
    connect(_object, SIGNAL(activated(int)),
810
            this, SIGNAL(dataSourceChanged()));
811
    connect(_property, SIGNAL(activated(int)),
812
            this, SIGNAL(dataSourceChanged()));
813
    connect(_index, SIGNAL(activated(int)),
814
815
816
            this, SIGNAL(dataSourceChanged()));
}

817
818
819
820
void DataSourceWidget::addObjects(const QModelIndex& parent, const QString& indent)
{
    for(int i=0; i<_worldModel->rowCount(parent); ++i) {
        QModelIndex index = _worldModel->index(i, 0, parent);
821
822
        QString name = index.data(WorldModel::FormattedNameRole).toString();
        _object->addItem(indent + name, QVariant::fromValue(_worldModel->object(index)));
Carsten Niehaus's avatar
Carsten Niehaus committed
823
        addObjects(index, indent + ' ');
824
825
826
    }
}

827
828
829
830
831
832
833
834
StepCore::Object* DataSourceWidget::dataObject() const
{
    if(_object->currentIndex() < 0) return NULL;
    return _object->itemData(_object->currentIndex()).value<StepCore::Object*>();
}

void DataSourceWidget::setDataSource(WorldModel* worldModel,
            StepCore::Object* object, const QString& property, int index)
835
836
837
838
839
840
{
    _worldModel = worldModel;
    if(!_worldModel) return;

    _object->clear();

Laurent Montel's avatar
Laurent Montel committed
841
    addObjects(QModelIndex(), QLatin1String(""));
842

843
    int objIndex = _object->findData(QVariant::fromValue(object));
844
845
846
847
848
849
    _object->setCurrentIndex( objIndex );
    objectSelected(objIndex);

    int propIndex = _property->findData(property);
    _property->setCurrentIndex( propIndex );
    propertySelected(propIndex);
850
851
852
853

    _index->setCurrentIndex( index );
}

854
void DataSourceWidget::objectSelected(int index)
855
856
857
858
859
{
    Q_ASSERT(_worldModel);

    _property->clear();

860
    const StepCore::Object* obj = _object->itemData(index).value<StepCore::Object*>();
861
862
863
864
    if(obj != 0) {
        _property->setEnabled(true);
        for(int i=0; i<obj->metaObject()->propertyCount(); ++i) {
            const StepCore::MetaProperty* pr = obj->metaObject()->property(i);
865
            if(_skipReadOnly && !pr->isWritable()) continue;
866
867
            if(pr->userTypeId() == qMetaTypeId<double>() ||
                        pr->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) {
868
                _property->addItem(pr->nameTr(), pr->name());
869
870
            }
        }
871
        propertySelected(_property->currentIndex());
872
873
874
875
876
    } else {
        _property->setEnabled(false);
    }
}

877
void DataSourceWidget::propertySelected(int index)
878
879
880
{
    Q_ASSERT(_worldModel);

881
    QString text = _property->itemData(index).toString();
882
883
    const StepCore::Object* obj = _object->itemData(_object->currentIndex())
                                                        .value<StepCore::Object*>();
884
885
886
887
888
    const StepCore::MetaProperty* pr = obj ? obj->metaObject()->property(text) : 0;

    _index->clear();
    if(pr != 0 && pr->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) {
        _index->setEnabled(true);
Laurent Montel's avatar
Laurent Montel committed
889
890
        _index->addItem(QStringLiteral("0"));
        _index->addItem(QStringLiteral("1"));
891
892
893
894
    } else {
        _index->setEnabled(false);
    }
}
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
895

896
////////////////////////////////////////////////////
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
897
GraphGraphicsItem::GraphGraphicsItem(StepCore::Item* item, WorldModel* worldModel)
898
    : WidgetGraphicsItem(item, worldModel)
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
899
{
900
    Q_ASSERT(dynamic_cast<StepCore::Graph*>(_item) != NULL);
Vladimir Kuznetsov's avatar
Vladimir Kuznetsov committed
901
902
    setFlag(QGraphicsItem::ItemIsSelectable);
    setFlag(QGraphicsItem::ItemIsMovable);
903
    setAcceptHoverEvents(true);
904

905
    _plotWidget = new KPlotWidget();
906
    _plotWidget->setPalette(QPalette(Qt::lightGray));
907
908
909
910
911
912
    _plotWidget->setBackgroundColor(Qt::white);
    _plotWidget->setForegroundColor(Qt::black);
    //_plotWidget->setLeftPadding(0);
    //_plotWidget->setTopPadding(2);
    //_plotWidget->setRightPadding(3);

913
    _plotObject = new KPlotObject(Qt::black);
914
    _plotObject->setShowPoints(false);
915
916
917
    _plotObject->setShowLines(true);
    _plotObject->setPointStyle(KPlotObject::Square);

918
919
920
921
922
923
924
925
926
    _plotObject1 = new KPlotObject(Qt::red);
    _plotObject1->setShowPoints(true);
    _plotObject1->setShowLines(false);
    _plotObject1->setPointStyle(KPlotObject::Square);

    QList<KPlotObject*> plotObjects;
    plotObjects << _plotObject;
    plotObjects << _plotObject1;

927
    //_plotWidget->setAntialiasing(true);
928
    _plotWidget->addPlotObjects(plotObjects);
929

930
    _lastColor = 0xff000000;
931
    _lastPointTime = -HUGE_VAL;
932

933
    setCenteralWidget(_plotWidget);
934
935
936

    setOnHoverHandlerEnabled(true);
    _plotWidget->setMouseTracking(true);
937
938
}

939
inline StepCore::Graph* GraphGraphicsItem::graph() const
940
{
941
942
943
    return static_cast<StepCore::Graph*>(_item);
}

944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
void GraphGraphicsItem::adjustLimits()
{
    double minX =  HUGE_VAL, minY =  HUGE_VAL;
    double maxX = -HUGE_VAL, maxY = -HUGE_VAL;

    if(graph()->autoLimitsX() || graph()->autoLimitsY()) {
        for(int i=0; i<(int) graph()->points().size(); ++i) {
            StepCore::Vector2d p = graph()->points()[i];
            if(p[0] < minX) minX = p[0];
            if(p[0] > maxX) maxX = p[0];
            if(p[1] < minY) minY = p[1];
            if(p[1] > maxY) maxY = p[1];
        }
    }

    if(!graph()->autoLimitsX() || graph()->points().empty()) {
        minX = graph()->limitsX()[0];
        maxX = graph()->limitsX()[1];
    } else {
        double range = maxX - minX;
        if(range != 0) { minX -= 0.1*range; maxX += 0.1*range; }
        else { minX -= 0.5; maxX += 0.5; }
    }

    if(!graph()->autoLimitsY() || graph()->points().empty()) {
        minY = graph()->limitsY()[0];
        maxY = graph()->limitsY()[1];
    } else {
        double range = maxY - minY;
        if(range != 0) { minY -= 0.1*range; maxY += 0.1*range; }
        else { minY -= 0.5; maxY += 0.5; }
    }

    _plotWidget->setLimits(minX, maxX, minY, maxY);
}

980
981
982
void GraphGraphicsItem::worldDataChanged(bool dynamicOnly)
{
    if(!dynamicOnly) {
983
984
985
        viewScaleChanged();

        // Labels
986
987
        QString labelX, labelY;
        if(graph()->isValidX()) {
988
            labelX = i18n("%1.%2", _worldModel->formatName(graph()->objectX()), graph()->propertyX());
989
            if(graph()->indexX() >= 0) labelX.append(i18n("[%1]", graph()->indexX()));
990
991
            QString units = graph()->unitsX();
            if(!units.isEmpty()) labelX.append(" [").append(units).append("]");
992
993
        } else {
            labelX = i18n("[not configured]");
994
995
        }
        if(graph()->isValidY()) {
996
            labelY = i18n("%1.%2", _worldModel->formatName(graph()->objectY()), graph()->propertyY());
997
            if(graph()->indexY() >= 0) labelY.append(i18n("[%1]", graph()->indexY()));
998
999
            QString units = graph()->unitsY();
            if(!units.isEmpty()) labelY.append(" [").append(units).append("]");
1000
1001
        } else {
            labelY = i18n("[not configured]");
1002
1003
1004
        }
        _plotWidget->axis( KPlotWidget::BottomAxis )->setLabel(labelX);
        _plotWidget->axis( KPlotWidget::LeftAxis )->setLabel(labelY);
1005

1006
1007
        if(!graph()->autoLimitsX() && !graph()->autoLimitsY()) adjustLimits();

1008
1009
1010
        _plotObject->setShowPoints(graph()->showPoints());
        _plotObject->setShowLines(graph()->showLines());

1011
1012
1013
1014
1015
        if(_lastColor != graph()->color()) {
            _lastColor = graph()->color();
            _plotObject->setLinePen(QPen(QColor::fromRgba(_lastColor),0));
        }

1016
        /*
1017
1018
1019
1020
1021
1022
1023
1024
        // Points
        _plotObject->clearPoints();
        for(int i=0; i<(int) graph()->points().size(); ++i) {
            StepCore::Vector2d p = graph()->points()[i];
            _plotObject->addPoint(p[0], p[1]);
        }

        adjustLimits();
1025
        _plotWidget->update();
1026
        */
1027

1028
        WidgetGraphicsItem::worldDataChanged(dynamicOnly);
1029
1030
1031
1032
1033
    }
    
    if(_worldModel->isSimulationActive()) {
        if(_worldModel->world()->time() > _lastPointTime
                    + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) {
1034
            graph()->recordPoint();
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
            _lastPointTime = _worldModel->world()->time();
        }
    }

    int po_count, p_count;
    do {
        const QList<KPlotPoint*> points = _plotObject->points();
        po_count = points.count(); p_count = graph()->points().size();
        int count = qMin(po_count, p_count);
        for(int p=0; p < count; ++p)
            points[p]->setPosition(vectorToPoint(graph()->points()[p]));
    } while(0);

    if(po_count < p_count) {
        for(; po_count < p_count; ++po_count)
            _plotObject->addPoint(vectorToPoint(graph()->points()[po_count]));
    } else {
        for(--po_count; po_count >= p_count; --po_count)
            _plotObject->removePoint(po_count);
    }

1056
1057
1058
1059
1060
1061
1062
1063
1064
    if(p_count > 0) {
        if(_plotObject1->points().isEmpty()) {
            _plotObject1->addPoint(0,0);
        }
        _plotObject1->points()[0]->setPosition(vectorToPoint(graph()->points()[p_count-1]));
    } else {
        _plotObject1->clearPoints();
    }

1065
1066
1067
1068
1069
    if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits();
    _plotWidget->update();

#if 0
//#error Do setProperty here and remove DynamicOnly from points
1070
1071
1072
1073
1074
1075
1076
        if(ok) {
            _plotObject->addPoint(point[0], point[1]);
            if(graph()->autoLimitsX() || graph()->autoLimitsY()) 
                adjustLimits();
            _plotWidget->update();
        }
        _lastPointTime = _worldModel->world()->time();
1077
        worldDataChanged(false);
1078
        //_worldModel->setProperty(graph(), "name", QString("test"));