File indexing completed on 2024-04-28 15:10:33
0001 /* 0002 SPDX-FileCopyrightText: 2023 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "collimationoverlayoptions.h" 0008 #include <kstars_debug.h> 0009 0010 #include "kstarsdata.h" 0011 #include "kstars.h" 0012 #include "qmetaobject.h" 0013 0014 #include <QTimer> 0015 #include <QSqlTableModel> 0016 #include <QSqlRecord> 0017 #include <basedevice.h> 0018 #include <algorithm> 0019 0020 CollimationOverlayOptions *CollimationOverlayOptions::m_Instance = nullptr; 0021 0022 CollimationOverlayOptions *CollimationOverlayOptions::Instance(QWidget *parent) 0023 { 0024 if (m_Instance == nullptr) { 0025 m_Instance = new CollimationOverlayOptions(parent); 0026 } 0027 return m_Instance; 0028 } 0029 0030 void CollimationOverlayOptions::release() 0031 { 0032 delete(m_Instance); 0033 m_Instance = nullptr; 0034 } 0035 0036 CollimationOverlayOptions::CollimationOverlayOptions(QWidget *parent) : QDialog(parent) 0037 { 0038 #ifdef Q_OS_OSX 0039 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0040 #endif 0041 0042 setupUi(this); 0043 0044 // Enable Checkbox 0045 connect(EnableCheckBox, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this, 0046 [this](int state) { 0047 updateValue(state, "Enabled"); 0048 }); 0049 0050 // Type Combo 0051 connect(typeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0052 [this]() { 0053 updateValue(typeComboBox, "Type"); 0054 0055 // Anchor types can't have a size, rotation, count, PCD, thickness, or colour 0056 if (typeComboBox->currentIndex() == 0) { 0057 sizeXSpinBox->setValue(0); 0058 sizeYSpinBox->setValue(0); 0059 rotationDoubleSpinBox->setValue(0.0); 0060 countSpinBox->setValue(0); 0061 pcdSpinBox->setValue(0); 0062 thicknessSpinBox->setEnabled(false); 0063 sizeXSpinBox->setEnabled(false); 0064 sizeYSpinBox->setEnabled(false); 0065 rotationDoubleSpinBox->setEnabled(false); 0066 countSpinBox->setEnabled(false); 0067 pcdSpinBox->setEnabled(false); 0068 thicknessSpinBox->setEnabled(false); 0069 colourButton->setColor("Black"); 0070 colourButton->setEnabled(false); 0071 } else { 0072 sizeXSpinBox->setEnabled(true); 0073 sizeYSpinBox->setEnabled(true); 0074 rotationDoubleSpinBox->setEnabled(true); 0075 countSpinBox->setEnabled(true); 0076 pcdSpinBox->setEnabled(true); 0077 thicknessSpinBox->setEnabled(true); 0078 colourButton->setEnabled(true); 0079 } 0080 0081 // Default to linked XY size for Ellipse types only 0082 if (typeComboBox->currentIndex() == 1) { 0083 linkXYB->setIcon(QIcon::fromTheme("document-encrypt")); 0084 } else { 0085 linkXYB->setIcon(QIcon::fromTheme("document-decrypt")); 0086 } 0087 0088 // Allow sizeY = 0 for lines 0089 if (typeComboBox->currentIndex() == 3){ 0090 sizeYSpinBox->setMinimum(0); 0091 } else { 0092 sizeYSpinBox->setMinimum(1); 0093 } 0094 }); 0095 0096 //Populate typeComboBox 0097 QStringList typeValues; 0098 collimationoverlaytype m_types; 0099 const QMetaObject *m_metaobject = m_types.metaObject(); 0100 QMetaEnum m_metaEnum = m_metaobject->enumerator(m_metaobject->indexOfEnumerator("Types")); 0101 for (int i = 0; i < m_metaEnum.keyCount(); i++) { 0102 typeValues << tr(m_metaEnum.key(i)); 0103 } 0104 typeComboBox->clear(); 0105 typeComboBox->addItems(typeValues); 0106 typeComboBox->setCurrentIndex(0); 0107 0108 // SizeX SpinBox 0109 connect(sizeXSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0110 [this](int value) { 0111 updateValue(value, "SizeX"); 0112 if (linkXYB->icon().name() == "document-encrypt") { 0113 sizeYSpinBox->setValue(sizeXSpinBox->value()); 0114 updateValue(value, "SizeY"); 0115 } 0116 }); 0117 0118 // SizeY SpinBox 0119 connect(sizeYSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0120 [this](int value) { 0121 updateValue(value, "SizeY"); 0122 }); 0123 0124 //LinkXY Button 0125 linkXYB->setIcon(QIcon::fromTheme("document-decrypt")); 0126 connect(linkXYB, &QPushButton::clicked, this, [this] { 0127 if (linkXYB->icon().name() == "document-decrypt") { 0128 sizeYSpinBox->setValue(sizeXSpinBox->value()); 0129 linkXYB->setIcon(QIcon::fromTheme("document-encrypt")); 0130 sizeYSpinBox->setEnabled(false); 0131 } else if (linkXYB->icon().name() == "document-encrypt") { 0132 linkXYB->setIcon(QIcon::fromTheme("document-decrypt")); 0133 sizeYSpinBox->setEnabled(true); 0134 } 0135 }); 0136 0137 // OffsetX SpinBox 0138 connect(offsetXSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0139 [this](int value) { 0140 updateValue(value, "OffsetX"); 0141 }); 0142 0143 // OffsetY SpinBox 0144 connect(offsetYSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0145 [this](int value) { 0146 updateValue(value, "OffsetY"); 0147 }); 0148 0149 // Count SpinBox 0150 connect(countSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0151 [this](int value) { 0152 updateValue(value, "Count"); 0153 // Single count elements can't have rotation or PCD 0154 if (value == 1) { 0155 pcdSpinBox->setEnabled(false); 0156 rotationDoubleSpinBox->setEnabled(false); 0157 } else { 0158 pcdSpinBox->setEnabled(true); 0159 rotationDoubleSpinBox->setEnabled(true); 0160 } 0161 }); 0162 0163 //PCD SpinBox 0164 connect(pcdSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0165 [this](int value) { 0166 updateValue(value, "PCD"); 0167 }); 0168 0169 // Rotation DoubleSpinBox 0170 connect(rotationDoubleSpinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this, 0171 [this](double value) { 0172 updateValue(value, "Rotation"); 0173 }); 0174 0175 // Color KColorButton 0176 colourButton->setAlphaChannelEnabled(true); 0177 connect(colourButton, static_cast<void (KColorButton::*)(const QColor&)>(&KColorButton::changed), this, 0178 [this](QColor value) { 0179 updateValue(value, "Colour"); 0180 }); 0181 0182 // Thickness SpinBox 0183 connect(thicknessSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, 0184 [this](int value) { 0185 updateValue(value, "Thickness"); 0186 }); 0187 0188 connect(addB, &QPushButton::clicked, this, [this]() { 0189 if (addB->icon().name() == "dialog-ok-apply") { 0190 elementNamesList->clearSelection(); 0191 addB->setIcon(QIcon::fromTheme("list-add")); 0192 selectCollimationOverlayElement(""); 0193 } else { 0194 addElement(nameLineEdit->text()); 0195 m_CollimationOverlayElementsModel->select(); 0196 refreshModel(); 0197 elementNamesList->clearSelection(); 0198 } 0199 }); 0200 0201 connect(removeB, &QPushButton::clicked, this, [this]() { 0202 if (elementNamesList->currentItem() != nullptr) { 0203 removeCollimationOverlayElement(elementNamesList->currentItem()->text()); 0204 refreshElements(); 0205 elementNamesList->clearSelection(); 0206 addB->setIcon(QIcon::fromTheme("list-add")); 0207 } 0208 }); 0209 0210 connect(elementNamesList, &QListWidget::itemClicked, this, [this](QListWidgetItem * item) { 0211 Q_UNUSED(item); 0212 addB->setIcon(QIcon::fromTheme("list-add")); 0213 removeB->setEnabled(true); 0214 }); 0215 0216 connect(elementNamesList, &QListWidget::itemDoubleClicked, this, [this](QListWidgetItem * item) { 0217 selectCollimationOverlayElement(item); 0218 addB->setIcon(QIcon::fromTheme("dialog-ok-apply")); 0219 if (typeComboBox->currentIndex() == 1) { 0220 linkXYB->setIcon(QIcon::fromTheme("document-encrypt")); 0221 } else { 0222 linkXYB->setIcon(QIcon::fromTheme("document-decrypt")); 0223 } 0224 }); 0225 0226 connect(renameB, &QPushButton::clicked, this, [this] { 0227 renameCollimationOverlayElement(nameLineEdit->text()); 0228 }) ; 0229 0230 connect(elementNamesList, &QListWidget::currentRowChanged, this, [this](int row) { 0231 Q_UNUSED(row); 0232 selectCollimationOverlayElement(""); 0233 }); 0234 0235 initModel(); 0236 } 0237 0238 void CollimationOverlayOptions::initModel() 0239 { 0240 m_CollimationOverlayElements.clear(); 0241 auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName()); 0242 m_CollimationOverlayElementsModel = new QSqlTableModel(this, userdb); 0243 connect(m_CollimationOverlayElementsModel, &QSqlTableModel::dataChanged, this, [this]() { 0244 m_CollimationOverlayElements.clear(); 0245 for (int i = 0; i < m_CollimationOverlayElementsModel->rowCount(); ++i) { 0246 QVariantMap recordMap; 0247 QSqlRecord record = m_CollimationOverlayElementsModel->record(i); 0248 for (int j = 0; j < record.count(); j++) 0249 recordMap[record.fieldName(j)] = record.value(j); 0250 0251 m_CollimationOverlayElements.append(recordMap); 0252 } 0253 m_ElementNames.clear(); 0254 for (auto &oneElement : m_CollimationOverlayElements) { 0255 m_ElementNames << oneElement["Name"].toString(); 0256 } 0257 elementNamesList->clear(); 0258 elementNamesList->addItems(m_ElementNames); 0259 elementNamesList->setEditTriggers(QAbstractItemView::AllEditTriggers); 0260 emit updated(); 0261 }); 0262 refreshModel(); 0263 } 0264 0265 void CollimationOverlayOptions::refreshModel() 0266 { 0267 m_CollimationOverlayElements.clear(); 0268 KStars::Instance()->data()->userdb()->GetCollimationOverlayElements(m_CollimationOverlayElements); 0269 m_ElementNames.clear(); 0270 for (auto &oneElement : m_CollimationOverlayElements) { 0271 m_ElementNames << oneElement["Name"].toString(); 0272 } 0273 elementNamesList->clear(); 0274 elementNamesList->addItems(m_ElementNames); 0275 } 0276 0277 QString CollimationOverlayOptions::addElement(const QString &name) 0278 { 0279 QVariantMap element; 0280 element["Name"] = uniqueElementName(name, typeComboBox->currentText()); 0281 element["Enabled"] = EnableCheckBox->checkState(); 0282 element["Type"] = typeComboBox->currentText(); 0283 element["SizeX"] = sizeXSpinBox->value(); 0284 element["SizeY"] = sizeYSpinBox->value(); 0285 element["OffsetX"] = offsetXSpinBox->value(); 0286 element["OffsetY"] = offsetYSpinBox->value(); 0287 element["Count"] = countSpinBox->value(); 0288 element["PCD"] = pcdSpinBox->value(); 0289 element["Rotation"] = rotationDoubleSpinBox->value(); 0290 element["Colour"] = colourButton->color(); 0291 element["Thickness"] = thicknessSpinBox->value(); 0292 0293 KStarsData::Instance()->userdb()->AddCollimationOverlayElement(element); 0294 emit updated(); 0295 return element["Name"].toString(); 0296 } 0297 0298 bool CollimationOverlayOptions::setCollimationOverlayElementValue(const QString &name, const QString &field, const QVariant &value) 0299 { 0300 for (auto &oneElement : m_CollimationOverlayElements) { 0301 if (oneElement["Name"].toString() == name) { 0302 // If value did not change, just return true 0303 if (oneElement[field] == value) { 0304 return true; 0305 } 0306 // Update field and database. 0307 oneElement[field] = value; 0308 KStarsData::Instance()->userdb()->UpdateCollimationOverlayElement(oneElement, oneElement["id"].toInt()); 0309 emit updated(); 0310 return true; 0311 } 0312 } 0313 return false; 0314 } 0315 0316 void CollimationOverlayOptions::renameCollimationOverlayElement(const QString &name) 0317 { 0318 if (m_CurrentElement != nullptr && (*m_CurrentElement)["Name"] != name) { 0319 auto pos = elementNamesList->currentRow(); 0320 // ensure element name uniqueness 0321 auto unique = uniqueElementName(name, (*m_CurrentElement)["Type"].toString()); 0322 // update the element database entry 0323 setCollimationOverlayElementValue((*m_CurrentElement)["Name"].toString(), "Name", unique); 0324 // propagate the unique name to the current selection 0325 elementNamesList->currentItem()->setText(unique); 0326 // refresh the trains 0327 refreshElements(); 0328 // refresh selection 0329 elementNamesList->setCurrentRow(pos); 0330 selectCollimationOverlayElement(unique); 0331 } 0332 } 0333 0334 bool CollimationOverlayOptions::setCollimationOverlayElement(const QJsonObject &element) 0335 { 0336 auto oneElement = getCollimationOverlayElement(element["id"].toInt()); 0337 if (!oneElement.empty()) { 0338 KStarsData::Instance()->userdb()->UpdateCollimationOverlayElement(element.toVariantMap(), oneElement["id"].toInt()); 0339 refreshElements(); 0340 return true; 0341 } 0342 return false; 0343 } 0344 0345 bool CollimationOverlayOptions::removeCollimationOverlayElement(const QString &name) 0346 { 0347 for (auto &oneElement : m_CollimationOverlayElements) { 0348 if (oneElement["Name"].toString() == name) { 0349 auto id = oneElement["id"].toInt(); 0350 KStarsData::Instance()->userdb()->DeleteCollimationOverlayElement(id); 0351 refreshElements(); 0352 return true; 0353 } 0354 } 0355 return false; 0356 } 0357 0358 QString CollimationOverlayOptions::uniqueElementName(QString name, QString type) 0359 { 0360 if ("" == name) name = type; 0361 QString result = name; 0362 int nr = 1; 0363 while (m_ElementNames.contains(result)) { 0364 result = QString("%1 (%2)").arg(name).arg(nr++); 0365 } 0366 return result; 0367 } 0368 0369 bool CollimationOverlayOptions::selectCollimationOverlayElement(QListWidgetItem *item) 0370 { 0371 if (item != nullptr && selectCollimationOverlayElement(item->text())) { 0372 return true; 0373 } 0374 return false; 0375 } 0376 0377 bool CollimationOverlayOptions::selectCollimationOverlayElement(const QString &name) 0378 { 0379 for (auto &oneElement : m_CollimationOverlayElements) { 0380 if (oneElement["Name"].toString() == name) { 0381 editing = true; 0382 m_CurrentElement = &oneElement; 0383 nameLineEdit->setText(oneElement["Name"].toString()); 0384 renameB->setEnabled(true); 0385 EnableCheckBox->setChecked(oneElement["Enabled"].toUInt()); 0386 typeComboBox->setCurrentText(oneElement["Type"].toString()); 0387 sizeXSpinBox->setValue(oneElement["SizeX"].toUInt()); 0388 sizeYSpinBox->setValue(oneElement["SizeY"].toUInt()); 0389 offsetXSpinBox->setValue(oneElement["OffsetX"].toUInt()); 0390 offsetYSpinBox->setValue(oneElement["OffsetY"].toUInt()); 0391 countSpinBox->setValue(oneElement["Count"].toUInt()); 0392 pcdSpinBox->setValue(oneElement["PCD"].toUInt()); 0393 rotationDoubleSpinBox->setValue(oneElement["Rotation"].toDouble()); 0394 QColor tempColour; 0395 tempColour.setNamedColor(oneElement["Colour"].toString()); 0396 colourButton->setColor(tempColour); 0397 thicknessSpinBox->setValue(oneElement["Thickness"].toUInt()); 0398 removeB->setEnabled(m_CollimationOverlayElements.length() > 0); 0399 elementConfigBox->setEnabled(true); 0400 return true; 0401 } 0402 } 0403 0404 // none found 0405 editing = false; 0406 nameLineEdit->setText(""); 0407 renameB->setEnabled(false); 0408 EnableCheckBox->setCheckState(Qt::Checked); 0409 typeComboBox->setCurrentText("--"); 0410 sizeXSpinBox->setValue(0); 0411 sizeYSpinBox->setValue(0); 0412 offsetXSpinBox->setValue(0); 0413 offsetYSpinBox->setValue(0); 0414 countSpinBox->setValue(0); 0415 pcdSpinBox->setValue(0); 0416 rotationDoubleSpinBox->setValue(0.0); 0417 QColor tempColour; 0418 tempColour.setNamedColor("White"); 0419 colourButton->setColor(tempColour); 0420 thicknessSpinBox->setValue(1); 0421 0422 removeB->setEnabled(false); 0423 return false; 0424 } 0425 0426 void CollimationOverlayOptions::openEditor() 0427 { 0428 initModel(); 0429 show(); 0430 } 0431 0432 const QVariantMap CollimationOverlayOptions::getCollimationOverlayElement(uint8_t id) const 0433 { 0434 for (auto &oneElement : m_CollimationOverlayElements) { 0435 if (oneElement["id"].toInt() == id) 0436 return oneElement; 0437 } 0438 return QVariantMap(); 0439 } 0440 0441 bool CollimationOverlayOptions::exists(uint8_t id) const 0442 { 0443 for (auto &oneElement : m_CollimationOverlayElements) { 0444 if (oneElement["id"].toInt() == id) 0445 return true; 0446 } 0447 return false; 0448 } 0449 0450 const QVariantMap CollimationOverlayOptions::getCollimationOverlayElement(const QString &name) const 0451 { 0452 for (auto &oneElement : m_CollimationOverlayElements) { 0453 if (oneElement["Name"].toString() == name) { 0454 return oneElement; 0455 } 0456 } 0457 return QVariantMap(); 0458 } 0459 0460 void CollimationOverlayOptions::refreshElements() 0461 { 0462 refreshModel(); 0463 emit updated(); 0464 } 0465 0466 int CollimationOverlayOptions::id(const QString &name) const 0467 { 0468 for (auto &oneElement : m_CollimationOverlayElements) { 0469 if (oneElement["Name"].toString() == name) 0470 return oneElement["id"].toUInt(); 0471 } 0472 return -1; 0473 } 0474 0475 QString CollimationOverlayOptions::name(int id) const 0476 { 0477 for (auto &oneElement : m_CollimationOverlayElements) { 0478 if (oneElement["id"].toInt() == id) 0479 return oneElement["name"].toString(); 0480 } 0481 return QString(); 0482 } 0483 0484 void CollimationOverlayOptions::updateValue(QComboBox *cb, const QString &element) 0485 { 0486 if (elementNamesList->currentItem() != nullptr && editing == true) { 0487 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, cb->currentText()); 0488 } 0489 } 0490 0491 void CollimationOverlayOptions::updateValue(double value, const QString &element) 0492 { 0493 if (elementNamesList->currentItem() != nullptr && editing == true) { 0494 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value); 0495 } 0496 } 0497 0498 void CollimationOverlayOptions::updateValue(int value, const QString &element) 0499 { 0500 if (elementNamesList->currentItem() != nullptr && editing == true) { 0501 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value); 0502 } 0503 } 0504 0505 void CollimationOverlayOptions::updateValue(QColor value, const QString &element) 0506 { 0507 if (elementNamesList->currentItem() != nullptr && editing == true) { 0508 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value); 0509 } 0510 } 0511 0512 void CollimationOverlayOptions::updateValue(QString value, const QString &element) 0513 { 0514 if (elementNamesList->currentItem() != nullptr && editing == true) { 0515 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value); 0516 } 0517 }