2018-10-25 00:00:19 +01:00
#include "WorkedBefore.hpp"
2018-11-14 10:44:36 -05:00
#include <functional>
2018-11-30 16:26:46 +00:00
#include <stdexcept>
2018-11-14 10:44:36 -05:00
#include <boost/functional/hash.hpp>
2018-10-25 00:00:19 +01:00
#include <boost/multi_index_container.hpp>
2018-11-14 10:44:36 -05:00
#include <boost/multi_index/hashed_index.hpp>
2018-10-25 00:00:19 +01:00
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/key_extractors.hpp>
2019-08-09 11:25:50 +01:00
#include <boost/range/iterator_range.hpp>
2020-09-09 22:40:07 +01:00
#include <QCoreApplication>
2018-11-30 16:26:46 +00:00
#include <QtConcurrent/QtConcurrentRun>
#include <QFuture>
#include <QFutureWatcher>
2018-11-14 10:44:36 -05:00
#include <QChar>
#include <QString>
2018-10-25 00:00:19 +01:00
#include <QByteArray>
#include <QStandardPaths>
#include <QDir>
2018-11-30 16:26:46 +00:00
#include <QFileInfo>
2018-10-25 00:00:19 +01:00
#include <QFile>
#include <QTextStream>
2020-09-07 20:36:09 +01:00
#include <QDateTime>
2019-05-29 23:35:18 +01:00
#include "Configuration.hpp"
2020-09-07 20:36:09 +01:00
#include "revision_utils.hpp"
2023-03-15 20:42:03 -07:00
#include "Logger.hpp"
2018-11-14 10:44:36 -05:00
#include "qt_helpers.hpp"
2018-10-25 00:00:19 +01:00
#include "pimpl_impl.hpp"
2018-11-30 16:26:46 +00:00
#include "moc_WorkedBefore.cpp"
2018-10-26 03:24:36 +01:00
using namespace boost :: multi_index ;
2018-11-14 10:44:36 -05:00
// hash function for QString members in hashed indexes
inline
std :: size_t hash_value ( QString const & s )
{
return std :: hash < QString > {} ( s );
}
//
2018-10-25 00:00:19 +01:00
// worked before set element
2018-11-14 10:44:36 -05:00
//
2018-10-25 00:00:19 +01:00
struct worked_entry
{
2018-10-26 03:24:36 +01:00
explicit worked_entry ( QString const & call , QString const & grid , QString const & band
, QString const & mode , QString const & country , AD1CCty :: Continent continent
, int CQ_zone , int ITU_zone )
: call_ { call }
, grid_ { grid }
, band_ { band }
, mode_ { mode }
, country_ { country }
, continent_ { continent }
, CQ_zone_ { CQ_zone }
, ITU_zone_ { ITU_zone }
2018-10-25 00:00:19 +01:00
{
}
2018-10-26 03:24:36 +01:00
QString call_ ;
QString grid_ ;
QString band_ ;
QString mode_ ;
QString country_ ;
AD1CCty :: Continent continent_ ;
int CQ_zone_ ;
int ITU_zone_ ;
};
2018-11-14 10:44:36 -05:00
bool operator == ( worked_entry const & lhs , worked_entry const & rhs )
{
return
lhs . continent_ == rhs . continent_ // check 1st as it is fast
&& lhs . CQ_zone_ == rhs . CQ_zone_ // ditto
&& lhs . ITU_zone_ == rhs . ITU_zone_ // ditto
&& lhs . call_ == rhs . call_ // check the rest in decreasing
&& lhs . grid_ == rhs . grid_ // domain size order to shortcut
&& lhs . country_ == rhs . country_ // differences as quickly as possible
&& lhs . band_ == rhs . band_
&& lhs . mode_ == rhs . mode_ ;
}
std :: size_t hash_value ( worked_entry const & we )
{
std :: size_t seed { 0 };
boost :: hash_combine ( seed , we . call_ );
boost :: hash_combine ( seed , we . grid_ );
boost :: hash_combine ( seed , we . band_ );
boost :: hash_combine ( seed , we . mode_ );
boost :: hash_combine ( seed , we . country_ );
boost :: hash_combine ( seed , we . continent_ );
boost :: hash_combine ( seed , we . CQ_zone_ );
boost :: hash_combine ( seed , we . ITU_zone_ );
return seed ;
}
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << ( QDebug dbg , worked_entry const & e )
{
QDebugStateSaver saver { dbg };
dbg . nospace () << "worked_entry("
<< e . call_ << ", "
<< e . grid_ << ", "
<< e . band_ << ", "
<< e . mode_ << ", "
<< e . country_ << ", "
<< e . continent_ << ", "
<< e . CQ_zone_ << ", "
<< e . ITU_zone_ << ')' ;
return dbg ;
}
#endif
// less then predidate for the Continent enum class, needed for
// ordered indexes
2018-10-26 03:24:36 +01:00
struct Continent_less
{
2018-10-26 16:51:54 +01:00
bool operator () ( AD1CCty :: Continent lhs , AD1CCty :: Continent rhs ) const
2018-10-26 03:24:36 +01:00
{
return static_cast < int > ( lhs ) < static_cast < int > ( rhs );
}
2018-10-25 00:00:19 +01:00
};
2018-11-14 10:44:36 -05:00
// index tags
2018-10-25 00:00:19 +01:00
struct call_mode_band {};
struct call_band {};
struct grid_mode_band {};
struct grid_band {};
struct entity_mode_band {};
struct entity_band {};
2018-10-26 03:24:36 +01:00
struct continent_mode_band {};
struct continent_band {};
struct CQ_zone_mode_band {};
struct CQ_zone_band {};
struct ITU_zone_mode_band {};
struct ITU_zone_band {};
2018-10-25 00:00:19 +01:00
2018-11-14 10:44:36 -05:00
// set with multiple ordered unique indexes that allow for optimally
// efficient determination of various categories of worked before
// status
2018-10-26 03:24:36 +01:00
typedef multi_index_container <
2018-10-25 00:00:19 +01:00
worked_entry ,
2018-10-26 03:24:36 +01:00
indexed_by <
2018-11-14 10:44:36 -05:00
// basic unordered set constraint - we don't need duplicate worked entries
hashed_unique < identity < worked_entry >> ,
//
// The following indexes are used to discover worked before stuff.
//
// They are ordered so as to support partial lookups and
// non-unique because container inserts must be valid for all
// indexes.
//
2018-10-25 00:00:19 +01:00
// call+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < call_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: call_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-25 00:00:19 +01:00
// call+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < call_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: call_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-25 00:00:19 +01:00
// grid+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < grid_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: grid_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-25 00:00:19 +01:00
// grid+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < grid_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: grid_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-25 00:00:19 +01:00
// country+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < entity_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: country_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-26 03:24:36 +01:00
// country+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < entity_band > ,
composite_key < worked_entry ,
member < worked_entry , QString , & worked_entry :: country_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-26 03:24:36 +01:00
// continent+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < continent_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , AD1CCty :: Continent , & worked_entry :: continent_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > ,
composite_key_compare < Continent_less , std :: less < QString > , std :: less < QString > > > ,
2018-10-26 03:24:36 +01:00
// continent+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < continent_band > ,
composite_key < worked_entry ,
member < worked_entry , AD1CCty :: Continent , & worked_entry :: continent_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > ,
composite_key_compare < Continent_less , std :: less < QString > > > ,
2018-10-26 03:24:36 +01:00
// CQ-zone+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < CQ_zone_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , int , & worked_entry :: CQ_zone_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-26 03:24:36 +01:00
// CQ-zone+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < CQ_zone_band > ,
composite_key < worked_entry ,
member < worked_entry , int , & worked_entry :: CQ_zone_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-26 03:24:36 +01:00
// ITU-zone+mode+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < ITU_zone_mode_band > ,
composite_key < worked_entry ,
member < worked_entry , int , & worked_entry :: ITU_zone_ > ,
member < worked_entry , QString , & worked_entry :: mode_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > ,
2018-10-26 03:24:36 +01:00
// ITU-zone+band
2018-11-14 10:44:36 -05:00
ordered_non_unique < tag < ITU_zone_band > ,
composite_key < worked_entry ,
member < worked_entry , int , & worked_entry :: ITU_zone_ > ,
member < worked_entry , QString , & worked_entry :: band_ > > > >
> worked_before_database_type ;
2018-10-25 00:00:19 +01:00
namespace
{
auto const logFileName = "wsjtx_log.adi" ;
2023-03-15 20:42:03 -07:00
// Exception class suitable for using with QtConcurrent across
2018-11-30 16:26:46 +00:00
// thread boundaries
class LoaderException final
: public QException
{
public :
LoaderException ( std :: exception const & e ) : error_ { e . what ()} {}
QString error () const { return error_ ;}
void raise () const override { throw * this ;}
LoaderException * clone () const override { return new LoaderException { * this };}
private :
QString error_ ;
};
2018-10-26 03:24:36 +01:00
QString extractField ( QString const & record , QString const & fieldName )
2018-10-25 00:00:19 +01:00
{
int fieldNameIndex = record . indexOf ( '<' + fieldName + ':' , 0 , Qt :: CaseInsensitive );
if ( fieldNameIndex >= 0 )
{
int closingBracketIndex = record . indexOf ( '>' , fieldNameIndex );
int fieldLengthIndex = record . indexOf ( ':' , fieldNameIndex ); // find the size delimiter
int dataTypeIndex = - 1 ;
if ( fieldLengthIndex >= 0 )
{
dataTypeIndex = record . indexOf ( ':' , fieldLengthIndex + 1 ); // check for a second : indicating there is a data type
if ( dataTypeIndex > closingBracketIndex )
dataTypeIndex = - 1 ; // second : was found but it was beyond the closing >
}
2018-11-30 16:26:46 +00:00
else
{
2020-09-09 22:40:07 +01:00
throw LoaderException ( std :: runtime_error { QCoreApplication :: translate ( "WorkedBefore" , "Invalid ADIF field %0: %1" ). arg ( fieldName ). arg ( record ). toLocal8Bit ()});
2018-11-30 16:26:46 +00:00
}
2018-10-25 00:00:19 +01:00
2018-11-30 16:26:46 +00:00
if ( closingBracketIndex > fieldNameIndex && fieldLengthIndex > fieldNameIndex && fieldLengthIndex < closingBracketIndex )
2018-10-25 00:00:19 +01:00
{
int fieldLengthCharCount = closingBracketIndex - fieldLengthIndex - 1 ;
if ( dataTypeIndex >= 0 )
fieldLengthCharCount -= 2 ; // data type indicator is always a colon followed by a single character
QString fieldLengthString = record . mid ( fieldLengthIndex + 1 , fieldLengthCharCount );
int fieldLength = fieldLengthString . toInt ();
if ( fieldLength > 0 )
{
2018-10-26 03:24:36 +01:00
return record . mid ( closingBracketIndex + 1 , fieldLength );
2018-10-25 00:00:19 +01:00
}
}
2018-11-30 16:26:46 +00:00
else
{
2020-09-09 22:40:07 +01:00
throw LoaderException ( std :: runtime_error { QCoreApplication :: translate ( "WorkedBefore" , "Malformed ADIF field %0: %1" ). arg ( fieldName ). arg ( record ). toLocal8Bit ()});
2018-11-30 16:26:46 +00:00
}
2018-10-25 00:00:19 +01:00
}
2018-11-30 16:26:46 +00:00
return QString {};
}
worked_before_database_type loader ( QString const & path , AD1CCty const * prefixes )
{
worked_before_database_type worked ;
QFile inputFile { path };
if ( inputFile . exists ())
{
if ( inputFile . open ( QFile :: ReadOnly ))
{
QTextStream in { & inputFile };
QString buffer ;
bool pre_read { false };
int end_position { - 1 };
// skip optional header record
do
{
buffer += in . readLine () + '\n' ;
if ( buffer . startsWith ( QChar { '<' })) // denotes no header
{
pre_read = true ;
}
else
{
end_position = buffer . indexOf ( "<EOH>" , 0 , Qt :: CaseInsensitive );
}
}
while ( ! in . atEnd () && ! pre_read && end_position < 0 );
if ( ! pre_read ) // found header
{
if ( end_position < 0 )
{
2020-09-09 22:40:07 +01:00
throw LoaderException ( std :: runtime_error { QCoreApplication :: translate ( "WorkedBefore" , "Invalid ADIF header" ). toLocal8Bit ()});
2018-11-30 16:26:46 +00:00
}
buffer . remove ( 0 , end_position + 5 );
}
while ( ! in . atEnd ())
{
end_position = buffer . indexOf ( "<EOR>" , 0 , Qt :: CaseInsensitive );
do
{
if ( ! in . atEnd () && end_position < 0 )
{
buffer += in . readLine () + '\n' ;
}
}
while (( end_position = buffer . indexOf ( "<EOR>" , 0 , Qt :: CaseInsensitive )) < 0 && ! in . atEnd ());
2018-12-10 17:06:02 +00:00
if ( end_position >= 0 ) // require valid ADIF record
// with terminator
2018-11-30 16:26:46 +00:00
{
2018-12-10 17:06:02 +00:00
auto record = buffer . left ( end_position + 5 ). trimmed ();
auto next_record = buffer . indexOf ( QChar { '<' }, end_position + 5 );
buffer . remove ( 0 , next_record >= 0 ? next_record : buffer . size ());
record = record . mid ( record . indexOf ( QChar { '<' }));
auto call = extractField ( record , "CALL" );
if ( call . size ()) // require CALL field before we
// will parse a record
{
auto const & entity = prefixes -> lookup ( call );
2019-05-22 02:23:04 +01:00
auto mode = extractField ( record , "MODE" ). toUpper ();
if ( ! mode . size () || "MFSK" == mode )
{
mode = extractField ( record , "SUBMODE" ). toUpper ();
}
2018-12-10 17:06:02 +00:00
worked . emplace ( call . toUpper ()
, extractField ( record , "GRIDSQUARE" ). left ( 4 ). toUpper () // not interested in 6-digit grids
, extractField ( record , "BAND" ). toUpper ()
2019-05-22 02:23:04 +01:00
, mode
2018-12-10 17:06:02 +00:00
, entity . entity_name
, entity . continent
, entity . CQ_zone
, entity . ITU_zone );
}
2018-11-30 16:26:46 +00:00
}
}
}
else
{
2020-09-09 22:40:07 +01:00
throw LoaderException ( std :: runtime_error { QCoreApplication :: translate ( "WorkedBefore" , "Error opening ADIF log file for read: %0" ). arg ( inputFile . errorString ()). toLocal8Bit ()});
2018-11-30 16:26:46 +00:00
}
}
return worked ;
2018-10-25 00:00:19 +01:00
}
}
class WorkedBefore :: impl final
{
public :
2019-05-29 23:35:18 +01:00
impl ( Configuration const * configuration )
2019-08-09 11:25:50 +01:00
: configuration_ { configuration }
, path_ { QDir { QStandardPaths :: writableLocation ( QStandardPaths :: DataLocation )}. absoluteFilePath ( logFileName )}
2019-05-29 23:35:18 +01:00
, prefixes_ { configuration }
2018-10-25 00:00:19 +01:00
{
}
2018-11-30 16:26:46 +00:00
void reload ()
{
2023-03-15 20:42:03 -07:00
prefixes_ . reload ( configuration_ );
2018-11-30 16:26:46 +00:00
async_loader_ = QtConcurrent :: run ( loader , path_ , & prefixes_ );
loader_watcher_ . setFuture ( async_loader_ );
}
2019-08-09 11:25:50 +01:00
Configuration const * configuration_ ;
2018-10-25 00:00:19 +01:00
QString path_ ;
2018-10-26 03:24:36 +01:00
AD1CCty prefixes_ ;
2018-11-30 16:26:46 +00:00
QFutureWatcher < worked_before_database_type > loader_watcher_ ;
QFuture < worked_before_database_type > async_loader_ ;
2018-11-14 10:44:36 -05:00
worked_before_database_type worked_ ;
2018-10-25 00:00:19 +01:00
};
2019-05-29 23:35:18 +01:00
WorkedBefore :: WorkedBefore ( Configuration const * configuration )
: m_ { configuration }
2018-10-25 00:00:19 +01:00
{
2019-05-29 23:35:18 +01:00
Q_ASSERT ( configuration );
2018-11-30 21:50:44 +00:00
connect ( & m_ -> loader_watcher_ , & QFutureWatcher < worked_before_database_type >:: finished , [ this ] () {
2018-11-30 16:26:46 +00:00
QString error ;
size_t n { 0 };
try
2018-10-25 00:00:19 +01:00
{
2018-11-30 16:26:46 +00:00
m_ -> worked_ = m_ -> loader_watcher_ . result ();
n = m_ -> worked_ . size ();
2018-10-25 00:00:19 +01:00
}
2018-11-30 16:26:46 +00:00
catch ( LoaderException const & e )
2018-10-25 00:00:19 +01:00
{
2018-11-30 16:26:46 +00:00
error = e . error ();
2018-10-25 00:00:19 +01:00
}
2023-03-15 20:42:03 -07:00
QString cty_ver = m_ -> prefixes_ . version ();
LOG_DEBUG ( QString { "WorkedBefore::reload: CTY.DAT version %1" }. arg ( cty_ver ));
Q_EMIT finished_loading ( n , cty_ver , error );
2018-11-30 16:26:46 +00:00
});
reload ();
}
2023-03-15 20:42:03 -07:00
QString WorkedBefore :: cty_version () const
{
return m_ -> prefixes_ . version ();
}
2018-11-30 16:26:46 +00:00
void WorkedBefore :: reload ()
{
m_ -> reload ();
2018-10-25 00:00:19 +01:00
}
WorkedBefore ::~ WorkedBefore ()
{
}
QString const & WorkedBefore :: path () const
{
return m_ -> path_ ;
}
2019-05-30 22:20:09 +01:00
AD1CCty const * WorkedBefore :: countries () const
2018-10-25 00:00:19 +01:00
{
2019-05-30 22:20:09 +01:00
return & m_ -> prefixes_ ;
2018-10-25 00:00:19 +01:00
}
bool WorkedBefore :: add ( QString const & call
, QString const & grid
, QString const & band
, QString const & mode
, QByteArray const & ADIF_record )
{
if ( call . size ())
{
2018-10-26 03:24:36 +01:00
auto const & entity = m_ -> prefixes_ . lookup ( call );
2018-10-25 00:00:19 +01:00
QFile file { m_ -> path_ };
if ( ! file . open ( QIODevice :: Text | QIODevice :: Append ))
{
return false ;
}
else
{
QTextStream out { & file };
if ( ! file . size ())
{
2020-09-07 20:36:09 +01:00
auto ts = QDateTime :: currentDateTimeUtc (). toString ( "yyyyMMdd HHmmss" );
auto ver = version ( true );
out << // new file
QString {
"ADIF Export \n "
"<adif_ver:5>3.1.1 \n "
"<created_timestamp:15>%0 \n "
"<programid:6>WSJT-X \n "
"<programversion:%1>%2 \n "
"<eoh>"
}. arg ( ts ). arg ( ver . size ()). arg ( ver )
<<
2020-06-13 16:04:41 +01:00
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
endl
#else
Qt :: endl
#endif
;
2018-10-25 00:00:19 +01:00
}
2020-06-13 16:04:41 +01:00
out << ADIF_record << " <eor>" <<
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
endl
#else
Qt :: endl
#endif
;
2018-10-25 00:00:19 +01:00
}
2018-11-29 00:56:53 +00:00
m_ -> worked_ . emplace ( call . toUpper (), grid . left ( 4 ). toUpper (), band . toUpper (), mode . toUpper ()
, entity . entity_name , entity . continent , entity . CQ_zone , entity . ITU_zone );
2018-10-25 00:00:19 +01:00
}
return true ;
}
bool WorkedBefore :: country_worked ( QString const & country , QString const & mode , QString const & band ) const
{
if ( mode . size ())
{
if ( band . size ())
{
return
country . size ()
&& m_ -> worked_ . get < entity_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < entity_mode_band > (). find ( std :: make_tuple ( country , mode . toUpper (), band . toUpper ()));
2018-10-25 00:00:19 +01:00
}
else
{
// partial key lookup
return
country . size ()
&& m_ -> worked_ . get < entity_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < entity_mode_band > (). find ( std :: make_tuple ( country , mode . toUpper ()));
2018-10-25 00:00:19 +01:00
}
}
else
{
if ( band . size ())
{
return
country . size ()
&& m_ -> worked_ . get < entity_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < entity_band > (). find ( std :: make_tuple ( country , band . toUpper ()));
2018-10-25 00:00:19 +01:00
}
else
{
// partial key lookup
return
country . size ()
&& m_ -> worked_ . get < entity_band > (). end ()
2018-10-26 03:24:36 +01:00
!= m_ -> worked_ . get < entity_band > (). find ( country );
2018-10-25 00:00:19 +01:00
}
}
}
bool WorkedBefore :: grid_worked ( QString const & grid , QString const & mode , QString const & band ) const
{
2019-08-09 11:25:50 +01:00
auto gridsquare = grid . left ( 4 ). toUpper ();
if ( m_ -> configuration_ -> highlight_only_fields ())
2018-10-25 00:00:19 +01:00
{
2019-08-09 11:25:50 +01:00
// can't use a direct set find operation or a set operation with
// a (CompatibleKey, CompatibleCompare) concept so we must
// partially scan the index
auto range = boost :: make_iterator_range (
m_ -> worked_ . get < grid_mode_band > (). lower_bound ( gridsquare . left ( 2 ))
, m_ -> worked_ . get < grid_mode_band > (). upper_bound ( gridsquare . left ( 2 ) + "99" ));
for ( worked_entry const & worked : range )
2018-10-25 00:00:19 +01:00
{
2019-08-09 11:25:50 +01:00
if (( ! mode . size () || mode . toUpper () == worked . mode_ )
&& ( ! band . size () || worked . band_ == band . toUpper ()))
{
return true ;
}
2018-10-25 00:00:19 +01:00
}
}
else
{
2019-08-09 11:25:50 +01:00
if ( mode . size ())
2018-10-25 00:00:19 +01:00
{
2019-08-09 11:25:50 +01:00
if ( band . size ())
{
return m_ -> worked_ . get < grid_mode_band > (). end ()
!= m_ -> worked_ . get < grid_mode_band > (). find ( std :: make_tuple ( gridsquare , mode . toUpper (), band . toUpper ()));
}
else
{
// partial key lookup
return m_ -> worked_ . get < grid_mode_band > (). end ()
!= m_ -> worked_ . get < grid_mode_band > (). find ( std :: make_tuple ( gridsquare , mode . toUpper ()));
}
2018-10-25 00:00:19 +01:00
}
else
{
2019-08-09 11:25:50 +01:00
if ( band . size ())
{
return m_ -> worked_ . get < grid_band > (). end ()
!= m_ -> worked_ . get < grid_band > (). find ( std :: make_tuple ( gridsquare , band . toUpper ()));
}
else
{
// partial key lookup
return m_ -> worked_ . get < grid_band > (). end ()
!= m_ -> worked_ . get < grid_band > (). find ( gridsquare );
}
2018-10-25 00:00:19 +01:00
}
}
2019-08-09 11:25:50 +01:00
return false ;
2018-10-25 00:00:19 +01:00
}
bool WorkedBefore :: call_worked ( QString const & call , QString const & mode , QString const & band ) const
{
if ( mode . size ())
{
if ( band . size ())
{
return m_ -> worked_ . get < call_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < call_mode_band > (). find ( std :: make_tuple ( call . toUpper (), mode . toUpper (), band . toUpper ()));
2018-10-25 00:00:19 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < call_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < call_mode_band > (). find ( std :: make_tuple ( call . toUpper (), mode . toUpper ()));
2018-10-25 00:00:19 +01:00
}
}
else
{
if ( band . size ())
{
return m_ -> worked_ . get < call_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < call_band > (). find ( std :: make_tuple ( call . toUpper (), band . toUpper ()));
2018-10-25 00:00:19 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < call_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < call_band > (). find ( std :: make_tuple ( call . toUpper ()));
2018-10-26 03:24:36 +01:00
}
}
}
bool WorkedBefore :: continent_worked ( Continent continent , QString const & mode , QString const & band ) const
{
if ( mode . size ())
{
if ( band . size ())
{
return m_ -> worked_ . get < continent_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < continent_mode_band > (). find ( std :: make_tuple ( continent , mode . toUpper (), band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < continent_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < continent_mode_band > (). find ( std :: make_tuple ( continent , mode . toUpper ()));
2018-10-26 03:24:36 +01:00
}
}
else
{
if ( band . size ())
{
return m_ -> worked_ . get < continent_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < continent_band > (). find ( std :: make_tuple ( continent , band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < continent_band > (). end ()
!= m_ -> worked_ . get < continent_band > (). find ( continent );
}
}
}
bool WorkedBefore :: CQ_zone_worked ( int CQ_zone , QString const & mode , QString const & band ) const
{
if ( mode . size ())
{
if ( band . size ())
{
return m_ -> worked_ . get < CQ_zone_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < CQ_zone_mode_band > (). find ( std :: make_tuple ( CQ_zone , mode . toUpper (), band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < CQ_zone_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < CQ_zone_mode_band > (). find ( std :: make_tuple ( CQ_zone , mode . toUpper ()));
2018-10-26 03:24:36 +01:00
}
}
else
{
if ( band . size ())
{
return m_ -> worked_ . get < CQ_zone_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < CQ_zone_band > (). find ( std :: make_tuple ( CQ_zone , band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < CQ_zone_band > (). end ()
!= m_ -> worked_ . get < CQ_zone_band > (). find ( CQ_zone );
}
}
}
2023-03-15 20:42:03 -07:00
2018-10-26 03:24:36 +01:00
bool WorkedBefore :: ITU_zone_worked ( int ITU_zone , QString const & mode , QString const & band ) const
{
if ( mode . size ())
{
if ( band . size ())
{
return m_ -> worked_ . get < ITU_zone_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < ITU_zone_mode_band > (). find ( std :: make_tuple ( ITU_zone , mode . toUpper (), band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < ITU_zone_mode_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < ITU_zone_mode_band > (). find ( std :: make_tuple ( ITU_zone , mode . toUpper ()));
2018-10-26 03:24:36 +01:00
}
}
else
{
if ( band . size ())
{
return m_ -> worked_ . get < ITU_zone_band > (). end ()
2018-11-29 00:56:53 +00:00
!= m_ -> worked_ . get < ITU_zone_band > (). find ( std :: make_tuple ( ITU_zone , band . toUpper ()));
2018-10-26 03:24:36 +01:00
}
else
{
// partial key lookup
return m_ -> worked_ . get < ITU_zone_band > (). end ()
!= m_ -> worked_ . get < ITU_zone_band > (). find ( ITU_zone );
2018-10-25 00:00:19 +01:00
}
}
}
2023-03-15 20:42:03 -07:00