1 #include "logqso.h"
2 
3 #include <QLocale>
4 #include <QString>
5 #include <QSettings>
6 #include <QStandardPaths>
7 #include <QDir>
8 
9 #include "logbook/logbook.h"
10 #include "MessageBox.hpp"
11 #include "Configuration.hpp"
12 #include "models/Bands.hpp"
13 #include "models/CabrilloLog.hpp"
14 #include "validators/MaidenheadLocatorValidator.hpp"
15 
16 #include "ui_logqso.h"
17 #include "moc_logqso.cpp"
18 
19 namespace
20 {
21   struct PropMode
22   {
23     char const * id_;
24     char const * name_;
25   };
26   constexpr PropMode prop_modes[] =
27     {
28      {"", ""}
29      , {"AS", QT_TRANSLATE_NOOP ("LogQSO", "Aircraft scatter")}
30      , {"AUE", QT_TRANSLATE_NOOP ("LogQSO", "Aurora-E")}
31      , {"AUR", QT_TRANSLATE_NOOP ("LogQSO", "Aurora")}
32      , {"BS", QT_TRANSLATE_NOOP ("LogQSO", "Back scatter")}
33      , {"ECH", QT_TRANSLATE_NOOP ("LogQSO", "Echolink")}
34      , {"EME", QT_TRANSLATE_NOOP ("LogQSO", "Earth-moon-earth")}
35      , {"ES", QT_TRANSLATE_NOOP ("LogQSO", "Sporadic E")}
36      , {"F2", QT_TRANSLATE_NOOP ("LogQSO", "F2 Reflection")}
37      , {"FAI", QT_TRANSLATE_NOOP ("LogQSO", "Field aligned irregularities")}
38      , {"INTERNET", QT_TRANSLATE_NOOP ("LogQSO", "Internet-assisted")}
39      , {"ION", QT_TRANSLATE_NOOP ("LogQSO", "Ionoscatter")}
40      , {"IRL", QT_TRANSLATE_NOOP ("LogQSO", "IRLP")}
41      , {"MS", QT_TRANSLATE_NOOP ("LogQSO", "Meteor scatter")}
42      , {"RPT", QT_TRANSLATE_NOOP ("LogQSO", "Non-satellite repeater or transponder")}
43      , {"RS", QT_TRANSLATE_NOOP ("LogQSO", "Rain scatter")}
44      , {"SAT", QT_TRANSLATE_NOOP ("LogQSO", "Satellite")}
45      , {"TEP", QT_TRANSLATE_NOOP ("LogQSO", "Trans-equatorial")}
46      , {"TR", QT_TRANSLATE_NOOP ("LogQSO", "Troposheric ducting")}
47     };
48 }
49 
LogQSO(QString const & programTitle,QSettings * settings,Configuration const * config,LogBook * log,QWidget * parent)50 LogQSO::LogQSO(QString const& programTitle, QSettings * settings
51                , Configuration const * config, LogBook * log, QWidget *parent)
52   : QDialog {parent, Qt::WindowStaysOnTopHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint}
53   , ui(new Ui::LogQSO)
54   , m_settings (settings)
55   , m_config {config}
56   , m_log {log}
57 {
58   ui->setupUi(this);
59   setWindowTitle(programTitle + " - Log QSO");
60   for (auto const& prop_mode : prop_modes)
61     {
62       ui->comboBoxPropMode->addItem (prop_mode.name_, prop_mode.id_);
63     }
64   loadSettings ();
65   auto date_time_format = QLocale {}.dateFormat (QLocale::ShortFormat) + " hh:mm:ss";
66   ui->start_date_time->setDisplayFormat (date_time_format);
67   ui->end_date_time->setDisplayFormat (date_time_format);
68   ui->grid->setValidator (new MaidenheadLocatorValidator {this});
69 }
70 
~LogQSO()71 LogQSO::~LogQSO ()
72 {
73 }
74 
loadSettings()75 void LogQSO::loadSettings ()
76 {
77   m_settings->beginGroup ("LogQSO");
78   restoreGeometry (m_settings->value ("geometry", saveGeometry ()).toByteArray ());
79   ui->cbTxPower->setChecked (m_settings->value ("SaveTxPower", false).toBool ());
80   ui->cbComments->setChecked (m_settings->value ("SaveComments", false).toBool ());
81   ui->cbPropMode->setChecked (m_settings->value ("SavePropMode", false).toBool ());
82   m_txPower = m_settings->value ("TxPower", "").toString ();
83   m_comments = m_settings->value ("LogComments", "").toString();
84   int prop_index {0};
85   if (ui->cbPropMode->isChecked ())
86     {
87       prop_index = ui->comboBoxPropMode->findData (m_settings->value ("PropMode", "").toString());
88     }
89   ui->comboBoxPropMode->setCurrentIndex (prop_index);
90   m_settings->endGroup ();
91 }
92 
storeSettings() const93 void LogQSO::storeSettings () const
94 {
95   m_settings->beginGroup ("LogQSO");
96   m_settings->setValue ("geometry", saveGeometry ());
97   m_settings->setValue ("SaveTxPower", ui->cbTxPower->isChecked ());
98   m_settings->setValue ("SaveComments", ui->cbComments->isChecked ());
99   m_settings->setValue ("SavePropMode", ui->cbPropMode->isChecked ());
100   m_settings->setValue ("TxPower", m_txPower);
101   m_settings->setValue ("LogComments", m_comments);
102   m_settings->setValue ("PropMode", ui->comboBoxPropMode->currentData ());
103   m_settings->endGroup ();
104 }
105 
initLogQSO(QString const & hisCall,QString const & hisGrid,QString mode,QString const & rptSent,QString const & rptRcvd,QDateTime const & dateTimeOn,QDateTime const & dateTimeOff,Radio::Frequency dialFreq,bool noSuffix,QString xSent,QString xRcvd)106 void LogQSO::initLogQSO(QString const& hisCall, QString const& hisGrid, QString mode,
107                         QString const& rptSent, QString const& rptRcvd,
108                         QDateTime const& dateTimeOn, QDateTime const& dateTimeOff,
109                         Radio::Frequency dialFreq, bool noSuffix, QString xSent, QString xRcvd)
110 {
111   if(!isHidden()) return;
112   ui->call->setText (hisCall);
113   ui->grid->setText (hisGrid);
114   ui->name->clear ();
115   if (ui->cbTxPower->isChecked ())
116     {
117       ui->txPower->setText (m_txPower);
118     }
119   else
120     {
121       ui->txPower->clear ();
122     }
123   if (ui->cbComments->isChecked ())
124     {
125       ui->comments->setText (m_comments);
126     }
127   else
128     {
129       ui->comments->clear ();
130     }
131   if (m_config->report_in_comments()) {
132     auto t=mode;
133     if(rptSent!="") t+="  Sent: " + rptSent;
134     if(rptRcvd!="") t+="  Rcvd: " + rptRcvd;
135     ui->comments->setText(t);
136   }
137   if(noSuffix and mode.mid(0,3)=="JT9") mode="JT9";
138   if(m_config->log_as_RTTY() and mode.mid(0,3)=="JT9") mode="RTTY";
139   ui->mode->setText(mode);
140   ui->sent->setText(rptSent);
141   ui->rcvd->setText(rptRcvd);
142   ui->start_date_time->setDateTime (dateTimeOn);
143   ui->end_date_time->setDateTime (dateTimeOff);
144   m_dialFreq=dialFreq;
145   m_myCall=m_config->my_callsign();
146   m_myGrid=m_config->my_grid();
147   ui->band->setText (m_config->bands ()->find (dialFreq));
148   ui->loggedOperator->setText(m_config->opCall());
149   ui->exchSent->setText (xSent);
150   ui->exchRcvd->setText (xRcvd);
151   if (!ui->cbPropMode->isChecked ())
152     {
153       ui->comboBoxPropMode->setCurrentIndex (-1);
154     }
155 
156   using SpOp = Configuration::SpecialOperatingActivity;
157   auto special_op = m_config->special_op_id ();
158   if (SpOp::FOX == special_op
159       || (m_config->autoLog ()
160           && SpOp::NONE < special_op && special_op < SpOp::FOX))
161     {
162       // allow auto logging in Fox mode and contests
163       accept();
164     }
165   else
166     {
167       show();
168     }
169 }
170 
accept()171 void LogQSO::accept()
172 {
173   auto hisCall = ui->call->text ();
174   auto hisGrid = ui->grid->text ();
175   auto mode = ui->mode->text ();
176   auto rptSent = ui->sent->text ();
177   auto rptRcvd = ui->rcvd->text ();
178   auto dateTimeOn = ui->start_date_time->dateTime ();
179   auto dateTimeOff = ui->end_date_time->dateTime ();
180   auto band = ui->band->text ();
181   auto name = ui->name->text ();
182   m_txPower = ui->txPower->text ();
183   m_comments = ui->comments->text ();
184   auto strDialFreq = QString::number (m_dialFreq / 1.e6,'f',6);
185   auto operator_call = ui->loggedOperator->text ();
186   auto xsent = ui->exchSent->text ();
187   auto xrcvd = ui->exchRcvd->text ();
188 
189   using SpOp = Configuration::SpecialOperatingActivity;
190   auto special_op = m_config->special_op_id ();
191 
192   if (special_op == SpOp::NA_VHF or special_op == SpOp::WW_DIGI) {
193     if(xrcvd!="" and hisGrid!=xrcvd) hisGrid=xrcvd;
194   }
195 
196   if ((special_op == SpOp::RTTY and xsent!="" and xrcvd!="")) {
197     if(rptSent=="" or !xsent.contains(rptSent+" ")) rptSent=xsent.split(" "
198 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
199                                                                         , QString::SkipEmptyParts
200 #else
201                                                                         , Qt::SkipEmptyParts
202 #endif
203                                                                         ).at(0);
204     if(rptRcvd=="" or !xrcvd.contains(rptRcvd+" ")) rptRcvd=xrcvd.split(" "
205 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
206                                                                         , QString::SkipEmptyParts
207 #else
208                                                                         , Qt::SkipEmptyParts
209 #endif
210                                                                         ).at(0);
211   }
212 
213   // validate
214   if (SpOp::NONE < special_op && special_op < SpOp::FOX)
215     {
216       if (xsent.isEmpty () || xrcvd.isEmpty ())
217         {
218           show ();
219           MessageBox::warning_message (this, tr ("Invalid QSO Data"),
220                                        tr ("Check exchange sent and received"));
221           return;               // without accepting
222         }
223 
224       if (!m_log->contest_log ()->add_QSO (m_dialFreq, mode, dateTimeOff, hisCall, xsent, xrcvd))
225         {
226           show ();
227           MessageBox::warning_message (this, tr ("Invalid QSO Data"),
228                                        tr ("Check all fields"));
229           return;               // without accepting
230         }
231     }
232 
233   auto const& prop_mode = ui->comboBoxPropMode->currentData ().toString ();
234   //Log this QSO to file "wsjtx.log"
235   static QFile f {QDir {QStandardPaths::writableLocation (QStandardPaths::DataLocation)}.absoluteFilePath ("wsjtx.log")};
236   if(!f.open(QIODevice::Text | QIODevice::Append)) {
237     MessageBox::warning_message (this, tr ("Log file error"),
238                                  tr ("Cannot open \"%1\" for append").arg (f.fileName ()),
239                                  tr ("Error: %1").arg (f.errorString ()));
240   } else {
241     QString logEntry=dateTimeOn.date().toString("yyyy-MM-dd,") +
242       dateTimeOn.time().toString("hh:mm:ss,") +
243       dateTimeOff.date().toString("yyyy-MM-dd,") +
244       dateTimeOff.time().toString("hh:mm:ss,") + hisCall + "," +
245       hisGrid + "," + strDialFreq + "," + mode +
246       "," + rptSent + "," + rptRcvd + "," + m_txPower +
247       "," + m_comments + "," + name + "," + prop_mode;
248     QTextStream out(&f);
249     out << logEntry <<
250 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
251                  endl
252 #else
253                  Qt::endl
254 #endif
255                  ;
256     f.close();
257   }
258 
259   //Clean up and finish logging
260   Q_EMIT acceptQSO (dateTimeOff
261                     , hisCall
262                     , hisGrid
263                     , m_dialFreq
264                     , mode
265                     , rptSent
266                     , rptRcvd
267                     , m_txPower
268                     , m_comments
269                     , name
270                     , dateTimeOn
271                     , operator_call
272                     , m_myCall
273                     , m_myGrid
274                     , xsent
275                     , xrcvd
276                     , prop_mode
277                     , m_log->QSOToADIF (hisCall
278                                         , hisGrid
279                                         , mode
280                                         , rptSent
281                                         , rptRcvd
282                                         , dateTimeOn
283                                         , dateTimeOff
284                                         , band
285                                         , m_comments
286                                         , name
287                                         , strDialFreq
288                                         , m_myCall
289                                         , m_myGrid
290                                         , m_txPower
291                                         , operator_call
292                                         , xsent
293                                         , xrcvd
294                                         , prop_mode));
295   QDialog::accept();
296 }
297 
298 // closeEvent is only called from the system menu close widget for a
299 // modeless dialog so we use the hideEvent override to store the
300 // window settings
hideEvent(QHideEvent * e)301 void LogQSO::hideEvent (QHideEvent * e)
302 {
303   storeSettings ();
304   QDialog::hideEvent (e);
305 }
306