File indexing completed on 2024-04-28 03:51:21
0001 /*. 0002 SPDX-FileCopyrightText: 2007 Vladimir Kuznetsov <ks.vladimir@gmail.com> 0003 SPDX-FileCopyrightText: 2014 Inge Wallin <inge@lysator.liu.se> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "toolgraphics.h" 0009 0010 #include "ui_configure_graph.h" 0011 #include "ui_configure_meter.h" 0012 #include "ui_configure_controller.h" 0013 0014 #include <stepcore/tool.h> 0015 #include <stepcore/particle.h> 0016 #include <stepcore/rigidbody.h> 0017 #include <stepcore/solver.h> 0018 #include <stepcore/collisionsolver.h> 0019 0020 #include "worldmodel.h" 0021 #include "worldscene.h" 0022 #include "worldfactory.h" 0023 #include "latexformula.h" 0024 0025 #include <QAbstractButton> 0026 #include <QAbstractTextDocumentLayout> 0027 #include <QAction> 0028 #include <QApplication> 0029 #include <QColorDialog> 0030 #include <QDialog> 0031 #include <QDialogButtonBox> 0032 #include <QEvent> 0033 #include <QFileDialog> 0034 #include <QFocusEvent> 0035 #include <QGraphicsScene> 0036 #include <QGraphicsSceneMouseEvent> 0037 #include <QGraphicsView> 0038 #include <QGridLayout> 0039 #include <QInputDialog> 0040 #include <QItemSelectionModel> 0041 #include <QLCDNumber> 0042 #include <QLabel> 0043 #include <QPainter> 0044 #include <QPushButton> 0045 #include <QStyleOptionGraphicsItem> 0046 #include <QTemporaryFile> 0047 #include <QTextDocument> 0048 #include <QUrl> 0049 #include <QVBoxLayout> 0050 0051 #include <KFontAction> 0052 #include <KFontSizeAction> 0053 #include <KIO/CopyJob> 0054 #include <KIO/Job> 0055 #include <KIO/FileCopyJob> 0056 #include <KJobWidgets> 0057 #include <KLocalizedString> 0058 #include <KMessageBox> 0059 #include <KPlotAxis> 0060 #include <KPlotObject> 0061 #include <KPlotPoint> 0062 #include <KPlotWidget> 0063 #include <KToggleAction> 0064 #include <KToolBar> 0065 0066 #include <float.h> 0067 0068 0069 StepCore::Vector2d WidgetVertexHandlerGraphicsItem::value() 0070 { 0071 double s = currentViewScale(); 0072 StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))-> 0073 readVariant(_item).value<StepCore::Vector2d>()/s; 0074 return (size.array()* corners[_vertexNum].array()).matrix(); 0075 } 0076 0077 void WidgetVertexHandlerGraphicsItem::setValue(const StepCore::Vector2d& value) 0078 { 0079 double s = currentViewScale(); 0080 0081 QGraphicsView* activeView = scene()->views().first(); 0082 QTransform viewportTransform = activeView->viewportTransform(); 0083 0084 StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))-> 0085 readVariant(_item).value<StepCore::Vector2d>()/s; 0086 StepCore::Vector2d position = _item->metaObject()->property(QStringLiteral("position"))-> 0087 readVariant(_item).value<StepCore::Vector2d>(); 0088 0089 StepCore::Vector2d oCorner = position - (size.array()*((corners[_vertexNum]).array())).matrix(); 0090 0091 oCorner = pointToVector( viewportTransform.inverted().map( 0092 QPointF(viewportTransform.map(vectorToPoint(oCorner)).toPoint()) )); 0093 0094 StepCore::Vector2d delta = (value + position - oCorner)/2.0; 0095 StepCore::Vector2d newPos = oCorner + delta; 0096 newPos = pointToVector( viewportTransform.inverted().map( 0097 QPointF(viewportTransform.map(vectorToPoint(newPos)).toPoint()) )); 0098 StepCore::Vector2d newSize = (newPos - oCorner)*2.0; 0099 0100 StepCore::Vector2d sign = (delta.array()*(corners[_vertexNum].array())).matrix(); 0101 double d = -0.1/s; 0102 if(sign[0] < d || sign[1] < d) { 0103 if(sign[0] < d) { 0104 newPos[0] = oCorner[0]; newSize[0] = 0; 0105 _vertexNum ^= 1; 0106 } 0107 if(sign[1] < d) { 0108 newPos[1] = oCorner[1]; newSize[1] = 0; 0109 _vertexNum ^= 2; 0110 } 0111 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos)); 0112 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue((newSize*s).eval())); 0113 setValue(value); 0114 return; 0115 } 0116 0117 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos)); 0118 _worldModel->setProperty(_item, QStringLiteral("size"), QVariant::fromValue((newSize*s).eval())); 0119 } 0120 0121 WidgetGraphicsItem::WidgetGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0122 : StepGraphicsItem(item, worldModel), _centralWidget(nullptr) 0123 { 0124 setFlag(QGraphicsItem::ItemIsSelectable); 0125 setFlag(QGraphicsItem::ItemIsMovable); 0126 setAcceptHoverEvents(true); 0127 0128 _backgroundBrush = Qt::NoBrush; 0129 0130 _boundingRect = QRectF(0, 0, 0, 0); 0131 } 0132 0133 WidgetGraphicsItem::~WidgetGraphicsItem() 0134 { 0135 if(_centralWidget) { 0136 _centralWidget->hide(); 0137 _centralWidget->deleteLater(); 0138 } 0139 } 0140 0141 OnHoverHandlerGraphicsItem* WidgetGraphicsItem::createOnHoverHandler(const QPointF& pos) 0142 { 0143 double s = currentViewScale(); 0144 StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))-> 0145 readVariant(_item).value<StepCore::Vector2d>()/s; 0146 StepCore::Vector2d position = _item->metaObject()->property(QStringLiteral("position"))-> 0147 readVariant(_item).value<StepCore::Vector2d>(); 0148 StepCore::Vector2d l = pointToVector(pos) - position; 0149 0150 int num = -1; double minDist2 = HANDLER_SNAP_SIZE*HANDLER_SNAP_SIZE/s/s; 0151 for(unsigned int i=0; i<4; ++i) { 0152 double dist2 = (l - (size.array()*(WidgetVertexHandlerGraphicsItem::corners[i]).array()).matrix()).squaredNorm(); 0153 if(dist2 < minDist2) { num = i; minDist2 = dist2; } 0154 } 0155 0156 if(_onHoverHandler && _onHoverHandler->vertexNum() == num) 0157 return _onHoverHandler; 0158 0159 if(num >= 0) 0160 return new WidgetVertexHandlerGraphicsItem(_item, _worldModel, this, num); 0161 0162 return nullptr; 0163 } 0164 0165 // XXX: ??? 0166 void WidgetGraphicsItem::mouseSetPos(const QPointF& pos, const QPointF&, MovingState) 0167 { 0168 QGraphicsView* activeView = scene()->views().first(); 0169 QTransform itemTransform = activeView->transform() * deviceTransform(activeView->viewportTransform()); 0170 StepCore::Vector2d newPos = pointToVector( itemTransform.inverted().map( 0171 QPointF(itemTransform.map(pos/*/50.0*/).toPoint()) ))/**50.0*/; 0172 _worldModel->setProperty(_item, QStringLiteral("position"), QVariant::fromValue(newPos)); 0173 } 0174 0175 void WidgetGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) 0176 { 0177 painter->setRenderHint(QPainter::Antialiasing, true); 0178 if(_isSelected) painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); 0179 else painter->setPen(QPen(Qt::NoPen)); 0180 painter->setBrush(_backgroundBrush); 0181 painter->drawRect(_boundingRect); 0182 } 0183 0184 void WidgetGraphicsItem::setCenteralWidget(QWidget* widget) 0185 { 0186 if(_centralWidget) { 0187 _centralWidget->hide(); 0188 _centralWidget->deleteLater(); 0189 } 0190 _centralWidget = widget; 0191 viewScaleChanged(); 0192 } 0193 0194 void WidgetGraphicsItem::viewScaleChanged() 0195 { 0196 if(!scene() || scene()->views().isEmpty()) return; 0197 QGraphicsView* activeView = scene()->views().first(); 0198 0199 QPointF position = vectorToPoint(_item->metaObject()->property(QStringLiteral("position"))-> 0200 readVariant(_item).value<StepCore::Vector2d>()); 0201 0202 // Move item to the closest pixel position 0203 QPoint viewPosition = activeView->mapFromScene(position); 0204 setPos(activeView->mapToScene(viewPosition)); 0205 0206 StepCore::Vector2d size = _item->metaObject()->property(QStringLiteral("size"))-> 0207 readVariant(_item).value<StepCore::Vector2d>(); 0208 0209 QSize viewSize(qRound(size[0]), qRound(size[1])); 0210 QPoint viewTopLeft = 0211 viewPosition - QPoint(viewSize.width() / 2, viewSize.height() / 2); 0212 QRect viewRect(viewTopLeft, viewSize); 0213 0214 QRectF sceneRect = 0215 activeView->mapToScene(viewRect.adjusted(0, 0, 1, 1)).boundingRect(); 0216 QRectF boundingRect = mapRectFromScene(sceneRect); 0217 double s = currentViewScale(); 0218 boundingRect.adjust(-SELECTION_MARGIN/s, -SELECTION_MARGIN/s, 0219 SELECTION_MARGIN/s, SELECTION_MARGIN/s); 0220 0221 if(boundingRect != _boundingRect) { 0222 prepareGeometryChange(); 0223 _boundingRect = boundingRect; 0224 update(); 0225 } 0226 0227 // Reparent the widget if necessary. 0228 if(_centralWidget->parentWidget() != activeView->viewport()) { 0229 _centralWidget->setParent(activeView->viewport()); 0230 _centralWidget->show(); 0231 } 0232 0233 _centralWidget->setGeometry(viewRect.adjusted(0, 0, 1, 1)); 0234 } 0235 0236 void WidgetGraphicsItem::stateChanged() 0237 { 0238 update(); 0239 } 0240 0241 void WidgetGraphicsItem::worldDataChanged(bool dynamicOnly) 0242 { 0243 if(!dynamicOnly) { 0244 /*QPointF position = vectorToPoint(_item->metaObject()->property("position")-> 0245 readVariant(_item).value<StepCore::Vector2d>()); 0246 setPos(position);*/ 0247 viewScaleChanged(); 0248 update(); 0249 } 0250 } 0251 0252 ///////////////////////////////////////////////////////////////////////////////////////// 0253 0254 QString NoteTextEdit::emptyNotice() const 0255 { 0256 return i18n("Click to enter text"); 0257 } 0258 0259 /* 0260 void NoteTextEdit::focusInEvent(QFocusEvent *event) 0261 { 0262 if(_noteItem->note()->text().isEmpty()) { 0263 ++_noteItem->_updating; 0264 setPlainText(""); 0265 _noteItem->worldDataChanged(false); 0266 --_noteItem->_updating; 0267 } 0268 _noteItem->_hasFocus = true; 0269 _noteItem->_toolBar->show(); 0270 _noteItem->setSelected(true); 0271 _noteItem->viewScaleChanged(); 0272 KTextEdit::focusInEvent(event); 0273 } 0274 0275 void NoteTextEdit::focusOutEvent(QFocusEvent *event) 0276 { 0277 qDebug() << event->reason() << endl; 0278 qDebug() << QApplication::focusWidget()->metaObject()->className() << endl; 0279 0280 QObject* f = QApplication::focusWidget(); 0281 if(f == this) f = NULL; 0282 while(f && f != _noteItem->_widget) f = f->parent(); 0283 0284 if(!f && event->reason() != Qt::PopupFocusReason) { 0285 if(_noteItem->note()->text().isEmpty()) { 0286 ++_noteItem->_updating; 0287 setPlainText(emptyNotice()); 0288 _noteItem->worldDataChanged(false); 0289 --_noteItem->_updating; 0290 } 0291 _noteItem->_hasFocus = false; 0292 _noteItem->_toolBar->hide(); 0293 _noteItem->viewScaleChanged(); 0294 } 0295 KTextEdit::focusOutEvent(event); 0296 } 0297 */ 0298 0299 StepCore::NoteFormula* NoteTextEdit::formulaAt(const QPoint& pos) 0300 { 0301 int p = document()->documentLayout()->hitTest(pos, Qt::ExactHit); 0302 if(p < 0) return nullptr; 0303 0304 QTextCursor cursor(document()); 0305 cursor.setPosition(p); 0306 if(cursor.atEnd()) return nullptr; 0307 cursor.setPosition(p+1); 0308 0309 QTextFormat format = cursor.charFormat(); 0310 if(!format.isImageFormat()) return nullptr; 0311 QString image = format.toImageFormat().name(); 0312 0313 StepCore::Item* item = _noteItem->note()->childItem(image); 0314 if(!item) { 0315 foreach(StepCore::Item* it, _noteItem->_newItems) 0316 if(it->name() == image) { item = it; break; } 0317 } 0318 0319 if(!item || !item->metaObject()->inherits<StepCore::NoteFormula>()) 0320 return nullptr; 0321 0322 return static_cast<StepCore::NoteFormula*>(item); 0323 } 0324 0325 void NoteTextEdit::mousePressEvent(QMouseEvent *e) 0326 { 0327 if(e->button() == Qt::LeftButton) _mousePressPoint = e->pos(); 0328 KTextEdit::mousePressEvent(e); 0329 } 0330 0331 void NoteTextEdit::mouseMoveEvent(QMouseEvent *e) 0332 { 0333 if(formulaAt(e->pos()) != nullptr) { 0334 viewport()->setCursor(Qt::PointingHandCursor); 0335 } else { 0336 viewport()->setCursor(Qt::IBeamCursor); 0337 } 0338 _mousePressPoint.setX(-1); 0339 KTextEdit::mouseMoveEvent(e); 0340 } 0341 0342 void NoteTextEdit::mouseReleaseEvent(QMouseEvent *e) 0343 { 0344 if(e->button() == Qt::LeftButton && e->pos() == _mousePressPoint) { 0345 StepCore::NoteFormula* formula = formulaAt(e->pos()); 0346 if(formula) { 0347 e->accept(); 0348 _noteItem->editFormula(formula); 0349 setDocument(document()); 0350 return; 0351 } 0352 } 0353 KTextEdit::mouseReleaseEvent(e); 0354 } 0355 0356 NoteGraphicsItem::NoteGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0357 : WidgetGraphicsItem(item, worldModel) 0358 { 0359 Q_ASSERT(dynamic_cast<StepCore::Note*>(_item) != nullptr); 0360 setFlag(QGraphicsItem::ItemIsSelectable); 0361 setFlag(QGraphicsItem::ItemIsMovable); 0362 setAcceptHoverEvents(true); 0363 0364 _widget = new QWidget(); 0365 _widget->setPalette(QPalette(Qt::lightGray)); 0366 0367 _textEdit = new NoteTextEdit(this, _widget); 0368 _textEdit->setFrameShape(QFrame::NoFrame); 0369 QPalette p = _textEdit->palette(); 0370 p.setColor(QPalette::Base, Qt::transparent); 0371 _textEdit->setPalette(p); 0372 //_textEdit->setStyleSheet(".NoteTextEdit {background-color: rgba(0,0,0,0%);}"); 0373 0374 _toolBar = new KToolBar(_widget); 0375 _toolBar->setIconDimensions(16); 0376 _toolBar->setToolButtonStyle(Qt::ToolButtonIconOnly); 0377 _toolBar->setContentsMargins(0,0,0,0); 0378 if(_toolBar->layout()) _toolBar->layout()->setSpacing(0); 0379 //_toolBar->setStyleSheet(".KToolBar {margin: 0px; border-width: 0px; padding: 0px; }"); 0380 0381 _actionColor = new QAction(QIcon(), i18n("&Color"), _toolBar); 0382 0383 _actionBold = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-bold")), i18n("&Bold"), _toolBar); 0384 _actionBold->setShortcut(Qt::CTRL | Qt::Key_B); 0385 _actionItalic = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-italic")), i18n("&Italic"), _toolBar); 0386 _actionItalic->setShortcut(Qt::CTRL | Qt::Key_I); 0387 _actionUnderline = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-text-underline")), i18n("&Underline"), _toolBar); 0388 _actionUnderline->setShortcut(Qt::CTRL | Qt::Key_U); 0389 0390 _actionAlignLeft = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-left")), i18n("Align &Left"), _toolBar); 0391 _actionAlignLeft->setShortcut(Qt::CTRL | Qt::Key_L); 0392 _actionAlignCenter = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-center")), i18n("Align C&enter"), _toolBar); 0393 _actionAlignCenter->setShortcut(Qt::CTRL | Qt::Key_E); 0394 _actionAlignRight = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-right")), i18n("Align &Right"), _toolBar); 0395 _actionAlignRight->setShortcut(Qt::CTRL | Qt::Key_R); 0396 _actionAlignJustify = new KToggleAction(QIcon::fromTheme(QStringLiteral("format-justify-fill")), i18n("Align &Justify"), _toolBar); 0397 _actionAlignJustify->setShortcut(Qt::CTRL | Qt::Key_J); 0398 0399 _actionAlign = new KSelectAction(i18n("&Align"), _toolBar); 0400 _actionAlign->setToolBarMode(KSelectAction::MenuMode); 0401 _actionAlign->setToolButtonPopupMode(QToolButton::InstantPopup); 0402 _actionAlign->addAction(_actionAlignLeft); 0403 _actionAlign->addAction(_actionAlignCenter); 0404 _actionAlign->addAction(_actionAlignRight); 0405 _actionAlign->addAction(_actionAlignJustify); 0406 _actionAlignLeft->setChecked(true); 0407 0408 _actionFont = new KFontAction(i18n("&Font"), _toolBar); 0409 _actionFontSize = new KFontSizeAction(i18n("Font &Size"), _toolBar); 0410 0411 _actionInsertImage = new QAction(QIcon::fromTheme(QStringLiteral("insert-image")), i18n("Insert &Image..."), _toolBar); 0412 #ifdef __GNUC__ 0413 #warning Select right icon here 0414 #endif 0415 _actionInsertFormula = new QAction(QIcon::fromTheme(QStringLiteral("application-vnd.oasis.opendocument.formula")), 0416 i18n("Insert &Formula..."), _toolBar); 0417 0418 connect(_actionColor, &QAction::triggered, this, &NoteGraphicsItem::formatColor); 0419 connect(_actionBold, &QAction::triggered, this, &NoteGraphicsItem::formatBold); 0420 connect(_actionItalic, &QAction::triggered, _textEdit, &QTextEdit::setFontItalic); 0421 connect(_actionUnderline, &QAction::triggered, _textEdit, &QTextEdit::setFontUnderline); 0422 connect(_actionAlign, SIGNAL(triggered(QAction*)), this, SLOT(formatAlign(QAction*))); 0423 connect(_actionFont, SIGNAL(triggered(QString)), this, SLOT(formatFontFamily(QString))); 0424 connect(_actionFontSize, &KFontSizeAction::fontSizeChanged, this, &NoteGraphicsItem::formatFontSize); 0425 connect(_actionInsertImage, &QAction::triggered, this, &NoteGraphicsItem::insertImage); 0426 connect(_actionInsertFormula, &QAction::triggered, this, &NoteGraphicsItem::insertFormula); 0427 0428 connect(_textEdit, &QTextEdit::currentCharFormatChanged, 0429 this, &NoteGraphicsItem::currentCharFormatChanged); 0430 connect(_textEdit, &QTextEdit::cursorPositionChanged, this, &NoteGraphicsItem::cursorPositionChanged); 0431 0432 0433 connect(_toolBar, SIGNAL(actionTriggered(QAction*)), _textEdit, SLOT(setFocus())); 0434 0435 _toolBar->addAction(_actionAlign); 0436 _toolBar->addAction(_actionBold); 0437 _toolBar->addAction(_actionItalic); 0438 _toolBar->addAction(_actionUnderline); 0439 _toolBar->addAction(_actionColor); 0440 //_toolBar->addSeparator(); 0441 0442 //_toolBar->addSeparator(); 0443 0444 _toolBar->addAction(_actionInsertImage); 0445 _toolBar->addAction(_actionInsertFormula); 0446 0447 _toolBar->addAction(_actionFontSize); 0448 _toolBar->addAction(_actionFont); 0449 0450 QVBoxLayout* layout = new QVBoxLayout(_widget); 0451 layout->setContentsMargins(0,0,0,0); 0452 layout->setSpacing(0); 0453 layout->addWidget(_textEdit); 0454 layout->addWidget(_toolBar); 0455 0456 // without it focus is passed to QGrahicsView 0457 _toolBar->setFocusPolicy(Qt::ClickFocus); 0458 _toolBar->setFocusProxy(_textEdit); 0459 _widget->setFocusProxy(_textEdit); 0460 0461 _hasFocus = false; 0462 _toolBar->hide(); 0463 0464 _textEdit->installEventFilter(this); 0465 0466 QComboBox* font = qobject_cast<QComboBox*>(_toolBar->widgetForAction(_actionFont)); 0467 if(font) { 0468 font->setMinimumContentsLength(5); 0469 font->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0470 font->installEventFilter(this); 0471 font->setToolTip(_actionFont->toolTip()); 0472 } 0473 0474 QComboBox* fontSize = qobject_cast<QComboBox*>(_toolBar->widgetForAction(_actionFontSize)); 0475 if(fontSize) { 0476 fontSize->setMinimumContentsLength(2); 0477 fontSize->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0478 fontSize->installEventFilter(this); 0479 fontSize->setToolTip(_actionFontSize->toolTip()); 0480 } 0481 0482 setCenteralWidget(_widget); 0483 setOnHoverHandlerEnabled(true); 0484 _widget->setMouseTracking(true); 0485 _textEdit->setMouseTracking(true); 0486 _toolBar->setMouseTracking(true); 0487 } 0488 0489 inline StepCore::Note* NoteGraphicsItem::note() const 0490 { 0491 return static_cast<StepCore::Note*>(_item); 0492 } 0493 0494 bool NoteGraphicsItem::eventFilter(QObject* obj, QEvent* event) 0495 { 0496 if(event->type() == QEvent::FocusIn) { 0497 if(!_hasFocus) { 0498 _hasFocus = true; 0499 if(note()->text().isEmpty()) { 0500 //++_updating; 0501 _textEdit->setPlainText(QLatin1String("")); 0502 worldDataChanged(false); 0503 //--_updating; 0504 } 0505 _toolBar->show(); 0506 bool multiSelect = (QApplication::keyboardModifiers() & Qt::ControlModifier) != 0; 0507 if(!multiSelect/* && !isSelected()*/) { 0508 if(scene()) scene()->clearSelection(); 0509 _worldModel->selectionModel()->clearSelection(); 0510 } 0511 setSelected(true); 0512 viewScaleChanged(); 0513 } 0514 } else if(event->type() == QEvent::FocusOut && 0515 static_cast<QFocusEvent*>(event)->reason() != Qt::PopupFocusReason) { 0516 0517 QObject* f = QApplication::focusWidget(); 0518 if(f == obj) f = nullptr; 0519 while(f && f != _widget) f = f->parent(); 0520 0521 if(!f) { 0522 _worldModel->simulationPause(); 0523 0524 QString newText = _textEdit->toHtml(); 0525 if(newText != note()->text()) { 0526 //++_updating; 0527 _worldModel->beginMacro(i18n("Edit %1", _item->name())); 0528 0529 foreach(StepCore::Item* item, note()->items()) 0530 if(!newText.contains(item->name())) _worldModel->deleteItem(item); 0531 0532 foreach(StepCore::Item* item, _newItems) 0533 if(!newText.contains(item->name())) _newItems.removeAll(item); 0534 0535 foreach(StepCore::Item* item, _newItems) 0536 _worldModel->addItem(item, note()); 0537 0538 _newItems.clear(); 0539 0540 _worldModel->setProperty(_item, QStringLiteral("text"), newText); 0541 0542 _worldModel->endMacro(); 0543 //--_updating; 0544 } 0545 0546 _hasFocus = false; 0547 _toolBar->hide(); 0548 _textEdit->clear(); 0549 0550 viewScaleChanged(); 0551 worldDataChanged(false); 0552 } 0553 } 0554 return QObject::eventFilter(obj, event); 0555 } 0556 0557 void NoteGraphicsItem::formatColor() 0558 { 0559 QColor color = _textEdit->textColor(); 0560 color = QColorDialog::getColor(color, _widget); 0561 if(QColorDialog::Accepted && color.isValid()) { 0562 _textEdit->setTextColor(color); 0563 } 0564 } 0565 0566 void NoteGraphicsItem::formatBold(bool checked) 0567 { 0568 _textEdit->setFontWeight(checked ? QFont::Bold : QFont::Normal); 0569 } 0570 0571 void NoteGraphicsItem::formatAlign(QAction* action) 0572 { 0573 if(action == _actionAlignLeft) 0574 _textEdit->setAlignment(Qt::AlignLeft); 0575 else if(action == _actionAlignCenter) 0576 _textEdit->setAlignment(Qt::AlignHCenter); 0577 else if(action == _actionAlignRight) 0578 _textEdit->setAlignment(Qt::AlignRight); 0579 else if(action == _actionAlignJustify) 0580 _textEdit->setAlignment(Qt::AlignJustify); 0581 0582 _actionAlign->setIcon(action->icon()); 0583 } 0584 0585 void NoteGraphicsItem::formatFontFamily(const QString& family) 0586 { 0587 _textEdit->setFontFamily(family); 0588 _textEdit->setFocus(); 0589 } 0590 0591 void NoteGraphicsItem::formatFontSize(int size) 0592 { 0593 if(size > 0) _textEdit->setFontPointSize(size); 0594 else currentCharFormatChanged(_textEdit->currentCharFormat()); 0595 _textEdit->setFocus(); 0596 } 0597 0598 void NoteGraphicsItem::currentCharFormatChanged(const QTextCharFormat& f) 0599 { 0600 _actionBold->setChecked(f.fontWeight() >= QFont::Bold); 0601 _actionItalic->setChecked(f.fontItalic()); 0602 _actionUnderline->setChecked(f.fontUnderline()); 0603 0604 QPixmap pix(16,16); 0605 pix.fill(_textEdit->textColor()); 0606 _actionColor->setIcon(pix); 0607 0608 QFontInfo ff(f.font()); 0609 #ifdef __GNUC__ 0610 #warning Strange, the following line does nothing ! 0611 #endif 0612 _actionFont->setFont(ff.family()); 0613 _actionFontSize->setFontSize(ff.pointSize()); 0614 } 0615 0616 void NoteGraphicsItem::cursorPositionChanged() 0617 { 0618 if(_textEdit->alignment() & Qt::AlignLeft) 0619 _actionAlignLeft->setChecked(true); 0620 else if(_textEdit->alignment() & Qt::AlignHCenter) 0621 _actionAlignCenter->setChecked(true); 0622 else if(_textEdit->alignment() & Qt::AlignRight) 0623 _actionAlignRight->setChecked(true); 0624 else if(_textEdit->alignment() & Qt::AlignJustify) 0625 _actionAlignJustify->setChecked(true); 0626 0627 _actionAlign->setIcon(_actionAlign->currentAction()->icon()); 0628 } 0629 0630 void NoteGraphicsItem::insertImage() 0631 { 0632 QUrl url = QFileDialog::getOpenFileUrl(_widget, i18nc("@title:window", "Open Image File"), QUrl(), i18n("Images (*.png *.jpg *.jpeg)")); 0633 if(url.isEmpty()) return; 0634 0635 QTemporaryFile tempFile; 0636 tempFile.open(); 0637 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(tempFile.fileName()), -1, KIO::Overwrite); 0638 KJobWidgets::setWindow(job, _widget); 0639 job->exec(); 0640 if (job->error()) { 0641 KMessageBox::error(_widget, job->errorString()); 0642 return; 0643 } 0644 0645 QByteArray data = tempFile.readAll(); 0646 tempFile.close(); 0647 0648 QPixmap pixmap; 0649 if(!pixmap.loadFromData(data)) { 0650 KMessageBox::error(_widget, i18n("Cannot parse file '%1'", tempFile.fileName())); 0651 return; 0652 } 0653 0654 QString imgName; 0655 for(int n=0;; ++n) { 0656 imgName = QStringLiteral("img:%1").arg(n); 0657 if(note()->childItem(imgName) != nullptr) continue; 0658 bool found = false; 0659 foreach(StepCore::Item* item, _newItems) 0660 if(item->name() == imgName) { found = true; break; } 0661 if(!found) break; 0662 } 0663 0664 _newItems << new StepCore::NoteImage(imgName, data); 0665 //_textEdit->document()->addResource(QTextDocument::ImageResource, imgName, pixmap); 0666 _textEdit->insertHtml(QStringLiteral("<img src=\"%1\" />").arg(imgName)); 0667 } 0668 0669 void NoteGraphicsItem::insertFormula() 0670 { 0671 QString imgName; 0672 for(int n=0;; ++n) { 0673 imgName = QStringLiteral("fml:%1").arg(n); 0674 if(note()->childItem(imgName) != nullptr) continue; 0675 bool found = false; 0676 foreach(StepCore::Item* item, _newItems) 0677 if(item->name() == imgName) { found = true; break; } 0678 if(!found) break; 0679 } 0680 0681 StepCore::NoteFormula* formula = new StepCore::NoteFormula(imgName); 0682 if(!editFormula(formula)) { 0683 delete formula; 0684 return; 0685 } 0686 0687 _newItems << formula; 0688 _textEdit->insertHtml(QStringLiteral("<img src=\"%1\" />").arg(imgName)); 0689 } 0690 0691 bool NoteGraphicsItem::editFormula(StepCore::NoteFormula* formula) 0692 { 0693 if(!LatexFormula::isLatexInstalled()) { 0694 KMessageBox::error(_widget, i18n("Cannot find latex installation. " 0695 "You need 'latex', 'dvips' and 'gs' executables installed and accessible from $PATH")); 0696 return false; 0697 } 0698 0699 bool ok; 0700 QString code = QInputDialog::getMultiLineText(_widget, i18nc("@title:window", "LaTex Formula - Step"), 0701 i18n("Enter LaTeX formula string:"), QString(formula->code()), &ok); 0702 if(!ok) return false; 0703 0704 QByteArray image; 0705 QString error; 0706 0707 bool result = LatexFormula::compileFormula(code, &image, &error); 0708 0709 if(!result) { 0710 KMessageBox::error(_widget, i18n("Cannot compile LaTeX formula: %1", error)); 0711 return false; 0712 } 0713 0714 QPixmap pixmap; 0715 if(!pixmap.loadFromData(image)) { 0716 KMessageBox::error(_widget, i18n("Cannot parse result image")); 0717 return false; 0718 } 0719 0720 formula->setCode(code); 0721 formula->setImage(image); 0722 0723 _textEdit->document()->addResource(QTextDocument::ImageResource, 0724 QUrl(formula->name()), pixmap); 0725 return true; 0726 } 0727 0728 void NoteGraphicsItem::worldDataChanged(bool dynamicOnly) 0729 { 0730 if(!dynamicOnly) { 0731 if(!_hasFocus && _textEdit->toHtml() != note()->text()) { 0732 //++_updating; 0733 0734 const StepCore::ItemList::const_iterator end = note()->items().end(); 0735 for(StepCore::ItemList::const_iterator it = note()->items().begin(); it != end; ++it) { 0736 if((*it)->metaObject()->inherits<StepCore::NoteImage>()) { 0737 QPixmap pix; 0738 pix.loadFromData(static_cast<StepCore::NoteImage*>(*it)->image()); 0739 _textEdit->document()->addResource(QTextDocument::ImageResource, QUrl((*it)->name()), pix); 0740 } 0741 } 0742 0743 /* 0744 const StepCore::NoteDataMap::const_iterator end = note()->dataMap().constEnd(); 0745 for(StepCore::NoteDataMap::const_iterator it = note()->dataMap().constBegin(); 0746 it != end; ++it) { 0747 QPixmap pix; 0748 pix.loadFromData(it.value()); 0749 _textEdit->document()->addResource(QTextDocument::ImageResource, it.key(), pix); 0750 } 0751 */ 0752 0753 if(!_textEdit->hasFocus() && note()->text().isEmpty()) { 0754 _textEdit->setPlainText(_textEdit->emptyNotice()); 0755 } else { 0756 _textEdit->setHtml(note()->text()); 0757 } 0758 0759 currentCharFormatChanged(_textEdit->currentCharFormat()); 0760 cursorPositionChanged(); 0761 //--_updating; 0762 } 0763 WidgetGraphicsItem::worldDataChanged(dynamicOnly); 0764 } 0765 } 0766 0767 //////////////////////////////////////////////////// 0768 DataSourceWidget::DataSourceWidget(QWidget* parent) 0769 : QWidget(parent), _worldModel(nullptr) 0770 { 0771 _skipReadOnly = false; 0772 0773 QHBoxLayout *layout = new QHBoxLayout(this); 0774 layout->setContentsMargins(0,0,0,0); 0775 0776 _object = new KComboBox(this); 0777 _object->setToolTip(i18n("Object name")); 0778 _object->setMinimumContentsLength(10); 0779 layout->addWidget(_object, 1); 0780 0781 _property = new KComboBox(this); 0782 _property->setToolTip(i18n("Property name")); 0783 _property->setEnabled(false); 0784 _property->setMinimumContentsLength(10); 0785 layout->addWidget(_property, 1); 0786 0787 _index = new KComboBox(this); 0788 _index->setToolTip(i18n("Vector index")); 0789 _index->setMinimumContentsLength(1); 0790 _index->setSizeAdjustPolicy(QComboBox::AdjustToContents); 0791 layout->addWidget(_index, 0); 0792 0793 connect(_object, SIGNAL(activated(int)), 0794 this, SLOT(objectSelected(int))); 0795 connect(_property, SIGNAL(activated(int)), 0796 this, SLOT(propertySelected(int))); 0797 0798 connect(_object, SIGNAL(activated(int)), 0799 this, SIGNAL(dataSourceChanged())); 0800 connect(_property, SIGNAL(activated(int)), 0801 this, SIGNAL(dataSourceChanged())); 0802 connect(_index, SIGNAL(activated(int)), 0803 this, SIGNAL(dataSourceChanged())); 0804 } 0805 0806 void DataSourceWidget::addObjects(const QModelIndex& parent, const QString& indent) 0807 { 0808 for(int i=0; i<_worldModel->rowCount(parent); ++i) { 0809 QModelIndex index = _worldModel->index(i, 0, parent); 0810 QString name = index.data(WorldModel::FormattedNameRole).toString(); 0811 _object->addItem(indent + name, QVariant::fromValue(_worldModel->object(index))); 0812 addObjects(index, indent + ' '); 0813 } 0814 } 0815 0816 StepCore::Object* DataSourceWidget::dataObject() const 0817 { 0818 if(_object->currentIndex() < 0) return nullptr; 0819 return _object->itemData(_object->currentIndex()).value<StepCore::Object*>(); 0820 } 0821 0822 void DataSourceWidget::setDataSource(WorldModel* worldModel, 0823 StepCore::Object* object, const QString& property, int index) 0824 { 0825 _worldModel = worldModel; 0826 if(!_worldModel) return; 0827 0828 _object->clear(); 0829 0830 addObjects(QModelIndex(), QLatin1String("")); 0831 0832 int objIndex = _object->findData(QVariant::fromValue(object)); 0833 _object->setCurrentIndex( objIndex ); 0834 objectSelected(objIndex); 0835 0836 int propIndex = _property->findData(property); 0837 _property->setCurrentIndex( propIndex ); 0838 propertySelected(propIndex); 0839 0840 _index->setCurrentIndex( index ); 0841 } 0842 0843 void DataSourceWidget::objectSelected(int index) 0844 { 0845 Q_ASSERT(_worldModel); 0846 0847 _property->clear(); 0848 0849 const StepCore::Object* obj = _object->itemData(index).value<StepCore::Object*>(); 0850 if(obj != nullptr) { 0851 _property->setEnabled(true); 0852 for(int i=0; i<obj->metaObject()->propertyCount(); ++i) { 0853 const StepCore::MetaProperty* pr = obj->metaObject()->property(i); 0854 if(_skipReadOnly && !pr->isWritable()) continue; 0855 if(pr->userTypeId() == qMetaTypeId<double>() || 0856 pr->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) { 0857 _property->addItem(pr->nameTr(), pr->name()); 0858 } 0859 } 0860 propertySelected(_property->currentIndex()); 0861 } else { 0862 _property->setEnabled(false); 0863 } 0864 } 0865 0866 void DataSourceWidget::propertySelected(int index) 0867 { 0868 Q_ASSERT(_worldModel); 0869 0870 QString text = _property->itemData(index).toString(); 0871 const StepCore::Object* obj = _object->itemData(_object->currentIndex()) 0872 .value<StepCore::Object*>(); 0873 const StepCore::MetaProperty* pr = obj ? obj->metaObject()->property(text) : nullptr; 0874 0875 _index->clear(); 0876 if(pr != nullptr && pr->userTypeId() == qMetaTypeId<StepCore::Vector2d>()) { 0877 _index->setEnabled(true); 0878 _index->addItem(QStringLiteral("0")); 0879 _index->addItem(QStringLiteral("1")); 0880 } else { 0881 _index->setEnabled(false); 0882 } 0883 } 0884 0885 //////////////////////////////////////////////////// 0886 GraphGraphicsItem::GraphGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 0887 : WidgetGraphicsItem(item, worldModel) 0888 { 0889 Q_ASSERT(dynamic_cast<StepCore::Graph*>(_item) != nullptr); 0890 setFlag(QGraphicsItem::ItemIsSelectable); 0891 setFlag(QGraphicsItem::ItemIsMovable); 0892 setAcceptHoverEvents(true); 0893 0894 _plotWidget = new KPlotWidget(); 0895 _plotWidget->setPalette(QPalette(Qt::lightGray)); 0896 _plotWidget->setBackgroundColor(Qt::white); 0897 _plotWidget->setForegroundColor(Qt::black); 0898 //_plotWidget->setLeftPadding(0); 0899 //_plotWidget->setTopPadding(2); 0900 //_plotWidget->setRightPadding(3); 0901 0902 _plotObject = new KPlotObject(Qt::black); 0903 _plotObject->setShowPoints(false); 0904 _plotObject->setShowLines(true); 0905 _plotObject->setPointStyle(KPlotObject::Square); 0906 0907 _plotObject1 = new KPlotObject(Qt::red); 0908 _plotObject1->setShowPoints(true); 0909 _plotObject1->setShowLines(false); 0910 _plotObject1->setPointStyle(KPlotObject::Square); 0911 0912 QList<KPlotObject*> plotObjects; 0913 plotObjects << _plotObject; 0914 plotObjects << _plotObject1; 0915 0916 //_plotWidget->setAntialiasing(true); 0917 _plotWidget->addPlotObjects(plotObjects); 0918 0919 _lastColor = 0xff000000; 0920 _lastPointTime = -HUGE_VAL; 0921 0922 setCenteralWidget(_plotWidget); 0923 0924 setOnHoverHandlerEnabled(true); 0925 _plotWidget->setMouseTracking(true); 0926 } 0927 0928 inline StepCore::Graph* GraphGraphicsItem::graph() const 0929 { 0930 return static_cast<StepCore::Graph*>(_item); 0931 } 0932 0933 void GraphGraphicsItem::adjustLimits() 0934 { 0935 double minX = HUGE_VAL, minY = HUGE_VAL; 0936 double maxX = -HUGE_VAL, maxY = -HUGE_VAL; 0937 0938 if(graph()->autoLimitsX() || graph()->autoLimitsY()) { 0939 for(int i=0; i<(int) graph()->points().size(); ++i) { 0940 StepCore::Vector2d p = graph()->points()[i]; 0941 if(p[0] < minX) minX = p[0]; 0942 if(p[0] > maxX) maxX = p[0]; 0943 if(p[1] < minY) minY = p[1]; 0944 if(p[1] > maxY) maxY = p[1]; 0945 } 0946 } 0947 0948 if(!graph()->autoLimitsX() || graph()->points().empty()) { 0949 minX = graph()->limitsX()[0]; 0950 maxX = graph()->limitsX()[1]; 0951 } else { 0952 double range = maxX - minX; 0953 if(range != 0) { minX -= 0.1*range; maxX += 0.1*range; } 0954 else { minX -= 0.5; maxX += 0.5; } 0955 } 0956 0957 if(!graph()->autoLimitsY() || graph()->points().empty()) { 0958 minY = graph()->limitsY()[0]; 0959 maxY = graph()->limitsY()[1]; 0960 } else { 0961 double range = maxY - minY; 0962 if(range != 0) { minY -= 0.1*range; maxY += 0.1*range; } 0963 else { minY -= 0.5; maxY += 0.5; } 0964 } 0965 0966 _plotWidget->setLimits(minX, maxX, minY, maxY); 0967 } 0968 0969 void GraphGraphicsItem::worldDataChanged(bool dynamicOnly) 0970 { 0971 if(!dynamicOnly) { 0972 viewScaleChanged(); 0973 0974 // Labels 0975 QString labelX, labelY; 0976 if(graph()->isValidX()) { 0977 labelX = i18n("%1.%2", _worldModel->formatName(graph()->objectX()), graph()->propertyX()); 0978 if(graph()->indexX() >= 0) labelX.append(i18n("[%1]", graph()->indexX())); 0979 QString units = graph()->unitsX(); 0980 if(!units.isEmpty()) labelX.append(" [").append(units).append("]"); 0981 } else { 0982 labelX = i18n("[not configured]"); 0983 } 0984 if(graph()->isValidY()) { 0985 labelY = i18n("%1.%2", _worldModel->formatName(graph()->objectY()), graph()->propertyY()); 0986 if(graph()->indexY() >= 0) labelY.append(i18n("[%1]", graph()->indexY())); 0987 QString units = graph()->unitsY(); 0988 if(!units.isEmpty()) labelY.append(" [").append(units).append("]"); 0989 } else { 0990 labelY = i18n("[not configured]"); 0991 } 0992 _plotWidget->axis( KPlotWidget::BottomAxis )->setLabel(labelX); 0993 _plotWidget->axis( KPlotWidget::LeftAxis )->setLabel(labelY); 0994 0995 if(!graph()->autoLimitsX() && !graph()->autoLimitsY()) adjustLimits(); 0996 0997 _plotObject->setShowPoints(graph()->showPoints()); 0998 _plotObject->setShowLines(graph()->showLines()); 0999 1000 if(_lastColor != graph()->color()) { 1001 _lastColor = graph()->color(); 1002 _plotObject->setLinePen(QPen(QColor::fromRgba(_lastColor),0)); 1003 } 1004 1005 /* 1006 // Points 1007 _plotObject->clearPoints(); 1008 for(int i=0; i<(int) graph()->points().size(); ++i) { 1009 StepCore::Vector2d p = graph()->points()[i]; 1010 _plotObject->addPoint(p[0], p[1]); 1011 } 1012 1013 adjustLimits(); 1014 _plotWidget->update(); 1015 */ 1016 1017 WidgetGraphicsItem::worldDataChanged(dynamicOnly); 1018 } 1019 1020 if(_worldModel->isSimulationActive()) { 1021 if(_worldModel->world()->time() > _lastPointTime 1022 + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { 1023 graph()->recordPoint(); 1024 _lastPointTime = _worldModel->world()->time(); 1025 } 1026 } 1027 1028 int po_count, p_count; 1029 do { 1030 const QList<KPlotPoint*> points = _plotObject->points(); 1031 po_count = points.count(); p_count = graph()->points().size(); 1032 int count = qMin(po_count, p_count); 1033 for(int p=0; p < count; ++p) 1034 points[p]->setPosition(vectorToPoint(graph()->points()[p])); 1035 } while(0); 1036 1037 if(po_count < p_count) { 1038 for(; po_count < p_count; ++po_count) 1039 _plotObject->addPoint(vectorToPoint(graph()->points()[po_count])); 1040 } else { 1041 for(--po_count; po_count >= p_count; --po_count) 1042 _plotObject->removePoint(po_count); 1043 } 1044 1045 if(p_count > 0) { 1046 if(_plotObject1->points().isEmpty()) { 1047 _plotObject1->addPoint(0,0); 1048 } 1049 _plotObject1->points()[0]->setPosition(vectorToPoint(graph()->points()[p_count-1])); 1050 } else { 1051 _plotObject1->clearPoints(); 1052 } 1053 1054 if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); 1055 _plotWidget->update(); 1056 1057 #if 0 1058 //#error Do setProperty here and remove DynamicOnly from points 1059 if(ok) { 1060 _plotObject->addPoint(point[0], point[1]); 1061 if(graph()->autoLimitsX() || graph()->autoLimitsY()) 1062 adjustLimits(); 1063 _plotWidget->update(); 1064 } 1065 _lastPointTime = _worldModel->world()->time(); 1066 worldDataChanged(false); 1067 //_worldModel->setProperty(graph(), "name", QString("test")); 1068 } 1069 #endif 1070 } 1071 1072 void GraphMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) 1073 { 1074 _confUi = nullptr; 1075 _confDialog = nullptr; 1076 _confChanged = false; 1077 1078 menu->addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), i18n("Clear Graph"), this, &GraphMenuHandler::clearGraph); 1079 menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Graph..."), this, &GraphMenuHandler::configureGraph); 1080 menu->addSeparator(); 1081 ItemMenuHandler::populateMenu(menu, actions); 1082 } 1083 1084 inline StepCore::Graph* GraphMenuHandler::graph() const 1085 { 1086 return static_cast<StepCore::Graph*>(_object); 1087 } 1088 1089 void GraphMenuHandler::configureGraph() 1090 { 1091 if(_worldModel->isSimulationActive()) 1092 _worldModel->simulationStop(); 1093 1094 _confChanged = false; 1095 _confDialog = new QDialog(); // XXX: parent? 1096 1097 _confDialog->setWindowTitle(i18nc("@title:window", "Configure Graph")); 1098 _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok 1099 | QDialogButtonBox::Cancel 1100 | QDialogButtonBox::Apply); 1101 QWidget *mainWidget = new QWidget(); 1102 QVBoxLayout *mainLayout = new QVBoxLayout; 1103 _confDialog->setLayout(mainLayout); 1104 mainLayout->addWidget(mainWidget); 1105 1106 QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); 1107 okButton->setDefault(true); 1108 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 1109 _confDialog->connect(_buttonBox, &QDialogButtonBox::accepted, _confDialog, &QDialog::accept); 1110 _confDialog->connect(_buttonBox, &QDialogButtonBox::rejected, _confDialog, &QDialog::reject); 1111 mainLayout->addWidget(_buttonBox); 1112 1113 _confUi = new Ui::WidgetConfigureGraph; 1114 _confUi->setupUi(mainWidget); 1115 1116 _confUi->dataSourceX->setDataSource(_worldModel, 1117 graph()->objectX(), graph()->propertyX(), graph()->indexX()); 1118 _confUi->dataSourceY->setDataSource(_worldModel, 1119 graph()->objectY(), graph()->propertyY(), graph()->indexY()); 1120 1121 _confUi->checkBoxAutoX->setChecked(graph()->autoLimitsX()); 1122 _confUi->checkBoxAutoY->setChecked(graph()->autoLimitsY()); 1123 1124 _confUi->lineEditMinX->setValidator( 1125 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMinX)); 1126 _confUi->lineEditMaxX->setValidator( 1127 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMaxX)); 1128 _confUi->lineEditMinY->setValidator( 1129 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMinY)); 1130 _confUi->lineEditMaxY->setValidator( 1131 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMaxY)); 1132 1133 _confUi->lineEditMinX->setText(QString::number(graph()->limitsX()[0])); 1134 _confUi->lineEditMaxX->setText(QString::number(graph()->limitsX()[1])); 1135 _confUi->lineEditMinY->setText(QString::number(graph()->limitsY()[0])); 1136 _confUi->lineEditMaxY->setText(QString::number(graph()->limitsY()[1])); 1137 1138 _confUi->checkBoxShowLines->setChecked(graph()->showLines()); 1139 _confUi->checkBoxShowPoints->setChecked(graph()->showPoints()); 1140 1141 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); 1142 1143 connect(_buttonBox, &QDialogButtonBox::clicked, this, &GraphMenuHandler::confApply); 1144 1145 connect(_confUi->dataSourceX, &DataSourceWidget::dataSourceChanged, this, &GraphMenuHandler::confChanged); 1146 connect(_confUi->dataSourceY, &DataSourceWidget::dataSourceChanged, this, &GraphMenuHandler::confChanged); 1147 connect(_confUi->checkBoxAutoX, &QCheckBox::stateChanged, this, &GraphMenuHandler::confChanged); 1148 connect(_confUi->checkBoxAutoY, &QCheckBox::stateChanged, this, &GraphMenuHandler::confChanged); 1149 connect(_confUi->lineEditMinX, &QLineEdit::textEdited, this, &GraphMenuHandler::confChanged); 1150 connect(_confUi->lineEditMaxX, &QLineEdit::textEdited, this, &GraphMenuHandler::confChanged); 1151 connect(_confUi->lineEditMinY, &QLineEdit::textEdited, this, &GraphMenuHandler::confChanged); 1152 connect(_confUi->lineEditMaxY, &QLineEdit::textEdited, this, &GraphMenuHandler::confChanged); 1153 connect(_confUi->checkBoxShowLines, &QCheckBox::stateChanged, this, &GraphMenuHandler::confChanged); 1154 connect(_confUi->checkBoxShowPoints, &QCheckBox::stateChanged, this, &GraphMenuHandler::confChanged); 1155 1156 _confDialog->exec(); 1157 1158 delete _confDialog; _confDialog = nullptr; 1159 delete _confUi; _confUi = nullptr; 1160 } 1161 1162 void GraphMenuHandler::confApply(QAbstractButton *button) 1163 { 1164 if (_buttonBox->button(QDialogButtonBox::Apply) != button 1165 && _buttonBox->button(QDialogButtonBox::Ok) != button) { 1166 return; 1167 } 1168 1169 Q_ASSERT(_confUi && _confDialog); 1170 1171 // XXX: check for actual change ? 1172 if(!_confChanged) return; 1173 _worldModel->beginMacro(i18n("Edit properties of %1", graph()->name())); 1174 1175 QVariant objX = QVariant::fromValue(_confUi->dataSourceX->dataObject()); 1176 1177 QVariant objY = QVariant::fromValue(_confUi->dataSourceY->dataObject()); 1178 1179 _worldModel->setProperty(graph(), QStringLiteral("objectX"), objX); 1180 _worldModel->setProperty(graph(), QStringLiteral("propertyX"), 1181 _confUi->dataSourceX->dataProperty()); 1182 _worldModel->setProperty(graph(), QStringLiteral("indexX"), 1183 _confUi->dataSourceX->dataIndex()); 1184 1185 _worldModel->setProperty(graph(), QStringLiteral("objectY"), objY); 1186 _worldModel->setProperty(graph(), QStringLiteral("propertyY"), 1187 _confUi->dataSourceY->dataProperty()); 1188 _worldModel->setProperty(graph(), QStringLiteral("indexY"), 1189 _confUi->dataSourceY->dataIndex()); 1190 1191 _worldModel->setProperty(graph(), QStringLiteral("autoLimitsX"), 1192 _confUi->checkBoxAutoX->isChecked()); 1193 _worldModel->setProperty(graph(), QStringLiteral("autoLimitsY"), 1194 _confUi->checkBoxAutoY->isChecked()); 1195 1196 StepCore::Vector2d limitsX(_confUi->lineEditMinX->text().toDouble(), 1197 _confUi->lineEditMaxX->text().toDouble()); 1198 StepCore::Vector2d limitsY(_confUi->lineEditMinY->text().toDouble(), 1199 _confUi->lineEditMaxY->text().toDouble()); 1200 1201 _worldModel->setProperty(graph(), QStringLiteral("limitsX"), 1202 QVariant::fromValue(limitsX)); 1203 _worldModel->setProperty(graph(), QStringLiteral("limitsY"), 1204 QVariant::fromValue(limitsY)); 1205 1206 _worldModel->setProperty(graph(), QStringLiteral("showLines"), 1207 _confUi->checkBoxShowLines->isChecked()); 1208 _worldModel->setProperty(graph(), QStringLiteral("showPoints"), 1209 _confUi->checkBoxShowPoints->isChecked()); 1210 1211 _worldModel->endMacro(); 1212 } 1213 1214 void GraphMenuHandler::confChanged() 1215 { 1216 Q_ASSERT(_confUi && _confDialog); 1217 _confChanged = true; 1218 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); 1219 } 1220 1221 void GraphMenuHandler::clearGraph() 1222 { 1223 _worldModel->simulationPause(); 1224 //_lastPointTime = -HUGE_VAL; // XXX 1225 _worldModel->beginMacro(i18n("Clear graph %1", _object->name())); 1226 _worldModel->setProperty(graph(), QStringLiteral("points"), 1227 QVariant::fromValue(StepCore::Vector2dList()) ); 1228 _worldModel->endMacro(); 1229 } 1230 1231 //////////////////////////////////////////////////// 1232 MeterGraphicsItem::MeterGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 1233 : WidgetGraphicsItem(item, worldModel) 1234 { 1235 Q_ASSERT(dynamic_cast<StepCore::Meter*>(_item) != nullptr); 1236 setFlag(QGraphicsItem::ItemIsSelectable); 1237 setFlag(QGraphicsItem::ItemIsMovable); 1238 setAcceptHoverEvents(true); 1239 setBackgroundBrush(QBrush(Qt::white)); 1240 1241 _widget = new QFrame(); 1242 _widget->setFrameShape(QFrame::Box); 1243 _widget->setPalette(QPalette(Qt::lightGray)); 1244 1245 QGridLayout* layout = new QGridLayout(_widget); 1246 layout->setContentsMargins(0,0,2,0); 1247 layout->setSpacing(0); 1248 1249 _lcdNumber = new QLCDNumber(_widget); 1250 _lcdNumber->setFrameShape(QFrame::NoFrame); 1251 _lcdNumber->setSegmentStyle(QLCDNumber::Flat); 1252 _lcdNumber->display(0); 1253 1254 _labelUnits = new QLabel(_widget); 1255 _labelUnits->setAlignment(Qt::AlignRight | Qt::AlignVCenter); 1256 1257 layout->addWidget(_lcdNumber, 0, 0, 1, 1); 1258 layout->addWidget(_labelUnits, 0, 1, 1, 1); 1259 1260 setCenteralWidget(_widget); 1261 setOnHoverHandlerEnabled(true); 1262 _widget->setMouseTracking(true); 1263 _lcdNumber->setMouseTracking(true); 1264 _labelUnits->setMouseTracking(true); 1265 } 1266 1267 inline StepCore::Meter* MeterGraphicsItem::meter() const 1268 { 1269 return static_cast<StepCore::Meter*>(_item); 1270 } 1271 1272 void MeterGraphicsItem::worldDataChanged(bool dynamicOnly) 1273 { 1274 if(!dynamicOnly) { 1275 viewScaleChanged(); 1276 1277 if(meter()->digits() != _lcdNumber->digitCount()) 1278 _lcdNumber->setDigitCount(meter()->digits()); 1279 1280 QString units = meter()->units(); 1281 if(units != _labelUnits->text()) { 1282 QFont font(_labelUnits->font()); 1283 int pixelSize = int(meter()->size()[1]/2); 1284 for(; pixelSize > 0; --pixelSize) { 1285 font.setPixelSize(pixelSize); 1286 QFontMetrics fm(font); 1287 if(fm.boundingRect(units).width() < int(meter()->size()[0]/3)) break; 1288 } 1289 _labelUnits->setFont(font); 1290 _labelUnits->setText(units); 1291 } 1292 1293 WidgetGraphicsItem::worldDataChanged(dynamicOnly); 1294 } 1295 1296 double value = meter()->value(); 1297 _lcdNumber->display(value); 1298 } 1299 1300 void MeterMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) 1301 { 1302 _confUi = nullptr; 1303 _confDialog = nullptr; 1304 _confChanged = false; 1305 1306 menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Meter..."), this, &MeterMenuHandler::configureMeter); 1307 menu->addSeparator(); 1308 ItemMenuHandler::populateMenu(menu, actions); 1309 } 1310 1311 inline StepCore::Meter* MeterMenuHandler::meter() const 1312 { 1313 return static_cast<StepCore::Meter*>(_object); 1314 } 1315 1316 void MeterMenuHandler::configureMeter() 1317 { 1318 if(_worldModel->isSimulationActive()) 1319 _worldModel->simulationStop(); 1320 1321 _confChanged = false; 1322 1323 _confDialog = new QDialog(); // XXX 1324 _confDialog->setWindowTitle(i18nc("@title:window", "Configure Meter")); 1325 _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok 1326 | QDialogButtonBox::Cancel 1327 | QDialogButtonBox::Apply); 1328 QWidget *mainWidget = new QWidget(); 1329 QVBoxLayout *mainLayout = new QVBoxLayout; 1330 _confDialog->setLayout(mainLayout); 1331 mainLayout->addWidget(mainWidget); 1332 1333 QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); 1334 okButton->setDefault(true); 1335 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 1336 _confDialog->connect(_buttonBox, &QDialogButtonBox::accepted, _confDialog, &QDialog::accept); 1337 _confDialog->connect(_buttonBox, &QDialogButtonBox::rejected, _confDialog, &QDialog::reject); 1338 mainLayout->addWidget(_buttonBox); 1339 1340 _confUi = new Ui::WidgetConfigureMeter; 1341 _confUi->setupUi(mainWidget); 1342 1343 _confUi->dataSource->setDataSource(_worldModel, 1344 meter()->object(), meter()->property(), meter()->index()); 1345 1346 _confUi->lineEditDigits->setValidator( 1347 new QIntValidator(0, 100, _confUi->lineEditDigits)); 1348 _confUi->lineEditDigits->setText(QString::number(meter()->digits())); 1349 1350 connect(_buttonBox, &QDialogButtonBox::clicked, this, &MeterMenuHandler::confApply); 1351 1352 connect(_confUi->dataSource, &DataSourceWidget::dataSourceChanged, this, &MeterMenuHandler::confChanged); 1353 connect(_confUi->lineEditDigits, &QLineEdit::textEdited, this, &MeterMenuHandler::confChanged); 1354 1355 _confDialog->exec(); 1356 1357 delete _confDialog; _confDialog = nullptr; 1358 delete _confUi; _confUi = nullptr; 1359 } 1360 1361 void MeterMenuHandler::confApply(QAbstractButton *button) 1362 { 1363 if (_buttonBox->button(QDialogButtonBox::Apply) != button 1364 && _buttonBox->button(QDialogButtonBox::Ok) != button) { 1365 return; 1366 } 1367 1368 Q_ASSERT(_confUi && _confDialog); 1369 1370 // XXX: check for actual change ? 1371 if(!_confChanged) return; 1372 _worldModel->beginMacro(i18n("Edit properties of %1", meter()->name())); 1373 1374 _worldModel->setProperty(meter(), QStringLiteral("object"), 1375 QVariant::fromValue(_confUi->dataSource->dataObject())); 1376 _worldModel->setProperty(meter(), QStringLiteral("property"), 1377 _confUi->dataSource->dataProperty()); 1378 _worldModel->setProperty(meter(), QStringLiteral("index"), 1379 _confUi->dataSource->dataIndex()); 1380 1381 _worldModel->setProperty(meter(), QStringLiteral("digits"), 1382 _confUi->lineEditDigits->text().toInt()); 1383 1384 _worldModel->endMacro(); 1385 } 1386 1387 void MeterMenuHandler::confChanged() 1388 { 1389 Q_ASSERT(_confUi && _confDialog); 1390 _confChanged = true; 1391 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); 1392 } 1393 1394 //////////////////////////////////////////////////// 1395 ControllerGraphicsItem::ControllerGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 1396 : WidgetGraphicsItem(item, worldModel) 1397 { 1398 Q_ASSERT(dynamic_cast<StepCore::Controller*>(_item) != nullptr); 1399 setFlag(QGraphicsItem::ItemIsSelectable); 1400 setFlag(QGraphicsItem::ItemIsMovable); 1401 setAcceptHoverEvents(true); 1402 setBackgroundBrush(QBrush(Qt::white)); 1403 1404 _widget = new QWidget(); 1405 _widget->setPalette(QPalette(Qt::lightGray)); 1406 QGridLayout* layout = new QGridLayout(_widget); 1407 1408 _labelMin = new QLabel(_widget); _labelMin->setAlignment(Qt::AlignRight); 1409 _labelMax = new QLabel(_widget); _labelMax->setAlignment(Qt::AlignLeft); 1410 _labelSource = new QLabel(_widget); _labelSource->setAlignment(Qt::AlignHCenter | Qt::AlignTop); 1411 _slider = new QSlider(Qt::Horizontal, _widget); 1412 _slider->setRange(SLIDER_MIN, SLIDER_MAX); 1413 connect(_slider, &QAbstractSlider::sliderMoved, this, &ControllerGraphicsItem::sliderChanged); 1414 connect(_slider, &QAbstractSlider::sliderReleased, this, &ControllerGraphicsItem::sliderReleased); 1415 1416 layout->addWidget(_labelMin, 0, 0, 1, 1); 1417 layout->addWidget(_slider, 0, 1, 1, 1); 1418 layout->addWidget(_labelMax, 0, 2, 1, 1); 1419 layout->addWidget(_labelSource, 1, 0, 1, 3); 1420 1421 _incAction = new QAction(i18n("Increase Value"), _widget); 1422 _decAction = new QAction(i18n("Decrease Value"), _widget); 1423 1424 connect(_incAction, &QAction::triggered, this, &ControllerGraphicsItem::incTriggered); 1425 connect(_decAction, &QAction::triggered, this, &ControllerGraphicsItem::decTriggered); 1426 1427 _widget->addAction(_incAction); 1428 _widget->addAction(_decAction); 1429 //_widget->addAction(_configureAction); 1430 //_widget->setContextMenuPolicy(Qt::ActionsContextMenu); 1431 1432 _lastValue = 1; 1433 _changed = false; 1434 1435 setCenteralWidget(_widget); 1436 setOnHoverHandlerEnabled(true); 1437 _widget->setMouseTracking(true); 1438 _labelMin->setMouseTracking(true); 1439 _labelMax->setMouseTracking(true); 1440 _labelSource->setMouseTracking(true); 1441 _slider->setMouseTracking(true); 1442 } 1443 1444 inline StepCore::Controller* ControllerGraphicsItem::controller() const 1445 { 1446 return static_cast<StepCore::Controller*>(_item); 1447 } 1448 1449 void ControllerGraphicsItem::decTriggered() 1450 { 1451 _worldModel->simulationPause(); 1452 _worldModel->beginMacro(i18n("Decrease controller %1", _item->name())); 1453 _worldModel->setProperty(controller(), QStringLiteral("value"), 1454 controller()->value() - controller()->increment()); 1455 _worldModel->endMacro(); 1456 } 1457 1458 void ControllerGraphicsItem::incTriggered() 1459 { 1460 _worldModel->simulationPause(); 1461 _worldModel->beginMacro(i18n("Increase controller %1", _item->name())); 1462 _worldModel->setProperty(controller(), QStringLiteral("value"), 1463 controller()->value() + controller()->increment()); 1464 _worldModel->endMacro(); 1465 } 1466 1467 void ControllerGraphicsItem::worldDataChanged(bool dynamicOnly) 1468 { 1469 if(!dynamicOnly) { 1470 viewScaleChanged(); 1471 1472 // Labels 1473 _labelMin->setText(QString::number(controller()->limits()[0])); 1474 _labelMax->setText(QString::number(controller()->limits()[1])); 1475 1476 QString source; 1477 if(controller()->isValid()) { 1478 source = i18n("%1.%2", _worldModel->formatName(controller()->object()), controller()->property()); 1479 if(controller()->index() >= 0) source.append(i18n("[%1]", controller()->index())); 1480 QString units = controller()->units(); 1481 if(!units.isEmpty()) source.append(" [").append(units).append("]"); 1482 } else { 1483 source = i18n("[not configured]"); 1484 } 1485 _labelSource->setText(source); 1486 1487 if(_incAction->isEnabled() != controller()->isValid()) { 1488 _incAction->setEnabled(controller()->isValid()); 1489 _decAction->setEnabled(controller()->isValid()); 1490 } 1491 1492 if(_incShortcut != controller()->increaseShortcut()) { 1493 _incShortcut = controller()->increaseShortcut(); 1494 _incAction->setShortcut(QKeySequence(_incShortcut)); 1495 } 1496 1497 if(_decShortcut != controller()->decreaseShortcut()) { 1498 _decShortcut = controller()->decreaseShortcut(); 1499 _decAction->setShortcut(QKeySequence(_decShortcut)); 1500 } 1501 1502 //if(!graph()->autoLimitsX() && !graph()->autoLimitsY()) adjustLimits(); 1503 1504 /* 1505 // Points 1506 _plotObject->clearPoints(); 1507 for(int i=0; i<(int) graph()->points().size(); ++i) { 1508 StepCore::Vector2d p = graph()->points()[i]; 1509 _plotObject->addPoint(p[0], p[1]); 1510 } 1511 1512 adjustLimits(); 1513 _plotWidget->update(); 1514 */ 1515 1516 WidgetGraphicsItem::worldDataChanged(dynamicOnly); 1517 } 1518 1519 double value = round((controller()->value() - controller()->limits()[0]) * 1520 (SLIDER_MAX - SLIDER_MIN) / (controller()->limits()[1] - controller()->limits()[0]) + SLIDER_MIN); 1521 1522 if(value <= SLIDER_MIN && _lastValue > SLIDER_MIN) { 1523 QPalette palette; palette.setColor(_labelMin->foregroundRole(), Qt::red); 1524 _labelMin->setPalette(palette); 1525 } else if(value > SLIDER_MIN && _lastValue <= SLIDER_MIN) { 1526 QPalette palette; _labelMin->setPalette(palette); 1527 } 1528 1529 if(value >= SLIDER_MAX-1 && _lastValue < SLIDER_MAX-1) { 1530 QPalette palette; palette.setColor(_labelMax->foregroundRole(), Qt::red); 1531 _labelMax->setPalette(palette); 1532 } else if(value < SLIDER_MAX-1 && _lastValue >= SLIDER_MAX-1) { 1533 QPalette palette; _labelMax->setPalette(palette); 1534 } 1535 1536 _lastValue = value; 1537 1538 if(value < SLIDER_MIN) value = SLIDER_MIN; 1539 else if(value > SLIDER_MAX-1) value = SLIDER_MAX-1; 1540 1541 _slider->setValue(int(value)); 1542 1543 #if 0 1544 1545 if(_worldModel->isSimulationActive()) { 1546 if(_worldModel->world()->time() > _lastPointTime 1547 + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { 1548 StepCore::Vector2d point = graph()->recordPoint(); 1549 _lastPointTime = _worldModel->world()->time(); 1550 } 1551 } 1552 1553 int po_count, p_count; 1554 do { 1555 const QList<KPlotPoint*> points = _plotObject->points(); 1556 po_count = points.count(); p_count = graph()->points().size(); 1557 int count = qMin(po_count, p_count); 1558 for(int p=0; p < count; ++p) 1559 points[p]->setPosition(vectorToPoint(graph()->points()[p])); 1560 } while(0); 1561 1562 if(po_count < p_count) { 1563 for(; po_count < p_count; ++po_count) 1564 _plotObject->addPoint(vectorToPoint(graph()->points()[po_count])); 1565 } else { 1566 for(--po_count; po_count >= p_count; --po_count) 1567 _plotObject->removePoint(po_count); 1568 } 1569 1570 if(graph()->autoLimitsX() || graph()->autoLimitsY()) adjustLimits(); 1571 _plotWidget->update(); 1572 #endif 1573 #if 0 1574 //#error Do setProperty here and remove DynamicOnly from points 1575 if(ok) { 1576 _plotObject->addPoint(point[0], point[1]); 1577 if(graph()->autoLimitsX() || graph()->autoLimitsY()) 1578 adjustLimits(); 1579 _plotWidget->update(); 1580 } 1581 _lastPointTime = _worldModel->world()->time(); 1582 worldDataChanged(false); 1583 //_worldModel->setProperty(graph(), "name", QString("test")); 1584 } 1585 #endif 1586 } 1587 1588 void ControllerGraphicsItem::sliderChanged(int value) 1589 { 1590 Q_ASSERT(value == _slider->sliderPosition()); 1591 if(!controller()->isValid()) return; 1592 //if(!_worldModel->isSimulationActive()) { 1593 _worldModel->simulationPause(); 1594 if(!_changed) { 1595 _worldModel->beginMacro(i18n("Change controller %1", controller()->name())); 1596 _changed = true; 1597 } 1598 double v = controller()->limits()[0] + (value - SLIDER_MIN) * 1599 (controller()->limits()[1] - controller()->limits()[0]) / (SLIDER_MAX - SLIDER_MIN); 1600 _worldModel->setProperty(controller(), QStringLiteral("value"), v); 1601 //} 1602 } 1603 1604 void ControllerGraphicsItem::sliderReleased() 1605 { 1606 if(_changed) { 1607 _worldModel->endMacro(); 1608 _changed = false; 1609 } 1610 } 1611 1612 void ControllerMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) 1613 { 1614 _confUi = nullptr; 1615 _confDialog = nullptr; 1616 _confChanged = false; 1617 1618 menu->addAction(QIcon::fromTheme(QStringLiteral("arrow-up")), i18n("Increase Value"), this, &ControllerMenuHandler::incTriggered); 1619 menu->addAction(QIcon::fromTheme(QStringLiteral("arrow-down")), i18n("Decrease Value"), this, &ControllerMenuHandler::decTriggered); 1620 menu->addSeparator(); 1621 menu->addAction(QIcon::fromTheme(QStringLiteral("configure")), i18n("Configure Controller..."), this, &ControllerMenuHandler::configureController); 1622 menu->addSeparator(); 1623 ItemMenuHandler::populateMenu(menu, actions); 1624 } 1625 1626 inline StepCore::Controller* ControllerMenuHandler::controller() const 1627 { 1628 return static_cast<StepCore::Controller*>(_object); 1629 } 1630 1631 void ControllerMenuHandler::configureController() 1632 { 1633 if(_worldModel->isSimulationActive()) 1634 _worldModel->simulationStop(); 1635 1636 _confChanged = false; 1637 _confDialog = new QDialog(); // XXX 1638 1639 _confDialog->setWindowTitle(i18nc("@title:window", "Configure Controller")); 1640 _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok 1641 | QDialogButtonBox::Cancel 1642 | QDialogButtonBox::Apply); 1643 QWidget *mainWidget = new QWidget(); 1644 QVBoxLayout *mainLayout = new QVBoxLayout; 1645 _confDialog->setLayout(mainLayout); 1646 mainLayout->addWidget(mainWidget); 1647 1648 QPushButton *okButton = _buttonBox->button(QDialogButtonBox::Ok); 1649 okButton->setDefault(true); 1650 okButton->setShortcut(Qt::CTRL | Qt::Key_Return); 1651 _confDialog->connect(_buttonBox, &QDialogButtonBox::accepted, _confDialog, &QDialog::accept); 1652 _confDialog->connect(_buttonBox, &QDialogButtonBox::rejected, _confDialog, &QDialog::reject); 1653 mainLayout->addWidget(_buttonBox); 1654 1655 _confUi = new Ui::WidgetConfigureController; 1656 _confUi->setupUi(mainWidget); 1657 1658 _confUi->dataSource->setSkipReadOnly(true); 1659 _confUi->dataSource->setDataSource(_worldModel, controller()->object(), 1660 controller()->property(), controller()->index()); 1661 1662 _confUi->lineEditMin->setValidator( 1663 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMin)); 1664 _confUi->lineEditMax->setValidator( 1665 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditMax)); 1666 1667 _confUi->lineEditMin->setText(QString::number(controller()->limits()[0])); 1668 _confUi->lineEditMax->setText(QString::number(controller()->limits()[1])); 1669 1670 _confUi->keyIncrease->setModifierlessAllowed(true); 1671 _confUi->keyDecrease->setModifierlessAllowed(true); 1672 1673 //_confUi->keyIncrease->setKeySequence(_incAction->shortcut().primary()); 1674 //_confUi->keyDecrease->setKeySequence(_decAction->shortcut().primary()); 1675 _confUi->keyIncrease->setKeySequence(QKeySequence(controller()->increaseShortcut())); 1676 _confUi->keyDecrease->setKeySequence(QKeySequence(controller()->decreaseShortcut())); 1677 1678 _confUi->lineEditIncrement->setValidator( 1679 new QDoubleValidator(-HUGE_VAL, HUGE_VAL, DBL_DIG, _confUi->lineEditIncrement)); 1680 _confUi->lineEditIncrement->setText(QString::number(controller()->increment())); 1681 1682 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(false); 1683 1684 connect(_buttonBox, &QDialogButtonBox::clicked, this, &ControllerMenuHandler::confApply); 1685 1686 connect(_confUi->dataSource, &DataSourceWidget::dataSourceChanged, this, &ControllerMenuHandler::confChanged); 1687 connect(_confUi->lineEditMin, &QLineEdit::textEdited, this, &ControllerMenuHandler::confChanged); 1688 connect(_confUi->lineEditMax, &QLineEdit::textEdited, this, &ControllerMenuHandler::confChanged); 1689 connect(_confUi->keyIncrease, &KKeySequenceWidget::keySequenceChanged, this, &ControllerMenuHandler::confChanged); 1690 connect(_confUi->keyDecrease, &KKeySequenceWidget::keySequenceChanged, this, &ControllerMenuHandler::confChanged); 1691 connect(_confUi->lineEditIncrement, &QLineEdit::textEdited, this, &ControllerMenuHandler::confChanged); 1692 1693 _confDialog->exec(); 1694 1695 delete _confDialog; _confDialog = nullptr; 1696 delete _confUi; _confUi = nullptr; 1697 } 1698 1699 void ControllerMenuHandler::confApply(QAbstractButton *button) 1700 { 1701 if (_buttonBox->button(QDialogButtonBox::Apply) != button 1702 && _buttonBox->button(QDialogButtonBox::Ok) != button) { 1703 return; 1704 } 1705 1706 Q_ASSERT(_confUi && _confDialog); 1707 1708 // XXX: check for actual change ? 1709 if(!_confChanged) return; 1710 _worldModel->beginMacro(i18n("Edit properties of %1", controller()->name())); 1711 1712 _worldModel->setProperty(controller(), QStringLiteral("object"), 1713 QVariant::fromValue(_confUi->dataSource->dataObject())); 1714 _worldModel->setProperty(controller(), QStringLiteral("property"), 1715 _confUi->dataSource->dataProperty()); 1716 _worldModel->setProperty(controller(), QStringLiteral("index"), 1717 _confUi->dataSource->dataIndex()); 1718 1719 StepCore::Vector2d limits(_confUi->lineEditMin->text().toDouble(), 1720 _confUi->lineEditMax->text().toDouble()); 1721 1722 _worldModel->setProperty(controller(), QStringLiteral("limits"), 1723 QVariant::fromValue(limits)); 1724 1725 _worldModel->setProperty(controller(), QStringLiteral("increaseShortcut"), 1726 QVariant::fromValue(_confUi->keyIncrease->keySequence().toString())); 1727 1728 _worldModel->setProperty(controller(), QStringLiteral("decreaseShortcut"), 1729 QVariant::fromValue(_confUi->keyDecrease->keySequence().toString())); 1730 1731 _worldModel->setProperty(controller(), QStringLiteral("increment"), 1732 QVariant::fromValue(_confUi->lineEditIncrement->text().toDouble())); 1733 1734 _worldModel->endMacro(); 1735 } 1736 1737 void ControllerMenuHandler::confChanged() 1738 { 1739 Q_ASSERT(_confUi && _confDialog); 1740 _confChanged = true; 1741 _buttonBox->button(QDialogButtonBox::Apply)->setEnabled(true); 1742 } 1743 1744 void ControllerMenuHandler::decTriggered() 1745 { 1746 _worldModel->simulationPause(); 1747 _worldModel->beginMacro(i18n("Decrease controller %1", _object->name())); 1748 _worldModel->setProperty(controller(), QStringLiteral("value"), 1749 controller()->value() - controller()->increment()); 1750 _worldModel->endMacro(); 1751 } 1752 1753 void ControllerMenuHandler::incTriggered() 1754 { 1755 _worldModel->simulationPause(); 1756 _worldModel->beginMacro(i18n("Increase controller %1", _object->name())); 1757 _worldModel->setProperty(controller(), QStringLiteral("value"), 1758 controller()->value() + controller()->increment()); 1759 _worldModel->endMacro(); 1760 } 1761 1762 //////////////////////////////////////////////////// 1763 1764 TracerGraphicsItem::TracerGraphicsItem(StepCore::Item* item, WorldModel* worldModel) 1765 : StepGraphicsItem(item, worldModel), _moving(false), _movingDelta(0,0) 1766 { 1767 Q_ASSERT(dynamic_cast<StepCore::Tracer*>(_item) != nullptr); 1768 setFlag(QGraphicsItem::ItemIsSelectable); 1769 setFlag(QGraphicsItem::ItemIsMovable); 1770 setZValue(HANDLER_ZVALUE); 1771 1772 /* 1773 _lastArrowRadius = -1; 1774 _velocityHandler = new ArrowHandlerGraphicsItem(item, worldModel, this, 1775 _item->metaObject()->property("velocity")); 1776 _velocityHandler->setVisible(false);*/ 1777 //scene()->addItem(_velocityHandler); 1778 1779 _lastPos = QPointF(0,0); 1780 _lastPointTime = -HUGE_VAL; 1781 } 1782 1783 inline StepCore::Tracer* TracerGraphicsItem::tracer() const 1784 { 1785 return static_cast<StepCore::Tracer*>(_item); 1786 } 1787 1788 1789 QPainterPath TracerGraphicsItem::shape() const 1790 { 1791 QPainterPath path; 1792 double w = (HANDLER_SIZE+1)/currentViewScale(); 1793 // XXX: add _points here! 1794 path.addEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w,w*2,w*2)); 1795 return path; 1796 } 1797 1798 void TracerGraphicsItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/) 1799 { 1800 double s = currentViewScale(); 1801 double w = HANDLER_SIZE/s; 1802 1803 painter->setRenderHint(QPainter::Antialiasing, true); 1804 painter->setPen(QPen(QColor::fromRgba(tracer()->color()), 0)); 1805 //painter->setBrush(QBrush(Qt::black)); 1806 painter->drawPolyline(_points); 1807 painter->drawEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w, w*2,w*2)); 1808 painter->drawPoint(_lastPos); 1809 1810 if(_isSelected) { 1811 painter->setPen(QPen(SELECTION_COLOR, 0, Qt::DashLine)); 1812 //painter->setBrush(QBrush()); 1813 //painter->setBrush(QBrush(QColor(0, 0x99, 0xff))); 1814 w = (HANDLER_SIZE + SELECTION_MARGIN)/s; 1815 painter->drawEllipse(QRectF(_lastPos.x()-w, _lastPos.y()-w, w*2, w*2)); 1816 } 1817 1818 } 1819 1820 void TracerGraphicsItem::viewScaleChanged() 1821 { 1822 prepareGeometryChange(); 1823 1824 double s = currentViewScale(); 1825 double w = (HANDLER_SIZE+SELECTION_MARGIN)/s; 1826 QPointF p = vectorToPoint(tracer()->position()); 1827 1828 _boundingRect = _points.boundingRect() | QRectF(p.x()-w, p.y()-w,2*w,2*w); 1829 update(); 1830 } 1831 1832 void TracerGraphicsItem::worldDataChanged(bool) 1833 { 1834 /* 1835 if(_isMouseOverItem || _isSelected) { 1836 double vnorm = particle()->velocity().norm(); 1837 double anorm = particle()->force().norm() / particle()->mass(); 1838 double arrowRadius = qMax(vnorm, anorm) + ARROW_STROKE/currentViewScale(); 1839 if(arrowRadius > _lastArrowRadius || arrowRadius < _lastArrowRadius/2) { 1840 _lastArrowRadius = arrowRadius; 1841 viewScaleChanged(); 1842 } 1843 update(); 1844 } 1845 */ 1846 1847 //setPos(vectorToPoint(tracer()->position())); 1848 1849 if(_worldModel->isSimulationActive()) { 1850 if(_worldModel->world()->time() > _lastPointTime 1851 + 1.0/_worldModel->simulationFps() - 1e-2/_worldModel->simulationFps()) { 1852 tracer()->recordPoint(); 1853 _lastPointTime = _worldModel->world()->time(); 1854 } 1855 } 1856 1857 bool geometryChange = false; 1858 int po_count, p_count; 1859 do { 1860 po_count = _points.size(); p_count = tracer()->points().size(); 1861 int count = qMin(po_count, p_count); 1862 for(int p=0; p < count; ++p) { 1863 QPointF point = vectorToPoint(tracer()->points()[p]); 1864 if(point != _points[p]) { 1865 geometryChange = true; 1866 _points[p] = point; 1867 } 1868 } 1869 } while(0); 1870 1871 if(po_count < p_count) { 1872 geometryChange = true; 1873 for(; po_count < p_count; ++po_count) 1874 _points << vectorToPoint(tracer()->points()[po_count]); 1875 } else { 1876 geometryChange = true; 1877 _points.resize(p_count); 1878 } 1879 1880 QPointF point = vectorToPoint(tracer()->position()); 1881 if(point != _lastPos) { 1882 geometryChange = true; 1883 _lastPos = point; 1884 } 1885 1886 if(geometryChange) { 1887 viewScaleChanged(); 1888 } 1889 } 1890 1891 void TracerGraphicsItem::mouseSetPos(const QPointF&, const QPointF& diff, MovingState movingState) 1892 { 1893 static_cast<WorldScene*>(scene())->snapItem(vectorToPoint(tracer()->position()) + diff, 1894 WorldScene::SnapRigidBody | WorldScene::SnapParticle | 1895 WorldScene::SnapSetLocalPosition, nullptr, movingState, _item); 1896 } 1897 1898 void TracerMenuHandler::populateMenu(QMenu* menu, KActionCollection* actions) 1899 { 1900 menu->addAction(QIcon::fromTheme(QStringLiteral("edit-clear")), i18n("Clear Trace"), this, &TracerMenuHandler::clearTracer); 1901 menu->addSeparator(); 1902 ItemMenuHandler::populateMenu(menu, actions); 1903 } 1904 1905 void TracerMenuHandler::clearTracer() 1906 { 1907 _worldModel->simulationPause(); 1908 //_lastPointTime = -HUGE_VAL; // XX 1909 _worldModel->beginMacro(i18n("Clear tracer %1", _object->name())); 1910 _worldModel->setProperty(_object, QStringLiteral("points"), 1911 QVariant::fromValue(StepCore::Vector2dList()) ); 1912 _worldModel->endMacro(); 1913 } 1914 1915 #include "moc_toolgraphics.cpp"