File indexing completed on 2024-04-14 14:11:35

0001 /*
0002     SPDX-FileCopyrightText: 2002-2003 Pablo de Vicente <vicente@oan.es>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "altvstime.h"
0008 
0009 #include "avtplotwidget.h"
0010 #include "dms.h"
0011 #include "ksalmanac.h"
0012 #include "kstarsdata.h"
0013 #include "kstarsdatetime.h"
0014 #include "ksnumbers.h"
0015 #include "simclock.h"
0016 #include "kssun.h"
0017 #include "dialogs/finddialog.h"
0018 #include "dialogs/locationdialog.h"
0019 #include "geolocation.h"
0020 #include "skyobjects/skypoint.h"
0021 #include "skyobjects/skyobject.h"
0022 #include "skyobjects/starobject.h"
0023 
0024 #include <KLocalizedString>
0025 #include <kplotwidget.h>
0026 
0027 #include <QVBoxLayout>
0028 #include <QFrame>
0029 #include <QDialog>
0030 #include <QPainter>
0031 #include <QtPrintSupport/QPrinter>
0032 #include <QtPrintSupport/QPrintDialog>
0033 
0034 #include "kstars_debug.h"
0035 
0036 AltVsTimeUI::AltVsTimeUI(QWidget *p) : QFrame(p)
0037 {
0038     setupUi(this);
0039 }
0040 
0041 AltVsTime::AltVsTime(QWidget *parent) : QDialog(parent)
0042 {
0043 #ifdef Q_OS_OSX
0044     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0045 #endif
0046 
0047     setWindowTitle(i18nc("@title:window", "Altitude vs. Time"));
0048 
0049     setModal(false);
0050 
0051     QVBoxLayout *topLayout = new QVBoxLayout;
0052     setLayout(topLayout);
0053     topLayout->setContentsMargins(0, 0, 0, 0);
0054     avtUI = new AltVsTimeUI(this);
0055 
0056     // Layers for setting up the plot's priority: the current curve should be above the other curves.
0057     // The Rise/Set/Transit markers should be on top, with highest priority.
0058     avtUI->View->addLayer("currentCurveLayer", avtUI->View->layer("main"), QCustomPlot::limAbove);
0059     avtUI->View->addLayer("markersLayer", avtUI->View->layer("currentCurveLayer"), QCustomPlot::limAbove);
0060 
0061     // Set up the Graph Window:
0062     avtUI->View->setBackground(QBrush(QColor(0, 0, 0)));
0063     avtUI->View->xAxis->grid()->setVisible(false);
0064     avtUI->View->yAxis->grid()->setVisible(false);
0065     QColor axisColor(Qt::white);
0066     QPen axisPen(axisColor, 1);
0067     avtUI->View->xAxis->setBasePen(axisPen);
0068     avtUI->View->xAxis->setTickPen(axisPen);
0069     avtUI->View->xAxis->setSubTickPen(axisPen);
0070     avtUI->View->xAxis->setTickLabelColor(axisColor);
0071     avtUI->View->xAxis->setLabelColor(axisColor);
0072 
0073     avtUI->View->xAxis2->setBasePen(axisPen);
0074     avtUI->View->xAxis2->setTickPen(axisPen);
0075     avtUI->View->xAxis2->setSubTickPen(axisPen);
0076     avtUI->View->xAxis2->setTickLabelColor(axisColor);
0077     avtUI->View->xAxis2->setLabelColor(axisColor);
0078 
0079     avtUI->View->yAxis->setBasePen(axisPen);
0080     avtUI->View->yAxis->setTickPen(axisPen);
0081     avtUI->View->yAxis->setSubTickPen(axisPen);
0082     avtUI->View->yAxis->setTickLabelColor(axisColor);
0083     avtUI->View->yAxis->setLabelColor(axisColor);
0084 
0085     avtUI->View->yAxis2->setBasePen(axisPen);
0086     avtUI->View->yAxis2->setTickPen(axisPen);
0087     avtUI->View->yAxis2->setSubTickPen(axisPen);
0088     avtUI->View->yAxis2->setTickLabelColor(axisColor);
0089     avtUI->View->yAxis2->setLabelColor(axisColor);
0090 
0091     // give the axis some labels:
0092     avtUI->View->xAxis2->setLabel(i18n("Local Sidereal Time"));
0093     avtUI->View->xAxis2->setVisible(true);
0094     avtUI->View->yAxis2->setVisible(true);
0095     avtUI->View->yAxis2->setTickLength(0, 0);
0096     avtUI->View->xAxis->setLabel(i18n("Local Time"));
0097     avtUI->View->yAxis->setLabel(i18n("Altitude"));
0098     avtUI->View->xAxis->setRange(43200, 129600);
0099     avtUI->View->xAxis2->setRange(61200, 147600);
0100 
0101     // configure the bottom axis to show time instead of number:
0102     QSharedPointer<QCPAxisTickerTime> xAxisTimeTicker(new QCPAxisTickerTime);
0103     xAxisTimeTicker->setTimeFormat("%h:%m");
0104     xAxisTimeTicker->setTickCount(12);
0105     xAxisTimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
0106     xAxisTimeTicker->setTickOrigin(Qt::UTC);
0107     avtUI->View->xAxis->setTicker(xAxisTimeTicker);
0108 
0109     // configure the top axis to show time instead of number:
0110     QSharedPointer<QCPAxisTickerTime> xAxis2TimeTicker(new QCPAxisTickerTime);
0111     xAxis2TimeTicker->setTimeFormat("%h:%m");
0112     xAxis2TimeTicker->setTickCount(12);
0113     xAxis2TimeTicker->setTickStepStrategy(QCPAxisTicker::tssReadability);
0114     xAxis2TimeTicker->setTickOrigin(Qt::UTC);
0115     avtUI->View->xAxis2->setTicker(xAxis2TimeTicker);
0116 
0117     // set up the Zoom/Pan features for the Top X Axis
0118     avtUI->View->axisRect()->setRangeDragAxes(avtUI->View->xAxis2, avtUI->View->yAxis);
0119     avtUI->View->axisRect()->setRangeZoomAxes(avtUI->View->xAxis2, avtUI->View->yAxis);
0120 
0121     // set up the margins, for a nice view
0122     avtUI->View->axisRect()->setAutoMargins(QCP::msBottom | QCP::msLeft | QCP::msTop);
0123     avtUI->View->axisRect()->setMargins(QMargins(0, 0, 7, 0));
0124 
0125     // set up the interaction set:
0126     avtUI->View->setInteraction(QCP::iRangeZoom, true);
0127     avtUI->View->setInteraction(QCP::iRangeDrag, true);
0128 
0129     // set up the selection tolerance for checking if a certain graph is or not selected:
0130     avtUI->View->setSelectionTolerance(5);
0131 
0132     // draw the gradient:
0133     drawGradient();
0134 
0135     // set up the background image:
0136     background = new QCPItemPixmap(avtUI->View);
0137     background->setPixmap(*gradient);
0138     background->topLeft->setType(QCPItemPosition::ptPlotCoords);
0139     background->bottomRight->setType(QCPItemPosition::ptPlotCoords);
0140     background->setScaled(true, Qt::IgnoreAspectRatio);
0141     background->setLayer("background");
0142     background->setVisible(true);
0143 
0144     avtUI->raBox->setUnits(dmsBox::HOURS);
0145     avtUI->decBox->setUnits(dmsBox::DEGREES);
0146 
0147     //FIXME:
0148     //Doesn't make sense to manually adjust long/lat unless we can modify TZ also
0149     avtUI->longBox->setReadOnly(true);
0150     avtUI->latBox->setReadOnly(true);
0151 
0152     topLayout->addWidget(avtUI);
0153 
0154     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close);
0155     topLayout->addWidget(buttonBox);
0156     connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
0157 
0158     QPushButton *printB = new QPushButton(QIcon::fromTheme("document-print"), i18n("&Print..."));
0159     printB->setToolTip(i18n("Print the Altitude vs. time plot"));
0160     buttonBox->addButton(printB, QDialogButtonBox::ActionRole);
0161     connect(printB, SIGNAL(clicked()), this, SLOT(slotPrint()));
0162 
0163     geo = KStarsData::Instance()->geo();
0164 
0165     DayOffset = 0;
0166     // set up the initial minimum and maximum altitude
0167     minAlt = 0;
0168     maxAlt = 0;
0169     showCurrentDate();
0170     if (getDate().time().hour() > 12)
0171         DayOffset = 1;
0172 
0173     avtUI->longBox->show(geo->lng());
0174     avtUI->latBox->show(geo->lat());
0175 
0176     computeSunRiseSetTimes();
0177     setLSTLimits();
0178     setDawnDusk();
0179 
0180     connect(avtUI->View->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onYRangeChanged(QCPRange)));
0181     connect(avtUI->View->xAxis2, SIGNAL(rangeChanged(QCPRange)), this, SLOT(onXRangeChanged(QCPRange)));
0182     connect(avtUI->View, SIGNAL(plottableClick(QCPAbstractPlottable*, int, QMouseEvent*)), this,
0183             SLOT(plotMousePress(QCPAbstractPlottable*, int, QMouseEvent*)));
0184     connect(avtUI->View, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseOverLine(QMouseEvent*)));
0185 
0186     connect(avtUI->browseButton, SIGNAL(clicked()), this, SLOT(slotBrowseObject()));
0187     connect(avtUI->cityButton, SIGNAL(clicked()), this, SLOT(slotChooseCity()));
0188     connect(avtUI->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateDateLoc()));
0189     connect(avtUI->clearButton, SIGNAL(clicked()), this, SLOT(slotClear()));
0190     connect(avtUI->addButton, SIGNAL(clicked()), this, SLOT(slotAddSource()));
0191     connect(avtUI->nameBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
0192     connect(avtUI->raBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
0193     connect(avtUI->decBox, SIGNAL(returnPressed()), this, SLOT(slotAddSource()));
0194     connect(avtUI->clearFieldsButton, SIGNAL(clicked()), this, SLOT(slotClearBoxes()));
0195     connect(avtUI->longBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
0196     connect(avtUI->latBox, SIGNAL(returnPressed()), this, SLOT(slotAdvanceFocus()));
0197     connect(avtUI->PlotList, SIGNAL(currentRowChanged(int)), this, SLOT(slotHighlight(int)));
0198     connect(avtUI->computeButton, SIGNAL(clicked()), this, SLOT(slotComputeAltitudeByTime()));
0199     connect(avtUI->riseButton, SIGNAL(clicked()), this, SLOT(slotMarkRiseTime()));
0200     connect(avtUI->setButton, SIGNAL(clicked()), this, SLOT(slotMarkSetTime()));
0201     connect(avtUI->transitButton, SIGNAL(clicked()), this, SLOT(slotMarkTransitTime()));
0202 
0203     // Set up the Rise/Set/Transit buttons' icons:
0204 
0205     QPixmap redButton(100, 100);
0206     redButton.fill(Qt::transparent);
0207     QPainter p;
0208     p.begin(&redButton);
0209     p.setRenderHint(QPainter::Antialiasing, true);
0210     QPen pen(Qt::red, 2);
0211     p.setPen(pen);
0212     QBrush brush(Qt::red);
0213     p.setBrush(brush);
0214     p.drawEllipse(15, 15, 80, 80);
0215     p.end();
0216 
0217     QPixmap blueButton(100, 100);
0218     blueButton.fill(Qt::transparent);
0219     QPainter p1;
0220     p1.begin(&blueButton);
0221     p1.setRenderHint(QPainter::Antialiasing, true);
0222     QPen pen1(Qt::blue, 2);
0223     p1.setPen(pen1);
0224     QBrush brush1(Qt::blue);
0225     p1.setBrush(brush1);
0226     p1.drawEllipse(15, 15, 80, 80);
0227     p1.end();
0228 
0229     QPixmap greenButton(100, 100);
0230     greenButton.fill(Qt::transparent);
0231     QPainter p2;
0232     p2.begin(&greenButton);
0233     p2.setRenderHint(QPainter::Antialiasing, true);
0234     QPen pen2(Qt::green, 2);
0235     p2.setPen(pen2);
0236     QBrush brush2(Qt::green);
0237     p2.setBrush(brush2);
0238     p2.drawEllipse(15, 15, 80, 80);
0239     p2.end();
0240 
0241     avtUI->riseButton->setIcon(QIcon(redButton));
0242     avtUI->setButton->setIcon(QIcon(blueButton));
0243     avtUI->transitButton->setIcon(QIcon(greenButton));
0244 
0245     setMouseTracking(true);
0246 }
0247 
0248 AltVsTime::~AltVsTime()
0249 {
0250     //WARNING: need to delete deleteList items!
0251 }
0252 void AltVsTime::slotAddSource()
0253 {
0254 
0255     SkyObject *obj = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
0256     if (!obj)
0257     {
0258         QString name = FindDialog::processSearchText(avtUI->nameBox->text());
0259         obj = KStarsData::Instance()->objectNamed(name);
0260     }
0261     if (obj)
0262     {
0263         //An object with the current name exists.  If the object is not already
0264         //in the avt list, add it.
0265         bool found = false;
0266         foreach (SkyObject *o, pList)
0267         {
0268             //if ( o->name() == obj->name() ) {
0269             if (getObjectName(o, false) == getObjectName(obj, false))
0270             {
0271                 found = true;
0272                 break;
0273             }
0274         }
0275         if (!found)
0276             processObject(obj);
0277     }
0278     else
0279     {
0280         //Object with the current name doesn't exist.  It's possible that the
0281         //user is trying to add a custom object.  Assume this is the case if
0282         //the RA and Dec fields are filled in.
0283 
0284         if (!avtUI->nameBox->text().isEmpty() && !avtUI->raBox->text().isEmpty() && !avtUI->decBox->text().isEmpty())
0285         {
0286             bool okRA, okDec;
0287             dms newRA  = avtUI->raBox->createDms(&okRA);
0288             dms newDec = avtUI->decBox->createDms(&okDec);
0289             if (!okRA || !okDec)
0290                 return;
0291 
0292             //If the epochName is blank (or any non-double), we assume J2000
0293             //Otherwise, precess to J2000.
0294             KStarsDateTime dt;
0295             dt.setFromEpoch(getEpoch(avtUI->epochName->text()));
0296             long double jd = dt.djd();
0297             if (jd != J2000)
0298             {
0299                 SkyPoint ptest(newRA, newDec);
0300                 //ptest.precessFromAnyEpoch(jd, J2000);
0301                 ptest.catalogueCoord(jd);
0302                 newRA.setH(ptest.ra().Hours());
0303                 newDec.setD(ptest.dec().Degrees());
0304             }
0305 
0306             //make sure the coords do not already exist from another object
0307             bool found = false;
0308             foreach (SkyObject *p, pList)
0309             {
0310                 //within an arcsecond?
0311                 if (fabs(newRA.Degrees() - p->ra().Degrees()) < 0.0003 &&
0312                         fabs(newDec.Degrees() - p->dec().Degrees()) < 0.0003)
0313                 {
0314                     found = true;
0315                     break;
0316                 }
0317             }
0318             if (!found)
0319             {
0320                 SkyObject *obj = new SkyObject(8, newRA, newDec, 1.0, avtUI->nameBox->text());
0321                 deleteList.append(obj); //this object will be deleted when window is destroyed
0322                 processObject(obj);
0323             }
0324         }
0325 
0326         //If the Ra and Dec boxes are filled, but the name field is empty,
0327         //move input focus to nameBox.  If either coordinate box is empty,
0328         //move focus there
0329         if (avtUI->nameBox->text().isEmpty())
0330         {
0331             avtUI->nameBox->QWidget::setFocus();
0332         }
0333         if (avtUI->raBox->text().isEmpty())
0334         {
0335             avtUI->raBox->QWidget::setFocus();
0336         }
0337         else
0338         {
0339             if (avtUI->decBox->text().isEmpty())
0340                 avtUI->decBox->QWidget::setFocus();
0341         }
0342     }
0343 
0344     avtUI->View->update();
0345 }
0346 
0347 //Use find dialog to choose an object
0348 void AltVsTime::slotBrowseObject()
0349 {
0350     if (FindDialog::Instance()->exec() == QDialog::Accepted)
0351     {
0352         SkyObject *o = FindDialog::Instance()->targetObject();
0353         processObject(o);
0354     }
0355 
0356     avtUI->View->update();
0357     avtUI->View->replot();
0358 }
0359 
0360 void AltVsTime::processObject(SkyObject *o, bool forceAdd)
0361 {
0362     if (!o)
0363         return;
0364 
0365     KSNumbers *num    = new KSNumbers(getDate().djd());
0366     KSNumbers *oldNum = nullptr;
0367 
0368     //If the object is in the solar system, recompute its position for the given epochLabel
0369     KStarsData *data = KStarsData::Instance();
0370     if (o->isSolarSystem())
0371     {
0372         oldNum = new KSNumbers(data->ut().djd());
0373         o->updateCoords(num, true, geo->lat(), data->lst(), true);
0374     }
0375 
0376     //precess coords to target epoch
0377     o->updateCoordsNow(num);
0378 
0379     // vector used for computing the points needed for drawing the graph
0380     QVector<double> y(100), t(100);
0381 
0382     //If this point is not in list already, add it to list
0383     bool found(false);
0384     foreach (SkyObject *p, pList)
0385     {
0386         if (o->ra().Degrees() == p->ra().Degrees() && o->dec().Degrees() == p->dec().Degrees())
0387         {
0388             found = true;
0389             break;
0390         }
0391     }
0392     if (found && !forceAdd)
0393     {
0394         qCWarning(KSTARS) << "This point is already displayed; It will not be duplicated.";
0395     }
0396     else
0397     {
0398         pList.append(o);
0399 
0400         // make sure existing curves are thin and red:
0401 
0402         for (int i = 0; i < avtUI->View->graphCount(); i++)
0403         {
0404             if (avtUI->View->graph(i)->pen().color() == Qt::white)
0405             {
0406                 avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
0407             }
0408         }
0409 
0410         // SET up the curve's name
0411         avtUI->View->addGraph()->setName(o->name());
0412 
0413         // compute the current graph:
0414         // time range: 24h
0415 
0416         int offset = 3;
0417         for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
0418         {
0419             y[i] = findAltitude(o, h);
0420             if (y[i] > maxAlt)
0421                 maxAlt = y[i];
0422             if (y[i] < minAlt)
0423                 minAlt = y[i];
0424             t[i] = i * 900 + 43200;
0425             avtUI->View->graph(avtUI->View->graphCount() - 1)->addData(t[i], y[i]);
0426         }
0427         avtUI->View->graph(avtUI->View->graphCount() - 1)->setPen(QPen(Qt::white, 3));
0428 
0429         // Go into initial state: without Zoom/Pan
0430         avtUI->View->xAxis->setRange(43200, 129600);
0431         avtUI->View->xAxis2->setRange(61200, 147600);
0432         if (abs(minAlt) > maxAlt)
0433             maxAlt = abs(minAlt);
0434         else
0435             minAlt = -maxAlt;
0436 
0437         avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
0438 
0439         // Update background coordinates:
0440         background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
0441         background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
0442 
0443         avtUI->View->replot();
0444 
0445         avtUI->PlotList->addItem(getObjectName(o));
0446         avtUI->PlotList->setCurrentRow(avtUI->PlotList->count() - 1);
0447         avtUI->raBox->show(o->ra());
0448         avtUI->decBox->show(o->dec());
0449         avtUI->nameBox->setText(getObjectName(o));
0450 
0451         //Set epochName to epoch shown in date tab
0452         avtUI->epochName->setText(QString().setNum(getDate().epoch()));
0453     }
0454     //qCDebug() << "Currently, there are " << avtUI->View->graphCount() << " objects displayed.";
0455 
0456     //restore original position
0457     if (o->isSolarSystem())
0458     {
0459         o->updateCoords(oldNum, true, data->geo()->lat(), data->lst(), true);
0460         delete oldNum;
0461     }
0462     o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0463     delete num;
0464 }
0465 
0466 double AltVsTime::findAltitude(SkyPoint *p, double hour)
0467 {
0468     hour += 24.0 * DayOffset;
0469 
0470     //getDate converts the user-entered local time to UT
0471     KStarsDateTime ut = getDate().addSecs(hour * 3600.0);
0472 
0473     CachingDms LST = geo->GSTtoLST(ut.gst());
0474     p->EquatorialToHorizontal(&LST, geo->lat());
0475     return p->alt().Degrees();
0476 }
0477 
0478 void AltVsTime::slotHighlight(int row)
0479 {
0480     if (row < 0)
0481         return;
0482 
0483     int rowIndex = 0;
0484     //highlight the curve of the selected object
0485     for (int i = 0; i < avtUI->View->graphCount(); i++)
0486     {
0487         if (i == row)
0488             rowIndex = row;
0489         else
0490         {
0491             avtUI->View->graph(i)->setPen(QPen(Qt::red, 2));
0492             avtUI->View->graph(i)->setLayer("main");
0493         }
0494     }
0495     avtUI->View->graph(rowIndex)->setPen(QPen(Qt::white, 3));
0496     avtUI->View->graph(rowIndex)->setLayer("currentCurveLayer");
0497     avtUI->View->update();
0498     avtUI->View->replot();
0499 
0500     if (row >= 0 && row < pList.size())
0501     {
0502         SkyObject *p = pList.at(row);
0503         avtUI->raBox->show(p->ra());
0504         avtUI->decBox->show(p->dec());
0505         avtUI->nameBox->setText(avtUI->PlotList->currentItem()->text());
0506     }
0507 
0508     SkyObject *selectedObject = KStarsData::Instance()->objectNamed(avtUI->nameBox->text());
0509     const KStarsDateTime &ut  = KStarsData::Instance()->ut();
0510     if (selectedObject)
0511     {
0512         QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
0513         if (rt.isValid() == false)
0514         {
0515             avtUI->riseButton->setEnabled(false);
0516             avtUI->setButton->setEnabled(false);
0517         }
0518         else
0519         {
0520             avtUI->riseButton->setEnabled(true);
0521             avtUI->setButton->setEnabled(true);
0522         }
0523     }
0524 }
0525 
0526 void AltVsTime::onXRangeChanged(const QCPRange &range)
0527 {
0528     QCPRange aux = avtUI->View->xAxis2->range();
0529     avtUI->View->xAxis->setRange(aux -= 18000);
0530     avtUI->View->xAxis2->setRange(range.bounded(61200, 147600));
0531     // if ZOOM is detected then remove the gold lines that indicate current position:
0532     if (avtUI->View->xAxis->range().size() != 86400)
0533     {
0534         // Refresh the background:
0535         background->setScaled(false);
0536         background->setScaled(true, Qt::IgnoreAspectRatio);
0537         background->setPixmap(*gradient);
0538 
0539         avtUI->View->update();
0540         avtUI->View->replot();
0541     }
0542 }
0543 
0544 void AltVsTime::onYRangeChanged(const QCPRange &range)
0545 {
0546     int offset = 3;
0547     avtUI->View->yAxis->setRange(range.bounded(minAlt - offset, maxAlt + offset));
0548 }
0549 
0550 void AltVsTime::plotMousePress(QCPAbstractPlottable *abstractPlottable, int dataIndex, QMouseEvent *event)
0551 {
0552     //Do we need this?
0553     Q_UNUSED(dataIndex);
0554 
0555     if (event->button() == Qt::RightButton)
0556     {
0557         QCPAbstractPlottable *plottable = abstractPlottable;
0558         if (plottable)
0559         {
0560             double x = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
0561             double y = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
0562 
0563             QCPGraph *graph = qobject_cast<QCPGraph *>(plottable);
0564 
0565             if (graph)
0566             {
0567                 double yValue = y;
0568                 double xValue = x;
0569 
0570                 // Compute time value:
0571                 QTime localTime(0, 0, 0, 0);
0572                 QTime localSiderealTime(5, 0, 0, 0);
0573 
0574                 localTime         = localTime.addSecs(int(xValue));
0575                 localSiderealTime = localSiderealTime.addSecs(int(xValue));
0576 
0577                 QToolTip::hideText();
0578                 QToolTip::showText(event->globalPos(),
0579                                    i18n("<table>"
0580                                         "<tr>"
0581                                         "<th colspan=\"2\">%1</th>"
0582                                         "</tr>"
0583                                         "<tr>"
0584                                         "<td>LST:   </td>"
0585                                         "<td>%3</td>"
0586                                         "</tr>"
0587                                         "<tr>"
0588                                         "<td>LT:   </td>"
0589                                         "<td>%2</td>"
0590                                         "</tr>"
0591                                         "<tr>"
0592                                         "<td>Altitude:   </td>"
0593                                         "<td>%4</td>"
0594                                         "</tr>"
0595                                         "</table>",
0596                                         graph->name().isEmpty() ? "???" : graph->name(),
0597                                         localTime.toString(),
0598                                         localSiderealTime.toString(),
0599                                         QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
0600                                    avtUI->View, avtUI->View->rect());
0601             }
0602         }
0603     }
0604 }
0605 
0606 //move input focus to the next logical widget
0607 void AltVsTime::slotAdvanceFocus()
0608 {
0609     if (sender()->objectName() == QString("nameBox"))
0610         avtUI->addButton->setFocus();
0611     if (sender()->objectName() == QString("raBox"))
0612         avtUI->decBox->setFocus();
0613     if (sender()->objectName() == QString("decbox"))
0614         avtUI->addButton->setFocus();
0615     if (sender()->objectName() == QString("longBox"))
0616         avtUI->latBox->setFocus();
0617     if (sender()->objectName() == QString("latBox"))
0618         avtUI->updateButton->setFocus();
0619 }
0620 
0621 void AltVsTime::slotClear()
0622 {
0623     pList.clear();
0624     //Need to delete the pointers in deleteList
0625     while (!deleteList.isEmpty())
0626         delete deleteList.takeFirst();
0627 
0628     avtUI->PlotList->clear();
0629     avtUI->nameBox->clear();
0630     avtUI->raBox->clear();
0631     avtUI->decBox->clear();
0632     avtUI->epochName->clear();
0633     // remove all graphs from the plot:
0634     avtUI->View->clearGraphs();
0635     // we remove all the dots (rise/set/transit) from the chart
0636     // without removing the background image
0637     int indexItem = 0, noItems = avtUI->View->itemCount();
0638     QCPAbstractItem *item;
0639     QCPItemPixmap *background;
0640     // remove every item at a time:
0641     while (noItems > 1 && indexItem < noItems)
0642     {
0643         // test if the current item is the background:
0644         item       = avtUI->View->item(indexItem);
0645         background = qobject_cast<QCPItemPixmap *>(item);
0646         if (background)
0647             indexItem++;
0648         else
0649         {
0650             // if not, then remove this item:
0651             avtUI->View->removeItem(indexItem);
0652             noItems--;
0653         }
0654     }
0655     // update & replot the chart:
0656     avtUI->View->update();
0657     avtUI->View->replot();
0658 }
0659 
0660 void AltVsTime::slotClearBoxes()
0661 {
0662     avtUI->nameBox->clear();
0663     avtUI->raBox->clear();
0664     avtUI->decBox->clear();
0665     avtUI->epochName->clear();
0666 }
0667 
0668 void AltVsTime::slotComputeAltitudeByTime()
0669 {
0670     if (avtUI->PlotList->currentRow() < 0)
0671         return;
0672 
0673     SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
0674     if (selectedObject == nullptr)
0675     {
0676         if (avtUI->PlotList->currentItem())
0677             qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find" << avtUI->PlotList->currentItem()->text();
0678         else
0679         {
0680             qCWarning(KSTARS) << "slotComputeAltitudeByTime: Unable to find item";
0681         }
0682         return;
0683     }
0684 
0685     // Get Local Date & Time
0686     KStarsDateTime lt = KStarsDateTime(avtUI->DateWidget->date(), avtUI->timeSpin->time(), Qt::LocalTime);
0687     // Convert to UT
0688     KStarsDateTime ut = geo->LTtoUT(lt);
0689     // Get LST from GST
0690     CachingDms LST = geo->GSTtoLST(ut.gst());
0691     SkyObject *tempObject = selectedObject->clone();
0692     // Update coords
0693     KSNumbers num(ut.djd());
0694     tempObject->updateCoords(&num, true, geo->lat(), &LST);
0695     // Find Horizontal coordinates from LST & Latitude
0696     selectedObject->EquatorialToHorizontal(&LST, geo->lat());
0697 
0698     // Set altitude
0699     avtUI->altitudeBox->setText(selectedObject->altRefracted().toDMSString(true));
0700 
0701     delete (tempObject);
0702 }
0703 
0704 void AltVsTime::slotMarkRiseTime()
0705 {
0706     if (avtUI->PlotList->currentRow() < 0)
0707         return;
0708 
0709     const KStarsDateTime &ut  = KStarsData::Instance()->ut();
0710     SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
0711     if (selectedObject == nullptr)
0712     {
0713         qCWarning(KSTARS) << "Mark Rise Time: Unable to find" << avtUI->PlotList->currentItem()->text();
0714         return;
0715     }
0716 
0717     QCPItemTracer *riseTimeTracer;
0718     // check if at least one graph exists in the plot
0719     if (avtUI->View->graphCount() > 0)
0720     {
0721         double time = 0;
0722         double hours, minutes;
0723 
0724         QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
0725 
0726         QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
0727         // mark the Rise time with a solid red circle
0728         if (rt.isValid() && selectedGraph)
0729         {
0730             hours   = rt.hour();
0731             minutes = rt.minute();
0732             if (hours < 12)
0733                 hours += 24;
0734             time           = hours * 3600 + minutes * 60;
0735             riseTimeTracer = new QCPItemTracer(avtUI->View);
0736             riseTimeTracer->setLayer("markersLayer");
0737             riseTimeTracer->setGraph(selectedGraph);
0738             riseTimeTracer->setInterpolating(true);
0739             riseTimeTracer->setStyle(QCPItemTracer::tsCircle);
0740             riseTimeTracer->setPen(QPen(Qt::red));
0741             riseTimeTracer->setBrush(Qt::red);
0742             riseTimeTracer->setSize(10);
0743             riseTimeTracer->setGraphKey(time);
0744             riseTimeTracer->setVisible(true);
0745             avtUI->View->update();
0746             avtUI->View->replot();
0747         }
0748     }
0749 }
0750 
0751 void AltVsTime::slotMarkSetTime()
0752 {
0753     if (avtUI->PlotList->currentRow() < 0)
0754         return;
0755 
0756     const KStarsDateTime &ut  = KStarsData::Instance()->ut();
0757     SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
0758     if (selectedObject == nullptr)
0759     {
0760         qCWarning(KSTARS) << "Mark Set Time: Unable to find" << avtUI->PlotList->currentItem()->text();
0761         return;
0762     }
0763     QCPItemTracer *setTimeTracer;
0764     // check if at least one graph exists in the plot
0765     if (avtUI->View->graphCount() > 0)
0766     {
0767         double time = 0;
0768         double hours, minutes;
0769 
0770         QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
0771 
0772         QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
0773         //If set time is before rise time, use set time for tomorrow
0774         QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time
0775         if (st < rt)
0776             st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time
0777         // mark the Set time with a solid blue circle
0778         if (rt.isValid())
0779         {
0780             hours   = st.hour();
0781             minutes = st.minute();
0782             if (hours < 12)
0783                 hours += 24;
0784             time          = hours * 3600 + minutes * 60;
0785             setTimeTracer = new QCPItemTracer(avtUI->View);
0786             setTimeTracer->setLayer("markersLayer");
0787             setTimeTracer->setGraph(selectedGraph);
0788             setTimeTracer->setInterpolating(true);
0789             setTimeTracer->setStyle(QCPItemTracer::tsCircle);
0790             setTimeTracer->setPen(QPen(Qt::blue));
0791             setTimeTracer->setBrush(Qt::blue);
0792             setTimeTracer->setSize(10);
0793             setTimeTracer->setGraphKey(time);
0794             setTimeTracer->setVisible(true);
0795             avtUI->View->update();
0796             avtUI->View->replot();
0797         }
0798     }
0799 }
0800 
0801 void AltVsTime::slotMarkTransitTime()
0802 {
0803     if (avtUI->PlotList->currentRow() < 0)
0804         return;
0805 
0806     const KStarsDateTime &ut  = KStarsData::Instance()->ut();
0807     SkyObject *selectedObject = pList.at(avtUI->PlotList->currentRow());
0808     if (selectedObject == nullptr)
0809     {
0810         qCWarning(KSTARS) << "Mark Transit Time: Unable to find" << avtUI->PlotList->currentItem()->text();
0811         return;
0812     }
0813     QCPItemTracer *transitTimeTracer;
0814     // check if at least one graph exists in the plot
0815     if (avtUI->View->graphCount() > 0)
0816     {
0817         double time = 0;
0818         double hours, minutes;
0819 
0820         QCPGraph *selectedGraph = avtUI->View->graph(avtUI->PlotList->currentRow());
0821 
0822         QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time
0823         //If transit time is before rise time, use transit time for tomorrow
0824         QTime tt = selectedObject->transitTime(ut, geo);
0825 
0826         if (tt < rt)
0827             tt = selectedObject->transitTime(ut.addDays(1), geo);
0828         // mark the Transit time with a solid green circle
0829         hours   = tt.hour();
0830         minutes = tt.minute();
0831         if (hours < 12)
0832             hours += 24;
0833         time              = hours * 3600 + minutes * 60;
0834         transitTimeTracer = new QCPItemTracer(avtUI->View);
0835         transitTimeTracer->setLayer("markersLayer");
0836         transitTimeTracer->setGraph(selectedGraph);
0837         transitTimeTracer->setInterpolating(true);
0838         transitTimeTracer->setStyle(QCPItemTracer::tsCircle);
0839         transitTimeTracer->setPen(QPen(Qt::green));
0840         transitTimeTracer->setBrush(Qt::green);
0841         transitTimeTracer->setSize(10);
0842         transitTimeTracer->setGraphKey(time);
0843         transitTimeTracer->setVisible(true);
0844         avtUI->View->update();
0845         avtUI->View->replot();
0846     }
0847 }
0848 
0849 void AltVsTime::computeSunRiseSetTimes()
0850 {
0851     //Determine the time of sunset and sunrise for the desired date and location
0852     //expressed as doubles, the fraction of a full day.
0853 
0854     /* KSAlmanac ksal(getDate(), geo); */
0855 
0856     /* ... */
0857 }
0858 
0859 //FIXME
0860 /*
0861 void AltVsTime::mouseOverLine(QMouseEvent *event){
0862     // Get the mouse position's coordinates relative to axes:
0863     double x = avtUI->View->xAxis->pixelToCoord(event->pos().x());
0864     double y = avtUI->View->yAxis->pixelToCoord(event->pos().y());
0865     // Save the actual values:
0866     double yValue = y;
0867     double xValue = x;
0868     // The offset used for the Y axis: top/bottom
0869     int offset = 3;
0870     // Compute the Y axis maximum value:
0871     int yAxisMaxValue = maxAlt + offset;
0872     // Compute the X axis minimum and maximum values:
0873     int xAxisMinValue = 43200;
0874     int xAxisMaxValue = 129600;
0875     // Ignore the upper and left margins:
0876     y = yAxisMaxValue - y;
0877     x -= xAxisMinValue;
0878     // We make a copy to gradient background in order to have one set of lines at a time:
0879     // Otherwise, the chart would have been covered by lines
0880     QPixmap copy = gradient->copy(gradient->rect());
0881     // If ZOOM is not active, then draw the gold lines that indicate current mouse pisition:
0882     if(avtUI->View->xAxis->range().size() == 86400){
0883         QPainter p;
0884 
0885         p.begin(&copy);
0886         p.setPen( QPen( QBrush("gold"), 2, Qt::SolidLine ) );
0887 
0888         // Get the gradient background's width and height:
0889         int pW = gradient->rect().width();
0890         int pH = gradient->rect().height();
0891 
0892         // Compute the real coordinates within the chart:
0893         y = (y*pH/2)/yAxisMaxValue;
0894         x = (x*pW)/(xAxisMaxValue-xAxisMinValue);
0895 
0896         // Draw the horizontal line (altitude):
0897         p.drawLine( QLineF( 0.5, y, avtUI->View->rect().width()-0.5,y ) );
0898         // Draw the altitude value:
0899         p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
0900         p.drawText( 25, y + 15, QString::number(yValue,'f',2) + QChar(176) );
0901         p.setPen( QPen( QBrush("gold"), 1, Qt::SolidLine ) );
0902         // Draw the vertical line (time):
0903         p.drawLine( QLineF( x, 0.5, x, avtUI->View->rect().height()-0.5 ) );
0904         // Compute and draw the time value:
0905         QTime localTime(0,0,0,0);
0906         localTime = localTime.addSecs(int(xValue));
0907         p.save();
0908         p.translate( x + 10, pH - 20 );
0909         p.rotate(-90);
0910         p.setPen( QPen( QBrush("gold"), 3, Qt::SolidLine ) );
0911         p.drawText( 5, 5, QLocale().toString( localTime, QLocale::ShortFormat ) ); // short format necessary to avoid false time-zone labeling
0912         p.restore();
0913         p.end();
0914     }
0915     // Refresh the background:
0916     background->setScaled(false);
0917     background->setScaled(true, Qt::IgnoreAspectRatio);
0918     background->setPixmap(copy);
0919 
0920     avtUI->View->update();
0921     avtUI->View->replot();
0922 }
0923 */
0924 
0925 void AltVsTime::mouseOverLine(QMouseEvent *event)
0926 {
0927     double x                            = avtUI->View->xAxis->pixelToCoord(event->localPos().x());
0928     double y                            = avtUI->View->yAxis->pixelToCoord(event->localPos().y());
0929     QCPAbstractPlottable *abstractGraph = avtUI->View->plottableAt(event->pos(), false);
0930     QCPGraph *graph                     = qobject_cast<QCPGraph *>(abstractGraph);
0931 
0932     if (x > avtUI->View->xAxis->range().lower && x < avtUI->View->xAxis->range().upper)
0933         if (y > avtUI->View->yAxis->range().lower && y < avtUI->View->yAxis->range().upper)
0934         {
0935             if (graph)
0936             {
0937                 double yValue = y;
0938                 double xValue = x;
0939 
0940                 // Compute time value:
0941                 QTime localTime(0, 0, 0, 0);
0942                 QTime localSiderealTime(5, 0, 0, 0);
0943 
0944                 localTime         = localTime.addSecs(int(xValue));
0945                 localSiderealTime = localSiderealTime.addSecs(int(xValue));
0946 
0947                 QToolTip::hideText();
0948                 QToolTip::showText(event->globalPos(),
0949                                    i18n("<table>"
0950                                         "<tr>"
0951                                         "<th colspan=\"2\">%1</th>"
0952                                         "</tr>"
0953                                         "<tr>"
0954                                         "<td>LST:   </td>"
0955                                         "<td>%3</td>"
0956                                         "</tr>"
0957                                         "<tr>"
0958                                         "<td>LT:   </td>"
0959                                         "<td>%2</td>"
0960                                         "</tr>"
0961                                         "<tr>"
0962                                         "<td>Altitude:   </td>"
0963                                         "<td>%4</td>"
0964                                         "</tr>"
0965                                         "</table>",
0966                                         graph->name().isEmpty() ? "???" : graph->name(),
0967                                         localTime.toString(), localSiderealTime.toString(),
0968                                         QString::number(yValue, 'f', 2) + ' ' + QChar(176)),
0969                                    avtUI->View, avtUI->View->rect());
0970             }
0971             else
0972                 QToolTip::hideText();
0973         }
0974 
0975     avtUI->View->update();
0976     avtUI->View->replot();
0977 }
0978 
0979 void AltVsTime::slotUpdateDateLoc()
0980 {
0981     KStarsData *data     = KStarsData::Instance();
0982     KStarsDateTime today = getDate();
0983     KSNumbers *num       = new KSNumbers(today.djd());
0984     KSNumbers *oldNum    = nullptr;
0985     CachingDms LST       = geo->GSTtoLST(today.gst());
0986 
0987     //First determine time of sunset and sunrise
0988     computeSunRiseSetTimes();
0989     // Determine dawn/dusk time and min/max sun elevation
0990     setDawnDusk();
0991 
0992     for (int i = 0; i < pList.count(); ++i)
0993     {
0994         SkyObject *o = pList.at(i);
0995         if (o)
0996         {
0997             //If the object is in the solar system, recompute its position for the given date
0998             if (o->isSolarSystem())
0999             {
1000                 oldNum = new KSNumbers(data->ut().djd());
1001                 o->updateCoords(num, true, geo->lat(), &LST, true);
1002             }
1003 
1004             //precess coords to target epoch
1005             o->updateCoordsNow(num);
1006 
1007             //update pList entry
1008             pList.replace(i, o);
1009 
1010             // We are creating a new data set (time, altitude) for the new date:
1011             QVector<double> time_dataSet, altitude_dataSet;
1012             double point_altitudeValue, point_timeValue;
1013             // compute the new graph values:
1014             // time range: 24h
1015             int offset = 3;
1016             for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
1017             {
1018                 point_altitudeValue = findAltitude(o, h);
1019                 altitude_dataSet.push_back(point_altitudeValue);
1020                 if (point_altitudeValue > maxAlt)
1021                     maxAlt = point_altitudeValue;
1022                 if (point_altitudeValue < minAlt)
1023                     minAlt = point_altitudeValue;
1024                 point_timeValue = i * 900 + 43200;
1025                 time_dataSet.push_back(point_timeValue);
1026             }
1027 
1028             // Replace graph data set:
1029             avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1030 
1031             // Go into initial state: without Zoom/Pan
1032             avtUI->View->xAxis->setRange(43200, 129600);
1033             avtUI->View->xAxis2->setRange(61200, 147600);
1034 
1035             // Center the altitude axis in 0 value:
1036             if (abs(minAlt) > maxAlt)
1037                 maxAlt = abs(minAlt);
1038             else
1039                 minAlt = -maxAlt;
1040             avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1041 
1042             // Update background coordinates:
1043             background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1044             background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1045 
1046             // Redraw the plot:
1047             avtUI->View->replot();
1048 
1049             //restore original position
1050             if (o->isSolarSystem())
1051             {
1052                 o->updateCoords(oldNum, true, data->geo()->lat(), data->lst());
1053                 delete oldNum;
1054                 oldNum = nullptr;
1055             }
1056             o->EquatorialToHorizontal(data->lst(), data->geo()->lat());
1057         }
1058         else //assume unfound object is a custom object
1059         {
1060             pList.at(i)->updateCoordsNow(num); //precess to desired epoch
1061 
1062             // We are creating a new data set (time, altitude) for the new date:
1063             QVector<double> time_dataSet, altitude_dataSet;
1064             double point_altitudeValue, point_timeValue;
1065             // compute the new graph values:
1066             // time range: 24h
1067             int offset = 3;
1068             for (double h = -12.0, i = 0; h <= 12.0; h += 0.25, i++)
1069             {
1070                 point_altitudeValue = findAltitude(pList.at(i), h);
1071                 altitude_dataSet.push_back(point_altitudeValue);
1072                 if (point_altitudeValue > maxAlt)
1073                     maxAlt = point_altitudeValue;
1074                 if (point_altitudeValue < minAlt)
1075                     minAlt = point_altitudeValue;
1076                 point_timeValue = i * 900 + 43200;
1077                 time_dataSet.push_back(point_timeValue);
1078             }
1079 
1080             // Replace graph data set:
1081             avtUI->View->graph(i)->setData(time_dataSet, altitude_dataSet);
1082 
1083             // Go into initial state: without Zoom/Pan
1084             avtUI->View->xAxis->setRange(43200, 129600);
1085             avtUI->View->xAxis2->setRange(61200, 147600);
1086 
1087             // Center the altitude axis in 0 value:
1088             if (abs(minAlt) > maxAlt)
1089                 maxAlt = abs(minAlt);
1090             else
1091                 minAlt = -maxAlt;
1092             avtUI->View->yAxis->setRange(minAlt - offset, maxAlt + offset);
1093 
1094             // Update background coordinates:
1095             background->topLeft->setCoords(avtUI->View->xAxis->range().lower, avtUI->View->yAxis->range().upper);
1096             background->bottomRight->setCoords(avtUI->View->xAxis->range().upper, avtUI->View->yAxis->range().lower);
1097 
1098             // Redraw the plot:
1099             avtUI->View->replot();
1100         }
1101     }
1102 
1103     if (getDate().time().hour() > 12)
1104         DayOffset = 1;
1105     else
1106         DayOffset = 0;
1107 
1108     setLSTLimits();
1109     slotHighlight(avtUI->PlotList->currentRow());
1110     avtUI->View->update();
1111 
1112     delete num;
1113 }
1114 
1115 void AltVsTime::slotChooseCity()
1116 {
1117     QPointer<LocationDialog> ld = new LocationDialog(this);
1118     if (ld->exec() == QDialog::Accepted)
1119     {
1120         GeoLocation *newGeo = ld->selectedCity();
1121         if (newGeo)
1122         {
1123             geo = newGeo;
1124             avtUI->latBox->show(geo->lat());
1125             avtUI->longBox->show(geo->lng());
1126         }
1127     }
1128     delete ld;
1129 }
1130 
1131 // FIXME: should we remove this method?
1132 void AltVsTime::setLSTLimits()
1133 {
1134     /*
1135     //UT at noon on target date
1136     KStarsDateTime ut = getDate().addSecs(((double)DayOffset + 0.5)*86400.);
1137 
1138     dms lst = geo->GSTtoLST(ut.gst());
1139     double h1 = lst.Hours();
1140     if(h1 > 12.0)
1141         h1 -= 24.0;
1142     double h2 = h1 + 24.0;
1143     avtUI->View->setSecondaryLimits(h1, h2, -90.0, 90.0);
1144     */
1145 }
1146 
1147 void AltVsTime::showCurrentDate()
1148 {
1149     KStarsDateTime dt = KStarsDateTime::currentDateTime();
1150     if (dt.time() > QTime(12, 0, 0))
1151         dt = dt.addDays(1);
1152     avtUI->DateWidget->setDate(dt.date());
1153 }
1154 
1155 void AltVsTime::drawGradient()
1156 {
1157     // Things needed for Gradient:
1158     KStarsDateTime dtt  = KStarsDateTime::currentDateTime();
1159     GeoLocation *geoLoc = KStarsData::Instance()->geo();
1160     QDateTime midnight  = QDateTime(dtt.date(), QTime());
1161     KStarsDateTime const utt  = geoLoc->LTtoUT(KStarsDateTime(midnight));
1162 
1163     // Variables needed for Gradient:
1164     double SunRise, SunSet, Dawn, Dusk, SunMinAlt, SunMaxAlt;
1165     double MoonRise, MoonSet, MoonIllum;
1166 
1167     KSAlmanac ksal(utt, geoLoc);
1168 
1169     // Get the values:
1170     SunRise   = ksal.getSunRise();
1171     SunSet    = ksal.getSunSet();
1172     SunMaxAlt = ksal.getSunMaxAlt();
1173     SunMinAlt = ksal.getSunMinAlt();
1174     MoonRise  = ksal.getMoonRise();
1175     MoonSet   = ksal.getMoonSet();
1176     MoonIllum = ksal.getMoonIllum();
1177     Dawn      = ksal.getDawnAstronomicalTwilight();
1178     Dusk      = ksal.getDuskAstronomicalTwilight();
1179 
1180     gradient = new QPixmap(avtUI->View->rect().width(), avtUI->View->rect().height());
1181 
1182     QPainter p;
1183 
1184     p.begin(gradient);
1185     KPlotWidget *kPW = new KPlotWidget;
1186     p.setRenderHint(QPainter::Antialiasing, kPW->antialiasing());
1187     p.fillRect(gradient->rect(), kPW->backgroundColor());
1188 
1189     p.setClipRect(gradient->rect());
1190     p.setClipping(true);
1191 
1192     int pW = gradient->rect().width();
1193     int pH = gradient->rect().height();
1194 
1195     QColor SkyColor(0, 100, 200);
1196     //    TODO
1197     //    if( Options::darkAppColors() )
1198     //        SkyColor = QColor( 200, 0, 0 ); // use something red, visible through a red filter
1199 
1200     // Draw gradient representing lunar interference in the sky
1201     if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
1202     {
1203         int moonrise = int(pW * (0.5 + MoonRise));
1204         int moonset  = int(pW * (MoonSet - 0.5));
1205         if (moonset < 0)
1206             moonset += pW;
1207         if (moonrise > pW)
1208             moonrise -= pW;
1209         int moonalpha = int(10 + MoonIllum * 130);
1210         int fadewidth =
1211             pW *
1212             0.01; // pW * fraction of day to fade the moon brightness over (0.01 corresponds to roughly 15 minutes, 0.007 to 10 minutes), both before and after actual set.
1213         QColor MoonColor(255, 255, 255, moonalpha);
1214 
1215         if (moonset < moonrise)
1216         {
1217             QLinearGradient grad =
1218                 QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0));
1219             grad.setColorAt(0, MoonColor);
1220             grad.setColorAt(1, Qt::transparent);
1221             p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH),
1222                        grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs)
1223             grad.setStart(QPointF(moonrise + fadewidth, 0.0));
1224             grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0));
1225             p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad);
1226         }
1227         else
1228         {
1229             qreal opacity = p.opacity();
1230             p.setOpacity(opacity / 4);
1231             p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor);
1232             QLinearGradient grad =
1233                 QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0));
1234             grad.setColorAt(0, MoonColor);
1235             grad.setColorAt(1, Qt::transparent);
1236             p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad);
1237             grad.setStart(QPointF(moonset - fadewidth, 0.0));
1238             grad.setFinalStop(QPointF(moonset + fadewidth, 0.0));
1239             p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad);
1240             p.setOpacity(opacity);
1241         }
1242     }
1243 
1244     //draw daytime sky if the Sun rises for the current date/location
1245     if (SunMaxAlt > -18.0)
1246     {
1247         //Display centered on midnight, so need to modulate dawn/dusk by 0.5
1248         int rise = int(pW * (0.5 + SunRise));
1249         int set  = int(pW * (SunSet - 0.5));
1250         int da   = int(pW * (0.5 + Dawn));
1251         int du   = int(pW * (Dusk - 0.5));
1252 
1253         if (SunMinAlt > 0.0)
1254         {
1255             // The sun never set and the sky is always blue
1256             p.fillRect(rect(), SkyColor);
1257         }
1258         else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0)
1259         {
1260             // The sun never rise but the sky is not completely dark
1261             QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));
1262 
1263             QColor gradStartColor = SkyColor;
1264             gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1265 
1266             grad.setColorAt(0, gradStartColor);
1267             grad.setColorAt(1, Qt::transparent);
1268             p.fillRect(QRectF(0.0, 0.0, du, pH), grad);
1269             grad.setStart(QPointF(pW, 0.0));
1270             grad.setFinalStop(QPointF(da, 0.0));
1271             p.fillRect(QRectF(da, 0.0, pW, pH), grad);
1272         }
1273         else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0)
1274         {
1275             // The sun never rise but the sky is NEVER completely dark
1276             QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0));
1277 
1278             QColor gradStartEndColor = SkyColor;
1279             gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
1280             QColor gradMidColor = SkyColor;
1281             gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1282 
1283             grad.setColorAt(0, gradStartEndColor);
1284             grad.setColorAt(0.5, gradMidColor);
1285             grad.setColorAt(1, gradStartEndColor);
1286             p.fillRect(QRectF(0.0, 0.0, pW, pH), grad);
1287         }
1288         else if (Dawn < 0.0)
1289         {
1290             // The sun sets and rises but the sky is never completely dark
1291             p.fillRect(0, 0, set, int(0.5 * pH), SkyColor);
1292             p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor);
1293 
1294             QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));
1295 
1296             QColor gradMidColor = SkyColor;
1297             gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
1298 
1299             grad.setColorAt(0, SkyColor);
1300             grad.setColorAt(0.5, gradMidColor);
1301             grad.setColorAt(1, SkyColor);
1302             p.fillRect(QRectF(set, 0.0, rise - set, pH), grad);
1303         }
1304         else
1305         {
1306             p.fillRect(0, 0, set, pH, SkyColor);
1307             p.fillRect(rise, 0, pW, pH, SkyColor);
1308 
1309             QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
1310             grad.setColorAt(0, SkyColor);
1311             grad.setColorAt(
1312                 1,
1313                 Qt::transparent); // FIXME?: The sky appears black well before the actual end of twilight if the gradient is too slow (eg: latitudes above arctic circle)
1314             p.fillRect(QRectF(set, 0.0, du - set, pH), grad);
1315 
1316             grad.setStart(QPointF(rise, 0.0));
1317             grad.setFinalStop(QPointF(da, 0.0));
1318             p.fillRect(QRectF(da, 0.0, rise - da, pH), grad);
1319         }
1320     }
1321 
1322     p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH), KStarsData::Instance()->colorScheme()->colorNamed("HorzColor"));
1323 
1324     p.setClipping(false);
1325 
1326     // Add vertical line indicating "now"
1327     // Convert the current system clock time to the TZ corresponding to geo
1328     QTime t = geoLoc->UTtoLT(KStarsDateTime::currentDateTimeUtc()).time();
1329     double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
1330 
1331     while (x > 24.0)
1332         x -= 24.0;
1333 
1334     // Convert to screen pixel coords
1335     int ix = int(x * pW / 24.0);
1336 
1337     p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
1338     p.drawLine(ix, 0, ix, pH);
1339 
1340     QFont largeFont = p.font();
1341 
1342     largeFont.setPointSize(largeFont.pointSize() + 1);
1343     // Label this vertical line with the current time
1344     p.save();
1345     p.setFont(largeFont);
1346     p.translate(ix + 15, pH - 20);
1347     p.rotate(-90);
1348     // Short format necessary to avoid false time-zone labeling
1349     p.drawText(0, 0, QLocale().toString(t, QLocale::ShortFormat));
1350     p.restore();
1351     p.end();
1352 }
1353 
1354 KStarsDateTime AltVsTime::getDate()
1355 {
1356     //convert midnight local time to UT:
1357     QDateTime lt(avtUI->DateWidget->date(), QTime());
1358     return geo->LTtoUT(KStarsDateTime(lt));
1359 }
1360 
1361 double AltVsTime::getEpoch(const QString &eName)
1362 {
1363     //If Epoch field not a double, assume J2000
1364     bool ok;
1365     double epoch = eName.toDouble(&ok);
1366     if (!ok)
1367     {
1368         qCWarning(KSTARS) << "Invalid Epoch.  Assuming 2000.0.";
1369         return 2000.0;
1370     }
1371     return epoch;
1372 }
1373 
1374 void AltVsTime::setDawnDusk()
1375 {
1376     /* TODO */
1377 
1378     /*
1379     KSAlmanac almanac(getDate(), geo);
1380 
1381     avtUI->View->setDawnDuskTimes(almanac.getDawnAstronomicalTwilight(), almanac.getDuskAstronomicalTwilight());
1382     avtUI->View->setMinMaxSunAlt(almanac.getSunMinAlt(), almanac.getSunMaxAlt());
1383     */
1384 
1385     /* ... */
1386 }
1387 
1388 void AltVsTime::slotPrint()
1389 {
1390     QPainter p;            // Our painter object
1391     QPrinter printer;      // Our printer object
1392     QString str_legend;    // Text legend
1393     int text_height = 200; // Height of legend text zone in points
1394     QSize plot_size;       // Initial plot widget size
1395     QFont plot_font;       // Initial plot widget font
1396     int plot_font_size;    // Initial plot widget font size
1397 
1398     // Set printer resolution to 300 dpi
1399     printer.setResolution(300);
1400 
1401     // Open print dialog
1402     //QPointer<QPrintDialog> dialog( KdePrint::createPrintDialog( &printer, this ) );
1403     //QPointer<QPrintDialog> dialog( &printer, this );
1404     QPrintDialog dialog(&printer, this);
1405     dialog.setWindowTitle(i18nc("@title:window", "Print elevation vs time plot"));
1406     if (dialog.exec() == QDialog::Accepted)
1407     {
1408         // Change mouse cursor
1409         QApplication::setOverrideCursor(Qt::WaitCursor);
1410 
1411         // Save plot widget font
1412         plot_font = avtUI->View->font();
1413         // Save plot widget font size
1414         plot_font_size = plot_font.pointSize();
1415         // Save calendar widget size
1416         plot_size = avtUI->View->size();
1417 
1418         // Set text legend
1419         str_legend = i18n("Elevation vs. Time Plot");
1420         str_legend += '\n';
1421         str_legend += geo->fullName();
1422         str_legend += " - ";
1423         str_legend += avtUI->DateWidget->date().toString("dd/MM/yyyy");
1424 
1425         // Create a rectangle for legend text zone
1426         QRect text_rect(0, 0, printer.width(), text_height);
1427 
1428         // Increase plot widget font size so it looks good in 300 dpi
1429         plot_font.setPointSize(plot_font_size * 2.5);
1430         avtUI->View->setFont(plot_font);
1431         // Increase plot widget size to fit the entire page
1432         avtUI->View->resize(printer.width(), printer.height() - text_height);
1433 
1434         // Create a pixmap and render plot widget into it
1435         QPixmap pixmap(avtUI->View->size());
1436         avtUI->View->render(&pixmap);
1437 
1438         // Begin painting on printer
1439         p.begin(&printer);
1440         // Draw legend
1441         p.drawText(text_rect, Qt::AlignLeft, str_legend);
1442         // Draw plot widget
1443         p.drawPixmap(0, text_height, pixmap);
1444         // Ending painting
1445         p.end();
1446 
1447         // Restore plot widget font size
1448         plot_font.setPointSize(plot_font_size);
1449         avtUI->View->setFont(plot_font);
1450         // Restore calendar widget size
1451         avtUI->View->resize(plot_size);
1452 
1453         // Restore mouse cursor
1454         QApplication::restoreOverrideCursor();
1455     }
1456     //delete dialog;
1457 }
1458 
1459 QString AltVsTime::getObjectName(const SkyObject *o, bool translated)
1460 {
1461     QString finalObjectName;
1462     if (o->name() == "star")
1463     {
1464         StarObject *s = (StarObject *)o;
1465 
1466         // JM: Enable HD Index stars to be added to the observing list.
1467         if (s->getHDIndex() != 0)
1468             finalObjectName = QString("HD %1").arg(QString::number(s->getHDIndex()));
1469     }
1470     else
1471         finalObjectName = translated ? o->translatedName() : o->name();
1472 
1473     return finalObjectName;
1474 }