File indexing completed on 2024-05-12 03:48:19
0001 /* 0002 File : InfoElement.cpp 0003 Project : LabPlot 0004 Description : Marker which can highlight points of curves and 0005 show their values 0006 -------------------------------------------------------------------- 0007 SPDX-FileCopyrightText: 2020 Martin Marmsoler <martin.marmsoler@gmail.com> 0008 SPDX-FileCopyrightText: 2020-2023 Alexander Semke <alexander.semke@web.de> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include "InfoElement.h" 0014 0015 #include "backend/core/AbstractColumn.h" 0016 #include "backend/core/Project.h" 0017 #include "backend/lib/XmlStreamReader.h" 0018 #include "backend/lib/commandtemplates.h" 0019 #include "backend/worksheet/InfoElementPrivate.h" 0020 #include "backend/worksheet/Line.h" 0021 #include "backend/worksheet/Worksheet.h" 0022 #include "backend/worksheet/plots/cartesian/CartesianCoordinateSystem.h" 0023 #include "backend/worksheet/plots/cartesian/CartesianPlot.h" 0024 #include "backend/worksheet/plots/cartesian/CustomPoint.h" 0025 #include "backend/worksheet/plots/cartesian/XYCurve.h" 0026 0027 #include <KConfig> 0028 #include <KConfigGroup> 0029 0030 #include <QAction> 0031 #include <QDateTime> 0032 #include <QGraphicsSceneMouseEvent> 0033 #include <QKeyEvent> 0034 #include <QMenu> 0035 #include <QPainter> 0036 #include <QTextEdit> 0037 0038 InfoElement::InfoElement(const QString& name, CartesianPlot* plot) 0039 : WorksheetElement(name, new InfoElementPrivate(this), AspectType::InfoElement) { 0040 m_plot = plot; 0041 0042 init(); 0043 setVisible(false); 0044 } 0045 0046 InfoElement::InfoElement(const QString& name, CartesianPlot* p, const XYCurve* curve, double logicalPos) 0047 : WorksheetElement(name, new InfoElementPrivate(this, curve), AspectType::InfoElement) { 0048 Q_D(InfoElement); 0049 m_plot = p; 0050 0051 init(); 0052 0053 m_suppressChildPositionChanged = true; 0054 0055 if (curve) { 0056 d->connectionLineCurveName = curve->name(); 0057 auto* custompoint = new CustomPoint(m_plot, curve->name()); 0058 custompoint->setFixed(true); 0059 custompoint->setCoordinateBindingEnabled(true); 0060 custompoint->setCoordinateSystemIndex(curve->coordinateSystemIndex()); 0061 addChild(custompoint); 0062 InfoElement::MarkerPoints_T markerpoint(custompoint, curve, curve->path()); 0063 markerpoints.append(markerpoint); 0064 0065 // setpos after label was created 0066 if (curve->xColumn() && curve->yColumn()) { 0067 bool valueFound; 0068 double xpos; 0069 double y = curve->y(logicalPos, xpos, valueFound); 0070 if (valueFound) { 0071 d->positionLogical = xpos; 0072 d->m_index = curve->xColumn()->indexForValue(xpos); 0073 custompoint->setPositionLogical(QPointF(xpos, y)); 0074 DEBUG("Value found") 0075 } 0076 } else { 0077 d->positionLogical = 0; 0078 custompoint->setPositionLogical(cSystem->mapSceneToLogical(QPointF(0, 0))); 0079 DEBUG("Value not found") 0080 } 0081 0082 TextLabel::TextWrapper text; 0083 text.allowPlaceholder = true; 0084 0085 QString textString = QString::number(markerpoints[0].customPoint->positionLogical().x()) + QStringLiteral(", "); 0086 textString.append(QString(QString(markerpoints[0].curve->name() + QStringLiteral(":")))); 0087 textString.append(QString::number(markerpoints[0].customPoint->positionLogical().y())); 0088 text.text = textString; 0089 0090 // TODO: Find better solution than using textedit 0091 QString str = QStringLiteral("&(x), ") + markerpoints[0].curve->name() + QLatin1Char(':') + QLatin1String("&(") + markerpoints[0].curve->name() 0092 + QLatin1Char(')'); 0093 QTextEdit textedit(str); 0094 text.textPlaceholder = textedit.toHtml(); 0095 0096 m_setTextLabelText = true; 0097 m_title->setUndoAware(false); 0098 m_title->setText(text); 0099 m_title->setUndoAware(true); 0100 m_setTextLabelText = false; 0101 0102 initCurveConnections(curve); 0103 custompoint->setVisible(curve->isVisible()); 0104 0105 setVisible(true); 0106 } else 0107 setVisible(false); 0108 0109 m_suppressChildPositionChanged = false; 0110 } 0111 0112 InfoElement::~InfoElement() { 0113 // m_suppressChildRemoved = true; 0114 // // this function is not called, when deleting marker 0115 // // don't understand why I have to remove them manually 0116 // // I think it is because of the graphicsitem, which exists 0117 // for (auto markerpoint : markerpoints) { 0118 // removeChild(markerpoint.customPoint); 0119 // } 0120 0121 // removeChild(m_title); 0122 0123 // m_suppressChildRemoved = false; 0124 } 0125 0126 void InfoElement::init() { 0127 cSystem = dynamic_cast<const CartesianCoordinateSystem*>(m_plot->coordinateSystem(m_cSystemIndex)); 0128 0129 initActions(); 0130 initMenus(); 0131 0132 connect(this, &InfoElement::childAspectRemoved, this, &InfoElement::childRemoved); 0133 connect(this, &InfoElement::childAspectAdded, this, &InfoElement::childAdded); 0134 0135 m_title = new TextLabel(i18n("Label"), m_plot); 0136 m_title->setHidden(true); 0137 TextLabel::TextWrapper text; 0138 text.allowPlaceholder = true; 0139 m_setTextLabelText = true; 0140 m_title->setUndoAware(false); 0141 m_title->setText(text); // set placeholder to true 0142 m_title->setUndoAware(true); 0143 m_setTextLabelText = false; 0144 addChild(m_title); 0145 0146 // use the line properties of axis line also for the info element lines 0147 KConfig config; 0148 const auto& group = config.group(QStringLiteral("Axis")); 0149 0150 // lines 0151 Q_D(InfoElement); 0152 d->verticalLine = new Line(QString()); 0153 d->verticalLine->setHidden(true); 0154 d->verticalLine->setPrefix(QStringLiteral("VerticalLine")); 0155 addChild(d->verticalLine); 0156 d->verticalLine->init(group); 0157 connect(d->verticalLine, &Line::updatePixmapRequested, [=] { 0158 d->update(); 0159 }); 0160 connect(d->verticalLine, &Line::updateRequested, [=] { 0161 d->updateVerticalLine(); 0162 }); 0163 0164 d->connectionLine = new Line(QString()); 0165 d->connectionLine->setHidden(true); 0166 d->connectionLine->setPrefix(QStringLiteral("ConnectionLine")); 0167 addChild(d->connectionLine); 0168 d->connectionLine->init(group); 0169 connect(d->connectionLine, &Line::updatePixmapRequested, [=] { 0170 d->update(); 0171 }); 0172 connect(d->connectionLine, &Line::updateRequested, [=] { 0173 d->updateConnectionLine(); 0174 }); 0175 } 0176 0177 void InfoElement::initActions() { 0178 } 0179 0180 void InfoElement::initMenus() { 0181 m_menusInitialized = true; 0182 } 0183 0184 void InfoElement::initCurveConnections(const XYCurve* curve) { 0185 connect(curve, &XYCurve::visibleChanged, this, &InfoElement::curveVisibilityChanged); 0186 connect(curve, &XYCurve::coordinateSystemIndexChanged, this, &InfoElement::curveCoordinateSystemIndexChanged); 0187 connect(curve, &XYCurve::dataChanged, this, &InfoElement::curveDataChanged); 0188 connect(curve, &XYCurve::moveBegin, this, [this]() { 0189 m_curveGetsMoved = true; 0190 }); 0191 connect(curve, &XYCurve::moveEnd, this, [this]() { 0192 m_curveGetsMoved = false; 0193 }); 0194 } 0195 0196 QMenu* InfoElement::createContextMenu() { 0197 if (!m_menusInitialized) 0198 initMenus(); 0199 0200 return WorksheetElement::createContextMenu(); 0201 } 0202 0203 /*! 0204 * @brief InfoElement::addCurve 0205 * Adds a new markerpoint to the plot which is placed on the curve curve 0206 * @param curve Curve on which the markerpoints sits 0207 * @param custompoint Use existing point, if the project was loaded the custompoint can have different settings 0208 */ 0209 void InfoElement::addCurve(const XYCurve* curve, CustomPoint* custompoint) { 0210 Q_D(InfoElement); 0211 0212 for (auto& markerpoint : markerpoints) { 0213 if (curve == markerpoint.curve) 0214 return; 0215 } 0216 0217 project()->setSuppressAspectAddedSignal(true); 0218 0219 if (!custompoint) { 0220 m_suppressChildPositionChanged = true; 0221 custompoint = new CustomPoint(m_plot, curve->name()); 0222 custompoint->setCoordinateBindingEnabled(true); 0223 custompoint->setCoordinateSystemIndex(curve->coordinateSystemIndex()); 0224 addChild(custompoint); 0225 0226 if (curve->xColumn() && curve->yColumn()) { 0227 bool valueFound; 0228 double x_new, y; 0229 y = curve->y(d->positionLogical, x_new, valueFound); 0230 0231 custompoint->setUndoAware(false); 0232 custompoint->setPositionLogical(QPointF(x_new, y)); 0233 custompoint->setUndoAware(true); 0234 } 0235 m_suppressChildPositionChanged = false; 0236 } else 0237 addChild(custompoint); 0238 0239 project()->setSuppressAspectAddedSignal(true); 0240 0241 initCurveConnections(curve); 0242 0243 custompoint->setUndoAware(false); 0244 custompoint->setVisible(curve->isVisible()); 0245 custompoint->setUndoAware(true); 0246 0247 if (d->m_index < 0 && curve->xColumn()) 0248 d->m_index = curve->xColumn()->indexForValue(custompoint->positionLogical().x()); 0249 0250 struct MarkerPoints_T markerpoint = {custompoint, curve, curve->path()}; 0251 markerpoints.append(markerpoint); 0252 0253 if (markerpoints.count() == 1) // first point 0254 setConnectionLineCurveName(curve->name()); 0255 0256 m_title->setUndoAware(false); 0257 m_title->setText(createTextLabelText()); 0258 0259 if (markerpoints.length() == 1) { 0260 // Do a retransform, because when the first markerpoint 0261 // was added, after a curve was removed and added, the 0262 // position of the connection line must be recalculated 0263 retransform(); 0264 } 0265 0266 m_title->setVisible(true); // show in the worksheet view 0267 m_title->setUndoAware(true); 0268 } 0269 0270 /*! 0271 * \brief InfoElement::addCurvePath 0272 * When loading infoelement from xml file, there is no information available, which curves are loaded. 0273 * So only the path will be stored and after all curves where loaded the curves will be assigned to the InfoElement 0274 * with the function assignCurve 0275 * Assumption: if custompoint!=nullptr then the custompoint was already added to the InfoElement previously. Here 0276 * only new created CustomPoints will be added to the InfoElement 0277 * @param curvePath path from the curve 0278 * @param custompoint adding already created custom point 0279 */ 0280 void InfoElement::addCurvePath(QString& curvePath, CustomPoint* custompoint) { 0281 for (auto& markerpoint : markerpoints) { 0282 if (curvePath == markerpoint.curvePath) 0283 return; 0284 } 0285 0286 if (!custompoint) { 0287 custompoint = new CustomPoint(m_plot, i18n("Symbol")); 0288 custompoint->setVisible(false); 0289 m_suppressChildPositionChanged = true; 0290 custompoint->setCoordinateBindingEnabled(true); 0291 m_suppressChildPositionChanged = false; 0292 addChild(custompoint); 0293 } 0294 0295 struct MarkerPoints_T markerpoint = {custompoint, nullptr, curvePath}; 0296 markerpoints.append(markerpoint); 0297 } 0298 0299 /*! 0300 * \brief assignCurve 0301 * Finds the curve with the path stored in the markerpoints and assigns the pointer to markerpoints 0302 * @param curves 0303 * \return true if all markerpoints are assigned with a curve, false if one or more markerpoints don't have a curve assigned 0304 */ 0305 bool InfoElement::assignCurve(const QVector<XYCurve*>& curves) { 0306 bool success = true; 0307 for (auto& mp : markerpoints) { 0308 for (auto curve : curves) { 0309 if (mp.curvePath == curve->path()) { 0310 mp.curve = curve; 0311 initCurveConnections(curve); 0312 mp.customPoint->setVisible(curve->isVisible()); // initial visibility 0313 mp.customPoint->setCoordinateSystemIndex(curve->coordinateSystemIndex()); 0314 break; 0315 } 0316 } 0317 } 0318 0319 // check if all markerpoints have a valid curve 0320 // otherwise delete customPoint with no valid curve 0321 for (int i = markerpoints.count() - 1; i >= 0; i--) { 0322 if (markerpoints[i].curve == nullptr) { 0323 removeChild(markerpoints[i].customPoint); 0324 success = false; 0325 } 0326 } 0327 0328 return success; 0329 } 0330 0331 /*! 0332 * Remove markerpoint from a curve 0333 * @param curve 0334 */ 0335 void InfoElement::removeCurve(const XYCurve* curve) { 0336 if (m_curveGetsMoved) 0337 return; 0338 0339 for (const auto& mp : markerpoints) { 0340 if (mp.curve == curve) { 0341 disconnect(curve, nullptr, this, nullptr); 0342 removeChild(mp.customPoint); 0343 } 0344 } 0345 0346 setUndoAware(false); 0347 if (curve->name() == connectionLineCurveName()) 0348 setConnectionLineCurveName(markerpoints.count() > 0 ? markerpoints.at(0).curve->name() : QStringLiteral()); 0349 setUndoAware(true); 0350 0351 m_title->setUndoAware(false); 0352 m_title->setText(createTextLabelText()); 0353 0354 // hide the label if now curves are selected 0355 if (markerpoints.isEmpty()) { 0356 m_title->setUndoAware(false); 0357 m_title->setVisible(false); // hide in the worksheet view 0358 m_title->setUndoAware(true); 0359 Q_D(InfoElement); 0360 d->update(); // redraw to remove all children graphic items belonging to InfoElement 0361 } 0362 0363 m_title->setUndoAware(true); 0364 } 0365 0366 /*! 0367 * Set the z value of the m_title and the custompoints higher than the infoelement 0368 * @param value 0369 */ 0370 void InfoElement::setZValue(qreal value) { 0371 graphicsItem()->setZValue(value); 0372 0373 m_title->setZValue(value + 1); 0374 0375 for (auto& markerpoint : markerpoints) 0376 markerpoint.customPoint->setZValue(value + 1); 0377 } 0378 0379 /*! 0380 * Returns the amount of markerpoints. Used in the InfoElementDock to fill listWidget. 0381 */ 0382 int InfoElement::markerPointsCount() { 0383 return markerpoints.length(); 0384 } 0385 0386 TextLabel::GluePoint InfoElement::gluePoint(int index) { 0387 return m_title->gluePointAt(index); 0388 } 0389 0390 int InfoElement::gluePointsCount() { 0391 return m_title->gluePointCount(); 0392 } 0393 0394 /*! 0395 * Returns the Markerpoint at index \p index. Used in the InfoElementDock to fill listWidget 0396 * @param index 0397 */ 0398 InfoElement::MarkerPoints_T InfoElement::markerPointAt(int index) { 0399 return markerpoints.at(index); 0400 } 0401 0402 /*! 0403 * create Text which will be shown in the TextLabel 0404 * Called when: 0405 * - The position of the infoelement was changed 0406 * - a curve was removed 0407 * - a curve was added 0408 * @return Text 0409 */ 0410 TextLabel::TextWrapper InfoElement::createTextLabelText() { 0411 // TODO: save positions of the variables in extra variables to replace faster, because replace takes long time 0412 TextLabel::TextWrapper wrapper = m_title->text(); 0413 if (markerPointsCount() < 1) { 0414 DEBUG(STDSTRING(wrapper.text)) 0415 DEBUG(STDSTRING(wrapper.textPlaceholder)) 0416 wrapper.text = wrapper.textPlaceholder; 0417 return wrapper; 0418 } 0419 0420 if (!(markerpoints.at(0).curve && markerpoints.at(0).curve->xColumn())) 0421 return wrapper; // no data is set in the curve yet, nothing to do 0422 0423 Q_D(const InfoElement); 0424 0425 QString text = wrapper.textPlaceholder; 0426 0427 // replace the placeholder for the x-value 0428 QString xValueStr; 0429 auto columnMode = markerpoints.at(0).curve->xColumn()->columnMode(); 0430 if (columnMode == AbstractColumn::ColumnMode::Double || columnMode == AbstractColumn::ColumnMode::Integer 0431 || columnMode == AbstractColumn::ColumnMode::BigInt) 0432 xValueStr = QString::number(d->positionLogical); 0433 else if (columnMode == AbstractColumn::ColumnMode::Day || columnMode == AbstractColumn::ColumnMode::Month 0434 || columnMode == AbstractColumn::ColumnMode::DateTime) { 0435 const auto& dateTime = QDateTime::fromMSecsSinceEpoch(d->positionLogical, Qt::UTC); 0436 xValueStr = dateTime.toString(m_plot->rangeDateTimeFormat(Dimension::X)); 0437 } 0438 0439 if (wrapper.mode == TextLabel::Mode::Text) 0440 text.replace(QStringLiteral("&(x)"), xValueStr); 0441 else 0442 text.replace(QStringLiteral("&(x)"), xValueStr); 0443 0444 // replace the placeholders for curve's y-values 0445 for (const auto& markerpoint : qAsConst(markerpoints)) { 0446 QString replace; 0447 if (wrapper.mode == TextLabel::Mode::Text) 0448 replace = QStringLiteral("&("); 0449 else 0450 replace = QStringLiteral("&("); 0451 0452 replace += markerpoint.curve->name() + QLatin1Char(')'); 0453 text.replace(replace, QString::number(markerpoint.customPoint->positionLogical().y())); 0454 } 0455 0456 wrapper.text = text; 0457 return wrapper; 0458 } 0459 0460 TextLabel* InfoElement::title() { 0461 return m_title; 0462 } 0463 0464 bool InfoElement::isTextLabel() const { 0465 return m_title != nullptr; 0466 } 0467 0468 /*! 0469 * Will be called, when the label changes his position 0470 * @param position 0471 */ 0472 void InfoElement::labelPositionChanged(TextLabel::PositionWrapper /*position*/) { 0473 if (m_suppressChildPositionChanged) 0474 return; 0475 0476 Q_D(InfoElement); 0477 d->retransform(); 0478 } 0479 0480 void InfoElement::labelVisibleChanged(bool /*visible*/) { 0481 Q_D(InfoElement); 0482 d->update(); 0483 } 0484 0485 void InfoElement::labelTextWrapperChanged(TextLabel::TextWrapper) { 0486 if (m_setTextLabelText) 0487 return; 0488 0489 m_setTextLabelText = true; 0490 m_title->setUndoAware(false); 0491 m_title->setText(createTextLabelText()); 0492 m_title->setUndoAware(true); 0493 m_setTextLabelText = false; 0494 0495 Q_D(InfoElement); 0496 d->retransform(); 0497 } 0498 0499 /*! 0500 * \brief InfoElement::moveElementBegin 0501 * Called, when a child is moved in front or behind another element. 0502 * Needed, because the child calls child removed, when moving and then 0503 * everything will be deleted 0504 */ 0505 void InfoElement::moveElementBegin() { 0506 m_suppressChildRemoved = true; 0507 } 0508 0509 /*! 0510 * \brief InfoElement::moveElementEnd 0511 * Called, when a child is moved in front or behind another element. 0512 * Needed, because the child calls child removed, when moving and then 0513 * everything will be deleted 0514 */ 0515 void InfoElement::moveElementEnd() { 0516 m_suppressChildRemoved = false; 0517 } 0518 0519 void InfoElement::curveCoordinateSystemIndexChanged(int /*index*/) { 0520 auto* curve = static_cast<XYCurve*>(QObject::sender()); 0521 auto cSystemIndex = curve->coordinateSystemIndex(); 0522 0523 for (auto& custompoint : markerpoints) { 0524 if (custompoint.curve == curve) { 0525 custompoint.customPoint->setCoordinateSystemIndex(cSystemIndex); 0526 break; 0527 } 0528 } 0529 0530 retransform(); 0531 } 0532 0533 void InfoElement::curveVisibilityChanged() { 0534 XYCurve* curve = static_cast<XYCurve*>(QObject::sender()); 0535 bool visible = curve->isVisible(); 0536 0537 bool oneMarkerpointVisible = false; 0538 for (auto& custompoint : markerpoints) { 0539 if (custompoint.curve == curve) 0540 custompoint.customPoint->setVisible(visible); 0541 0542 if (custompoint.customPoint->isVisible()) 0543 oneMarkerpointVisible = true; 0544 } 0545 0546 // if curve was set to hidden, set InfoElement to first visible curve 0547 if (!visible) { 0548 for (auto& custompoint : markerpoints) { 0549 if (custompoint.curve->isVisible()) { 0550 setConnectionLineCurveName(custompoint.curve->name()); 0551 break; 0552 } 0553 } 0554 } 0555 0556 // if no markerpoints are visible, hide the title label 0557 m_title->setUndoAware(false); 0558 if ((!visible && markerpoints.count() == 0) || !oneMarkerpointVisible) 0559 m_title->setVisible(false); 0560 else 0561 m_title->setVisible(true); 0562 m_title->setUndoAware(true); 0563 } 0564 0565 void InfoElement::curveDataChanged() { 0566 Q_D(InfoElement); 0567 setMarkerpointPosition(d->positionLogical); 0568 m_setTextLabelText = true; 0569 m_title->setUndoAware(false); 0570 m_title->setText(createTextLabelText()); 0571 m_title->setUndoAware(true); 0572 m_setTextLabelText = false; 0573 retransform(); 0574 } 0575 0576 void InfoElement::labelBorderShapeChanged() { 0577 Q_D(InfoElement); 0578 Q_EMIT labelBorderShapeChangedSignal(); 0579 d->retransform(); 0580 } 0581 0582 /*! 0583 * Delete child and remove from markerpoint list if it is a markerpoint. If it is a textlabel delete complete InfoElement 0584 */ 0585 void InfoElement::childRemoved(const AbstractAspect* parent, const AbstractAspect* /*before*/, const AbstractAspect* child) { 0586 Q_D(InfoElement); 0587 0588 // when childs are reordered, don't remove them 0589 // problem: when the order was changed the elements are deleted for a short time and recreated. This function will called then 0590 if (m_suppressChildRemoved) 0591 return; 0592 0593 if (parent != this) // why do I need this? 0594 return; 0595 0596 // point removed 0597 const auto* point = dynamic_cast<const CustomPoint*>(child); 0598 if (point) { 0599 for (int i = 0; i < markerpoints.length(); i++) { 0600 if (point == markerpoints[i].customPoint) 0601 markerpoints.removeAt(i); 0602 // no point->remove() needed, because it was already deleted 0603 } 0604 // recreate text, because when marker was deleted, 0605 // the placeholder should not be replaced anymore by a value 0606 m_title->setUndoAware(false); 0607 m_title->setText(createTextLabelText()); 0608 m_title->setUndoAware(true); 0609 } 0610 0611 // textlabel was deleted 0612 const auto* textlabel = dynamic_cast<const TextLabel*>(child); 0613 if (textlabel) { 0614 Q_ASSERT(m_title == textlabel); 0615 m_title = nullptr; 0616 for (int i = 0; i < markerpoints.length(); i++) { // why it's not working without? 0617 m_suppressChildRemoved = true; 0618 markerpoints[i].customPoint->remove(); 0619 markerpoints.removeAt(i); 0620 m_suppressChildRemoved = false; 0621 } 0622 remove(); // delete marker if textlabel was deleted, because there is no use case of this 0623 } 0624 0625 d->retransform(); 0626 } 0627 0628 void InfoElement::childAdded(const AbstractAspect* child) { 0629 const auto* point = dynamic_cast<const CustomPoint*>(child); 0630 if (point) { 0631 auto* p = const_cast<CustomPoint*>(point); 0632 // otherwise Custom point must be patched to handle discrete curve points. 0633 // This makes it much easier 0634 p->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); 0635 p->setParentGraphicsItem(graphicsItem()); 0636 // Must be done after setCoordinateBindingEnabled, otherwise positionChanged will be called and 0637 // then the InfoElement position will be set incorrectly 0638 connect(point, &CustomPoint::positionChanged, this, &InfoElement::pointPositionChanged); 0639 connect(point, &CustomPoint::moveBegin, this, &InfoElement::moveElementBegin); 0640 connect(point, &CustomPoint::moveEnd, this, &InfoElement::moveElementEnd); 0641 return; 0642 } 0643 0644 const auto* m_titleChild = dynamic_cast<const TextLabel*>(child); 0645 if (m_titleChild) { 0646 connect(m_title, &TextLabel::positionChanged, this, &InfoElement::labelPositionChanged); 0647 connect(m_title, &TextLabel::visibleChanged, this, &InfoElement::labelVisibleChanged); 0648 connect(m_title, &TextLabel::textWrapperChanged, this, &InfoElement::labelTextWrapperChanged); 0649 connect(m_title, &TextLabel::borderShapeChanged, this, &InfoElement::labelBorderShapeChanged); 0650 connect(m_title, &TextLabel::moveBegin, this, &InfoElement::moveElementBegin); 0651 connect(m_title, &TextLabel::moveEnd, this, &InfoElement::moveElementEnd); 0652 connect(m_title, &TextLabel::rotationAngleChanged, this, &InfoElement::retransform); 0653 0654 auto* l = const_cast<TextLabel*>(m_titleChild); 0655 l->setParentGraphicsItem(graphicsItem()); 0656 } 0657 } 0658 0659 /*! 0660 * \brief InfoElement::currentValue 0661 * Calculates the new x position from 0662 * \param new_x 0663 * \return 0664 */ 0665 int InfoElement::currentIndex(double x, double* found_x) { 0666 for (auto& markerpoint : markerpoints) { 0667 if (markerpoint.curve->name() == connectionLineCurveName()) { 0668 int index = markerpoint.curve->xColumn()->indexForValue(x); 0669 0670 if (found_x && index >= 0) { 0671 auto mode = markerpoint.curve->xColumn()->columnMode(); 0672 switch (mode) { 0673 case AbstractColumn::ColumnMode::Double: 0674 case AbstractColumn::ColumnMode::Integer: 0675 case AbstractColumn::ColumnMode::BigInt: 0676 *found_x = markerpoint.curve->xColumn()->valueAt(index); 0677 break; 0678 case AbstractColumn::ColumnMode::Text: 0679 break; 0680 case AbstractColumn::ColumnMode::DateTime: 0681 case AbstractColumn::ColumnMode::Month: 0682 case AbstractColumn::ColumnMode::Day: 0683 *found_x = markerpoint.curve->xColumn()->dateTimeAt(index).toMSecsSinceEpoch(); 0684 break; 0685 } 0686 0687 return index; 0688 } 0689 } 0690 } 0691 0692 return -1; 0693 } 0694 0695 double InfoElement::setMarkerpointPosition(double x) { 0696 // TODO: can be optimized when it will be checked if all markerpoints have the same xColumn, then the index m_index is the same 0697 Q_D(InfoElement); 0698 double x_new; 0699 double x_new_first = 0; 0700 for (int i = 0; i < markerpoints.length(); i++) { 0701 bool valueFound; 0702 double y = markerpoints[i].curve->y(x, x_new, valueFound); 0703 d->positionLogical = x_new; 0704 if (i == 0) 0705 x_new_first = x_new; 0706 0707 if (valueFound) { 0708 m_suppressChildPositionChanged = true; 0709 auto* point = markerpoints[i].customPoint; 0710 point->graphicsItem()->setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); 0711 point->setUndoAware(false); 0712 point->setPositionLogical(QPointF(x_new, y)); 0713 point->setUndoAware(false); 0714 point->graphicsItem()->setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); 0715 m_suppressChildPositionChanged = false; 0716 } 0717 } 0718 return x_new_first; 0719 } 0720 0721 /*! 0722 * Will be called, when the customPoint changes his position 0723 * @param pos 0724 */ 0725 void InfoElement::pointPositionChanged(const WorksheetElement::PositionWrapper&) { 0726 if (m_suppressChildPositionChanged) 0727 return; 0728 0729 const auto* point = dynamic_cast<CustomPoint*>(QObject::sender()); 0730 if (!point) 0731 return; 0732 0733 setPositionLogical(point->positionLogical().x()); 0734 } 0735 0736 void InfoElement::setParentGraphicsItem(QGraphicsItem* item) { 0737 Q_D(InfoElement); 0738 d->setParentItem(item); 0739 d->updatePosition(); 0740 } 0741 0742 void InfoElement::retransform() { 0743 Q_D(InfoElement); 0744 d->retransform(); 0745 } 0746 0747 void InfoElement::handleResize(double /*horizontalRatio*/, double /*verticalRatio*/, bool /*pageResize*/) { 0748 } 0749 0750 // ############################################################################## 0751 // ###### Getter and setter methods ############################################ 0752 // ############################################################################## 0753 0754 /* ============================ getter methods ================= */ 0755 BASIC_SHARED_D_READER_IMPL(InfoElement, double, positionLogical, positionLogical) 0756 BASIC_SHARED_D_READER_IMPL(InfoElement, int, gluePointIndex, gluePointIndex) 0757 BASIC_SHARED_D_READER_IMPL(InfoElement, QString, connectionLineCurveName, connectionLineCurveName) 0758 0759 Line* InfoElement::verticalLine() const { 0760 Q_D(const InfoElement); 0761 return d->verticalLine; 0762 } 0763 0764 Line* InfoElement::connectionLine() const { 0765 Q_D(const InfoElement); 0766 return d->connectionLine; 0767 } 0768 0769 /* ============================ setter methods ================= */ 0770 STD_SETTER_CMD_IMPL(InfoElement, SetPositionLogical, double, positionLogical) 0771 void InfoElement::setPositionLogical(double pos) { 0772 Q_D(InfoElement); 0773 double value = 0.; 0774 int index = currentIndex(pos, &value); 0775 if (index < 0) 0776 return; 0777 0778 if (value != d->positionLogical) { 0779 d->m_index = index; 0780 exec(new InfoElementSetPositionLogicalCmd(d, pos, ki18n("%1: set position"))); 0781 setMarkerpointPosition(value); 0782 m_setTextLabelText = true; 0783 m_title->setUndoAware(false); 0784 m_title->setText(createTextLabelText()); 0785 m_title->setUndoAware(true); 0786 m_setTextLabelText = false; 0787 retransform(); 0788 positionLogicalChanged(d->positionLogical); 0789 } 0790 } 0791 0792 STD_SETTER_CMD_IMPL_F_S(InfoElement, SetGluePointIndex, int, gluePointIndex, retransform) 0793 void InfoElement::setGluePointIndex(int value) { 0794 Q_D(InfoElement); 0795 if (value != d->gluePointIndex) 0796 exec(new InfoElementSetGluePointIndexCmd(d, value, ki18n("%1: set gluepoint index"))); 0797 } 0798 0799 STD_SETTER_CMD_IMPL_F_S(InfoElement, SetConnectionLineCurveName, QString, connectionLineCurveName, retransform) 0800 void InfoElement::setConnectionLineCurveName(const QString& name) { 0801 Q_D(InfoElement); 0802 if (name.compare(d->connectionLineCurveName) != 0) 0803 exec(new InfoElementSetConnectionLineCurveNameCmd(d, name, ki18n("%1: set connectionline curve name"))); 0804 } 0805 0806 STD_SWAP_METHOD_SETTER_CMD_IMPL(InfoElement, SetVisible, bool, changeVisibility) 0807 void InfoElement::setVisible(bool on) { 0808 Q_D(InfoElement); 0809 if (on != isVisible()) 0810 exec(new InfoElementSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); 0811 } 0812 0813 // ############################################################################## 0814 // ###### SLOTs for changes triggered via QActions in the context menu ######## 0815 // ############################################################################## 0816 0817 // ############################################################################## 0818 // ####################### Private implementation ############################### 0819 // ############################################################################## 0820 0821 InfoElementPrivate::InfoElementPrivate(InfoElement* owner) 0822 : WorksheetElementPrivate(owner) 0823 , q(owner) { 0824 init(); 0825 } 0826 0827 InfoElementPrivate::InfoElementPrivate(InfoElement* owner, const XYCurve*) 0828 : WorksheetElementPrivate(owner) 0829 , q(owner) { 0830 init(); 0831 } 0832 0833 void InfoElementPrivate::init() { 0834 setFlag(QGraphicsItem::ItemIsMovable, false); 0835 setFlag(QGraphicsItem::ItemClipsChildrenToShape, false); 0836 setFlag(QGraphicsItem::ItemIsSelectable, true); 0837 setFlag(QGraphicsItem::ItemIsFocusable, true); 0838 0839 setFlag(QGraphicsItem::ItemSendsGeometryChanges, false); 0840 setPos(QPointF(0, 0)); 0841 setFlag(QGraphicsItem::ItemSendsGeometryChanges, true); 0842 } 0843 0844 /*! 0845 calculates the position and the bounding box of the InfoElement. Called on geometry changes. 0846 Or when the label or the point where moved 0847 */ 0848 void InfoElementPrivate::retransform() { 0849 DEBUG(Q_FUNC_INFO) 0850 if (!q->m_title || q->markerpoints.isEmpty() || q->isLoading() || !parentItem()) 0851 return; 0852 0853 q->m_suppressChildPositionChanged = true; 0854 0855 // new bounding rectangle 0856 const QRectF& rect = parentItem()->mapRectFromScene(q->plot()->rect()); 0857 const QRectF& newBoundingRect = mapFromParent(rect).boundingRect(); 0858 QDEBUG(Q_FUNC_INFO << ", rect = " << rect) 0859 QDEBUG(Q_FUNC_INFO << ", bounding rect = " << newBoundingRect) 0860 0861 // TODO: why do I need to retransform the label and the custompoints? 0862 q->m_title->retransform(); 0863 for (auto& markerpoint : q->markerpoints) 0864 markerpoint.customPoint->retransform(); 0865 0866 // determine the position to connect the line to 0867 QPointF pointPos; 0868 for (int i = 0; i < q->markerPointsCount(); ++i) { 0869 const auto* curve = q->markerpoints.at(i).curve; 0870 if (curve && curve->name() == connectionLineCurveName) { 0871 const auto& point = q->markerpoints.at(i).customPoint; 0872 const auto* cSystem = q->plot()->coordinateSystem(point->coordinateSystemIndex()); 0873 pointPos = cSystem->mapLogicalToScene(point->positionLogical(), insidePlot, AbstractCoordinateSystem::MappingFlag::SuppressPageClippingVisible); 0874 break; 0875 } 0876 } 0877 0878 if (!insidePlot) 0879 return; 0880 0881 // use limit function like in the cursor! So the line will be drawn only till the border of the cartesian Plot 0882 QPointF m_titlePos; 0883 if (gluePointIndex < 0) 0884 m_titlePos = q->m_title->findNearestGluePoint(pointPos); 0885 else 0886 m_titlePos = q->m_title->gluePointAt(gluePointIndex).point; 0887 0888 // connection line 0889 const QPointF m_titlePosItemCoords = mapFromParent(m_titlePos); // calculate item coords from scene coords 0890 const QPointF pointPosItemCoords = mapFromParent(mapPlotAreaToParent(pointPos)); // calculate item coords from scene coords 0891 if (newBoundingRect.contains(m_titlePosItemCoords) && newBoundingRect.contains(pointPosItemCoords)) 0892 m_connectionLine = QLineF(m_titlePosItemCoords.x(), m_titlePosItemCoords.y(), pointPosItemCoords.x(), pointPosItemCoords.y()); 0893 else 0894 m_connectionLine = QLineF(); 0895 QDEBUG(Q_FUNC_INFO << ", connection line = " << m_connectionLine) 0896 0897 // vertical line 0898 const QRectF& dataRect = mapFromParent(q->m_plot->dataRect()).boundingRect(); 0899 xposLine = QLineF(pointPosItemCoords.x(), dataRect.bottom(), pointPosItemCoords.x(), dataRect.top()); 0900 QDEBUG(Q_FUNC_INFO << ", vertical line " << xposLine) 0901 0902 recalcShapeAndBoundingRect(newBoundingRect); 0903 0904 q->m_suppressChildPositionChanged = false; 0905 } 0906 0907 void InfoElementPrivate::updatePosition() { 0908 // TODO? 0909 } 0910 0911 /*! 0912 * Repainting to update xposLine 0913 */ 0914 void InfoElementPrivate::updateVerticalLine() { 0915 recalcShapeAndBoundingRect(); 0916 } 0917 0918 /*! 0919 * Repainting to updateConnectionLine 0920 */ 0921 void InfoElementPrivate::updateConnectionLine() { 0922 recalcShapeAndBoundingRect(); 0923 } 0924 0925 bool InfoElementPrivate::changeVisibility(bool on) { 0926 bool oldValue = isVisible(); 0927 setVisible(on); 0928 for (auto& markerpoint : q->markerpoints) 0929 markerpoint.customPoint->setVisible(on); 0930 if (q->m_title) { 0931 q->m_title->setUndoAware(false); 0932 q->m_title->setVisible(on); 0933 q->m_title->setUndoAware(true); 0934 } 0935 update(boundingRect()); 0936 return oldValue; 0937 } 0938 0939 void InfoElementPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*) { 0940 if (!insidePlot) 0941 return; 0942 0943 if (q->markerpoints.isEmpty()) 0944 return; 0945 0946 // do not draw connection line when the label is not visible 0947 if (connectionLine->style() != Qt::NoPen && q->m_title->isVisible()) { 0948 painter->setOpacity(connectionLine->opacity()); 0949 painter->setPen(connectionLine->pen()); 0950 painter->drawLine(m_connectionLine); 0951 } 0952 0953 // draw vertical line, which connects all points together 0954 if (verticalLine->style() != Qt::NoPen) { 0955 painter->setOpacity(verticalLine->opacity()); 0956 painter->setPen(verticalLine->pen()); 0957 painter->drawLine(xposLine); 0958 } 0959 } 0960 0961 void InfoElementPrivate::mousePressEvent(QGraphicsSceneMouseEvent* event) { 0962 if (event->button() == Qt::MouseButton::LeftButton) { 0963 if (verticalLine->style() != Qt::NoPen) { 0964 const double width = verticalLine->pen().widthF(); 0965 if (abs(xposLine.x1() - event->pos().x()) < ((width < 3) ? 3 : width)) { 0966 if (!isSelected()) 0967 setSelected(true); 0968 m_suppressKeyPressEvents = false; 0969 oldMousePos = mapToParent(event->pos()); 0970 event->accept(); 0971 setFocus(); 0972 return; 0973 } 0974 } /* else { 0975 for (int i=0; i< q->markerPointsCount(); i++) { 0976 InfoElement::MarkerPoints_T markerpoint = q->markerPointAt(i); 0977 //if (markerpoint.customPoint->symbolSize()) 0978 } 0979 }*/ 0980 0981 // https://stackoverflow.com/questions/11604680/point-laying-near-line 0982 double dx12 = m_connectionLine.x2() - m_connectionLine.x1(); 0983 double dy12 = m_connectionLine.y2() - m_connectionLine.y1(); 0984 double vecLenght = sqrt(pow(dx12, 2) + pow(dy12, 2)); 0985 QPointF unitvec(dx12 / vecLenght, dy12 / vecLenght); 0986 0987 double dx1m = event->pos().x() - m_connectionLine.x1(); 0988 double dy1m = event->pos().y() - m_connectionLine.y1(); 0989 0990 double dist_segm = abs(dx1m * unitvec.y() - dy1m * unitvec.x()); 0991 double scalar_product = dx1m * unitvec.x() + dy1m * unitvec.y(); 0992 // DEBUG("DIST_SEGMENT " << dist_segm << "SCALAR_PRODUCT: " << scalar_product << "VEC_LENGTH: " << vecLenght); 0993 0994 if (scalar_product > 0) { 0995 const double width = connectionLine->width(); 0996 if (scalar_product < vecLenght && dist_segm < ((width < 3) ? 3 : width)) { 0997 event->accept(); 0998 if (!isSelected()) 0999 setSelected(true); 1000 oldMousePos = mapToParent(event->pos()); 1001 m_suppressKeyPressEvents = false; 1002 event->accept(); 1003 setFocus(); 1004 return; 1005 } 1006 } 1007 1008 m_suppressKeyPressEvents = true; 1009 event->ignore(); 1010 if (isSelected()) 1011 setSelected(false); 1012 } 1013 QGraphicsItem::mousePressEvent(event); 1014 } 1015 1016 void InfoElementPrivate::mouseMoveEvent(QGraphicsSceneMouseEvent* event) { 1017 QPointF eventPos = mapToParent(event->pos()); 1018 // DEBUG("EventPos: " << eventPos.x() << " Y: " << eventPos.y()); 1019 QPointF delta = eventPos - oldMousePos; 1020 if (delta == QPointF(0, 0)) 1021 return; 1022 1023 if (!q->cSystem->isValid()) 1024 return; 1025 QPointF eventLogicPos = q->cSystem->mapSceneToLogical(eventPos, AbstractCoordinateSystem::MappingFlag::SuppressPageClipping); 1026 QPointF delta_logic = eventLogicPos - q->cSystem->mapSceneToLogical(oldMousePos); 1027 1028 if (!q->m_title) 1029 return; 1030 if (q->markerpoints.isEmpty()) 1031 return; 1032 1033 // TODO: find better method to do this. It's inefficient. 1034 // Finding which curve should be used to find the new values 1035 double x = positionLogical; 1036 int activeIndex = 0; 1037 for (int i = 1; i < q->markerPointsCount(); i++) { 1038 if (q->markerpoints[i].curve->name().compare(connectionLineCurveName) == 0) { 1039 // not possible to use index, because when the number of elements in the columns of the curves are not the same there is a problem 1040 x = q->markerpoints[i].customPoint->positionLogical().x(); // q->markerpoints[i].curve->xColumn()->valueAt(m_index) 1041 activeIndex = i; 1042 break; 1043 } 1044 } 1045 x += delta_logic.x(); 1046 auto xColumn = q->markerpoints[activeIndex].curve->xColumn(); 1047 int xindex = xColumn->indexForValue(x); 1048 double x_new = NAN; 1049 if (xColumn->isNumeric()) 1050 x_new = xColumn->valueAt(xindex); 1051 else 1052 x_new = xColumn->dateTimeAt(xindex).toMSecsSinceEpoch(); 1053 1054 auto pointPos = q->markerpoints[activeIndex].customPoint->positionLogical().x(); 1055 if (abs(x_new - pointPos) > 0) { 1056 if ((xColumn->rowCount() - 1 == xindex && pointPos > x_new) || (xindex == 0 && pointPos < x_new)) { 1057 q->setPositionLogical(x_new); 1058 } else { 1059 oldMousePos = eventPos; 1060 q->setPositionLogical(x); 1061 } 1062 } 1063 } 1064 1065 void InfoElementPrivate::keyPressEvent(QKeyEvent* event) { 1066 if (m_suppressKeyPressEvents) { 1067 event->ignore(); 1068 return QGraphicsItem::keyPressEvent(event); 1069 } 1070 1071 TextLabel::TextWrapper text; 1072 if (event->key() == Qt::Key_Right || event->key() == Qt::Key_Left) { 1073 int index; 1074 if (event->key() == Qt::Key_Right) 1075 index = 1; 1076 else 1077 index = -1; 1078 1079 // problem: when curves have different number of samples, the points are anymore aligned 1080 // with the vertical line 1081 // find markerpoint to which the values matches (curvename is stored in connectionLineCurveName) 1082 for (int i = 0; i < q->markerPointsCount(); i++) { 1083 const auto* curve = q->markerpoints[i].curve; 1084 if (curve->name().compare(connectionLineCurveName) == 0) { 1085 auto rowCount = curve->xColumn()->rowCount(); 1086 m_index += index; 1087 if (m_index > rowCount - 1) 1088 m_index = rowCount - 1; 1089 if (m_index < 0) 1090 m_index = 0; 1091 if (curve->xColumn()->isNumeric()) 1092 q->setPositionLogical(curve->xColumn()->valueAt(m_index)); 1093 else 1094 q->setPositionLogical(curve->xColumn()->dateTimeAt(m_index).toMSecsSinceEpoch()); 1095 break; 1096 } 1097 } 1098 } 1099 } 1100 1101 bool InfoElementPrivate::activate(QPointF mouseScenePos, double /*maxDist*/) { 1102 if (!isVisible()) 1103 return false; 1104 1105 return m_shape.contains(mouseScenePos); 1106 } 1107 1108 void InfoElementPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { 1109 if (activate(event->pos())) { 1110 q->createContextMenu()->exec(event->screenPos()); 1111 return; 1112 } 1113 QGraphicsItem::contextMenuEvent(event); 1114 } 1115 1116 void InfoElementPrivate::recalcShape() { 1117 m_shape = QPainterPath(); 1118 1119 if (verticalLine->style() != Qt::PenStyle::NoPen) { 1120 QPainterPath path; 1121 path.moveTo(xposLine.p1()); 1122 path.lineTo(xposLine.p2()); 1123 m_shape.addPath(WorksheetElement::shapeFromPath(path, verticalLine->pen())); 1124 } 1125 1126 if (connectionLine->style() != Qt::PenStyle::NoPen) { 1127 QPainterPath path; 1128 path.moveTo(m_connectionLine.p1()); 1129 path.lineTo(m_connectionLine.p2()); 1130 m_shape.addPath(WorksheetElement::shapeFromPath(path, connectionLine->pen())); 1131 } 1132 } 1133 1134 void InfoElementPrivate::recalcShapeAndBoundingRect(const QRectF& rect) { 1135 prepareGeometryChange(); 1136 m_boundingRectangle = rect; 1137 recalcShape(); 1138 update(m_boundingRectangle); 1139 } 1140 1141 void InfoElementPrivate::recalcShapeAndBoundingRect() { 1142 prepareGeometryChange(); 1143 recalcShape(); 1144 update(m_boundingRectangle); 1145 } 1146 1147 // ############################################################################## 1148 // ################## Serialization/Deserialization ########################### 1149 // ############################################################################## 1150 void InfoElement::save(QXmlStreamWriter* writer) const { 1151 Q_D(const InfoElement); 1152 1153 writer->writeStartElement(QStringLiteral("infoElement")); 1154 writeBasicAttributes(writer); 1155 writeCommentElement(writer); 1156 1157 // general 1158 writer->writeStartElement(QStringLiteral("general")); 1159 writer->writeAttribute(QStringLiteral("position"), QString::number(d->positionLogical)); 1160 writer->writeAttribute(QStringLiteral("curve"), d->connectionLineCurveName); 1161 writer->writeAttribute(QStringLiteral("gluePointIndex"), QString::number(d->gluePointIndex)); 1162 writer->writeAttribute(QStringLiteral("markerIndex"), QString::number(d->m_index)); 1163 writer->writeAttribute(QStringLiteral("plotRangeIndex"), QString::number(m_cSystemIndex)); 1164 writer->writeAttribute(QStringLiteral("visible"), QString::number(d->isVisible())); 1165 writer->writeEndElement(); 1166 1167 // lines 1168 d->verticalLine->save(writer); 1169 d->connectionLine->save(writer); 1170 1171 // text label 1172 m_title->save(writer); 1173 1174 // custom points 1175 if (!markerpoints.isEmpty()) { 1176 writer->writeStartElement(QStringLiteral("points")); 1177 for (const auto& custompoint : markerpoints) { 1178 writer->writeStartElement(QStringLiteral("point")); 1179 writer->writeAttribute(QLatin1String("curvepath"), custompoint.curve->path()); 1180 custompoint.customPoint->save(writer); 1181 writer->writeEndElement(); // close "point" 1182 } 1183 writer->writeEndElement(); // close "points" 1184 } 1185 1186 writer->writeEndElement(); // close "infoElement" 1187 } 1188 1189 bool InfoElement::load(XmlStreamReader* reader, bool preview) { 1190 if (!readBasicAttributes(reader)) 1191 return false; 1192 1193 Q_D(InfoElement); 1194 1195 QXmlStreamAttributes attribs; 1196 QString str; 1197 QString curvePath; 1198 1199 while (!reader->atEnd()) { 1200 reader->readNext(); 1201 if (reader->isEndElement() && reader->name() == QLatin1String("infoElement")) 1202 break; 1203 1204 if (!reader->isStartElement()) 1205 continue; 1206 1207 if (!preview && reader->name() == QLatin1String("comment")) { 1208 if (!readCommentElement(reader)) 1209 return false; 1210 } else if (reader->name() == QLatin1String("general")) { 1211 attribs = reader->attributes(); 1212 1213 READ_INT_VALUE_DIRECT("plotRangeIndex", m_cSystemIndex, int); 1214 1215 str = attribs.value(QStringLiteral("visible")).toString(); 1216 if (str.isEmpty()) 1217 reader->raiseMissingAttributeWarning(QStringLiteral("x")); 1218 else 1219 setVisible(str.toInt()); 1220 1221 READ_DOUBLE_VALUE("position", positionLogical); 1222 READ_INT_VALUE("gluePointIndex", gluePointIndex, int); 1223 READ_INT_VALUE("markerIndex", m_index, int); 1224 READ_STRING_VALUE("curve", connectionLineCurveName); 1225 } else if (reader->name() == QLatin1String("verticalLine")) { 1226 d->verticalLine->load(reader, preview); 1227 } else if (reader->name() == QLatin1String("connectionLine")) { 1228 d->connectionLine->load(reader, preview); 1229 } else if (reader->name() == QLatin1String("textLabel")) { 1230 if (!m_title) { 1231 m_title = new TextLabel(i18n("Label"), m_plot); 1232 m_title->setIsLoading(true); 1233 this->addChild(m_title); 1234 } 1235 if (!m_title->load(reader, preview)) 1236 return false; 1237 } else if (reader->name() == QLatin1String("customPoint")) { 1238 if (curvePath.isEmpty()) // safety check in case the xml is broken 1239 continue; 1240 1241 auto* point = new CustomPoint(m_plot, QString()); 1242 point->setIsLoading(true); 1243 if (!point->load(reader, preview)) { 1244 delete point; 1245 return false; 1246 } 1247 this->addChild(point); 1248 addCurvePath(curvePath, point); 1249 curvePath.clear(); 1250 } else if (reader->name() == QLatin1String("point")) { 1251 attribs = reader->attributes(); 1252 curvePath = attribs.value(QStringLiteral("curvepath")).toString(); 1253 } else { // unknown element 1254 reader->raiseUnknownElementWarning(); 1255 if (!reader->skipToEndElement()) 1256 return false; 1257 } 1258 } 1259 1260 return true; 1261 } 1262 1263 // ############################################################################## 1264 // ######################### Theme management ################################## 1265 // ############################################################################## 1266 void InfoElement::loadThemeConfig(const KConfig& config) { 1267 // use the color for the axis line from the theme also for info element's lines 1268 const KConfigGroup& group = config.group(QStringLiteral("Axis")); 1269 1270 const QColor& themeColor = group.readEntry(QStringLiteral("LineColor"), QColor(Qt::black)); 1271 Q_D(InfoElement); 1272 d->verticalLine->loadThemeConfig(group, themeColor); 1273 d->connectionLine->loadThemeConfig(group, themeColor); 1274 1275 // load the theme for all the children 1276 const auto& children = this->children<WorksheetElement>(ChildIndexFlag::IncludeHidden); 1277 for (auto* child : children) 1278 child->loadThemeConfig(config); 1279 }