Kstars

collimationoverlayoptions.cpp
1/*
2 SPDX-FileCopyrightText: 2023 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "collimationoverlayoptions.h"
8#include <kstars_debug.h>
9
10#include "kstarsdata.h"
11#include "kstars.h"
12#include "qmetaobject.h"
13
14#include <QTimer>
15#include <QSqlTableModel>
16#include <QSqlRecord>
17#include <basedevice.h>
18#include <algorithm>
19
20CollimationOverlayOptions *CollimationOverlayOptions::m_Instance = nullptr;
21
22CollimationOverlayOptions *CollimationOverlayOptions::Instance(QWidget *parent)
23{
24 if (m_Instance == nullptr) {
25 m_Instance = new CollimationOverlayOptions(parent);
26 }
27 return m_Instance;
28}
29
30void CollimationOverlayOptions::release()
31{
32 delete(m_Instance);
33 m_Instance = nullptr;
34}
35
36CollimationOverlayOptions::CollimationOverlayOptions(QWidget *parent) : QDialog(parent)
37{
38#ifdef Q_OS_OSX
39 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
40#endif
41
42 setupUi(this);
43
44 // Enable Checkbox
45 connect(EnableCheckBox, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this,
46 [this](int state) {
47 updateValue(state, "Enabled");
48 });
49
50 // Type Combo
51 connect(typeComboBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
52 [this]() {
53 updateValue(typeComboBox, "Type");
54
55 // Anchor types can't have a size, rotation, count, PCD, thickness, or colour
56 if (typeComboBox->currentIndex() == 0) {
57 sizeXSpinBox->setValue(0);
58 sizeYSpinBox->setValue(0);
59 rotationDoubleSpinBox->setValue(0.0);
60 countSpinBox->setValue(0);
61 pcdSpinBox->setValue(0);
62 thicknessSpinBox->setEnabled(false);
63 sizeXSpinBox->setEnabled(false);
64 sizeYSpinBox->setEnabled(false);
65 rotationDoubleSpinBox->setEnabled(false);
66 countSpinBox->setEnabled(false);
67 pcdSpinBox->setEnabled(false);
68 thicknessSpinBox->setEnabled(false);
69 colourButton->setColor("Black");
70 colourButton->setEnabled(false);
71 } else {
72 sizeXSpinBox->setEnabled(true);
73 sizeYSpinBox->setEnabled(true);
74 rotationDoubleSpinBox->setEnabled(true);
75 countSpinBox->setEnabled(true);
76 pcdSpinBox->setEnabled(true);
77 thicknessSpinBox->setEnabled(true);
78 colourButton->setEnabled(true);
79 }
80
81 // Default to linked XY size for Ellipse types only
82 if (typeComboBox->currentIndex() == 1) {
83 linkXYB->setIcon(QIcon::fromTheme("document-encrypt"));
84 } else {
85 linkXYB->setIcon(QIcon::fromTheme("document-decrypt"));
86 }
87
88 // Allow sizeY = 0 for lines
89 if (typeComboBox->currentIndex() == 3){
90 sizeYSpinBox->setMinimum(0);
91 } else {
92 sizeYSpinBox->setMinimum(1);
93 }
94 });
95
96 //Populate typeComboBox
97 QStringList typeValues;
98 collimationoverlaytype m_types;
99 const QMetaObject *m_metaobject = m_types.metaObject();
100 QMetaEnum m_metaEnum = m_metaobject->enumerator(m_metaobject->indexOfEnumerator("Types"));
101 for (int i = 0; i < m_metaEnum.keyCount(); i++) {
102 typeValues << tr(m_metaEnum.key(i));
103 }
104 typeComboBox->clear();
105 typeComboBox->addItems(typeValues);
106 typeComboBox->setCurrentIndex(0);
107
108 // SizeX SpinBox
109 connect(sizeXSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
110 [this](int value) {
111 updateValue(value, "SizeX");
112 if (linkXYB->icon().name() == "document-encrypt") {
113 sizeYSpinBox->setValue(sizeXSpinBox->value());
114 updateValue(value, "SizeY");
115 }
116 });
117
118 // SizeY SpinBox
119 connect(sizeYSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
120 [this](int value) {
121 updateValue(value, "SizeY");
122 });
123
124 //LinkXY Button
125 linkXYB->setIcon(QIcon::fromTheme("document-decrypt"));
126 connect(linkXYB, &QPushButton::clicked, this, [this] {
127 if (linkXYB->icon().name() == "document-decrypt") {
128 sizeYSpinBox->setValue(sizeXSpinBox->value());
129 linkXYB->setIcon(QIcon::fromTheme("document-encrypt"));
130 sizeYSpinBox->setEnabled(false);
131 } else if (linkXYB->icon().name() == "document-encrypt") {
132 linkXYB->setIcon(QIcon::fromTheme("document-decrypt"));
133 sizeYSpinBox->setEnabled(true);
134 }
135 });
136
137 // OffsetX SpinBox
138 connect(offsetXSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
139 [this](int value) {
140 updateValue(value, "OffsetX");
141 });
142
143 // OffsetY SpinBox
144 connect(offsetYSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
145 [this](int value) {
146 updateValue(value, "OffsetY");
147 });
148
149 // Count SpinBox
150 connect(countSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
151 [this](int value) {
152 updateValue(value, "Count");
153 // Single count elements can't have rotation or PCD
154 if (value == 1) {
155 pcdSpinBox->setEnabled(false);
156 rotationDoubleSpinBox->setEnabled(false);
157 } else {
158 pcdSpinBox->setEnabled(true);
159 rotationDoubleSpinBox->setEnabled(true);
160 }
161 });
162
163 //PCD SpinBox
164 connect(pcdSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
165 [this](int value) {
166 updateValue(value, "PCD");
167 });
168
169 // Rotation DoubleSpinBox
170 connect(rotationDoubleSpinBox, static_cast<void (QDoubleSpinBox::*)(double)>(&QDoubleSpinBox::valueChanged), this,
171 [this](double value) {
172 updateValue(value, "Rotation");
173 });
174
175 // Color KColorButton
176 colourButton->setAlphaChannelEnabled(true);
177 connect(colourButton, static_cast<void (KColorButton::*)(const QColor&)>(&KColorButton::changed), this,
178 [this](QColor value) {
179 updateValue(value, "Colour");
180 });
181
182 // Thickness SpinBox
183 connect(thicknessSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this,
184 [this](int value) {
185 updateValue(value, "Thickness");
186 });
187
188 connect(addB, &QPushButton::clicked, this, [this]() {
189 if (addB->icon().name() == "dialog-ok-apply") {
190 elementNamesList->clearSelection();
191 addB->setIcon(QIcon::fromTheme("list-add"));
192 selectCollimationOverlayElement("");
193 } else {
194 addElement(nameLineEdit->text());
195 m_CollimationOverlayElementsModel->select();
196 refreshModel();
197 elementNamesList->clearSelection();
198 }
199 });
200
201 connect(removeB, &QPushButton::clicked, this, [this]() {
202 if (elementNamesList->currentItem() != nullptr) {
203 removeCollimationOverlayElement(elementNamesList->currentItem()->text());
204 refreshElements();
205 elementNamesList->clearSelection();
206 addB->setIcon(QIcon::fromTheme("list-add"));
207 }
208 });
209
211 Q_UNUSED(item);
212 addB->setIcon(QIcon::fromTheme("list-add"));
213 removeB->setEnabled(true);
214 });
215
217 selectCollimationOverlayElement(item);
218 addB->setIcon(QIcon::fromTheme("dialog-ok-apply"));
219 if (typeComboBox->currentIndex() == 1) {
220 linkXYB->setIcon(QIcon::fromTheme("document-encrypt"));
221 } else {
222 linkXYB->setIcon(QIcon::fromTheme("document-decrypt"));
223 }
224 });
225
226 connect(renameB, &QPushButton::clicked, this, [this] {
227 renameCollimationOverlayElement(nameLineEdit->text());
228 }) ;
229
231 Q_UNUSED(row);
232 selectCollimationOverlayElement("");
233 });
234
235 initModel();
236}
237
238void CollimationOverlayOptions::initModel()
239{
240 m_CollimationOverlayElements.clear();
241 auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
242 m_CollimationOverlayElementsModel = new QSqlTableModel(this, userdb);
243 connect(m_CollimationOverlayElementsModel, &QSqlTableModel::dataChanged, this, [this]() {
244 m_CollimationOverlayElements.clear();
245 for (int i = 0; i < m_CollimationOverlayElementsModel->rowCount(); ++i) {
246 QVariantMap recordMap;
247 QSqlRecord record = m_CollimationOverlayElementsModel->record(i);
248 for (int j = 0; j < record.count(); j++)
249 recordMap[record.fieldName(j)] = record.value(j);
250
251 m_CollimationOverlayElements.append(recordMap);
252 }
253 m_ElementNames.clear();
254 for (auto &oneElement : m_CollimationOverlayElements) {
255 m_ElementNames << oneElement["Name"].toString();
256 }
257 elementNamesList->clear();
258 elementNamesList->addItems(m_ElementNames);
260 emit updated();
261 });
262 refreshModel();
263}
264
265void CollimationOverlayOptions::refreshModel()
266{
267 m_CollimationOverlayElements.clear();
268 KStars::Instance()->data()->userdb()->GetCollimationOverlayElements(m_CollimationOverlayElements);
269 m_ElementNames.clear();
270 for (auto &oneElement : m_CollimationOverlayElements) {
271 m_ElementNames << oneElement["Name"].toString();
272 }
273 elementNamesList->clear();
274 elementNamesList->addItems(m_ElementNames);
275}
276
277QString CollimationOverlayOptions::addElement(const QString &name)
278{
279 QVariantMap element;
280 element["Name"] = uniqueElementName(name, typeComboBox->currentText());
281 element["Enabled"] = EnableCheckBox->checkState();
282 element["Type"] = typeComboBox->currentText();
283 element["SizeX"] = sizeXSpinBox->value();
284 element["SizeY"] = sizeYSpinBox->value();
285 element["OffsetX"] = offsetXSpinBox->value();
286 element["OffsetY"] = offsetYSpinBox->value();
287 element["Count"] = countSpinBox->value();
288 element["PCD"] = pcdSpinBox->value();
289 element["Rotation"] = rotationDoubleSpinBox->value();
290 element["Colour"] = colourButton->color();
291 element["Thickness"] = thicknessSpinBox->value();
292
293 KStarsData::Instance()->userdb()->AddCollimationOverlayElement(element);
294 emit updated();
295 return element["Name"].toString();
296}
297
298bool CollimationOverlayOptions::setCollimationOverlayElementValue(const QString &name, const QString &field, const QVariant &value)
299{
300 for (auto &oneElement : m_CollimationOverlayElements) {
301 if (oneElement["Name"].toString() == name) {
302 // If value did not change, just return true
303 if (oneElement[field] == value) {
304 return true;
305 }
306 // Update field and database.
307 oneElement[field] = value;
308 KStarsData::Instance()->userdb()->UpdateCollimationOverlayElement(oneElement, oneElement["id"].toInt());
309 emit updated();
310 return true;
311 }
312 }
313 return false;
314}
315
316void CollimationOverlayOptions::renameCollimationOverlayElement(const QString &name)
317{
318 if (m_CurrentElement != nullptr && (*m_CurrentElement)["Name"] != name) {
319 auto pos = elementNamesList->currentRow();
320 // ensure element name uniqueness
321 auto unique = uniqueElementName(name, (*m_CurrentElement)["Type"].toString());
322 // update the element database entry
323 setCollimationOverlayElementValue((*m_CurrentElement)["Name"].toString(), "Name", unique);
324 // propagate the unique name to the current selection
325 elementNamesList->currentItem()->setText(unique);
326 // refresh the trains
327 refreshElements();
328 // refresh selection
329 elementNamesList->setCurrentRow(pos);
330 selectCollimationOverlayElement(unique);
331 }
332}
333
334bool CollimationOverlayOptions::setCollimationOverlayElement(const QJsonObject &element)
335{
336 auto oneElement = getCollimationOverlayElement(element["id"].toInt());
337 if (!oneElement.empty()) {
338 KStarsData::Instance()->userdb()->UpdateCollimationOverlayElement(element.toVariantMap(), oneElement["id"].toInt());
339 refreshElements();
340 return true;
341 }
342 return false;
343}
344
345bool CollimationOverlayOptions::removeCollimationOverlayElement(const QString &name)
346{
347 for (auto &oneElement : m_CollimationOverlayElements) {
348 if (oneElement["Name"].toString() == name) {
349 auto id = oneElement["id"].toInt();
350 KStarsData::Instance()->userdb()->DeleteCollimationOverlayElement(id);
351 refreshElements();
352 return true;
353 }
354 }
355 return false;
356}
357
358QString CollimationOverlayOptions::uniqueElementName(QString name, QString type)
359{
360 if ("" == name) name = type;
362 int nr = 1;
363 while (m_ElementNames.contains(result)) {
364 result = QString("%1 (%2)").arg(name).arg(nr++);
365 }
366 return result;
367}
368
369bool CollimationOverlayOptions::selectCollimationOverlayElement(QListWidgetItem *item)
370{
371 if (item != nullptr && selectCollimationOverlayElement(item->text())) {
372 return true;
373 }
374 return false;
375}
376
377bool CollimationOverlayOptions::selectCollimationOverlayElement(const QString &name)
378{
379 for (auto &oneElement : m_CollimationOverlayElements) {
380 if (oneElement["Name"].toString() == name) {
381 editing = true;
382 m_CurrentElement = &oneElement;
383 nameLineEdit->setText(oneElement["Name"].toString());
384 renameB->setEnabled(true);
385 EnableCheckBox->setChecked(oneElement["Enabled"].toUInt());
386 typeComboBox->setCurrentText(oneElement["Type"].toString());
387 sizeXSpinBox->setValue(oneElement["SizeX"].toUInt());
388 sizeYSpinBox->setValue(oneElement["SizeY"].toUInt());
389 offsetXSpinBox->setValue(oneElement["OffsetX"].toUInt());
390 offsetYSpinBox->setValue(oneElement["OffsetY"].toUInt());
391 countSpinBox->setValue(oneElement["Count"].toUInt());
392 pcdSpinBox->setValue(oneElement["PCD"].toUInt());
393 rotationDoubleSpinBox->setValue(oneElement["Rotation"].toDouble());
395 tempColour.setNamedColor(oneElement["Colour"].toString());
396 colourButton->setColor(tempColour);
397 thicknessSpinBox->setValue(oneElement["Thickness"].toUInt());
398 removeB->setEnabled(m_CollimationOverlayElements.length() > 0);
399 elementConfigBox->setEnabled(true);
400 return true;
401 }
402 }
403
404 // none found
405 editing = false;
406 nameLineEdit->setText("");
407 renameB->setEnabled(false);
408 EnableCheckBox->setCheckState(Qt::Checked);
409 typeComboBox->setCurrentText("--");
410 sizeXSpinBox->setValue(0);
411 sizeYSpinBox->setValue(0);
412 offsetXSpinBox->setValue(0);
413 offsetYSpinBox->setValue(0);
414 countSpinBox->setValue(0);
415 pcdSpinBox->setValue(0);
416 rotationDoubleSpinBox->setValue(0.0);
418 tempColour.setNamedColor("White");
419 colourButton->setColor(tempColour);
420 thicknessSpinBox->setValue(1);
421
422 removeB->setEnabled(false);
423 return false;
424}
425
426void CollimationOverlayOptions::openEditor()
427{
428 initModel();
429 show();
430}
431
432const QVariantMap CollimationOverlayOptions::getCollimationOverlayElement(uint8_t id) const
433{
434 for (auto &oneElement : m_CollimationOverlayElements) {
435 if (oneElement["id"].toInt() == id)
436 return oneElement;
437 }
438 return QVariantMap();
439}
440
441bool CollimationOverlayOptions::exists(uint8_t id) const
442{
443 for (auto &oneElement : m_CollimationOverlayElements) {
444 if (oneElement["id"].toInt() == id)
445 return true;
446 }
447 return false;
448}
449
450const QVariantMap CollimationOverlayOptions::getCollimationOverlayElement(const QString &name) const
451{
452 for (auto &oneElement : m_CollimationOverlayElements) {
453 if (oneElement["Name"].toString() == name) {
454 return oneElement;
455 }
456 }
457 return QVariantMap();
458}
459
460void CollimationOverlayOptions::refreshElements()
461{
462 refreshModel();
463 emit updated();
464}
465
466int CollimationOverlayOptions::id(const QString &name) const
467{
468 for (auto &oneElement : m_CollimationOverlayElements) {
469 if (oneElement["Name"].toString() == name)
470 return oneElement["id"].toUInt();
471 }
472 return -1;
473}
474
475QString CollimationOverlayOptions::name(int id) const
476{
477 for (auto &oneElement : m_CollimationOverlayElements) {
478 if (oneElement["id"].toInt() == id)
479 return oneElement["name"].toString();
480 }
481 return QString();
482}
483
484void CollimationOverlayOptions::updateValue(QComboBox *cb, const QString &element)
485{
486 if (elementNamesList->currentItem() != nullptr && editing == true) {
487 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, cb->currentText());
488 }
489}
490
491void CollimationOverlayOptions::updateValue(double value, const QString &element)
492{
493 if (elementNamesList->currentItem() != nullptr && editing == true) {
494 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value);
495 }
496}
497
498void CollimationOverlayOptions::updateValue(int value, const QString &element)
499{
500 if (elementNamesList->currentItem() != nullptr && editing == true) {
501 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value);
502 }
503}
504
505void CollimationOverlayOptions::updateValue(QColor value, const QString &element)
506{
507 if (elementNamesList->currentItem() != nullptr && editing == true) {
508 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value);
509 }
510}
511
512void CollimationOverlayOptions::updateValue(QString value, const QString &element)
513{
514 if (elementNamesList->currentItem() != nullptr && editing == true) {
515 setCollimationOverlayElementValue(elementNamesList->currentItem()->text(), element, value);
516 }
517}
void changed(const QColor &newColor)
static KStars * Instance()
Definition kstars.h:123
Type type(const QSqlDatabase &db)
char * toString(const EngineQuery &query)
QString name(StandardShortcut id)
void clicked(bool checked)
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QList< int > &roles)
void stateChanged(int state)
void currentIndexChanged(int index)
int result() const const
void valueChanged(double d)
Int toInt() const const
QIcon fromTheme(const QString &name)
QVariantMap toVariantMap() const const
void append(QList< T > &&value)
void clear()
bool contains(const AT &value) const const
qsizetype length() const const
void currentRowChanged(int currentRow)
void itemClicked(QListWidgetItem *item)
void itemDoubleClicked(QListWidgetItem *item)
QString text() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void valueChanged(int i)
QSqlDatabase database(const QString &connectionName, bool open)
int count() const const
QString fieldName(int index) const const
QVariant value(const QString &name) const const
QSqlRecord record() const const
virtual int rowCount(const QModelIndex &parent) const const override
QString arg(Args &&... args) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Apr 27 2024 22:13:27 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.