File indexing completed on 2025-01-05 03:35:30

0001 /*
0002     File                 : InfoElement.cpp
0003     Project              : LabPlot
0004     Description          : Dock widget for InfoElemnt
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2020 Martin Marmsoler <martin.marmsoler@gmail.com>
0007     SPDX-FileCopyrightText: 2020-2022 Alexander Semke <alexander.semke@web.de>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "InfoElementDock.h"
0012 #include "backend/worksheet/InfoElement.h"
0013 #include "backend/worksheet/plots/cartesian/CartesianPlot.h"
0014 #include "backend/worksheet/plots/cartesian/XYCurve.h"
0015 #include "kdefrontend/GuiTools.h"
0016 #include "kdefrontend/widgets/LabelWidget.h"
0017 #include "kdefrontend/widgets/LineWidget.h"
0018 #include "ui_infoelementdock.h"
0019 
0020 InfoElementDock::InfoElementDock(QWidget* parent)
0021     : BaseDock(parent)
0022     , ui(new Ui::InfoElementDock) {
0023     ui->setupUi(this);
0024     setPlotRangeCombobox(ui->cbPlotRanges);
0025     setBaseWidgets(ui->leName, ui->teComment);
0026     setVisibilityWidgets(ui->chbVisible);
0027 
0028     //"Title"-tab
0029     auto* hboxLayout = new QHBoxLayout(ui->tabTitle);
0030     m_labelWidget = new LabelWidget(ui->tabTitle);
0031     hboxLayout->addWidget(m_labelWidget);
0032     hboxLayout->setContentsMargins(2, 2, 2, 2);
0033     hboxLayout->setSpacing(2);
0034 
0035     // "Lines"-tab
0036     auto* layout = static_cast<QGridLayout*>(ui->tabLines->layout());
0037     m_verticalLineWidget = new LineWidget(ui->tabLines);
0038     layout->addWidget(m_verticalLineWidget, 1, 0, 1, 3);
0039 
0040     m_connectionLineWidget = new LineWidget(ui->tabLines);
0041     layout->addWidget(m_connectionLineWidget, 4, 0, 1, 3);
0042 
0043     // set the current locale
0044     ui->sbPosition->setLocale(QLocale());
0045     m_labelWidget->updateLocale();
0046     m_verticalLineWidget->updateLocale();
0047     m_connectionLineWidget->updateLocale();
0048 
0049     //**********************************  Slots **********************************************
0050     // general
0051     connect(ui->sbPosition, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &InfoElementDock::positionChanged);
0052     connect(ui->dateTimeEditPosition, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &InfoElementDock::positionDateTimeChanged);
0053     connect(ui->cbConnectToCurve, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InfoElementDock::curveChanged);
0054     connect(ui->cbConnectToAnchor, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &InfoElementDock::gluePointChanged);
0055 }
0056 
0057 void InfoElementDock::setInfoElements(QList<InfoElement*> list) {
0058     CONDITIONAL_LOCK_RETURN;
0059 
0060     m_elements = list;
0061     m_element = list.first();
0062     setAspects(list);
0063 
0064     // check if all InfoElements have the same CartesianPlot as Parent
0065     m_sameParent = true;
0066     if (!m_elements.isEmpty()) {
0067         const auto* parent = m_elements.constFirst()->parentAspect();
0068         for (auto* element : m_elements) {
0069             if (element->parentAspect() != parent) {
0070                 m_sameParent = false;
0071                 break;
0072             }
0073         }
0074     }
0075 
0076     QList<TextLabel*> labels;
0077     QList<Line*> verticalLines;
0078     QList<Line*> connectionLines;
0079     for (auto* element : list) {
0080         labels << element->title();
0081         verticalLines << element->verticalLine();
0082         connectionLines << element->connectionLine();
0083     }
0084 
0085     m_labelWidget->setLabels(labels);
0086     m_verticalLineWidget->setLines(verticalLines);
0087     m_connectionLineWidget->setLines(connectionLines);
0088 
0089     ui->lwCurves->clear();
0090     ui->cbConnectToCurve->clear();
0091 
0092     ui->chbVisible->setChecked(m_element->isVisible());
0093 
0094     // disable if not all worksheetelements do not have the same parent (different CartesianPlots),
0095     // because then the available curves are different
0096     if (m_sameParent) {
0097         ui->lwCurves->setEnabled(true);
0098         ui->cbConnectToCurve->setEnabled(true);
0099 
0100         const auto& curves = m_element->plot()->children<XYCurve>();
0101         for (const auto* curve : curves) {
0102             auto* item = new QListWidgetItem();
0103             auto* checkBox = new QCheckBox(curve->name());
0104             for (int i = 0; i < m_element->markerPointsCount(); i++) {
0105                 if (curve->path() == m_element->markerPointAt(i).curvePath) {
0106                     checkBox->setChecked(true);
0107                     ui->cbConnectToCurve->addItem(curve->name());
0108                     break;
0109                 }
0110             }
0111             connect(checkBox, &QCheckBox::toggled, this, &InfoElementDock::curveSelectionChanged);
0112             ui->lwCurves->addItem(item);
0113             ui->lwCurves->setItemWidget(item, checkBox);
0114         }
0115     } else {
0116         ui->lwCurves->setEnabled(false);
0117         ui->cbConnectToCurve->setEnabled(false);
0118     }
0119 
0120     const QString& curveName = m_element->connectionLineCurveName();
0121     for (int i = 0; i < ui->cbConnectToCurve->count(); i++) {
0122         if (ui->cbConnectToCurve->itemData(i, Qt::DisplayRole).toString().compare(curveName) == 0) {
0123             ui->cbConnectToCurve->setCurrentIndex(i);
0124             break;
0125         }
0126     }
0127 
0128     // possible anchor points
0129     ui->cbConnectToAnchor->clear();
0130     ui->cbConnectToAnchor->addItem(i18n("Auto"));
0131     for (int i = 0; i < m_element->gluePointsCount(); i++)
0132         ui->cbConnectToAnchor->addItem(m_element->gluePoint(i).name);
0133     ui->cbConnectToAnchor->setCurrentIndex(m_element->gluePointIndex() + 1);
0134 
0135     if (m_element->plot()->xRangeFormatDefault() == RangeT::Format::Numeric) {
0136         ui->sbPosition->setValue(m_element->positionLogical());
0137         ui->lPosition->show();
0138         ui->sbPosition->show();
0139         ui->lPositionDateTime->hide();
0140         ui->dateTimeEditPosition->hide();
0141     } else {
0142         ui->dateTimeEditPosition->setDisplayFormat(m_element->plot()->rangeDateTimeFormat(Dimension::X));
0143         ui->dateTimeEditPosition->setMSecsSinceEpochUTC(m_element->positionLogical());
0144         ui->lPosition->hide();
0145         ui->sbPosition->hide();
0146         ui->lPositionDateTime->show();
0147         ui->dateTimeEditPosition->show();
0148     }
0149 
0150     updatePlotRangeList(); // needed when loading project
0151 
0152     // general
0153     connect(m_element, &InfoElement::positionLogicalChanged, this, &InfoElementDock::elementPositionChanged);
0154     connect(m_element, &InfoElement::gluePointIndexChanged, this, &InfoElementDock::elementGluePointIndexChanged);
0155     connect(m_element, &InfoElement::connectionLineCurveNameChanged, this, &InfoElementDock::elementConnectionLineCurveChanged);
0156     connect(m_element, &InfoElement::labelBorderShapeChangedSignal, this, &InfoElementDock::elementLabelBorderShapeChanged);
0157     connect(m_element, &InfoElement::curveRemoved, this, &InfoElementDock::elementCurveRemoved);
0158 }
0159 
0160 //*************************************************************
0161 //******* SLOTs for changes triggered in InfoElementDock ******
0162 //*************************************************************
0163 InfoElementDock::~InfoElementDock() {
0164     delete ui;
0165 }
0166 
0167 // general tab
0168 void InfoElementDock::positionChanged(double pos) {
0169     CONDITIONAL_RETURN_NO_LOCK;
0170 
0171     for (auto* element : m_elements)
0172         element->setPositionLogical(pos);
0173 }
0174 
0175 void InfoElementDock::positionDateTimeChanged(qint64 value) {
0176     CONDITIONAL_LOCK_RETURN;
0177 
0178     for (auto* element : m_elements)
0179         element->setPositionLogical(value);
0180 }
0181 
0182 void InfoElementDock::curveSelectionChanged(bool enabled) {
0183     CONDITIONAL_LOCK_RETURN;
0184     if (!m_sameParent)
0185         return;
0186 
0187     // determine the curve for which the selection was changed
0188     auto* checkBox = static_cast<QCheckBox*>(QObject::sender());
0189     QString curveName = checkBox->text().remove(QLatin1Char('&'));
0190     XYCurve* curve = nullptr;
0191     for (auto* c : m_elements[0]->plot()->children<XYCurve>()) {
0192         if (c->name() == curveName) {
0193             curve = c;
0194             break;
0195         }
0196     }
0197 
0198     // add/remove the changed curve
0199     if (enabled && curve) {
0200         for (auto* element : m_elements)
0201             element->addCurve(curve);
0202 
0203         // TODO: add the new curve at the proper index via insertItem();
0204         ui->cbConnectToCurve->addItem(curveName);
0205     } else {
0206         bool macroStarted = false;
0207 
0208         // update the "connect to" combobox
0209         for (int i = 0; ui->cbConnectToCurve->count(); ++i) {
0210             if (ui->cbConnectToCurve->itemText(i) == curveName) {
0211                 // removing an entry from combo box automatically triggers
0212                 // the selection of a new time which leads to a selection of
0213                 // a new "connect to"-curve in the InfoElement. To make only
0214                 // one single entry on the undo-stak we need to start a macro here:
0215                 macroStarted = true;
0216                 int size = m_elements.size();
0217                 if (size > 1)
0218                     m_element->beginMacro(i18n("%1 info elements: curve \"%2\" removed", size, curveName));
0219                 else
0220                     m_element->beginMacro(i18n("%1: curve \"%2\" removed", m_element->name(), curveName));
0221 
0222                 ui->cbConnectToCurve->removeItem(i);
0223                 break;
0224             }
0225         }
0226 
0227         for (auto* element : m_elements)
0228             element->removeCurve(curve);
0229 
0230         if (macroStarted)
0231             m_element->endMacro();
0232     }
0233 }
0234 
0235 void InfoElementDock::curveChanged() {
0236     CONDITIONAL_LOCK_RETURN;
0237 
0238     const QString& name = ui->cbConnectToCurve->currentText();
0239     for (auto* infoElement : m_elements)
0240         infoElement->setConnectionLineCurveName(name);
0241 }
0242 
0243 void InfoElementDock::gluePointChanged(int index) {
0244     CONDITIONAL_LOCK_RETURN;
0245 
0246     for (auto* infoElement : m_elements)
0247         infoElement->setGluePointIndex(index - 1); // index 0 means automatic, which is defined as -1
0248 }
0249 
0250 //***********************************************************
0251 //******* SLOTs for changes triggered in InfoElement ********
0252 //***********************************************************
0253 void InfoElementDock::elementGluePointIndexChanged(const int index) {
0254     CONDITIONAL_LOCK_RETURN;
0255     if (index < 0)
0256         ui->cbConnectToAnchor->setCurrentIndex(0);
0257     else
0258         ui->cbConnectToAnchor->setCurrentIndex(index + 1); // automatic label is in same combo box
0259 }
0260 
0261 void InfoElementDock::elementConnectionLineCurveChanged(const QString& name) {
0262     CONDITIONAL_LOCK_RETURN;
0263     for (int i = 0; i < ui->cbConnectToCurve->count(); i++) {
0264         if (ui->cbConnectToCurve->itemData(i).toString().compare(name) == 0) {
0265             ui->cbConnectToCurve->setCurrentIndex(i);
0266             break;
0267         }
0268     }
0269 }
0270 
0271 void InfoElementDock::elementLabelBorderShapeChanged() {
0272     CONDITIONAL_LOCK_RETURN;
0273     ui->cbConnectToAnchor->clear();
0274     ui->cbConnectToAnchor->addItem(i18n("Auto"));
0275     for (int i = 0; i < m_element->gluePointsCount(); i++)
0276         ui->cbConnectToAnchor->addItem(m_element->gluePoint(i).name);
0277 }
0278 
0279 void InfoElementDock::elementPositionChanged(double pos) {
0280     CONDITIONAL_LOCK_RETURN;
0281     ui->sbPosition->setValue(pos);
0282     ui->dateTimeEditPosition->setMSecsSinceEpochUTC(pos);
0283 }
0284 
0285 void InfoElementDock::elementCurveRemoved(const QString& name) {
0286     CONDITIONAL_LOCK_RETURN;
0287     for (int i = 0; i < ui->lwCurves->count(); ++i) {
0288         auto* item = ui->lwCurves->item(i);
0289         auto* checkBox = static_cast<QCheckBox*>(ui->lwCurves->itemWidget(item));
0290         if (checkBox->text() == name) {
0291             ui->lwCurves->takeItem(i);
0292             break;
0293         }
0294     }
0295 }