js8call/JS8_UI/logqso.cpp
Chris-AC9KH 356bdc0f50 Code cleanup - move loose source and header files in source tree to group folders
Rename all header files with .h extension
Fix function declaration in HamlibTransceiver.h that overrode member function but was not marked override
Remove unused variable in Modulator.cpp
Remove unused files from source tree
Remove duplicate LazyFillComboBox files
Reformat codebase to LLVM C++ standard
2026-01-02 16:46:38 -06:00

330 lines
9.8 KiB
C++

#include "logqso.h"
#include <QAbstractItemView>
#include <QComboBox>
#include <QDebug>
#include <QDir>
#include <QSettings>
#include <QStandardPaths>
#include <QString>
#include <QUdpSocket>
#include "Configuration.h"
#include "JS8_Include/Maidenhead.h"
#include "JS8_Logbook/adif.h"
#include "JS8_Main/Bands.h"
#include "JS8_Main/DriftingDateTime.h"
#include "JS8_Main/JS8MessageBox.h"
#include "moc_logqso.cpp"
#include "ui_logqso.h"
LogQSO::LogQSO(QString const &programTitle, QSettings *settings,
Configuration const *config, QWidget *parent)
: QDialog{parent, Qt::WindowStaysOnTopHint | Qt::WindowTitleHint |
Qt::WindowSystemMenuHint},
ui(new Ui::LogQSO), m_settings(settings), m_config{config} {
ui->setupUi(this);
setWindowTitle(programTitle + " - Log QSO");
ui->grid->setValidator(new Maidenhead::StandardValidator{this});
auto b = ui->buttonBox->button(QDialogButtonBox::Save);
if (b) {
b->setText("Add to Log");
}
}
LogQSO::~LogQSO() {}
bool LogQSO::acceptText(QString text) {
auto w = focusWidget();
if (!w) {
return false;
}
auto name = QString(w->metaObject()->className());
if (name != "QLineEdit") {
return false;
}
auto l = static_cast<QLineEdit *>(w);
if (l->text().isEmpty()) {
// set
l->setText(text);
} else {
// append
l->setText(QString("%1 %2").arg(l->text()).arg(text));
}
return true;
}
QString LogQSO::currentCall() { return ui->call->text().trimmed(); }
void LogQSO::on_start_now_button_pressed() {
ui->start_date_time->setDateTime(DriftingDateTime::currentDateTimeUtc());
}
void LogQSO::on_end_now_button_pressed() {
ui->end_date_time->setDateTime(DriftingDateTime::currentDateTimeUtc());
}
void LogQSO::on_add_new_field_button_pressed() { createAdditionalField(); }
void LogQSO::createAdditionalField(QString key, QString value) {
QLineEdit *l = new QLineEdit(this);
if (!value.isEmpty()) {
l->setText(value);
}
QComboBox *c = new QComboBox(this);
c->insertItems(0, ADIF_FIELDS);
c->insertItem(0, "");
c->setEditable(true);
c->view()->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
connect(c, &QComboBox::currentTextChanged, this, [l](const QString &text) {
l->setProperty("fieldKey", QVariant(text));
});
c->setCurrentText(key);
auto layout = static_cast<QFormLayout *>(ui->additionalFields->layout());
layout->addRow(c, l);
// set tab ordering
if (m_additionalFieldsControls.isEmpty()) {
setTabOrder(ui->cbComments, c);
} else {
setTabOrder(m_additionalFieldsControls.last(), c);
}
setTabOrder(c, l);
setTabOrder(l, ui->add_new_field_button);
c->setFocus();
m_additionalFieldsControls.append(l);
ui->additionalFields->setVisible(true);
ui->additionalFields->adjustSize();
// update the window layout
updateGeometry();
}
QVariantMap LogQSO::collectAdditionalFields() {
QVariantMap additionalFields;
foreach (auto field, m_additionalFieldsControls) {
auto key = field->property("fieldKey").toString();
if (key.isEmpty()) {
continue;
}
additionalFields[key] = QVariant(field->text());
}
return additionalFields;
}
void LogQSO::resetAdditionalFields() {
ui->additionalFields->setVisible(false);
if (!m_additionalFieldsControls.isEmpty()) {
auto layout =
static_cast<QFormLayout *>(ui->additionalFields->layout());
#if QT_VERSION >= 0x050800
for (int i = 0, count = layout->rowCount(); i < count; i++) {
layout->removeRow(0);
}
#else
QLayoutItem *child;
while ((child = layout->takeAt(0)) != 0) {
delete child;
}
#endif
m_additionalFieldsControls.clear();
}
setTabOrder(ui->cbComments, ui->add_new_field_button);
updateGeometry();
}
void LogQSO::loadSettings() {
m_settings->beginGroup("LogQSO");
restoreGeometry(
m_settings->value("geometry", saveGeometry()).toByteArray());
ui->cbComments->setChecked(
m_settings->value("SaveComments", false).toBool());
m_comments = m_settings->value("LogComments", "").toString();
resetAdditionalFields();
auto additionalFields =
m_settings->value("AdditionalFields", {}).toStringList();
QSet<QString> additionalFieldsSet;
foreach (auto key, additionalFields) {
if (key.isEmpty()) {
continue;
}
if (additionalFieldsSet.contains(key)) {
continue;
}
createAdditionalField(key);
additionalFieldsSet.insert(key);
}
m_settings->endGroup();
}
void LogQSO::storeSettings() const {
m_settings->beginGroup("LogQSO");
m_settings->setValue("geometry", saveGeometry());
m_settings->setValue("SaveComments", ui->cbComments->isChecked());
m_settings->setValue("LogComments", m_comments);
auto additionalFields = QStringList{};
foreach (auto field, m_additionalFieldsControls) {
auto key = field->property("fieldKey").toString();
if (key.isEmpty()) {
continue;
}
additionalFields.append(key);
}
m_settings->setValue("AdditionalFields", additionalFields);
m_settings->endGroup();
}
void LogQSO::initLogQSO(QString const &hisCall, QString const &hisGrid,
QString mode, QString const &rptSent,
QString const &rptRcvd, QDateTime const &dateTimeOn,
QDateTime const &dateTimeOff, Radio::Frequency dialFreq,
QString const &myCall, QString const &myGrid,
QString const &opCall, QString const &comments) {
if (!isHidden())
return;
loadSettings();
ui->call->setFocus();
ui->call->setText(hisCall);
ui->grid->setText(hisGrid);
ui->name->setText("");
ui->comments->setText("");
if (ui->cbComments->isChecked())
ui->comments->setText(m_comments);
if (!comments.isEmpty()) {
ui->comments->setText(comments);
}
ui->mode->setText(mode);
ui->sent->setText(rptSent);
ui->rcvd->setText(rptRcvd);
ui->start_date_time->setDateTime(dateTimeOn);
ui->end_date_time->setDateTime(dateTimeOff);
m_dialFreq = dialFreq;
m_myCall = myCall;
m_myGrid = myGrid;
ui->band->setText(m_config->bands()->find(dialFreq));
ui->loggedOperator->setText(opCall);
show();
}
void LogQSO::accept() {
QString hisCall, hisGrid, mode, submode, rptSent, rptRcvd, dateOn, dateOff,
timeOn, timeOff, band, operator_call;
QString comments, name;
hisCall = ui->call->text().toUpper();
hisGrid = ui->grid->text().toUpper();
mode = ui->mode->text().toUpper();
if (mode == "JS8") {
mode = "MFSK";
submode = "JS8";
}
rptSent = ui->sent->text();
rptRcvd = ui->rcvd->text();
m_dateTimeOn = ui->start_date_time->dateTime();
m_dateTimeOff = ui->end_date_time->dateTime();
band = ui->band->text();
name = ui->name->text();
comments = ui->comments->text();
m_comments = comments;
QString strDialFreq(QString::number(m_dialFreq / 1.e6, 'f', 6));
operator_call = ui->loggedOperator->text();
// Log this QSO to ADIF file "js8call_log.adi"
QString filename = "js8call_log.adi"; // TODO allow user to set
ADIF adifile;
auto adifilePath = QDir{QStandardPaths::writableLocation(
QStandardPaths::AppLocalDataLocation)}
.absoluteFilePath(filename);
adifile.init(adifilePath);
auto additionalFields = collectAdditionalFields();
QByteArray ADIF{adifile.QSOToADIF(
hisCall, hisGrid, mode, submode, rptSent, rptRcvd, m_dateTimeOn,
m_dateTimeOff, band, comments, name, strDialFreq, m_myCall, m_myGrid,
operator_call, additionalFields)};
if (!adifile.addQSOToFile(ADIF)) {
JS8MessageBox::warning_message(
this, tr("Log file error"),
tr("Cannot open \"%1\"").arg(adifilePath));
}
// Log this QSO to file "js8call.log"
static QFile f{QDir{
QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)}
.absoluteFilePath("js8call.log")};
if (!f.open(QIODevice::Text | QIODevice::Append)) {
JS8MessageBox::warning_message(
this, tr("Log file error"),
tr("Cannot open \"%1\" for append").arg(f.fileName()),
tr("Error: %1").arg(f.errorString()));
} else {
QStringList logEntryItems = {
m_dateTimeOn.date().toString("yyyy-MM-dd"),
m_dateTimeOn.time().toString("hh:mm:ss"),
m_dateTimeOff.date().toString("yyyy-MM-dd"),
m_dateTimeOff.time().toString("hh:mm:ss"),
hisCall,
hisGrid,
strDialFreq,
(mode == "MFSK" ? "JS8" : mode),
rptSent,
rptRcvd,
comments,
name};
if (!additionalFields.isEmpty()) {
foreach (auto value, additionalFields.values()) {
logEntryItems.append(value.toString());
}
}
QTextStream out(&f);
out << logEntryItems.join(",") << Qt::endl;
out.flush();
flushFileBuffer(f);
f.close();
}
Q_EMIT acceptQSO(m_dateTimeOff, hisCall, hisGrid, m_dialFreq, mode, submode,
rptSent, rptRcvd, comments, name, m_dateTimeOn,
operator_call, m_myCall, m_myGrid, ADIF, additionalFields);
// Clean up and finish logging
ui->call->clear();
QDialog::accept();
}
// closeEvent is only called from the system menu close widget for a
// modeless dialog so we use the hideEvent override to store the
// window settings
void LogQSO::hideEvent(QHideEvent *e) {
storeSettings();
QDialog::hideEvent(e);
}