7#include "horizonmanager.h"
10#include "kstarsdata.h"
12#include "ksnotification.h"
15#include "projections/projector.h"
16#include "skycomponents/artificialhorizoncomponent.h"
17#include "skycomponents/skymapcomposite.h"
20#include <QStandardItemModel>
22#include <kstars_debug.h>
24#define MIN_NUMBER_POINTS 2
25#define HORIZON_KEYWORD "Horizon"
26#define CEILING_KEYWORD "Ceiling"
38 ui =
new HorizonManagerUI(
this);
40 ui->setStyleSheet(
"QPushButton:checked { background-color: red; }");
51 ui->selectPointsB->setIcon(
54 ui->tipLabel->setPixmap(
57 ui->regionValidation->setPixmap(
59 ui->regionValidation->setToolTip(
i18n(
"Region is invalid."));
60 ui->regionValidation->hide();
78 m_RegionsModel->setHorizontalHeaderLabels(
QStringList()
79 <<
i18n(
"Region") <<
i18nc(
"Azimuth",
"Az") <<
i18nc(
"Altitude",
"Alt"));
81 ui->regionsList->setModel(m_RegionsModel);
83 ui->pointsList->setModel(m_RegionsModel);
85 ui->pointsList->verticalHeader()->hide();
86 ui->pointsList->setColumnHidden(0,
true);
88 horizonComponent = KStarsData::Instance()->skyComposite()->artificialHorizon();
93 for (
auto &horizon : *horizonList)
99 if (horizon->ceiling())
105 m_RegionsModel->appendRow(regionItem);
107 SkyList *points = horizon->list()->points();
109 for (
auto &p : *points)
118 ui->removeRegionB->setEnabled(
true);
119 ui->toggleCeilingB->setEnabled(
true);
126 connect(ui->toggleCeilingB, SIGNAL(clicked()),
this, SLOT(slotToggleCeiling()));
130 connect(ui->addPointB, SIGNAL(clicked()),
this, SLOT(slotAddPoint()));
131 connect(ui->removePointB, SIGNAL(clicked()),
this, SLOT(slotRemovePoint()));
132 connect(ui->clearPointsB, SIGNAL(clicked()),
this, SLOT(clearPoints()));
133 connect(ui->exportHorizonB, SIGNAL(clicked()),
this, SLOT(exportHorizon()));
134 connect(ui->importHorizonB, SIGNAL(clicked()),
this, SLOT(importHorizon()));
135 connect(ui->selectPointsB, SIGNAL(clicked(
bool)),
this, SLOT(setSelectPoints(
bool)));
140 connect(ui->saveB, SIGNAL(clicked()),
this, SLOT(slotSaveChanges()));
142 if (horizonList->
count() > 0)
144 ui->regionsList->selectionModel()->setCurrentIndex(m_RegionsModel->index(0, 0),
158void HorizonManager::showEvent(
QShowEvent *event)
161 QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0);
164 setupLivePreview(regionItem);
172 Q_UNUSED(deselected);
173 if (livePreview.get() !=
nullptr &&
174 selected.
row() >= 0 &&
175 selected.
row() < livePreview->points()->size())
176 horizonComponent->setSelectedPreviewPoint(selected.
row());
178 horizonComponent->setSelectedPreviewPoint(-1);
186void HorizonManager::setupValidation(
int region)
188 QStandardItem *regionItem = m_RegionsModel->item(region, 0);
190 if (regionItem && regionItem->
rowCount() >= MIN_NUMBER_POINTS)
192 if (validate(region))
194 ui->regionValidation->setPixmap(
196 ui->regionValidation->setEnabled(
true);
197 ui->regionValidation->setToolTip(
i18n(
"Region is valid"));
201 ui->regionValidation->setPixmap(
203 ui->regionValidation->setEnabled(
false);
204 ui->regionValidation->setToolTip(
i18n(
"Region is invalid."));
207 ui->regionValidation->show();
210 ui->regionValidation->hide();
213void HorizonManager::showRegion(
int regionID)
215 if (regionID < 0 || regionID >= m_RegionsModel->rowCount())
219 ui->pointsList->setRootIndex(m_RegionsModel->index(regionID, 0));
220 ui->pointsList->setColumnHidden(0,
true);
222 QStandardItem *regionItem = m_RegionsModel->item(regionID, 0);
225 ui->pointsList->setCurrentIndex(regionItem->
child(regionItem->
rowCount() - 1, 0)->
index());
228 ui->pointsList->setCurrentIndex(QModelIndex());
230 setupValidation(regionID);
232 ui->addPointB->setEnabled(
true);
233 ui->removePointB->setEnabled(
true);
234 ui->selectPointsB->setEnabled(
true);
235 ui->clearPointsB->setEnabled(
true);
236 ui->exportHorizonB->setEnabled(
true);
237 ui->importHorizonB->setEnabled(
true);
239 if (regionItem !=
nullptr)
241 setupLivePreview(regionItem);
249bool HorizonManager::validate(
int regionID)
251 QStandardItem *regionItem = m_RegionsModel->item(regionID, 0);
253 if (regionItem ==
nullptr || regionItem->
rowCount() < MIN_NUMBER_POINTS)
256 for (
int i = 0; i < regionItem->
rowCount(); i++)
268void HorizonManager::removeEmptyRows(
int regionID)
270 QStandardItem *regionItem = m_RegionsModel->item(regionID, 0);
272 if (regionItem ==
nullptr)
275 QList<int> emptyRows;
276 for (
int i = 0; i < regionItem->
rowCount(); i++)
284 std::sort(emptyRows.
begin(), emptyRows.
end(), [](
int a,
int b) ->
bool
288 for (
int i = 0; i < emptyRows.
size(); ++i)
295 QString name =
i18n(
"Region %1", m_RegionsModel->rowCount() + 1);
299void HorizonManager::addRegion(
const QString &name)
301 terminateLivePreview();
303 setPointSelection(
false);
313 showRegion(m_RegionsModel->
rowCount() - 1);
316void HorizonManager::slotToggleCeiling()
318 int regionID = ui->regionsList->currentIndex().row();
319 QStandardItem *regionItem = m_RegionsModel->item(regionID, 0);
340 terminateLivePreview();
342 setPointSelection(
false);
344 int regionID = ui->regionsList->currentIndex().row();
345 deleteRegion(regionID);
348 showRegion(regionID - 1);
349 else if (m_RegionsModel->rowCount() == 0)
351 ui->regionValidation->hide();
352 m_RegionsModel->clear();
356void HorizonManager::deleteRegion(
int regionID)
361 if (regionID < m_RegionsModel->rowCount())
365 SkyMap::Instance()->forceUpdate();
369void HorizonManager::slotClosed()
371 setSelectPoints(
false);
372 terminateLivePreview();
376void HorizonManager::slotSaveChanges()
378 terminateLivePreview();
379 setPointSelection(
false);
381 for (
int i = 0; i < m_RegionsModel->rowCount(); i++)
384 if (validate(i) ==
false)
386 KSNotification::error(
i18n(
"%1 region is invalid.",
392 for (
int i = 0; i < m_RegionsModel->rowCount(); i++)
394 QStandardItem *regionItem = m_RegionsModel->item(i, 0);
397 horizonComponent->removeRegion(regionName);
399 std::shared_ptr<LineList>
list(
new LineList());
401 std::shared_ptr<SkyPoint> p;
403 for (
int j = 0; j < regionItem->
rowCount(); j++)
409 p.reset(
new SkyPoint());
412 p->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->
geo()->lat());
418 horizonComponent->addRegion(regionName, regionItem->
checkState() ==
Qt::Checked ?
true :
false, list, ceiling);
421 horizonComponent->save();
426void HorizonManager::slotSetShownRegion(
QModelIndex idx)
428 showRegion(idx.
row());
435 if (region ==
nullptr)
return;
436 livePreview.reset(
new LineList());
437 const int numPoints = region->
rowCount();
438 for (
int i = 0; i < numPoints; i++)
440 QStandardItem *azItem = region->
child(i, 1);
441 QStandardItem *altItem = region->
child(i, 2);
449 std::shared_ptr<SkyPoint> point(
new SkyPoint());
452 point->HorizontalToEquatorial(KStarsData::Instance()->lst(), KStarsData::Instance()->
geo()->lat());
454 livePreview->append(point);
457 horizonComponent->setLivePreview(livePreview);
460void HorizonManager::addPoint(
const SkyPoint *skyPoint)
462 QStandardItem *region = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0);
463 if (region ==
nullptr)
469 int row = ui->pointsList->currentIndex().
row();
470 if ((row < 0) || (row >= region->
rowCount()))
474 QList<QStandardItem *> pointsList;
475 pointsList <<
new QStandardItem(
"") <<
new QStandardItem(
"") <<
new QStandardItem(
"");
479 ui->pointsList->setCurrentIndex(index);
481 m_RegionsModel->setHorizontalHeaderLabels(QStringList()
482 <<
i18n(
"Region") <<
i18nc(
"Azimuth",
"Az") <<
i18nc(
"Altitude",
"Alt"));
483 ui->pointsList->setColumnHidden(0,
true);
484 ui->pointsList->setRootIndex(region->
index());
488 if (skyPoint !=
nullptr)
490 QStandardItem *az = region->
child(row, 1);
491 QStandardItem *alt = region->
child(row, 2);
496 setupLivePreview(region);
497 slotCurrentPointChanged(ui->pointsList->currentIndex(), ui->pointsList->currentIndex());
502void HorizonManager::addSkyPoint(
SkyPoint * skypoint)
504 if (selectPoints ==
false)
507 SkyPoint pt = *skypoint;
512void HorizonManager::slotAddPoint()
517void HorizonManager::slotRemovePoint()
519 int regionID = ui->regionsList->currentIndex().row();
520 QStandardItem *regionItem = m_RegionsModel->item(regionID, 0);
521 if (regionItem ==
nullptr)
524 int row = ui->pointsList->currentIndex().row();
529 setupValidation(regionID);
531 if (livePreview.get() && row < livePreview->points()->count())
533 livePreview->points()->takeAt(row);
535 if (livePreview->points()->isEmpty())
536 terminateLivePreview();
542void HorizonManager::exportHorizon()
544 QStandardItem *region = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0);
545 if (region ==
nullptr)
550 const int numRows = region->
rowCount();
555 QFile file(filename);
557 QTextStream out(&file);
558 out <<
"# KStars Artificial Horizon export (Alt Az)\n";
561 out << QString(
"%1 %2\n").arg(CEILING_KEYWORD).arg(name);
563 out << QString(
"%1 %2\n").arg(HORIZON_KEYWORD).arg(name);
564 for (
int i = 0; i < numRows; ++i)
568 out << QString(
"%1 %2\n")
575void HorizonManager::importHorizon()
579 if (fileName.
isEmpty())
return;
580 QFile inputFile(fileName);
584 QString
name = QFileInfo(fileName).fileName();
585 bool isCeiling =
false;
588 QTextStream in(&inputFile);
591 const QString line = in.readLine().trimmed();
596 name = line.
mid(strlen(CEILING_KEYWORD));
600 name = line.
mid(strlen(HORIZON_KEYWORD));
604 const QStringList columns = line.
split(
" ");
605 if (columns.
size() != 2 || columns[0].isEmpty() || columns[1].isEmpty())
continue;
614 m_ForceUpdates =
false;
618 for (SkyPoint pt : pts)
620 m_ForceUpdates =
true;
626void HorizonManager::clearPoints()
628 QStandardItem *regionItem = m_RegionsModel->item(ui->regionsList->currentIndex().row(), 0);
636 ui->regionValidation->hide();
639 terminateLivePreview();
642void HorizonManager::setSelectPoints(
bool enable)
644 selectPoints = enable;
645 ui->selectPointsB->clearFocus();
650 bool azOK =
true, altOK =
true;
659 if (std::isnan(azAngle.
Degrees()))
661 if (std::isnan(altAngle.
Degrees()))
664 if ((item->
column() == 1 && azOK ==
false) || (item->
column() == 2 && altOK ==
false))
668 disconnect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(verifyItemValue(QStandardItem*)));
670 connect(m_RegionsModel, SIGNAL(itemChanged(QStandardItem*)),
this, SLOT(verifyItemValue(QStandardItem*)));
673 else if (azOK && altOK)
675 setupLivePreview(item->
parent());
676 setupValidation(ui->regionsList->currentIndex().row());
683void HorizonManager::terminateLivePreview()
685 if (!livePreview.get())
689 horizonComponent->setLivePreview(livePreview);
692void HorizonManager::setPointSelection(
bool enable)
694 selectPoints = enable;
695 ui->selectPointsB->setChecked(enable);
void slotRemoveRegion()
Delete region.
HorizonManager(QWidget *ks)
Constructor.
void slotAddRegion()
Add region.
void forceUpdate(bool now=false)
Recalculates the positions of objects in the sky, and then repaints the sky map.
void forceUpdateNow()
Convenience function; simply calls forceUpdate(true).
The sky coordinates of a point in the sky.
void setAlt(dms alt)
Sets Alt, the Altitude.
void setAz(dms az)
Sets Az, the Azimuth.
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
const double & Degrees() const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
GeoCoordinates geo(const QVariant &location)
QString name(const QVariant &location)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
bool removeRow(int row, const QModelIndex &parent)
QDialog(QWidget *parent, Qt::WindowFlags f)
QString getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QString getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, Options options)
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
qsizetype count() const const
qsizetype size() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
bool disconnect(const QMetaObject::Connection &connection)
virtual bool event(QEvent *e)
QObject * parent() const const
QString tr(const char *sourceText, const char *disambiguation, int n)
void appendRow(QStandardItem *item)
Qt::CheckState checkState() const const
QStandardItem * child(int row, int column) const const
virtual QVariant data(int role) const const
QModelIndex index() const const
void insertRow(int row, QStandardItem *item)
QStandardItem * parent() const const
void removeRows(int row, int count)
int rowCount() const const
void setCheckState(Qt::CheckState state)
void setCheckable(bool checkable)
virtual void setData(const QVariant &value, int role)
void setEnabled(bool enabled)
void appendRow(QStandardItem *item)
QStandardItem * item(int row, int column) const const
virtual int rowCount(const QModelIndex &parent) const const override
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool toBool() const const
QString toString() const const