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 }