kopia lustrzana https://github.com/jamescoxon/dl-fldigi
1676 wiersze
42 KiB
C++
1676 wiersze
42 KiB
C++
// ----------------------------------------------------------------------------
|
|
// logsupport.cxx
|
|
//
|
|
// Copyright (C) 2006-2010
|
|
// Dave Freese, W1HKJ
|
|
// Copyright (C) 2008-2009
|
|
// Stelios Bounanos, M0GLD
|
|
//
|
|
// This file is part of fldigi.
|
|
//
|
|
// Fldigi is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// Fldigi is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with fldigi. If not, see <http://www.gnu.org/licenses/>.
|
|
// ----------------------------------------------------------------------------
|
|
|
|
#include <config.h>
|
|
|
|
#include <string>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
|
|
#include "main.h"
|
|
#include "trx.h"
|
|
#include "debug.h"
|
|
#include "macros.h"
|
|
#include "status.h"
|
|
#include "date.h"
|
|
|
|
#include "logger.h"
|
|
#include "adif_io.h"
|
|
#include "textio.h"
|
|
#include "logbook.h"
|
|
#include "rigsupport.h"
|
|
|
|
#include "fl_digi.h"
|
|
#include "fileselect.h"
|
|
#include "configuration.h"
|
|
#include "main.h"
|
|
#include "locator.h"
|
|
#include "icons.h"
|
|
#include "gettext.h"
|
|
#include "qrunner.h"
|
|
#include "flmisc.h"
|
|
|
|
#include <FL/filename.H>
|
|
#include <FL/fl_ask.H>
|
|
|
|
using namespace std;
|
|
|
|
cQsoDb qsodb;
|
|
cAdifIO adifFile;
|
|
cTextFile txtFile;
|
|
|
|
string logbook_filename;
|
|
|
|
enum sorttype {NONE, SORTCALL, SORTDATE, SORTFREQ, SORTMODE};
|
|
sorttype lastsort = SORTDATE;
|
|
bool callfwd = true;
|
|
bool modefwd = true;
|
|
bool freqfwd = true;
|
|
|
|
void restore_sort();
|
|
|
|
// convert to and from "00:00:00" <=> "000000"
|
|
const char *timeview(const char *s)
|
|
{
|
|
static char ds[9];
|
|
int len = strlen(s);
|
|
strcpy(ds, "00:00:00");
|
|
if (len < 4)
|
|
return ds;
|
|
ds[0] = s[0]; ds[1] = s[1];
|
|
ds[3] = s[2]; ds[4] = s[3];
|
|
if (len < 6)
|
|
return ds;
|
|
ds[6] = s[4];
|
|
ds[7] = s[5];
|
|
return ds;
|
|
}
|
|
|
|
const char *timestring(const char *s)
|
|
{
|
|
static char ds[7];
|
|
int len = strlen(s);
|
|
if (len <= 4) return s;
|
|
ds[0] = s[0]; ds[1] = s[1];
|
|
ds[2] = s[3]; ds[3] = s[4];
|
|
if (len < 8) {
|
|
ds[4] = ds[5] = '0';
|
|
ds[6] = 0;
|
|
return ds;
|
|
}
|
|
ds[4] = s[6];
|
|
ds[5] = s[7];
|
|
ds[6] = 0;
|
|
return ds;
|
|
}
|
|
|
|
const char *timeview4(const char *s)
|
|
{
|
|
static char ds[6];
|
|
int len = strlen(s);
|
|
strcpy(ds, "00:00");
|
|
if (len < 5)
|
|
return ds;
|
|
ds[0] = s[0]; ds[1] = s[1];
|
|
ds[3] = s[2]; ds[4] = s[3];
|
|
return ds;
|
|
}
|
|
|
|
const char *time4(const char *s)
|
|
{
|
|
static char ds[5];
|
|
int len = strlen(s);
|
|
if (len <= 4)
|
|
return ds;
|
|
memset(ds, 0, 5);
|
|
strncpy(ds, s, 4);
|
|
return ds;
|
|
}
|
|
|
|
void Export_CSV()
|
|
{
|
|
if (chkExportBrowser->nchecked() == 0) return;
|
|
|
|
cQsoRec *rec;
|
|
|
|
string title = _("Export to CSV file");
|
|
string filters = "CSV\t*.csv";
|
|
#ifdef __APPLE__
|
|
filters.append("\n");
|
|
#endif
|
|
const char* p = FSEL::saveas( title.c_str(), filters.c_str(), "export.csv");
|
|
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
for (int i = 0; i < chkExportBrowser->FLTK_nitems(); i++) {
|
|
if (chkExportBrowser->checked(i + 1)) {
|
|
rec = qsodb.getRec(i);
|
|
rec->putField(EXPORT, "E");
|
|
qsodb.qsoUpdRec (i, rec);
|
|
}
|
|
}
|
|
string sp = p;
|
|
if (sp.find(".csv") == string::npos) sp.append(".csv");
|
|
txtFile.writeCSVFile(sp.c_str(), &qsodb);
|
|
}
|
|
|
|
void Export_TXT()
|
|
{
|
|
if (chkExportBrowser->nchecked() == 0) return;
|
|
|
|
cQsoRec *rec;
|
|
string title = _("Export to fixed field text file");
|
|
string filters = "TEXT\t*.txt";
|
|
#ifdef __APPLE__
|
|
filters.append("\n");
|
|
#endif
|
|
const char* p = FSEL::saveas( title.c_str(), filters.c_str(), "export.txt");
|
|
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
for (int i = 0; i < chkExportBrowser->FLTK_nitems(); i++) {
|
|
if (chkExportBrowser->checked(i + 1)) {
|
|
rec = qsodb.getRec(i);
|
|
rec->putField(EXPORT, "E");
|
|
qsodb.qsoUpdRec (i, rec);
|
|
}
|
|
}
|
|
string sp = p;
|
|
if (sp.find(".txt") == string::npos) sp.append(".txt");
|
|
txtFile.writeTXTFile(p, &qsodb);
|
|
}
|
|
|
|
void Export_ADIF()
|
|
{
|
|
if (chkExportBrowser->nchecked() == 0) return;
|
|
|
|
cQsoRec *rec;
|
|
|
|
string title = _("Export to ADIF file");
|
|
string filters;
|
|
string defname;
|
|
filters.assign("ADIF\t*.").append(ADIF_SUFFIX);
|
|
#ifdef __APPLE__
|
|
filters.append("\n");
|
|
#endif
|
|
defname.assign("export.").append(ADIF_SUFFIX);
|
|
const char* p = FSEL::saveas( title.c_str(), filters.c_str(), defname.c_str());
|
|
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
for (int i = 0; i < chkExportBrowser->FLTK_nitems(); i++) {
|
|
if (chkExportBrowser->checked(i + 1)) {
|
|
rec = qsodb.getRec(i);
|
|
rec->putField(EXPORT, "E");
|
|
qsodb.qsoUpdRec (i, rec);
|
|
}
|
|
}
|
|
string sp = p;
|
|
if (sp.find("." ADIF_SUFFIX) == string::npos) sp.append("." ADIF_SUFFIX);
|
|
adifFile.writeFile (sp.c_str(), &qsodb);
|
|
}
|
|
|
|
static savetype export_to = ADIF;
|
|
|
|
void Export_log()
|
|
{
|
|
if (export_to == ADIF) Export_ADIF();
|
|
else if (export_to == CSV) Export_CSV();
|
|
else Export_TXT();
|
|
}
|
|
|
|
void saveLogbook()
|
|
{
|
|
if (!qsodb.isdirty()) return;
|
|
if (progdefaults.NagMe)
|
|
if (!fl_choice2(_("Save changed Logbook?"), _("No"), _("Yes"), NULL))
|
|
return;
|
|
|
|
cQsoDb::reverse = false;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
|
|
qsodb.isdirty(0);
|
|
restore_sort();
|
|
|
|
adifFile.writeLog (logbook_filename.c_str(), &qsodb);
|
|
|
|
}
|
|
|
|
static void dxcc_entity_cache_clear(void);
|
|
static void dxcc_entity_cache_add(cQsoRec* r);
|
|
static void dxcc_entity_cache_rm(cQsoRec* r);
|
|
static void dxcc_entity_cache_add(cQsoDb& db);
|
|
|
|
void cb_mnuNewLogbook(Fl_Menu_* m, void* d){
|
|
saveLogbook();
|
|
|
|
logbook_filename = LogsDir;
|
|
logbook_filename.append("newlog." ADIF_SUFFIX);
|
|
progdefaults.logbookfilename = logbook_filename;
|
|
dlgLogbook->label(fl_filename_name(logbook_filename.c_str()));
|
|
progdefaults.changed = true;
|
|
qsodb.deleteRecs();
|
|
dxcc_entity_cache_clear();
|
|
wBrowser->clear();
|
|
clearRecord();
|
|
}
|
|
|
|
void adif_read_OK()
|
|
{
|
|
if (qsodb.nbrRecs() == 0)
|
|
adifFile.writeFile(logbook_filename.c_str(), &qsodb);
|
|
dxcc_entity_cache_clear();
|
|
dxcc_entity_cache_add(qsodb);
|
|
restore_sort();
|
|
activateButtons();
|
|
loadBrowser();
|
|
}
|
|
|
|
void cb_mnuOpenLogbook(Fl_Menu_* m, void* d)
|
|
{
|
|
string title = _("Open logbook file");
|
|
string filter;
|
|
filter.assign("ADIF file\t*.{adi,adif}");
|
|
#ifdef __APPLE__
|
|
filter.append("\n");
|
|
#endif
|
|
|
|
std::string deffilename = LogsDir;
|
|
deffilename.append(fl_filename_name(logbook_filename.c_str()));
|
|
|
|
const char* p = FSEL::select( title.c_str(), filter.c_str(), deffilename.c_str());
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
saveLogbook();
|
|
qsodb.deleteRecs();
|
|
|
|
logbook_filename = p;
|
|
progdefaults.logbookfilename = logbook_filename;
|
|
progdefaults.changed = true;
|
|
|
|
adifFile.readFile (logbook_filename.c_str(), &qsodb);
|
|
dlgLogbook->label(fl_filename_name(logbook_filename.c_str()));
|
|
qsodb.isdirty(0);
|
|
|
|
}
|
|
|
|
void cb_mnuSaveLogbook(Fl_Menu_*m, void* d) {
|
|
string title = _("Save logbook file");
|
|
string filter;
|
|
filter.assign("ADIF\t*.").append(ADIF_SUFFIX);
|
|
#ifdef __APPLE__
|
|
filter.append("\n");
|
|
#endif
|
|
std::string deffilename = LogsDir;
|
|
deffilename.append(fl_filename_name(logbook_filename.c_str()));
|
|
|
|
const char* p = FSEL::saveas( title.c_str(), filter.c_str(), deffilename.c_str());
|
|
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
logbook_filename = p;
|
|
if (logbook_filename.find("." ADIF_SUFFIX) == string::npos)
|
|
logbook_filename.append("." ADIF_SUFFIX);
|
|
|
|
progdefaults.logbookfilename = logbook_filename;
|
|
progdefaults.changed = true;
|
|
|
|
dlgLogbook->label(fl_filename_name(logbook_filename.c_str()));
|
|
|
|
cQsoDb::reverse = false;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
|
|
qsodb.isdirty(0);
|
|
restore_sort();
|
|
adifFile.writeLog (logbook_filename.c_str(), &qsodb);
|
|
|
|
}
|
|
|
|
int comparerecs (const void *rp1, const void *rp2) { // rp1 needle, rp2 haystack
|
|
cQsoRec *r1 = (cQsoRec *)rp1;
|
|
cQsoRec *r2 = (cQsoRec *)rp2;
|
|
int cmp = 0;
|
|
// compare by call
|
|
const char * s1 = r1->getField(CALL);
|
|
const char * s2 = r2->getField(CALL);
|
|
|
|
const char *p1 = strpbrk (s1+1, "0123456789");
|
|
const char *p2 = strpbrk (s2+1, "0123456789");
|
|
if (p1 && p2) {
|
|
cmp = (*p1 < *p2) ? -1 : (*p1 > *p2) ? 1 : 0;
|
|
if (cmp == 0) {
|
|
cmp = strncmp (s1, s2, max(p1 - s1, p2 - s2));
|
|
if (cmp == 0)
|
|
cmp = strcmp(p1+1, p2+1);
|
|
}
|
|
} else // not a normal call, do a simple string comparison
|
|
cmp = strcmp(s1, s2);
|
|
|
|
if (cmp != 0)
|
|
return cmp;
|
|
|
|
// compare by date
|
|
cmp = strcmp( r1->getField(QSO_DATE), r2->getField(QSO_DATE));
|
|
if (cmp != 0) return cmp;
|
|
|
|
// compare by time
|
|
int t1 = atoi(r1->getField(TIME_ON));
|
|
int t2 = atoi(r2->getField(TIME_ON));
|
|
if (abs(t1 - t2) > 200) { // changed from 2 to accommodate seconds
|
|
if (t1 < t2)
|
|
return -1;
|
|
if (t1 > t2)
|
|
return 1;
|
|
} // matched with +/- 2 minutes
|
|
|
|
// compare by mode
|
|
const char *m1 = r1->getField(MODE); // needle
|
|
const char *m2 = r2->getField(MODE); // haystack
|
|
if (strcasestr(m1, "DOM"))
|
|
cmp = strncasecmp("DOM", m2, 3);
|
|
else if (strcasestr(m2, m1)) // eQSL, LoTW use sparse MODE designators
|
|
cmp = 0;
|
|
else
|
|
cmp = strcasecmp(m1, m2);
|
|
if (cmp != 0) return cmp;
|
|
|
|
// compare by band
|
|
cmp = strcasecmp( r1->getField(BAND), r2->getField(BAND));
|
|
|
|
if (cmp == 0) printf("r1: %s, %s, %s, %s, %s\nr2: %s, %s, %s, %s, %s\n",
|
|
r1->getField(CALL), r1->getField(QSO_DATE), r1->getField(TIME_ON), r1->getField(MODE), r1->getField(BAND),
|
|
r2->getField(CALL), r2->getField(QSO_DATE), r2->getField(TIME_ON), r2->getField(MODE), r2->getField(BAND));
|
|
|
|
return cmp;
|
|
}
|
|
|
|
static void rxtext(const char *s)
|
|
{
|
|
ReceiveText->addstr(s);
|
|
}
|
|
|
|
void merge_recs( cQsoDb *db, cQsoDb *mrgdb ) // (haystack, needle)
|
|
{
|
|
static char msg1[100];
|
|
static char msg2[100];
|
|
static char msg3[100];
|
|
sorttype origsort = lastsort;
|
|
cQsoDb::reverse = false;
|
|
cQsoDb *reject = new cQsoDb;
|
|
cQsoDb *copy = new cQsoDb(db);
|
|
cQsoDb *merged = new cQsoDb;
|
|
|
|
snprintf(msg1, sizeof(msg1), "Read %d records", mrgdb->nbrRecs());
|
|
LOG_INFO("%s", msg1);
|
|
REQ(rxtext, "\n*** ");
|
|
REQ(rxtext, msg1);
|
|
|
|
db->clearDatabase();
|
|
|
|
copy->SortByCall();
|
|
mrgdb->SortByCall();
|
|
|
|
int n = 0; // copy
|
|
int m = 0; // merge
|
|
int N = copy->nbrRecs();
|
|
int M = mrgdb->nbrRecs();
|
|
|
|
int cmp;
|
|
for (;;) {
|
|
if (n == N && m == M) break;
|
|
if (n < N && m < M) {
|
|
if ((cmp = comparerecs(copy->getRec(n), mrgdb->getRec(m))) <= 0) {
|
|
db->qsoNewRec(copy->getRec(n));
|
|
n++;
|
|
if (cmp == 0) {
|
|
reject->qsoNewRec(mrgdb->getRec(m));
|
|
m++;
|
|
}
|
|
} else {
|
|
if (db->nbrRecs() == 0) {
|
|
db->qsoNewRec(mrgdb->getRec(m));
|
|
merged->qsoNewRec(mrgdb->getRec(m));
|
|
} else if (comparerecs(db->getRec(db->nbrRecs()-1), mrgdb->getRec(m)) != 0) {
|
|
db->qsoNewRec(mrgdb->getRec(m));
|
|
merged->qsoNewRec(mrgdb->getRec(m));
|
|
} else {
|
|
reject->qsoNewRec(mrgdb->getRec(m));
|
|
}
|
|
m++;
|
|
}
|
|
} else if (n == N) {
|
|
if (db->nbrRecs() == 0) {
|
|
db->qsoNewRec(mrgdb->getRec(m));
|
|
merged->qsoNewRec(mrgdb->getRec(m));
|
|
} else if (comparerecs(db->getRec(db->nbrRecs()-1), mrgdb->getRec(m)) != 0) {
|
|
db->qsoNewRec(mrgdb->getRec(m));
|
|
merged->qsoNewRec(mrgdb->getRec(m));
|
|
} else {
|
|
reject->qsoNewRec(mrgdb->getRec(m));
|
|
}
|
|
m++;
|
|
} else {
|
|
db->qsoNewRec(copy->getRec(n));
|
|
n++;
|
|
}
|
|
}
|
|
|
|
if (merged->nbrRecs()) {
|
|
string mergedname = LogsDir;
|
|
mergedname.append("merged_recs");
|
|
#ifdef __WIN32__
|
|
mergedname.append(".adi");
|
|
#else
|
|
mergedname.append(".adif");
|
|
#endif
|
|
adifFile.writeLog (mergedname.c_str(), merged, true);
|
|
snprintf(msg2, sizeof(msg2), "%d merged records saved in\n*** %s",
|
|
merged->nbrRecs(), mergedname.c_str());
|
|
REQ(rxtext, "\n*** ");
|
|
REQ(rxtext, msg2);
|
|
LOG_INFO("%s", msg2);
|
|
db->isdirty(1);
|
|
}
|
|
|
|
if (reject->nbrRecs()) {
|
|
string rejname = LogsDir;
|
|
rejname.append("duplicate_recs");
|
|
#ifdef __WIN32__
|
|
rejname.append(".adi");
|
|
#else
|
|
rejname.append(".adif");
|
|
#endif
|
|
adifFile.writeLog (rejname.c_str(), reject, true);
|
|
snprintf(msg3, sizeof(msg3), "%d duplicates's saved in\n %s",
|
|
reject->nbrRecs(), rejname.c_str());
|
|
REQ(rxtext, "\n*** ");
|
|
REQ(rxtext, msg3);
|
|
LOG_INFO("%s", msg3);
|
|
}
|
|
|
|
delete reject;
|
|
delete copy;
|
|
delete merged;
|
|
|
|
lastsort = origsort;
|
|
restore_sort();
|
|
loadBrowser();
|
|
}
|
|
|
|
void cb_mnuMergeADIF_log(Fl_Menu_* m, void* d) {
|
|
const char* p = FSEL::select(_("Merge ADIF file"), "ADIF\t*.{adi,adif}", LogsDir.c_str());
|
|
Fl::wait();
|
|
fl_digi_main->redraw();
|
|
Fl::awake();
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
cQsoDb *mrgdb = new cQsoDb;
|
|
adifFile.do_readfile (p, mrgdb);
|
|
merge_recs(&qsodb, mrgdb);
|
|
delete mrgdb;
|
|
|
|
}
|
|
|
|
void cb_export_date_select() {
|
|
if (qsodb.nbrRecs() == 0) return;
|
|
int start = atoi(inp_export_start_date->value());
|
|
int stop = atoi(inp_export_stop_date->value());
|
|
if (!start || !stop) return;
|
|
int chkdate;
|
|
chkExportBrowser->check_none();
|
|
if (!btn_export_by_date->value()) return;
|
|
|
|
cQsoRec *rec;
|
|
for (int i = 0; i < qsodb.nbrRecs(); i++) {
|
|
rec = qsodb.getRec (i);
|
|
chkdate = atoi(rec->getField(progdefaults.sort_date_time_off ? QSO_DATE_OFF : QSO_DATE));
|
|
if (chkdate >= start && chkdate <= stop)
|
|
chkExportBrowser->checked(i+1, 1);
|
|
}
|
|
chkExportBrowser->redraw();
|
|
}
|
|
|
|
inline const char *szfreq(const char *freq)
|
|
{
|
|
static char szf[11];
|
|
float f = atof(freq);
|
|
snprintf(szf, sizeof(szf), "%10.6f", f);
|
|
return szf;
|
|
}
|
|
|
|
void cb_Export_log() {
|
|
if (qsodb.nbrRecs() == 0) return;
|
|
cQsoRec *rec;
|
|
char line[80];
|
|
chkExportBrowser->clear();
|
|
#ifdef __APPLE__
|
|
chkExportBrowser->textfont(FL_SCREEN_BOLD);
|
|
chkExportBrowser->textsize(12);
|
|
#else
|
|
chkExportBrowser->textfont(FL_COURIER);
|
|
chkExportBrowser->textsize(12);
|
|
#endif
|
|
for( int i = 0; i < qsodb.nbrRecs(); i++ ) {
|
|
rec = qsodb.getRec (i);
|
|
snprintf(line,sizeof(line),"%8s %4s %-10s %-10s %-s",
|
|
rec->getField(QSO_DATE),
|
|
rec->getField(TIME_OFF),
|
|
rec->getField(CALL),
|
|
szfreq(rec->getField(FREQ)),
|
|
rec->getField(MODE) );
|
|
chkExportBrowser->add(line);
|
|
}
|
|
cb_export_date_select();
|
|
wExport->show();
|
|
}
|
|
|
|
void cb_mnuExportADIF_log(Fl_Menu_* m, void* d) {
|
|
export_to = ADIF;
|
|
cb_Export_log();
|
|
}
|
|
|
|
void cb_mnuExportCSV_log(Fl_Menu_* m, void* d) {
|
|
export_to = CSV;
|
|
cb_Export_log();
|
|
}
|
|
|
|
void cb_mnuExportTEXT_log(Fl_Menu_* m, void *d) {
|
|
export_to = TEXT;
|
|
cb_Export_log();
|
|
}
|
|
|
|
|
|
void cb_mnuShowLogbook(Fl_Menu_* m, void* d)
|
|
{
|
|
dlgLogbook->show();
|
|
}
|
|
|
|
enum State {VIEWREC, NEWREC};
|
|
static State logState = VIEWREC;
|
|
|
|
void activateButtons()
|
|
{
|
|
if (logState == NEWREC) {
|
|
bNewSave->label(_("Save"));
|
|
bUpdateCancel->label(_("Cancel"));
|
|
bUpdateCancel->activate();
|
|
bDelete->deactivate ();
|
|
bSearchNext->deactivate ();
|
|
bSearchPrev->deactivate ();
|
|
inpDate_log->take_focus();
|
|
return;
|
|
}
|
|
bNewSave->label(_("New"));
|
|
bUpdateCancel->label(_("Update"));
|
|
if (qsodb.nbrRecs() > 0) {
|
|
bDelete->activate();
|
|
bUpdateCancel->activate();
|
|
bSearchNext->activate ();
|
|
bSearchPrev->activate ();
|
|
wBrowser->take_focus();
|
|
} else {
|
|
bDelete->deactivate();
|
|
bUpdateCancel->deactivate();
|
|
bSearchNext->deactivate();
|
|
bSearchPrev->deactivate();
|
|
}
|
|
}
|
|
|
|
void cb_btnNewSave(Fl_Button* b, void* d) {
|
|
if (logState == VIEWREC) {
|
|
logState = NEWREC;
|
|
clearRecord();
|
|
activateButtons();
|
|
} else {
|
|
saveRecord();
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
loadBrowser();
|
|
logState = VIEWREC;
|
|
activateButtons();
|
|
}
|
|
}
|
|
|
|
void cb_btnUpdateCancel(Fl_Button* b, void* d) {
|
|
if (logState == NEWREC) {
|
|
logState = VIEWREC;
|
|
activateButtons ();
|
|
} else {
|
|
updateRecord();
|
|
wBrowser->take_focus();
|
|
}
|
|
}
|
|
|
|
void cb_btnDelete(Fl_Button* b, void* d) {
|
|
deleteRecord();
|
|
wBrowser->take_focus();
|
|
}
|
|
|
|
void restore_sort()
|
|
{
|
|
switch (lastsort) {
|
|
case SORTCALL :
|
|
cQsoDb::reverse = callfwd;
|
|
qsodb.SortByCall();
|
|
break;
|
|
case SORTDATE :
|
|
cQsoDb::reverse = progStatus.logbook_reverse;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
break;
|
|
case SORTFREQ :
|
|
cQsoDb::reverse = freqfwd;
|
|
qsodb.SortByFreq();
|
|
break;
|
|
case SORTMODE :
|
|
cQsoDb::reverse = modefwd;
|
|
qsodb.SortByMode();
|
|
break;
|
|
default: break;
|
|
}
|
|
}
|
|
|
|
void cb_SortByCall (void) {
|
|
if (lastsort == SORTCALL)
|
|
callfwd = !callfwd;
|
|
else {
|
|
callfwd = false;
|
|
lastsort = SORTCALL;
|
|
}
|
|
cQsoDb::reverse = callfwd;
|
|
qsodb.SortByCall();
|
|
loadBrowser();
|
|
}
|
|
|
|
void cb_SortByDate (void) {
|
|
if (lastsort == SORTDATE)
|
|
progStatus.logbook_reverse = !progStatus.logbook_reverse;
|
|
else {
|
|
lastsort = SORTDATE;
|
|
}
|
|
cQsoDb::reverse = progStatus.logbook_reverse;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
loadBrowser();
|
|
}
|
|
|
|
void reload_browser()
|
|
{
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
loadBrowser();
|
|
}
|
|
|
|
void cb_SortByMode (void) {
|
|
if (lastsort == SORTMODE)
|
|
modefwd = !modefwd;
|
|
else {
|
|
modefwd = false;
|
|
lastsort = SORTMODE;
|
|
}
|
|
cQsoDb::reverse = modefwd;
|
|
qsodb.SortByMode();
|
|
loadBrowser();
|
|
}
|
|
|
|
void cb_SortByFreq (void) {
|
|
if (lastsort == SORTFREQ)
|
|
freqfwd = !freqfwd;
|
|
else {
|
|
freqfwd = false;
|
|
lastsort = SORTFREQ;
|
|
}
|
|
cQsoDb::reverse = freqfwd;
|
|
qsodb.SortByFreq();
|
|
loadBrowser();
|
|
}
|
|
|
|
void DupCheck()
|
|
{
|
|
Fl_Color call_clr = progdefaults.LOGGINGcolor;
|
|
|
|
if (progdefaults.xml_logbook)
|
|
if (xml_check_dup())
|
|
call_clr = fl_rgb_color(
|
|
progdefaults.dup_color.R,
|
|
progdefaults.dup_color.G,
|
|
progdefaults.dup_color.B);
|
|
|
|
if (!progdefaults.xml_logbook && qsodb.duplicate(
|
|
inpCall->value(),
|
|
zdate(), ztime(), progdefaults.timespan, progdefaults.duptimespan,
|
|
inpFreq->value(), progdefaults.dupband,
|
|
inpState->value(), progdefaults.dupstate,
|
|
mode_info[active_modem->get_mode()].adif_name, progdefaults.dupmode,
|
|
inpXchgIn->value(), progdefaults.dupxchg1 ) ) {
|
|
call_clr = fl_rgb_color(
|
|
progdefaults.dup_color.R,
|
|
progdefaults.dup_color.G,
|
|
progdefaults.dup_color.B);
|
|
}
|
|
|
|
inpCall1->color(call_clr);
|
|
inpCall2->color(call_clr);
|
|
inpCall3->color(call_clr);
|
|
inpCall4->color(call_clr);
|
|
inpCall1->redraw();
|
|
inpCall2->redraw();
|
|
inpCall3->redraw();
|
|
inpCall4->redraw();
|
|
}
|
|
|
|
cQsoRec* SearchLog(const char *callsign)
|
|
{
|
|
size_t len = strlen(callsign);
|
|
char* re = new char[len + 3];
|
|
snprintf(re, len + 3, "^%s$", callsign);
|
|
|
|
int row = 0, col = 2;
|
|
return wBrowser->search(row, col, !cQsoDb::reverse, re) ? qsodb.getRec(row) : 0;
|
|
}
|
|
|
|
void SearchLastQSO(const char *callsign)
|
|
{
|
|
size_t len = strlen(callsign);
|
|
if (len < 3)
|
|
return;
|
|
|
|
if (progdefaults.xml_logbook) {
|
|
if(xml_get_record(callsign))
|
|
return;
|
|
}
|
|
|
|
Fl::focus(inpCall);
|
|
|
|
char* re = new char[len + 3];
|
|
snprintf(re, len + 3, "^%s$", callsign);
|
|
|
|
int row = 0, col = 2;
|
|
if (wBrowser->search(row, col, !cQsoDb::reverse, re)) {
|
|
wBrowser->GotoRow(row);
|
|
inpName->value(inpName_log->value());
|
|
inpQth->value(inpQth_log->value());
|
|
inpLoc->value(inpLoc_log->value());
|
|
inpState->value(inpState_log->value());
|
|
inpVEprov->value(inpVE_Prov_log->value ());
|
|
inpCountry->value(inpCountry_log->value ());
|
|
inpSearchString->value(callsign);
|
|
if (inpLoc->value()[0]) {
|
|
double lon1, lat1, lon2, lat2;
|
|
double azimuth, distance;
|
|
char szAZ[4];
|
|
if ( QRB::locator2longlat(&lon1, &lat1, progdefaults.myLocator.c_str()) == QRB::QRB_OK &&
|
|
QRB::locator2longlat(&lon2, &lat2, inpLoc->value()) == QRB::QRB_OK &&
|
|
QRB::qrb(lon1, lat1, lon2, lat2, &distance, &azimuth) == QRB::QRB_OK ) {
|
|
snprintf(szAZ,sizeof(szAZ),"%0.f", azimuth);
|
|
inpAZ->value(szAZ);
|
|
} else
|
|
inpAZ->value("");
|
|
} else
|
|
inpAZ->value("");
|
|
} else {
|
|
inpName->value("");
|
|
inpQth->value("");
|
|
inpLoc->value("");
|
|
inpState->value("");
|
|
inpVEprov->value("");
|
|
inpCountry->value("");
|
|
inpAZ->value("");
|
|
inpSearchString->value("");
|
|
}
|
|
delete [] re;
|
|
}
|
|
|
|
void cb_search(Fl_Widget* w, void*)
|
|
{
|
|
const char* str = inpSearchString->value();
|
|
if (!*str)
|
|
return;
|
|
|
|
bool rev = w == bSearchPrev;
|
|
int col = 2, row = wBrowser->value() + (rev ? -1 : 1);
|
|
row = WCLAMP(row, 0, wBrowser->rows() - 1);
|
|
|
|
if (wBrowser->search(row, col, rev, str))
|
|
wBrowser->GotoRow(row);
|
|
wBrowser->take_focus();
|
|
}
|
|
|
|
int log_search_handler(int)
|
|
{
|
|
if (!(Fl::event_state() & FL_CTRL))
|
|
return 0;
|
|
|
|
switch (Fl::event_key()) {
|
|
case 's':
|
|
bSearchNext->do_callback();
|
|
break;
|
|
case 'r':
|
|
bSearchPrev->do_callback();
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
static int editNbr = 0;
|
|
|
|
void cb_btnDialFreq(Fl_Button* b, void* d)
|
|
{
|
|
double drf = atof(inpFreq_log->value());
|
|
if (!drf) return;
|
|
|
|
int rf1, rf, audio;
|
|
rf1 = drf * 1e6;
|
|
rf = rf1 / 10000;
|
|
rf *= 10000;
|
|
audio = rf1 - rf;
|
|
// try to keep within normal xcvr bw, 500 - 3000 Hz
|
|
while (audio > 3000) {
|
|
audio -= 3000;
|
|
rf += 3000;
|
|
}
|
|
if (audio < 500) {
|
|
audio += 500;
|
|
rf -= 500;
|
|
}
|
|
qsy(rf, audio);
|
|
|
|
std::string mode_name = inpMode_log->value();
|
|
trx_mode m;
|
|
for (m = 0; m < NUM_MODES; m++)
|
|
if (mode_name == mode_info[m].adif_name)
|
|
break;
|
|
// do we have a valid modem?
|
|
if (m < NUM_MODES && active_modem->get_mode() != mode_info[m].mode)
|
|
init_modem(mode_info[m].mode);
|
|
|
|
const cQsoRec *qsoPtr = qsodb.getRec(editNbr);
|
|
inpCall->value(qsoPtr->getField(CALL));
|
|
inpName->value (qsoPtr->getField(NAME));
|
|
inpTimeOn->value (inpTimeOff->value());
|
|
inpState->value (qsoPtr->getField(STATE));
|
|
inpCountry->value (qsoPtr->getField(COUNTRY));
|
|
inpXchgIn->value(qsoPtr->getField(XCHG1));
|
|
inpQth->value (qsoPtr->getField(QTH));
|
|
inpLoc->value (qsoPtr->getField(GRIDSQUARE));
|
|
inpNotes->value (qsoPtr->getField(NOTES));
|
|
|
|
wBrowser->take_focus();
|
|
}
|
|
|
|
void clearRecord() {
|
|
Date tdy;
|
|
inpCall_log->value ("");
|
|
inpName_log->value ("");
|
|
inpDate_log->value (tdy.szDate(2));
|
|
inpDateOff_log->value (tdy.szDate(2));
|
|
inpTimeOn_log->value ("");
|
|
inpTimeOff_log->value ("");
|
|
inpRstR_log->value ("");
|
|
inpRstS_log->value ("");
|
|
inpFreq_log->value ("");
|
|
inpMode_log->value ("");
|
|
inpQth_log->value ("");
|
|
inpState_log->value ("");
|
|
inpVE_Prov_log->value ("");
|
|
inpCountry_log->value ("");
|
|
inpLoc_log->value ("");
|
|
inpQSLrcvddate_log->value ("");
|
|
inpQSLsentdate_log->value ("");
|
|
inpSerNoOut_log->value ("");
|
|
inpSerNoIn_log->value ("");
|
|
inpXchgIn_log->value("");
|
|
inpMyXchg_log->value(progdefaults.myXchg.c_str());
|
|
inpNotes_log->value ("");
|
|
inpIOTA_log->value("");
|
|
inpDXCC_log->value("");
|
|
inpQSL_VIA_log->value("");
|
|
inpCONT_log->value("");
|
|
inpCNTY_log->value("");
|
|
inpCQZ_log->value("");
|
|
inpITUZ_log->value("");
|
|
inpTX_pwr_log->value("");
|
|
inpSearchString->value ("");
|
|
}
|
|
|
|
void saveRecord() {
|
|
cQsoRec rec;
|
|
rec.putField(CALL, inpCall_log->value());
|
|
rec.putField(NAME, inpName_log->value());
|
|
rec.putField(QSO_DATE, inpDate_log->value());
|
|
rec.putField(QSO_DATE_OFF, inpDateOff_log->value());
|
|
|
|
string tm = timestring(inpTimeOn_log->value());
|
|
rec.putField(TIME_ON, tm.c_str());
|
|
inpTimeOn_log->value(timeview(tm.c_str()));
|
|
|
|
tm = timestring(inpTimeOff_log->value());
|
|
rec.putField(TIME_OFF, tm.c_str());
|
|
inpTimeOff_log->value(timeview(tm.c_str()));
|
|
|
|
rec.putField(FREQ, inpFreq_log->value());
|
|
rec.putField(MODE, inpMode_log->value());
|
|
rec.putField(QTH, inpQth_log->value());
|
|
rec.putField(STATE, inpState_log->value());
|
|
rec.putField(VE_PROV, inpVE_Prov_log->value());
|
|
rec.putField(COUNTRY, inpCountry_log->value());
|
|
rec.putField(GRIDSQUARE, inpLoc_log->value());
|
|
rec.putField(NOTES, inpNotes_log->value());
|
|
rec.putField(QSLRDATE, inpQSLrcvddate_log->value());
|
|
rec.putField(QSLSDATE, inpQSLsentdate_log->value());
|
|
rec.putField(RST_RCVD, inpRstR_log->value ());
|
|
rec.putField(RST_SENT, inpRstS_log->value ());
|
|
rec.putField(SRX, inpSerNoIn_log->value());
|
|
rec.putField(STX, inpSerNoOut_log->value());
|
|
rec.putField(XCHG1, inpXchgIn_log->value());
|
|
if (!qso_exchange.empty()) {
|
|
rec.putField(MYXCHG, qso_exchange.c_str());
|
|
qso_exchange.clear();
|
|
qso_time.clear();
|
|
} else if (!qso_time.empty()) {
|
|
string myexch = inpMyXchg_log->value();
|
|
myexch.append(" ").append(qso_time);
|
|
rec.putField(MYXCHG, myexch.c_str());
|
|
qso_time.clear();
|
|
} else {
|
|
rec.putField(MYXCHG, inpMyXchg_log->value());
|
|
}
|
|
rec.putField(CNTY, inpCNTY_log->value());
|
|
rec.putField(IOTA, inpIOTA_log->value());
|
|
rec.putField(DXCC, inpDXCC_log->value());
|
|
rec.putField(DXCC, inpQSL_VIA_log->value());
|
|
rec.putField(CONT, inpCONT_log->value());
|
|
rec.putField(CQZ, inpCQZ_log->value());
|
|
rec.putField(ITUZ, inpITUZ_log->value());
|
|
rec.putField(TX_PWR, inpTX_pwr_log->value());
|
|
|
|
qsodb.qsoNewRec (&rec);
|
|
dxcc_entity_cache_add(&rec);
|
|
submit_record(rec);
|
|
|
|
cQsoDb::reverse = false;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
|
|
qsodb.isdirty(0);
|
|
|
|
loadBrowser();
|
|
|
|
adifFile.writeLog (logbook_filename.c_str(), &qsodb);
|
|
}
|
|
|
|
void updateRecord() {
|
|
cQsoRec rec;
|
|
if (qsodb.nbrRecs() == 0) return;
|
|
rec.putField(CALL, inpCall_log->value());
|
|
rec.putField(NAME, inpName_log->value());
|
|
rec.putField(QSO_DATE, inpDate_log->value());
|
|
rec.putField(QSO_DATE_OFF, inpDateOff_log->value());
|
|
|
|
string tm = timestring(inpTimeOn_log->value());
|
|
rec.putField(TIME_ON, tm.c_str());
|
|
inpTimeOn_log->value(timeview(tm.c_str()));
|
|
|
|
tm = timestring(inpTimeOff_log->value());
|
|
rec.putField(TIME_OFF, tm.c_str());
|
|
inpTimeOff_log->value(timeview(tm.c_str()));
|
|
|
|
rec.putField(FREQ, inpFreq_log->value());
|
|
rec.putField(MODE, inpMode_log->value());
|
|
rec.putField(QTH, inpQth_log->value());
|
|
rec.putField(STATE, inpState_log->value());
|
|
rec.putField(VE_PROV, inpVE_Prov_log->value());
|
|
rec.putField(COUNTRY, inpCountry_log->value());
|
|
rec.putField(GRIDSQUARE, inpLoc_log->value());
|
|
rec.putField(NOTES, inpNotes_log->value());
|
|
rec.putField(QSLRDATE, inpQSLrcvddate_log->value());
|
|
rec.putField(QSLSDATE, inpQSLsentdate_log->value());
|
|
rec.putField(RST_RCVD, inpRstR_log->value ());
|
|
rec.putField(RST_SENT, inpRstS_log->value ());
|
|
rec.putField(SRX, inpSerNoIn_log->value());
|
|
rec.putField(STX, inpSerNoOut_log->value());
|
|
rec.putField(XCHG1, inpXchgIn_log->value());
|
|
rec.putField(MYXCHG, inpMyXchg_log->value());
|
|
rec.putField(CNTY, inpCNTY_log->value());
|
|
rec.putField(IOTA, inpIOTA_log->value());
|
|
rec.putField(DXCC, inpDXCC_log->value());
|
|
rec.putField(QSL_VIA, inpQSL_VIA_log->value());
|
|
rec.putField(CONT, inpCONT_log->value());
|
|
rec.putField(CQZ, inpCQZ_log->value());
|
|
rec.putField(ITUZ, inpITUZ_log->value());
|
|
rec.putField(TX_PWR, inpTX_pwr_log->value());
|
|
dxcc_entity_cache_rm(qsodb.getRec(editNbr));
|
|
qsodb.qsoUpdRec (editNbr, &rec);
|
|
dxcc_entity_cache_add(&rec);
|
|
|
|
cQsoDb::reverse = false;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
|
|
qsodb.isdirty(0);
|
|
restore_sort();
|
|
|
|
loadBrowser(true);
|
|
|
|
adifFile.writeLog (logbook_filename.c_str(), &qsodb);
|
|
|
|
}
|
|
|
|
void deleteRecord () {
|
|
if (qsodb.nbrRecs() == 0 || fl_choice2(_("Really delete record for \"%s\"?"),
|
|
_("Yes"), _("No"), NULL, wBrowser->valueAt(-1, 2)))
|
|
return;
|
|
|
|
dxcc_entity_cache_rm(qsodb.getRec(editNbr));
|
|
qsodb.qsoDelRec(editNbr);
|
|
|
|
cQsoDb::reverse = false;
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
|
|
qsodb.isdirty(0);
|
|
restore_sort();
|
|
|
|
loadBrowser(true);
|
|
|
|
adifFile.writeLog (logbook_filename.c_str(), &qsodb);
|
|
|
|
}
|
|
|
|
void EditRecord( int i )
|
|
{
|
|
cQsoRec *editQSO = qsodb.getRec (i);
|
|
if( !editQSO )
|
|
return;
|
|
|
|
inpCall_log->value (editQSO->getField(CALL));
|
|
inpName_log->value (editQSO->getField(NAME));
|
|
inpDate_log->value (editQSO->getField(QSO_DATE));
|
|
inpDateOff_log->value (editQSO->getField(QSO_DATE_OFF));
|
|
inpTimeOn_log->value (timeview(editQSO->getField(TIME_ON)));
|
|
inpTimeOff_log->value (timeview(editQSO->getField(TIME_OFF)));
|
|
inpRstR_log->value (editQSO->getField(RST_RCVD));
|
|
inpRstS_log->value (editQSO->getField(RST_SENT));
|
|
inpFreq_log->value (editQSO->getField(FREQ));
|
|
inpMode_log->value (editQSO->getField(MODE));
|
|
inpState_log->value (editQSO->getField(STATE));
|
|
inpVE_Prov_log->value (editQSO->getField(VE_PROV));
|
|
inpCountry_log->value (editQSO->getField(COUNTRY));
|
|
inpQth_log->value (editQSO->getField(QTH));
|
|
inpLoc_log->value (editQSO->getField(GRIDSQUARE));
|
|
inpQSLrcvddate_log->value (editQSO->getField(QSLRDATE));
|
|
inpQSLsentdate_log->value (editQSO->getField(QSLSDATE));
|
|
inpNotes_log->value (editQSO->getField(NOTES));
|
|
inpSerNoIn_log->value(editQSO->getField(SRX));
|
|
inpSerNoOut_log->value(editQSO->getField(STX));
|
|
inpXchgIn_log->value(editQSO->getField(XCHG1));
|
|
inpMyXchg_log->value(editQSO->getField(MYXCHG));
|
|
inpCNTY_log->value(editQSO->getField(CNTY));
|
|
inpIOTA_log->value(editQSO->getField(IOTA));
|
|
inpDXCC_log->value(editQSO->getField(DXCC));
|
|
inpQSL_VIA_log->value(editQSO->getField(QSL_VIA));
|
|
inpCONT_log->value(editQSO->getField(CONT));
|
|
inpCQZ_log->value(editQSO->getField(CQZ));
|
|
inpITUZ_log->value(editQSO->getField(ITUZ));
|
|
inpTX_pwr_log->value(editQSO->getField(TX_PWR));
|
|
}
|
|
|
|
std::string sDate_on = "";
|
|
std::string sTime_on = "";
|
|
std::string sDate_off = "";
|
|
std::string sTime_off = "";
|
|
|
|
void AddRecord ()
|
|
{
|
|
inpCall_log->value(inpCall->value());
|
|
inpName_log->value (inpName->value());
|
|
|
|
if (progdefaults.force_date_time) {
|
|
inpDate_log->value(sDate_off.c_str());
|
|
inpTimeOn_log->value (timeview(sTime_off.c_str()));
|
|
} else {
|
|
inpDate_log->value(sDate_on.c_str());
|
|
inpTimeOn_log->value (timeview(sTime_on.c_str()));
|
|
}
|
|
inpDateOff_log->value(sDate_off.c_str());
|
|
inpTimeOff_log->value (timeview(sTime_off.c_str()));
|
|
|
|
inpRstR_log->value (inpRstIn->value());
|
|
inpRstS_log->value (inpRstOut->value());
|
|
{
|
|
char Mhz[30];
|
|
snprintf(Mhz, sizeof(Mhz), "%-f", atof(inpFreq->value()) / 1000.0);
|
|
inpFreq_log->value(Mhz);
|
|
}
|
|
inpMode_log->value (logmode);
|
|
inpState_log->value (inpState->value());
|
|
inpVE_Prov_log->value (inpVEprov->value());
|
|
inpCountry_log->value (inpCountry->value());
|
|
|
|
inpSerNoIn_log->value(inpSerNo->value());
|
|
inpSerNoOut_log->value(outSerNo->value());
|
|
inpXchgIn_log->value(inpXchgIn->value());
|
|
inpMyXchg_log->value(progdefaults.myXchg.c_str());
|
|
|
|
inpQth_log->value (inpQth->value());
|
|
inpLoc_log->value (inpLoc->value());
|
|
inpQSLrcvddate_log->value ("");
|
|
inpQSLsentdate_log->value ("");
|
|
inpNotes_log->value (inpNotes->value());
|
|
|
|
inpTX_pwr_log->value (progdefaults.mytxpower.c_str());
|
|
inpCNTY_log->value("");
|
|
inpIOTA_log->value("");
|
|
inpDXCC_log->value("");
|
|
inpQSL_VIA_log->value("");
|
|
inpCONT_log->value("");
|
|
inpCQZ_log->value("");
|
|
inpITUZ_log->value("");
|
|
|
|
saveRecord();
|
|
|
|
restore_sort();
|
|
loadBrowser();
|
|
|
|
logState = VIEWREC;
|
|
activateButtons();
|
|
}
|
|
|
|
void cb_browser (Fl_Widget *w, void *data )
|
|
{
|
|
Table *table = (Table *)w;
|
|
editNbr = atoi(table->valueAt(-1,6));
|
|
EditRecord (editNbr);
|
|
}
|
|
|
|
void loadBrowser(bool keep_pos)
|
|
{
|
|
cQsoRec *rec;
|
|
char sNbr[6];
|
|
int row = wBrowser->value(), pos = wBrowser->scrollPos();
|
|
if (row >= qsodb.nbrRecs()) row = qsodb.nbrRecs() - 1;
|
|
wBrowser->clear();
|
|
if (qsodb.nbrRecs() == 0)
|
|
return;
|
|
for( int i = 0; i < qsodb.nbrRecs(); i++ ) {
|
|
rec = qsodb.getRec (i);
|
|
snprintf(sNbr,sizeof(sNbr),"%d",i);
|
|
wBrowser->addRow (7,
|
|
rec->getField(progdefaults.sort_date_time_off ? QSO_DATE_OFF : QSO_DATE),
|
|
timeview4(rec->getField(progdefaults.sort_date_time_off ? TIME_OFF : TIME_ON)),
|
|
rec->getField(CALL),
|
|
rec->getField(NAME),
|
|
rec->getField(FREQ),
|
|
rec->getField(MODE),
|
|
sNbr);
|
|
}
|
|
if (keep_pos && row >= 0) {
|
|
wBrowser->value(row);
|
|
wBrowser->scrollTo(pos);
|
|
}
|
|
else {
|
|
if (cQsoDb::reverse == true)
|
|
wBrowser->FirstRow ();
|
|
else
|
|
wBrowser->LastRow ();
|
|
}
|
|
char szRecs[6];
|
|
snprintf(szRecs, sizeof(szRecs), "%5d", qsodb.nbrRecs());
|
|
txtNbrRecs_log->value(szRecs);
|
|
}
|
|
|
|
//=============================================================================
|
|
// Cabrillo reporter
|
|
//=============================================================================
|
|
|
|
const char *contests[] =
|
|
{ "AP-SPRINT",
|
|
"ARRL-10", "ARRL-160", "ARRL-DX-CW", "ARRL-DX-SSB", "ARRL-SS-CW",
|
|
"ARRL-SS-SSB", "ARRL-UHF-AUG", "ARRL-VHF-JAN", "ARRL-VHF-JUN", "ARRL-VHF-SEP",
|
|
"ARRL-RTTY",
|
|
"BARTG-RTTY", "BARTG-SPRINT",
|
|
"CQ-160-CW", "CQ-160-SSB", "CQ-WPX-CW", "CQ-WPX-RTTY", "CQ-WPX-SSB", "CQ-VHF",
|
|
"CQ-WW-CW", "CQ-WW-RTTY", "CQ-WW-SSB",
|
|
"DARC-WAEDC-CW", "DARC-WAEDC-RTTY", "DARC-WAEDC-SSB",
|
|
"FCG-FQP", "IARU-HF", "JIDX-CW", "JIDX-SSB",
|
|
"NAQP-CW", "NAQP-RTTY", "NAQP-SSB", "NA-SPRINT-CW", "NA-SPRINT-SSB", "NCCC-CQP",
|
|
"NEQP", "OCEANIA-DX-CW", "OCEANIA-DX-SSB", "RDXC", "RSGB-IOTA",
|
|
"SAC-CW", "SAC-SSB", "STEW-PERRY", "TARA-RTTY", 0 };
|
|
|
|
enum icontest {
|
|
AP_SPRINT,
|
|
ARRL_10, ARRL_160, ARRL_DX_CW, ARRL_DX_SSB, ARRL_SS_CW,
|
|
ARRL_SS_SSB, ARRL_UHF_AUG, ARRL_VHF_JAN, ARRL_VHF_JUN, ARRL_VHF_SEP,
|
|
ARRL_RTTY,
|
|
BARTG_RTTY, BARTG_SPRINT,
|
|
CQ_160_CW, CQ_160_SSB, CQ_WPX_CW, CQ_WPX_RTTY, CQ_WPX_SSB, CQ_VHF,
|
|
CQ_WW_CW, CQ_WW_RTTY, CQ_WW_SSB,
|
|
DARC_WAEDC_CW, DARC_WAEDC_RTTY, DARC_WAEDC_SSB,
|
|
FCG_FQP, IARU_HF, JIDX_CW, JIDX_SSB,
|
|
NAQP_CW, NAQP_RTTY, NAQP_SSB, NA_SPRINT_CW, NA_SPRINT_SSB, NCCC_CQP,
|
|
NEQP, OCEANIA_DX_CW, OCEANIA_DX_SSB, RDXC, RSGB_IOTA,
|
|
SAC_CW, SAC_SSB, STEW_PERRY, TARA_RTTY
|
|
};
|
|
|
|
bool bInitCombo = true;
|
|
icontest contestnbr;
|
|
|
|
void setContestType()
|
|
{
|
|
contestnbr = (icontest)cboContest->index();
|
|
|
|
btnCabCall->value(true); btnCabFreq->value(true); btnCabMode->value(true);
|
|
btnCabQSOdate->value(true); btnCabTimeOFF->value(true); btnCabRSTsent->value(true);
|
|
btnCabRSTrcvd->value(true); btnCabSerialIN->value(true);btnCabSerialOUT->value(true);
|
|
btnCabXchgIn->value(true); btnCabMyXchg->value(true);
|
|
|
|
switch (contestnbr) {
|
|
case ARRL_SS_CW :
|
|
case ARRL_SS_SSB :
|
|
btnCabRSTrcvd->value(false);
|
|
break;
|
|
case BARTG_RTTY :
|
|
case BARTG_SPRINT :
|
|
break;
|
|
case ARRL_UHF_AUG :
|
|
case ARRL_VHF_JAN :
|
|
case ARRL_VHF_JUN :
|
|
case ARRL_VHF_SEP :
|
|
case CQ_VHF :
|
|
btnCabRSTrcvd->value(false);
|
|
btnCabSerialIN->value(false);
|
|
btnCabSerialOUT->value(false);
|
|
break;
|
|
case AP_SPRINT :
|
|
case ARRL_10 :
|
|
case ARRL_160 :
|
|
case ARRL_DX_CW :
|
|
case ARRL_DX_SSB :
|
|
case CQ_160_CW :
|
|
case CQ_160_SSB :
|
|
case CQ_WPX_CW :
|
|
case CQ_WPX_RTTY :
|
|
case CQ_WPX_SSB :
|
|
case RDXC :
|
|
case OCEANIA_DX_CW :
|
|
case OCEANIA_DX_SSB :
|
|
break;
|
|
case DARC_WAEDC_CW :
|
|
case DARC_WAEDC_RTTY :
|
|
case DARC_WAEDC_SSB :
|
|
break;
|
|
case NAQP_CW :
|
|
case NAQP_RTTY :
|
|
case NAQP_SSB :
|
|
case NA_SPRINT_CW :
|
|
case NA_SPRINT_SSB :
|
|
break;
|
|
case RSGB_IOTA :
|
|
break;
|
|
default :
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cb_Export_Cabrillo(Fl_Menu_* m, void* d) {
|
|
if (qsodb.nbrRecs() == 0) return;
|
|
cQsoRec *rec;
|
|
char line[80];
|
|
int indx = 0;
|
|
|
|
if (bInitCombo) {
|
|
bInitCombo = false;
|
|
while (contests[indx]) {
|
|
cboContest->add(contests[indx]);
|
|
indx++;
|
|
}
|
|
}
|
|
cboContest->index(0);
|
|
chkCabBrowser->clear();
|
|
#ifdef __APPLE__
|
|
chkCabBrowser->textfont(FL_SCREEN_BOLD);
|
|
chkCabBrowser->textsize(12);
|
|
#else
|
|
chkCabBrowser->textfont(FL_COURIER);
|
|
chkCabBrowser->textsize(12);
|
|
#endif
|
|
for( int i = 0; i < qsodb.nbrRecs(); i++ ) {
|
|
rec = qsodb.getRec (i);
|
|
memset(line, 0, sizeof(line));
|
|
snprintf(line,sizeof(line),"%8s %4s %-10s %-10s %-s",
|
|
rec->getField(QSO_DATE),
|
|
time4(rec->getField(TIME_OFF)),
|
|
rec->getField(CALL),
|
|
szfreq(rec->getField(FREQ)),
|
|
rec->getField(MODE) );
|
|
std::cout << line << "\n";
|
|
chkCabBrowser->add(line);
|
|
}
|
|
wCabrillo->show();
|
|
}
|
|
|
|
void cabrillo_append_qso (FILE *fp, cQsoRec *rec)
|
|
{
|
|
char freq[16] = "";
|
|
string rst_in, rst_out, exch_in, exch_out, date, time, mode, mycall, call, exch;
|
|
string qsoline = "QSO: ";
|
|
int ifreq = 0;
|
|
size_t len = 0;
|
|
size_t p = 0;
|
|
|
|
exch_out.clear();
|
|
exch_in.clear();
|
|
exch.clear();
|
|
|
|
if (btnCabFreq->value()) {
|
|
ifreq = (int)(1000.0 * atof(rec->getField(FREQ)));
|
|
snprintf(freq, sizeof(freq), "%7d", ifreq);
|
|
qsoline.append(freq); qsoline.append(" ");
|
|
}
|
|
|
|
if (btnCabMode->value()) {
|
|
mode = rec->getField(MODE);
|
|
if (mode.compare("USB") == 0 || mode.compare("LSB") == 0 ||
|
|
mode.compare("SSB") == 0 || mode.compare("PH") == 0 ) mode = "PH";
|
|
else if (mode.compare("FM") == 0 || mode.compare("CW") == 0 ) ;
|
|
else mode = "RY";
|
|
qsoline.append(mode); qsoline.append(" ");
|
|
}
|
|
|
|
if (btnCabQSOdate->value()) {
|
|
date = rec->getField(progdefaults.sort_date_time_off ? QSO_DATE_OFF : QSO_DATE);
|
|
date.insert(4,"-");
|
|
date.insert(7,"-");
|
|
qsoline.append(date); qsoline.append(" ");
|
|
}
|
|
|
|
if (btnCabTimeOFF->value()) {
|
|
time = rec->getField(progdefaults.sort_date_time_off ? TIME_OFF : TIME_ON);
|
|
qsoline.append(time4(time.c_str())); qsoline.append(" ");
|
|
}
|
|
|
|
mycall = progdefaults.myCall;
|
|
if (mycall.length() > 13) mycall = mycall.substr(0,13);
|
|
len = mycall.length();
|
|
if (len < 13) mycall.append(13 - len, ' ');
|
|
qsoline.append(mycall); qsoline.append(" ");
|
|
|
|
if (btnCabRSTsent->value() || contestnbr == BARTG_RTTY) {
|
|
rst_out = rec->getField(RST_SENT);
|
|
if (rst_out.length() > 3) rst_out = rst_out.substr(0, 3);
|
|
len = rst_out.length();
|
|
if (len < 3) rst_out.append(3 - len, ' ');
|
|
exch_out.append(rst_out).append(" ");
|
|
}
|
|
|
|
if (btnCabSerialOUT->value() || contestnbr == BARTG_RTTY) {
|
|
exch_out.append(rec->getField(STX)).append(" ");
|
|
}
|
|
|
|
if (btnCabMyXchg->value()) {
|
|
exch = rec->getField(MYXCHG);
|
|
if (!exch.empty())
|
|
exch_out.append(rec->getField(MYXCHG)).append(" ");
|
|
}
|
|
|
|
if (contestnbr == BARTG_RTTY) {
|
|
string toff = rec->getField(TIME_OFF);
|
|
if (toff.length() > 4) toff = toff.substr(0,4);
|
|
toff = toff.append(" ");
|
|
exch_out.append(toff);
|
|
}
|
|
|
|
if (exch_out.length() > 20) exch_out = exch_out.substr(0,20);
|
|
len = exch_out.length();
|
|
if (len < 20) exch_out.append(20 - len, ' ');
|
|
|
|
qsoline.append(exch_out);
|
|
|
|
if (btnCabCall->value()) {
|
|
call = rec->getField(CALL);
|
|
if (call.length() > 13) call = call.substr(0,13);
|
|
len = call.length();
|
|
if (len < 13) call.append(13 - len, ' ');
|
|
qsoline.append(call); qsoline.append(" ");
|
|
}
|
|
|
|
if (btnCabRSTrcvd->value()) {
|
|
rst_in = rec->getField(RST_RCVD);
|
|
if (rst_in.length() > 3) rst_in = rst_in.substr(0,3);
|
|
len = rst_in.length();
|
|
if (len < 3) rst_in.append(3 - len, ' ');
|
|
qsoline.append(rst_in); qsoline.append(" ");
|
|
}
|
|
|
|
if (btnCabSerialIN->value()) {
|
|
exch_in = exch_in.append(rec->getField(SRX));
|
|
if (exch_in.length())
|
|
exch_in += ' ';
|
|
}
|
|
|
|
if (btnCabXchgIn->value()) {
|
|
exch = rec->getField(XCHG1);
|
|
while ((p = exch.find(":")) != string::npos) exch.erase(p,1);
|
|
while ((p = exch.find(" ")) != string::npos) exch.erase(p,1);
|
|
if (exch[0] == ' ') exch.erase(0,1);
|
|
exch_in.append(exch);
|
|
}
|
|
|
|
if (exch_in.length() > 14) exch_in = exch_in.substr(0,14);
|
|
len = exch_in.length();
|
|
if (len < 14) exch_in.append(14 - len, ' ');
|
|
|
|
qsoline.append(exch_in);
|
|
|
|
fprintf (fp, "%s\n", qsoline.c_str());
|
|
return;
|
|
}
|
|
|
|
void WriteCabrillo()
|
|
{
|
|
if (chkCabBrowser->nchecked() == 0) return;
|
|
|
|
cQsoRec *rec;
|
|
|
|
string title = _("Create cabrillo report");
|
|
string filters = "TEXT\t*.txt";
|
|
#ifdef __APPLE__
|
|
filters.append("\n");
|
|
#endif
|
|
string strContest = "";
|
|
|
|
const char* p = FSEL::saveas( title.c_str(), filters.c_str(), "contest.txt");
|
|
|
|
if (!p) return;
|
|
if (!*p) return;
|
|
|
|
for (int i = 0; i < chkCabBrowser->FLTK_nitems(); i++) {
|
|
if (chkCabBrowser->checked(i + 1)) {
|
|
rec = qsodb.getRec(i);
|
|
rec->putField(EXPORT, "E");
|
|
qsodb.qsoUpdRec (i, rec);
|
|
}
|
|
}
|
|
|
|
string sp = p;
|
|
if (sp.find(".txt") == string::npos) sp.append(".txt");
|
|
FILE *cabFile = fopen (p, "w");
|
|
if (!cabFile)
|
|
return;
|
|
|
|
strContest = cboContest->value();
|
|
contestnbr = (icontest)cboContest->index();
|
|
|
|
fprintf (cabFile,
|
|
"START-OF-LOG: 3.0\n\
|
|
CREATED-BY: %s %s\n\
|
|
\n\
|
|
# The callsign used during the contest.\n\
|
|
CALLSIGN: %s\n\
|
|
\n\
|
|
# ASSISTED or NON-ASSISTED\n\
|
|
CATEGORY-ASSISTED: \n\
|
|
\n\
|
|
# Band: ALL, 160M, 80M, 40M, 20M, 15M, 10M, 6M, 2M, 222, 432, 902, 1.2G\n\
|
|
CATEGORY-BAND: \n\
|
|
\n\
|
|
# Mode: SSB, CW, RTTY, MIXED \n\
|
|
CATEGORY-MODE: \n\
|
|
\n\
|
|
# Operator: SINGLE-OP, MULTI-OP, CHECKLOG \n\
|
|
CATEGORY-OPERATOR: \n\
|
|
\n\
|
|
# Power: HIGH, LOW, QRP \n\
|
|
CATEGORY-POWER: \n\
|
|
\n\
|
|
# Station: FIXED, MOBILE, PORTABLE, ROVER, EXPEDITION, HQ, SCHOOL \n\
|
|
CATEGORY-STATION: \n\
|
|
\n\
|
|
# Time: 6-HOURS, 12-HOURS, 24-HOURS \n\
|
|
CATEGORY-TIME: \n\
|
|
\n\
|
|
# Transmitter: ONE, TWO, LIMITED, UNLIMITED, SWL \n\
|
|
CATEGORY-TRANSMITTER: \n\
|
|
\n\
|
|
# Overlay: ROOKIE, TB-WIRES, NOVICE-TECH, OVER-50 \n\
|
|
CATEGORY-OVERLAY: \n\
|
|
\n\
|
|
# Integer number\n\
|
|
CLAIMED-SCORE: \n\
|
|
\n\
|
|
# Name of the radio club with which the score should be aggregated.\n\
|
|
CLUB: \n\
|
|
\n\
|
|
# Contest: AP-SPRINT, ARRL-10, ARRL-160, ARRL-DX-CW, ARRL-DX-SSB, ARRL-SS-CW,\n\
|
|
# ARRL-SS-SSB, ARRL-UHF-AUG, ARRL-VHF-JAN, ARRL-VHF-JUN, ARRL-VHF-SEP,\n\
|
|
# ARRL-RTTY, BARTG-RTTY, CQ-160-CW, CQ-160-SSB, CQ-WPX-CW, CQ-WPX-RTTY,\n\
|
|
# CQ-WPX-SSB, CQ-VHF, CQ-WW-CW, CQ-WW-RTTY, CQ-WW-SSB, DARC-WAEDC-CW,\n\
|
|
# DARC-WAEDC-RTTY, DARC-WAEDC-SSB, FCG-FQP, IARU-HF, JIDX-CW, JIDX-SSB,\n\
|
|
# NAQP-CW, NAQP-RTTY, NAQP-SSB, NA-SPRINT-CW, NA-SPRINT-SSB, NCCC-CQP,\n\
|
|
# NEQP, OCEANIA-DX-CW, OCEANIA-DX-SSB, RDXC, RSGB-IOTA, SAC-CW, SAC-SSB,\n\
|
|
# STEW-PERRY, TARA-RTTY \n\
|
|
CONTEST: %s\n\
|
|
\n\
|
|
# Optional email address\n\
|
|
EMAIL: \n\
|
|
\n\
|
|
LOCATION: \n\
|
|
\n\
|
|
# Operator name\n\
|
|
NAME: \n\
|
|
\n\
|
|
# Maximum 4 address lines.\n\
|
|
ADDRESS: \n\
|
|
ADDRESS: \n\
|
|
ADDRESS: \n\
|
|
ADDRESS: \n\
|
|
\n\
|
|
# A space-delimited list of operator callsign(s). \n\
|
|
OPERATORS: \n\
|
|
\n\
|
|
# Offtime yyyy-mm-dd nnnn yyyy-mm-dd nnnn \n\
|
|
# OFFTIME: \n\
|
|
\n\
|
|
# Soapbox comments.\n\
|
|
SOAPBOX: \n\
|
|
SOAPBOX: \n\
|
|
SOAPBOX: \n\n",
|
|
PACKAGE_NAME, PACKAGE_VERSION,
|
|
progdefaults.myCall.c_str(),
|
|
strContest.c_str() );
|
|
|
|
qsodb.SortByDate(progdefaults.sort_date_time_off);
|
|
for (int i = 0; i < qsodb.nbrRecs(); i++) {
|
|
rec = qsodb.getRec(i);
|
|
if (rec->getField(EXPORT)[0] == 'E') {
|
|
cabrillo_append_qso(cabFile, rec);
|
|
rec->putField(EXPORT,"");
|
|
qsodb.qsoUpdRec(i, rec);
|
|
}
|
|
}
|
|
fprintf(cabFile, "END-OF-LOG:\n");
|
|
fclose (cabFile);
|
|
return;
|
|
}
|
|
|
|
#if HAVE_STD_HASH
|
|
# include <unordered_map>
|
|
typedef std::unordered_map<string, unsigned> dxcc_entity_cache_t;
|
|
#elif HAVE_STD_TR1_HASH
|
|
# include <tr1/unordered_map>
|
|
typedef tr1::unordered_map<string, unsigned> dxcc_entity_cache_t;
|
|
#else
|
|
# error "No std::hash or std::tr1::hash support"
|
|
#endif
|
|
|
|
static dxcc_entity_cache_t dxcc_entity_cache;
|
|
static bool dxcc_entity_cache_enabled = false;
|
|
|
|
#include "dxcc.h"
|
|
|
|
static void dxcc_entity_cache_clear(void)
|
|
{
|
|
if (dxcc_entity_cache_enabled)
|
|
dxcc_entity_cache.clear();
|
|
}
|
|
|
|
static void dxcc_entity_cache_add(cQsoRec* r)
|
|
{
|
|
if (!dxcc_entity_cache_enabled | !r)
|
|
return;
|
|
|
|
const dxcc* e = dxcc_lookup(r->getField(CALL));
|
|
if (e)
|
|
dxcc_entity_cache[e->country]++;
|
|
}
|
|
static void dxcc_entity_cache_add(cQsoDb& db)
|
|
{
|
|
if (!dxcc_entity_cache_enabled)
|
|
return;
|
|
|
|
int n = db.nbrRecs();
|
|
for (int i = 0; i < n; i++)
|
|
dxcc_entity_cache_add(db.getRec(i));
|
|
if (!dxcc_entity_cache.empty())
|
|
LOG_INFO("Found %" PRIuSZ " countries in %d QSO records",
|
|
dxcc_entity_cache.size(), n);
|
|
}
|
|
|
|
static void dxcc_entity_cache_rm(cQsoRec* r)
|
|
{
|
|
if (!dxcc_entity_cache_enabled || !r)
|
|
return;
|
|
|
|
const dxcc* e = dxcc_lookup(r->getField(CALL));
|
|
if (!e)
|
|
return;
|
|
dxcc_entity_cache_t::iterator i = dxcc_entity_cache.find(e->country);
|
|
if (i != dxcc_entity_cache.end()) {
|
|
if (i->second)
|
|
i->second--;
|
|
else
|
|
dxcc_entity_cache.erase(i);
|
|
}
|
|
}
|
|
|
|
void dxcc_entity_cache_enable(bool v)
|
|
{
|
|
if (dxcc_entity_cache_enabled == v)
|
|
return;
|
|
|
|
dxcc_entity_cache_clear();
|
|
if ((dxcc_entity_cache_enabled = v))
|
|
dxcc_entity_cache_add(qsodb);
|
|
}
|
|
|
|
bool qsodb_dxcc_entity_find(const char* country)
|
|
{
|
|
return dxcc_entity_cache.find(country) != dxcc_entity_cache.end();
|
|
}
|