1 /*
2     SPDX-FileCopyrightText: 2002 Pablo de Vicente <vicente@oan.es>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "modcalcsidtime.h"
8 
9 #include "kstarsdata.h"
10 #include "kstarsdatetime.h"
11 #include "ksnotification.h"
12 #include "dialogs/locationdialog.h"
13 
14 #include <KLineEdit>
15 
16 #include <QTextStream>
17 
modCalcSidTime(QWidget * parent)18 modCalcSidTime::modCalcSidTime(QWidget *parent) : QFrame(parent)
19 {
20     setupUi(this);
21 
22     //Preset date and location
23     showCurrentTimeAndLocation();
24 
25     // signals and slots connections
26     connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotChangeLocation()));
27     connect(Date, SIGNAL(dateChanged(QDate)), this, SLOT(slotChangeDate()));
28     connect(LT, SIGNAL(timeChanged(QTime)), this, SLOT(slotConvertST(QTime)));
29     connect(ST, SIGNAL(timeChanged(QTime)), this, SLOT(slotConvertLT(QTime)));
30 
31     connect(LocationCheckBatch, SIGNAL(clicked()), this, SLOT(slotLocationChecked()));
32     connect(DateCheckBatch, SIGNAL(clicked()), this, SLOT(slotDateChecked()));
33     connect(LocationCheckBatch, SIGNAL(clicked()), this, SLOT(slotHelpLabel()));
34     connect(DateCheckBatch, SIGNAL(clicked()), this, SLOT(slotHelpLabel()));
35     connect(ComputeComboBatch, SIGNAL(currentIndexChanged(int)), this, SLOT(slotHelpLabel()));
36 
37     connect(InputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles()));
38     connect(OutputFileBatch, SIGNAL(urlSelected(QUrl)), this, SLOT(slotCheckFiles()));
39     connect(LocationButtonBatch, SIGNAL(clicked()), this, SLOT(slotLocationBatch()));
40     connect(RunButtonBatch, SIGNAL(clicked()), this, SLOT(slotRunBatch()));
41     connect(ViewButtonBatch, SIGNAL(clicked()), this, SLOT(slotViewBatch()));
42 
43     RunButtonBatch->setEnabled(false);
44     ViewButtonBatch->setEnabled(false);
45 
46     show();
47 }
48 
showCurrentTimeAndLocation()49 void modCalcSidTime::showCurrentTimeAndLocation()
50 {
51     KStarsData *data = KStarsData::Instance();
52     LT->setTime(data->lt().time());
53     Date->setDate(data->lt().date());
54 
55     geo = data->geo();
56     LocationButton->setText(geo->fullName());
57     geoBatch = data->geo();
58     LocationButtonBatch->setText(geoBatch->fullName());
59 
60     slotConvertST(LT->time());
61 }
62 
slotChangeLocation()63 void modCalcSidTime::slotChangeLocation()
64 {
65     QPointer<LocationDialog> ld = new LocationDialog(this);
66 
67     if (ld->exec() == QDialog::Accepted)
68     {
69         GeoLocation *newGeo = ld->selectedCity();
70         if (newGeo)
71         {
72             geo = newGeo;
73             LocationButton->setText(geo->fullName());
74 
75             //Update the displayed ST
76             slotConvertST(LT->time());
77         }
78     }
79     delete ld;
80 }
81 
slotChangeDate()82 void modCalcSidTime::slotChangeDate()
83 {
84     slotConvertST(LT->time());
85 }
86 
slotConvertST(const QTime & lt)87 void modCalcSidTime::slotConvertST(const QTime &lt)
88 {
89     // blockSignals is used to break signal loop
90     ST->blockSignals(true);
91     ST->setTime(computeLTtoST(lt));
92     ST->blockSignals(false);
93 }
94 
slotConvertLT(const QTime & st)95 void modCalcSidTime::slotConvertLT(const QTime &st)
96 {
97     // blockSignals is used to break signal loop
98     LT->blockSignals(true);
99     LT->setTime(computeSTtoLT(st));
100     LT->blockSignals(false);
101 }
102 
computeLTtoST(QTime lt)103 QTime modCalcSidTime::computeLTtoST(QTime lt)
104 {
105     KStarsDateTime utdt = geo->LTtoUT(KStarsDateTime(Date->date(), lt));
106     dms st              = geo->GSTtoLST(utdt.gst());
107     return QTime(st.hour(), st.minute(), st.second());
108 }
109 
computeSTtoLT(QTime st)110 QTime modCalcSidTime::computeSTtoLT(QTime st)
111 {
112     KStarsDateTime dt0 = KStarsDateTime(Date->date(), QTime(0, 0, 0));
113     dms lst;
114     lst.setH(st.hour(), st.minute(), st.second());
115     dms gst = geo->LSTtoGST(lst);
116     return geo->UTtoLT(KStarsDateTime(Date->date(), dt0.GSTtoUT(gst))).time();
117 }
118 
119 //** Batch mode **//
slotDateChecked()120 void modCalcSidTime::slotDateChecked()
121 {
122     DateBatch->setEnabled(!DateCheckBatch->isChecked());
123 }
124 
slotLocationChecked()125 void modCalcSidTime::slotLocationChecked()
126 {
127     LocationButtonBatch->setEnabled(!LocationCheckBatch->isChecked());
128 
129     if (LocationCheckBatch->isChecked())
130     {
131         QString message = i18n("Location strings consist of the "
132                                "comma-separated names of the city, province and country.  "
133                                "If the string contains spaces, enclose it in quotes so it "
134                                "gets parsed properly.");
135 
136         KMessageBox::information(nullptr, message, i18n("Hint for writing location strings"),
137                                  "DontShowLocationStringMessageBox");
138     }
139 }
140 
slotHelpLabel()141 void modCalcSidTime::slotHelpLabel()
142 {
143     QStringList inList;
144     if (ComputeComboBatch->currentIndex() == 0)
145         inList.append(i18n("local time"));
146     else
147         inList.append(i18n("sidereal time"));
148 
149     if (DateCheckBatch->checkState() == Qt::Checked)
150         inList.append(i18n("date"));
151 
152     if (LocationCheckBatch->checkState() == Qt::Checked)
153         inList.append(i18n("location"));
154 
155     QString inListString = inList[0];
156     if (inList.size() == 2)
157         inListString = i18n("%1 and %2", inList[0], inList[1]);
158     if (inList.size() == 3)
159         inListString = i18n("%1, %2 and %3", inList[0], inList[1], inList[2]);
160 
161     HelpLabel->setText(i18n("Specify %1 in the input file.", inListString));
162 }
163 
slotLocationBatch()164 void modCalcSidTime::slotLocationBatch()
165 {
166     QPointer<LocationDialog> ld = new LocationDialog(this);
167 
168     if (ld->exec() == QDialog::Accepted)
169     {
170         GeoLocation *newGeo = ld->selectedCity();
171         if (newGeo)
172         {
173             geoBatch = newGeo;
174             LocationButtonBatch->setText(geoBatch->fullName());
175         }
176     }
177     delete ld;
178 }
179 
slotCheckFiles()180 void modCalcSidTime::slotCheckFiles()
181 {
182     if (!InputFileBatch->lineEdit()->text().isEmpty() && !OutputFileBatch->lineEdit()->text().isEmpty())
183     {
184         RunButtonBatch->setEnabled(true);
185     }
186     else
187     {
188         RunButtonBatch->setEnabled(false);
189     }
190 }
191 
slotRunBatch()192 void modCalcSidTime::slotRunBatch()
193 {
194     QString inputFileName = InputFileBatch->url().toLocalFile();
195 
196     if (QFile::exists(inputFileName))
197     {
198         QFile f(inputFileName);
199         if (!f.open(QIODevice::ReadOnly))
200         {
201             QString message = i18n("Could not open file %1.", f.fileName());
202             KSNotification::sorry(message, i18n("Could Not Open File"));
203             inputFileName.clear();
204             return;
205         }
206 
207         QTextStream istream(&f);
208         processLines(istream);
209 
210         ViewButtonBatch->setEnabled(true);
211 
212         f.close();
213     }
214     else
215     {
216         QString message = i18n("Invalid file: %1", inputFileName);
217         KSNotification::sorry(message, i18n("Invalid file"));
218         inputFileName.clear();
219         return;
220     }
221 }
222 
processLines(QTextStream & istream)223 void modCalcSidTime::processLines(QTextStream &istream)
224 {
225     QFile fOut(OutputFileBatch->url().toLocalFile());
226     fOut.open(QIODevice::WriteOnly);
227     QTextStream ostream(&fOut);
228 
229     QString line;
230     dms LST;
231     QTime inTime, outTime;
232     QDate dt;
233 
234     if (!DateCheckBatch->isChecked())
235         dt = DateBatch->date();
236 
237     while (!istream.atEnd())
238     {
239         line = istream.readLine();
240         line = line.trimmed();
241 
242         QStringList fields = line.split(' ', QString::SkipEmptyParts);
243 
244         //Find and parse the location string
245         if (LocationCheckBatch->isChecked())
246         {
247             //First, look for a pair of quotation marks, and parse the string between them
248             QChar q = '\"';
249             if (line.indexOf(q) == -1)
250                 q = '\'';
251             if (line.count(q) == 2)
252             {
253                 int iStart             = line.indexOf(q);
254                 int iEnd               = line.indexOf(q, iStart + 1);
255                 QString locationString = line.mid(iStart, iEnd - iStart + 1);
256                 line.remove(locationString);
257                 fields = line.split(' ', QString::SkipEmptyParts);
258                 locationString.remove(q);
259 
260                 QStringList locationFields = locationString.split(',', QString::SkipEmptyParts);
261                 for (int i = 0; i < locationFields.size(); i++)
262                     locationFields[i] = locationFields[i].trimmed();
263 
264                 if (locationFields.size() == 1)
265                     locationFields.insert(1, "");
266                 if (locationFields.size() == 2)
267                     locationFields.insert(1, "");
268                 if (locationFields.size() != 3)
269                 {
270                     qDebug() << "Error: could not parse location string: " << locationString;
271                     continue;
272                 }
273 
274                 geoBatch = KStarsData::Instance()->locationNamed(locationFields[0], locationFields[1],
275                            locationFields[2]);
276                 if (geoBatch == nullptr)
277                 {
278                     qDebug() << "Error: location not found in database: " << locationString;
279                     continue;
280                 }
281             }
282         }
283 
284         if (DateCheckBatch->isChecked())
285         {
286             //Parse one of the fields as the date
287             for (auto &s : fields)
288             {
289                 dt = QDate::fromString(s);
290                 if (dt.isValid())
291                     break;
292             }
293             if (!dt.isValid())
294             {
295                 qDebug() << "Error: did not find a valid date string in: " << line;
296                 continue;
297             }
298         }
299 
300         //Parse one of the fields as the time
301         for (auto &s : fields)
302         {
303             if (s.contains(':'))
304             {
305                 inTime = QTime::fromString(s.length() == 4 ? '0' + s : s);
306                 if (inTime.isValid())
307                     break;
308             }
309         }
310         if (!inTime.isValid())
311         {
312             qDebug() << "Error: did not find a valid time string in: " << line;
313             continue;
314         }
315 
316         if (geoBatch != nullptr)
317         {
318             if (ComputeComboBatch->currentIndex() == 0)
319             {
320                 //inTime is the local time, compute LST
321                 KStarsDateTime ksdt(dt, inTime);
322                 ksdt    = geoBatch->LTtoUT(ksdt);
323                 dms lst = geoBatch->GSTtoLST(ksdt.gst());
324                 outTime = QTime(lst.hour(), lst.minute(), lst.second());
325             }
326             else
327             {
328                 //inTime is the sidereal time, compute the local time
329                 KStarsDateTime ksdt(dt, QTime(0, 0, 0));
330                 dms lst;
331                 lst.setH(inTime.hour(), inTime.minute(), inTime.second());
332                 QTime ut = ksdt.GSTtoUT(geoBatch->LSTtoGST(lst));
333                 ksdt.setTime(ut);
334                 ksdt    = geoBatch->UTtoLT(ksdt);
335                 outTime = ksdt.time();
336             }
337 
338             //Write to output file
339             ostream << QLocale().toString(dt, QLocale::LongFormat) << "  \"" << geoBatch->fullName() << "\"  "
340                     << QLocale().toString(inTime) << "  " << QLocale().toString(outTime) << '\n';
341         }
342     }
343 
344     fOut.close();
345 }
346 
slotViewBatch()347 void modCalcSidTime::slotViewBatch()
348 {
349     QFile fOut(OutputFileBatch->url().toLocalFile());
350     fOut.open(QIODevice::ReadOnly);
351     QTextStream istream(&fOut);
352     QStringList text;
353 
354     while (!istream.atEnd())
355         text.append(istream.readLine());
356 
357     fOut.close();
358 
359     KMessageBox::informationList(nullptr, i18n("Results of Sidereal time calculation"), text,
360                                  OutputFileBatch->url().toLocalFile());
361 }
362