File indexing completed on 2025-10-19 03:37:35

0001 /*
0002     File                 : CustomPointDock.cpp
0003     Project              : LabPlot
0004     Description          : widget for CustomPoint properties
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2015-2023 Alexander Semke <alexander.semke@web.de>
0007     SPDX-FileCopyrightText: 2021 Stefan Gerlach <stefan.gerlach@uni.kn>
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "CustomPointDock.h"
0012 #include "backend/worksheet/plots/cartesian/CustomPoint.h"
0013 #include "kdefrontend/TemplateHandler.h"
0014 #include "kdefrontend/widgets/SymbolWidget.h"
0015 
0016 #include <KConfig>
0017 #include <KLocalizedString>
0018 
0019 CustomPointDock::CustomPointDock(QWidget* parent)
0020     : BaseDock(parent) {
0021     ui.setupUi(this);
0022     setPlotRangeCombobox(ui.cbPlotRanges);
0023     setBaseWidgets(ui.leName, ui.teComment);
0024     setVisibilityWidgets(ui.chkVisible);
0025 
0026     //"Symbol"-tab
0027     auto* hboxLayout = new QHBoxLayout(ui.tabSymbol);
0028     symbolWidget = new SymbolWidget(ui.tabSymbol);
0029     hboxLayout->addWidget(symbolWidget);
0030     hboxLayout->setContentsMargins(2, 2, 2, 2);
0031     hboxLayout->setSpacing(2);
0032 
0033     // adjust layouts in the tabs
0034     for (int i = 0; i < ui.tabWidget->count(); ++i) {
0035         auto* layout = dynamic_cast<QGridLayout*>(ui.tabWidget->widget(i)->layout());
0036         if (!layout)
0037             continue;
0038 
0039         layout->setContentsMargins(2, 2, 2, 2);
0040         layout->setHorizontalSpacing(2);
0041         layout->setVerticalSpacing(2);
0042     }
0043 
0044     CustomPointDock::updateLocale();
0045 
0046     // Positioning and alignment
0047     ui.cbPositionX->addItem(i18n("Left"));
0048     ui.cbPositionX->addItem(i18n("Center"));
0049     ui.cbPositionX->addItem(i18n("Right"));
0050 
0051     ui.cbPositionY->addItem(i18n("Top"));
0052     ui.cbPositionY->addItem(i18n("Center"));
0053     ui.cbPositionY->addItem(i18n("Bottom"));
0054 
0055     // SLOTS
0056     // General
0057     connect(ui.chbLock, &QCheckBox::clicked, this, &CustomPointDock::lockChanged);
0058 
0059     // positioning
0060     connect(ui.chbBindLogicalPos, &QCheckBox::clicked, this, &CustomPointDock::bindingChanged);
0061     connect(ui.cbPositionX, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &CustomPointDock::positionXChanged);
0062     connect(ui.cbPositionY, QOverload<int>::of(&KComboBox::currentIndexChanged), this, &CustomPointDock::positionYChanged);
0063     connect(ui.sbPositionX, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &CustomPointDock::customPositionXChanged);
0064     connect(ui.sbPositionY, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &CustomPointDock::customPositionYChanged);
0065     connect(ui.sbPositionXLogical, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &CustomPointDock::positionXLogicalChanged);
0066     connect(ui.dtePositionXLogical, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &CustomPointDock::positionXLogicalDateTimeChanged);
0067     connect(ui.sbPositionYLogical, QOverload<double>::of(&NumberSpinBox::valueChanged), this, &CustomPointDock::positionYLogicalChanged);
0068     connect(ui.dtePositionYLogical, &UTCDateTimeEdit::mSecsSinceEpochUTCChanged, this, &CustomPointDock::positionYLogicalDateTimeChanged);
0069 
0070     // Template handler
0071     auto* frame = new QFrame(this);
0072     auto* hlayout = new QHBoxLayout(frame);
0073     hlayout->setContentsMargins(0, 11, 0, 11);
0074 
0075     auto* templateHandler = new TemplateHandler(this, QLatin1String("CustomPoint"));
0076     hlayout->addWidget(templateHandler);
0077     connect(templateHandler, &TemplateHandler::loadConfigRequested, this, &CustomPointDock::loadConfigFromTemplate);
0078     connect(templateHandler, &TemplateHandler::saveConfigRequested, this, &CustomPointDock::saveConfigAsTemplate);
0079     connect(templateHandler, &TemplateHandler::info, this, &CustomPointDock::info);
0080 
0081     ui.verticalLayout->addWidget(frame);
0082 }
0083 
0084 void CustomPointDock::setPoints(QList<CustomPoint*> points) {
0085     CONDITIONAL_LOCK_RETURN;
0086     m_points = points;
0087     m_point = m_points.first();
0088     setAspects(points);
0089     Q_ASSERT(m_point);
0090 
0091     QList<Symbol*> symbols;
0092     for (auto* point : m_points)
0093         symbols << point->symbol();
0094 
0095     symbolWidget->setSymbols(symbols);
0096 
0097     // show the properties of the first custom point
0098     this->load();
0099     initConnections();
0100     updatePlotRangeList(); // needed when loading project
0101 
0102     // for custom points being children of an InfoElement, the position is changed
0103     // via the parent settings -> disable the positioning here.
0104     bool enabled = (m_point->parentAspect()->type() != AspectType::InfoElement);
0105     ui.chbBindLogicalPos->setEnabled(enabled);
0106     ui.sbPositionXLogical->setEnabled(enabled);
0107     ui.lPositionXLogicalDateTime->setEnabled(enabled);
0108     ui.sbPositionYLogical->setEnabled(enabled);
0109     ui.lPositionYLogicalDateTime->setEnabled(enabled);
0110 }
0111 
0112 void CustomPointDock::initConnections() const {
0113     // SIGNALs/SLOTs
0114     connect(m_point, &CustomPoint::lockChanged, this, &CustomPointDock::pointLockChanged);
0115     connect(m_point, &CustomPoint::positionChanged, this, &CustomPointDock::pointPositionChanged);
0116     connect(m_point, &CustomPoint::positionLogicalChanged, this, &CustomPointDock::pointPositionLogicalChanged);
0117     connect(m_point, &CustomPoint::coordinateBindingEnabledChanged, this, &CustomPointDock::pointCoordinateBindingEnabledChanged);
0118 }
0119 
0120 /*
0121  * updates the locale in the widgets. called when the application settins are changed.
0122  */
0123 void CustomPointDock::updateLocale() {
0124     const auto numberLocale = QLocale();
0125     ui.sbPositionX->setLocale(numberLocale);
0126     ui.sbPositionY->setLocale(numberLocale);
0127     ui.sbPositionXLogical->setLocale(numberLocale);
0128     ui.sbPositionYLogical->setLocale(numberLocale);
0129     symbolWidget->updateLocale();
0130 }
0131 
0132 //**********************************************************
0133 //**** SLOTs for changes triggered in CustomPointDock ******
0134 //**********************************************************
0135 //"General"-tab
0136 /*!
0137     called when label's current horizontal position relative to its parent (left, center, right ) is changed.
0138 */
0139 void CustomPointDock::positionXChanged(int index) {
0140     CONDITIONAL_LOCK_RETURN;
0141 
0142     auto horPos = WorksheetElement::HorizontalPosition(index);
0143     for (auto* point : m_points) {
0144         auto position = point->position();
0145         position.horizontalPosition = horPos;
0146         point->setPosition(position);
0147     }
0148 }
0149 
0150 /*!
0151     called when label's current horizontal position relative to its parent (top, center, bottom) is changed.
0152 */
0153 void CustomPointDock::positionYChanged(int index) {
0154     CONDITIONAL_LOCK_RETURN;
0155 
0156     auto verPos = WorksheetElement::VerticalPosition(index);
0157     for (auto* point : m_points) {
0158         auto position = point->position();
0159         position.verticalPosition = verPos;
0160         point->setPosition(position);
0161     }
0162 }
0163 
0164 void CustomPointDock::customPositionXChanged(double value) {
0165     CONDITIONAL_RETURN_NO_LOCK;
0166 
0167     const double x = Worksheet::convertToSceneUnits(value, m_worksheetUnit);
0168     for (auto* point : m_points) {
0169         auto position = point->position();
0170         position.point.setX(x);
0171         point->setPosition(position);
0172     }
0173 }
0174 
0175 void CustomPointDock::customPositionYChanged(double value) {
0176     CONDITIONAL_RETURN_NO_LOCK;
0177 
0178     const double y = Worksheet::convertToSceneUnits(value, m_worksheetUnit);
0179     for (auto* point : m_points) {
0180         auto position = point->position();
0181         position.point.setY(y);
0182         point->setPosition(position);
0183     }
0184 }
0185 
0186 // positioning using logical plot coordinates
0187 void CustomPointDock::positionXLogicalChanged(double value) {
0188     CONDITIONAL_RETURN_NO_LOCK;
0189 
0190     QPointF pos = m_point->positionLogical();
0191     pos.setX(value);
0192     for (auto* point : m_points)
0193         point->setPositionLogical(pos);
0194 }
0195 
0196 void CustomPointDock::positionXLogicalDateTimeChanged(qint64 value) {
0197     CONDITIONAL_LOCK_RETURN;
0198 
0199     QPointF pos = m_point->positionLogical();
0200     pos.setX(value);
0201     for (auto* point : m_points)
0202         point->setPositionLogical(pos);
0203 }
0204 
0205 void CustomPointDock::positionYLogicalChanged(double value) {
0206     CONDITIONAL_RETURN_NO_LOCK;
0207 
0208     QPointF pos = m_point->positionLogical();
0209     pos.setY(value);
0210     for (auto* point : m_points)
0211         point->setPositionLogical(pos);
0212 }
0213 
0214 void CustomPointDock::positionYLogicalDateTimeChanged(qint64 value) {
0215     CONDITIONAL_LOCK_RETURN;
0216 
0217     QPointF pos = m_point->positionLogical();
0218     pos.setY(value);
0219     for (auto* point : m_points)
0220         point->setPositionLogical(pos);
0221 }
0222 
0223 void CustomPointDock::lockChanged(bool locked) {
0224     CONDITIONAL_LOCK_RETURN;
0225     for (auto* point : m_points)
0226         point->setLock(locked);
0227 }
0228 
0229 /*!
0230  * \brief CustomPointDock::bindingChanged
0231  * Bind CustomPoint to the cartesian plot coords or not
0232  * \param checked
0233  */
0234 void CustomPointDock::bindingChanged(bool checked) {
0235     ui.chbBindLogicalPos->setChecked(checked);
0236 
0237     // widgets for positioning using absolute plot distances
0238     ui.lPositionX->setVisible(!checked);
0239     ui.cbPositionX->setVisible(!checked);
0240     ui.sbPositionX->setVisible(!checked);
0241 
0242     ui.lPositionY->setVisible(!checked);
0243     ui.cbPositionY->setVisible(!checked);
0244     ui.sbPositionY->setVisible(!checked);
0245 
0246     // widgets for positioning using logical plot coordinates
0247     const auto* plot = static_cast<const CartesianPlot*>(m_point->parent(AspectType::CartesianPlot));
0248     if (plot) {
0249         // x
0250         bool numeric = (plot->xRangeFormatDefault() == RangeT::Format::Numeric);
0251         if (numeric) {
0252             ui.lPositionXLogical->setVisible(checked);
0253             ui.sbPositionXLogical->setVisible(checked);
0254         } else {
0255             ui.lPositionXLogicalDateTime->setVisible(checked);
0256             ui.dtePositionXLogical->setVisible(checked);
0257         }
0258 
0259         // y
0260         numeric = (plot->yRangeFormatDefault() == RangeT::Format::Numeric);
0261         if (numeric) {
0262             ui.lPositionYLogical->setVisible(checked);
0263             ui.sbPositionYLogical->setVisible(checked);
0264         } else {
0265             ui.lPositionYLogicalDateTime->setVisible(checked);
0266             ui.dtePositionYLogical->setVisible(checked);
0267         }
0268     }
0269 
0270     CONDITIONAL_LOCK_RETURN;
0271 
0272     for (auto* point : m_points)
0273         point->setCoordinateBindingEnabled(checked);
0274 }
0275 
0276 //*********************************************************
0277 //**** SLOTs for changes triggered in CustomPoint *********
0278 //*********************************************************
0279 //"General"-tab
0280 void CustomPointDock::pointPositionChanged(const WorksheetElement::PositionWrapper& position) {
0281     CONDITIONAL_LOCK_RETURN;
0282     ui.sbPositionX->setValue(Worksheet::convertFromSceneUnits(position.point.x(), m_worksheetUnit));
0283     ui.sbPositionY->setValue(Worksheet::convertFromSceneUnits(position.point.y(), m_worksheetUnit));
0284     ui.cbPositionX->setCurrentIndex(static_cast<int>(position.horizontalPosition));
0285     ui.cbPositionY->setCurrentIndex(static_cast<int>(position.verticalPosition));
0286 }
0287 
0288 void CustomPointDock::pointCoordinateBindingEnabledChanged(bool enabled) {
0289     CONDITIONAL_LOCK_RETURN;
0290     bindingChanged(enabled);
0291 }
0292 
0293 void CustomPointDock::pointPositionLogicalChanged(QPointF pos) {
0294     CONDITIONAL_LOCK_RETURN;
0295     ui.sbPositionXLogical->setValue(pos.x());
0296     ui.dtePositionXLogical->setMSecsSinceEpochUTC(pos.x());
0297     ui.sbPositionYLogical->setValue(pos.y());
0298     ui.dtePositionYLogical->setMSecsSinceEpochUTC(pos.y());
0299 }
0300 
0301 void CustomPointDock::pointLockChanged(bool on) {
0302     CONDITIONAL_LOCK_RETURN;
0303     ui.chbLock->setChecked(on);
0304 }
0305 
0306 //**********************************************************
0307 //******************** SETTINGS ****************************
0308 //**********************************************************
0309 void CustomPointDock::load() {
0310     if (!m_point)
0311         return;
0312 
0313     // Geometry
0314     // widgets for positioning using absolute plot distances
0315     ui.cbPositionX->setCurrentIndex((int)m_point->position().horizontalPosition);
0316     positionXChanged(ui.cbPositionX->currentIndex());
0317     ui.sbPositionX->setValue(Worksheet::convertFromSceneUnits(m_point->position().point.x(), m_worksheetUnit));
0318     ui.cbPositionY->setCurrentIndex((int)m_point->position().verticalPosition);
0319     positionYChanged(ui.cbPositionY->currentIndex());
0320     ui.sbPositionY->setValue(Worksheet::convertFromSceneUnits(m_point->position().point.y(), m_worksheetUnit));
0321 
0322     // widgets for positioning using logical plot coordinates
0323     bool allowLogicalCoordinates = (m_point->plot() != nullptr);
0324     ui.lBindLogicalPos->setVisible(allowLogicalCoordinates);
0325     ui.chbBindLogicalPos->setVisible(allowLogicalCoordinates);
0326 
0327     if (allowLogicalCoordinates) {
0328         const auto* plot = static_cast<const CartesianPlot*>(m_point->plot());
0329 
0330         // x
0331         bool numeric = (plot->xRangeFormatDefault() == RangeT::Format::Numeric);
0332         ui.lPositionXLogical->setVisible(numeric);
0333         ui.sbPositionXLogical->setVisible(numeric);
0334         ui.lPositionXLogicalDateTime->setVisible(!numeric);
0335         ui.dtePositionXLogical->setVisible(!numeric);
0336         if (numeric)
0337             ui.sbPositionXLogical->setValue(m_point->positionLogical().x());
0338         else {
0339             ui.dtePositionXLogical->setDisplayFormat(plot->rangeDateTimeFormat(Dimension::X));
0340             ui.dtePositionXLogical->setMSecsSinceEpochUTC(m_point->positionLogical().x());
0341         }
0342 
0343         // y
0344         numeric = (plot->yRangeFormatDefault() == RangeT::Format::Numeric);
0345         ui.lPositionYLogical->setVisible(numeric);
0346         ui.sbPositionYLogical->setVisible(numeric);
0347         ui.lPositionYLogicalDateTime->setVisible(!numeric);
0348         ui.dtePositionYLogical->setVisible(!numeric);
0349         if (numeric)
0350             ui.sbPositionYLogical->setValue(m_point->positionLogical().y());
0351         else {
0352             ui.dtePositionYLogical->setDisplayFormat(plot->rangeDateTimeFormat(Dimension::Y));
0353             ui.dtePositionYLogical->setMSecsSinceEpochUTC(m_point->positionLogical().y());
0354         }
0355 
0356         bindingChanged(m_point->coordinateBindingEnabled());
0357     } else {
0358         ui.lPositionXLogical->hide();
0359         ui.sbPositionXLogical->hide();
0360         ui.lPositionYLogical->hide();
0361         ui.sbPositionYLogical->hide();
0362         ui.lPositionXLogicalDateTime->hide();
0363         ui.dtePositionXLogical->hide();
0364         ui.lPositionYLogicalDateTime->hide();
0365         ui.dtePositionYLogical->hide();
0366     }
0367 
0368     ui.chbLock->setChecked(m_point->isLocked());
0369     ui.chkVisible->setChecked(m_point->isVisible());
0370 }
0371 
0372 void CustomPointDock::loadConfigFromTemplate(KConfig& config) {
0373     auto name = TemplateHandler::templateName(config);
0374     int size = m_points.size();
0375     if (size > 1)
0376         m_point->beginMacro(i18n("%1 custom points: template \"%2\" loaded", size, name));
0377     else
0378         m_point->beginMacro(i18n("%1: template \"%2\" loaded", m_point->name(), name));
0379 
0380     symbolWidget->loadConfig(config.group(QStringLiteral("CustomPoint")));
0381 
0382     m_point->endMacro();
0383 }
0384 
0385 void CustomPointDock::saveConfigAsTemplate(KConfig& config) {
0386     KConfigGroup group = config.group(QStringLiteral("CustomPoint"));
0387     symbolWidget->saveConfig(group);
0388 }