Merge branch 'develop' into feat-fst280

This commit is contained in:
Bill Somerville 2020-12-06 22:27:07 +00:00
commit 2964bc6295
No known key found for this signature in database
GPG Key ID: D864B06D1E81618F
6410 changed files with 10517 additions and 1361972 deletions

43
AppVersion/AppVersion.cpp Normal file
View File

@ -0,0 +1,43 @@
//
// wsjtx_app_version - a console application that outputs WSJT-X
// application version
//
// This application is only provided as a simple console application
//
//
#include <cstdlib>
#include <iostream>
#include <exception>
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include "revision_utils.hpp"
int main (int argc, char * argv[])
{
QCoreApplication app {argc, argv};
try
{
app.setApplicationName ("WSJT-X");
app.setApplicationVersion (version());
QCommandLineParser parser;
// parser.setApplicationDescription ("\n" PROJECT_DESCRIPTION);
parser.addHelpOption ();
parser.addVersionOption ();
parser.process (app);
return EXIT_SUCCESS;
}
catch (std::exception const& e)
{
std::cerr << "Error: " << e.what () << '\n';
}
catch (...)
{
std::cerr << "Unexpected error\n";
}
return -1;
}

View File

@ -2,12 +2,15 @@
#include <cstdlib>
#include <cmath>
#include <iomanip>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QAudioInput>
#include <QSysInfo>
#include <QDebug>
#include "Logger.hpp"
#include "moc_soundin.cpp"
bool SoundInput::checkStream ()
@ -178,8 +181,18 @@ void SoundInput::reset (bool report_dropped_frames)
if (cummulative_lost_usec_ != std::numeric_limits<qint64>::min () && report_dropped_frames)
{
auto lost_usec = elapsed_usecs - m_stream->processedUSecs () - cummulative_lost_usec_;
Q_EMIT dropped_frames (m_stream->format ().framesForDuration (lost_usec), lost_usec);
//qDebug () << "SoundInput::reset: frames dropped:" << m_stream->format ().framesForDuration (lost_usec) << "sec:" << lost_usec / 1.e6;
if (std::abs (lost_usec) > 48000 / 5)
{
LOG_WARN ("Detected dropped audio source samples: "
<< m_stream->format ().framesForDuration (lost_usec)
<< " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)")
}
else if (std::abs (lost_usec) > 5 * 48000)
{
LOG_ERROR ("Detected excessive dropped audio source samples: "
<< m_stream->format ().framesForDuration (lost_usec)
<< " (" << std::setprecision (4) << lost_usec / 1.e6 << " S)")
}
}
cummulative_lost_usec_ = elapsed_usecs - m_stream->processedUSecs ();
}

View File

@ -40,7 +40,6 @@ public:
Q_SIGNAL void error (QString message) const;
Q_SIGNAL void status (QString message) const;
Q_SIGNAL void dropped_frames (qint32 dropped, qint64 usec);
private:
// used internally

View File

@ -7,6 +7,7 @@
#include <qmath.h>
#include <QDebug>
#include "Logger.hpp"
#include "Audio/AudioDevice.hpp"
#include "moc_soundout.cpp"
@ -104,15 +105,13 @@ void SoundOutput::restart (QIODevice * source)
// Windows implementation seems to forget the buffer size after a
// stop.
//qDebug () << "SoundOut default buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
if (m_framesBuffered)
if (m_framesBuffered > 0)
{
#if defined (Q_OS_WIN)
m_stream->setBufferSize (m_stream->format().bytesForFrames (m_framesBuffered));
#endif
}
m_stream->setCategory ("production");
m_stream->start (source);
// qDebug () << "SoundOut selected buffer size (bytes):" << m_stream->bufferSize () << "period size:" << m_stream->periodSize ();
LOG_INFO ("Selected buffer size (bytes): " << m_stream->bufferSize () << " period size: " << m_stream->periodSize ());
}
void SoundOutput::suspend ()

View File

@ -1,4 +1,4 @@
cmake_minimum_required (VERSION 3.9 FATAL_ERROR)
cmake_minimum_required (VERSION 3.7.2 FATAL_ERROR)
if (APPLE)
#
@ -13,7 +13,7 @@ if (APPLE)
set (CMAKE_OSX_DEPLOYMENT_TARGET 10.12
CACHE STRING "Earliest version of macOS supported
Earliest version we can support with Qt 5.12, C++11 & libc++ is 10.13.
Earliest version we can support with Qt 5.12, C++11 & libc++ is 10.12.
Do not override this if you intend to build an official deployable installer.")
endif (APPLE)
@ -29,7 +29,7 @@ if (POLICY CMP0043)
endif ()
if (POLICY CMP0048)
cmake_policy (SET CMP0048 NEW) # clear PROJECT_Version_* vriables if not set in project() command
cmake_policy (SET CMP0048 NEW) # clear PROJECT_Version_* variables if not set in project() command
endif ()
if (POLICY CMP0063)
@ -40,11 +40,15 @@ if (POLICY CMP0071)
cmake_policy (SET CMP0071 NEW) # run automoc and autouic on generated sources
endif ()
if (POLICY CMP0075)
cmake_policy (SET CMP0075 NEW) # honour CMAKE_REQUIRED_LIBRARIES in config checks
endif ()
project (wsjtx
VERSION 2.4.0.0
DESCRIPTION "WSJT-X: Digital Modes for Weak Signal Communications in Amateur Radio"
LANGUAGES C CXX Fortran
)
set (PROJECT_DESCRIPTION "WSJT-X: Digital Modes for Weak Signal Communications in Amateur Radio")
#
# Local CMake modules and support files
@ -115,9 +119,6 @@ option (UPDATE_TRANSLATIONS "Update source translation translations/*.ts
files (WARNING: make clean will delete the source .ts files! Danger!)")
option (WSJT_SHARED_RUNTIME "Debugging option that allows running from a shared Cloud directory.")
option (WSJT_QDEBUG_TO_FILE "Redirect Qt debuging messages to a trace file.")
option (WSJT_TRACE_CAT "Debugging option that turns on CAT diagnostics.")
option (WSJT_TRACE_CAT_POLLS "Debugging option that turns on CAT diagnostics during polling.")
option (WSJT_HAMLIB_TRACE "Debugging option that turns on minimal Hamlib internal diagnostics.")
option (WSJT_SOFT_KEYING "Apply a ramp to CW keying envelope to reduce transients." ON)
option (WSJT_SKIP_MANPAGES "Skip *nix manpage generation.")
option (WSJT_GENERATE_DOCS "Generate documentation files." ON)
@ -125,9 +126,6 @@ option (WSJT_RIG_NONE_CAN_SPLIT "Allow split operation with \"None\" as rig.")
option (WSJT_TRACE_UDP "Debugging option that turns on UDP message protocol diagnostics.")
option (WSJT_BUILD_UTILS "Build simulators and code demonstrators." ON)
CMAKE_DEPENDENT_OPTION (WSJT_HAMLIB_VERBOSE_TRACE "Debugging option that turns on full Hamlib internal diagnostics." OFF WSJT_HAMLIB_TRACE OFF)
CMAKE_DEPENDENT_OPTION (WSJT_QDEBUG_IN_RELEASE "Leave Qt debugging statements in Release configuration." OFF
"NOT is_debug_build" OFF)
CMAKE_DEPENDENT_OPTION (WSJT_ENABLE_EXPERIMENTAL_FEATURES "Enable features not fully ready for public releases." ON
is_debug_build OFF)
CMAKE_DEPENDENT_OPTION (WSJT_CREATE_WINMAIN
@ -179,6 +177,7 @@ set (wsjt_qt_CXXSRCS
WFPalette.cpp
Radio.cpp
RadioMetaType.cpp
NonInheritingProcess.cpp
models/IARURegions.cpp
models/Bands.cpp
models/Modes.cpp
@ -190,7 +189,6 @@ set (wsjt_qt_CXXSRCS
item_delegates/ForeignKeyDelegate.cpp
validators/LiveFrequencyValidator.cpp
GetUserId.cpp
TraceFile.cpp
Audio/AudioDevice.cpp
Transceiver/Transceiver.cpp
Transceiver/TransceiverBase.cpp
@ -237,6 +235,7 @@ set (wsjt_qt_CXXSRCS
logbook/Multiplier.cpp
Network/NetworkAccessManager.cpp
widgets/LazyFillComboBox.cpp
widgets/CheckableItemComboBox.cpp
)
set (wsjt_qtmm_CXXSRCS
@ -249,6 +248,7 @@ set (jt9_FSRCS
)
set (wsjtx_CXXSRCS
WSJTXLogging.cpp
logbook/logbook.cpp
Network/PSKReporter.cpp
Modulator/Modulator.cpp
@ -281,6 +281,7 @@ set (wsjtx_CXXSRCS
)
set (wsjt_CXXSRCS
Logger.cpp
lib/crc10.cpp
lib/crc13.cpp
lib/crc14.cpp
@ -770,17 +771,10 @@ set_source_files_properties (${WSJTX_ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATI
set_source_files_properties (lib/decoder.f90 PROPERTIES COMPILE_FLAGS "-Wno-unused-dummy-argument")
set_source_files_properties (lib/filbig.f90 PROPERTIES COMPILE_FLAGS "-Wno-aliasing")
if (WSJT_QDEBUG_IN_RELEASE)
# context info in Qt message handler in release configuration
set_property (DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<NOT:$<CONFIG:Debug>>:QT_MESSAGELOGCONTEXT>
)
else (WSJT_QDEBUG_IN_RELEASE)
# disable Qt trace and warning messages from release configurations
set_property (DIRECTORY APPEND PROPERTY
COMPILE_DEFINITIONS $<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT>
)
endif (WSJT_QDEBUG_IN_RELEASE)
## disable Qt trace and warning messages from release configurations
#set_property (DIRECTORY APPEND PROPERTY
# COMPILE_DEFINITIONS $<$<NOT:$<CONFIG:Debug>>:QT_NO_DEBUG_OUTPUT;QT_NO_WARNING_OUTPUT>
# )
set_property (SOURCE ${all_C_and_CXXSRCS} APPEND_STRING PROPERTY COMPILE_FLAGS " -include wsjtx_config.h")
set_property (SOURCE ${all_C_and_CXXSRCS} APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/wsjtx_config.h)
@ -824,6 +818,7 @@ endif (APPLE)
#
# find some useful tools
#
include (CheckTypeSize)
include (CheckCSourceCompiles)
include (CheckSymbolExists)
include (generate_version_info)
@ -851,11 +846,10 @@ endif ()
#
# Boost
#
set (Boost_NO_SYSTEM_PATHS TRUE)
if (Boost_NO_SYSTEM_PATHS)
set (BOOST_ROOT ${PROJECT_SOURCE_DIR}/boost)
if (WIN32)
set (Boost_USE_STATIC_LIBS OFF)
endif ()
find_package (Boost 1.63 REQUIRED)
find_package (Boost 1.62 REQUIRED COMPONENTS log_setup log)
#
# OpenMP
@ -882,7 +876,8 @@ message (STATUS "hamlib_LIBRARY_DIRS: ${hamlib_LIBRARY_DIRS}")
set (CMAKE_REQUIRED_INCLUDES "${hamlib_INCLUDE_DIRS}")
set (CMAKE_REQUIRED_LIBRARIES "${hamlib_LIBRARIES}")
check_symbol_exists (CACHE_ALL "hamlib/rig.h" HAVE_HAMLIB_OLD_CACHING)
set (CMAKE_EXTRA_INCLUDE_FILES "hamlib/rig.h")
check_type_size (CACHE_ALL HAMLIB_OLD_CACHING)
check_symbol_exists (rig_set_cache_timeout_ms "hamlib/rig.h" HAVE_HAMLIB_CACHING)
@ -1069,7 +1064,7 @@ query_qmake (QT_HOST_DATA QT_DATA_DIR)
set (QT_MKSPECS_DIR ${QT_DATA_DIR}/mkspecs)
# project definitions
add_definitions (-DQT5 -DCMAKE_BUILD -DBIGSYM=1)
add_definitions (-DQT5 -DCMAKE_BUILD -DBIGSYM=1 -DBOOST_ALL_DYN_LINK)
if (CMAKE_HOST_UNIX)
add_definitions (-DUNIX)
elseif (CMAKE_HOST_WIN32)
@ -1078,7 +1073,7 @@ endif ()
# build a library of package functionality (without and optionally with OpenMP support)
add_library (wsjt_cxx STATIC ${wsjt_CSRCS} ${wsjt_CXXSRCS})
target_link_libraries (wsjt_cxx ${LIBM_LIBRARIES})
target_link_libraries (wsjt_cxx ${LIBM_LIBRARIES} Boost::log_setup ${LIBM_LIBRARIES})
# build an OpenMP variant of the Fortran library routines
add_library (wsjt_fort STATIC ${wsjt_FSRCS})
@ -1187,6 +1182,11 @@ target_link_libraries (ft4sim_mult wsjt_fort wsjt_cxx)
add_executable (fst4sim lib/fst4/fst4sim.f90)
target_link_libraries (fst4sim wsjt_fort wsjt_cxx)
if (WIN32)
set_target_properties (fst4sim PROPERTIES
LINK_FLAGS -Wl,--stack,0x4000000,--heap,0x6000000
)
endif ()
add_executable (ldpcsim240_101 lib/fst4/ldpcsim240_101.f90)
target_link_libraries (ldpcsim240_101 wsjt_fort wsjt_cxx)
@ -1282,11 +1282,6 @@ else ()
endif ()
add_custom_target (translations DEPENDS ${QM_FILES})
set_property (DIRECTORY PROPERTY CLEAN_NO_CUSTOM TRUE)
# do this after i18n to stop lupdate walking the boost tree which it
# chokes on
if (Boost_FOUND)
include_directories (${Boost_INCLUDE_DIRS})
endif ()
# embedded resources
function (add_resources resources path)
@ -1329,9 +1324,6 @@ endif (WIN32)
# targets dependent on Qt
#
add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
# build a library for the QCustomPlot widget
add_library (qcp STATIC ${qcp_CXXSRCS})
target_include_directories (qcp PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/qcustomplot-source>)
@ -1341,7 +1333,7 @@ target_link_libraries (qcp Qt5::Widgets Qt5::PrintSupport)
add_library (wsjt_qt STATIC ${wsjt_qt_CXXSRCS} ${wsjt_qt_GENUISRCS} ${GENAXSRCS})
# set wsjtx_udp exports to static variants
target_compile_definitions (wsjt_qt PUBLIC UDP_STATIC_DEFINE)
target_link_libraries (wsjt_qt qcp Qt5::Widgets Qt5::Network Qt5::Sql)
target_link_libraries (wsjt_qt Boost::log qcp Qt5::Widgets Qt5::Network Qt5::Sql)
target_include_directories (wsjt_qt BEFORE PRIVATE ${hamlib_INCLUDE_DIRS})
if (WIN32)
target_link_libraries (wsjt_qt Qt5::AxContainer Qt5::AxBase)
@ -1360,9 +1352,12 @@ generate_version_info (jt9_VERSION_RESOURCES
NAME jt9
BUNDLE ${PROJECT_BUNDLE_NAME}
ICON ${WSJTX_ICON_FILE}
FILE_DESCRIPTION "Slow mode decoder"
FILE_DESCRIPTION "jt9 - WSJT-X slow mode decoder"
)
add_executable (record_time_signal Audio/tools/record_time_signal.cpp)
target_link_libraries (record_time_signal wsjt_cxx wsjt_qtmm wsjt_qt)
add_executable (jt9 ${jt9_FSRCS} ${jt9_VERSION_RESOURCES})
if (${OPENMP_FOUND} OR APPLE)
if (APPLE)
@ -1449,7 +1444,7 @@ else ()
)
if (WIN32)
set_target_properties (wsjtx PROPERTIES
LINK_FLAGS -Wl,--stack,28000000
LINK_FLAGS -Wl,--stack,0x1000000,--heap,0x20000000
)
endif ()
endif ()
@ -1485,6 +1480,15 @@ generate_version_info (udp_daemon_VERSION_RESOURCES
add_executable (udp_daemon UDPExamples/UDPDaemon.cpp ${udp_daemon_VERSION_RESOURCES})
target_link_libraries (udp_daemon wsjtx_udp-static)
generate_version_info (wsjtx_app_version_VERSION_RESOURCES
NAME wsjtx_app_version
BUNDLE ${PROJECT_BUNDLE_NAME}
ICON ${WSJTX_ICON_FILE}
FILE_DESCRIPTION "Display WSJT-X Application Version on console"
)
add_executable (wsjtx_app_version AppVersion/AppVersion.cpp ${wsjtx_app_version_VERSION_RESOURCES})
target_link_libraries (wsjtx_app_version wsjt_qt)
generate_version_info (message_aggregator_VERSION_RESOURCES
NAME message_aggregator
BUNDLE ${PROJECT_BUNDLE_NAME}
@ -1502,7 +1506,7 @@ add_executable (message_aggregator
${message_aggregator_RESOURCES_RCC}
${message_aggregator_VERSION_RESOURCES}
)
target_link_libraries (message_aggregator Qt5::Widgets wsjtx_udp-static)
target_link_libraries (message_aggregator wsjt_qt Qt5::Widgets wsjtx_udp-static)
if (WSJT_CREATE_WINMAIN)
set_target_properties (message_aggregator PROPERTIES WIN32_EXECUTABLE ON)
@ -1544,7 +1548,7 @@ install (TARGETS wsjtx
# DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/wsjtx
# )
install (TARGETS udp_daemon message_aggregator
install (TARGETS udp_daemon message_aggregator wsjtx_app_version
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT runtime
)
@ -1601,6 +1605,13 @@ install (FILES
#COMPONENT runtime
)
install (DIRECTORY
example_log_configurations
DESTINATION ${CMAKE_INSTALL_DOCDIR}
FILES_MATCHING REGEX "^.*[^~]$"
#COMPONENT runtime
)
#
# Mac installer files
#
@ -1846,7 +1857,7 @@ set (CPACK_MONOLITHIC_INSTALL 1)
set (CPACK_PACKAGE_NAME "${CMAKE_PROJECT_NAME}")
set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set (CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}${BUILD_TYPE_REVISION}")
if (WIN32)
set (CPACK_GENERATOR "NSIS")
@ -1880,11 +1891,11 @@ else ()
endif ()
set (CPACK_DEBIAN_PACKAGE_HOMEPAGE "${PROJECT_HOMEPAGE}")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran5 (>=8.3) | libgfortran4 (>=7.3) | libgfortran3 (>=6.3), libfftw3-single3 (>=3.3), libgomp1 (>=6), libqt5serialport5 (>=5.7), libqt5multimedia5-plugins (>=5.7), libqt5widgets5 (>=5.7), libqt5network5 (>=5.7), libqt5printsupport5 (>=5.7), libqt5sql5-sqlite (>=5.7), libusb-1.0-0 (>=1.0.21), libboost-log1.65.1 (>=1.62.0) | libboost-log1.65.1 (>=1.65.1) | libboost-log1.67.0 (>=1.67.0) | libboost-log1.71.0 (>=1.71.0)")
set (CPACK_DEBIAN_PACKAGE_DEPENDS "libgfortran5 (>=8.3) | libgfortran4 (>=7.3) | libgfortran3 (>=6.3), libfftw3-single3 (>=3.3), libgomp1 (>=6), libqt5serialport5 (>=5.7), libqt5multimedia5-plugins (>=5.7), libqt5widgets5 (>=5.7), libqt5network5 (>=5.7), libqt5printsupport5 (>=5.7), libqt5sql5-sqlite (>=5.7), libusb-1.0-0 (>=1.0.21), libboost-log1.62.0 (>=1.62.0) | libboost-log1.65.1 (>=1.65.1) | libboost-log1.67.0 (>=1.67.0) | libboost-log1.71.0 (>=1.71.0) | libboost-log1.74.0 (>=1.74.0)")
set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
set (CPACK_RPM_PACKAGE_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR})
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase >= 5.9, qt5-qtserialport >= 5.9, qt5-qtmultimedia >= 5.9, qt5-qtsvg >= 5.9, libusbx >= 1.0.22, libgfortran >= 7, libgomp >= 7, fftw-libs-single >= 3.3")
set (CPACK_RPM_PACKAGE_REQUIRES "qt5-qtbase >= 5.9, qt5-qtserialport >= 5.9, qt5-qtmultimedia >= 5.9, qt5-qtsvg >= 5.9, libusbx >= 1.0.22, libgfortran >= 7, libgomp >= 7, fftw-libs-single >= 3.3, boost-log >= 1.62")
set (CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION /usr/share/pixmaps /usr/share/applications /usr/share/man /usr/share/man1)
configure_file ("${PROJECT_SOURCE_DIR}/CMakeCPackOptions.cmake.in"

View File

@ -163,9 +163,14 @@
#include <QFontDialog>
#include <QSerialPortInfo>
#include <QScopedPointer>
#include <QNetworkInterface>
#include <QHostInfo>
#include <QHostAddress>
#include <QStandardItem>
#include <QDebug>
#include "pimpl_impl.hpp"
#include "Logger.hpp"
#include "qt_helpers.hpp"
#include "MetaDataRegistry.hpp"
#include "SettingsGroup.hpp"
@ -439,6 +444,12 @@ private:
void load_audio_devices (QAudio::Mode, QComboBox *, QAudioDeviceInfo *);
void update_audio_channels (QComboBox const *, int, QComboBox *, bool);
void load_network_interfaces (CheckableItemComboBox *, QStringList current);
Q_SLOT void validate_network_interfaces (QString const&);
QStringList get_selected_network_interfaces (CheckableItemComboBox *);
Q_SLOT void host_info_results (QHostInfo);
void check_multicast (QHostAddress const&);
void find_tab (QWidget *);
void initialize_models ();
@ -492,6 +503,8 @@ private:
Q_SLOT void on_add_macro_line_edit_editingFinished ();
Q_SLOT void delete_macro ();
void delete_selected_macros (QModelIndexList);
Q_SLOT void on_udp_server_line_edit_textChanged (QString const&);
Q_SLOT void on_udp_server_line_edit_editingFinished ();
Q_SLOT void on_save_path_select_push_button_clicked (bool);
Q_SLOT void on_azel_path_select_push_button_clicked (bool);
Q_SLOT void on_calibration_intercept_spin_box_valueChanged (double);
@ -641,7 +654,12 @@ private:
bool use_dynamic_grid_;
QString opCall_;
QString udp_server_name_;
bool udp_server_name_edited_;
int dns_lookup_id_;
port_type udp_server_port_;
QStringList udp_interface_names_;
QString loopback_interface_name_;
int udp_TTL_;
QString n1mm_server_name_;
port_type n1mm_server_port_;
bool broadcast_to_n1mm_;
@ -741,6 +759,8 @@ QString Configuration::opCall() const {return m_->opCall_;}
void Configuration::opCall (QString const& call) {m_->opCall_ = call;}
QString Configuration::udp_server_name () const {return m_->udp_server_name_;}
auto Configuration::udp_server_port () const -> port_type {return m_->udp_server_port_;}
QStringList Configuration::udp_interface_names () const {return m_->udp_interface_names_;}
int Configuration::udp_TTL () const {return m_->udp_TTL_;}
bool Configuration::accept_udp_requests () const {return m_->accept_udp_requests_;}
QString Configuration::n1mm_server_name () const {return m_->n1mm_server_name_;}
auto Configuration::n1mm_server_port () const -> port_type {return m_->n1mm_server_port_;}
@ -792,10 +812,7 @@ bool Configuration::is_dummy_rig () const
bool Configuration::transceiver_online ()
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_online: " << m_->cached_rig_state_;
#endif
LOG_TRACE (m_->cached_rig_state_);
return m_->have_rig ();
}
@ -806,54 +823,37 @@ int Configuration::transceiver_resolution () const
void Configuration::transceiver_offline ()
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_offline:" << m_->cached_rig_state_;
#endif
LOG_TRACE (m_->cached_rig_state_);
m_->close_rig ();
}
void Configuration::transceiver_frequency (Frequency f)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_frequency:" << f << m_->cached_rig_state_;
#endif
LOG_TRACE (f << ' ' << m_->cached_rig_state_);
m_->transceiver_frequency (f);
}
void Configuration::transceiver_tx_frequency (Frequency f)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_tx_frequency:" << f << m_->cached_rig_state_;
#endif
LOG_TRACE (f << ' ' << m_->cached_rig_state_);
m_->transceiver_tx_frequency (f);
}
void Configuration::transceiver_mode (MODE mode)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_mode:" << mode << m_->cached_rig_state_;
#endif
LOG_TRACE (mode << ' ' << m_->cached_rig_state_);
m_->transceiver_mode (mode);
}
void Configuration::transceiver_ptt (bool on)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::transceiver_ptt:" << on << m_->cached_rig_state_;
#endif
LOG_TRACE (on << ' ' << m_->cached_rig_state_);
m_->transceiver_ptt (on);
}
void Configuration::sync_transceiver (bool force_signal, bool enforce_mode_and_split)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::sync_transceiver: force signal:" << force_signal << "enforce_mode_and_split:" << enforce_mode_and_split << m_->cached_rig_state_;
#endif
LOG_TRACE ("force signal: " << force_signal << " enforce_mode_and_split: " << enforce_mode_and_split << ' ' << m_->cached_rig_state_);
m_->sync_transceiver (force_signal);
if (!enforce_mode_and_split)
{
@ -995,6 +995,8 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
, transceiver_command_number_ {0}
, degrade_ {0.} // initialize to zero each run, not
// saved in settings
, udp_server_name_edited_ {false}
, dns_lookup_id_ {-1}
{
ui_->setupUi (this);
@ -1044,6 +1046,7 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
// this must be done after the default paths above are set
read_settings ();
// set up dynamic loading of audio devices
connect (ui_->sound_input_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
load_audio_devices (QAudio::AudioInput, ui_->sound_input_combo_box, &next_audio_input_device_);
@ -1059,29 +1062,29 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
QGuiApplication::restoreOverrideCursor ();
});
// set up dynamic loading of network interfaces
connect (ui_->udp_interfaces_combo_box, &LazyFillComboBox::about_to_show_popup, [this] () {
QGuiApplication::setOverrideCursor (QCursor {Qt::WaitCursor});
load_network_interfaces (ui_->udp_interfaces_combo_box, udp_interface_names_);
QGuiApplication::restoreOverrideCursor ();
});
connect (ui_->udp_interfaces_combo_box, &QComboBox::currentTextChanged, this, &Configuration::impl::validate_network_interfaces);
// set up LoTW users CSV file fetching
connect (&lotw_users_, &LotWUsers::load_finished, [this] () {
ui_->LotW_CSV_fetch_push_button->setEnabled (true);
});
lotw_users_.set_local_file_path (writeable_data_dir_.absoluteFilePath ("lotw-user-activity.csv"));
// load the dictionary if it exists
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), false);
//
// validation
//
ui_->callsign_line_edit->setValidator (new CallsignValidator {this});
ui_->grid_line_edit->setValidator (new MaidenheadLocatorValidator {this});
ui_->add_macro_line_edit->setValidator (new QRegularExpressionValidator {message_alphabet, this});
ui_->Field_Day_Exchange->setValidator (new QRegularExpressionValidator {field_day_exchange_re, this});
ui_->RTTY_Exchange->setValidator (new QRegularExpressionValidator {RTTY_roundup_exchange_re, this});
ui_->udp_server_port_spin_box->setMinimum (1);
ui_->udp_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
ui_->n1mm_server_port_spin_box->setMinimum (1);
ui_->n1mm_server_port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
//
// assign ids to radio buttons
//
@ -1233,6 +1236,19 @@ Configuration::impl::impl (Configuration * self, QNetworkAccessManager * network
audio_output_device_ = next_audio_output_device_;
audio_output_channel_ = next_audio_output_channel_;
bool fetch_if_needed {false};
for (auto const& item : decode_highlighing_model_.items ())
{
if (DecodeHighlightingModel::Highlight::LotW == item.type_)
{
fetch_if_needed = item.enabled_;
break;
}
}
// load the LoTW users dictionary if it exists, fetch and load if it
// doesn't and we need it
lotw_users_.load (ui_->LotW_CSV_URL_line_edit->text (), fetch_if_needed);
transceiver_thread_ = new QThread {this};
transceiver_thread_->start ();
}
@ -1340,7 +1356,14 @@ void Configuration::impl::initialize_models ()
ui_->CAT_poll_interval_spin_box->setValue (rig_params_.poll_interval);
ui_->opCallEntry->setText (opCall_);
ui_->udp_server_line_edit->setText (udp_server_name_);
on_udp_server_line_edit_editingFinished ();
ui_->udp_server_port_spin_box->setValue (udp_server_port_);
load_network_interfaces (ui_->udp_interfaces_combo_box, udp_interface_names_);
if (!udp_interface_names_.size ())
{
udp_interface_names_ = get_selected_network_interfaces (ui_->udp_interfaces_combo_box);
}
ui_->udp_TTL_spin_box->setValue (udp_TTL_);
ui_->accept_udp_requests_check_box->setChecked (accept_udp_requests_);
ui_->n1mm_server_name_line_edit->setText (n1mm_server_name_);
ui_->n1mm_server_port_spin_box->setValue (n1mm_server_port_);
@ -1518,6 +1541,8 @@ void Configuration::impl::read_settings ()
rig_params_.split_mode = settings_->value ("SplitMode", QVariant::fromValue (TransceiverFactory::split_mode_none)).value<TransceiverFactory::SplitMode> ();
opCall_ = settings_->value ("OpCall", "").toString ();
udp_server_name_ = settings_->value ("UDPServer", "127.0.0.1").toString ();
udp_interface_names_ = settings_->value ("UDPInterface").toStringList ();
udp_TTL_ = settings_->value ("UDPTTL", 1).toInt ();
udp_server_port_ = settings_->value ("UDPServerPort", 2237).toUInt ();
n1mm_server_name_ = settings_->value ("N1MMServer", "127.0.0.1").toString ();
n1mm_server_port_ = settings_->value ("N1MMServerPort", 2333).toUInt ();
@ -1646,6 +1671,8 @@ void Configuration::impl::write_settings ()
settings_->setValue ("OpCall", opCall_);
settings_->setValue ("UDPServer", udp_server_name_);
settings_->setValue ("UDPServerPort", udp_server_port_);
settings_->setValue ("UDPInterface", QVariant::fromValue (udp_interface_names_));
settings_->setValue ("UDPTTL", udp_TTL_);
settings_->setValue ("N1MMServer", n1mm_server_name_);
settings_->setValue ("N1MMServerPort", n1mm_server_port_);
settings_->setValue ("BroadcastToN1MM", broadcast_to_n1mm_);
@ -1848,6 +1875,12 @@ bool Configuration::impl::validate ()
return false;
}
if (dns_lookup_id_ > -1)
{
MessageBox::information_message (this, tr ("Pending DNS lookup, please try again later"));
return false;
}
return true;
}
@ -2066,20 +2099,30 @@ void Configuration::impl::accept ()
pwrBandTxMemory_ = ui_->checkBoxPwrBandTxMemory->isChecked ();
pwrBandTuneMemory_ = ui_->checkBoxPwrBandTuneMemory->isChecked ();
opCall_=ui_->opCallEntry->text();
auto new_server = ui_->udp_server_line_edit->text ();
if (new_server != udp_server_name_)
auto new_server = ui_->udp_server_line_edit->text ().trimmed ();
auto new_interfaces = get_selected_network_interfaces (ui_->udp_interfaces_combo_box);
if (new_server != udp_server_name_ || new_interfaces != udp_interface_names_)
{
udp_server_name_ = new_server;
Q_EMIT self_->udp_server_changed (new_server);
udp_interface_names_ = new_interfaces;
Q_EMIT self_->udp_server_changed (udp_server_name_, udp_interface_names_);
}
auto new_port = ui_->udp_server_port_spin_box->value ();
if (new_port != udp_server_port_)
{
udp_server_port_ = new_port;
Q_EMIT self_->udp_server_port_changed (new_port);
Q_EMIT self_->udp_server_port_changed (udp_server_port_);
}
auto new_TTL = ui_->udp_TTL_spin_box->value ();
if (new_TTL != udp_TTL_)
{
udp_TTL_ = new_TTL;
Q_EMIT self_->udp_TTL_changed (udp_TTL_);
}
if (ui_->accept_udp_requests_check_box->isChecked () != accept_udp_requests_)
{
accept_udp_requests_ = ui_->accept_udp_requests_check_box->isChecked ();
@ -2135,6 +2178,12 @@ void Configuration::impl::accept ()
void Configuration::impl::reject ()
{
if (dns_lookup_id_ > -1)
{
QHostInfo::abortHostLookup (dns_lookup_id_);
dns_lookup_id_ = -1;
}
initialize_models (); // reverts to settings as at exec ()
// check if the Transceiver instance changed, in which case we need
@ -2349,6 +2398,72 @@ void Configuration::impl::on_add_macro_push_button_clicked (bool /* checked */)
}
}
void Configuration::impl::on_udp_server_line_edit_textChanged (QString const&)
{
udp_server_name_edited_ = true;
}
void Configuration::impl::on_udp_server_line_edit_editingFinished ()
{
if (udp_server_name_edited_)
{
auto const& server = ui_->udp_server_line_edit->text ().trimmed ();
QHostAddress ha {server};
if (server.size () && ha.isNull ())
{
// queue a host address lookup
// qDebug () << "server host DNS lookup:" << server;
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
dns_lookup_id_ = QHostInfo::lookupHost (server, this, &Configuration::impl::host_info_results);
#else
dns_lookup_id_ = QHostInfo::lookupHost (server, this, SLOT (host_info_results (QHostInfo)));
#endif
}
else
{
check_multicast (ha);
}
}
}
void Configuration::impl::host_info_results (QHostInfo host_info)
{
if (host_info.lookupId () != dns_lookup_id_) return;
dns_lookup_id_ = -1;
if (QHostInfo::NoError != host_info.error ())
{
MessageBox::critical_message (this, tr ("UDP server DNS lookup failed"), host_info.errorString ());
}
else
{
auto const& server_addresses = host_info.addresses ();
// qDebug () << "message server addresses:" << server_addresses;
if (server_addresses.size ())
{
check_multicast (server_addresses[0]);
}
}
}
void Configuration::impl::check_multicast (QHostAddress const& ha)
{
auto is_multicast = is_multicast_address (ha);
ui_->udp_interfaces_label->setVisible (is_multicast);
ui_->udp_interfaces_combo_box->setVisible (is_multicast);
ui_->udp_TTL_label->setVisible (is_multicast);
ui_->udp_TTL_spin_box->setVisible (is_multicast);
if (isVisible ())
{
if (is_MAC_ambiguous_multicast_address (ha))
{
MessageBox::warning_message (this, tr ("MAC-ambiguous multicast groups addresses not supported"));
find_tab (ui_->udp_server_line_edit);
ui_->udp_server_line_edit->clear ();
}
}
udp_server_name_edited_ = false;
}
void Configuration::impl::delete_frequencies ()
{
auto selection_model = ui_->frequencies_table_view->selectionModel ();
@ -2723,9 +2838,7 @@ void Configuration::impl::sync_transceiver (bool /*force_signal*/)
void Configuration::impl::handle_transceiver_update (TransceiverState const& state,
unsigned sequence_number)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_update: Transceiver State #:" << sequence_number << state;
#endif
LOG_TRACE ("#: " << sequence_number << ' ' << state);
// only follow rig on some information, ignore other stuff
cached_rig_state_.online (state.online ());
@ -2773,10 +2886,7 @@ void Configuration::impl::handle_transceiver_update (TransceiverState const& sta
void Configuration::impl::handle_transceiver_failure (QString const& reason)
{
#if WSJT_TRACE_CAT
qDebug () << "Configuration::handle_transceiver_failure: reason:" << reason;
#endif
LOG_ERROR ("handle_transceiver_failure: reason: " << reason);
close_rig ();
ui_->test_PTT_push_button->setChecked (false);
@ -2873,6 +2983,85 @@ void Configuration::impl::load_audio_devices (QAudio::Mode mode, QComboBox * com
combo_box->setCurrentIndex (current_index);
}
// load the available network interfaces into the selection combo box
void Configuration::impl::load_network_interfaces (CheckableItemComboBox * combo_box, QStringList current)
{
combo_box->clear ();
for (auto const& net_if : QNetworkInterface::allInterfaces ())
{
auto flags = QNetworkInterface::IsUp | QNetworkInterface::CanMulticast;
if ((net_if.flags () & flags) == flags)
{
if (net_if.flags () & QNetworkInterface::IsLoopBack)
{
loopback_interface_name_ = net_if.name ();
}
auto item = combo_box->addCheckItem (net_if.humanReadableName ()
, net_if.name ()
, current.contains (net_if.name ()) ? Qt::Checked : Qt::Unchecked);
auto tip = QString {"name(index): %1(%2) - %3"}.arg (net_if.name ()).arg (net_if.index ())
.arg (net_if.flags () & QNetworkInterface::IsUp ? "Up" : "Down");
auto hw_addr = net_if.hardwareAddress ();
if (hw_addr.size ())
{
tip += QString {"\nhw: %1"}.arg (net_if.hardwareAddress ());
}
auto aes = net_if.addressEntries ();
if (aes.size ())
{
tip += "\naddresses:";
for (auto const& ae : aes)
{
tip += QString {"\n ip: %1/%2"}.arg (ae.ip ().toString ()).arg (ae.prefixLength ());
}
}
item->setToolTip (tip);
}
}
}
// get the select network interfaces from the selection combo box
void Configuration::impl::validate_network_interfaces (QString const& /*text*/)
{
auto model = static_cast<QStandardItemModel *> (ui_->udp_interfaces_combo_box->model ());
bool has_checked {false};
int loopback_row {-1};
for (int row = 0; row < model->rowCount (); ++row)
{
if (model->item (row)->data ().toString () == loopback_interface_name_)
{
loopback_row = row;
}
else if (Qt::Checked == model->item (row)->checkState ())
{
has_checked = true;
}
}
if (loopback_row >= 0)
{
if (!has_checked)
{
model->item (loopback_row)->setCheckState (Qt::Checked);
}
model->item (loopback_row)->setEnabled (has_checked);
}
}
// get the select network interfaces from the selection combo box
QStringList Configuration::impl::get_selected_network_interfaces (CheckableItemComboBox * combo_box)
{
QStringList interfaces;
auto model = static_cast<QStandardItemModel *> (combo_box->model ());
for (int row = 0; row < model->rowCount (); ++row)
{
if (Qt::Checked == model->item (row)->checkState ())
{
interfaces << model->item (row)->data ().toString ();
}
}
return interfaces;
}
// enable only the channels that are supported by the selected audio device
void Configuration::impl::update_audio_channels (QComboBox const * source_combo_box, int index, QComboBox * combo_box, bool allow_both)
{

View File

@ -3,6 +3,7 @@
#include <QObject>
#include <QFont>
#include <QString>
#include "Radio.hpp"
#include "models/IARURegions.hpp"
@ -14,14 +15,12 @@
class QSettings;
class QWidget;
class QAudioDeviceInfo;
class QString;
class QDir;
class QNetworkAccessManager;
class Bands;
class FrequencyList_v2;
class StationList;
class QStringListModel;
class QHostAddress;
class LotWUsers;
class DecodeHighlightingModel;
class LogBook;
@ -152,6 +151,8 @@ public:
void opCall (QString const&);
QString udp_server_name () const;
port_type udp_server_port () const;
QStringList udp_interface_names () const;
int udp_TTL () const;
QString n1mm_server_name () const;
port_type n1mm_server_port () const;
bool valid_n1mm_info () const;
@ -273,8 +274,9 @@ public:
//
// This signal is emitted when the UDP server changes
//
Q_SIGNAL void udp_server_changed (QString const& udp_server) const;
Q_SIGNAL void udp_server_changed (QString& udp_server_name, QStringList const& network_interfaces) const;
Q_SIGNAL void udp_server_port_changed (port_type server_port) const;
Q_SIGNAL void udp_TTL_changed (int TTL) const;
Q_SIGNAL void accept_udp_requests_changed (bool checked) const;
// signal updates to decode highlighting

View File

@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>554</width>
<height>556</height>
<height>560</height>
</rect>
</property>
<property name="windowTitle">
@ -1864,12 +1864,6 @@ and DX Grid fields when a 73 or free text message is sent.</string>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="udp_server_line_edit">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Optional hostname of network service to receive decodes.&lt;/p&gt;&lt;p&gt;Formats:&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;hostname&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv4 address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv6 address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv4 multicast group address&lt;/li&gt;&lt;li style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;IPv6 multicast group address&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Clearing this field will disable the broadcasting of UDP status updates.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
@ -1891,16 +1885,53 @@ and DX Grid fields when a 73 or free text message is sent.</string>
<item row="1" column="1">
<widget class="QSpinBox" name="udp_server_port_spin_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the service port number of the UDP server that WSJT-X should send updates to. If this is zero no updates will be broadcast.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="minimum">
<number>0</number>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the service port number of the UDP server that WSJT-X should send updates to. If this is zero no updates will be sent.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="udp_interfaces_label">
<property name="text">
<string>Outgoing interfaces:</string>
</property>
<property name="buddy">
<cstring>udp_interfaces_combo_box</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="CheckableItemComboBox" name="udp_interfaces_combo_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;When sending updates to a multicast group address it is necessary to specify which network interface(s) to send them to. If the loop-back interface is multicast capable then at least that one will be selected.&lt;/p&gt;&lt;p&gt;For most users the loop-back interface is all that is needed, that will allow multiple other applications on the same machine to interoperate with WSJT-X. If applications running on other hosts are to receive status updates then a suitable network interface should be used.&lt;/p&gt;&lt;p&gt;On some Linux systems it may be necessary to enable multicast on the loop-back network interface.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QSpinBox" name="udp_TTL_spin_box">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sets the number or router hops that multicast datagrams are allowed to make. Almost everyone should set this to 1 to keep outgoing multicast traffic withn the local subnet.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>255</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="udp_TTL_label">
<property name="text">
<string>Multicast TTL:</string>
</property>
<property name="buddy">
<cstring>udp_TTL_spin_box</cstring>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1">
@ -1988,6 +2019,9 @@ and DX Grid fields when a 73 or free text message is sent.</string>
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Enter the port number that WSJT-X should use for UDP broadcasts of ADIF log information. For N1MM Logger+, this value should be 2333. If this is zero, no updates will be broadcast.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="maximum">
<number>65534</number>
</property>
</widget>
</item>
</layout>
@ -3002,6 +3036,11 @@ Right click for insert and delete options.</string>
<extends>QComboBox</extends>
<header>widgets/LazyFillComboBox.hpp</header>
</customwidget>
<customwidget>
<class>CheckableItemComboBox</class>
<extends>QComboBox</extends>
<header>widgets/CheckableItemComboBox.hpp</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>configuration_tabs</tabstop>
@ -3084,6 +3123,8 @@ Right click for insert and delete options.</string>
<tabstop>psk_reporter_tcpip_check_box</tabstop>
<tabstop>udp_server_line_edit</tabstop>
<tabstop>udp_server_port_spin_box</tabstop>
<tabstop>udp_interfaces_combo_box</tabstop>
<tabstop>udp_TTL_spin_box</tabstop>
<tabstop>accept_udp_requests_check_box</tabstop>
<tabstop>udpWindowToFront</tabstop>
<tabstop>udpWindowRestore</tabstop>
@ -3101,8 +3142,8 @@ Right click for insert and delete options.</string>
<tabstop>include_WAE_check_box</tabstop>
<tabstop>rescan_log_push_button</tabstop>
<tabstop>LotW_CSV_URL_line_edit</tabstop>
<tabstop>LotW_days_since_upload_spin_box</tabstop>
<tabstop>LotW_CSV_fetch_push_button</tabstop>
<tabstop>LotW_days_since_upload_spin_box</tabstop>
<tabstop>sbNtrials</tabstop>
<tabstop>sbAggressive</tabstop>
<tabstop>cbTwoPass</tabstop>
@ -3118,11 +3159,11 @@ Right click for insert and delete options.</string>
<tabstop>rbHound</tabstop>
<tabstop>rbNA_VHF_Contest</tabstop>
<tabstop>rbField_Day</tabstop>
<tabstop>Field_Day_Exchange</tabstop>
<tabstop>rbEU_VHF_Contest</tabstop>
<tabstop>rbRTTY_Roundup</tabstop>
<tabstop>RTTY_Exchange</tabstop>
<tabstop>rbWW_DIGI</tabstop>
<tabstop>Field_Day_Exchange</tabstop>
<tabstop>RTTY_Exchange</tabstop>
</tabstops>
<resources/>
<connections>
@ -3192,13 +3233,13 @@ Right click for insert and delete options.</string>
</connection>
</connections>
<buttongroups>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="TX_audio_source_button_group"/>
<buttongroup name="CAT_stop_bits_button_group"/>
<buttongroup name="CAT_handshake_button_group"/>
<buttongroup name="CAT_data_bits_button_group"/>
<buttongroup name="TX_mode_button_group"/>
<buttongroup name="special_op_activity_button_group"/>
<buttongroup name="PTT_method_button_group"/>
<buttongroup name="split_mode_button_group"/>
</buttongroups>
</ui>

View File

@ -1,22 +1,41 @@
Notes on WSJT-X Installation for Mac OS X
-----------------------------------------
Important: If you are using the new Mac with the M1 chip then please read
the section marked: BEGIN M1. Otherwise BEGIN INTEL applies.
If you have already downloaded a previous version of WSJT-X then I suggest
you change the name in the Applications folder from WSJT-X to WSJT-X_previous
before proceeding.
If you have installed a previous version of WSJT-X before, then there is no
need to change anything on your system so proceed to NEXT. If you upgrade macOS
it is possible that you might need to re-install the sysctl.conf file.
I recommend that you follow the installation instructions especially if you
are moving from v2.2 to v2.3 of WSJT-X or you have upgraded macOS.
BEGIN:
BEGIN M1:
Double-click on the wsjtx-...-Darwin.dmg file you have downloaded from K1JT's web-site.
Now open a Terminal window by going to Applications->Utilities and clicking on Terminal.
There are two system variables that must be set manually since the M1 Macs do not recognise
automatic parameter settings by means of the sysctl.conf file present in the download.
Type these commands - you will be asked for your password which will not be echoed:
sudo sysctl -w kern.sysv.shmmax=104857600
sudo sysctl -w kern.sysv.shmall=25600
It is important to note that these parameter settings will not survive a reboot. If you
need to reboot your Mac, then these commands must be re-entered. Now proceed to NEXT.
BEGIN INTEL:
Double-click on the wsjtx-...-Darwin.dmg file you have downloaded from K1JT's web-site.
Now open a Terminal window by going to Applications->Utilities and clicking on Terminal.
Along with this ReadMe file there is a file: sysctl.conf which must be copied to a
system area by typing this line in the Terminal window and then pressing the Return key.
system area by typing these two lines in the Terminal window and then pressing the Return key
after each line.
sudo cp /Volumes/WSJT-X/sysctl.conf /etc
@ -28,7 +47,7 @@ change has been made by typing:
sysctl -a | grep sysv.shm
If shmmax is not shown as 14680064 then contact me since WSJT-X will fail to load with
If shmmax is not shown as 104857600 then contact me since WSJT-X will fail to load with
an error message: "Unable to create shared memory segment".
You can now close the Terminal window. It will not be necessary to repeat this procedure
@ -77,23 +96,20 @@ Please email me if you have problems.
--- John G4KLA (g4kla@rmnjmn.co.uk)
Addendum: Information about sysctl.conf and multiple instances of wsjt-x.
Addendum: Information about sysctl.conf and multiple instances of WSJT-X.
WSJT-X makes use of a block of memory which is shared between different parts of
the code. The normal allocation of shared memory on a Mac is insufficient and this
has to be increased. The sysctl.conf file is used for this purpose. You can
use a Mac editor to examine sysctl.conf. (Do not use another editor - the file
would be probably be corrupted.)
would probably be corrupted.)
There are two important parameters that you need to consider. shmmax determines the
amount of shared memory that must be allocated for WSJT-X to operate. This is 14680064 (14MB)
and this is defined in the sysctl.conf file and should not be changed.
It is possible to run two instances of WSJT-X simultaneously. See "Section 16.2
Frequently asked Questions" in the User Guide. If you wish to run more than two instances
simultaneously, the sysctl.conf file needs to be modified. Please email me with your
requirements and I will provide a replacement sysctl.conf to suit.
It is possible to run more than one instance of WSJT-X simultaneously. See
"Section 16.2 Frequently asked Questions" in the User Guide. The second important parameter
shmall=17920 determines how many instances are permitted. This is calculated as:
(shmall x 4096/14680064) = 5.
The sysctl.conf file is configured to permit up to 5 instances of wsjtx to run simultaneously.
If this limitation is acceptable then you can continue to install the sysctl.conf file without making any
alterations. Otherwise you must edit the file to increase shmall according to this calculation.
If two instances of WSJT-X are running, it is likely that you might need additional
audio devices, from two rigs for example. Visit Audio MIDI Setup and create an Aggregate Device
which will allow you to specific more than one interface. I recommend you consult Apple's guide
on combining multiple audio interfaces which is at https://support.apple.com/en-us/HT202000.

View File

@ -1,3 +1,3 @@
SOURCES += Detector/Detector.cpp
HEADERS += Detector/Detector.hpp
HEADERS += Detector/Detector.hpp

View File

@ -0,0 +1,45 @@
#ifndef EXCEPTION_CATCHING_APPLICATION_HPP__
#define EXCEPTION_CATCHING_APPLICATION_HPP__
#include <QApplication>
#include "Logger.hpp"
class QObject;
class QEvent;
//
// We can't use the GUI after QApplication::exit() is called so
// uncaught exceptions can get lost on Windows systems where there is
// no console terminal, so here we override QApplication::notify() and
// wrap the base class call with a try block to catch and display
// exceptions in a message box.
//
class ExceptionCatchingApplication
: public QApplication
{
public:
explicit ExceptionCatchingApplication (int& argc, char * * argv)
: QApplication {argc, argv}
{
}
bool notify (QObject * receiver, QEvent * e) override
{
try
{
return QApplication::notify (receiver, e);
}
catch (std::exception const& e)
{
LOG_FATAL (e.what ());
throw;
}
catch (...)
{
LOG_FATAL ("Unexpected fatal error");
throw;
}
}
};
#endif

21
INSTALL
View File

@ -23,10 +23,10 @@ Building from Source
====================
On Linux systems some of the prerequisite libraries are available in
the mainstream distribution repositories. They are Qt v5 and FFTW v3.
For MS Windows see the section "Building from Source on MS Windows"
below. For Apple Mac see the section "Building from Source on Apple
Mac".
the mainstream distribution repositories. They are Qt v5, FFTW v3, and
the Boost C++ libraries. For MS Windows see the section "Building
from Source on MS Windows" below. For Apple Mac see the section
"Building from Source on Apple Mac".
Qt v5, preferably v5.9 or later is required to build WSJT-X.
@ -43,12 +43,13 @@ the libfftw library development package. Normally installing the
library development package pulls in all the FFTW v3 libraries
including the single precision variant.
The Hamlib library optionally requires the libusb-1.0-1 library, if
the development version (libusb-1.0-0-dev) is available Hamlib will
configure its custom USB device back end drivers. Most rigs do not
require this so normally you can choose not to install libusb-1.0-dev
but if you have a SoftRock USB or similar SDR that uses a custom USB
interface then it is required.
The Hamlib library requires the readline development package and
optionally requires the libusb-1.0-1 library, if the development
version (libusb-1.0-0-dev) is available Hamlib will configure its
custom USB device back end drivers. Most rigs do not require this so
normally you can choose not to install libusb-1.0-dev but if you have
a SoftRock USB or similar SDR that uses a custom USB interface then it
is required.
The Hamlib library is required. Currently WSJT-X needs to be built
using a forked version of the Hamlib git master. This fork contains

View File

@ -9,6 +9,9 @@
#include <QRegularExpression>
#include <QDebug>
#include "qt_helpers.hpp"
#include "Logger.hpp"
#include "pimpl_impl.hpp"
class L10nLoader::impl final
@ -60,11 +63,11 @@ public:
L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const& language_override)
: m_ {app}
{
qDebug () << QString {"locale: language: %1 script: %2 country: %3 ui-languages: %4"}
LOG_INFO (QString {"locale: language: %1 script: %2 country: %3 ui-languages: %4"}
.arg (QLocale::languageToString (locale.language ()))
.arg (QLocale::scriptToString (locale.script ()))
.arg (QLocale::countryToString (locale.country ()))
.arg (locale.uiLanguages ().join (", "));
.arg (locale.uiLanguages ().join (", ")));
// we don't load translators if the language override is 'en',
// 'en_US', or 'en-US'. In these cases we assume the user is trying
@ -78,10 +81,10 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
QString translations_dir {":/Translations"};
if (!skip_locale)
{
qDebug () << "Looking for locale based Qt translations in resources filesystem";
LOG_TRACE ("Looking for locale based Qt translations in resources filesystem");
if (m_->load_translator (locale, "qt", "_", translations_dir))
{
qDebug () << "Loaded Qt translations for current locale from resources";
LOG_INFO ("Loaded Qt translations for current locale from resources");
}
// Default translations for releases use translations stored in
@ -96,28 +99,28 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
// source control for translators to access and update.
// try and load the base translation
qDebug () << "Looking for WSJT-X translations based on UI languages in the resources filesystem";
LOG_TRACE ("Looking for WSJT-X translations based on UI languages in the resources filesystem");
for (QString locale_name : locale.uiLanguages ())
{
auto language = locale_name.left (2);
if (locale.uiLanguages ().front ().left (2) == language)
{
qDebug () << QString {"Trying %1"}.arg (language);
LOG_TRACE (QString {"Trying %1"}.arg (language));
if (m_->load_translator ("wsjtx_" + language, translations_dir))
{
qDebug () << QString {"Loaded WSJT-X base translation file from %1 based on language %2"}
LOG_INFO (QString {"Loaded WSJT-X base translation file from %1 based on language %2"}
.arg (translations_dir)
.arg (language);
.arg (language));
break;
}
}
}
// now try and load the most specific translations (may be a
// duplicate but we shouldn't care)
qDebug () << "Looking for WSJT-X translations based on locale in the resources filesystem";
LOG_TRACE ("Looking for WSJT-X translations based on locale in the resources filesystem");
if (m_->load_translator (locale, "wsjtx", "_", translations_dir))
{
qDebug () << "Loaded WSJT-X translations for current locale from resources";
LOG_INFO ("Loaded WSJT-X translations for current locale from resources");
}
}
@ -133,21 +136,21 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
language.replace ('-', '_');
// try and load the base translation
auto base_language = language.left (2);
qDebug () << "Looking for WSJT-X translations based on command line region override in the resources filesystem";
LOG_TRACE ("Looking for WSJT-X translations based on command line region override in the resources filesystem");
if (m_->load_translator ("wsjtx_" + base_language, translations_dir))
{
qDebug () << QString {"Loaded base translation file from %1 based on language %2"}
LOG_INFO (QString {"Loaded base translation file from %1 based on language %2"}
.arg (translations_dir)
.arg (base_language);
.arg (base_language));
}
// now load the requested translations (may be a duplicate
// but we shouldn't care)
qDebug () << "Looking for WSJT-X translations based on command line override country in the resources filesystem";
LOG_TRACE ("Looking for WSJT-X translations based on command line override country in the resources filesystem");
if (m_->load_translator ("wsjtx_" + language, translations_dir))
{
qDebug () << QString {"Loaded translation file from %1 based on language %2"}
LOG_INFO (QString {"Loaded translation file from %1 based on language %2"}
.arg (translations_dir)
.arg (language);
.arg (language));
}
}
@ -160,16 +163,16 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
// the LANG environment variable on non-Windows system.
// try and load the base translation
qDebug () << "Looking for WSJT-X translations based on command line override country in the current directory";
LOG_TRACE ("Looking for WSJT-X translations based on command line override country in the current directory");
for (QString locale_name : locale.uiLanguages ())
{
auto language = locale_name.left (2);
if (locale.uiLanguages ().front ().left (2) == language)
{
qDebug () << QString {"Trying %1"}.arg (language);
LOG_TRACE (QString {"Trying %1"}.arg (language));
if (m_->load_translator ("wsjtx_" + language))
{
qDebug () << QString {"Loaded base translation file from $cwd based on language %1"}.arg (language);
LOG_INFO (QString {"Loaded base translation file from $cwd based on language %1"}.arg (language));
break;
}
}
@ -179,10 +182,10 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
{
// now try and load the most specific translations (may be a
// duplicate but we shouldn't care)
qDebug () << "Looking for WSJT-X translations based on locale in the resources filesystem";
LOG_TRACE ("Looking for WSJT-X translations based on locale in the resources filesystem");
if (m_->load_translator (locale, "wsjtx", "_"))
{
qDebug () << "loaded translations for current locale from a file";
LOG_INFO ("loaded translations for current locale from a file");
}
}
@ -198,17 +201,17 @@ L10nLoader::L10nLoader (QApplication * app, QLocale const& locale, QString const
language.replace ('-', '_');
// try and load the base translation
auto base_language = language.left (2);
qDebug () << "Looking for WSJT-X translations based on command line override country in the current directory";
LOG_TRACE ("Looking for WSJT-X translations based on command line override country in the current directory");
if (m_->load_translator ("wsjtx_" + base_language))
{
qDebug () << QString {"Loaded base translation file from $cwd based on language %1"}.arg (base_language);
LOG_INFO (QString {"Loaded base translation file from $cwd based on language %1"}.arg (base_language));
}
// now load the requested translations (may be a duplicate
// but we shouldn't care)
qDebug () << "Looking for WSJT-X translations based on command line region in the current directory";
LOG_TRACE ("Looking for WSJT-X translations based on command line region in the current directory");
if (m_->load_translator ("wsjtx_" + language))
{
qDebug () << QString {"loaded translation file from $cwd based on language %1"}.arg (language);
LOG_INFO (QString {"loaded translation file from $cwd based on language %1"}.arg (language));
}
}
}

185
Logger.cpp Normal file
View File

@ -0,0 +1,185 @@
#include "Logger.hpp"
#include <boost/algorithm/string/predicate.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/log/core.hpp>
#include <boost/log/common.hpp>
#include <boost/log/sinks.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/keyword.hpp>
#include <boost/log/attributes.hpp>
#include <boost/log/attributes/clock.hpp>
#include <boost/log/attributes/counter.hpp>
#include <boost/log/attributes/current_process_id.hpp>
#include <boost/log/attributes/current_thread_id.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/filter_parser.hpp>
#include <boost/log/utility/setup/from_stream.hpp>
#include <boost/log/utility/setup/settings.hpp>
#include <boost/log/sinks/sync_frontend.hpp>
#include <boost/log/sinks/text_ostream_backend.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/filesystem/fstream.hpp>
#include <string>
namespace fs = boost::filesystem;
namespace logging = boost::log;
namespace srcs = logging::sources;
namespace sinks = logging::sinks;
namespace keywords = logging::keywords;
namespace expr = logging::expressions;
namespace attrs = logging::attributes;
namespace ptime = boost::posix_time;
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS (sys,
srcs::severity_channel_logger_mt<logging::trivial::severity_level>,
(keywords::channel = "SYSLOG"));
BOOST_LOG_GLOBAL_LOGGER_CTOR_ARGS (data,
srcs::severity_channel_logger_mt<logging::trivial::severity_level>,
(keywords::channel = "DATALOG"));
namespace Logger
{
namespace
{
// Custom formatter factory to add TimeStamp format support in config ini file.
// Allows %TimeStamp(format=\"%Y.%m.%d %H:%M:%S.%f\")% to be used in ini config file for property Format.
class TimeStampFormatterFactory
: public logging::basic_formatter_factory<char, ptime::ptime>
{
public:
formatter_type create_formatter (logging::attribute_name const& name, args_map const& args)
{
args_map::const_iterator it = args.find ("format");
if (it != args.end ())
{
return expr::stream
<< expr::format_date_time<ptime::ptime>
(
expr::attr<ptime::ptime> (name), it->second
);
}
else
{
return expr::stream
<< expr::attr<ptime::ptime> (name);
}
}
};
// Custom formatter factory to add Uptime format support in config ini file.
// Allows %Uptime(format=\"%O:%M:%S.%f\")% to be used in ini config file for property Format.
// attrs::timer value type is ptime::time_duration
class UptimeFormatterFactory
: public logging::basic_formatter_factory<char, ptime::time_duration>
{
public:
formatter_type create_formatter (logging::attribute_name const& name, args_map const& args)
{
args_map::const_iterator it = args.find ("format");
if (it != args.end ())
{
return expr::stream
<< expr::format_date_time<ptime::time_duration>
(
expr::attr<ptime::time_duration> (name), it->second
);
}
else
{
return expr::stream
<< expr::attr<ptime::time_duration> (name);
}
}
};
class CommonInitialization
{
public:
CommonInitialization ()
{
// Add attributes: LineID, TimeStamp, ProcessID, ThreadID, and Uptime
auto core = logging::core::get ();
core->add_global_attribute ("LineID", attrs::counter<unsigned int> (1));
core->add_global_attribute ("TimeStamp", attrs::utc_clock ());
core->add_global_attribute ("ProcessID", attrs::current_process_id ());
core->add_global_attribute ("ThreadID", attrs::current_thread_id ());
core->add_global_attribute ("Uptime", attrs::timer ());
// Allows %Severity% to be used in ini config file for property Filter.
logging::register_simple_filter_factory<logging::trivial::severity_level, char> ("Severity");
// Allows %Severity% to be used in ini config file for property Format.
logging::register_simple_formatter_factory<logging::trivial::severity_level, char> ("Severity");
// Allows %TimeStamp(format=\"%Y.%m.%d %H:%M:%S.%f\")% to be used in ini config file for property Format.
logging::register_formatter_factory ("TimeStamp", boost::make_shared<TimeStampFormatterFactory> ());
// Allows %Uptime(format=\"%O:%M:%S.%f\")% to be used in ini config file for property Format.
logging::register_formatter_factory ("Uptime", boost::make_shared<UptimeFormatterFactory> ());
}
~CommonInitialization ()
{
}
};
}
void init ()
{
CommonInitialization ci;
}
void init_from_config (std::wistream& stream)
{
CommonInitialization ci;
try
{
// Still can throw even with the exception suppressor above.
logging::init_from_stream (stream);
}
catch (std::exception& e)
{
std::string err = "Caught exception initializing boost logging: ";
err += e.what ();
// Since we cannot be sure of boost log state, output to cerr and cout.
std::cerr << "ERROR: " << err << std::endl;
std::cout << "ERROR: " << err << std::endl;
LOG_ERROR (err);
}
}
void disable ()
{
logging::core::get ()->set_logging_enabled (false);
}
void add_datafile_log (std::wstring const& log_file_name)
{
// Create a text file sink
boost::shared_ptr<sinks::wtext_ostream_backend> backend
(
new sinks::wtext_ostream_backend()
);
backend->add_stream (boost::shared_ptr<std::wostream> (new fs::wofstream (log_file_name)));
// Flush after each log record
backend->auto_flush (true);
// Create a sink for the backend
typedef sinks::synchronous_sink<sinks::wtext_ostream_backend> sink_t;
boost::shared_ptr<sink_t> sink (new sink_t (backend));
// The log output formatter
sink->set_formatter (expr::format (L"[%1%][%2%] %3%")
% expr::attr<ptime::ptime> ("TimeStamp")
% logging::trivial::severity
% expr::message
);
// Filter by severity and by DATALOG channel
sink->set_filter (logging::trivial::severity >= logging::trivial::info &&
expr::attr<std::string> ("Channel") == "DATALOG");
// Add it to the core
logging::core::get ()->add_sink (sink);
}
}

58
Logger.hpp Normal file
View File

@ -0,0 +1,58 @@
#ifndef LOGGER_HPP__
#define LOGGER_HPP__
#include <boost/log/trivial.hpp>
#include <boost/log/sources/global_logger_storage.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/attributes/mutable_constant.hpp>
#include <boost/log/utility/manipulators/add_value.hpp>
#include <iosfwd>
#include <string>
BOOST_LOG_GLOBAL_LOGGER (sys,
boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level>);
BOOST_LOG_GLOBAL_LOGGER (data,
boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level>);
namespace Logger
{
// trivial logging to console
void init ();
// define logger(s) and sinks from a configuration stream
void init_from_config (std::wistream& config_stream);
// disable logging - useful for unit testing etc.
void disable ();
// add a new file sink for LOG_DATA_* for Severity >= INFO
// this file sink will be used alongside any configured above
void add_data_file_log (std::wstring const& log_file_name);
}
#define LOG_LOG_LOCATION(LOGGER, LEVEL, ARG) \
BOOST_LOG_SEV (LOGGER, boost::log::trivial::LEVEL) \
<< boost::log::add_value ("Line", __LINE__) \
<< boost::log::add_value ("File", __FILE__) \
<< boost::log::add_value ("Function", __FUNCTION__) << ARG;
/// System Log macros.
/// TRACE < DEBUG < INFO < WARN < ERROR < FATAL
#define LOG_TRACE(ARG) LOG_LOG_LOCATION (sys::get(), trace, ARG);
#define LOG_DEBUG(ARG) LOG_LOG_LOCATION (sys::get(), debug, ARG);
#define LOG_INFO(ARG) LOG_LOG_LOCATION (sys::get(), info, ARG);
#define LOG_WARN(ARG) LOG_LOG_LOCATION (sys::get(), warning, ARG);
#define LOG_ERROR(ARG) LOG_LOG_LOCATION (sys::get(), error, ARG);
#define LOG_FATAL(ARG) LOG_LOG_LOCATION (sys::get(), fatal, ARG);
/// Data Log macros. Does not include LINE, FILE, FUNCTION.
/// TRACE < DEBUG < INFO < WARN < ERROR < FATAL
#define LOG_DATA_TRACE(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::trace) << ARG
#define LOG_DATA_DEBUG(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::debug) << ARG
#define LOG_DATA_INFO(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::info) << ARG
#define LOG_DATA_WARN(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::warning) << ARG
#define LOG_DATA_ERROR(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::error) << ARG
#define LOG_DATA_FATAL(ARG) BOOST_LOG_SEV (data::get(), boost::log::trivial::fatal) << ARG
#endif

View File

@ -54,6 +54,7 @@ void register_types ()
// Frequency list model
qRegisterMetaTypeStreamOperators<FrequencyList_v2::Item> ("Item_v2");
QMetaType::registerConverter<FrequencyList_v2::Item, QString> (&FrequencyList_v2::Item::toString);
qRegisterMetaTypeStreamOperators<FrequencyList_v2::FrequencyItems> ("FrequencyItems_v2");
// defunct old versions
@ -69,6 +70,7 @@ void register_types ()
// Station details
qRegisterMetaType<StationList::Station> ("Station");
QMetaType::registerConverter<StationList::Station, QString> (&StationList::Station::toString);
qRegisterMetaType<StationList::Stations> ("Stations");
qRegisterMetaTypeStreamOperators<StationList::Station> ("Station");
qRegisterMetaTypeStreamOperators<StationList::Stations> ("Stations");
@ -92,5 +94,6 @@ void register_types ()
// DecodeHighlightingModel
qRegisterMetaTypeStreamOperators<DecodeHighlightingModel::HighlightInfo> ("HighlightInfo");
QMetaType::registerConverter<DecodeHighlightingModel::HighlightInfo, QString> (&DecodeHighlightingModel::HighlightInfo::toString);
qRegisterMetaTypeStreamOperators<DecodeHighlightingModel::HighlightItems> ("HighlightItems");
}

View File

@ -1,3 +1,3 @@
SOURCES += Modulator/Modulator.cpp
HEADERS += Modulator/Mpdulator.hpp
HEADERS += Modulator/Modulator.hpp

View File

@ -6,16 +6,16 @@
#include <limits>
#include <QUdpSocket>
#include <QNetworkInterface>
#include <QHostInfo>
#include <QTimer>
#include <QQueue>
#include <QByteArray>
#include <QHostAddress>
#include <QColor>
#include <QDebug>
#include "NetworkMessage.hpp"
#include "qt_helpers.hpp"
#include "pimpl_impl.hpp"
#include "moc_MessageClient.cpp"
@ -34,14 +34,15 @@ class MessageClient::impl
public:
impl (QString const& id, QString const& version, QString const& revision,
port_type server_port, MessageClient * self)
port_type server_port, int TTL, MessageClient * self)
: self_ {self}
, dns_lookup_id_ {0}
, enabled_ {false}
, id_ {id}
, version_ {version}
, revision_ {revision}
, dns_lookup_id_ {-1}
, server_port_ {server_port}
, TTL_ {TTL}
, schema_ {2} // use 2 prior to negotiation not 1 which is broken
, heartbeat_timer_ {new QTimer {this}}
{
@ -49,47 +50,50 @@ public:
connect (this, &QIODevice::readyRead, this, &impl::pending_datagrams);
heartbeat_timer_->start (NetworkMessage::pulse * 1000);
// bind to an ephemeral port
bind ();
}
~impl ()
{
closedown ();
if (dns_lookup_id_ != -1)
{
QHostInfo::abortHostLookup (dns_lookup_id_);
}
}
enum StreamStatus {Fail, Short, OK};
void parse_message (QByteArray const& msg);
void set_server (QString const& server_name, QStringList const& network_interface_names);
Q_SLOT void host_info_results (QHostInfo);
void start ();
void parse_message (QByteArray const&);
void pending_datagrams ();
void heartbeat ();
void closedown ();
StreamStatus check_status (QDataStream const&) const;
void send_message (QByteArray const&);
void send_message (QDataStream const& out, QByteArray const& message)
void send_message (QByteArray const&, bool queue_if_pending = true);
void send_message (QDataStream const& out, QByteArray const& message, bool queue_if_pending = true)
{
if (OK == check_status (out))
{
send_message (message);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
if (OK == check_status (out))
{
send_message (message, queue_if_pending);
}
else
{
Q_EMIT self_->error ("Error creating UDP message");
}
}
Q_SLOT void host_info_results (QHostInfo);
MessageClient * self_;
int dns_lookup_id_;
bool enabled_;
QString id_;
QString version_;
QString revision_;
QString server_string_;
port_type server_port_;
int dns_lookup_id_;
QHostAddress server_;
port_type server_port_;
int TTL_;
std::vector<QNetworkInterface> network_interfaces_;
quint32 schema_;
QTimer * heartbeat_timer_;
std::vector<QHostAddress> blocked_addresses_;
@ -101,36 +105,99 @@ public:
#include "MessageClient.moc"
void MessageClient::impl::set_server (QString const& server_name, QStringList const& network_interface_names)
{
// qDebug () << "MessageClient server:" << server_name << "port:" << server_port_ << "interfaces:" << network_interface_names;
server_.setAddress (server_name);
network_interfaces_.clear ();
for (auto const& net_if_name : network_interface_names)
{
network_interfaces_.push_back (QNetworkInterface::interfaceFromName (net_if_name));
}
if (server_.isNull () && server_name.size ()) // DNS lookup required
{
// queue a host address lookup
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
dns_lookup_id_ = QHostInfo::lookupHost (server_name, this, &MessageClient::impl::host_info_results);
#else
dns_lookup_id_ = QHostInfo::lookupHost (server_name, this, SLOT (host_info_results (QHostInfo)));
#endif
}
else
{
start ();
}
}
void MessageClient::impl::host_info_results (QHostInfo host_info)
{
if (host_info.lookupId () != dns_lookup_id_) return;
dns_lookup_id_ = -1;
if (QHostInfo::NoError != host_info.error ())
{
Q_EMIT self_->error ("UDP server lookup failed:\n" + host_info.errorString ());
pending_messages_.clear (); // discard
Q_EMIT self_->error ("UDP server DNS lookup failed: " + host_info.errorString ());
}
else if (host_info.addresses ().size ())
else
{
auto server = host_info.addresses ()[0];
if (blocked_addresses_.end () == std::find (blocked_addresses_.begin (), blocked_addresses_.end (), server))
auto const& server_addresses = host_info.addresses ();
if (server_addresses.size ())
{
server_ = server;
TRACE_UDP ("resulting server:" << server);
// send initial heartbeat which allows schema negotiation
heartbeat ();
// clear any backlog
while (pending_messages_.size ())
{
send_message (pending_messages_.dequeue ());
}
server_ = server_addresses[0];
}
else
}
start ();
}
void MessageClient::impl::start ()
{
if (server_.isNull ())
{
Q_EMIT self_->close ();
pending_messages_.clear (); // discard
return;
}
if (is_broadcast_address (server_))
{
Q_EMIT self_->error ("IPv4 broadcast not supported, please specify the loop-back address, a server host address, or multicast group address");
pending_messages_.clear (); // discard
return;
}
if (blocked_addresses_.end () != std::find (blocked_addresses_.begin (), blocked_addresses_.end (), server_))
{
Q_EMIT self_->error ("UDP server blocked, please try another");
pending_messages_.clear (); // discard
return;
}
TRACE_UDP ("Trying server:" << server_.toString ());
QHostAddress interface_addr {IPv6Protocol == server_.protocol () ? QHostAddress::AnyIPv6 : QHostAddress::AnyIPv4};
if (localAddress () != interface_addr)
{
if (UnconnectedState != state () || state ())
{
Q_EMIT self_->error ("UDP server blocked, please try another");
pending_messages_.clear (); // discard
close ();
}
// bind to an ephemeral port on the selected interface and set
// up for sending datagrams
bind (interface_addr);
// qDebug () << "Bound to UDP port:" << localPort () << "on:" << localAddress ();
// set multicast TTL to limit scope when sending to multicast
// group addresses
setSocketOption (MulticastTtlOption, TTL_);
}
// send initial heartbeat which allows schema negotiation
heartbeat ();
// clear any backlog
while (pending_messages_.size ())
{
send_message (pending_messages_.dequeue (), false);
}
}
@ -358,14 +425,11 @@ void MessageClient::impl::heartbeat ()
if (server_port_ && !server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder hb {&message, NetworkMessage::Heartbeat, id_, schema_};
hb << NetworkMessage::Builder::schema_number // maximum schema number accepted
<< version_.toUtf8 () << revision_.toUtf8 ();
if (OK == check_status (hb))
{
TRACE_UDP ("schema:" << schema_ << "max schema:" << NetworkMessage::Builder::schema_number << "version:" << version_ << "revision:" << revision_);
writeDatagram (message, server_, server_port_);
}
NetworkMessage::Builder out {&message, NetworkMessage::Heartbeat, id_, schema_};
out << NetworkMessage::Builder::schema_number // maximum schema number accepted
<< version_.toUtf8 () << revision_.toUtf8 ();
TRACE_UDP ("schema:" << schema_ << "max schema:" << NetworkMessage::Builder::schema_number << "version:" << version_ << "revision:" << revision_);
send_message (out, message, false);
}
}
@ -375,15 +439,12 @@ void MessageClient::impl::closedown ()
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Close, id_, schema_};
if (OK == check_status (out))
{
TRACE_UDP ("");
writeDatagram (message, server_, server_port_);
}
TRACE_UDP ("");
send_message (out, message, false);
}
}
void MessageClient::impl::send_message (QByteArray const& message)
void MessageClient::impl::send_message (QByteArray const& message, bool queue_if_pending)
{
if (server_port_)
{
@ -391,11 +452,25 @@ void MessageClient::impl::send_message (QByteArray const& message)
{
if (message != last_message_) // avoid duplicates
{
writeDatagram (message, server_, server_port_);
if (is_multicast_address (server_))
{
// send datagram on each selected network interface
std::for_each (network_interfaces_.begin (), network_interfaces_.end ()
, [&] (QNetworkInterface const& net_if) {
setMulticastInterface (net_if);
// qDebug () << "Multicast UDP datagram sent to:" << server_ << "port:" << server_port_ << "on:" << multicastInterface ().humanReadableName ();
writeDatagram (message, server_, server_port_);
});
}
else
{
// qDebug () << "Unicast UDP datagram sent to:" << server_ << "port:" << server_port_;
writeDatagram (message, server_, server_port_);
}
last_message_ = message;
}
}
else
else if (queue_if_pending)
{
pending_messages_.enqueue (message);
}
@ -428,9 +503,11 @@ auto MessageClient::impl::check_status (QDataStream const& stream) const -> Stre
}
MessageClient::MessageClient (QString const& id, QString const& version, QString const& revision,
QString const& server, port_type server_port, QObject * self)
QString const& server_name, port_type server_port,
QStringList const& network_interface_names,
int TTL, QObject * self)
: QObject {self}
, m_ {id, version, revision, server_port, this}
, m_ {id, version, revision, server_port, TTL, this}
{
connect (&*m_
#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
@ -449,8 +526,8 @@ MessageClient::MessageClient (QString const& id, QString const& version, QString
#endif
Q_EMIT error (m_->errorString ());
}
});
set_server (server);
});
m_->set_server (server_name, network_interface_names);
}
QHostAddress MessageClient::server_address () const
@ -463,20 +540,9 @@ auto MessageClient::server_port () const -> port_type
return m_->server_port_;
}
void MessageClient::set_server (QString const& server)
void MessageClient::set_server (QString const& server_name, QStringList const& network_interface_names)
{
m_->server_.clear ();
m_->server_string_ = server;
if (server.size ())
{
// queue a host address lookup
TRACE_UDP ("server host DNS lookup:" << server);
#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, &MessageClient::impl::host_info_results);
#else
m_->dns_lookup_id_ = QHostInfo::lookupHost (server, &*m_, SLOT (host_info_results (QHostInfo)));
#endif
}
m_->set_server (server_name, network_interface_names);
}
void MessageClient::set_server_port (port_type server_port)
@ -484,6 +550,12 @@ void MessageClient::set_server_port (port_type server_port)
m_->server_port_ = server_port;
}
void MessageClient::set_TTL (int TTL)
{
m_->TTL_ = TTL;
m_->setSocketOption (QAbstractSocket::MulticastTtlOption, m_->TTL_);
}
void MessageClient::enable (bool flag)
{
m_->enabled_ = flag;
@ -499,7 +571,7 @@ void MessageClient::status_update (Frequency f, QString const& mode, QString con
, quint32 frequency_tolerance, quint32 tr_period
, QString const& configuration_name)
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Status, m_->id_, m_->schema_};
@ -516,7 +588,7 @@ void MessageClient::decode (bool is_new, QTime time, qint32 snr, float delta_tim
, QString const& mode, QString const& message_text, bool low_confidence
, bool off_air)
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Decode, m_->id_, m_->schema_};
@ -531,7 +603,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt
, qint32 drift, QString const& callsign, QString const& grid, qint32 power
, bool off_air)
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::WSPRDecode, m_->id_, m_->schema_};
@ -544,7 +616,7 @@ void MessageClient::WSPR_decode (bool is_new, QTime time, qint32 snr, float delt
void MessageClient::decodes_cleared ()
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Clear, m_->id_, m_->schema_};
@ -561,7 +633,7 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
, QString const& my_grid, QString const& exchange_sent
, QString const& exchange_rcvd, QString const& propmode)
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::QSOLogged, m_->id_, m_->schema_};
@ -576,7 +648,7 @@ void MessageClient::qso_logged (QDateTime time_off, QString const& dx_call, QStr
void MessageClient::logged_ADIF (QByteArray const& ADIF_record)
{
if (m_->server_port_ && !m_->server_string_.isEmpty ())
if (m_->server_port_ && !m_->server_.isNull ())
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::LoggedADIF, m_->id_, m_->schema_};

View File

@ -5,6 +5,7 @@
#include <QTime>
#include <QDateTime>
#include <QString>
#include <QHostAddress>
#include "Radio.hpp"
#include "pimpl_h.hpp"
@ -34,19 +35,25 @@ public:
//
// messages will be silently dropped until a server host lookup is complete
MessageClient (QString const& id, QString const& version, QString const& revision,
QString const& server, port_type server_port, QObject * parent = nullptr);
QString const& server_name, port_type server_port,
QStringList const& network_interface_names,
int TTL, QObject * parent = nullptr);
// query server details
QHostAddress server_address () const;
port_type server_port () const;
// initiate a new server host lookup or is the server name is empty
// the sending of messages is disabled
Q_SLOT void set_server (QString const& server = QString {});
// initiate a new server host lookup or if the server name is empty
// the sending of messages is disabled, if an interface is specified
// then that interface is used for outgoing datagrams
Q_SLOT void set_server (QString const& server_name, QStringList const& network_interface_names);
// change the server port messages are sent to
Q_SLOT void set_server_port (port_type server_port = 0u);
// change the server port messages are sent to
Q_SLOT void set_TTL (int TTL);
// enable incoming messages
Q_SLOT void enable (bool);

View File

@ -28,13 +28,13 @@ namespace NetworkMessage
{
setVersion (QDataStream::Qt_5_0); // Qt schema version
}
#if QT_VERSION >= 0x050200
#if QT_VERSION >= QT_VERSION_CHECK (5, 2, 0)
else if (schema <= 2)
{
setVersion (QDataStream::Qt_5_2); // Qt schema version
}
#endif
#if QT_VERSION >= 0x050400
#if QT_VERSION >= QT_VERSION_CHECK (5, 4, 0)
else if (schema <= 3)
{
setVersion (QDataStream::Qt_5_4); // Qt schema version
@ -73,13 +73,13 @@ namespace NetworkMessage
{
parent->setVersion (QDataStream::Qt_5_0);
}
#if QT_VERSION >= 0x050200
#if QT_VERSION >= QT_VERSION_CHECK (5, 2, 0)
else if (schema_ <= 2)
{
parent->setVersion (QDataStream::Qt_5_2);
}
#endif
#if QT_VERSION >= 0x050400
#if QT_VERSION >= QT_VERSION_CHECK (5, 4, 0)
else if (schema_ <= 3)
{
parent->setVersion (QDataStream::Qt_5_4);

View File

@ -540,9 +540,9 @@ namespace NetworkMessage
// increment this if a newer Qt schema is required and add decode
// logic to the Builder and Reader class implementations
#if QT_VERSION >= 0x050400
#if QT_VERSION >= QT_VERSION_CHECK (5, 4, 0)
static quint32 constexpr schema_number {3};
#elif QT_VERSION >= 0x050200
#elif QT_VERSION >= QT_VERSION_CHECK (5, 2, 0)
static quint32 constexpr schema_number {2};
#else
// Schema 1 (Qt_5_0) is broken

View File

@ -84,7 +84,7 @@ public:
{
if (!socket_
|| QAbstractSocket::UnconnectedState == socket_->state ()
|| (socket_->socketType () != config_->psk_reporter_tcpip () ? QAbstractSocket::TcpSocket : QAbstractSocket::UdpSocket))
|| (socket_->socketType () != (config_->psk_reporter_tcpip () ? QAbstractSocket::TcpSocket : QAbstractSocket::UdpSocket)))
{
// we need to create the appropriate socket
if (socket_

View File

@ -86,26 +86,29 @@ void WSPRNet::upload (QString const& call, QString const& grid, QString const& r
m_file = fileName;
// Open the wsprd.out file
QFile wsprdOutFile (fileName);
if (!wsprdOutFile.open (QIODevice::ReadOnly | QIODevice::Text) || !wsprdOutFile.size ())
if (m_uploadType != 3)
{
spot_queue_.enqueue (urlEncodeNoSpot ());
m_uploadType = 1;
}
else
{
// Read the contents
while (!wsprdOutFile.atEnd())
QFile wsprdOutFile (fileName);
if (!wsprdOutFile.open (QIODevice::ReadOnly | QIODevice::Text) || !wsprdOutFile.size ())
{
SpotQueue::value_type query;
if (decodeLine (wsprdOutFile.readLine(), query))
spot_queue_.enqueue (urlEncodeNoSpot ());
m_uploadType = 1;
}
else
{
// Read the contents
while (!wsprdOutFile.atEnd())
{
// Prevent reporting data ouside of the current frequency band
float f = fabs (m_rfreq.toFloat() - query.queryItemValue ("tqrg", QUrl::FullyDecoded).toFloat());
if (f < 0.0002)
SpotQueue::value_type query;
if (decodeLine (wsprdOutFile.readLine(), query))
{
spot_queue_.enqueue(urlEncodeSpot (query));
m_uploadType = 2;
// Prevent reporting data ouside of the current frequency band
float f = fabs (m_rfreq.toFloat() - query.queryItemValue ("tqrg", QUrl::FullyDecoded).toFloat());
if (f < 0.01) // MHz
{
spot_queue_.enqueue(urlEncodeSpot (query));
m_uploadType = 2;
}
}
}
}
@ -133,10 +136,8 @@ void WSPRNet::post (QString const& call, QString const& grid, QString const& rfr
if (!spot_queue_.size ())
{
spot_queue_.enqueue (urlEncodeNoSpot ());
m_uploadType = 1;
m_uploadType = 3;
}
spots_to_send_ = spot_queue_.size ();
upload_timer_.start (200);
}
else
{
@ -144,9 +145,11 @@ void WSPRNet::post (QString const& call, QString const& grid, QString const& rfr
if (match.hasMatch ())
{
SpotQueue::value_type query;
// Prevent reporting data ouside of the current frequency band
// Prevent reporting data ouside of the current frequency
// band - removed by G4WJS to accommodate FST4W spots
// outside of WSPR segments
auto tqrg = match.captured ("freq").toInt ();
if (tqrg >= 1400 && tqrg <= 1600)
// if (tqrg >= 1400 && tqrg <= 1600)
{
query.addQueryItem ("function", "wspr");
// use time as at 3/4 of T/R period before current to
@ -208,7 +211,7 @@ void WSPRNet::networkReply (QNetworkReply * reply)
}
}
bool WSPRNet::decodeLine (QString const& line, SpotQueue::value_type& query)
bool WSPRNet::decodeLine (QString const& line, SpotQueue::value_type& query) const
{
auto const& rx_match = wspr_re.match (line);
if (rx_match.hasMatch ()) {
@ -268,7 +271,23 @@ bool WSPRNet::decodeLine (QString const& line, SpotQueue::value_type& query)
return true;
}
auto WSPRNet::urlEncodeNoSpot () -> SpotQueue::value_type
QString WSPRNet::encode_mode () const
{
if (m_mode == "WSPR") return "2";
if (m_mode == "WSPR-15") return "15";
if (m_mode == "FST4W")
{
auto tr = static_cast<int> ((TR_period_ / 60.)+.5);
if (2 == tr || 15 == tr)
{
tr += 1; // distinguish from WSPR-2 and WSPR-15
}
return QString::number (tr);
}
return "";
}
auto WSPRNet::urlEncodeNoSpot () const -> SpotQueue::value_type
{
SpotQueue::value_type query;
query.addQueryItem ("function", "wsprstat");
@ -279,28 +298,18 @@ auto WSPRNet::urlEncodeNoSpot () -> SpotQueue::value_type
query.addQueryItem ("tqrg", m_tfreq);
query.addQueryItem ("dbm", m_dbm);
query.addQueryItem ("version", m_vers);
if (m_mode == "WSPR") query.addQueryItem ("mode", "2");
if (m_mode == "WSPR-15") query.addQueryItem ("mode", "15");
if (m_mode == "FST4W")
{
query.addQueryItem ("mode", QString::number (static_cast<int> ((TR_period_ / 60.)+.5)));
}
query.addQueryItem ("mode", encode_mode ());
return query;;
}
auto WSPRNet::urlEncodeSpot (SpotQueue::value_type& query) -> SpotQueue::value_type
auto WSPRNet::urlEncodeSpot (SpotQueue::value_type& query) const -> SpotQueue::value_type
{
query.addQueryItem ("version", m_vers);
query.addQueryItem ("rcall", m_call);
query.addQueryItem ("rgrid", m_grid);
query.addQueryItem ("rqrg", m_rfreq);
if (m_mode == "WSPR") query.addQueryItem ("mode", "2");
if (m_mode == "WSPR-15") query.addQueryItem ("mode", "15");
if (m_mode == "FST4W")
{
query.addQueryItem ("mode", QString::number (static_cast<int> ((TR_period_ / 60.)+.5)));
}
return query;
query.addQueryItem ("mode", encode_mode ());
return query;
}
void WSPRNet::work()

View File

@ -34,9 +34,10 @@ public slots:
void abortOutstandingRequests ();
private:
bool decodeLine (QString const& line, SpotQueue::value_type& query);
SpotQueue::value_type urlEncodeNoSpot ();
SpotQueue::value_type urlEncodeSpot (SpotQueue::value_type& spot);
bool decodeLine (QString const& line, SpotQueue::value_type& query) const;
SpotQueue::value_type urlEncodeNoSpot () const;
SpotQueue::value_type urlEncodeSpot (SpotQueue::value_type& spot) const;
QString encode_mode () const;
QNetworkAccessManager * network_manager_;
QList<QNetworkReply *> m_outstandingRequests;

116
NonInheritingProcess.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "NonInheritingProcess.hpp"
#ifdef Q_OS_WIN
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef _UNICODE
#define _UNICODE
#endif
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT 0x0601
#include <windows.h>
#endif
#include <memory>
#include <functional>
#include "pimpl_impl.hpp"
namespace
{
#ifdef Q_OS_WIN
struct start_info_deleter
{
void operator () (STARTUPINFOEXW * si)
{
if (si->lpAttributeList)
{
::DeleteProcThreadAttributeList (si->lpAttributeList);
}
delete si;
}
};
#endif
}
class NonInheritingProcess::impl
{
public:
#ifdef Q_OS_WIN
void extend_CreateProcessArguments (QProcess::CreateProcessArguments * args)
{
//
// Here we modify the CreateProcessArguments structure to use a
// STARTUPINFOEX extended argument to CreateProcess. In that we
// set up a list of handles for the new process to inherit. By
// doing this we stop all inherited handles from being
// inherited. Unfortunately UpdateProcThreadAttribute does not let
// us set up an empty handle list, so we populate the list with
// the three standard stream handles that QProcess::start has set
// up as Pipes to do IPC. Even though these Pipe handles are
// created with inheritance disabled, UpdateProcThreadAtribute and
// CreateProcess don't seem to mind, which suits us fine.
//
// Note: that we cannot just clear the inheritHandles flag as that
// stops the standard stream handles being inherited which breaks
// our IPC using std(in|out|err). Only be using a
// PROC_THREAD_ATTRIBUTE_HANDLE_LIST attribute in a STARTUPINFOEX
// structure can we avoid the all or nothing behaviour of
// CreateProcess /w respect to handle inheritance.
//
BOOL fSuccess;
SIZE_T size {0};
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList = nullptr;
::InitializeProcThreadAttributeList (nullptr, 1, 0, &size);
lpAttributeList = reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST> (::HeapAlloc (::GetProcessHeap (), 0, size));
fSuccess = !!lpAttributeList;
if (fSuccess)
{
fSuccess = ::InitializeProcThreadAttributeList (lpAttributeList, 1, 0, &size);
}
if (fSuccess)
{
// empty list of handles
fSuccess = ::UpdateProcThreadAttribute (lpAttributeList, 0,
PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
&args->startupInfo->hStdInput, 3 * sizeof (HANDLE),
nullptr, 0);
}
if (fSuccess)
{
start_info_.reset (new STARTUPINFOEXW);
start_info_->StartupInfo = *args->startupInfo;
start_info_->StartupInfo.cb = sizeof (STARTUPINFOEXW);
start_info_->lpAttributeList = lpAttributeList;
args->startupInfo = reinterpret_cast<Q_STARTUPINFO*> (start_info_.get ());
args->flags |= EXTENDED_STARTUPINFO_PRESENT;
}
}
using start_info_type = std::unique_ptr<STARTUPINFOEXW, start_info_deleter>;
start_info_type start_info_;
#endif
};
NonInheritingProcess::NonInheritingProcess (QObject * parent)
: QProcess {parent}
{
#ifdef Q_OS_WIN
using namespace std::placeholders;
// enable cleanup after process starts or fails to start
connect (this, &QProcess::started, [this] {m_->start_info_.reset ();});
connect (this, &QProcess::errorOccurred, [this] (QProcess::ProcessError) {m_->start_info_.reset ();});
setCreateProcessArgumentsModifier (std::bind (&NonInheritingProcess::impl::extend_CreateProcessArguments, &*m_, _1));
#endif
}
NonInheritingProcess::~NonInheritingProcess ()
{
}

35
NonInheritingProcess.hpp Normal file
View File

@ -0,0 +1,35 @@
#ifndef NON_INHERITING_PROCESS_HPP__
#define NON_INHERITING_PROCESS_HPP__
#include <QProcess>
#include "pimpl_h.hpp"
class QObject;
//
// class NonInheritingProcess - Manage a process without it inheriting
// all inheritable handles
//
// On MS Windows QProcess creates sub-processes which inherit all
// inheritable handles, and handles on Windows are inheritable by
// default. This can cause the lifetime of objects to be unexpectedly
// extended, which in turn can cause unexpected errors. The motivation
// for this class was implementing log file rotation using the Boost
// log library. The current log file's handle gets inherited by any
// long running sub-process started by QProcess and that causes a
// sharing violation when attempting to rename the log file on
// rotation, even though the log library closes the current log file
// before trying to rename it.
//
class NonInheritingProcess
: public QProcess
{
public:
NonInheritingProcess (QObject * parent = nullptr);
~NonInheritingProcess ();
private:
class impl;
pimpl<impl> m_;
};
#endif

View File

@ -14,10 +14,16 @@ namespace Radio
double constexpr MHz_factor {1.e6};
int constexpr frequency_precsion {6};
// valid callsign alphabet
QRegularExpression callsign_alphabet_re {R"(^[A-Z0-9/]{3,11}$)"};
// very loose validation - callsign must contain a letter next to
// a number
QRegularExpression valid_callsign_regexp {R"(\d[[:alpha:]]|[[:alpha:]]\d)"};
// standard callsign
QRegularExpression strict_standard_callsign_re {R"(^([A-Z][0-9]?|[0-9A-Z][A-Z])[0-9][A-Z]{0,3}$)"};
// suffixes that are often used and should not be interpreted as a
// DXCC Entity prefix used as a suffix
QRegularExpression non_prefix_suffix {R"(\A([0-9AMPQR]|QRP|F[DF]|[AM]M|L[HT]|LGT)\z)"};
@ -112,6 +118,12 @@ namespace Radio
return callsign.contains ('/');
}
bool is_77bit_nonstandard_callsign (QString const& callsign)
{
return callsign.contains (callsign_alphabet_re)
&& !callsign.contains (strict_standard_callsign_re);
}
// split on first '/' and return the larger portion or the whole if
// there is no '/'
QString base_callsign (QString callsign)

View File

@ -53,6 +53,7 @@ namespace Radio
//
bool UDP_EXPORT is_callsign (QString const&);
bool UDP_EXPORT is_compound_callsign (QString const&);
bool is_77bit_nonstandard_callsign (QString const&);
QString UDP_EXPORT base_callsign (QString);
QString UDP_EXPORT effective_prefix (QString);
}

View File

@ -13,6 +13,38 @@
Copyright 2001 - 2020 by Joe Taylor, K1JT.
Release: WSJT-X 2.3.0-rc2
Nov 16, 2020
-------------------------
WSJT-X 2.3.0 Release Candidate 2 fixes issues found in RC1 and
includes some new functionality that missed the RC1 cut off deadline.
- Dropped audio samples message box removed, warnings and errors for
these are now sent to the WSJT-X system log.
- FST4W spots to WSPRNet.org will be augmented such that the server
can distinguish the mode being spotted. Spots to WSPRNet.org will
no longer be restricted to WSPR sub-bands.
- A new internal system and data logging facility used to provide
trace, debug, information, warning, error, and fatal error
messages. The verbosity and filtering of messages is user definable
via a configuration file. Without a configuration file a basic log
is written with information, warning and error messages only. Log
files are automatically rotated to limit disk usage.
- Due to some users using inappropriate multicast IP addresses for
their interoperating severs the default behaviour now is to only
send multicast UDP datagrams to the loop-back network interface.
Users who require WSJT-X UDP Message Protocol datagrams to reach
other hosts will now have to configure WSJT-X to send on an
appropriate network interface, and use an appropriately scoped
multicast group address for their server applications. If you are
not sure then 224.0.0.1 (or ff02::1 if IPv6 is desired) is a safe
choice.
Release: WSJT-X 2.3.0-rc1
Sept 28, 2020
-------------------------

View File

@ -6,6 +6,7 @@
#include <QThread>
#include <QDateTime>
#include "qt_helpers.hpp"
#include "Network/NetworkServerLookup.hpp"
#include "moc_DXLabSuiteCommanderTransceiver.cpp"
@ -36,15 +37,18 @@ namespace
}
}
void DXLabSuiteCommanderTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
void DXLabSuiteCommanderTransceiver::register_transceivers (logger_type * /*logger*/,
TransceiverFactory::Transceivers * registry,
int id)
{
(*registry)[commander_transceiver_name] = TransceiverFactory::Capabilities {id, TransceiverFactory::Capabilities::network, true};
}
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (logger_type * logger,
std::unique_ptr<TransceiverBase> wrapped,
QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent)
: PollingTransceiver {poll_interval, parent}
: PollingTransceiver {logger, poll_interval, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, server_ {address}
@ -54,7 +58,7 @@ DXLabSuiteCommanderTransceiver::DXLabSuiteCommanderTransceiver (std::unique_ptr<
int DXLabSuiteCommanderTransceiver::do_start ()
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "starting");
CAT_TRACE ("starting");
if (wrapped_) wrapped_->start (0);
auto server_details = network_server_lookup (server_, 52002u, QHostAddress::LocalHost, QAbstractSocket::IPv4Protocol);
@ -67,7 +71,7 @@ int DXLabSuiteCommanderTransceiver::do_start ()
commander_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!commander_->waitForConnected ())
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to connect" << commander_->errorString ());
CAT_ERROR ("failed to connect" << commander_->errorString ());
throw error {tr ("Failed to connect to DX Lab Suite Commander\n") + commander_->errorString ()};
}
@ -123,7 +127,7 @@ int DXLabSuiteCommanderTransceiver::do_start ()
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
CAT_ERROR ("get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly reading frequency: ") + reply};
}
@ -140,12 +144,12 @@ void DXLabSuiteCommanderTransceiver::do_stop ()
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "stopped");
CAT_TRACE ("stopped");
}
void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", on << state ());
CAT_TRACE (on << state ());
if (use_for_ptt_)
{
simple_command (on ? "<command:5>CmdTX<parameters:0>" : "<command:5>CmdRX<parameters:0>");
@ -170,13 +174,13 @@ void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "unexpected TX state" << state);
CAT_ERROR ("unexpected TX state" << state);
throw error {tr ("DX Lab Suite Commander sent an unrecognised TX state: ") + state};
}
}
else
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "get TX unexpected response" << reply);
CAT_ERROR ("get TX unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX status: ") + reply};
}
if (tx != on) QThread::msleep (10); // don't thrash Commander
@ -184,7 +188,7 @@ void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
update_PTT (tx);
if (tx != on)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "rig failed to respond to PTT: " << on);
CAT_ERROR ("rig failed to respond to PTT: " << on);
throw error {tr ("DX Lab Suite Commander rig did not respond to PTT: ") + (on ? "ON" : "OFF")};
}
}
@ -200,7 +204,7 @@ void DXLabSuiteCommanderTransceiver::do_ptt (bool on)
void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", f << state ());
CAT_TRACE (f << state ());
auto f_string = frequency_to_string (f);
if (UNK != m && m != get_mode ())
{
@ -219,7 +223,7 @@ void DXLabSuiteCommanderTransceiver::do_frequency (Frequency f, MODE m, bool /*n
void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool /*no_ignore*/)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", tx << state ());
CAT_TRACE (tx << state ());
if (tx)
{
auto f_string = frequency_to_string (tx);
@ -240,7 +244,7 @@ void DXLabSuiteCommanderTransceiver::do_tx_frequency (Frequency tx, MODE mode, b
void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", m << state ());
CAT_TRACE (m << state ());
auto m_string = map_mode (m);
auto params = ("<1:%1>" + m_string).arg (m_string.size ());
simple_command (("<command:10>CmdSetMode<parameters:%1>" + params).arg (params.size ()));
@ -249,13 +253,7 @@ void DXLabSuiteCommanderTransceiver::do_mode (MODE m)
void DXLabSuiteCommanderTransceiver::do_poll ()
{
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
bool quiet {false};
#else
bool quiet {true};
#endif
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>", quiet);
auto reply = command_with_reply ("<command:10>CmdGetFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
@ -270,13 +268,13 @@ void DXLabSuiteCommanderTransceiver::do_poll ()
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get frequency unexpected response" << reply);
CAT_ERROR ("get frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling frequency: ") + reply};
}
if (state ().split ())
{
reply = command_with_reply ("<command:12>CmdGetTXFreq<parameters:0>", quiet);
reply = command_with_reply ("<command:12>CmdGetTXFreq<parameters:0>");
if (0 == reply.indexOf ("<CmdTXFreq:"))
{
auto f = string_to_frequency (reply.mid (reply.indexOf ('>') + 1));
@ -291,12 +289,12 @@ void DXLabSuiteCommanderTransceiver::do_poll ()
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get tx frequency unexpected response" << reply);
CAT_ERROR ("get tx frequency unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling TX frequency: ") + reply};
}
}
reply = command_with_reply ("<command:12>CmdSendSplit<parameters:0>", quiet);
reply = command_with_reply ("<command:12>CmdSendSplit<parameters:0>");
if (0 == reply.indexOf ("<CmdSplit:"))
{
auto split = reply.mid (reply.indexOf ('>') + 1);
@ -310,23 +308,23 @@ void DXLabSuiteCommanderTransceiver::do_poll ()
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected split state" << split);
CAT_ERROR ("unexpected split state" << split);
throw error {tr ("DX Lab Suite Commander sent an unrecognised split state: ") + split};
}
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "get split mode unexpected response" << reply);
CAT_ERROR ("get split mode unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling split status: ") + reply};
}
get_mode (quiet);
get_mode ();
}
auto DXLabSuiteCommanderTransceiver::get_mode (bool no_debug) -> MODE
auto DXLabSuiteCommanderTransceiver::get_mode () -> MODE
{
MODE m {UNK};
auto reply = command_with_reply ("<command:11>CmdSendMode<parameters:0>", no_debug);
auto reply = command_with_reply ("<command:11>CmdSendMode<parameters:0>");
if (0 == reply.indexOf ("<CmdMode:"))
{
auto mode = reply.mid (reply.indexOf ('>') + 1);
@ -372,42 +370,39 @@ auto DXLabSuiteCommanderTransceiver::get_mode (bool no_debug) -> MODE
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected mode name" << mode);
CAT_ERROR ("unexpected mode name" << mode);
throw error {tr ("DX Lab Suite Commander sent an unrecognised mode: \"") + mode + '"'};
}
update_mode (m);
}
else
{
TRACE_CAT_POLL ("DXLabSuiteCommanderTransceiver", "unexpected response" << reply);
CAT_ERROR ("unexpected response" << reply);
throw error {tr ("DX Lab Suite Commander didn't respond correctly polling mode: ") + reply};
}
return m;
}
void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd, bool no_debug)
void DXLabSuiteCommanderTransceiver::simple_command (QString const& cmd)
{
Q_ASSERT (commander_);
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd);
}
CAT_TRACE (cmd);
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed:" << commander_->errorString ());
CAT_ERROR ("failed:" << commander_->errorString ());
throw error {tr ("DX Lab Suite Commander send command failed\n") + commander_->errorString ()};
}
}
QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd, bool no_debug)
QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd)
{
Q_ASSERT (commander_);
if (!write_to_port (cmd))
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", "failed to send command:" << commander_->errorString ());
CAT_ERROR ("failed to send command:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander send command failed \"%1\": %2\n")
.arg (cmd)
@ -424,7 +419,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
replied = commander_->waitForReadyRead ();
if (!replied && commander_->error () != commander_->SocketTimeoutError)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "failed to read reply:" << commander_->errorString ());
CAT_ERROR (cmd << "failed to read reply:" << commander_->errorString ());
throw error {
tr ("DX Lab Suite Commander send command \"%1\" read reply failed: %2\n")
.arg (cmd)
@ -435,7 +430,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
if (!replied)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "retries exhausted");
CAT_ERROR (cmd << "retries exhausted");
throw error {
tr ("DX Lab Suite Commander retries exhausted sending command \"%1\"")
.arg (cmd)
@ -449,11 +444,7 @@ QString DXLabSuiteCommanderTransceiver::command_with_reply (QString const& cmd,
// qDebug () << i << ":" << hex << int (result[i]);
// }
if (!no_debug)
{
TRACE_CAT ("DXLabSuiteCommanderTransceiver", cmd << "->" << result);
}
CAT_TRACE (cmd << "->" << QString {result});
return result; // converting raw UTF-8 bytes to QString
}

View File

@ -24,10 +24,10 @@ class DXLabSuiteCommanderTransceiver final
Q_OBJECT; // for translation context
public:
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id);
// takes ownership of wrapped Transceiver
explicit DXLabSuiteCommanderTransceiver (std::unique_ptr<TransceiverBase> wrapped,
explicit DXLabSuiteCommanderTransceiver (logger_type *, std::unique_ptr<TransceiverBase> wrapped,
QString const& address, bool use_for_ptt,
int poll_interval, QObject * parent = nullptr);
@ -42,9 +42,9 @@ protected:
void do_poll () override;
private:
MODE get_mode (bool no_debug = false);
void simple_command (QString const&, bool no_debug = false);
QString command_with_reply (QString const&, bool no_debug = false);
MODE get_mode ();
void simple_command (QString const&);
QString command_with_reply (QString const&);
bool write_to_port (QString const&);
QString frequency_to_string (Frequency) const;
Frequency string_to_frequency (QString) const;

View File

@ -2,8 +2,10 @@
#include "moc_EmulateSplitTransceiver.cpp"
EmulateSplitTransceiver::EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped, QObject * parent)
: Transceiver {parent}
EmulateSplitTransceiver::EmulateSplitTransceiver (logger_type * logger,
std::unique_ptr<Transceiver> wrapped,
QObject * parent)
: Transceiver {logger, parent}
, wrapped_ {std::move (wrapped)}
, rx_frequency_ {0}
, tx_frequency_ {0}

View File

@ -31,7 +31,8 @@ class EmulateSplitTransceiver final
public:
// takes ownership of wrapped Transceiver
explicit EmulateSplitTransceiver (std::unique_ptr<Transceiver> wrapped,
explicit EmulateSplitTransceiver (logger_type *,
std::unique_ptr<Transceiver> wrapped,
QObject * parent = nullptr);
void set (TransceiverState const&,

View File

@ -23,7 +23,9 @@ namespace
#include "moc_HRDTransceiver.cpp"
void HRDTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id)
void HRDTransceiver::register_transceivers (logger_type *,
TransceiverFactory::Transceivers * registry,
int id)
{
(*registry)[HRD_transceiver_name] = TransceiverFactory::Capabilities (id, TransceiverFactory::Capabilities::network, true, true /* maybe */);
}
@ -70,13 +72,14 @@ struct HRDMessage
static qint32 constexpr magic_2_value_ = 0xABCD1234;
};
HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
HRDTransceiver::HRDTransceiver (logger_type * logger
, std::unique_ptr<TransceiverBase> wrapped
, QString const& server
, bool use_for_ptt
, TransceiverFactory::TXAudioSource audio_source
, int poll_interval
, QObject * parent)
: PollingTransceiver {poll_interval, parent}
: PollingTransceiver {logger, poll_interval, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {use_for_ptt}
, audio_source_ {audio_source}
@ -111,7 +114,7 @@ HRDTransceiver::HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
int HRDTransceiver::do_start ()
{
TRACE_CAT ("HRDTransceiver", "starting");
CAT_TRACE ("starting");
if (wrapped_) wrapped_->start (0);
auto server_details = network_server_lookup (server_, 7809u);
@ -122,7 +125,7 @@ int HRDTransceiver::do_start ()
hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!hrd_->waitForConnected ())
{
TRACE_CAT ("HRDTransceiver", "failed to connect:" << hrd_->errorString ());
CAT_ERROR ("failed to connect:" << hrd_->errorString ());
throw error {tr ("Failed to connect to Ham Radio Deluxe\n") + hrd_->errorString ()};
}
@ -147,7 +150,7 @@ int HRDTransceiver::do_start ()
hrd_->connectToHost (std::get<0> (server_details), std::get<1> (server_details));
if (!hrd_->waitForConnected ())
{
TRACE_CAT ("HRDTransceiver", "failed to connect:" << hrd_->errorString ());
CAT_ERROR ("failed to connect:" << hrd_->errorString ());
throw error {tr ("Failed to connect to Ham Radio Deluxe\n") + hrd_->errorString ()};
}
@ -164,14 +167,14 @@ int HRDTransceiver::do_start ()
auto id = send_command ("get id", false, false);
auto version = send_command ("get version", false, false);
TRACE_CAT ("HRDTransceiver", "Id:" << id << "Version:" << version);
CAT_INFO ("Id: " << id << "Version: " << version);
HRD_info << "Id: " << id << "\n";
HRD_info << "Version: " << version << "\n";
auto radios = send_command ("get radios", false, false).trimmed ().split (',', SkipEmptyParts);
if (radios.isEmpty ())
{
TRACE_CAT ("HRDTransceiver", "no rig found");
CAT_ERROR ("no rig found");
throw error {tr ("Ham Radio Deluxe: no rig found")};
}
@ -183,48 +186,46 @@ int HRDTransceiver::do_start ()
radios_.push_back (std::forward_as_tuple (entries[0].toUInt (), entries[1]));
}
#if WSJT_TRACE_CAT
TRACE_CAT ("HRDTransceiver", "radios:-");
CAT_TRACE ("radios:-");
Q_FOREACH (auto const& radio, radios_)
{
TRACE_CAT ("HRDTransceiver", "\t[" << std::get<0> (radio) << "] " << std::get<1> (radio));
CAT_TRACE ("\t[" << std::get<0> (radio) << "] " << std::get<1> (radio));
}
#endif
auto current_radio_name = send_command ("get radio", false, false, true);
auto current_radio_name = send_command ("get radio", false, false);
HRD_info << "Current radio: " << current_radio_name << "\n";
if (current_radio_name.isEmpty ())
{
TRACE_CAT ("HRDTransceiver", "no rig found");
CAT_ERROR ("no rig found");
throw error {tr ("Ham Radio Deluxe: no rig found")};
}
vfo_count_ = send_command ("get vfo-count").toUInt ();
HRD_info << "VFO count: " << vfo_count_ << "\n";
TRACE_CAT ("HRDTransceiver", "vfo count:" << vfo_count_);
CAT_TRACE ("vfo count:" << vfo_count_);
buttons_ = send_command ("get buttons").trimmed ().split (',', SkipEmptyParts).replaceInStrings (" ", "~");
TRACE_CAT ("HRDTransceiver", "HRD Buttons: " << buttons_);
CAT_TRACE ("HRD Buttons: " << buttons_.join (", "));
HRD_info << "Buttons: {" << buttons_.join (", ") << "}\n";
dropdown_names_ = send_command ("get dropdowns").trimmed ().split (',', SkipEmptyParts);
TRACE_CAT ("HRDTransceiver", "Dropdowns:");
CAT_TRACE ("Dropdowns:");
HRD_info << "Dropdowns:\n";
Q_FOREACH (auto const& dd, dropdown_names_)
{
auto selections = send_command ("get dropdown-list {" + dd + "}").trimmed ().split (',');
TRACE_CAT ("HRDTransceiver", "\t" << dd << ": {" << selections.join (", ") << "}");
CAT_TRACE ("\t" << dd << ": {" << selections.join (", ") << "}");
HRD_info << "\t" << dd << ": {" << selections.join (", ") << "}\n";
dropdowns_[dd] = selections;
}
slider_names_ = send_command ("get sliders").trimmed ().split (',', SkipEmptyParts).replaceInStrings (" ", "~");
TRACE_CAT ("HRDTransceiver", "Sliders:-");
CAT_TRACE ("Sliders:-");
HRD_info << "Sliders:\n";
Q_FOREACH (auto const& s, slider_names_)
{
auto range = send_command ("get slider-range " + current_radio_name + " " + s).trimmed ().split (',', SkipEmptyParts);
TRACE_CAT ("HRDTransceiver", "\t" << s << ": {" << range.join (", ") << "}");
CAT_TRACE ("\t" << s << ": {" << range.join (", ") << "}");
HRD_info << "\t" << s << ": {" << range.join (", ") << "}\n";
sliders_[s] = range;
}
@ -358,7 +359,7 @@ void HRDTransceiver::do_stop ()
}
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("HRDTransceiver", "stopped" << state () << "reversed" << reversed_);
CAT_TRACE ("stopped" << state () << "reversed" << reversed_);
}
int HRDTransceiver::find_button (QRegExp const& re) const
@ -405,14 +406,12 @@ void HRDTransceiver::map_modes (int dropdown, ModeMap *map)
map->push_back (std::forward_as_tuple (FM, find_dropdown_selection (dropdown, QRegExp ("^(FM|FM\\(N\\)|FM-N|WFM)$"))));
map->push_back (std::forward_as_tuple (DIG_FM, find_dropdown_selection (dropdown, QRegExp ("^(PKT-FM|PKT|DATA\\(FM\\)|FM)$"))));
#if WSJT_TRACE_CAT
TRACE_CAT ("HRDTransceiver", "for dropdown" << dropdown_names_[dropdown]);
CAT_TRACE ("for dropdown" << dropdown_names_[dropdown]);
std::for_each (map->begin (), map->end (), [this, dropdown] (ModeMap::value_type const& item)
{
auto const& rhs = std::get<1> (item);
TRACE_CAT ("HRDTransceiver", '\t' << std::get<0> (item) << "<->" << (rhs.size () ? dropdowns_[dropdown_names_[dropdown]][rhs.front ()] : "None"));
CAT_TRACE ('\t' << std::get<0> (item) << "<->" << (rhs.size () ? dropdowns_[dropdown_names_[dropdown]][rhs.front ()] : "None"));
});
#endif
}
int HRDTransceiver::lookup_mode (MODE mode, ModeMap const& map) const
@ -444,7 +443,7 @@ auto HRDTransceiver::lookup_mode (int mode, ModeMap const& map) const -> MODE
return std::get<0> (*it);
}
int HRDTransceiver::get_dropdown (int dd, bool no_debug)
int HRDTransceiver::get_dropdown (int dd)
{
if (dd < 0)
{
@ -452,7 +451,7 @@ int HRDTransceiver::get_dropdown (int dd, bool no_debug)
}
auto dd_name = dropdown_names_.value (dd);
auto reply = send_command ("get dropdown-text {" + dd_name + "}", no_debug);
auto reply = send_command ("get dropdown-text {" + dd_name + "}");
auto colon_index = reply.indexOf (':');
if (colon_index < 0)
@ -473,14 +472,14 @@ void HRDTransceiver::set_dropdown (int dd, int value)
}
else
{
TRACE_CAT ("HRDTransceiver", "item" << value << "not found in" << dd_name);
CAT_ERROR ("item" << value << "not found in" << dd_name);
throw error {tr ("Ham Radio Deluxe: item not found in %1 dropdown list").arg (dd_name)};
}
}
void HRDTransceiver::do_ptt (bool on)
{
TRACE_CAT ("HRDTransceiver", on);
CAT_TRACE (on);
if (use_for_ptt_)
{
if (alt_ptt_button_ >= 0 && TransceiverFactory::TX_audio_source_rear == audio_source_)
@ -517,7 +516,7 @@ void HRDTransceiver::set_button (int button_index, bool checked)
}
else
{
TRACE_CAT ("HRDTransceiver", "invalid button");
CAT_ERROR ("invalid button");
throw error {tr ("Ham Radio Deluxe: button not available")};
}
}
@ -570,12 +569,12 @@ void HRDTransceiver::set_data_mode (MODE m)
}
}
auto HRDTransceiver::get_data_mode (MODE m, bool quiet) -> MODE
auto HRDTransceiver::get_data_mode (MODE m) -> MODE
{
if (data_mode_dropdown_ >= 0
&& data_mode_dropdown_selection_off_.size ())
{
auto selection = get_dropdown (data_mode_dropdown_, quiet);
auto selection = get_dropdown (data_mode_dropdown_);
// can't check for on here as there may be multiple on values so
// we must rely on the initial parse finding valid on values
if (selection >= 0 && selection != data_mode_dropdown_selection_off_.front ())
@ -594,7 +593,7 @@ auto HRDTransceiver::get_data_mode (MODE m, bool quiet) -> MODE
void HRDTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("HRDTransceiver", f << "reversed" << reversed_);
CAT_TRACE (f << "reversed" << reversed_);
if (UNK != m)
{
do_mode (m);
@ -614,7 +613,7 @@ void HRDTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
void HRDTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool /*no_ignore*/)
{
TRACE_CAT ("HRDTransceiver", tx << "reversed" << reversed_);
CAT_TRACE (tx << "reversed" << reversed_);
// re-check if reversed VFOs
bool rx_A {true};
@ -781,7 +780,7 @@ void HRDTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool /*no_ignore*
void HRDTransceiver::do_mode (MODE mode)
{
TRACE_CAT ("HRDTransceiver", mode);
CAT_TRACE (mode);
if (reversed_ && mode_B_dropdown_ >= 0)
{
set_dropdown (mode_B_dropdown_, lookup_mode (mode, mode_B_map_));
@ -873,17 +872,17 @@ void HRDTransceiver::do_mode (MODE mode)
update_mode (mode);
}
bool HRDTransceiver::is_button_checked (int button_index, bool no_debug)
bool HRDTransceiver::is_button_checked (int button_index)
{
if (button_index < 0)
{
return false;
}
auto reply = send_command ("get button-select " + buttons_.value (button_index), no_debug);
auto reply = send_command ("get button-select " + buttons_.value (button_index));
if ("1" != reply && "0" != reply)
{
TRACE_CAT ("HRDTransceiver", "bad response");
CAT_ERROR ("bad response");
throw error {tr ("Ham Radio Deluxe didn't respond as expected")};
}
return "1" == reply;
@ -891,10 +890,8 @@ bool HRDTransceiver::is_button_checked (int button_index, bool no_debug)
void HRDTransceiver::do_poll ()
{
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
bool quiet {false};
qDebug () << "+++++++ poll dump +++++++";
qDebug () << "reversed:" << reversed_;
CAT_TRACE ("+++++++ poll dump +++++++");
CAT_TRACE ("reversed:" << reversed_);
is_button_checked (vfo_A_button_);
is_button_checked (vfo_B_button_);
is_button_checked (vfo_toggle_button_);
@ -922,10 +919,7 @@ void HRDTransceiver::do_poll ()
{
get_dropdown (split_mode_dropdown_);
}
qDebug () << "------- poll dump -------";
#else
bool quiet {true};
#endif
CAT_TRACE ("------- poll dump -------");
if (split_off_button_ >= 0)
{
@ -933,13 +927,13 @@ void HRDTransceiver::do_poll ()
}
else if (split_mode_button_ >= 0 && !(tx_A_button_ >= 0 && tx_B_button_ >= 0))
{
update_split (is_button_checked (split_mode_button_, quiet));
update_split (is_button_checked (split_mode_button_));
}
else if (split_mode_dropdown_ >= 0)
{
if (!split_mode_dropdown_write_only_)
{
auto selection = get_dropdown (split_mode_dropdown_, quiet);
auto selection = get_dropdown (split_mode_dropdown_);
if (selection >= 0
&& split_mode_dropdown_selection_off_.size ())
{
@ -958,7 +952,7 @@ void HRDTransceiver::do_poll ()
bool rx_A {true}; // no Rx taken as not reversed
bool rx_B {false};
auto tx_A = is_button_checked (tx_A_button_, quiet);
auto tx_A = is_button_checked (tx_A_button_);
// some rigs have dual Rx, we take VFO A/MAIN receiving as
// normal and only say reversed when only VFO B/SUB is active
@ -974,10 +968,10 @@ void HRDTransceiver::do_poll ()
}
else if (vfo_B_button_ >= 0 || rx_B_button_ >= 0)
{
rx_A = is_button_checked (rx_A_button_ >= 0 ? rx_A_button_ : vfo_A_button_, quiet);
rx_A = is_button_checked (rx_A_button_ >= 0 ? rx_A_button_ : vfo_A_button_);
if (!rx_A)
{
rx_B = is_button_checked (rx_B_button_ >= 0 ? rx_B_button_ : vfo_B_button_, quiet);
rx_B = is_button_checked (rx_B_button_ >= 0 ? rx_B_button_ : vfo_B_button_);
}
}
@ -987,7 +981,7 @@ void HRDTransceiver::do_poll ()
if (vfo_count_ > 1)
{
auto frequencies = send_command ("get frequencies", quiet).trimmed ().split ('-', SkipEmptyParts);
auto frequencies = send_command ("get frequencies").trimmed ().split ('-', SkipEmptyParts);
update_rx_frequency (frequencies[reversed_ ? 1 : 0].toUInt ());
update_other_frequency (frequencies[reversed_ ? 0 : 1].toUInt ());
}
@ -997,7 +991,7 @@ void HRDTransceiver::do_poll ()
// while transmitting
if (!state ().ptt ())
{
update_rx_frequency (send_command ("get frequency", quiet).toUInt ());
update_rx_frequency (send_command ("get frequency").toUInt ());
}
}
@ -1005,11 +999,11 @@ void HRDTransceiver::do_poll ()
// transmitting
if (vfo_count_ > 1 || !state ().ptt ())
{
update_mode (get_data_mode (lookup_mode (get_dropdown (mode_A_dropdown_, quiet), mode_A_map_), quiet));
update_mode (get_data_mode (lookup_mode (get_dropdown (mode_A_dropdown_), mode_A_map_)));
}
}
QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool prepend_context, bool recurse)
QString HRDTransceiver::send_command (QString const& cmd, bool prepend_context, bool recurse)
{
Q_ASSERT (hrd_);
@ -1024,7 +1018,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
if (!recurse && prepend_context)
{
auto radio_name = send_command ("get radio", true, current_radio_, true);
auto radio_name = send_command ("get radio", current_radio_, true);
qDebug () << "HRDTransceiver::send_command: radio_name:" << radio_name;
auto radio_iter = std::find_if (radios_.begin (), radios_.end (), [&radio_name] (RadioMap::value_type const& radio)
{
@ -1032,7 +1026,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
});
if (radio_iter == radios_.end ())
{
TRACE_CAT ("HRDTransceiver", "rig disappeared or changed");
CAT_TRACE ("rig disappeared or changed");
throw error {tr ("Ham Radio Deluxe: rig has disappeared or changed")};
}
@ -1046,7 +1040,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
if (QTcpSocket::ConnectedState != hrd_->state ())
{
TRACE_CAT ("HRDTransceiver", cmd << "failed" << hrd_->errorString ());
CAT_ERROR (cmd << "failed" << hrd_->errorString ());
throw error {
tr ("Ham Radio Deluxe send command \"%1\" failed %2\n")
.arg (cmd)
@ -1059,7 +1053,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
auto message = ((prepend_context ? context + cmd : cmd) + "\r").toLocal8Bit ();
if (!write_to_port (message.constData (), message.size ()))
{
TRACE_CAT ("HRDTransceiver", "failed to write command" << cmd << "to HRD");
CAT_ERROR ("failed to write command" << cmd << "to HRD");
throw error {
tr ("Ham Radio Deluxe: failed to write command \"%1\"")
.arg (cmd)
@ -1072,7 +1066,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
QScopedPointer<HRDMessage> message {new (string) HRDMessage};
if (!write_to_port (reinterpret_cast<char const *> (message.data ()), message->size_))
{
TRACE_CAT ("HRDTransceiver", "failed to write command" << cmd << "to HRD");
CAT_ERROR ("failed to write command" << cmd << "to HRD");
throw error {
tr ("Ham Radio Deluxe: failed to write command \"%1\"")
.arg (cmd)
@ -1089,7 +1083,7 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
HRDMessage const * reply {new (buffer) HRDMessage};
if (reply->magic_1_value_ != reply->magic_1_ && reply->magic_2_value_ != reply->magic_2_)
{
TRACE_CAT ("HRDTransceiver", cmd << "invalid reply");
CAT_ERROR (cmd << "invalid reply");
throw error {
tr ("Ham Radio Deluxe sent an invalid reply to our command \"%1\"")
.arg (cmd)
@ -1099,20 +1093,14 @@ QString HRDTransceiver::send_command (QString const& cmd, bool no_debug, bool pr
// keep reading until expected size arrives
while (buffer.size () - offsetof (HRDMessage, size_) < reply->size_)
{
if (!no_debug)
{
TRACE_CAT ("HRDTransceiver", cmd << "reading more reply data");
}
CAT_TRACE (cmd << "reading more reply data");
buffer += read_reply (cmd);
reply = new (buffer) HRDMessage;
}
result = QString {reply->payload_}; // this is not a memory leak (honest!)
}
if (!no_debug)
{
TRACE_CAT ("HRDTransceiver", cmd << " ->" << result);
}
CAT_TRACE (cmd << " ->" << result);
return result;
}
@ -1143,7 +1131,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd)
replied = hrd_->waitForReadyRead ();
if (!replied && hrd_->error () != hrd_->SocketTimeoutError)
{
TRACE_CAT ("HRDTransceiver", cmd << "failed to reply" << hrd_->errorString ());
CAT_ERROR (cmd << "failed to reply" << hrd_->errorString ());
throw error {
tr ("Ham Radio Deluxe failed to reply to command \"%1\" %2\n")
.arg (cmd)
@ -1153,7 +1141,7 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd)
}
if (!replied)
{
TRACE_CAT ("HRDTransceiver", cmd << "retries exhausted");
CAT_ERROR (cmd << "retries exhausted");
throw error {
tr ("Ham Radio Deluxe retries exhausted sending command \"%1\"")
.arg (cmd)
@ -1162,11 +1150,11 @@ QByteArray HRDTransceiver::read_reply (QString const& cmd)
return hrd_->readAll ();
}
void HRDTransceiver::send_simple_command (QString const& command, bool no_debug)
void HRDTransceiver::send_simple_command (QString const& command)
{
if ("OK" != send_command (command, no_debug))
if ("OK" != send_command (command))
{
TRACE_CAT ("HRDTransceiver", command << "unexpected response");
CAT_ERROR (command << "unexpected response");
throw error {
tr ("Ham Radio Deluxe didn't respond to command \"%1\" as expected")
.arg (command)

View File

@ -30,10 +30,11 @@ class HRDTransceiver final
Q_OBJECT
public:
static void register_transceivers (TransceiverFactory::Transceivers *, int id);
static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id);
// takes ownership of wrapped Transceiver
explicit HRDTransceiver (std::unique_ptr<TransceiverBase> wrapped
explicit HRDTransceiver (logger_type *
, std::unique_ptr<TransceiverBase> wrapped
, QString const& server
, bool use_for_ptt
, TransceiverFactory::TXAudioSource
@ -53,17 +54,17 @@ protected:
void do_poll () override;
private:
QString send_command (QString const&, bool no_debug = false, bool prepend_context = true, bool recurse = false);
QString send_command (QString const&, bool prepend_context = true, bool recurse = false);
QByteArray read_reply (QString const& command);
void send_simple_command (QString const&, bool no_debug = false);
void send_simple_command (QString const&);
bool write_to_port (char const *, qint64 length);
int find_button (QRegExp const&) const;
int find_dropdown (QRegExp const&) const;
std::vector<int> find_dropdown_selection (int dropdown, QRegExp const&) const;
int get_dropdown (int, bool no_debug = false);
int get_dropdown (int);
void set_dropdown (int, int);
void set_button (int button_index, bool checked = true);
bool is_button_checked (int button_index, bool no_debug = false);
bool is_button_checked (int button_index);
// This dictionary type maps Transceiver::MODE to a list of mode
// drop down selection indexes that equate to that mode. It is used
@ -75,7 +76,7 @@ private:
int lookup_mode (MODE, ModeMap const&) const;
MODE lookup_mode (int, ModeMap const&) const;
void set_data_mode (MODE);
MODE get_data_mode (MODE, bool no_debug = false);
MODE get_data_mode (MODE);
// An alternate TransceiverBase instance that can be used to drive
// PTT if required.

View File

@ -34,35 +34,6 @@ namespace
// As an ultimate workaround make sure the user always has the
// option to skip mode setting altogether.
// reroute Hamlib diagnostic messages to Qt
int debug_callback (enum rig_debug_level_e level, rig_ptr_t /* arg */, char const * format, va_list ap)
{
QString message;
static char constexpr fmt[] = "Hamlib: %s";
message = message.vasprintf (format, ap).trimmed ();
switch (level)
{
case RIG_DEBUG_BUG:
qFatal (fmt, message.toLocal8Bit ().data ());
break;
case RIG_DEBUG_ERR:
qCritical (fmt, message.toLocal8Bit ().data ());
break;
case RIG_DEBUG_WARN:
qWarning (fmt, message.toLocal8Bit ().data ());
break;
default:
qDebug (fmt, message.toLocal8Bit ().data ());
break;
}
return 0;
}
// callback function that receives transceiver capabilities from the
// hamlib libraries
int register_callback (rig_caps const * caps, void * callback_data)
@ -161,22 +132,35 @@ namespace
freq_t HamlibTransceiver::dummy_frequency_;
rmode_t HamlibTransceiver::dummy_mode_ {RIG_MODE_NONE};
void HamlibTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry)
// reroute Hamlib diagnostic messages to Qt
int HamlibTransceiver::debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap)
{
rig_set_debug_callback (debug_callback, nullptr);
auto logger = reinterpret_cast<logger_type *> (arg);
auto message = QString::vasprintf (format, ap);
va_end (ap);
auto severity = boost::log::trivial::trace;
switch (level)
{
case RIG_DEBUG_BUG: severity = boost::log::trivial::fatal; break;
case RIG_DEBUG_ERR: severity = boost::log::trivial::error; break;
case RIG_DEBUG_WARN: severity = boost::log::trivial::warning; break;
case RIG_DEBUG_VERBOSE: severity = boost::log::trivial::debug; break;
case RIG_DEBUG_TRACE: severity = boost::log::trivial::trace; break;
default: break;
};
if (level != RIG_DEBUG_NONE) // no idea what level NONE means so
// ignore it
{
BOOST_LOG_SEV (*logger, severity) << message.trimmed ().toStdString ();
}
return 0;
}
#if WSJT_HAMLIB_TRACE
#if WSJT_HAMLIB_VERBOSE_TRACE
void HamlibTransceiver::register_transceivers (logger_type * logger,
TransceiverFactory::Transceivers * registry)
{
rig_set_debug_callback (debug_callback, logger);
rig_set_debug (RIG_DEBUG_TRACE);
#else
rig_set_debug (RIG_DEBUG_VERBOSE);
#endif
#elif defined (NDEBUG)
rig_set_debug (RIG_DEBUG_ERR);
#else
rig_set_debug (RIG_DEBUG_WARN);
#endif
rig_load_all_backends ();
rig_list_foreach (register_callback, registry);
}
@ -195,9 +179,10 @@ void HamlibTransceiver::RIGDeleter::cleanup (RIG * rig)
}
}
HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
HamlibTransceiver::HamlibTransceiver (logger_type * logger,
TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
QObject * parent)
: PollingTransceiver {0, parent}
: PollingTransceiver {logger, 0, parent}
, rig_ {rig_init (RIG_MODEL_DUMMY)}
, ptt_only_ {true}
, back_ptt_port_ {false}
@ -249,9 +234,11 @@ HamlibTransceiver::HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QS
}
}
HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const& params,
HamlibTransceiver::HamlibTransceiver (logger_type * logger,
int model_number,
TransceiverFactory::ParameterPack const& params,
QObject * parent)
: PollingTransceiver {params.poll_interval, parent}
: PollingTransceiver {logger, params.poll_interval, parent}
, rig_ {rig_init (model_number)}
, ptt_only_ {false}
, back_ptt_port_ {TransceiverFactory::TX_audio_source_rear == params.audio_source}
@ -272,53 +259,53 @@ HamlibTransceiver::HamlibTransceiver (int model_number, TransceiverFactory::Para
// rig_->state.obj = this;
if (!is_dummy_)
//
// user defined Hamlib settings
//
auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation
, "hamlib_settings.json");
if (!settings_file_name.isEmpty ())
{
//
// user defined Hamlib settings
//
auto settings_file_name = QStandardPaths::locate (QStandardPaths::AppConfigLocation
, "hamlib_settings.json");
if (!settings_file_name.isEmpty ())
QFile settings_file {settings_file_name};
qDebug () << "Using Hamlib settings file:" << settings_file_name;
if (settings_file.open (QFile::ReadOnly))
{
QFile settings_file {settings_file_name};
qDebug () << "Using Hamlib settings file:" << settings_file_name;
if (settings_file.open (QFile::ReadOnly))
QJsonParseError status;
auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status);
if (status.error)
{
QJsonParseError status;
auto settings_doc = QJsonDocument::fromJson (settings_file.readAll (), &status);
if (status.error)
{
throw error {tr ("Hamlib settings file error: %1 at character offset %2")
.arg (status.errorString ()).arg (status.offset)};
}
qDebug () << "Hamlib settings JSON:" << settings_doc.toJson ();
if (!settings_doc.isObject ())
{
throw error {tr ("Hamlib settings file error: top level must be a JSON object")};
}
auto const& settings = settings_doc.object ();
throw error {tr ("Hamlib settings file error: %1 at character offset %2")
.arg (status.errorString ()).arg (status.offset)};
}
qDebug () << "Hamlib settings JSON:" << settings_doc.toJson ();
if (!settings_doc.isObject ())
{
throw error {tr ("Hamlib settings file error: top level must be a JSON object")};
}
auto const& settings = settings_doc.object ();
//
// configuration settings
//
auto const& config = settings["config"];
if (!config.isUndefined ())
//
// configuration settings
//
auto const& config = settings["config"];
if (!config.isUndefined ())
{
if (!config.isObject ())
{
if (!config.isObject ())
{
throw error {tr ("Hamlib settings file error: config must be a JSON object")};
}
auto const& config_list = config.toObject ();
for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item)
{
set_conf (item.key ().toLocal8Bit ().constData ()
, (*item).toVariant ().toString ().toLocal8Bit ().constData ());
}
throw error {tr ("Hamlib settings file error: config must be a JSON object")};
}
auto const& config_list = config.toObject ();
for (auto item = config_list.constBegin (); item != config_list.constEnd (); ++item)
{
set_conf (item.key ().toLocal8Bit ().constData ()
, (*item).toVariant ().toString ().toLocal8Bit ().constData ());
}
}
}
}
if (!is_dummy_)
{
switch (rig_->caps->port_type)
{
case RIG_PORT_SERIAL:
@ -424,16 +411,15 @@ void HamlibTransceiver::error_check (int ret_code, QString const& doing) const
{
if (RIG_OK != ret_code)
{
TRACE_CAT_POLL ("HamlibTransceiver", "error:" << rigerror (ret_code));
CAT_ERROR ("error: " << rigerror (ret_code));
throw error {tr ("Hamlib error: %1 while %2").arg (rigerror (ret_code)).arg (doing)};
}
}
int HamlibTransceiver::do_start ()
{
TRACE_CAT ("HamlibTransceiver",
QString::fromLatin1 (rig_->caps->mfg_name).trimmed ()
<< QString::fromLatin1 (rig_->caps->model_name).trimmed ());
CAT_TRACE ("starting: " << rig_->caps->mfg_name
<< ": " << rig_->caps->model_name);
error_check (rig_open (rig_.data ()), tr ("opening connection to rig"));
@ -484,25 +470,25 @@ int HamlibTransceiver::do_start ()
// here. We also gather/set other initial state.
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1), tr ("getting current frequency"));
f1 = std::round (f1);
TRACE_CAT ("HamlibTransceiver", "current frequency =" << f1);
CAT_TRACE ("current frequency=" << f1);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting current mode"));
TRACE_CAT ("HamlibTransceiver", "current mode =" << rig_strrmode (m) << "bw =" << w);
CAT_TRACE ("current mode=" << rig_strrmode (m) << " bw=" << w);
if (!rig_->caps->set_vfo)
{
TRACE_CAT ("HamlibTransceiver", "rig_vfo_op TOGGLE");
CAT_TRACE ("rig_vfo_op TOGGLE");
rc = rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE);
}
else
{
TRACE_CAT ("HamlibTransceiver", "rig_set_vfo to other VFO");
CAT_TRACE ("rig_set_vfo to other VFO");
rc = rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB);
if (-RIG_ENAVAIL == rc || -RIG_ENIMPL == rc)
{
// if we are talking to netrigctl then toggle VFO op
// may still work
TRACE_CAT ("HamlibTransceiver", "rig_vfo_op TOGGLE");
CAT_TRACE ("rig_vfo_op TOGGLE");
rc = rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE);
}
}
@ -525,21 +511,21 @@ int HamlibTransceiver::do_start ()
// need to execute this block
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f2), tr ("getting other VFO frequency"));
f2 = std::round (f2);
TRACE_CAT ("HamlibTransceiver", "rig_get_freq other frequency =" << f2);
CAT_TRACE ("rig_get_freq other frequency=" << f2);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &mb, &wb), tr ("getting other VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode other mode =" << rig_strrmode (mb) << "bw =" << wb);
CAT_TRACE ("rig_get_mode other mode=" << rig_strrmode (mb) << " bw=" << wb);
update_other_frequency (f2);
if (!rig_->caps->set_vfo)
{
TRACE_CAT ("HamlibTransceiver", "rig_vfo_op TOGGLE");
CAT_TRACE ("rig_vfo_op TOGGLE");
error_check (rig_vfo_op (rig_.data (), RIG_VFO_CURR, RIG_OP_TOGGLE), tr ("exchanging VFOs"));
}
else
{
TRACE_CAT ("HamlibTransceiver", "rig_set_vfo A/MAIN");
CAT_TRACE ("rig_set_vfo A/MAIN");
error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN), tr ("setting current VFO"));
}
@ -551,16 +537,16 @@ int HamlibTransceiver::do_start ()
{
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f1), tr ("getting frequency"));
f1 = std::round (f1);
TRACE_CAT ("HamlibTransceiver", "rig_get_freq frequency =" << f1);
CAT_TRACE ("rig_get_freq frequency=" << f1);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w), tr ("getting mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w);
update_rx_frequency (f1);
}
}
// TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split off");
// TRACE_CAT ("rig_set_split_vfo split off");
// error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, RIG_SPLIT_OFF, RIG_VFO_CURR), tr ("setting split off"));
// update_split (false);
}
@ -571,7 +557,7 @@ int HamlibTransceiver::do_start ()
if (get_vfo_works_ && rig_->caps->get_vfo)
{
error_check (rig_get_vfo (rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib
TRACE_CAT ("HamlibTransceiver", "rig_get_vfo current VFO = " << rig_strvfo (v));
CAT_TRACE ("rig_get_vfo current VFO=" << rig_strvfo (v));
}
reversed_ = RIG_VFO_B == v;
@ -580,7 +566,7 @@ int HamlibTransceiver::do_start ()
{
if (RIG_OK == rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w))
{
TRACE_CAT ("HamlibTransceiver", "rig_get_mode current mode =" << rig_strrmode (m) << "bw =" << w);
CAT_TRACE ("rig_get_mode current mode=" << rig_strrmode (m) << " bw=" << w);
}
else
{
@ -588,7 +574,7 @@ int HamlibTransceiver::do_start ()
// Some rigs (HDSDR) don't have a working way of
// reporting MODE so we give up on mode queries -
// sets will still cause an error
TRACE_CAT ("HamlibTransceiver", "rig_get_mode can't do on this rig");
CAT_TRACE ("rig_get_mode can't do on this rig");
}
}
}
@ -664,7 +650,7 @@ int HamlibTransceiver::do_start ()
do_poll ();
TRACE_CAT ("HamlibTransceiver", "exit" << state () << "reversed =" << reversed_ << "resolution = " << resolution);
CAT_TRACE ("finished start " << state () << " reversed=" << reversed_ << " resolution=" << resolution);
return resolution;
}
@ -685,7 +671,7 @@ void HamlibTransceiver::do_stop ()
rig_close (rig_.data ());
}
TRACE_CAT ("HamlibTransceiver", "state:" << state () << "reversed =" << reversed_);
CAT_TRACE ("state: " << state () << " reversed=" << reversed_);
}
std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
@ -694,7 +680,7 @@ std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
{
vfo_t v;
error_check (rig_get_vfo (rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib
TRACE_CAT ("HamlibTransceiver", "rig_get_vfo VFO = " << rig_strvfo (v));
CAT_TRACE ("rig_get_vfo VFO=" << rig_strvfo (v));
reversed_ = RIG_VFO_B == v;
}
@ -704,7 +690,7 @@ std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
// frequency if split since these type of radios can only
// support this way around
TRACE_CAT ("HamlibTransceiver", "rig_set_vfo VFO = A/MAIN");
CAT_TRACE ("rig_set_vfo VFO=A/MAIN");
error_check (rig_set_vfo (rig_.data (), rig_->state.vfo_list & RIG_VFO_A ? RIG_VFO_A : RIG_VFO_MAIN), tr ("setting current VFO"));
}
// else only toggle available but VFOs should be substitutable
@ -715,17 +701,17 @@ std::tuple<vfo_t, vfo_t> HamlibTransceiver::get_vfos (bool for_split) const
: rx_vfo;
if (reversed_)
{
TRACE_CAT ("HamlibTransceiver", "reversing VFOs");
CAT_TRACE ("reversing VFOs");
std::swap (rx_vfo, tx_vfo);
}
TRACE_CAT ("HamlibTransceiver", "RX VFO = " << rig_strvfo (rx_vfo) << " TX VFO = " << rig_strvfo (tx_vfo));
CAT_TRACE ("RX VFO=" << rig_strvfo (rx_vfo) << " TX VFO=" << rig_strvfo (tx_vfo));
return std::make_tuple (rx_vfo, tx_vfo);
}
void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore)
{
TRACE_CAT ("HamlibTransceiver", f << "mode:" << m << "reversed:" << reversed_);
CAT_TRACE ("f: " << f << " mode: " << m << " reversed: " << reversed_);
// only change when receiving or simplex or direct VFO addressing
// unavailable or forced
@ -742,11 +728,11 @@ void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore)
pbwidth_t current_width;
auto new_mode = map_mode (m);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting current VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width);
if (new_mode != current_mode)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode"));
// for the 2nd time because a mode change may have caused a
@ -755,7 +741,7 @@ void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore)
// for the second time because some rigs change mode according
// to frequency such as the TS-2000 auto mode setting
TRACE_CAT ("HamlibTransceiver", "rig_set_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode"));
}
update_mode (m);
@ -765,7 +751,7 @@ void HamlibTransceiver::do_frequency (Frequency f, MODE m, bool no_ignore)
void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore)
{
TRACE_CAT ("HamlibTransceiver", tx << "reversed:" << reversed_);
CAT_TRACE ("txf: " << tx << " reversed: " << reversed_);
if (WSJT_RIG_NONE_CAN_SPLIT || !is_dummy_) // split is meaningless if you can't see it
{
@ -789,7 +775,7 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
// much we can do since the Hamlib Library needs this
// call at least once to establish the Tx VFO. Best we
// can do is only do this once per session.
TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
CAT_TRACE ("rig_set_split_vfo split=" << split);
auto rc = rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo);
if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc))
{
@ -809,7 +795,7 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
// addressing
if (state ().ptt () && one_VFO_)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
CAT_TRACE ("rig_set_split_vfo split=" << split);
error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo), tr ("setting split mode"));
error_check (rig_set_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting frequency"));
@ -820,11 +806,11 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
pbwidth_t current_width;
auto new_mode = map_mode (mode);
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting current VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width);
if (new_mode != current_mode)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode"));
}
}
@ -836,20 +822,20 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
if (UNK != mode)
{
auto new_mode = map_mode (mode);
TRACE_CAT ("HamlibTransceiver", "rig_set_split_freq_mode freq = " << tx
CAT_TRACE ("rig_set_split_freq_mode freq=" << tx
<< " mode = " << rig_strrmode (new_mode));
error_check (rig_set_split_freq_mode (rig_.data (), RIG_VFO_CURR, tx, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX frequency and mode"));
}
else
{
TRACE_CAT ("HamlibTransceiver", "rig_set_split_freq freq = " << tx);
CAT_TRACE ("rig_set_split_freq freq=" << tx);
error_check (rig_set_split_freq (rig_.data (), RIG_VFO_CURR, tx), tr ("setting split TX frequency"));
}
// Enable split last since some rigs (Kenwood for one) come out
// of split when you switch RX VFO (to set split mode above for
// example). Also the Elecraft K3 will refuse to go to split
// with certain VFO A/B mode combinations.
TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
CAT_TRACE ("rig_set_split_vfo split=" << split);
error_check (rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo), tr ("setting split mode"));
update_other_frequency (tx);
update_split (tx);
@ -858,7 +844,7 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
else
{
// Disable split
TRACE_CAT ("HamlibTransceiver", "rig_set_split_vfo split =" << split);
CAT_TRACE ("rig_set_split_vfo split=" << split);
auto rc = rig_set_split_vfo (rig_.data (), RIG_VFO_CURR, split, tx_vfo);
if (tx || (-RIG_ENAVAIL != rc && -RIG_ENIMPL != rc))
{
@ -878,7 +864,7 @@ void HamlibTransceiver::do_tx_frequency (Frequency tx, MODE mode, bool no_ignore
void HamlibTransceiver::do_mode (MODE mode)
{
TRACE_CAT ("HamlibTransceiver", mode);
CAT_TRACE (mode);
auto vfos = get_vfos (state ().split ());
// auto rx_vfo = std::get<0> (vfos);
@ -892,11 +878,11 @@ void HamlibTransceiver::do_mode (MODE mode)
if (!(state ().ptt () && state ().split () && one_VFO_))
{
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting current VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width);
if (new_mode != current_mode)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode"));
}
}
@ -905,22 +891,22 @@ void HamlibTransceiver::do_mode (MODE mode)
if (state ().ptt () && state ().split () && one_VFO_)
{
error_check (rig_get_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting current VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width);
if (new_mode != current_mode)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_mode mode=" << rig_strrmode (new_mode));
error_check (rig_set_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting current VFO mode"));
}
}
else if (state ().split () && !one_VFO_)
{
error_check (rig_get_split_mode (rig_.data (), RIG_VFO_CURR, &current_mode, &current_width), tr ("getting split TX VFO mode"));
TRACE_CAT ("HamlibTransceiver", "rig_get_split_mode mode = " << rig_strrmode (current_mode) << "bw =" << current_width);
CAT_TRACE ("rig_get_split_mode mode=" << rig_strrmode (current_mode) << " bw=" << current_width);
if (new_mode != current_mode)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_split_mode mode = " << rig_strrmode (new_mode));
CAT_TRACE ("rig_set_split_mode mode=" << rig_strrmode (new_mode));
hamlib_tx_vfo_fixup fixup (rig_.data (), tx_vfo);
error_check (rig_set_split_mode (rig_.data (), RIG_VFO_CURR, new_mode, RIG_PASSBAND_NOCHANGE), tr ("setting split TX VFO mode"));
}
@ -930,14 +916,6 @@ void HamlibTransceiver::do_mode (MODE mode)
void HamlibTransceiver::do_poll ()
{
#if !WSJT_TRACE_CAT_POLLS
#if defined (NDEBUG)
rig_set_debug (RIG_DEBUG_ERR);
#else
rig_set_debug (RIG_DEBUG_WARN);
#endif
#endif
freq_t f;
rmode_t m;
pbwidth_t w;
@ -947,7 +925,7 @@ void HamlibTransceiver::do_poll ()
{
vfo_t v;
error_check (rig_get_vfo (rig_.data (), &v), tr ("getting current VFO")); // has side effect of establishing current VFO inside hamlib
TRACE_CAT_POLL ("HamlibTransceiver", "VFO =" << rig_strvfo (v));
CAT_TRACE ("VFO=" << rig_strvfo (v));
reversed_ = RIG_VFO_B == v;
}
@ -958,7 +936,7 @@ void HamlibTransceiver::do_poll ()
auto rc = rig_get_split_vfo (rig_.data (), RIG_VFO_CURR, &s, &v);
if (-RIG_OK == rc && RIG_SPLIT_ON == s)
{
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_split_vfo split = " << s << " VFO = " << rig_strvfo (v));
CAT_TRACE ("rig_get_split_vfo split=" << s << " VFO=" << rig_strvfo (v));
update_split (true);
// if (RIG_VFO_A == v)
// {
@ -967,14 +945,14 @@ void HamlibTransceiver::do_poll ()
}
else if (-RIG_OK == rc) // not split
{
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_split_vfo split = " << s << " VFO = " << rig_strvfo (v));
CAT_TRACE ("rig_get_split_vfo split=" << s << " VFO=" << rig_strvfo (v));
update_split (false);
}
else
{
// Some rigs (Icom) don't have a way of reporting SPLIT
// mode
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_split_vfo can't do on this rig");
CAT_TRACE ("rig_get_split_vfo can't do on this rig");
// just report how we see it based on prior commands
split_query_works_ = false;
}
@ -987,7 +965,7 @@ void HamlibTransceiver::do_poll ()
{
error_check (rig_get_freq (rig_.data (), RIG_VFO_CURR, &f), tr ("getting current VFO frequency"));
f = std::round (f);
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_freq frequency =" << f);
CAT_TRACE ("rig_get_freq frequency=" << f);
update_rx_frequency (f);
}
@ -1008,7 +986,7 @@ void HamlibTransceiver::do_poll ()
: (rig_->state.vfo_list & RIG_VFO_B ? RIG_VFO_B : RIG_VFO_SUB)
, &f), tr ("getting other VFO frequency"));
f = std::round (f);
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_freq other VFO =" << f);
CAT_TRACE ("rig_get_freq other VFO=" << f);
update_other_frequency (f);
}
}
@ -1026,12 +1004,12 @@ void HamlibTransceiver::do_poll ()
auto rc = rig_get_mode (rig_.data (), RIG_VFO_CURR, &m, &w);
if (RIG_OK == rc)
{
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_mode mode =" << rig_strrmode (m) << "bw =" << w);
CAT_TRACE ("rig_get_mode mode=" << rig_strrmode (m) << " bw=" << w);
update_mode (map_mode (m));
}
else
{
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_mode mode failed with rc:" << rc << "ignoring");
CAT_TRACE ("rig_get_mode mode failed with rc: " << rc << " ignoring");
}
}
@ -1044,34 +1022,20 @@ void HamlibTransceiver::do_poll ()
// support command
{
error_check (rc, tr ("getting PTT state"));
TRACE_CAT_POLL ("HamlibTransceiver", "rig_get_ptt PTT =" << p);
CAT_TRACE ("rig_get_ptt PTT=" << p);
update_PTT (!(RIG_PTT_OFF == p));
}
}
#if !WSJT_TRACE_CAT_POLLS
#if WSJT_HAMLIB_TRACE
#if WSJT_HAMLIB_VERBOSE_TRACE
rig_set_debug (RIG_DEBUG_TRACE);
#else
rig_set_debug (RIG_DEBUG_VERBOSE);
#endif
#elif defined (NDEBUG)
rig_set_debug (RIG_DEBUG_ERR);
#else
rig_set_debug (RIG_DEBUG_WARN);
#endif
#endif
}
void HamlibTransceiver::do_ptt (bool on)
{
TRACE_CAT ("HamlibTransceiver", on << state () << "reversed =" << reversed_);
CAT_TRACE ("PTT: " << on << " " << state () << " reversed=" << reversed_);
if (on)
{
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_ptt PTT = true");
CAT_TRACE ("rig_set_ptt PTT=true");
error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR
, RIG_PTT_RIG_MICDATA == rig_->caps->ptt_type && back_ptt_port_
? RIG_PTT_ON_DATA : RIG_PTT_ON), tr ("setting PTT on"));
@ -1081,7 +1045,7 @@ void HamlibTransceiver::do_ptt (bool on)
{
if (RIG_PTT_NONE != rig_->state.pttport.type.ptt)
{
TRACE_CAT ("HamlibTransceiver", "rig_set_ptt PTT = false");
CAT_TRACE ("rig_set_ptt PTT=false");
error_check (rig_set_ptt (rig_.data (), RIG_VFO_CURR, RIG_PTT_OFF), tr ("setting PTT off"));
}
}

View File

@ -17,15 +17,15 @@ class HamlibTransceiver final
Q_OBJECT // for translation context
public:
static void register_transceivers (TransceiverFactory::Transceivers *);
static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *);
static void unregister_transceivers ();
explicit HamlibTransceiver (int model_number, TransceiverFactory::ParameterPack const&,
explicit HamlibTransceiver (logger_type *, int model_number, TransceiverFactory::ParameterPack const&,
QObject * parent = nullptr);
explicit HamlibTransceiver (TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
explicit HamlibTransceiver (logger_type *, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port,
QObject * parent = nullptr);
private:
private:
int do_start () override;
void do_stop () override;
void do_frequency (Frequency, MODE, bool no_ignore) override;
@ -65,6 +65,8 @@ public:
// establish the Tx VFO
bool get_vfo_works_; // Net rigctl promises what it can't deliver
bool set_vfo_works_; // More rigctl promises which it can't deliver
static int debug_callback (enum rig_debug_level_e level, rig_ptr_t arg, char const * format, va_list ap);
};
#endif

View File

@ -49,7 +49,7 @@ auto OmniRigTransceiver::map_mode (OmniRig::RigParamX param) -> MODE
{
return FM;
}
TRACE_CAT ("OmniRigTransceiver", "unrecognized mode");
CAT_ERROR ("unrecognized mode");
throw_qstring (tr ("OmniRig: unrecognized mode"));
return UNK;
}
@ -74,7 +74,9 @@ OmniRig::RigParamX OmniRigTransceiver::map_mode (MODE mode)
return OmniRig::PM_SSB_U; // quieten compiler grumble
}
void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers * registry, int id1, int id2)
void OmniRigTransceiver::register_transceivers (logger_type *,
TransceiverFactory::Transceivers * registry,
int id1, int id2)
{
(*registry)[OmniRig_transceiver_one_name] = TransceiverFactory::Capabilities {
id1
@ -94,10 +96,11 @@ void OmniRigTransceiver::register_transceivers (TransceiverFactory::Transceivers
};
}
OmniRigTransceiver::OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped,
OmniRigTransceiver::OmniRigTransceiver (logger_type * logger,
std::unique_ptr<TransceiverBase> wrapped,
RigNumber n, TransceiverFactory::PTTMethod ptt_type,
QString const& ptt_port, QObject * parent)
: TransceiverBase {parent}
: TransceiverBase {logger, parent}
, wrapped_ {std::move (wrapped)}
, use_for_ptt_ {TransceiverFactory::PTT_method_CAT == ptt_type || ("CAT" == ptt_port && (TransceiverFactory::PTT_method_RTS == ptt_type || TransceiverFactory::PTT_method_DTR == ptt_type))}
, ptt_type_ {ptt_type}
@ -126,14 +129,14 @@ bool OmniRigTransceiver::await_notification_with_timeout (int timeout)
int OmniRigTransceiver::do_start ()
{
TRACE_CAT ("OmniRigTransceiver", "starting");
CAT_TRACE ("starting");
if (wrapped_) wrapped_->start (0);
omni_rig_.reset (new OmniRig::OmniRigX {this});
if (omni_rig_->isNull ())
{
TRACE_CAT ("OmniRigTransceiver", "failed to start COM server");
CAT_ERROR ("failed to start COM server");
throw_qstring (tr ("Failed to start OmniRig COM server"));
}
@ -149,8 +152,8 @@ int OmniRigTransceiver::do_start ()
, SIGNAL (CustomReply (int, QVariant const&, QVariant const&))
, this, SLOT (handle_custom_reply (int, QVariant const&, QVariant const&)));
TRACE_CAT ("OmniRigTransceiver", "OmniRig s/w version:" << QString::number (omni_rig_->SoftwareVersion ()).toLocal8Bit ()
<< "i/f version:" << QString::number (omni_rig_->InterfaceVersion ()).toLocal8Bit ());
CAT_INFO ("OmniRig s/w version: " << omni_rig_->SoftwareVersion ()
<< "i/f version: " << omni_rig_->InterfaceVersion ());
// fetch the interface of the RigX CoClass and instantiate a proxy object
switch (rig_number_)
@ -182,12 +185,12 @@ int OmniRigTransceiver::do_start ()
// COM/OLE exceptions get signaled
connect (&*port_, SIGNAL (exception (int, QString, QString, QString)), this, SLOT (handle_COM_exception (int, QString, QString, QString)));
TRACE_CAT ("OmniRigTransceiver", "OmniRig RTS state:" << port_->Rts ());
CAT_TRACE ("OmniRig RTS state: " << port_->Rts ());
// remove locking because it doesn't seem to work properly
// if (!port_->Lock ()) // try to take exclusive use of the OmniRig serial port for PTT
// {
// TRACE_CAT ("OmniRigTransceiver", "Failed to get exclusive use of serial port for PTT from OmniRig");
// CAT_WARNING ("Failed to get exclusive use of serial port for PTT from OmniRig");
// }
// start off so we don't accidentally key the radio
@ -205,11 +208,11 @@ int OmniRigTransceiver::do_start ()
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"OmniRig initial rig type: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
CAT_INFO (QString {"OmniRig initial rig type: %1 readable params=0x%2 writable params=0x%3 for rig %4"}
.arg (rig_type_)
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number_).toLocal8Bit ());
.arg (rig_number_));
for (int i = 0; i < 5; ++i)
{
if (OmniRig::ST_ONLINE == rig_->Status ())
@ -261,7 +264,7 @@ int OmniRigTransceiver::do_start ()
}
if (!await_notification_with_timeout (1000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 1: wait timed out");
CAT_ERROR ("do_start 1: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
switch (rig_->GetRxFrequency () - test_frequency)
@ -289,7 +292,7 @@ int OmniRigTransceiver::do_start ()
}
if (!await_notification_with_timeout (2000))
{
TRACE_CAT ("OmniRigTransceiver", "do_start 2: wait timed out");
CAT_ERROR ("do_start 2: wait timed out");
throw_qstring (tr ("OmniRig: timeout waiting for update from rig"));
}
if (9 == rig_->GetRxFrequency () - test_frequency)
@ -341,45 +344,45 @@ void OmniRigTransceiver::do_stop ()
if (wrapped_) wrapped_->stop ();
TRACE_CAT ("OmniRigTransceiver", "stopped");
CAT_TRACE ("stopped");
}
void OmniRigTransceiver::handle_COM_exception (int code, QString source, QString desc, QString help)
{
TRACE_CAT ("OmniRigTransceiver", QString::number (code) + " at " + source + ": " + desc + " (" + help + ')');
CAT_ERROR ((QString::number (code) + " at " + source + ": " + desc + " (" + help + ')'));
throw_qstring (tr ("OmniRig COM/OLE error: %1 at %2: %3 (%4)").arg (QString::number (code)).arg (source). arg (desc). arg (help));
}
void OmniRigTransceiver::handle_visible_change ()
{
if (!omni_rig_ || omni_rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "visibility change: visibility =" << omni_rig_->DialogVisible ());
CAT_TRACE ("visibility change: visibility =" << omni_rig_->DialogVisible ());
}
void OmniRigTransceiver::handle_rig_type_change (int rig_number)
{
TRACE_CAT ("OmniRigTransceiver", "rig type change: rig =" << rig_number);
CAT_TRACE ("rig type change: rig =" << rig_number);
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
readable_params_ = rig_->ReadableParams ();
writable_params_ = rig_->WriteableParams ();
TRACE_CAT ("OmniRigTransceiver", QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
CAT_INFO (QString {"rig type change to: %1 readable params = 0x%2 writable params = 0x%3 for rig %4"}
.arg (rig_->RigType ())
.arg (readable_params_, 8, 16, QChar ('0'))
.arg (writable_params_, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ());
.arg (rig_number));
}
}
void OmniRigTransceiver::handle_status_change (int rig_number)
{
TRACE_CAT ("OmniRigTransceiver", QString {"status change for rig %1"}.arg (rig_number).toLocal8Bit ());
CAT_TRACE (QString {"status change for rig %1"}.arg (rig_number));
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
auto const& status = rig_->StatusStr ().toLocal8Bit ();
TRACE_CAT ("OmniRigTransceiver", "OmniRig status change: new status = " << status);
auto const& status = rig_->StatusStr ();
CAT_TRACE ("OmniRig status change: new status = " << status);
if (OmniRig::ST_ONLINE != rig_->Status ())
{
if (!offline_timer_->isActive ())
@ -399,16 +402,16 @@ void OmniRigTransceiver::handle_status_change (int rig_number)
// {
// update_rx_frequency (rig_->GetRxFrequency ());
// update_complete ();
// TRACE_CAT ("OmniRigTransceiver", "frequency:" << state ().frequency ());
// CAT_TRACE ("frequency:" << state ().frequency ());
// }
}
}
void OmniRigTransceiver::handle_params_change (int rig_number, int params)
{
TRACE_CAT ("OmniRigTransceiver", QString {"params change: params = 0x%1 for rig %2"}
CAT_TRACE (QString {"params change: params=0x%1 for rig %2"}
.arg (params, 8, 16, QChar ('0'))
.arg (rig_number).toLocal8Bit ()
.arg (rig_number)
<< "state before:" << state ());
if (rig_number_ == rig_number)
{
@ -419,7 +422,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
if (params & OmniRig::PM_VFOAA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAA");
CAT_TRACE ("VFOAA");
update_split (false);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
@ -427,7 +430,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
}
if (params & OmniRig::PM_VFOAB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOAB");
CAT_TRACE ("VFOAB");
update_split (true);
reversed_ = false;
update_rx_frequency (rig_->FreqA ());
@ -435,7 +438,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
}
if (params & OmniRig::PM_VFOBA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBA");
CAT_TRACE ("VFOBA");
update_split (true);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
@ -443,7 +446,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
}
if (params & OmniRig::PM_VFOBB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOBB");
CAT_TRACE ("VFOBB");
update_split (false);
reversed_ = true;
update_other_frequency (rig_->FreqA ());
@ -451,26 +454,26 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
}
if (params & OmniRig::PM_VFOA)
{
TRACE_CAT ("OmniRigTransceiver", "VFOA");
CAT_TRACE ("VFOA");
reversed_ = false;
need_frequency = true;
}
if (params & OmniRig::PM_VFOB)
{
TRACE_CAT ("OmniRigTransceiver", "VFOB");
CAT_TRACE ("VFOB");
reversed_ = true;
need_frequency = true;
}
if (params & OmniRig::PM_FREQ)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ");
CAT_TRACE ("FREQ");
need_frequency = true;
}
if (params & OmniRig::PM_FREQA)
{
auto f = rig_->FreqA ();
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
CAT_TRACE ("FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
@ -483,7 +486,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
if (params & OmniRig::PM_FREQB)
{
auto f = rig_->FreqB ();
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
CAT_TRACE ("FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
@ -500,7 +503,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
auto f = rig_->FreqA ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQA = " << f);
CAT_TRACE ("FREQA = " << f);
if (reversed_)
{
update_other_frequency (f);
@ -516,7 +519,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
auto f = rig_->FreqB ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQB = " << f);
CAT_TRACE ("FREQB = " << f);
if (reversed_)
{
update_rx_frequency (f);
@ -532,35 +535,35 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
auto f = rig_->Freq ();
if (f)
{
TRACE_CAT ("OmniRigTransceiver", "FREQ = " << f);
CAT_TRACE ("FREQ = " << f);
update_rx_frequency (f);
}
}
}
if (params & OmniRig::PM_PITCH)
{
TRACE_CAT ("OmniRigTransceiver", "PITCH");
CAT_TRACE ("PITCH");
}
if (params & OmniRig::PM_RITOFFSET)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFFSET");
CAT_TRACE ("RITOFFSET");
}
if (params & OmniRig::PM_RIT0)
{
TRACE_CAT ("OmniRigTransceiver", "RIT0");
CAT_TRACE ("RIT0");
}
if (params & OmniRig::PM_VFOEQUAL)
{
auto f = readable_params_ & OmniRig::PM_FREQA ? rig_->FreqA () : rig_->Freq ();
auto m = map_mode (rig_->Mode ());
TRACE_CAT ("OmniRigTransceiver", QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m));
CAT_TRACE (QString {"VFOEQUAL f=%1 m=%2"}.arg (f).arg (m));
update_rx_frequency (f);
update_other_frequency (f);
update_mode (m);
}
if (params & OmniRig::PM_VFOSWAP)
{
TRACE_CAT ("OmniRigTransceiver", "VFOSWAP");
CAT_TRACE ("VFOSWAP");
auto f = state ().tx_frequency ();
update_other_frequency (state ().frequency ());
update_rx_frequency (f);
@ -568,78 +571,78 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
}
if (params & OmniRig::PM_SPLITON)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITON");
CAT_TRACE ("SPLITON");
update_split (true);
}
if (params & OmniRig::PM_SPLITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "SPLITOFF");
CAT_TRACE ("SPLITOFF");
update_split (false);
}
if (params & OmniRig::PM_RITON)
{
TRACE_CAT ("OmniRigTransceiver", "RITON");
CAT_TRACE ("RITON");
}
if (params & OmniRig::PM_RITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "RITOFF");
CAT_TRACE ("RITOFF");
}
if (params & OmniRig::PM_XITON)
{
TRACE_CAT ("OmniRigTransceiver", "XITON");
CAT_TRACE ("XITON");
}
if (params & OmniRig::PM_XITOFF)
{
TRACE_CAT ("OmniRigTransceiver", "XITOFF");
CAT_TRACE ("XITOFF");
}
if (params & OmniRig::PM_RX)
{
TRACE_CAT ("OmniRigTransceiver", "RX");
CAT_TRACE ("RX");
update_PTT (false);
}
if (params & OmniRig::PM_TX)
{
TRACE_CAT ("OmniRigTransceiver", "TX");
CAT_TRACE ("TX");
update_PTT ();
}
if (params & OmniRig::PM_CW_U)
{
TRACE_CAT ("OmniRigTransceiver", "CW-R");
CAT_TRACE ("CW-R");
update_mode (CW_R);
}
if (params & OmniRig::PM_CW_L)
{
TRACE_CAT ("OmniRigTransceiver", "CW");
CAT_TRACE ("CW");
update_mode (CW);
}
if (params & OmniRig::PM_SSB_U)
{
TRACE_CAT ("OmniRigTransceiver", "USB");
CAT_TRACE ("USB");
update_mode (USB);
}
if (params & OmniRig::PM_SSB_L)
{
TRACE_CAT ("OmniRigTransceiver", "LSB");
CAT_TRACE ("LSB");
update_mode (LSB);
}
if (params & OmniRig::PM_DIG_U)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-U");
CAT_TRACE ("DATA-U");
update_mode (DIG_U);
}
if (params & OmniRig::PM_DIG_L)
{
TRACE_CAT ("OmniRigTransceiver", "DATA-L");
CAT_TRACE ("DATA-L");
update_mode (DIG_L);
}
if (params & OmniRig::PM_AM)
{
TRACE_CAT ("OmniRigTransceiver", "AM");
CAT_TRACE ("AM");
update_mode (AM);
}
if (params & OmniRig::PM_FM)
{
TRACE_CAT ("OmniRigTransceiver", "FM");
CAT_TRACE ("FM");
update_mode (FM);
}
@ -648,7 +651,7 @@ void OmniRigTransceiver::handle_params_change (int rig_number, int params)
update_complete ();
send_update_signal_ = false;
}
TRACE_CAT ("OmniRigTransceiver", "OmniRig params change: state after:" << state ());
CAT_TRACE ("OmniRig params change: state after:" << state ());
}
Q_EMIT notified ();
}
@ -661,19 +664,19 @@ void OmniRigTransceiver::handle_custom_reply (int rig_number, QVariant const& co
if (rig_number_ == rig_number)
{
if (!rig_ || rig_->isNull ()) return;
TRACE_CAT ("OmniRigTransceiver", "custom command" << command.toString ().toLocal8Bit ()
<< "with reply" << reply.toString ().toLocal8Bit ()
<< QString ("for rig %1").arg (rig_number).toLocal8Bit ());
TRACE_CAT ("OmniRigTransceiver", "rig number:" << rig_number_ << ':' << state ());
CAT_TRACE ("custom command" << command.toString ()
<< "with reply" << reply.toString ()
<< QString ("for rig %1").arg (rig_number));
CAT_TRACE ("rig number:" << rig_number_ << ':' << state ());
}
}
void OmniRigTransceiver::do_ptt (bool on)
{
TRACE_CAT ("OmniRigTransceiver", on << state ());
CAT_TRACE (on << state ());
if (use_for_ptt_ && TransceiverFactory::PTT_method_CAT == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set PTT");
CAT_TRACE ("set PTT");
if (rig_ && !rig_->isNull ())
{
rig_->SetTx (on ? OmniRig::PM_TX : OmniRig::PM_RX);
@ -685,18 +688,18 @@ void OmniRigTransceiver::do_ptt (bool on)
{
if (TransceiverFactory::PTT_method_RTS == ptt_type_)
{
TRACE_CAT ("OmniRigTransceiver", "set RTS");
CAT_TRACE ("set RTS");
port_->SetRts (on);
}
else // "DTR"
{
TRACE_CAT ("OmniRigTransceiver", "set DTR");
CAT_TRACE ("set DTR");
port_->SetDtr (on);
}
}
else if (wrapped_)
{
TRACE_CAT ("OmniRigTransceiver", "set PTT using basic transceiver");
CAT_TRACE ("set PTT using basic transceiver");
TransceiverState new_state {wrapped_->state ()};
new_state.ptt (on);
wrapped_->set (new_state, 0);
@ -707,7 +710,7 @@ void OmniRigTransceiver::do_ptt (bool on)
void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", f << state ());
CAT_TRACE (f << state ());
if (!rig_ || rig_->isNull ()) return;
if (UNK != m)
{
@ -736,7 +739,7 @@ void OmniRigTransceiver::do_frequency (Frequency f, MODE m, bool /*no_ignore*/)
void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore*/)
{
TRACE_CAT ("OmniRigTransceiver", tx << state ());
CAT_TRACE (tx << state ());
if (!rig_ || rig_->isNull ()) return;
bool split {tx != 0};
if (split)
@ -766,14 +769,14 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore
}
}
}
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode on");
CAT_TRACE ("set SPLIT mode on");
rig_->SetSplitMode (state ().frequency (), tx);
update_other_frequency (tx);
update_split (true);
}
else
{
TRACE_CAT ("OmniRigTransceiver", "set SPLIT mode off");
CAT_TRACE ("set SPLIT mode off");
rig_->SetSimplexMode (state ().frequency ());
update_split (false);
}
@ -788,7 +791,7 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore
}
if (!((OmniRig::PM_VFOAB | OmniRig::PM_VFOBA | OmniRig::PM_SPLITON) & readable_params_))
{
TRACE_CAT ("OmniRigTransceiver", "setting SPLIT manually");
CAT_TRACE ("setting SPLIT manually");
update_split (split); // we can't read it so just set and
// hope op doesn't change it
notify = true;
@ -801,7 +804,7 @@ void OmniRigTransceiver::do_tx_frequency (Frequency tx, MODE m, bool /*no_ignore
void OmniRigTransceiver::do_mode (MODE mode)
{
TRACE_CAT ("OmniRigTransceiver", mode << state ());
CAT_TRACE (mode << state ());
if (!rig_ || rig_->isNull ()) return;
// TODO: G4WJS OmniRig doesn't seem to have any capability of tracking/setting VFO B mode
auto mapped = map_mode (mode);

View File

@ -26,12 +26,14 @@ class OmniRigTransceiver final
Q_OBJECT;
public:
static void register_transceivers (TransceiverFactory::Transceivers *, int id1, int id2);
static void register_transceivers (logger_type *, TransceiverFactory::Transceivers *, int id1, int id2);
enum RigNumber {One = 1, Two};
// takes ownership of wrapped Transceiver
explicit OmniRigTransceiver (std::unique_ptr<TransceiverBase> wrapped, RigNumber, TransceiverFactory::PTTMethod ptt_type, QString const& ptt_port, QObject * parent = nullptr);
explicit OmniRigTransceiver (logger_type *, std::unique_ptr<TransceiverBase> wrapped,
RigNumber, TransceiverFactory::PTTMethod ptt_type,
QString const& ptt_port, QObject * parent = nullptr);
~OmniRigTransceiver ();
int do_start () override;
@ -52,8 +54,8 @@ private:
Q_SLOT void handle_params_change (int rig_number, int params);
Q_SLOT void handle_custom_reply (int, QVariant const& command, QVariant const& reply);
static MODE map_mode (OmniRig::RigParamX param);
static OmniRig::RigParamX map_mode (MODE mode);
MODE map_mode (OmniRig::RigParamX param);
OmniRig::RigParamX map_mode (MODE mode);
std::unique_ptr<TransceiverBase> wrapped_; // may be null
bool use_for_ptt_;

View File

@ -13,8 +13,8 @@ namespace
unsigned const polls_to_stabilize {3};
}
PollingTransceiver::PollingTransceiver (int poll_interval, QObject * parent)
: TransceiverBase {parent}
PollingTransceiver::PollingTransceiver (logger_type * logger, int poll_interval, QObject * parent)
: TransceiverBase {logger, parent}
, interval_ {poll_interval * 1000}
, poll_timer_ {nullptr}
, retries_ {0}

View File

@ -35,7 +35,7 @@ class PollingTransceiver
Q_OBJECT; // for translation context
protected:
explicit PollingTransceiver (int poll_interval, // in seconds
explicit PollingTransceiver (logger_type *, int poll_interval, // in seconds
QObject * parent);
protected:

View File

@ -1,7 +1,15 @@
#include "Transceiver.hpp"
#include <ostream>
#include "moc_Transceiver.cpp"
Transceiver::Transceiver (logger_type * logger, QObject * parent)
: QObject {parent}
, logger_ {logger}
{
}
#if !defined (QT_NO_DEBUG_STREAM)
QDebug operator << (QDebug d, Transceiver::TransceiverState const& s)
{
@ -15,6 +23,16 @@ QDebug operator << (QDebug d, Transceiver::TransceiverState const& s)
}
#endif
std::ostream& operator << (std::ostream& os, Transceiver::TransceiverState const& s)
{
return os
<< "Transceiver::TransceiverState(online: " << (s.online_ ? "yes" : "no")
<< " Frequency {" << s.rx_frequency_ << "Hz, " << s.tx_frequency_ << "Hz} " << s.mode_
<< "; SPLIT: " << (Transceiver::TransceiverState::Split::on == s.split_ ? "on" : Transceiver::TransceiverState::Split::off == s.split_ ? "off" : "unknown")
<< "; PTT: " << (s.ptt_ ? "on" : "off")
<< ')';
}
ENUM_QDATASTREAM_OPS_IMPL (Transceiver, MODE);
ENUM_CONVERSION_OPS_IMPL (Transceiver, MODE);

View File

@ -1,6 +1,11 @@
#ifndef TRANSCEIVER_HPP__
#define TRANSCEIVER_HPP__
#include <iosfwd>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <QObject>
#include "qt_helpers.hpp"
@ -55,9 +60,11 @@ class Transceiver
public:
using Frequency = Radio::Frequency;
using logger_type = boost::log::sources::severity_channel_logger_mt<boost::log::trivial::severity_level>;
protected:
Transceiver (QObject * parent) : QObject {parent} {}
Transceiver (logger_type *, QObject * parent);
logger_type& logger () const {return *logger_;}
public:
virtual ~Transceiver () {}
@ -108,6 +115,7 @@ public:
friend QDebug operator << (QDebug, TransceiverState const&);
friend bool operator != (TransceiverState const&, TransceiverState const&);
friend std::ostream& operator << (std::ostream&, Transceiver::TransceiverState const&);
};
//
@ -150,6 +158,9 @@ public:
// Ready to be destroyed.
Q_SIGNAL void finished () const;
private:
logger_type mutable * logger_;
};
Q_DECLARE_METATYPE (Transceiver::TransceiverState);
@ -158,6 +169,8 @@ Q_DECLARE_METATYPE (Transceiver::TransceiverState);
QDebug operator << (QDebug, Transceiver::TransceiverState const&);
#endif
std::ostream& operator << (std::ostream&, Transceiver::TransceiverState const&);
ENUM_QDATASTREAM_OPS_DECL (Transceiver, MODE);
ENUM_CONVERSION_OPS_DECL (Transceiver, MODE);

View File

@ -41,7 +41,7 @@ void TransceiverBase::start (unsigned sequence_number) noexcept
void TransceiverBase::set (TransceiverState const& s,
unsigned sequence_number) noexcept
{
TRACE_CAT ("TransceiverBase", "#:" << sequence_number << s);
CAT_TRACE ("#: " << sequence_number << " " << s);
QString message;
try

View File

@ -5,6 +5,7 @@
#include <QString>
#include "Logger.hpp"
#include "Transceiver.hpp"
//
@ -61,8 +62,8 @@ class TransceiverBase
Q_OBJECT;
protected:
TransceiverBase (QObject * parent)
: Transceiver {parent}
TransceiverBase (logger_type * logger, QObject * parent)
: Transceiver {logger, parent}
, last_sequence_number_ {0}
{}
@ -152,17 +153,12 @@ private:
unsigned last_sequence_number_; // from set state operation
};
// some trace macros
#if WSJT_TRACE_CAT
#define TRACE_CAT(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG
#else
#define TRACE_CAT(FAC, MSG)
#endif
#if WSJT_TRACE_CAT && WSJT_TRACE_CAT_POLLS
#define TRACE_CAT_POLL(FAC, MSG) qDebug () << QString {"%1::%2:"}.arg ((FAC)).arg (__func__) << MSG
#else
#define TRACE_CAT_POLL(FAC, MSG)
#endif
// some loggimg macros
#define CAT_TRACE(MSG) LOG_LOG_LOCATION (logger (), trace, MSG)
#define CAT_DEBUG(MSG) LOG_LOG_LOCATION (logger (), debug, MSG)
#define CAT_INFO(MSG) LOG_LOG_LOCATION (logger (), info, MSG)
#define CAT_WARNING(MSG) LOG_LOG_LOCATION (logger (), warning, MSG)
#define CAT_ERROR(MSG) LOG_LOG_LOCATION (logger (), error, MSG)
#define CAT_FATAL(MSG) LOG_LOG_LOCATION (logger (), fatal, MSG)
#endif

View File

@ -32,14 +32,15 @@ namespace
}
TransceiverFactory::TransceiverFactory ()
: logger_ (boost::log::keywords::channel = "RIGCTRL")
{
HamlibTransceiver::register_transceivers (&transceivers_);
DXLabSuiteCommanderTransceiver::register_transceivers (&transceivers_, CommanderId);
HRDTransceiver::register_transceivers (&transceivers_, HRDId);
HamlibTransceiver::register_transceivers (&logger_, &transceivers_);
DXLabSuiteCommanderTransceiver::register_transceivers (&logger_, &transceivers_, CommanderId);
HRDTransceiver::register_transceivers (&logger_, &transceivers_, HRDId);
#if defined (WIN32)
// OmniRig is ActiveX/COM server so only on Windows
OmniRigTransceiver::register_transceivers (&transceivers_, OmniRigOneId, OmniRigTwoId);
OmniRigTransceiver::register_transceivers (&logger_, &transceivers_, OmniRigOneId, OmniRigTwoId);
#endif
}
@ -91,7 +92,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
if (PTT_method_CAT != params.ptt_type)
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
basic_transceiver.reset (new HamlibTransceiver {&logger_, params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
@ -99,7 +100,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
}
// wrap the basic Transceiver object instance with a decorator object that talks to DX Lab Suite Commander
result.reset (new DXLabSuiteCommanderTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval});
result.reset (new DXLabSuiteCommanderTransceiver {&logger_, std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.poll_interval});
if (target_thread)
{
result->moveToThread (target_thread);
@ -113,7 +114,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
if (PTT_method_CAT != params.ptt_type)
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
basic_transceiver.reset (new HamlibTransceiver {&logger_, params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
@ -121,7 +122,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
}
// wrap the basic Transceiver object instance with a decorator object that talks to ham Radio Deluxe
result.reset (new HRDTransceiver {std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.audio_source, params.poll_interval});
result.reset (new HRDTransceiver {&logger_, std::move (basic_transceiver), params.network_port, PTT_method_CAT == params.ptt_type, params.audio_source, params.poll_interval});
if (target_thread)
{
result->moveToThread (target_thread);
@ -136,7 +137,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
basic_transceiver.reset (new HamlibTransceiver {&logger_, params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
@ -144,7 +145,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
}
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig one
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::One, params.ptt_type, params.ptt_port});
result.reset (new OmniRigTransceiver {&logger_, std::move (basic_transceiver), OmniRigTransceiver::One, params.ptt_type, params.ptt_port});
if (target_thread)
{
result->moveToThread (target_thread);
@ -158,7 +159,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
if (PTT_method_CAT != params.ptt_type && "CAT" != params.ptt_port)
{
// we start with a dummy HamlibTransceiver object instance that can support direct PTT
basic_transceiver.reset (new HamlibTransceiver {params.ptt_type, params.ptt_port});
basic_transceiver.reset (new HamlibTransceiver {&logger_, params.ptt_type, params.ptt_port});
if (target_thread)
{
basic_transceiver.get ()->moveToThread (target_thread);
@ -166,7 +167,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
}
// wrap the basic Transceiver object instance with a decorator object that talks to OmniRig rig two
result.reset (new OmniRigTransceiver {std::move (basic_transceiver), OmniRigTransceiver::Two, params.ptt_type, params.ptt_port});
result.reset (new OmniRigTransceiver {&logger_, std::move (basic_transceiver), OmniRigTransceiver::Two, params.ptt_type, params.ptt_port});
if (target_thread)
{
result->moveToThread (target_thread);
@ -176,7 +177,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
#endif
default:
result.reset (new HamlibTransceiver {supported_transceivers ()[params.rig_name].model_number_, params});
result.reset (new HamlibTransceiver {&logger_, supported_transceivers ()[params.rig_name].model_number_, params});
if (target_thread)
{
result->moveToThread (target_thread);
@ -187,7 +188,7 @@ std::unique_ptr<Transceiver> TransceiverFactory::create (ParameterPack const& pa
if (split_mode_emulate == params.split_mode)
{
// wrap the Transceiver object instance with a decorator that emulates split mode
result.reset (new EmulateSplitTransceiver {std::move (result)});
result.reset (new EmulateSplitTransceiver {&logger_, std::move (result)});
if (target_thread)
{
result->moveToThread (target_thread);

View File

@ -3,6 +3,9 @@
#include <memory>
#include <boost/log/trivial.hpp>
#include <boost/log/sources/severity_channel_logger.hpp>
#include <QObject>
#include <QMap>
@ -151,6 +154,7 @@ public:
std::unique_ptr<Transceiver> create (ParameterPack const&, QThread * target_thread = nullptr);
private:
Transceiver::logger_type mutable logger_;
Transceivers transceivers_;
};

View File

@ -25,10 +25,13 @@ namespace
QFont text_font {"Courier", 10};
QList<QStandardItem *> make_row (QString const& client_id, QTime time, qint32 snr, float delta_time
QList<QStandardItem *> make_row (MessageServer::ClientKey const& key, QTime time, qint32 snr, float delta_time
, Frequency frequency, qint32 drift, QString const& callsign
, QString const& grid, qint32 power, bool off_air)
{
auto client_item = new QStandardItem {QString {"%1(%2)"}.arg (key.second).arg (key.first.toString ())};
client_item->setData (QVariant::fromValue (key));
auto time_item = new QStandardItem {time.toString ("hh:mm")};
time_item->setData (time);
time_item->setTextAlignment (Qt::AlignRight);
@ -60,7 +63,7 @@ namespace
live->setTextAlignment (Qt::AlignHCenter);
QList<QStandardItem *> row {
new QStandardItem {client_id}, time_item, snr_item, dt, freq, dri, gd, pwr, live, new QStandardItem {callsign}};
client_item, time_item, snr_item, dt, freq, dri, gd, pwr, live, new QStandardItem {callsign}};
Q_FOREACH (auto& item, row)
{
item->setEditable (false);
@ -81,7 +84,7 @@ BeaconsModel::BeaconsModel (QObject * parent)
}
}
void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
void BeaconsModel::add_beacon_spot (bool is_new, ClientKey const& key, QTime time, qint32 snr, float delta_time
, Frequency frequency, qint32 drift, QString const& callsign
, QString const& grid, qint32 power, bool off_air)
{
@ -90,7 +93,7 @@ void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime
int target_row {-1};
for (auto row = 0; row < rowCount (); ++row)
{
if (data (index (row, 0)).toString () == client_id)
if (item (row, 0)->data ().value<ClientKey> () == key)
{
auto row_time = item (row, 1)->data ().toTime ();
if (row_time == time
@ -113,19 +116,19 @@ void BeaconsModel::add_beacon_spot (bool is_new, QString const& client_id, QTime
}
if (target_row >= 0)
{
insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
insertRow (target_row + 1, make_row (key, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
return;
}
}
appendRow (make_row (client_id, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
appendRow (make_row (key, time, snr, delta_time, frequency, drift, callsign, grid, power, off_air));
}
void BeaconsModel::decodes_cleared (QString const& client_id)
void BeaconsModel::decodes_cleared (ClientKey const& key)
{
for (auto row = rowCount () - 1; row >= 0; --row)
{
if (data (index (row, 0)).toString () == client_id)
if (item (row, 0)->data ().value<ClientKey> () == key)
{
removeRow (row);
}

View File

@ -26,13 +26,15 @@ class BeaconsModel
{
Q_OBJECT;
using ClientKey = MessageServer::ClientKey;
public:
explicit BeaconsModel (QObject * parent = nullptr);
Q_SLOT void add_beacon_spot (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
Q_SLOT void add_beacon_spot (bool is_new, ClientKey const&, QTime time, qint32 snr, float delta_time
, Frequency frequency, qint32 drift, QString const& callsign, QString const& grid
, qint32 power, bool off_air);
Q_SLOT void decodes_cleared (QString const& client_id);
Q_SLOT void decodes_cleared (ClientKey const&);
};
#endif

View File

@ -5,7 +5,6 @@
#include <QColor>
#include <QtWidgets>
#include <QAction>
#include <QDebug>
#include "validators/MaidenheadLocatorValidator.hpp"
@ -27,9 +26,9 @@ namespace
}
}
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id, QObject * parent)
ClientWidget::IdFilterModel::IdFilterModel (ClientKey const& key, QObject * parent)
: QSortFilterProxyModel {parent}
, client_id_ {client_id}
, key_ {key}
, rx_df_ (quint32_max)
{
}
@ -73,7 +72,7 @@ bool ClientWidget::IdFilterModel::filterAcceptsRow (int source_row
, QModelIndex const& source_parent) const
{
auto source_index_col0 = sourceModel ()->index (source_row, 0, source_parent);
return sourceModel ()->data (source_index_col0).toString () == client_id_;
return sourceModel ()->data (source_index_col0, Qt::UserRole + 1).value<ClientKey> () == key_;
}
void ClientWidget::IdFilterModel::de_call (QString const& call)
@ -106,9 +105,9 @@ void ClientWidget::IdFilterModel::rx_df (quint32 df)
namespace
{
QString make_title (QString const& id, QString const& version, QString const& revision)
QString make_title (MessageServer::ClientKey const& key, QString const& version, QString const& revision)
{
QString title {id};
QString title {QString {"%1(%2)"}.arg (key.second).arg (key.first.toString ())};
if (version.size ())
{
title += QString {" v%1"}.arg (version);
@ -122,14 +121,14 @@ namespace
}
ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
, QString const& id, QString const& version, QString const& revision
, ClientKey const& key, QString const& version, QString const& revision
, QListWidget const * calls_of_interest, QWidget * parent)
: QDockWidget {make_title (id, version, revision), parent}
, id_ {id}
: QDockWidget {make_title (key, version, revision), parent}
, key_ {key}
, done_ {false}
, calls_of_interest_ {calls_of_interest}
, decodes_proxy_model_ {id}
, beacons_proxy_model_ {id}
, decodes_proxy_model_ {key}
, beacons_proxy_model_ {key}
, erase_action_ {new QAction {tr ("&Erase Band Activity"), this}}
, erase_rx_frequency_action_ {new QAction {tr ("Erase &Rx Frequency"), this}}
, erase_both_action_ {new QAction {tr ("Erase &Both"), this}}
@ -184,10 +183,10 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
message_line_edit_->setValidator (&message_validator);
grid_line_edit_->setValidator (&locator_validator);
dx_grid_line_edit_->setValidator (&locator_validator);
tr_period_spin_box_->setRange (5, 30);
tr_period_spin_box_->setRange (5, 1800);
tr_period_spin_box_->setSuffix (" s");
rx_df_spin_box_->setRange (200, 5000);
frequency_tolerance_spin_box_->setRange (10, 1000);
frequency_tolerance_spin_box_->setRange (1, 1000);
frequency_tolerance_spin_box_->setPrefix ("\u00b1");
frequency_tolerance_spin_box_->setSuffix (" Hz");
@ -209,56 +208,56 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
horizontal_layout_->addLayout (subform3_layout_);
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
Q_EMIT do_free_text (id_, text, false);
Q_EMIT do_free_text (key_, text, false);
});
connect (message_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT do_free_text (id_, message_line_edit_->text (), true);
Q_EMIT do_free_text (key_, message_line_edit_->text (), true);
});
connect (grid_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT location (id_, grid_line_edit_->text ());
Q_EMIT location (key_, grid_line_edit_->text ());
});
connect (configuration_line_edit_, &QLineEdit::editingFinished, [this] () {
Q_EMIT switch_configuration (id_, configuration_line_edit_->text ());
Q_EMIT switch_configuration (key_, configuration_line_edit_->text ());
});
connect (mode_line_edit_, &QLineEdit::editingFinished, [this] () {
QString empty;
Q_EMIT configure (id_, mode_line_edit_->text (), quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, mode_line_edit_->text (), quint32_max, empty, fast_mode ()
, quint32_max, quint32_max, empty, empty, false);
});
connect (frequency_tolerance_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
QString empty;
auto f = frequency_tolerance_spin_box_->specialValueText ().size () ? quint32_max : i;
Q_EMIT configure (id_, empty, f, empty, fast_mode ()
Q_EMIT configure (key_, empty, f, empty, fast_mode ()
, quint32_max, quint32_max, empty, empty, false);
});
connect (submode_line_edit_, &QLineEdit::editingFinished, [this] () {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, submode_line_edit_->text (), fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, submode_line_edit_->text (), fast_mode ()
, quint32_max, quint32_max, empty, empty, false);
});
connect (fast_mode_check_box_, &QCheckBox::stateChanged, [this] (int state) {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, Qt::Checked == state
Q_EMIT configure (key_, empty, quint32_max, empty, Qt::Checked == state
, quint32_max, quint32_max, empty, empty, false);
});
connect (tr_period_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, empty, fast_mode ()
, i, quint32_max, empty, empty, false);
});
connect (rx_df_spin_box_, static_cast<void (QSpinBox::*) (int)> (&QSpinBox::valueChanged), [this] (int i) {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, empty, fast_mode ()
, quint32_max, i, empty, empty, false);
});
connect (dx_call_line_edit_, &QLineEdit::editingFinished, [this] () {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, empty, fast_mode ()
, quint32_max, quint32_max, dx_call_line_edit_->text (), empty, false);
});
connect (dx_grid_line_edit_, &QLineEdit::editingFinished, [this] () {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, empty, fast_mode ()
, quint32_max, quint32_max, empty, dx_grid_line_edit_->text (), false);
});
@ -289,14 +288,14 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
halt_tx_button_ = control_button_box_->addButton (tr ("&Halt Tx"), QDialogButtonBox::ActionRole);
connect (generate_messages_push_button_, &QAbstractButton::clicked, [this] (bool /*checked*/) {
QString empty;
Q_EMIT configure (id_, empty, quint32_max, empty, fast_mode ()
Q_EMIT configure (key_, empty, quint32_max, empty, fast_mode ()
, quint32_max, quint32_max, empty, empty, true);
});
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
Q_EMIT do_halt_tx (id_, true);
Q_EMIT do_halt_tx (key_, true);
});
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
Q_EMIT do_halt_tx (id_, false);
Q_EMIT do_halt_tx (key_, false);
});
content_layout_->addWidget (control_button_box_);
@ -318,13 +317,13 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
// connect context menu actions
connect (erase_action_, &QAction::triggered, [this] (bool /*checked*/) {
Q_EMIT do_clear_decodes (id_);
Q_EMIT do_clear_decodes (key_);
});
connect (erase_rx_frequency_action_, &QAction::triggered, [this] (bool /*checked*/) {
Q_EMIT do_clear_decodes (id_, 1);
Q_EMIT do_clear_decodes (key_, 1);
});
connect (erase_both_action_, &QAction::triggered, [this] (bool /*checked*/) {
Q_EMIT do_clear_decodes (id_, 2);
Q_EMIT do_clear_decodes (key_, 2);
});
// connect up table view signals
@ -335,7 +334,7 @@ ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemMod
// tell new client about calls of interest
for (int row = 0; row < calls_of_interest_->count (); ++row)
{
Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text (), QColor {Qt::blue}, QColor {Qt::yellow});
Q_EMIT highlight_callsign (key_, calls_of_interest_->item (row)->text (), QColor {Qt::blue}, QColor {Qt::yellow});
}
}
@ -349,7 +348,7 @@ void ClientWidget::closeEvent (QCloseEvent *e)
{
if (!done_)
{
Q_EMIT do_close (id_);
Q_EMIT do_close (key_);
e->ignore (); // defer closure until client actually closes
}
else
@ -363,7 +362,7 @@ ClientWidget::~ClientWidget ()
for (int row = 0; row < calls_of_interest_->count (); ++row)
{
// tell client to forget calls of interest
Q_EMIT highlight_callsign (id_, calls_of_interest_->item (row)->text ());
Q_EMIT highlight_callsign (key_, calls_of_interest_->item (row)->text ());
}
}
@ -395,7 +394,7 @@ namespace
}
}
void ClientWidget::update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
void ClientWidget::update_status (ClientKey const& key, Frequency f, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode, bool tx_enabled
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid
@ -403,7 +402,7 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const&
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
, QString const& configuration_name)
{
if (id == id_)
if (key == key_)
{
fast_mode_check_box_->setChecked (fast_mode);
decodes_proxy_model_.de_call (de_call);
@ -447,11 +446,11 @@ void ClientWidget::update_status (QString const& id, Frequency f, QString const&
}
}
void ClientWidget::decode_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
void ClientWidget::decode_added (bool /*is_new*/, ClientKey const& key, QTime /*time*/, qint32 /*snr*/
, float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/
, QString const& /*message*/, bool /*low_confidence*/, bool /*off_air*/)
{
if (client_id == id_ && !columns_resized_)
if (key == key_ && !columns_resized_)
{
decodes_stack_->setCurrentIndex (0);
decodes_table_view_->resizeColumnsToContents ();
@ -460,12 +459,12 @@ void ClientWidget::decode_added (bool /*is_new*/, QString const& client_id, QTim
decodes_table_view_->scrollToBottom ();
}
void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
void ClientWidget::beacon_spot_added (bool /*is_new*/, ClientKey const& key, QTime /*time*/, qint32 /*snr*/
, float /*delta_time*/, Frequency /*delta_frequency*/, qint32 /*drift*/
, QString const& /*callsign*/, QString const& /*grid*/, qint32 /*power*/
, bool /*off_air*/)
{
if (client_id == id_ && !columns_resized_)
if (key == key_ && !columns_resized_)
{
decodes_stack_->setCurrentIndex (1);
beacons_table_view_->resizeColumnsToContents ();
@ -474,9 +473,9 @@ void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id,
beacons_table_view_->scrollToBottom ();
}
void ClientWidget::decodes_cleared (QString const& client_id)
void ClientWidget::decodes_cleared (ClientKey const& key)
{
if (client_id == id_)
if (key == key_)
{
columns_resized_ = false;
}

View File

@ -35,42 +35,44 @@ class ClientWidget
{
Q_OBJECT;
using ClientKey = MessageServer::ClientKey;
public:
explicit ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
, QString const& id, QString const& version, QString const& revision
, ClientKey const& key, QString const& version, QString const& revision
, QListWidget const * calls_of_interest, QWidget * parent = nullptr);
void dispose ();
~ClientWidget ();
bool fast_mode () const;
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& dx_call
Q_SLOT void update_status (ClientKey const& key, Frequency f, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode, bool tx_enabled
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
, QString const& configuration_name);
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime, qint32 snr
Q_SLOT void decode_added (bool is_new, ClientKey const& key, QTime, qint32 snr
, float delta_time, quint32 delta_frequency, QString const& mode
, QString const& message, bool low_confidence, bool off_air);
Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime, qint32 snr
Q_SLOT void beacon_spot_added (bool is_new, ClientKey const& key, QTime, qint32 snr
, float delta_time, Frequency delta_frequency, qint32 drift
, QString const& callsign, QString const& grid, qint32 power
, bool off_air);
Q_SLOT void decodes_cleared (QString const& client_id);
Q_SLOT void decodes_cleared (ClientKey const& key);
Q_SIGNAL void do_clear_decodes (QString const& id, quint8 window = 0);
Q_SIGNAL void do_close (QString const& id);
Q_SIGNAL void do_clear_decodes (ClientKey const& key, quint8 window = 0);
Q_SIGNAL void do_close (ClientKey const& key);
Q_SIGNAL void do_reply (QModelIndex const&, quint8 modifier);
Q_SIGNAL void do_halt_tx (QString const& id, bool auto_only);
Q_SIGNAL void do_free_text (QString const& id, QString const& text, bool);
Q_SIGNAL void location (QString const& id, QString const& text);
Q_SIGNAL void highlight_callsign (QString const& id, QString const& call
Q_SIGNAL void do_halt_tx (ClientKey const& key, bool auto_only);
Q_SIGNAL void do_free_text (ClientKey const& key, QString const& text, bool);
Q_SIGNAL void location (ClientKey const& key, QString const& text);
Q_SIGNAL void highlight_callsign (ClientKey const& key, QString const& call
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
, bool last_only = false);
Q_SIGNAL void switch_configuration (QString const& id, QString const& configuration_name);
Q_SIGNAL void configure (QString const& id, QString const& mode, quint32 frequency_tolerance
Q_SIGNAL void switch_configuration (ClientKey const& key, QString const& configuration_name);
Q_SIGNAL void configure (ClientKey const& key, QString const& mode, quint32 frequency_tolerance
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
, QString const& dx_call, QString const& dx_grid, bool generate_messages);
@ -79,7 +81,7 @@ private:
: public QSortFilterProxyModel
{
public:
IdFilterModel (QString const& client_id, QObject * = nullptr);
IdFilterModel (ClientKey const& key, QObject * = nullptr);
void de_call (QString const&);
void rx_df (quint32);
@ -88,7 +90,7 @@ private:
private:
bool filterAcceptsRow (int source_row, QModelIndex const& source_parent) const override;
QString client_id_;
ClientKey key_;
QString call_;
QRegularExpression base_call_re_;
quint32 rx_df_;
@ -96,7 +98,7 @@ private:
void closeEvent (QCloseEvent *) override;
QString id_;
ClientKey key_;
bool done_;
QListWidget const * calls_of_interest_;
IdFilterModel decodes_proxy_model_;

View File

@ -2,6 +2,7 @@
#include <QStandardItem>
#include <QModelIndex>
#include <QVariant>
#include <QTime>
#include <QString>
#include <QFont>
@ -33,10 +34,13 @@ namespace
QFont text_font {"Courier", 10};
QList<QStandardItem *> make_row (QString const& client_id, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode, QString const& message
, bool low_confidence, bool off_air, bool is_fast)
QList<QStandardItem *> make_row (MessageServer::ClientKey const& key, QTime time, qint32 snr
, float delta_time, quint32 delta_frequency, QString const& mode
, QString const& message, bool low_confidence, bool off_air, bool is_fast)
{
auto client_item = new QStandardItem {QString {"%1(%2)"}.arg (key.second).arg (key.first.toString ())};
client_item->setData (QVariant::fromValue (key));
auto time_item = new QStandardItem {time.toString (is_fast || "~" == mode ? "hh:mm:ss" : "hh:mm")};
time_item->setData (time);
time_item->setTextAlignment (Qt::AlignRight);
@ -63,7 +67,7 @@ namespace
live->setTextAlignment (Qt::AlignHCenter);
QList<QStandardItem *> row {
new QStandardItem {client_id}, time_item, snr_item, dt, df, md, confidence, live, new QStandardItem {message}};
client_item, time_item, snr_item, dt, df, md, confidence, live, new QStandardItem {message}};
Q_FOREACH (auto& item, row)
{
item->setEditable (false);
@ -84,7 +88,7 @@ DecodesModel::DecodesModel (QObject * parent)
}
}
void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
void DecodesModel::add_decode (bool is_new, ClientKey const& key, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode, QString const& message
, bool low_confidence, bool off_air, bool is_fast)
{
@ -93,7 +97,7 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time
int target_row {-1};
for (auto row = 0; row < rowCount (); ++row)
{
if (data (index (row, 0)).toString () == client_id)
if (item (row, 0)->data ().value<ClientKey> () == key)
{
auto row_time = item (row, 1)->data ().toTime ();
if (row_time == time
@ -115,21 +119,21 @@ void DecodesModel::add_decode (bool is_new, QString const& client_id, QTime time
}
if (target_row >= 0)
{
insertRow (target_row + 1, make_row (client_id, time, snr, delta_time, delta_frequency, mode
insertRow (target_row + 1, make_row (key, time, snr, delta_time, delta_frequency, mode
, message, low_confidence, off_air, is_fast));
return;
}
}
appendRow (make_row (client_id, time, snr, delta_time, delta_frequency, mode, message, low_confidence
appendRow (make_row (key, time, snr, delta_time, delta_frequency, mode, message, low_confidence
, off_air, is_fast));
}
void DecodesModel::decodes_cleared (QString const& client_id)
void DecodesModel::decodes_cleared (ClientKey const& key)
{
for (auto row = rowCount () - 1; row >= 0; --row)
{
if (data (index (row, 0)).toString () == client_id)
if (item (row, 0)->data ().value<ClientKey> () == key)
{
removeRow (row);
}
@ -139,7 +143,7 @@ void DecodesModel::decodes_cleared (QString const& client_id)
void DecodesModel::do_reply (QModelIndex const& source, quint8 modifiers)
{
auto row = source.row ();
Q_EMIT reply (data (index (row, 0)).toString ()
Q_EMIT reply (item (row, 0)->data ().value<ClientKey> ()
, item (row, 1)->data ().toTime ()
, item (row, 2)->data ().toInt ()
, item (row, 3)->data ().toFloat ()

View File

@ -5,8 +5,6 @@
#include "MessageServer.hpp"
using Frequency = MessageServer::Frequency;
class QTime;
class QString;
class QModelIndex;
@ -28,16 +26,18 @@ class DecodesModel
{
Q_OBJECT;
using ClientKey = MessageServer::ClientKey;
public:
explicit DecodesModel (QObject * parent = nullptr);
Q_SLOT void add_decode (bool is_new, QString const& client_id, QTime time, qint32 snr, float delta_time
Q_SLOT void add_decode (bool is_new, ClientKey const&, QTime, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode, QString const& message
, bool low_confidence, bool off_air, bool is_fast);
Q_SLOT void decodes_cleared (QString const& client_id);
Q_SLOT void decodes_cleared (ClientKey const&);
Q_SLOT void do_reply (QModelIndex const& source, quint8 modifiers);
Q_SIGNAL void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
Q_SIGNAL void reply (ClientKey const&, QTime, qint32 snr, float delta_time, quint32 delta_frequency
, QString const& mode, QString const& message, bool low_confidence, quint8 modifiers);
};

View File

@ -2,6 +2,8 @@
#include <QtWidgets>
#include <QDateTime>
#include <QNetworkInterface>
#include <QSet>
#include "DecodesModel.hpp"
#include "BeaconsModel.hpp"
@ -37,8 +39,10 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow ()
, decodes_model_ {new DecodesModel {this}}
, beacons_model_ {new BeaconsModel {this}}
, server_ {new MessageServer {this}}
, multicast_group_line_edit_ {new QLineEdit}
, log_table_view_ {new QTableView}
, port_spin_box_ {new QSpinBox {this}}
, multicast_group_line_edit_ {new QLineEdit {this}}
, network_interfaces_combo_box_ {new CheckableItemComboBox {this}}
, log_table_view_ {new QTableView {this}}
, add_call_of_interest_action_ {new QAction {tr ("&Add callsign"), this}}
, delete_call_of_interest_action_ {new QAction {tr ("&Delete callsign"), this}}
, last_call_of_interest_action_ {new QAction {tr ("&Highlight last only"), this}}
@ -68,16 +72,71 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow ()
auto central_layout = new QVBoxLayout;
// server details
auto port_spin_box = new QSpinBox;
port_spin_box->setMinimum (1);
port_spin_box->setMaximum (std::numeric_limits<port_type>::max ());
port_spin_box_->setMinimum (1);
port_spin_box_->setMaximum (std::numeric_limits<port_type>::max ());
auto group_box_layout = new QFormLayout;
group_box_layout->addRow (tr ("Port number:"), port_spin_box);
group_box_layout->addRow (tr ("Port number:"), port_spin_box_);
group_box_layout->addRow (tr ("Multicast Group (blank for unicast server):"), multicast_group_line_edit_);
group_box_layout->addRow (tr ("Network interfaces:"), network_interfaces_combo_box_);
int row;
QFormLayout::ItemRole role;
group_box_layout->getWidgetPosition (network_interfaces_combo_box_, &row, &role);
Q_ASSERT (row >= 0);
network_interfaces_form_label_widget_ = static_cast<QLabel *> (group_box_layout->itemAt (row, QFormLayout::LabelRole)->widget ());
network_interfaces_form_label_widget_->hide ();
network_interfaces_form_label_widget_->buddy ()->hide ();
connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this] {
if (multicast_group_line_edit_->text ().size ())
{
network_interfaces_form_label_widget_->show ();
network_interfaces_form_label_widget_->buddy ()->show ();
}
else
{
network_interfaces_form_label_widget_->hide ();
network_interfaces_form_label_widget_->buddy ()->hide ();
}
});
auto group_box = new QGroupBox {tr ("Server Details")};
group_box->setLayout (group_box_layout);
central_layout->addWidget (group_box);
// populate network interface list
for (auto const& net_if : QNetworkInterface::allInterfaces ())
{
auto flags = QNetworkInterface::IsRunning | QNetworkInterface::CanMulticast;
if ((net_if.flags () & flags) == flags)
{
if (net_if.flags () & QNetworkInterface::IsLoopBack)
{
loopback_interface_name_ = net_if.name ();
}
auto item = network_interfaces_combo_box_->addCheckItem (net_if.humanReadableName ()
, net_if.name ()
, Qt::Unchecked);
auto tip = QString {"name(index): %1(%2) - %3"}
.arg (net_if.name ()).arg (net_if.index ())
.arg (net_if.flags () & QNetworkInterface::IsUp ? "Up" : "Down");
auto hw_addr = net_if.hardwareAddress ();
if (hw_addr.size ())
{
tip += QString {"\nhw: %1"}.arg (net_if.hardwareAddress ());
}
auto aes = net_if.addressEntries ();
if (aes.size ())
{
tip += "\naddresses:";
for (auto const& ae : aes)
{
tip += QString {"\n ip: %1/%2"}.arg (ae.ip ().toString ()).arg (ae.prefixLength ());
}
}
item->setToolTip (tip);
}
}
connect (network_interfaces_combo_box_, &QComboBox::currentTextChanged, this, &MessageAggregatorMainWindow::validate_network_interfaces);
validate_network_interfaces (QString {});
log_table_view_->setModel (log_);
log_table_view_->verticalHeader ()->hide ();
central_layout->addWidget (log_table_view_);
@ -184,30 +243,52 @@ MessageAggregatorMainWindow::MessageAggregatorMainWindow ()
connect (server_, &MessageServer::client_closed, this, &MessageAggregatorMainWindow::remove_client);
connect (server_, &MessageServer::client_closed, decodes_model_, &DecodesModel::decodes_cleared);
connect (server_, &MessageServer::client_closed, beacons_model_, &BeaconsModel::decodes_cleared);
connect (server_, &MessageServer::decode, [this] (bool is_new, QString const& id, QTime time
connect (server_, &MessageServer::decode, [this] (bool is_new, ClientKey const& key, QTime time
, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode
, QString const& message, bool low_confidence
, bool off_air) {
decodes_model_->add_decode (is_new, id, time, snr, delta_time, delta_frequency, mode, message
, low_confidence, off_air, dock_widgets_[id]->fast_mode ());});
decodes_model_->add_decode (is_new, key, time, snr, delta_time
, delta_frequency, mode, message
, low_confidence, off_air
, dock_widgets_[key]->fast_mode ());
});
connect (server_, &MessageServer::WSPR_decode, beacons_model_, &BeaconsModel::add_beacon_spot);
connect (server_, &MessageServer::decodes_cleared, decodes_model_, &DecodesModel::decodes_cleared);
connect (server_, &MessageServer::decodes_cleared, beacons_model_, &BeaconsModel::decodes_cleared);
connect (decodes_model_, &DecodesModel::reply, server_, &MessageServer::reply);
// UI behaviour
connect (port_spin_box, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged)
, [this] (port_type port) {server_->start (port);});
connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this, port_spin_box] () {
server_->start (port_spin_box->value (), QHostAddress {multicast_group_line_edit_->text ()});
});
connect (port_spin_box_, static_cast<void (QSpinBox::*)(int)> (&QSpinBox::valueChanged)
, [this] (int /*port*/) {restart_server ();});
connect (multicast_group_line_edit_, &QLineEdit::editingFinished, [this] () {restart_server ();});
connect (network_interfaces_combo_box_, &QComboBox::currentTextChanged, [this] () {restart_server ();});
port_spin_box->setValue (2237); // start up in unicast mode
port_spin_box_->setValue (2237); // start up in unicast mode
show ();
}
void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call
void MessageAggregatorMainWindow::restart_server ()
{
QSet<QString> net_ifs;
if (network_interfaces_combo_box_->isVisible ())
{
auto model = static_cast<QStandardItemModel *> (network_interfaces_combo_box_->model ());
for (int row = 0; row < model->rowCount (); ++row)
{
if (Qt::Checked == model->item (row)->checkState ())
{
net_ifs << model->item (row)->data ().toString ();
}
}
}
server_->start (port_spin_box_->value ()
, QHostAddress {multicast_group_line_edit_->text ()}
, net_ifs);
}
void MessageAggregatorMainWindow::log_qso (ClientKey const& /*key*/, QDateTime time_off
, QString const& dx_call
, QString const& dx_grid, Frequency dial_frequency, QString const& mode
, QString const& report_sent, QString const& report_received
, QString const& tx_power, QString const& comments
@ -240,9 +321,9 @@ void MessageAggregatorMainWindow::log_qso (QString const& /*id*/, QDateTime time
log_table_view_->scrollToBottom ();
}
void MessageAggregatorMainWindow::add_client (QString const& id, QString const& version, QString const& revision)
void MessageAggregatorMainWindow::add_client (ClientKey const& key, QString const& version, QString const& revision)
{
auto dock = new ClientWidget {decodes_model_, beacons_model_, id, version, revision, calls_of_interest_, this};
auto dock = new ClientWidget {decodes_model_, beacons_model_, key, version, revision, calls_of_interest_, this};
dock->setAttribute (Qt::WA_DeleteOnClose);
auto view_action = dock->toggleViewAction ();
view_action->setEnabled (true);
@ -262,13 +343,13 @@ void MessageAggregatorMainWindow::add_client (QString const& id, QString const&
connect (dock, &ClientWidget::highlight_callsign, server_, &MessageServer::highlight_callsign);
connect (dock, &ClientWidget::switch_configuration, server_, &MessageServer::switch_configuration);
connect (dock, &ClientWidget::configure, server_, &MessageServer::configure);
dock_widgets_[id] = dock;
server_->replay (id); // request decodes and status
dock_widgets_[key] = dock;
server_->replay (key); // request decodes and status
}
void MessageAggregatorMainWindow::remove_client (QString const& id)
void MessageAggregatorMainWindow::remove_client (ClientKey const& key)
{
auto iter = dock_widgets_.find (id);
auto iter = dock_widgets_.find (key);
if (iter != std::end (dock_widgets_))
{
(*iter)->dispose ();
@ -287,9 +368,35 @@ MessageAggregatorMainWindow::~MessageAggregatorMainWindow ()
void MessageAggregatorMainWindow::change_highlighting (QString const& call, QColor const& bg, QColor const& fg
, bool last_only)
{
for (auto id : dock_widgets_.keys ())
for (auto key : dock_widgets_.keys ())
{
server_->highlight_callsign (id, call, bg, fg, last_only);
server_->highlight_callsign (key, call, bg, fg, last_only);
}
}
void MessageAggregatorMainWindow::validate_network_interfaces (QString const& /*text*/)
{
auto model = static_cast<QStandardItemModel *> (network_interfaces_combo_box_->model ());
bool has_checked {false};
int loopback_row {-1};
for (int row = 0; row < model->rowCount (); ++row)
{
if (model->item (row)->data ().toString () == loopback_interface_name_)
{
loopback_row = row;
}
else if (Qt::Checked == model->item (row)->checkState ())
{
has_checked = true;
}
}
if (loopback_row >= 0)
{
if (!has_checked)
{
model->item (loopback_row)->setCheckState (Qt::Checked);
}
model->item (loopback_row)->setEnabled (has_checked);
}
}

View File

@ -6,6 +6,7 @@
#include <QString>
#include "MessageServer.hpp"
#include "widgets/CheckableItemComboBox.hpp"
class QDateTime;
class QStandardItemModel;
@ -16,6 +17,8 @@ class QLineEdit;
class QTableView;
class ClientWidget;
class QListWidget;
class QLabel;
class QSpinBox;
using Frequency = MessageServer::Frequency;
@ -24,11 +27,13 @@ class MessageAggregatorMainWindow
{
Q_OBJECT;
using ClientKey = MessageServer::ClientKey;
public:
MessageAggregatorMainWindow ();
~MessageAggregatorMainWindow ();
Q_SLOT void log_qso (QString const& /*id*/, QDateTime time_off, QString const& dx_call, QString const& dx_grid
Q_SLOT void log_qso (ClientKey const&, QDateTime time_off, QString const& dx_call, QString const& dx_grid
, Frequency dial_frequency, QString const& mode, QString const& report_sent
, QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call
@ -36,13 +41,15 @@ public:
, QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode);
private:
void add_client (QString const& id, QString const& version, QString const& revision);
void remove_client (QString const& id);
void restart_server ();
void add_client (ClientKey const&, QString const& version, QString const& revision);
void remove_client (ClientKey const&);
void change_highlighting (QString const& call, QColor const& bg = QColor {}, QColor const& fg = QColor {},
bool last_only = false);
Q_SLOT void validate_network_interfaces (QString const&);
// maps client id to widgets
using ClientsDictionary = QHash<QString, ClientWidget *>;
using ClientsDictionary = QHash<ClientKey, ClientWidget *>;
ClientsDictionary dock_widgets_;
QStandardItemModel * log_;
@ -50,7 +57,11 @@ private:
DecodesModel * decodes_model_;
BeaconsModel * beacons_model_;
MessageServer * server_;
QSpinBox * port_spin_box_;
QLineEdit * multicast_group_line_edit_;
CheckableItemComboBox * network_interfaces_combo_box_;
QString loopback_interface_name_;
QLabel * network_interfaces_form_label_widget_;
QTableView * log_table_view_;
QListWidget * calls_of_interest_;
QAction * add_call_of_interest_action_;

View File

@ -5,7 +5,6 @@
#include <QNetworkInterface>
#include <QUdpSocket>
#include <QString>
#include <QTimer>
#include <QHash>
@ -32,7 +31,6 @@ public:
: self_ {self}
, version_ {version}
, revision_ {revision}
, port_ {0u}
, clock_ {new QTimer {this}}
{
// register the required types with Qt
@ -78,15 +76,14 @@ public:
MessageServer * self_;
QString version_;
QString revision_;
port_type port_;
QHostAddress multicast_group_address_;
QSet<QString> network_interfaces_;
static BindMode constexpr bind_mode_ = ShareAddress | ReuseAddressHint;
struct Client
{
Client () = default;
Client (QHostAddress const& sender_address, port_type const& sender_port)
: sender_address_ {sender_address}
, sender_port_ {sender_port}
Client (port_type const& sender_port)
: sender_port_ {sender_port}
, negotiated_schema_number_ {2} // not 1 because it's broken
, last_activity_ {QDateTime::currentDateTime ()}
{
@ -94,12 +91,11 @@ public:
Client (Client const&) = default;
Client& operator= (Client const&) = default;
QHostAddress sender_address_;
port_type sender_port_;
quint32 negotiated_schema_number_;
QDateTime last_activity_;
};
QHash<QString, Client> clients_; // maps id to Client
QHash<ClientKey, Client> clients_; // maps id to Client
QTimer * clock_;
};
@ -109,56 +105,39 @@ MessageServer::impl::BindMode constexpr MessageServer::impl::bind_mode_;
void MessageServer::impl::leave_multicast_group ()
{
if (!multicast_group_address_.isNull () && BoundState == state ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
if (BoundState == state () && is_multicast_address (multicast_group_address_))
{
for (auto const& interface : QNetworkInterface::allInterfaces ())
for (auto const& if_name : network_interfaces_)
{
if (QNetworkInterface::CanMulticast & interface.flags ())
{
leaveMulticastGroup (multicast_group_address_, interface);
}
leaveMulticastGroup (multicast_group_address_, QNetworkInterface::interfaceFromName (if_name));
}
}
}
void MessageServer::impl::join_multicast_group ()
{
if (BoundState == state ()
&& !multicast_group_address_.isNull ()
#if QT_VERSION >= 0x050600
&& multicast_group_address_.isMulticast ()
#endif
)
if (BoundState == state () && is_multicast_address (multicast_group_address_))
{
auto mcast_iface = multicastInterface ();
if (IPv4Protocol == multicast_group_address_.protocol ()
&& IPv4Protocol != localAddress ().protocol ())
if (network_interfaces_.size ())
{
close ();
bind (QHostAddress::AnyIPv4, port_, bind_mode_);
}
bool joined {false};
for (auto const& interface : QNetworkInterface::allInterfaces ())
{
if (QNetworkInterface::CanMulticast & interface.flags ())
for (auto const& if_name : network_interfaces_)
{
// Windows requires outgoing interface to match
// interface to be joined while joining, at least for
// IPv4 it seems to
setMulticastInterface (interface);
joined |= joinMulticastGroup (multicast_group_address_, interface);
joinMulticastGroup (multicast_group_address_, QNetworkInterface::interfaceFromName (if_name));
}
}
if (!joined)
else
{
multicast_group_address_.clear ();
// find the loop-back interface and join on that
for (auto const& net_if : QNetworkInterface::allInterfaces ())
{
auto flags = QNetworkInterface::IsUp | QNetworkInterface::IsLoopBack | QNetworkInterface::CanMulticast;
if ((net_if.flags () & flags) == flags)
{
joinMulticastGroup (multicast_group_address_, net_if);
break;
}
}
}
setMulticastInterface (mcast_iface);
}
}
@ -189,9 +168,10 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
auto id = in.id ();
if (OK == check_status (in))
{
if (!clients_.contains (id))
auto client_key = ClientKey {sender, id};
if (!clients_.contains (client_key))
{
auto& client = (clients_[id] = {sender, sender_port});
auto& client = (clients_[client_key] = {sender_port});
QByteArray client_version;
QByteArray client_revision;
@ -212,7 +192,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
<< version_.toUtf8 () << revision_.toUtf8 ();
if (impl::OK == check_status (hb))
{
writeDatagram (message, client.sender_address_, client.sender_port_);
writeDatagram (message, client_key.first, sender_port);
}
else
{
@ -222,10 +202,10 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
// we don't care if this fails to read
in >> client_version >> client_revision;
}
Q_EMIT self_->client_opened (id, QString::fromUtf8 (client_version),
Q_EMIT self_->client_opened (client_key, QString::fromUtf8 (client_version),
QString::fromUtf8 (client_revision));
}
clients_[id].last_activity_ = QDateTime::currentDateTime ();
clients_[client_key].last_activity_ = QDateTime::currentDateTime ();
//
// message format is described in NetworkMessage.hpp
@ -237,7 +217,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
break;
case NetworkMessage::Clear:
Q_EMIT self_->decodes_cleared (id);
Q_EMIT self_->decodes_cleared (client_key);
break;
case NetworkMessage::Status:
@ -268,7 +248,8 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
>> fast_mode >> special_op_mode >> frequency_tolerance >> tr_period >> configuration_name;
if (check_status (in) != Fail)
{
Q_EMIT self_->status_update (id, f, QString::fromUtf8 (mode), QString::fromUtf8 (dx_call)
Q_EMIT self_->status_update (client_key, f, QString::fromUtf8 (mode)
, QString::fromUtf8 (dx_call)
, QString::fromUtf8 (report), QString::fromUtf8 (tx_mode)
, tx_enabled, transmitting, decoding, rx_df, tx_df
, QString::fromUtf8 (de_call), QString::fromUtf8 (de_grid)
@ -296,7 +277,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
>> message >> low_confidence >> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->decode (is_new, id, time, snr, delta_time, delta_frequency
Q_EMIT self_->decode (is_new, client_key, time, snr, delta_time, delta_frequency
, QString::fromUtf8 (mode), QString::fromUtf8 (message)
, low_confidence, off_air);
}
@ -320,7 +301,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
>> off_air;
if (check_status (in) != Fail)
{
Q_EMIT self_->WSPR_decode (is_new, id, time, snr, delta_time, frequency, drift
Q_EMIT self_->WSPR_decode (is_new, client_key, time, snr, delta_time, frequency, drift
, QString::fromUtf8 (callsign), QString::fromUtf8 (grid)
, power, off_air);
}
@ -351,8 +332,10 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
>> exchange_sent >> exchange_rcvd >> prop_mode;
if (check_status (in) != Fail)
{
Q_EMIT self_->qso_logged (id, time_off, QString::fromUtf8 (dx_call), QString::fromUtf8 (dx_grid)
, dial_frequency, QString::fromUtf8 (mode), QString::fromUtf8 (report_sent)
Q_EMIT self_->qso_logged (client_key, time_off, QString::fromUtf8 (dx_call)
, QString::fromUtf8 (dx_grid)
, dial_frequency, QString::fromUtf8 (mode)
, QString::fromUtf8 (report_sent)
, QString::fromUtf8 (report_received), QString::fromUtf8 (tx_power)
, QString::fromUtf8 (comments), QString::fromUtf8 (name), time_on
, QString::fromUtf8 (operator_call), QString::fromUtf8 (my_call)
@ -363,8 +346,8 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
break;
case NetworkMessage::Close:
Q_EMIT self_->client_closed (id);
clients_.remove (id);
Q_EMIT self_->client_closed (client_key);
clients_.remove (client_key);
break;
case NetworkMessage::LoggedADIF:
@ -373,7 +356,7 @@ void MessageServer::impl::parse_message (QHostAddress const& sender, port_type s
in >> ADIF;
if (check_status (in) != Fail)
{
Q_EMIT self_->logged_ADIF (id, ADIF);
Q_EMIT self_->logged_ADIF (client_key, ADIF);
}
}
break;
@ -406,7 +389,7 @@ void MessageServer::impl::tick ()
{
if (now > (*iter).last_activity_.addSecs (NetworkMessage::pulse))
{
Q_EMIT self_->clear_decodes (iter.key ());
Q_EMIT self_->decodes_cleared (iter.key ());
Q_EMIT self_->client_closed (iter.key ());
iter = clients_.erase (iter); // safe while iterating as doesn't rehash
}
@ -448,152 +431,162 @@ MessageServer::MessageServer (QObject * parent, QString const& version, QString
{
}
void MessageServer::start (port_type port, QHostAddress const& multicast_group_address)
void MessageServer::start (port_type port, QHostAddress const& multicast_group_address
, QSet<QString> const& network_interface_names)
{
if (port != m_->port_
|| multicast_group_address != m_->multicast_group_address_)
// qDebug () << "MessageServer::start port:" << port << "multicast addr:" << multicast_group_address.toString () << "network interfaces:" << network_interface_names;
if (port != m_->localPort ()
|| multicast_group_address != m_->multicast_group_address_
|| network_interface_names != m_->network_interfaces_)
{
m_->leave_multicast_group ();
if (impl::BoundState == m_->state ())
if (impl::UnconnectedState != m_->state ())
{
m_->close ();
}
m_->multicast_group_address_ = multicast_group_address;
auto address = m_->multicast_group_address_.isNull ()
|| impl::IPv4Protocol != m_->multicast_group_address_.protocol () ? QHostAddress::Any : QHostAddress::AnyIPv4;
if (port && m_->bind (address, port, m_->bind_mode_))
if (!(multicast_group_address.isNull () || is_multicast_address (multicast_group_address)))
{
m_->port_ = port;
m_->join_multicast_group ();
Q_EMIT error ("Invalid multicast group address");
}
else if (is_MAC_ambiguous_multicast_address (multicast_group_address))
{
Q_EMIT error ("MAC-ambiguous IPv4 multicast group address not supported");
}
else
{
m_->port_ = 0;
m_->multicast_group_address_ = multicast_group_address;
m_->network_interfaces_ = network_interface_names;
QHostAddress local_addr {is_multicast_address (multicast_group_address)
&& impl::IPv4Protocol == multicast_group_address.protocol () ? QHostAddress::AnyIPv4 : QHostAddress::Any};
if (port && m_->bind (local_addr, port, m_->bind_mode_))
{
m_->join_multicast_group ();
}
}
}
}
void MessageServer::clear_decodes (QString const& id, quint8 window)
void MessageServer::clear_decodes (ClientKey const& key, quint8 window)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Clear, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::Clear, key.second, (*iter).negotiated_schema_number_};
out << window;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::reply (QString const& id, QTime time, qint32 snr, float delta_time
void MessageServer::reply (ClientKey const& key, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode
, QString const& message_text, bool low_confidence, quint8 modifiers)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Reply, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::Reply, key.second, (*iter).negotiated_schema_number_};
out << time << snr << delta_time << delta_frequency << mode.toUtf8 ()
<< message_text.toUtf8 () << low_confidence << modifiers;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::replay (QString const& id)
void MessageServer::replay (ClientKey const& key)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Replay, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
NetworkMessage::Builder out {&message, NetworkMessage::Replay, key.second, (*iter).negotiated_schema_number_};
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::close (QString const& id)
void MessageServer::close (ClientKey const& key)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Close, id, (*iter).negotiated_schema_number_};
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
NetworkMessage::Builder out {&message, NetworkMessage::Close, key.second, (*iter).negotiated_schema_number_};
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::halt_tx (QString const& id, bool auto_only)
void MessageServer::halt_tx (ClientKey const& key, bool auto_only)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::HaltTx, key.second, (*iter).negotiated_schema_number_};
out << auto_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::free_text (QString const& id, QString const& text, bool send)
void MessageServer::free_text (ClientKey const& key, QString const& text, bool send)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::FreeText, key.second, (*iter).negotiated_schema_number_};
out << text.toUtf8 () << send;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::location (QString const& id, QString const& loc)
void MessageServer::location (ClientKey const& key, QString const& loc)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Location, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::Location, key.second, (*iter).negotiated_schema_number_};
out << loc.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::highlight_callsign (QString const& id, QString const& callsign
void MessageServer::highlight_callsign (ClientKey const& key, QString const& callsign
, QColor const& bg, QColor const& fg, bool last_only)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::HighlightCallsign, key.second, (*iter).negotiated_schema_number_};
out << callsign.toUtf8 () << bg << fg << last_only;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::switch_configuration (QString const& id, QString const& configuration_name)
void MessageServer::switch_configuration (ClientKey const& key, QString const& configuration_name)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::SwitchConfiguration, key.second, (*iter).negotiated_schema_number_};
out << configuration_name.toUtf8 ();
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}
void MessageServer::configure (QString const& id, QString const& mode, quint32 frequency_tolerance
void MessageServer::configure (ClientKey const& key, QString const& mode, quint32 frequency_tolerance
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
, QString const& dx_call, QString const& dx_grid, bool generate_messages)
{
auto iter = m_->clients_.find (id);
auto iter = m_->clients_.find (key);
if (iter != std::end (m_->clients_))
{
QByteArray message;
NetworkMessage::Builder out {&message, NetworkMessage::Configure, id, (*iter).negotiated_schema_number_};
NetworkMessage::Builder out {&message, NetworkMessage::Configure, key.second, (*iter).negotiated_schema_number_};
out << mode.toUtf8 () << frequency_tolerance << submode.toUtf8 () << fast_mode << tr_period << rx_df
<< dx_call.toUtf8 () << dx_grid.toUtf8 () << generate_messages;
m_->send_message (out, message, iter.value ().sender_address_, (*iter).sender_port_);
m_->send_message (out, message, key.first, (*iter).sender_port_);
}
}

View File

@ -2,6 +2,9 @@
#define MESSAGE_SERVER_HPP__
#include <QObject>
#include <QPair>
#include <QString>
#include <QSet>
#include <QTime>
#include <QDateTime>
#include <QHostAddress>
@ -31,6 +34,7 @@ class UDP_EXPORT MessageServer
public:
using port_type = quint16;
using Frequency = Radio::Frequency;
using ClientKey = QPair<QHostAddress, QString>;
MessageServer (QObject * parent = nullptr,
QString const& version = QString {}, QString const& revision = QString {});
@ -38,77 +42,77 @@ public:
// start or restart the server, if the multicast_group_address
// argument is given it is assumed to be a multicast group address
// which the server will join
Q_SLOT void start (port_type port,
QHostAddress const& multicast_group_address = QHostAddress {});
Q_SLOT void start (port_type port
, QHostAddress const& multicast_group_address = QHostAddress {}
, QSet<QString> const& network_interface_names = QSet<QString> {});
// ask the client to clear one or both of the decode windows
Q_SLOT void clear_decodes (QString const& id, quint8 window = 0);
Q_SLOT void clear_decodes (ClientKey const&, quint8 window = 0);
// ask the client with identification 'id' to make the same action
// as a double click on the decode would
//
// note that the client is not obliged to take any action and only
// takes any action if the decode is present and is a CQ or QRZ message
Q_SLOT void reply (QString const& id, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
Q_SLOT void reply (ClientKey const&, QTime time, qint32 snr, float delta_time, quint32 delta_frequency
, QString const& mode, QString const& message, bool low_confidence, quint8 modifiers);
// ask the client with identification 'id' to close down gracefully
Q_SLOT void close (QString const& id);
// ask the client to close down gracefully
Q_SLOT void close (ClientKey const&);
// ask the client with identification 'id' to replay all decodes
Q_SLOT void replay (QString const& id);
// ask the client to replay all decodes
Q_SLOT void replay (ClientKey const&);
// ask the client with identification 'id' to halt transmitting
// auto_only just disables auto Tx, otherwise halt is immediate
Q_SLOT void halt_tx (QString const& id, bool auto_only);
// ask the client to halt transmitting auto_only just disables auto
// Tx, otherwise halt is immediate
Q_SLOT void halt_tx (ClientKey const&, bool auto_only);
// ask the client with identification 'id' to set the free text
// message and optionally send it ASAP
Q_SLOT void free_text (QString const& id, QString const& text, bool send);
// ask the client to set the free text message and optionally send
// it ASAP
Q_SLOT void free_text (ClientKey const&, QString const& text, bool send);
// ask the client with identification 'id' to set the location provided
Q_SLOT void location (QString const& id, QString const& location);
// ask the client to set the location provided
Q_SLOT void location (ClientKey const&, QString const& location);
// ask the client with identification 'id' to highlight the callsign
// specified with the given colors
Q_SLOT void highlight_callsign (QString const& id, QString const& callsign
// ask the client to highlight the callsign specified with the given
// colors
Q_SLOT void highlight_callsign (ClientKey const&, QString const& callsign
, QColor const& bg = QColor {}, QColor const& fg = QColor {}
, bool last_only = false);
// ask the client with identification 'id' to switch to
// configuration 'configuration_name'
Q_SLOT void switch_configuration (QString const& id, QString const& configuration_name);
// ask the client to switch to configuration 'configuration_name'
Q_SLOT void switch_configuration (ClientKey const&, QString const& configuration_name);
// ask the client with identification 'id' to change configuration
Q_SLOT void configure (QString const& id, QString const& mode, quint32 frequency_tolerance
// ask the client to change configuration
Q_SLOT void configure (ClientKey const&, QString const& mode, quint32 frequency_tolerance
, QString const& submode, bool fast_mode, quint32 tr_period, quint32 rx_df
, QString const& dx_call, QString const& dx_grid, bool generate_messages);
// the following signals are emitted when a client broadcasts the
// matching message
Q_SIGNAL void client_opened (QString const& id, QString const& version, QString const& revision);
Q_SIGNAL void status_update (QString const& id, Frequency, QString const& mode, QString const& dx_call
Q_SIGNAL void client_opened (ClientKey const&, QString const& version, QString const& revision);
Q_SIGNAL void status_update (ClientKey const&, Frequency, QString const& mode, QString const& dx_call
, QString const& report, QString const& tx_mode, bool tx_enabled
, bool transmitting, bool decoding, quint32 rx_df, quint32 tx_df
, QString const& de_call, QString const& de_grid, QString const& dx_grid
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode
, quint8 special_op_mode, quint32 frequency_tolerance, quint32 tr_period
, QString const& configuration_name);
Q_SIGNAL void client_closed (QString const& id);
Q_SIGNAL void decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time
Q_SIGNAL void client_closed (ClientKey const&);
Q_SIGNAL void decode (bool is_new, ClientKey const&, QTime time, qint32 snr, float delta_time
, quint32 delta_frequency, QString const& mode, QString const& message
, bool low_confidence, bool off_air);
Q_SIGNAL void WSPR_decode (bool is_new, QString const& id, QTime time, qint32 snr, float delta_time, Frequency
Q_SIGNAL void WSPR_decode (bool is_new, ClientKey const&, QTime time, qint32 snr, float delta_time, Frequency
, qint32 drift, QString const& callsign, QString const& grid, qint32 power
, bool off_air);
Q_SIGNAL void qso_logged (QString const& id, QDateTime time_off, QString const& dx_call, QString const& dx_grid
Q_SIGNAL void qso_logged (ClientKey const&, QDateTime time_off, QString const& dx_call, QString const& dx_grid
, Frequency dial_frequency, QString const& mode, QString const& report_sent
, QString const& report_received, QString const& tx_power, QString const& comments
, QString const& name, QDateTime time_on, QString const& operator_call
, QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode);
Q_SIGNAL void decodes_cleared (QString const& id);
Q_SIGNAL void logged_ADIF (QString const& id, QByteArray const& ADIF);
Q_SIGNAL void decodes_cleared (ClientKey const&);
Q_SIGNAL void logged_ADIF (ClientKey const&, QByteArray const& ADIF);
// this signal is emitted when a network error occurs
Q_SIGNAL void error (QString const&) const;
@ -118,4 +122,6 @@ private:
pimpl<impl> m_;
};
Q_DECLARE_METATYPE (MessageServer::ClientKey);
#endif

View File

@ -17,9 +17,14 @@
#include <iostream>
#include <exception>
#include <cstdlib>
#include <QCoreApplication>
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QString>
#include <QStringList>
#include <QNetworkInterface>
#include <QDateTime>
#include <QTime>
#include <QHash>
@ -38,15 +43,17 @@ class Client
{
Q_OBJECT
using ClientKey = MessageServer::ClientKey;
public:
explicit Client (QString const& id, QObject * parent = nullptr)
explicit Client (ClientKey const& key, QObject * parent = nullptr)
: QObject {parent}
, id_ {id}
, key_ {key}
, dial_frequency_ {0u}
{
}
Q_SLOT void update_status (QString const& id, Frequency f, QString const& mode, QString const& /*dx_call*/
Q_SLOT void update_status (ClientKey const& key, Frequency f, QString const& mode, QString const& /*dx_call*/
, QString const& /*report*/, QString const& /*tx_mode*/, bool /*tx_enabled*/
, bool /*transmitting*/, bool /*decoding*/, qint32 /*rx_df*/, qint32 /*tx_df*/
, QString const& /*de_call*/, QString const& /*de_grid*/, QString const& /*dx_grid*/
@ -54,77 +61,85 @@ public:
, quint8 /*special_op_mode*/, quint32 /*frequency_tolerance*/, quint32 /*tr_period*/
, QString const& /*configuration_name*/)
{
if (id == id_)
if (key == key_)
{
if (f != dial_frequency_)
{
std::cout << tr ("%1: Dial frequency changed to %2").arg (id_).arg (f).toStdString () << std::endl;
std::cout << QString {"%1(%2): "}.arg (key_.second).arg (key_.first.toString ()).toStdString ()
<< QString {"Dial frequency changed to %1"}.arg (f).toStdString () << std::endl;
dial_frequency_ = f;
}
if (mode + sub_mode != mode_)
{
std::cout << tr ("%1: Mode changed to %2").arg (id_).arg (mode + sub_mode).toStdString () << std::endl;
std::cout << QString {"%1(%2): "}.arg (key_.second).arg (key_.first.toString ()).toStdString ()
<< QString {"Mode changed to %1"}.arg (mode + sub_mode).toStdString () << std::endl;
mode_ = mode + sub_mode;
}
}
}
Q_SLOT void decode_added (bool is_new, QString const& client_id, QTime time, qint32 snr
Q_SLOT void decode_added (bool is_new, ClientKey const& key, QTime time, qint32 snr
, float delta_time, quint32 delta_frequency, QString const& mode
, QString const& message, bool low_confidence, bool off_air)
{
if (client_id == id_)
if (key == key_)
{
qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr
<< "Dt:" << delta_time << "Df:" << delta_frequency
<< "mode:" << mode << "Confidence:" << (low_confidence ? "low" : "high")
<< "On air:" << !off_air;
std::cout << tr ("%1: Decoded %2").arg (id_).arg (message).toStdString () << std::endl;
std::cout << QString {"%1(%2): "}.arg (key_.second).arg (key_.first.toString ()).toStdString ()
<< QString {"Decoded %1"}.arg (message).toStdString () << std::endl;
}
}
Q_SLOT void beacon_spot_added (bool is_new, QString const& client_id, QTime time, qint32 snr
Q_SLOT void beacon_spot_added (bool is_new, ClientKey const& key, QTime time, qint32 snr
, float delta_time, Frequency delta_frequency, qint32 drift, QString const& callsign
, QString const& grid, qint32 power, bool off_air)
{
if (client_id == id_)
if (key == key_)
{
qDebug () << "new:" << is_new << "t:" << time << "snr:" << snr
<< "Dt:" << delta_time << "Df:" << delta_frequency
<< "drift:" << drift;
std::cout << tr ("%1: WSPR decode %2 grid %3 power: %4").arg (id_).arg (callsign).arg (grid).arg (power).toStdString ()
std::cout << QString {"%1(%2): "}.arg (key_.second).arg (key_.first.toString ()).toStdString ()
<< QString {"WSPR decode %1 grid %2 power: %3"}
.arg (callsign).arg (grid).arg (power).toStdString ()
<< "On air:" << !off_air << std::endl;
}
}
Q_SLOT void qso_logged (QString const&client_id, QDateTime time_off, QString const& dx_call, QString const& dx_grid
Q_SLOT void qso_logged (ClientKey const& key, QDateTime time_off, QString const& dx_call, QString const& dx_grid
, Frequency dial_frequency, QString const& mode, QString const& report_sent
, QString const& report_received, QString const& tx_power
, QString const& comments, QString const& name, QDateTime time_on
, QString const& operator_call, QString const& my_call, QString const& my_grid
, QString const& exchange_sent, QString const& exchange_rcvd, QString const& prop_mode)
{
if (client_id == id_)
if (key == key_)
{
qDebug () << "time_on:" << time_on << "time_off:" << time_off << "dx_call:" << dx_call << "grid:" << dx_grid
qDebug () << "time_on:" << time_on << "time_off:" << time_off << "dx_call:"
<< dx_call << "grid:" << dx_grid
<< "freq:" << dial_frequency << "mode:" << mode << "rpt_sent:" << report_sent
<< "rpt_rcvd:" << report_received << "Tx_pwr:" << tx_power << "comments:" << comments
<< "name:" << name << "operator_call:" << operator_call << "my_call:" << my_call
<< "my_grid:" << my_grid << "exchange_sent:" << exchange_sent
<< "exchange_rcvd:" << exchange_rcvd << "prop_mode:" << prop_mode;
std::cout << QByteArray {80, '-'}.data () << '\n';
std::cout << tr ("%1: Logged %2 grid: %3 power: %4 sent: %5 recd: %6 freq: %7 time_off: %8 op: %9 my_call: %10 my_grid: %11 exchange_sent: %12 exchange_rcvd: %13 comments: %14 prop_mode: %15")
.arg (id_).arg (dx_call).arg (dx_grid).arg (tx_power).arg (report_sent).arg (report_received)
.arg (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call)
.arg (my_call).arg (my_grid).arg (exchange_sent).arg (exchange_rcvd)
.arg (comments).arg (prop_mode).toStdString ()
std::cout << QString {"%1(%2): "}.arg (key_.second).arg (key_.first.toString ()).toStdString ()
<< QString {"Logged %1 grid: %2 power: %3 sent: %4 recd: %5 freq: %6 time_off: %7 op: %8 my_call: %9 my_grid: %10 exchange_sent: %11 exchange_rcvd: %12 comments: %13 prop_mode: %14"}
.arg (dx_call).arg (dx_grid).arg (tx_power)
.arg (report_sent).arg (report_received)
.arg (dial_frequency).arg (time_off.toString("yyyy-MM-dd hh:mm:ss.z")).arg (operator_call)
.arg (my_call).arg (my_grid).arg (exchange_sent).arg (exchange_rcvd)
.arg (comments).arg (prop_mode).toStdString ()
<< std::endl;
}
}
Q_SLOT void logged_ADIF (QString const&client_id, QByteArray const& ADIF)
Q_SLOT void logged_ADIF (ClientKey const& key, QByteArray const& ADIF)
{
if (client_id == id_)
if (key == key_)
{
qDebug () << "ADIF:" << ADIF;
std::cout << QByteArray {80, '-'}.data () << '\n';
@ -133,7 +148,7 @@ public:
}
private:
QString id_;
ClientKey key_;
Frequency dial_frequency_;
QString mode_;
};
@ -143,8 +158,10 @@ class Server
{
Q_OBJECT
using ClientKey = MessageServer::ClientKey;
public:
Server (port_type port, QHostAddress const& multicast_group)
Server (port_type port, QHostAddress const& multicast_group, QStringList const& network_interface_names)
: server_ {new MessageServer {this}}
{
// connect up server
@ -154,21 +171,26 @@ public:
connect (server_, &MessageServer::client_opened, this, &Server::add_client);
connect (server_, &MessageServer::client_closed, this, &Server::remove_client);
server_->start (port, multicast_group);
#if QT_VERSION >= QT_VERSION_CHECK (5, 14, 0)
server_->start (port, multicast_group, QSet<QString> {network_interface_names.begin (), network_interface_names.end ()});
#else
server_->start (port, multicast_group, network_interface_names.toSet ());
#endif
}
private:
void add_client (QString const& id, QString const& version, QString const& revision)
void add_client (ClientKey const& key, QString const& version, QString const& revision)
{
auto client = new Client {id};
auto client = new Client {key};
connect (server_, &MessageServer::status_update, client, &Client::update_status);
connect (server_, &MessageServer::decode, client, &Client::decode_added);
connect (server_, &MessageServer::WSPR_decode, client, &Client::beacon_spot_added);
connect (server_, &MessageServer::qso_logged, client, &Client::qso_logged);
connect (server_, &MessageServer::logged_ADIF, client, &Client::logged_ADIF);
clients_[id] = client;
server_->replay (id);
std::cout << "Discovered WSJT-X instance: " << id.toStdString ();
clients_[key] = client;
server_->replay (key);
std::cout << "Discovered WSJT-X instance: " << key.second.toStdString ()
<< '(' << key.first.toString ().toStdString () << ')';
if (version.size ())
{
std::cout << " v" << version.toStdString ();
@ -180,23 +202,60 @@ private:
std::cout << std::endl;
}
void remove_client (QString const& id)
void remove_client (ClientKey const& key)
{
auto iter = clients_.find (id);
auto iter = clients_.find (key);
if (iter != std::end (clients_))
{
clients_.erase (iter);
(*iter)->deleteLater ();
}
std::cout << "Removed WSJT-X instance: " << id.toStdString () << std::endl;
std::cout << "Removed WSJT-X instance: " << key.second.toStdString ()
<< '(' << key.first.toString ().toStdString () << ')' << std::endl;
}
MessageServer * server_;
// maps client id to clients
QHash<QString, Client *> clients_;
// maps client key to clients
QHash<ClientKey, Client *> clients_;
};
void list_interfaces ()
{
for (auto const& net_if : QNetworkInterface::allInterfaces ())
{
if (net_if.flags () & QNetworkInterface::IsUp)
{
std::cout << net_if.humanReadableName ().toStdString () << ":\n"
" id: " << net_if.name ().toStdString () << " (" << net_if.index () << ")\n"
" addr: " << net_if.hardwareAddress ().toStdString () << "\n"
" flags: ";
if (net_if.flags () & QNetworkInterface::IsRunning)
{
std::cout << "Running ";
}
if (net_if.flags () & QNetworkInterface::CanBroadcast)
{
std::cout << "Broadcast ";
}
if (net_if.flags () & QNetworkInterface::CanMulticast)
{
std::cout << "Multicast ";
}
if (net_if.flags () & QNetworkInterface::IsLoopBack)
{
std::cout << "Loop-back ";
}
std::cout << "\n addresses:\n";
for (auto const& ae : net_if.addressEntries ())
{
std::cout << " " << ae.ip ().toString ().toStdString () << '\n';
}
std::cout << '\n';
}
}
}
#include "UDPDaemon.moc"
int main (int argc, char * argv[])
@ -217,6 +276,11 @@ int main (int argc, char * argv[])
auto help_option = parser.addHelpOption ();
auto version_option = parser.addVersionOption ();
QCommandLineOption list_option (QStringList {"l", "list-interfaces"},
app.translate ("UDPDaemon",
"Print the available network interfaces."));
parser.addOption (list_option);
QCommandLineOption port_option (QStringList {"p", "port"},
app.translate ("UDPDaemon",
"Where <PORT> is the UDP service port number to listen on.\n"
@ -232,9 +296,25 @@ int main (int argc, char * argv[])
app.translate ("UDPDaemon", "GROUP"));
parser.addOption (multicast_addr_option);
QCommandLineOption network_interface_option (QStringList {"i", "network-interface"},
app.translate ("UDPDaemon",
"Where <INTERFACE> is the network interface name to join on.\n"
"This option can be passed more than once to specify multiple network interfaces\n"
"The default is use just the loop back interface."),
app.translate ("UDPDaemon", "INTERFACE"));
parser.addOption (network_interface_option);
parser.process (app);
Server server {static_cast<port_type> (parser.value (port_option).toUInt ()), QHostAddress {parser.value (multicast_addr_option)}};
if (parser.isSet (list_option))
{
list_interfaces ();
return EXIT_SUCCESS;
}
Server server {static_cast<port_type> (parser.value (port_option).toUInt ())
, QHostAddress {parser.value (multicast_addr_option).trimmed ()}
, parser.values (network_interface_option)};
return app.exec ();
}

240
WSJTXLogging.cpp Normal file
View File

@ -0,0 +1,240 @@
#include "WSJTXLogging.hpp"
#include <string>
#include <exception>
#include <sstream>
#include <boost/version.hpp>
#include <boost/log/core.hpp>
#include <boost/log/utility/exception_handler.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/sinks/async_frontend.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/expressions/formatters/date_time.hpp>
#include <boost/log/expressions/predicates/channel_severity_filter.hpp>
#include <boost/log/support/date_time.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/date_time/gregorian/greg_day.hpp>
#include <boost/container/flat_map.hpp>
#include <QtGlobal>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QString>
#include <QStandardPaths>
#include <QRegularExpression>
#include <QMessageLogContext>
#include "Logger.hpp"
#include "qt_helpers.hpp"
namespace logging = boost::log;
namespace trivial = logging::trivial;
namespace keywords = logging::keywords;
namespace expr = logging::expressions;
namespace sinks = logging::sinks;
namespace posix_time = boost::posix_time;
namespace gregorian = boost::gregorian;
namespace container = boost::container;
BOOST_LOG_ATTRIBUTE_KEYWORD(severity, "Severity", trivial::severity_level)
BOOST_LOG_ATTRIBUTE_KEYWORD(channel, "Channel", std::string)
namespace
{
// Top level exception handler that gets exceptions from filters and
// formatters.
struct exception_handler
{
typedef void result;
void operator () (std::runtime_error const& e) const
{
std::cout << "std::runtime_error: " << e.what () << std::endl;
}
void operator () (std::logic_error const& e) const
{
std::cout << "std::logic_error: " << e.what () << std::endl;
//throw;
}
};
// Reroute Qt messages to the system logger
void qt_log_handler (QtMsgType type, QMessageLogContext const& context, QString const& msg)
{
// Convert Qt message types to logger severities
auto severity = trivial::trace;
switch (type)
{
case QtDebugMsg: severity = trivial::debug; break;
case QtInfoMsg: severity = trivial::info; break;
case QtWarningMsg: severity = trivial::warning; break;
case QtCriticalMsg: severity = trivial::error; break;
case QtFatalMsg: severity = trivial::fatal; break;
}
// Map non-default Qt categories to logger channels, Qt logger
// context is mapped to the appropriate logger attributes.
auto log = sys::get ();
std::string file;
std::string function;
if (context.file)
{
file = context.file;
}
if (context.function)
{
function = context.function;
}
if (!context.category || !qstrcmp (context.category, "default"))
{
BOOST_LOG_SEV (log, severity)
<< boost::log::add_value ("Line", context.line)
<< boost::log::add_value ("File", file)
<< boost::log::add_value ("Function", function)
<< msg.toStdWString ();
}
else
{
BOOST_LOG_SEV (log, severity)
<< boost::log::add_value ("Line", context.line)
<< boost::log::add_value ("File", file)
<< boost::log::add_value ("Function", function)
<< context.category << ": " << msg.toStdWString ();
}
if (QtFatalMsg == type)
{
// bail out
throw std::runtime_error {"Fatal Qt Error"};
}
}
}
WSJTXLogging::WSJTXLogging ()
{
auto core = logging::core::get ();
// Catch relevant exceptions from logging.
core->set_exception_handler
(
logging::make_exception_handler<std::runtime_error, std::logic_error> (exception_handler {})
);
// Check for a user-defined logging configuration settings file.
QFile log_config {QStandardPaths::locate (QStandardPaths::ConfigLocation, "wsjtx_log_config.ini")};
if (log_config.exists () && log_config.open (QFile::ReadOnly) && log_config.isReadable ())
{
QTextStream ts {&log_config};
auto config = ts.readAll ();
// Substitution variables.
container::flat_map<QString, QString> replacements =
{
{"DesktopLocation", QStandardPaths::writableLocation (QStandardPaths::DesktopLocation)},
{"DocumentsLocation", QStandardPaths::writableLocation (QStandardPaths::DocumentsLocation)},
{"TempLocation", QStandardPaths::writableLocation (QStandardPaths::TempLocation)},
{"HomeLocation", QStandardPaths::writableLocation (QStandardPaths::HomeLocation)},
{"CacheLocation", QStandardPaths::writableLocation (QStandardPaths::CacheLocation)},
{"GenericCacheLocation", QStandardPaths::writableLocation (QStandardPaths::GenericCacheLocation)},
{"GenericDataLocation", QStandardPaths::writableLocation (QStandardPaths::GenericDataLocation)},
{"AppDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)},
{"AppLocalDataLocation", QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)},
};
// Parse the configration settings substituting the variable if found.
QString new_config;
int pos {0};
QRegularExpression subst_vars {R"(\${([^}]+)})"};
auto iter = subst_vars.globalMatch (config);
while (iter.hasNext ())
{
auto match = iter.next ();
auto const& name = match.captured (1);
auto repl_iter = replacements.find (name);
auto repl = repl_iter != replacements.end () ? repl_iter->second : "${" + name + "}";
new_config += config.mid (pos, match.capturedStart (1) - 2 - pos) + repl;
pos = match.capturedEnd (0);
}
new_config += config.mid (pos);
std::wstringbuf buffer {new_config.toStdWString (), std::ios_base::in};
std::wistream stream {&buffer};
Logger::init_from_config (stream);
LOG_INFO ("Read logging configuration file: " << log_config.fileName ());
}
else // Default setup
{
//
// Define sinks, filters, and formatters using expression
// templates for efficiency.
//
// Default log file location.
QDir app_data {QStandardPaths::writableLocation (QStandardPaths::AppLocalDataLocation)};
Logger::init (); // Basic setup of attributes
//
// Sink intended for general use that passes everything above
// selected severity levels per channel. Log file is appended
// between sessions and rotated to limit storage space usage.
//
auto sys_sink = boost::make_shared<sinks::asynchronous_sink<sinks::text_file_backend>>
(
keywords::auto_flush = false
#if BOOST_VERSION / 100 >= 1070
, keywords::file_name = app_data.absoluteFilePath ("wsjtx_syslog.log").toStdWString ()
, keywords::target_file_name =
#else
, keywords::file_name =
#endif
app_data.absoluteFilePath ("logs/wsjtx_syslog_%Y-%m.log").toStdString ()
, keywords::time_based_rotation = sinks::file::rotation_at_time_point (gregorian::greg_day (1), 0, 0, 0)
, keywords::open_mode = std::ios_base::out | std::ios_base::app
#if BOOST_VERSION / 100 >= 1063
, keywords::enable_final_rotation = false
#endif
);
sys_sink->locked_backend ()->set_file_collector
(
sinks::file::make_collector
(
keywords::max_size = 40 * 1024 * 1024
, keywords::min_free_space = 1024 * 1024 * 1024
, keywords::max_files = 12
, keywords::target = app_data.absoluteFilePath ("logs").toStdWString ()
)
);
sys_sink->locked_backend ()->scan_for_files ();
// Per channel severity level filter
using min_severity_filter = expr::channel_severity_filter_actor<std::string, trivial::severity_level>;
min_severity_filter min_severity = expr::channel_severity_filter (channel, severity);
min_severity["SYSLOG"] = trivial::info;
min_severity["RIGCTRL"] = trivial::info;
min_severity["DATALOG"] = trivial::info;
sys_sink->set_filter (min_severity || severity >= trivial::fatal);
sys_sink->set_formatter
(
expr::stream
<< "[" << channel
<< "][" << expr::format_date_time<posix_time::ptime> ("TimeStamp", "%Y-%m-%d %H:%M:%S.%f")
<< "][" << expr::format_date_time<posix_time::time_duration> ("Uptime", "%O:%M:%S.%f")
<< "][" << trivial::severity
<< "] " << expr::message
);
core->add_sink (sys_sink);
}
// Indicate start of logging
LOG_INFO ("Log Start");
::qInstallMessageHandler (&qt_log_handler);
}
WSJTXLogging::~WSJTXLogging ()
{
LOG_INFO ("Log Finish");
auto core = logging::core::get ();
core->flush ();
core->remove_all_sinks ();
}

14
WSJTXLogging.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef WSJTX_LOGGING_HPP__
#define WSJTX_LOGGING_HPP__
//
// Class WSJTXLogging - wraps application specific logging
//
class WSJTXLogging final
{
public:
explicit WSJTXLogging ();
~WSJTXLogging ();
};
#endif

View File

@ -1,40 +0,0 @@
Boost "vendor" repo for K1JT projects
=====================================
This repository contains a subset of the Boost project with libraries
needed by K1JT project s such as WSJT-X. It contains two branches,
upstream and master. To upgrade the content do the following:
```bash
git checkout upstream
mv README.md /tmp
rm -r *
mv /tmp/README.md .
# use the bcp tool to populate with the new Boost libraries from a clean boost install.
# Something like:
#
# bcp --boost=../boost_1_70_0 --unix-lines iterator range math numeric crc circular_buffer multi_index intrusive .
#
# Clean out any unwanted files and directories (e.g. libs and docs for a header only subset).
# Use git add to stage any new files and directories.
git commit -a -m "Updated Boost v1.70.0 libraries including ..."
git tag boost_1_70_0
git push origin
git checkout master
git merge upstream
git push origin
```
The resulting master branch is now ready to be git-subtree merged into
any projects that need these libraries.
This is imported here using git-subtree
---------------------------------------
To update this tree when the upstream Boost libraries are updated use
git-subtree-pull to import the changes like this:
```bash
git remote add -f boost git@bitbucket.org:g4wjs/boost.git # for convienence
git subtree pull --prefix boost boost master --squash
```

View File

@ -1,83 +0,0 @@
/*
Copyright (c) Marshall Clow 2008-2012.
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
*/
/// \file all_of.hpp
/// \brief Test ranges to see if all elements match a value or predicate.
/// \author Marshall Clow
#ifndef BOOST_ALGORITHM_ALL_OF_HPP
#define BOOST_ALGORITHM_ALL_OF_HPP
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
namespace boost { namespace algorithm {
/// \fn all_of ( InputIterator first, InputIterator last, Predicate p )
/// \return true if all elements in [first, last) satisfy the predicate 'p'
/// \note returns true on an empty range
///
/// \param first The start of the input sequence
/// \param last One past the end of the input sequence
/// \param p A predicate for testing the elements of the sequence
///
/// \note This function is part of the C++2011 standard library.
template<typename InputIterator, typename Predicate>
BOOST_CXX14_CONSTEXPR bool all_of ( InputIterator first, InputIterator last, Predicate p )
{
for ( ; first != last; ++first )
if ( !p(*first))
return false;
return true;
}
/// \fn all_of ( const Range &r, Predicate p )
/// \return true if all elements in the range satisfy the predicate 'p'
/// \note returns true on an empty range
///
/// \param r The input range
/// \param p A predicate for testing the elements of the range
///
template<typename Range, typename Predicate>
BOOST_CXX14_CONSTEXPR bool all_of ( const Range &r, Predicate p )
{
return boost::algorithm::all_of ( boost::begin (r), boost::end (r), p );
}
/// \fn all_of_equal ( InputIterator first, InputIterator last, const T &val )
/// \return true if all elements in [first, last) are equal to 'val'
/// \note returns true on an empty range
///
/// \param first The start of the input sequence
/// \param last One past the end of the input sequence
/// \param val A value to compare against
///
template<typename InputIterator, typename T>
BOOST_CXX14_CONSTEXPR bool all_of_equal ( InputIterator first, InputIterator last, const T &val )
{
for ( ; first != last; ++first )
if ( val != *first )
return false;
return true;
}
/// \fn all_of_equal ( const Range &r, const T &val )
/// \return true if all elements in the range are equal to 'val'
/// \note returns true on an empty range
///
/// \param r The input range
/// \param val A value to compare against
///
template<typename Range, typename T>
BOOST_CXX14_CONSTEXPR bool all_of_equal ( const Range &r, const T &val )
{
return boost::algorithm::all_of_equal ( boost::begin (r), boost::end (r), val );
}
}} // namespace boost and algorithm
#endif // BOOST_ALGORITHM_ALL_OF_HPP

View File

@ -1,553 +0,0 @@
// (C) Copyright Herve Bronnimann 2004.
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
/*
Revision history:
1 July 2004
Split the code into two headers to lessen dependence on
Boost.tuple. (Herve)
26 June 2004
Added the code for the boost minmax library. (Herve)
*/
#ifndef BOOST_ALGORITHM_MINMAX_ELEMENT_HPP
#define BOOST_ALGORITHM_MINMAX_ELEMENT_HPP
/* PROPOSED STANDARD EXTENSIONS:
*
* minmax_element(first, last)
* Effect: std::make_pair( std::min_element(first, last),
* std::max_element(first, last) );
*
* minmax_element(first, last, comp)
* Effect: std::make_pair( std::min_element(first, last, comp),
* std::max_element(first, last, comp) );
*/
#include <utility> // for std::pair and std::make_pair
namespace boost {
namespace detail { // for obtaining a uniform version of minmax_element
// that compiles with VC++ 6.0 -- avoid the iterator_traits by
// having comparison object over iterator, not over dereferenced value
template <typename Iterator>
struct less_over_iter {
bool operator()(Iterator const& it1,
Iterator const& it2) const { return *it1 < *it2; }
};
template <typename Iterator, class BinaryPredicate>
struct binary_pred_over_iter {
explicit binary_pred_over_iter(BinaryPredicate const& p ) : m_p( p ) {}
bool operator()(Iterator const& it1,
Iterator const& it2) const { return m_p(*it1, *it2); }
private:
BinaryPredicate m_p;
};
// common base for the two minmax_element overloads
template <typename ForwardIter, class Compare >
std::pair<ForwardIter,ForwardIter>
basic_minmax_element(ForwardIter first, ForwardIter last, Compare comp)
{
if (first == last)
return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
// if only one element
ForwardIter second = first; ++second;
if (second == last)
return std::make_pair(min_result, max_result);
// treat first pair separately (only one comparison for first two elements)
ForwardIter potential_min_result = last;
if (comp(first, second))
max_result = second;
else {
min_result = second;
potential_min_result = first;
}
// then each element by pairs, with at most 3 comparisons per pair
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (comp(first, min_result)) {
min_result = first;
potential_min_result = last;
}
if (comp(max_result, second))
max_result = second;
} else {
if (comp(second, min_result)) {
min_result = second;
potential_min_result = first;
}
if (comp(max_result, first))
max_result = first;
}
first = ++second;
if (first != last) ++second;
}
// if odd number of elements, treat last element
if (first != last) { // odd number of elements
if (comp(first, min_result)) {
min_result = first;
potential_min_result = last;
}
else if (comp(max_result, first))
max_result = first;
}
// resolve min_result being incorrect with one extra comparison
// (in which case potential_min_result is necessarily the correct result)
if (potential_min_result != last
&& !comp(min_result, potential_min_result))
min_result = potential_min_result;
return std::make_pair(min_result,max_result);
}
} // namespace detail
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
minmax_element(ForwardIter first, ForwardIter last)
{
return detail::basic_minmax_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
minmax_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_minmax_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
}
/* PROPOSED BOOST EXTENSIONS
* In the description below, [rfirst,rlast) denotes the reversed range
* of [first,last). Even though the iterator type of first and last may
* be only a Forward Iterator, it is possible to explain the semantics
* by assuming that it is a Bidirectional Iterator. In the sequel,
* reverse(ForwardIterator&) returns the reverse_iterator adaptor.
* This is not how the functions would be implemented!
*
* first_min_element(first, last)
* Effect: std::min_element(first, last);
*
* first_min_element(first, last, comp)
* Effect: std::min_element(first, last, comp);
*
* last_min_element(first, last)
* Effect: reverse( std::min_element(reverse(last), reverse(first)) );
*
* last_min_element(first, last, comp)
* Effect: reverse( std::min_element(reverse(last), reverse(first), comp) );
*
* first_max_element(first, last)
* Effect: std::max_element(first, last);
*
* first_max_element(first, last, comp)
* Effect: max_element(first, last);
*
* last_max_element(first, last)
* Effect: reverse( std::max_element(reverse(last), reverse(first)) );
*
* last_max_element(first, last, comp)
* Effect: reverse( std::max_element(reverse(last), reverse(first), comp) );
*
* first_min_first_max_element(first, last)
* Effect: std::make_pair( first_min_element(first, last),
* first_max_element(first, last) );
*
* first_min_first_max_element(first, last, comp)
* Effect: std::make_pair( first_min_element(first, last, comp),
* first_max_element(first, last, comp) );
*
* first_min_last_max_element(first, last)
* Effect: std::make_pair( first_min_element(first, last),
* last_max_element(first, last) );
*
* first_min_last_max_element(first, last, comp)
* Effect: std::make_pair( first_min_element(first, last, comp),
* last_max_element(first, last, comp) );
*
* last_min_first_max_element(first, last)
* Effect: std::make_pair( last_min_element(first, last),
* first_max_element(first, last) );
*
* last_min_first_max_element(first, last, comp)
* Effect: std::make_pair( last_min_element(first, last, comp),
* first_max_element(first, last, comp) );
*
* last_min_last_max_element(first, last)
* Effect: std::make_pair( last_min_element(first, last),
* last_max_element(first, last) );
*
* last_min_last_max_element(first, last, comp)
* Effect: std::make_pair( last_min_element(first, last, comp),
* last_max_element(first, last, comp) );
*/
namespace boost {
// Min_element and max_element variants
namespace detail { // common base for the overloads
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_first_min_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter min_result = first;
while (++first != last)
if (comp(first, min_result))
min_result = first;
return min_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_last_min_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter min_result = first;
while (++first != last)
if (!comp(min_result, first))
min_result = first;
return min_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter max_result = first;
while (++first != last)
if (comp(max_result, first))
max_result = first;
return max_result;
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
basic_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return last;
ForwardIter max_result = first;
while (++first != last)
if (!comp(first, max_result))
max_result = first;
return max_result;
}
} // namespace detail
template <typename ForwardIter>
ForwardIter
first_min_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_min_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
first_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_first_min_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
last_min_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
last_min_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_last_min_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
first_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
first_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_first_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
ForwardIter
last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
ForwardIter
last_max_element(ForwardIter first, ForwardIter last, BinaryPredicate comp)
{
return detail::basic_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
// Minmax_element variants -- comments removed
namespace detail {
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_first_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last)
return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = ++first;
if (second == last)
return std::make_pair(min_result, max_result);
if (comp(second, min_result))
min_result = second;
else
max_result = second;
first = ++second; if (first != last) ++second;
while (second != last) {
if (!comp(second, first)) {
if (comp(first, min_result))
min_result = first;
if (!comp(second, max_result))
max_result = second;
} else {
if (comp(second, min_result))
min_result = second;
if (!comp(first, max_result))
max_result = first;
}
first = ++second; if (first != last) ++second;
}
if (first != last) {
if (comp(first, min_result))
min_result = first;
else if (!comp(first, max_result))
max_result = first;
}
return std::make_pair(min_result, max_result);
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_last_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = ++first;
if (second == last)
return std::make_pair(min_result, max_result);
if (comp(max_result, second))
max_result = second;
else
min_result = second;
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (!comp(min_result, first))
min_result = first;
if (comp(max_result, second))
max_result = second;
} else {
if (!comp(min_result, second))
min_result = second;
if (comp(max_result, first))
max_result = first;
}
first = ++second; if (first != last) ++second;
}
if (first != last) {
if (!comp(min_result, first))
min_result = first;
else if (comp(max_result, first))
max_result = first;
}
return std::make_pair(min_result, max_result);
}
template <typename ForwardIter, class BinaryPredicate>
std::pair<ForwardIter,ForwardIter>
basic_last_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
if (first == last) return std::make_pair(last,last);
ForwardIter min_result = first;
ForwardIter max_result = first;
ForwardIter second = first; ++second;
if (second == last)
return std::make_pair(min_result,max_result);
ForwardIter potential_max_result = last;
if (comp(first, second))
max_result = second;
else {
min_result = second;
potential_max_result = second;
}
first = ++second; if (first != last) ++second;
while (second != last) {
if (comp(first, second)) {
if (!comp(min_result, first))
min_result = first;
if (!comp(second, max_result)) {
max_result = second;
potential_max_result = last;
}
} else {
if (!comp(min_result, second))
min_result = second;
if (!comp(first, max_result)) {
max_result = first;
potential_max_result = second;
}
}
first = ++second;
if (first != last) ++second;
}
if (first != last) {
if (!comp(min_result, first))
min_result = first;
if (!comp(first, max_result)) {
max_result = first;
potential_max_result = last;
}
}
if (potential_max_result != last
&& !comp(potential_max_result, max_result))
max_result = potential_max_result;
return std::make_pair(min_result,max_result);
}
} // namespace detail
template <typename ForwardIter>
inline std::pair<ForwardIter,ForwardIter>
first_min_first_max_element(ForwardIter first, ForwardIter last)
{
return minmax_element(first, last);
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
first_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return minmax_element(first, last, comp);
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
first_min_last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_first_min_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
first_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_first_min_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
last_min_first_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_first_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
last_min_first_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_last_min_first_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
template <typename ForwardIter>
std::pair<ForwardIter,ForwardIter>
last_min_last_max_element(ForwardIter first, ForwardIter last)
{
return detail::basic_last_min_last_max_element(first, last,
detail::less_over_iter<ForwardIter>() );
}
template <typename ForwardIter, class BinaryPredicate>
inline std::pair<ForwardIter,ForwardIter>
last_min_last_max_element(ForwardIter first, ForwardIter last,
BinaryPredicate comp)
{
return detail::basic_last_min_last_max_element(first, last,
detail::binary_pred_over_iter<ForwardIter,BinaryPredicate>(comp) );
}
} // namespace boost
#endif // BOOST_ALGORITHM_MINMAX_ELEMENT_HPP

View File

@ -1,31 +0,0 @@
// Boost string_algo library string_algo.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2004.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_ALGO_HPP
#define BOOST_STRING_ALGO_HPP
/*! \file
Cumulative include for string_algo library
*/
#include <boost/algorithm/string/std_containers_traits.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/erase.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/find_iterator.hpp>
#endif // BOOST_STRING_ALGO_HPP

View File

@ -1,176 +0,0 @@
// Boost string_algo library case_conv.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CASE_CONV_HPP
#define BOOST_STRING_CASE_CONV_HPP
#include <boost/algorithm/string/config.hpp>
#include <algorithm>
#include <locale>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/value_type.hpp>
#include <boost/algorithm/string/detail/case_conv.hpp>
/*! \file
Defines sequence case-conversion algorithms.
Algorithms convert each element in the input sequence to the
desired case using provided locales.
*/
namespace boost {
namespace algorithm {
// to_lower -----------------------------------------------//
//! Convert to lower case
/*!
Each element of the input sequence is converted to lower
case. The result is a copy of the input converted to lower case.
It is returned as a sequence or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input range
\param Loc A locale used for conversion
\return
An output iterator pointing just after the last inserted character or
a copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<typename OutputIteratorT, typename RangeT>
inline OutputIteratorT
to_lower_copy(
OutputIteratorT Output,
const RangeT& Input,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::detail::transform_range_copy(
Output,
::boost::as_literal(Input),
::boost::algorithm::detail::to_lowerF<
typename range_value<RangeT>::type >(Loc));
}
//! Convert to lower case
/*!
\overload
*/
template<typename SequenceT>
inline SequenceT to_lower_copy(
const SequenceT& Input,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::detail::transform_range_copy<SequenceT>(
Input,
::boost::algorithm::detail::to_lowerF<
typename range_value<SequenceT>::type >(Loc));
}
//! Convert to lower case
/*!
Each element of the input sequence is converted to lower
case. The input sequence is modified in-place.
\param Input A range
\param Loc a locale used for conversion
*/
template<typename WritableRangeT>
inline void to_lower(
WritableRangeT& Input,
const std::locale& Loc=std::locale())
{
::boost::algorithm::detail::transform_range(
::boost::as_literal(Input),
::boost::algorithm::detail::to_lowerF<
typename range_value<WritableRangeT>::type >(Loc));
}
// to_upper -----------------------------------------------//
//! Convert to upper case
/*!
Each element of the input sequence is converted to upper
case. The result is a copy of the input converted to upper case.
It is returned as a sequence or copied to the output iterator
\param Output An output iterator to which the result will be copied
\param Input An input range
\param Loc A locale used for conversion
\return
An output iterator pointing just after the last inserted character or
a copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<typename OutputIteratorT, typename RangeT>
inline OutputIteratorT
to_upper_copy(
OutputIteratorT Output,
const RangeT& Input,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::detail::transform_range_copy(
Output,
::boost::as_literal(Input),
::boost::algorithm::detail::to_upperF<
typename range_value<RangeT>::type >(Loc));
}
//! Convert to upper case
/*!
\overload
*/
template<typename SequenceT>
inline SequenceT to_upper_copy(
const SequenceT& Input,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::detail::transform_range_copy<SequenceT>(
Input,
::boost::algorithm::detail::to_upperF<
typename range_value<SequenceT>::type >(Loc));
}
//! Convert to upper case
/*!
Each element of the input sequence is converted to upper
case. The input sequence is modified in-place.
\param Input An input range
\param Loc a locale used for conversion
*/
template<typename WritableRangeT>
inline void to_upper(
WritableRangeT& Input,
const std::locale& Loc=std::locale())
{
::boost::algorithm::detail::transform_range(
::boost::as_literal(Input),
::boost::algorithm::detail::to_upperF<
typename range_value<WritableRangeT>::type >(Loc));
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::to_lower;
using algorithm::to_lower_copy;
using algorithm::to_upper;
using algorithm::to_upper_copy;
} // namespace boost
#endif // BOOST_STRING_CASE_CONV_HPP

View File

@ -1,312 +0,0 @@
// Boost string_algo library classification.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CLASSIFICATION_HPP
#define BOOST_STRING_CLASSIFICATION_HPP
#include <algorithm>
#include <locale>
#include <boost/range/value_type.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/detail/classification.hpp>
#include <boost/algorithm/string/predicate_facade.hpp>
/*! \file
Classification predicates are included in the library to give
some more convenience when using algorithms like \c trim() and \c all().
They wrap functionality of STL classification functions ( e.g. \c std::isspace() )
into generic functors.
*/
namespace boost {
namespace algorithm {
// classification functor generator -------------------------------------//
//! is_classified predicate
/*!
Construct the \c is_classified predicate. This predicate holds if the input is
of specified \c std::ctype category.
\param Type A \c std::ctype category
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_classified(std::ctype_base::mask Type, const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(Type, Loc);
}
//! is_space predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::space category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_space(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::space, Loc);
}
//! is_alnum predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::alnum category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_alnum(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::alnum, Loc);
}
//! is_alpha predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::alpha category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_alpha(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::alpha, Loc);
}
//! is_cntrl predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::cntrl category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_cntrl(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::cntrl, Loc);
}
//! is_digit predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::digit category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_digit(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::digit, Loc);
}
//! is_graph predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::graph category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_graph(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::graph, Loc);
}
//! is_lower predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::lower category.
\param Loc A locale used for classification
\return An instance of \c is_classified predicate
*/
inline detail::is_classifiedF
is_lower(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::lower, Loc);
}
//! is_print predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::print category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_print(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::print, Loc);
}
//! is_punct predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::punct category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_punct(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::punct, Loc);
}
//! is_upper predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::upper category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_upper(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::upper, Loc);
}
//! is_xdigit predicate
/*!
Construct the \c is_classified predicate for the \c ctype_base::xdigit category.
\param Loc A locale used for classification
\return An instance of the \c is_classified predicate
*/
inline detail::is_classifiedF
is_xdigit(const std::locale& Loc=std::locale())
{
return detail::is_classifiedF(std::ctype_base::xdigit, Loc);
}
//! is_any_of predicate
/*!
Construct the \c is_any_of predicate. The predicate holds if the input
is included in the specified set of characters.
\param Set A set of characters to be recognized
\return An instance of the \c is_any_of predicate
*/
template<typename RangeT>
inline detail::is_any_ofF<
BOOST_STRING_TYPENAME range_value<RangeT>::type>
is_any_of( const RangeT& Set )
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> lit_set(boost::as_literal(Set));
return detail::is_any_ofF<BOOST_STRING_TYPENAME range_value<RangeT>::type>(lit_set);
}
//! is_from_range predicate
/*!
Construct the \c is_from_range predicate. The predicate holds if the input
is included in the specified range. (i.e. From <= Ch <= To )
\param From The start of the range
\param To The end of the range
\return An instance of the \c is_from_range predicate
*/
template<typename CharT>
inline detail::is_from_rangeF<CharT> is_from_range(CharT From, CharT To)
{
return detail::is_from_rangeF<CharT>(From,To);
}
// predicate combinators ---------------------------------------------------//
//! predicate 'and' composition predicate
/*!
Construct the \c class_and predicate. This predicate can be used
to logically combine two classification predicates. \c class_and holds,
if both predicates return true.
\param Pred1 The first predicate
\param Pred2 The second predicate
\return An instance of the \c class_and predicate
*/
template<typename Pred1T, typename Pred2T>
inline detail::pred_andF<Pred1T, Pred2T>
operator&&(
const predicate_facade<Pred1T>& Pred1,
const predicate_facade<Pred2T>& Pred2 )
{
// Doing the static_cast with the pointer instead of the reference
// is a workaround for some compilers which have problems with
// static_cast's of template references, i.e. CW8. /grafik/
return detail::pred_andF<Pred1T,Pred2T>(
*static_cast<const Pred1T*>(&Pred1),
*static_cast<const Pred2T*>(&Pred2) );
}
//! predicate 'or' composition predicate
/*!
Construct the \c class_or predicate. This predicate can be used
to logically combine two classification predicates. \c class_or holds,
if one of the predicates return true.
\param Pred1 The first predicate
\param Pred2 The second predicate
\return An instance of the \c class_or predicate
*/
template<typename Pred1T, typename Pred2T>
inline detail::pred_orF<Pred1T, Pred2T>
operator||(
const predicate_facade<Pred1T>& Pred1,
const predicate_facade<Pred2T>& Pred2 )
{
// Doing the static_cast with the pointer instead of the reference
// is a workaround for some compilers which have problems with
// static_cast's of template references, i.e. CW8. /grafik/
return detail::pred_orF<Pred1T,Pred2T>(
*static_cast<const Pred1T*>(&Pred1),
*static_cast<const Pred2T*>(&Pred2));
}
//! predicate negation operator
/*!
Construct the \c class_not predicate. This predicate represents a negation.
\c class_or holds if of the predicates return false.
\param Pred The predicate to be negated
\return An instance of the \c class_not predicate
*/
template<typename PredT>
inline detail::pred_notF<PredT>
operator!( const predicate_facade<PredT>& Pred )
{
// Doing the static_cast with the pointer instead of the reference
// is a workaround for some compilers which have problems with
// static_cast's of template references, i.e. CW8. /grafik/
return detail::pred_notF<PredT>(*static_cast<const PredT*>(&Pred));
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::is_classified;
using algorithm::is_space;
using algorithm::is_alnum;
using algorithm::is_alpha;
using algorithm::is_cntrl;
using algorithm::is_digit;
using algorithm::is_graph;
using algorithm::is_lower;
using algorithm::is_upper;
using algorithm::is_print;
using algorithm::is_punct;
using algorithm::is_xdigit;
using algorithm::is_any_of;
using algorithm::is_from_range;
} // namespace boost
#endif // BOOST_STRING_PREDICATE_HPP

View File

@ -1,199 +0,0 @@
// Boost string_algo library compare.hpp header file -------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_COMPARE_HPP
#define BOOST_STRING_COMPARE_HPP
#include <boost/algorithm/string/config.hpp>
#include <locale>
/*! \file
Defines element comparison predicates. Many algorithms in this library can
take an additional argument with a predicate used to compare elements.
This makes it possible, for instance, to have case insensitive versions
of the algorithms.
*/
namespace boost {
namespace algorithm {
// is_equal functor -----------------------------------------------//
//! is_equal functor
/*!
Standard STL equal_to only handle comparison between arguments
of the same type. This is a less restrictive version which wraps operator ==.
*/
struct is_equal
{
//! Function operator
/*!
Compare two operands for equality
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
return Arg1==Arg2;
}
};
//! case insensitive version of is_equal
/*!
Case insensitive comparison predicate. Comparison is done using
specified locales.
*/
struct is_iequal
{
//! Constructor
/*!
\param Loc locales used for comparison
*/
is_iequal( const std::locale& Loc=std::locale() ) :
m_Loc( Loc ) {}
//! Function operator
/*!
Compare two operands. Case is ignored.
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x564) && !defined(_USE_OLD_RW_STL)
return std::toupper(Arg1)==std::toupper(Arg2);
#else
return std::toupper<T1>(Arg1,m_Loc)==std::toupper<T2>(Arg2,m_Loc);
#endif
}
private:
std::locale m_Loc;
};
// is_less functor -----------------------------------------------//
//! is_less functor
/*!
Convenient version of standard std::less. Operation is templated, therefore it is
not required to specify the exact types upon the construction
*/
struct is_less
{
//! Functor operation
/*!
Compare two operands using > operator
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
return Arg1<Arg2;
}
};
//! case insensitive version of is_less
/*!
Case insensitive comparison predicate. Comparison is done using
specified locales.
*/
struct is_iless
{
//! Constructor
/*!
\param Loc locales used for comparison
*/
is_iless( const std::locale& Loc=std::locale() ) :
m_Loc( Loc ) {}
//! Function operator
/*!
Compare two operands. Case is ignored.
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x564) && !defined(_USE_OLD_RW_STL)
return std::toupper(Arg1)<std::toupper(Arg2);
#else
return std::toupper<T1>(Arg1,m_Loc)<std::toupper<T2>(Arg2,m_Loc);
#endif
}
private:
std::locale m_Loc;
};
// is_not_greater functor -----------------------------------------------//
//! is_not_greater functor
/*!
Convenient version of standard std::not_greater_to. Operation is templated, therefore it is
not required to specify the exact types upon the construction
*/
struct is_not_greater
{
//! Functor operation
/*!
Compare two operands using > operator
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
return Arg1<=Arg2;
}
};
//! case insensitive version of is_not_greater
/*!
Case insensitive comparison predicate. Comparison is done using
specified locales.
*/
struct is_not_igreater
{
//! Constructor
/*!
\param Loc locales used for comparison
*/
is_not_igreater( const std::locale& Loc=std::locale() ) :
m_Loc( Loc ) {}
//! Function operator
/*!
Compare two operands. Case is ignored.
*/
template< typename T1, typename T2 >
bool operator()( const T1& Arg1, const T2& Arg2 ) const
{
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x564) && !defined(_USE_OLD_RW_STL)
return std::toupper(Arg1)<=std::toupper(Arg2);
#else
return std::toupper<T1>(Arg1,m_Loc)<=std::toupper<T2>(Arg2,m_Loc);
#endif
}
private:
std::locale m_Loc;
};
} // namespace algorithm
// pull names to the boost namespace
using algorithm::is_equal;
using algorithm::is_iequal;
using algorithm::is_less;
using algorithm::is_iless;
using algorithm::is_not_greater;
using algorithm::is_not_igreater;
} // namespace boost
#endif // BOOST_STRING_COMPARE_HPP

View File

@ -1,83 +0,0 @@
// Boost string_algo library concept.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CONCEPT_HPP
#define BOOST_STRING_CONCEPT_HPP
#include <boost/concept_check.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
/*! \file
Defines concepts used in string_algo library
*/
namespace boost {
namespace algorithm {
//! Finder concept
/*!
Defines the Finder concept. Finder is a functor which selects
an arbitrary part of a string. Search is performed on
the range specified by starting and ending iterators.
Result of the find operation must be convertible to iterator_range.
*/
template<typename FinderT, typename IteratorT>
struct FinderConcept
{
private:
typedef iterator_range<IteratorT> range;
public:
void constraints()
{
// Operation
r=(*pF)(i,i);
}
private:
range r;
IteratorT i;
FinderT* pF;
}; // Finder_concept
//! Formatter concept
/*!
Defines the Formatter concept. Formatter is a functor, which
takes a result from a finder operation and transforms it
in a specific way.
Result must be a container supported by container_traits,
or a reference to it.
*/
template<typename FormatterT, typename FinderT, typename IteratorT>
struct FormatterConcept
{
public:
void constraints()
{
// Operation
::boost::begin((*pFo)( (*pF)(i,i) ));
::boost::end((*pFo)( (*pF)(i,i) ));
}
private:
IteratorT i;
FinderT* pF;
FormatterT *pFo;
}; // FormatterConcept;
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_CONCEPT_HPP

View File

@ -1,28 +0,0 @@
// Boost string_algo library config.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CONFIG_HPP
#define BOOST_STRING_CONFIG_HPP
#include <boost/config.hpp>
#include <boost/detail/workaround.hpp>
#ifdef BOOST_STRING_DEDUCED_TYPENAME
# error "macro already defined!"
#endif
#define BOOST_STRING_TYPENAME BOOST_DEDUCED_TYPENAME
// Metrowerks workaround
#if BOOST_WORKAROUND(__MWERKS__, <= 0x3003) // 8.x
#pragma parse_func_templ off
#endif
#endif // BOOST_STRING_CONFIG_HPP

View File

@ -1,36 +0,0 @@
// Boost string_algo library constants.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CONSTANTS_HPP
#define BOOST_STRING_CONSTANTS_HPP
namespace boost {
namespace algorithm {
//! Token compression mode
/*!
Specifies token compression mode for the token_finder.
*/
enum token_compress_mode_type
{
token_compress_on, //!< Compress adjacent tokens
token_compress_off //!< Do not compress adjacent tokens
};
} // namespace algorithm
// pull the names to the boost namespace
using algorithm::token_compress_on;
using algorithm::token_compress_off;
} // namespace boost
#endif // BOOST_STRING_CONSTANTS_HPP

View File

@ -1,127 +0,0 @@
// Boost string_algo library string_funct.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CASE_CONV_DETAIL_HPP
#define BOOST_STRING_CASE_CONV_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <locale>
#include <functional>
#include <boost/type_traits/make_unsigned.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// case conversion functors -----------------------------------------------//
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(push)
#pragma warning(disable:4512) //assignment operator could not be generated
#endif
// a tolower functor
template<typename CharT>
struct to_lowerF
{
typedef CharT argument_type;
typedef CharT result_type;
// Constructor
to_lowerF( const std::locale& Loc ) : m_Loc( &Loc ) {}
// Operation
CharT operator ()( CharT Ch ) const
{
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x564) && !defined(_USE_OLD_RW_STL)
return std::tolower( static_cast<typename boost::make_unsigned <CharT>::type> ( Ch ));
#else
return std::tolower<CharT>( Ch, *m_Loc );
#endif
}
private:
const std::locale* m_Loc;
};
// a toupper functor
template<typename CharT>
struct to_upperF
{
typedef CharT argument_type;
typedef CharT result_type;
// Constructor
to_upperF( const std::locale& Loc ) : m_Loc( &Loc ) {}
// Operation
CharT operator ()( CharT Ch ) const
{
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x564) && !defined(_USE_OLD_RW_STL)
return std::toupper( static_cast<typename boost::make_unsigned <CharT>::type> ( Ch ));
#else
return std::toupper<CharT>( Ch, *m_Loc );
#endif
}
private:
const std::locale* m_Loc;
};
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(pop)
#endif
// algorithm implementation -------------------------------------------------------------------------
// Transform a range
template<typename OutputIteratorT, typename RangeT, typename FunctorT>
OutputIteratorT transform_range_copy(
OutputIteratorT Output,
const RangeT& Input,
FunctorT Functor)
{
return std::transform(
::boost::begin(Input),
::boost::end(Input),
Output,
Functor);
}
// Transform a range (in-place)
template<typename RangeT, typename FunctorT>
void transform_range(
const RangeT& Input,
FunctorT Functor)
{
std::transform(
::boost::begin(Input),
::boost::end(Input),
::boost::begin(Input),
Functor);
}
template<typename SequenceT, typename RangeT, typename FunctorT>
inline SequenceT transform_range_copy(
const RangeT& Input,
FunctorT Functor)
{
return SequenceT(
::boost::make_transform_iterator(
::boost::begin(Input),
Functor),
::boost::make_transform_iterator(
::boost::end(Input),
Functor));
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_CASE_CONV_DETAIL_HPP

View File

@ -1,353 +0,0 @@
// Boost string_algo library classification.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_CLASSIFICATION_DETAIL_HPP
#define BOOST_STRING_CLASSIFICATION_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <algorithm>
#include <functional>
#include <locale>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/algorithm/string/predicate_facade.hpp>
#include <boost/type_traits/remove_const.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// classification functors -----------------------------------------------//
// is_classified functor
struct is_classifiedF :
public predicate_facade<is_classifiedF>
{
// Boost.ResultOf support
typedef bool result_type;
// Constructor from a locale
is_classifiedF(std::ctype_base::mask Type, std::locale const & Loc = std::locale()) :
m_Type(Type), m_Locale(Loc) {}
// Operation
template<typename CharT>
bool operator()( CharT Ch ) const
{
return std::use_facet< std::ctype<CharT> >(m_Locale).is( m_Type, Ch );
}
#if defined(__BORLANDC__) && (__BORLANDC__ >= 0x560) && (__BORLANDC__ <= 0x582) && !defined(_USE_OLD_RW_STL)
template<>
bool operator()( char const Ch ) const
{
return std::use_facet< std::ctype<char> >(m_Locale).is( m_Type, Ch );
}
#endif
private:
std::ctype_base::mask m_Type;
std::locale m_Locale;
};
// is_any_of functor
/*
returns true if the value is from the specified set
*/
template<typename CharT>
struct is_any_ofF :
public predicate_facade<is_any_ofF<CharT> >
{
private:
// set cannot operate on const value-type
typedef typename ::boost::remove_const<CharT>::type set_value_type;
public:
// Boost.ResultOf support
typedef bool result_type;
// Constructor
template<typename RangeT>
is_any_ofF( const RangeT& Range ) : m_Size(0)
{
// Prepare storage
m_Storage.m_dynSet=0;
std::size_t Size=::boost::distance(Range);
m_Size=Size;
set_value_type* Storage=0;
if(use_fixed_storage(m_Size))
{
// Use fixed storage
Storage=&m_Storage.m_fixSet[0];
}
else
{
// Use dynamic storage
m_Storage.m_dynSet=new set_value_type[m_Size];
Storage=m_Storage.m_dynSet;
}
// Use fixed storage
::std::copy(::boost::begin(Range), ::boost::end(Range), Storage);
::std::sort(Storage, Storage+m_Size);
}
// Copy constructor
is_any_ofF(const is_any_ofF& Other) : m_Size(Other.m_Size)
{
// Prepare storage
m_Storage.m_dynSet=0;
const set_value_type* SrcStorage=0;
set_value_type* DestStorage=0;
if(use_fixed_storage(m_Size))
{
// Use fixed storage
DestStorage=&m_Storage.m_fixSet[0];
SrcStorage=&Other.m_Storage.m_fixSet[0];
}
else
{
// Use dynamic storage
m_Storage.m_dynSet=new set_value_type[m_Size];
DestStorage=m_Storage.m_dynSet;
SrcStorage=Other.m_Storage.m_dynSet;
}
// Use fixed storage
::std::memcpy(DestStorage, SrcStorage, sizeof(set_value_type)*m_Size);
}
// Destructor
~is_any_ofF()
{
if(!use_fixed_storage(m_Size) && m_Storage.m_dynSet!=0)
{
delete [] m_Storage.m_dynSet;
}
}
// Assignment
is_any_ofF& operator=(const is_any_ofF& Other)
{
// Handle self assignment
if(this==&Other) return *this;
// Prepare storage
const set_value_type* SrcStorage;
set_value_type* DestStorage;
if(use_fixed_storage(Other.m_Size))
{
// Use fixed storage
DestStorage=&m_Storage.m_fixSet[0];
SrcStorage=&Other.m_Storage.m_fixSet[0];
// Delete old storage if was present
if(!use_fixed_storage(m_Size) && m_Storage.m_dynSet!=0)
{
delete [] m_Storage.m_dynSet;
}
// Set new size
m_Size=Other.m_Size;
}
else
{
// Other uses dynamic storage
SrcStorage=Other.m_Storage.m_dynSet;
// Check what kind of storage are we using right now
if(use_fixed_storage(m_Size))
{
// Using fixed storage, allocate new
set_value_type* pTemp=new set_value_type[Other.m_Size];
DestStorage=pTemp;
m_Storage.m_dynSet=pTemp;
m_Size=Other.m_Size;
}
else
{
// Using dynamic storage, check if can reuse
if(m_Storage.m_dynSet!=0 && m_Size>=Other.m_Size && m_Size<Other.m_Size*2)
{
// Reuse the current storage
DestStorage=m_Storage.m_dynSet;
m_Size=Other.m_Size;
}
else
{
// Allocate the new one
set_value_type* pTemp=new set_value_type[Other.m_Size];
DestStorage=pTemp;
// Delete old storage if necessary
if(m_Storage.m_dynSet!=0)
{
delete [] m_Storage.m_dynSet;
}
// Store the new storage
m_Storage.m_dynSet=pTemp;
// Set new size
m_Size=Other.m_Size;
}
}
}
// Copy the data
::std::memcpy(DestStorage, SrcStorage, sizeof(set_value_type)*m_Size);
return *this;
}
// Operation
template<typename Char2T>
bool operator()( Char2T Ch ) const
{
const set_value_type* Storage=
(use_fixed_storage(m_Size))
? &m_Storage.m_fixSet[0]
: m_Storage.m_dynSet;
return ::std::binary_search(Storage, Storage+m_Size, Ch);
}
private:
// check if the size is eligible for fixed storage
static bool use_fixed_storage(std::size_t size)
{
return size<=sizeof(set_value_type*)*2;
}
private:
// storage
// The actual used storage is selected on the type
union
{
set_value_type* m_dynSet;
set_value_type m_fixSet[sizeof(set_value_type*)*2];
}
m_Storage;
// storage size
::std::size_t m_Size;
};
// is_from_range functor
/*
returns true if the value is from the specified range.
(i.e. x>=From && x>=To)
*/
template<typename CharT>
struct is_from_rangeF :
public predicate_facade< is_from_rangeF<CharT> >
{
// Boost.ResultOf support
typedef bool result_type;
// Constructor
is_from_rangeF( CharT From, CharT To ) : m_From(From), m_To(To) {}
// Operation
template<typename Char2T>
bool operator()( Char2T Ch ) const
{
return ( m_From <= Ch ) && ( Ch <= m_To );
}
private:
CharT m_From;
CharT m_To;
};
// class_and composition predicate
template<typename Pred1T, typename Pred2T>
struct pred_andF :
public predicate_facade< pred_andF<Pred1T,Pred2T> >
{
public:
// Boost.ResultOf support
typedef bool result_type;
// Constructor
pred_andF( Pred1T Pred1, Pred2T Pred2 ) :
m_Pred1(Pred1), m_Pred2(Pred2) {}
// Operation
template<typename CharT>
bool operator()( CharT Ch ) const
{
return m_Pred1(Ch) && m_Pred2(Ch);
}
private:
Pred1T m_Pred1;
Pred2T m_Pred2;
};
// class_or composition predicate
template<typename Pred1T, typename Pred2T>
struct pred_orF :
public predicate_facade< pred_orF<Pred1T,Pred2T> >
{
public:
// Boost.ResultOf support
typedef bool result_type;
// Constructor
pred_orF( Pred1T Pred1, Pred2T Pred2 ) :
m_Pred1(Pred1), m_Pred2(Pred2) {}
// Operation
template<typename CharT>
bool operator()( CharT Ch ) const
{
return m_Pred1(Ch) || m_Pred2(Ch);
}
private:
Pred1T m_Pred1;
Pred2T m_Pred2;
};
// class_not composition predicate
template< typename PredT >
struct pred_notF :
public predicate_facade< pred_notF<PredT> >
{
public:
// Boost.ResultOf support
typedef bool result_type;
// Constructor
pred_notF( PredT Pred ) : m_Pred(Pred) {}
// Operation
template<typename CharT>
bool operator()( CharT Ch ) const
{
return !m_Pred(Ch);
}
private:
PredT m_Pred;
};
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_CLASSIFICATION_DETAIL_HPP

View File

@ -1,204 +0,0 @@
// Boost string_algo library find_format.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_FORMAT_DETAIL_HPP
#define BOOST_STRING_FIND_FORMAT_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/range/iterator.hpp>
#include <boost/algorithm/string/detail/find_format_store.hpp>
#include <boost/algorithm/string/detail/replace_storage.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// find_format_copy (iterator variant) implementation -------------------------------//
template<
typename OutputIteratorT,
typename InputT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline OutputIteratorT find_format_copy_impl2(
OutputIteratorT Output,
const InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult,
const FormatResultT& FormatResult )
{
typedef find_format_store<
BOOST_STRING_TYPENAME
range_const_iterator<InputT>::type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
if ( !M )
{
// Match not found - return original sequence
Output = std::copy( ::boost::begin(Input), ::boost::end(Input), Output );
return Output;
}
// Copy the beginning of the sequence
Output = std::copy( ::boost::begin(Input), ::boost::begin(M), Output );
// Format find result
// Copy formatted result
Output = std::copy( ::boost::begin(M.format_result()), ::boost::end(M.format_result()), Output );
// Copy the rest of the sequence
Output = std::copy( M.end(), ::boost::end(Input), Output );
return Output;
}
template<
typename OutputIteratorT,
typename InputT,
typename FormatterT,
typename FindResultT >
inline OutputIteratorT find_format_copy_impl(
OutputIteratorT Output,
const InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult )
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
return ::boost::algorithm::detail::find_format_copy_impl2(
Output,
Input,
Formatter,
FindResult,
Formatter(FindResult) );
} else {
return std::copy( ::boost::begin(Input), ::boost::end(Input), Output );
}
}
// find_format_copy implementation --------------------------------------------------//
template<
typename InputT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline InputT find_format_copy_impl2(
const InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult,
const FormatResultT& FormatResult)
{
typedef find_format_store<
BOOST_STRING_TYPENAME
range_const_iterator<InputT>::type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
if ( !M )
{
// Match not found - return original sequence
return InputT( Input );
}
InputT Output;
// Copy the beginning of the sequence
boost::algorithm::detail::insert( Output, ::boost::end(Output), ::boost::begin(Input), M.begin() );
// Copy formatted result
boost::algorithm::detail::insert( Output, ::boost::end(Output), M.format_result() );
// Copy the rest of the sequence
boost::algorithm::detail::insert( Output, ::boost::end(Output), M.end(), ::boost::end(Input) );
return Output;
}
template<
typename InputT,
typename FormatterT,
typename FindResultT >
inline InputT find_format_copy_impl(
const InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult)
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
return ::boost::algorithm::detail::find_format_copy_impl2(
Input,
Formatter,
FindResult,
Formatter(FindResult) );
} else {
return Input;
}
}
// replace implementation ----------------------------------------------------//
template<
typename InputT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline void find_format_impl2(
InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult,
const FormatResultT& FormatResult)
{
typedef find_format_store<
BOOST_STRING_TYPENAME
range_iterator<InputT>::type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
if ( !M )
{
// Search not found - return original sequence
return;
}
// Replace match
::boost::algorithm::detail::replace( Input, M.begin(), M.end(), M.format_result() );
}
template<
typename InputT,
typename FormatterT,
typename FindResultT >
inline void find_format_impl(
InputT& Input,
FormatterT Formatter,
const FindResultT& FindResult)
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
::boost::algorithm::detail::find_format_impl2(
Input,
Formatter,
FindResult,
Formatter(FindResult) );
}
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FIND_FORMAT_DETAIL_HPP

View File

@ -1,273 +0,0 @@
// Boost string_algo library find_format_all.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_FORMAT_ALL_DETAIL_HPP
#define BOOST_STRING_FIND_FORMAT_ALL_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/range/value_type.hpp>
#include <boost/algorithm/string/detail/find_format_store.hpp>
#include <boost/algorithm/string/detail/replace_storage.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// find_format_all_copy (iterator variant) implementation ---------------------------//
template<
typename OutputIteratorT,
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline OutputIteratorT find_format_all_copy_impl2(
OutputIteratorT Output,
const InputT& Input,
FinderT Finder,
FormatterT Formatter,
const FindResultT& FindResult,
const FormatResultT& FormatResult )
{
typedef BOOST_STRING_TYPENAME
range_const_iterator<InputT>::type input_iterator_type;
typedef find_format_store<
input_iterator_type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
// Initialize last match
input_iterator_type LastMatch=::boost::begin(Input);
// Iterate through all matches
while( M )
{
// Copy the beginning of the sequence
Output = std::copy( LastMatch, M.begin(), Output );
// Copy formatted result
Output = std::copy( ::boost::begin(M.format_result()), ::boost::end(M.format_result()), Output );
// Proceed to the next match
LastMatch=M.end();
M=Finder( LastMatch, ::boost::end(Input) );
}
// Copy the rest of the sequence
Output = std::copy( LastMatch, ::boost::end(Input), Output );
return Output;
}
template<
typename OutputIteratorT,
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT >
inline OutputIteratorT find_format_all_copy_impl(
OutputIteratorT Output,
const InputT& Input,
FinderT Finder,
FormatterT Formatter,
const FindResultT& FindResult )
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
return ::boost::algorithm::detail::find_format_all_copy_impl2(
Output,
Input,
Finder,
Formatter,
FindResult,
Formatter(FindResult) );
} else {
return std::copy( ::boost::begin(Input), ::boost::end(Input), Output );
}
}
// find_format_all_copy implementation ----------------------------------------------//
template<
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline InputT find_format_all_copy_impl2(
const InputT& Input,
FinderT Finder,
FormatterT Formatter,
const FindResultT& FindResult,
const FormatResultT& FormatResult)
{
typedef BOOST_STRING_TYPENAME
range_const_iterator<InputT>::type input_iterator_type;
typedef find_format_store<
input_iterator_type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
// Initialize last match
input_iterator_type LastMatch=::boost::begin(Input);
// Output temporary
InputT Output;
// Iterate through all matches
while( M )
{
// Copy the beginning of the sequence
boost::algorithm::detail::insert( Output, ::boost::end(Output), LastMatch, M.begin() );
// Copy formatted result
boost::algorithm::detail::insert( Output, ::boost::end(Output), M.format_result() );
// Proceed to the next match
LastMatch=M.end();
M=Finder( LastMatch, ::boost::end(Input) );
}
// Copy the rest of the sequence
::boost::algorithm::detail::insert( Output, ::boost::end(Output), LastMatch, ::boost::end(Input) );
return Output;
}
template<
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT >
inline InputT find_format_all_copy_impl(
const InputT& Input,
FinderT Finder,
FormatterT Formatter,
const FindResultT& FindResult)
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
return ::boost::algorithm::detail::find_format_all_copy_impl2(
Input,
Finder,
Formatter,
FindResult,
Formatter(FindResult) );
} else {
return Input;
}
}
// find_format_all implementation ------------------------------------------------//
template<
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT,
typename FormatResultT >
inline void find_format_all_impl2(
InputT& Input,
FinderT Finder,
FormatterT Formatter,
FindResultT FindResult,
FormatResultT FormatResult)
{
typedef BOOST_STRING_TYPENAME
range_iterator<InputT>::type input_iterator_type;
typedef find_format_store<
input_iterator_type,
FormatterT,
FormatResultT > store_type;
// Create store for the find result
store_type M( FindResult, FormatResult, Formatter );
// Instantiate replacement storage
std::deque<
BOOST_STRING_TYPENAME range_value<InputT>::type> Storage;
// Initialize replacement iterators
input_iterator_type InsertIt=::boost::begin(Input);
input_iterator_type SearchIt=::boost::begin(Input);
while( M )
{
// process the segment
InsertIt=process_segment(
Storage,
Input,
InsertIt,
SearchIt,
M.begin() );
// Adjust search iterator
SearchIt=M.end();
// Copy formatted replace to the storage
::boost::algorithm::detail::copy_to_storage( Storage, M.format_result() );
// Find range for a next match
M=Finder( SearchIt, ::boost::end(Input) );
}
// process the last segment
InsertIt=::boost::algorithm::detail::process_segment(
Storage,
Input,
InsertIt,
SearchIt,
::boost::end(Input) );
if ( Storage.empty() )
{
// Truncate input
::boost::algorithm::detail::erase( Input, InsertIt, ::boost::end(Input) );
}
else
{
// Copy remaining data to the end of input
::boost::algorithm::detail::insert( Input, ::boost::end(Input), Storage.begin(), Storage.end() );
}
}
template<
typename InputT,
typename FinderT,
typename FormatterT,
typename FindResultT >
inline void find_format_all_impl(
InputT& Input,
FinderT Finder,
FormatterT Formatter,
FindResultT FindResult)
{
if( ::boost::algorithm::detail::check_find_result(Input, FindResult) ) {
::boost::algorithm::detail::find_format_all_impl2(
Input,
Finder,
Formatter,
FindResult,
Formatter(FindResult) );
}
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FIND_FORMAT_ALL_DETAIL_HPP

View File

@ -1,89 +0,0 @@
// Boost string_algo library find_format_store.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_FORMAT_STORE_DETAIL_HPP
#define BOOST_STRING_FIND_FORMAT_STORE_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// temporary format and find result storage --------------------------------//
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(push)
#pragma warning(disable:4512) //assignment operator could not be generated
#endif
template<
typename ForwardIteratorT,
typename FormatterT,
typename FormatResultT >
class find_format_store :
public iterator_range<ForwardIteratorT>
{
public:
// typedefs
typedef iterator_range<ForwardIteratorT> base_type;
typedef FormatterT formatter_type;
typedef FormatResultT format_result_type;
public:
// Construction
find_format_store(
const base_type& FindResult,
const format_result_type& FormatResult,
const formatter_type& Formatter ) :
base_type(FindResult),
m_FormatResult(FormatResult),
m_Formatter(Formatter) {}
// Assignment
template< typename FindResultT >
find_format_store& operator=( FindResultT FindResult )
{
iterator_range<ForwardIteratorT>::operator=(FindResult);
if( !this->empty() ) {
m_FormatResult=m_Formatter(FindResult);
}
return *this;
}
// Retrieve format result
const format_result_type& format_result()
{
return m_FormatResult;
}
private:
format_result_type m_FormatResult;
const formatter_type& m_Formatter;
};
template<typename InputT, typename FindResultT>
bool check_find_result(InputT&, FindResultT& FindResult)
{
typedef BOOST_STRING_TYPENAME
range_const_iterator<InputT>::type input_iterator_type;
iterator_range<input_iterator_type> ResultRange(FindResult);
return !ResultRange.empty();
}
#if BOOST_WORKAROUND(BOOST_MSVC, >= 1400)
#pragma warning(pop)
#endif
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FIND_FORMAT_STORE_DETAIL_HPP

View File

@ -1,87 +0,0 @@
// Boost string_algo library find_iterator.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_ITERATOR_DETAIL_HPP
#define BOOST_STRING_FIND_ITERATOR_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/function.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// find_iterator base -----------------------------------------------//
// Find iterator base
template<typename IteratorT>
class find_iterator_base
{
protected:
// typedefs
typedef IteratorT input_iterator_type;
typedef iterator_range<IteratorT> match_type;
typedef function2<
match_type,
input_iterator_type,
input_iterator_type> finder_type;
protected:
// Protected construction/destruction
// Default constructor
find_iterator_base() {}
// Copy construction
find_iterator_base( const find_iterator_base& Other ) :
m_Finder(Other.m_Finder) {}
// Constructor
template<typename FinderT>
find_iterator_base( FinderT Finder, int ) :
m_Finder(Finder) {}
// Destructor
~find_iterator_base() {}
// Find operation
match_type do_find(
input_iterator_type Begin,
input_iterator_type End ) const
{
if (!m_Finder.empty())
{
return m_Finder(Begin,End);
}
else
{
return match_type(End,End);
}
}
// Check
bool is_null() const
{
return m_Finder.empty();
}
private:
// Finder
finder_type m_Finder;
};
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FIND_ITERATOR_DETAIL_HPP

View File

@ -1,639 +0,0 @@
// Boost string_algo library finder.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FINDER_DETAIL_HPP
#define BOOST_STRING_FINDER_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/algorithm/string/constants.hpp>
#include <boost/detail/iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/empty.hpp>
#include <boost/range/as_literal.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// find first functor -----------------------------------------------//
// find a subsequence in the sequence ( functor )
/*
Returns a pair <begin,end> marking the subsequence in the sequence.
If the find fails, functor returns <End,End>
*/
template<typename SearchIteratorT,typename PredicateT>
struct first_finderF
{
typedef SearchIteratorT search_iterator_type;
// Construction
template< typename SearchT >
first_finderF( const SearchT& Search, PredicateT Comp ) :
m_Search(::boost::begin(Search), ::boost::end(Search)), m_Comp(Comp) {}
first_finderF(
search_iterator_type SearchBegin,
search_iterator_type SearchEnd,
PredicateT Comp ) :
m_Search(SearchBegin, SearchEnd), m_Comp(Comp) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
typedef iterator_range<ForwardIteratorT> result_type;
typedef ForwardIteratorT input_iterator_type;
// Outer loop
for(input_iterator_type OuterIt=Begin;
OuterIt!=End;
++OuterIt)
{
// Sanity check
if( boost::empty(m_Search) )
return result_type( End, End );
input_iterator_type InnerIt=OuterIt;
search_iterator_type SubstrIt=m_Search.begin();
for(;
InnerIt!=End && SubstrIt!=m_Search.end();
++InnerIt,++SubstrIt)
{
if( !( m_Comp(*InnerIt,*SubstrIt) ) )
break;
}
// Substring matching succeeded
if ( SubstrIt==m_Search.end() )
return result_type( OuterIt, InnerIt );
}
return result_type( End, End );
}
private:
iterator_range<search_iterator_type> m_Search;
PredicateT m_Comp;
};
// find last functor -----------------------------------------------//
// find the last match a subsequence in the sequence ( functor )
/*
Returns a pair <begin,end> marking the subsequence in the sequence.
If the find fails, returns <End,End>
*/
template<typename SearchIteratorT, typename PredicateT>
struct last_finderF
{
typedef SearchIteratorT search_iterator_type;
typedef first_finderF<
search_iterator_type,
PredicateT> first_finder_type;
// Construction
template< typename SearchT >
last_finderF( const SearchT& Search, PredicateT Comp ) :
m_Search(::boost::begin(Search), ::boost::end(Search)), m_Comp(Comp) {}
last_finderF(
search_iterator_type SearchBegin,
search_iterator_type SearchEnd,
PredicateT Comp ) :
m_Search(SearchBegin, SearchEnd), m_Comp(Comp) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
typedef iterator_range<ForwardIteratorT> result_type;
if( boost::empty(m_Search) )
return result_type( End, End );
typedef BOOST_STRING_TYPENAME boost::detail::
iterator_traits<ForwardIteratorT>::iterator_category category;
return findit( Begin, End, category() );
}
private:
// forward iterator
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
findit(
ForwardIteratorT Begin,
ForwardIteratorT End,
std::forward_iterator_tag ) const
{
typedef iterator_range<ForwardIteratorT> result_type;
first_finder_type first_finder(
m_Search.begin(), m_Search.end(), m_Comp );
result_type M=first_finder( Begin, End );
result_type Last=M;
while( M )
{
Last=M;
M=first_finder( ::boost::end(M), End );
}
return Last;
}
// bidirectional iterator
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
findit(
ForwardIteratorT Begin,
ForwardIteratorT End,
std::bidirectional_iterator_tag ) const
{
typedef iterator_range<ForwardIteratorT> result_type;
typedef ForwardIteratorT input_iterator_type;
// Outer loop
for(input_iterator_type OuterIt=End;
OuterIt!=Begin; )
{
input_iterator_type OuterIt2=--OuterIt;
input_iterator_type InnerIt=OuterIt2;
search_iterator_type SubstrIt=m_Search.begin();
for(;
InnerIt!=End && SubstrIt!=m_Search.end();
++InnerIt,++SubstrIt)
{
if( !( m_Comp(*InnerIt,*SubstrIt) ) )
break;
}
// Substring matching succeeded
if( SubstrIt==m_Search.end() )
return result_type( OuterIt2, InnerIt );
}
return result_type( End, End );
}
private:
iterator_range<search_iterator_type> m_Search;
PredicateT m_Comp;
};
// find n-th functor -----------------------------------------------//
// find the n-th match of a subsequence in the sequence ( functor )
/*
Returns a pair <begin,end> marking the subsequence in the sequence.
If the find fails, returns <End,End>
*/
template<typename SearchIteratorT, typename PredicateT>
struct nth_finderF
{
typedef SearchIteratorT search_iterator_type;
typedef first_finderF<
search_iterator_type,
PredicateT> first_finder_type;
typedef last_finderF<
search_iterator_type,
PredicateT> last_finder_type;
// Construction
template< typename SearchT >
nth_finderF(
const SearchT& Search,
int Nth,
PredicateT Comp) :
m_Search(::boost::begin(Search), ::boost::end(Search)),
m_Nth(Nth),
m_Comp(Comp) {}
nth_finderF(
search_iterator_type SearchBegin,
search_iterator_type SearchEnd,
int Nth,
PredicateT Comp) :
m_Search(SearchBegin, SearchEnd),
m_Nth(Nth),
m_Comp(Comp) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
if(m_Nth>=0)
{
return find_forward(Begin, End, m_Nth);
}
else
{
return find_backward(Begin, End, -m_Nth);
}
}
private:
// Implementation helpers
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_forward(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N) const
{
typedef iterator_range<ForwardIteratorT> result_type;
// Sanity check
if( boost::empty(m_Search) )
return result_type( End, End );
// Instantiate find functor
first_finder_type first_finder(
m_Search.begin(), m_Search.end(), m_Comp );
result_type M( Begin, Begin );
for( unsigned int n=0; n<=N; ++n )
{
// find next match
M=first_finder( ::boost::end(M), End );
if ( !M )
{
// Subsequence not found, return
return M;
}
}
return M;
}
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_backward(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N) const
{
typedef iterator_range<ForwardIteratorT> result_type;
// Sanity check
if( boost::empty(m_Search) )
return result_type( End, End );
// Instantiate find functor
last_finder_type last_finder(
m_Search.begin(), m_Search.end(), m_Comp );
result_type M( End, End );
for( unsigned int n=1; n<=N; ++n )
{
// find next match
M=last_finder( Begin, ::boost::begin(M) );
if ( !M )
{
// Subsequence not found, return
return M;
}
}
return M;
}
private:
iterator_range<search_iterator_type> m_Search;
int m_Nth;
PredicateT m_Comp;
};
// find head/tail implementation helpers ---------------------------//
template<typename ForwardIteratorT>
iterator_range<ForwardIteratorT>
find_head_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N,
std::forward_iterator_tag )
{
typedef ForwardIteratorT input_iterator_type;
typedef iterator_range<ForwardIteratorT> result_type;
input_iterator_type It=Begin;
for(
unsigned int Index=0;
Index<N && It!=End; ++Index,++It ) {};
return result_type( Begin, It );
}
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_head_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N,
std::random_access_iterator_tag )
{
typedef iterator_range<ForwardIteratorT> result_type;
if ( (End<=Begin) || ( static_cast<unsigned int>(End-Begin) < N ) )
return result_type( Begin, End );
return result_type(Begin,Begin+N);
}
// Find head implementation
template<typename ForwardIteratorT>
iterator_range<ForwardIteratorT>
find_head_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N )
{
typedef BOOST_STRING_TYPENAME boost::detail::
iterator_traits<ForwardIteratorT>::iterator_category category;
return ::boost::algorithm::detail::find_head_impl( Begin, End, N, category() );
}
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_tail_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N,
std::forward_iterator_tag )
{
typedef ForwardIteratorT input_iterator_type;
typedef iterator_range<ForwardIteratorT> result_type;
unsigned int Index=0;
input_iterator_type It=Begin;
input_iterator_type It2=Begin;
// Advance It2 by N increments
for( Index=0; Index<N && It2!=End; ++Index,++It2 ) {};
// Advance It, It2 to the end
for(; It2!=End; ++It,++It2 ) {};
return result_type( It, It2 );
}
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_tail_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N,
std::bidirectional_iterator_tag )
{
typedef ForwardIteratorT input_iterator_type;
typedef iterator_range<ForwardIteratorT> result_type;
input_iterator_type It=End;
for(
unsigned int Index=0;
Index<N && It!=Begin; ++Index,--It ) {};
return result_type( It, End );
}
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_tail_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N,
std::random_access_iterator_tag )
{
typedef iterator_range<ForwardIteratorT> result_type;
if ( (End<=Begin) || ( static_cast<unsigned int>(End-Begin) < N ) )
return result_type( Begin, End );
return result_type( End-N, End );
}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
find_tail_impl(
ForwardIteratorT Begin,
ForwardIteratorT End,
unsigned int N )
{
typedef BOOST_STRING_TYPENAME boost::detail::
iterator_traits<ForwardIteratorT>::iterator_category category;
return ::boost::algorithm::detail::find_tail_impl( Begin, End, N, category() );
}
// find head functor -----------------------------------------------//
// find a head in the sequence ( functor )
/*
This functor find a head of the specified range. For
a specified N, the head is a subsequence of N starting
elements of the range.
*/
struct head_finderF
{
// Construction
head_finderF( int N ) : m_N(N) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
if(m_N>=0)
{
return ::boost::algorithm::detail::find_head_impl( Begin, End, m_N );
}
else
{
iterator_range<ForwardIteratorT> Res=
::boost::algorithm::detail::find_tail_impl( Begin, End, -m_N );
return ::boost::make_iterator_range(Begin, Res.begin());
}
}
private:
int m_N;
};
// find tail functor -----------------------------------------------//
// find a tail in the sequence ( functor )
/*
This functor find a tail of the specified range. For
a specified N, the head is a subsequence of N starting
elements of the range.
*/
struct tail_finderF
{
// Construction
tail_finderF( int N ) : m_N(N) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
if(m_N>=0)
{
return ::boost::algorithm::detail::find_tail_impl( Begin, End, m_N );
}
else
{
iterator_range<ForwardIteratorT> Res=
::boost::algorithm::detail::find_head_impl( Begin, End, -m_N );
return ::boost::make_iterator_range(Res.end(), End);
}
}
private:
int m_N;
};
// find token functor -----------------------------------------------//
// find a token in a sequence ( functor )
/*
This find functor finds a token specified be a predicate
in a sequence. It is equivalent of std::find algorithm,
with an exception that it return range instead of a single
iterator.
If bCompress is set to true, adjacent matching tokens are
concatenated into one match.
*/
template< typename PredicateT >
struct token_finderF
{
// Construction
token_finderF(
PredicateT Pred,
token_compress_mode_type eCompress=token_compress_off ) :
m_Pred(Pred), m_eCompress(eCompress) {}
// Operation
template< typename ForwardIteratorT >
iterator_range<ForwardIteratorT>
operator()(
ForwardIteratorT Begin,
ForwardIteratorT End ) const
{
typedef iterator_range<ForwardIteratorT> result_type;
ForwardIteratorT It=std::find_if( Begin, End, m_Pred );
if( It==End )
{
return result_type( End, End );
}
else
{
ForwardIteratorT It2=It;
if( m_eCompress==token_compress_on )
{
// Find first non-matching character
while( It2!=End && m_Pred(*It2) ) ++It2;
}
else
{
// Advance by one position
++It2;
}
return result_type( It, It2 );
}
}
private:
PredicateT m_Pred;
token_compress_mode_type m_eCompress;
};
// find range functor -----------------------------------------------//
// find a range in the sequence ( functor )
/*
This functor actually does not perform any find operation.
It always returns given iterator range as a result.
*/
template<typename ForwardIterator1T>
struct range_finderF
{
typedef ForwardIterator1T input_iterator_type;
typedef iterator_range<input_iterator_type> result_type;
// Construction
range_finderF(
input_iterator_type Begin,
input_iterator_type End ) : m_Range(Begin, End) {}
range_finderF(const iterator_range<input_iterator_type>& Range) :
m_Range(Range) {}
// Operation
template< typename ForwardIterator2T >
iterator_range<ForwardIterator2T>
operator()(
ForwardIterator2T,
ForwardIterator2T ) const
{
#if BOOST_WORKAROUND( __MWERKS__, <= 0x3003 )
return iterator_range<const ForwardIterator2T>(this->m_Range);
#else
return m_Range;
#endif
}
private:
iterator_range<input_iterator_type> m_Range;
};
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FINDER_DETAIL_HPP

View File

@ -1,119 +0,0 @@
// Boost string_algo library formatter.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org for updates, documentation, and revision history.
#ifndef BOOST_STRING_FORMATTER_DETAIL_HPP
#define BOOST_STRING_FORMATTER_DETAIL_HPP
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/algorithm/string/detail/util.hpp>
// generic replace functors -----------------------------------------------//
namespace boost {
namespace algorithm {
namespace detail {
// const format functor ----------------------------------------------------//
// constant format functor
template<typename RangeT>
struct const_formatF
{
private:
typedef BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type format_iterator;
typedef iterator_range<format_iterator> result_type;
public:
// Construction
const_formatF(const RangeT& Format) :
m_Format(::boost::begin(Format), ::boost::end(Format)) {}
// Operation
#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
template<typename Range2T>
result_type& operator()(const Range2T&)
{
return m_Format;
}
#endif
template<typename Range2T>
const result_type& operator()(const Range2T&) const
{
return m_Format;
}
private:
result_type m_Format;
};
// identity format functor ----------------------------------------------------//
// identity format functor
template<typename RangeT>
struct identity_formatF
{
// Operation
template< typename Range2T >
const RangeT& operator()(const Range2T& Replace) const
{
return RangeT(::boost::begin(Replace), ::boost::end(Replace));
}
};
// empty format functor ( used by erase ) ------------------------------------//
// empty format functor
template< typename CharT >
struct empty_formatF
{
template< typename ReplaceT >
empty_container<CharT> operator()(const ReplaceT&) const
{
return empty_container<CharT>();
}
};
// dissect format functor ----------------------------------------------------//
// dissect format functor
template<typename FinderT>
struct dissect_formatF
{
public:
// Construction
dissect_formatF(FinderT Finder) :
m_Finder(Finder) {}
// Operation
template<typename RangeT>
inline iterator_range<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type>
operator()(const RangeT& Replace) const
{
return m_Finder(::boost::begin(Replace), ::boost::end(Replace));
}
private:
FinderT m_Finder;
};
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_FORMATTER_DETAIL_HPP

View File

@ -1,77 +0,0 @@
// Boost string_algo library predicate.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_PREDICATE_DETAIL_HPP
#define BOOST_STRING_PREDICATE_DETAIL_HPP
#include <iterator>
#include <boost/algorithm/string/find.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// ends_with predicate implementation ----------------------------------//
template<
typename ForwardIterator1T,
typename ForwardIterator2T,
typename PredicateT>
inline bool ends_with_iter_select(
ForwardIterator1T Begin,
ForwardIterator1T End,
ForwardIterator2T SubBegin,
ForwardIterator2T SubEnd,
PredicateT Comp,
std::bidirectional_iterator_tag)
{
ForwardIterator1T it=End;
ForwardIterator2T pit=SubEnd;
for(;it!=Begin && pit!=SubBegin;)
{
if( !(Comp(*(--it),*(--pit))) )
return false;
}
return pit==SubBegin;
}
template<
typename ForwardIterator1T,
typename ForwardIterator2T,
typename PredicateT>
inline bool ends_with_iter_select(
ForwardIterator1T Begin,
ForwardIterator1T End,
ForwardIterator2T SubBegin,
ForwardIterator2T SubEnd,
PredicateT Comp,
std::forward_iterator_tag)
{
if ( SubBegin==SubEnd )
{
// empty subsequence check
return true;
}
iterator_range<ForwardIterator1T> Result
=last_finder(
::boost::make_iterator_range(SubBegin, SubEnd),
Comp)(Begin, End);
return !Result.empty() && Result.end()==End;
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_PREDICATE_DETAIL_HPP

View File

@ -1,159 +0,0 @@
// Boost string_algo library replace_storage.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_REPLACE_STORAGE_DETAIL_HPP
#define BOOST_STRING_REPLACE_STORAGE_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <algorithm>
#include <boost/mpl/bool.hpp>
#include <boost/algorithm/string/sequence_traits.hpp>
#include <boost/algorithm/string/detail/sequence.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// storage handling routines -----------------------------------------------//
template< typename StorageT, typename OutputIteratorT >
inline OutputIteratorT move_from_storage(
StorageT& Storage,
OutputIteratorT DestBegin,
OutputIteratorT DestEnd )
{
OutputIteratorT OutputIt=DestBegin;
while( !Storage.empty() && OutputIt!=DestEnd )
{
*OutputIt=Storage.front();
Storage.pop_front();
++OutputIt;
}
return OutputIt;
}
template< typename StorageT, typename WhatT >
inline void copy_to_storage(
StorageT& Storage,
const WhatT& What )
{
Storage.insert( Storage.end(), ::boost::begin(What), ::boost::end(What) );
}
// process segment routine -----------------------------------------------//
template< bool HasStableIterators >
struct process_segment_helper
{
// Optimized version of process_segment for generic sequence
template<
typename StorageT,
typename InputT,
typename ForwardIteratorT >
ForwardIteratorT operator()(
StorageT& Storage,
InputT& /*Input*/,
ForwardIteratorT InsertIt,
ForwardIteratorT SegmentBegin,
ForwardIteratorT SegmentEnd )
{
// Copy data from the storage until the beginning of the segment
ForwardIteratorT It=::boost::algorithm::detail::move_from_storage( Storage, InsertIt, SegmentBegin );
// 3 cases are possible :
// a) Storage is empty, It==SegmentBegin
// b) Storage is empty, It!=SegmentBegin
// c) Storage is not empty
if( Storage.empty() )
{
if( It==SegmentBegin )
{
// Case a) everything is grand, just return end of segment
return SegmentEnd;
}
else
{
// Case b) move the segment backwards
return std::copy( SegmentBegin, SegmentEnd, It );
}
}
else
{
// Case c) -> shift the segment to the left and keep the overlap in the storage
while( It!=SegmentEnd )
{
// Store value into storage
Storage.push_back( *It );
// Get the top from the storage and put it here
*It=Storage.front();
Storage.pop_front();
// Advance
++It;
}
return It;
}
}
};
template<>
struct process_segment_helper< true >
{
// Optimized version of process_segment for list-like sequence
template<
typename StorageT,
typename InputT,
typename ForwardIteratorT >
ForwardIteratorT operator()(
StorageT& Storage,
InputT& Input,
ForwardIteratorT InsertIt,
ForwardIteratorT SegmentBegin,
ForwardIteratorT SegmentEnd )
{
// Call replace to do the job
::boost::algorithm::detail::replace( Input, InsertIt, SegmentBegin, Storage );
// Empty the storage
Storage.clear();
// Iterators were not changed, simply return the end of segment
return SegmentEnd;
}
};
// Process one segment in the replace_all algorithm
template<
typename StorageT,
typename InputT,
typename ForwardIteratorT >
inline ForwardIteratorT process_segment(
StorageT& Storage,
InputT& Input,
ForwardIteratorT InsertIt,
ForwardIteratorT SegmentBegin,
ForwardIteratorT SegmentEnd )
{
return
process_segment_helper<
has_stable_iterators<InputT>::value>()(
Storage, Input, InsertIt, SegmentBegin, SegmentEnd );
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_REPLACE_STORAGE_DETAIL_HPP

View File

@ -1,200 +0,0 @@
// Boost string_algo library sequence.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_DETAIL_SEQUENCE_HPP
#define BOOST_STRING_DETAIL_SEQUENCE_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/logical.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/algorithm/string/sequence_traits.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// insert helpers -------------------------------------------------//
template< typename InputT, typename ForwardIteratorT >
inline void insert(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator At,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
Input.insert( At, Begin, End );
}
template< typename InputT, typename InsertT >
inline void insert(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator At,
const InsertT& Insert )
{
::boost::algorithm::detail::insert( Input, At, ::boost::begin(Insert), ::boost::end(Insert) );
}
// erase helper ---------------------------------------------------//
// Erase a range in the sequence
/*
Returns the iterator pointing just after the erase subrange
*/
template< typename InputT >
inline typename InputT::iterator erase(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To )
{
return Input.erase( From, To );
}
// replace helper implementation ----------------------------------//
// Optimized version of replace for generic sequence containers
// Assumption: insert and erase are expensive
template< bool HasConstTimeOperations >
struct replace_const_time_helper
{
template< typename InputT, typename ForwardIteratorT >
void operator()(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
// Copy data to the container ( as much as possible )
ForwardIteratorT InsertIt=Begin;
BOOST_STRING_TYPENAME InputT::iterator InputIt=From;
for(; InsertIt!=End && InputIt!=To; InsertIt++, InputIt++ )
{
*InputIt=*InsertIt;
}
if ( InsertIt!=End )
{
// Replace sequence is longer, insert it
Input.insert( InputIt, InsertIt, End );
}
else
{
if ( InputIt!=To )
{
// Replace sequence is shorter, erase the rest
Input.erase( InputIt, To );
}
}
}
};
template<>
struct replace_const_time_helper< true >
{
// Const-time erase and insert methods -> use them
template< typename InputT, typename ForwardIteratorT >
void operator()(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
BOOST_STRING_TYPENAME InputT::iterator At=Input.erase( From, To );
if ( Begin!=End )
{
if(!Input.empty())
{
Input.insert( At, Begin, End );
}
else
{
Input.insert( Input.begin(), Begin, End );
}
}
}
};
// No native replace method
template< bool HasNative >
struct replace_native_helper
{
template< typename InputT, typename ForwardIteratorT >
void operator()(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
replace_const_time_helper<
boost::mpl::and_<
has_const_time_insert<InputT>,
has_const_time_erase<InputT> >::value >()(
Input, From, To, Begin, End );
}
};
// Container has native replace method
template<>
struct replace_native_helper< true >
{
template< typename InputT, typename ForwardIteratorT >
void operator()(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
Input.replace( From, To, Begin, End );
}
};
// replace helper -------------------------------------------------//
template< typename InputT, typename ForwardIteratorT >
inline void replace(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
ForwardIteratorT Begin,
ForwardIteratorT End )
{
replace_native_helper< has_native_replace<InputT>::value >()(
Input, From, To, Begin, End );
}
template< typename InputT, typename InsertT >
inline void replace(
InputT& Input,
BOOST_STRING_TYPENAME InputT::iterator From,
BOOST_STRING_TYPENAME InputT::iterator To,
const InsertT& Insert )
{
if(From!=To)
{
::boost::algorithm::detail::replace( Input, From, To, ::boost::begin(Insert), ::boost::end(Insert) );
}
else
{
::boost::algorithm::detail::insert( Input, From, ::boost::begin(Insert), ::boost::end(Insert) );
}
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_DETAIL_SEQUENCE_HPP

View File

@ -1,95 +0,0 @@
// Boost string_algo library trim.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_TRIM_DETAIL_HPP
#define BOOST_STRING_TRIM_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/detail/iterator.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// trim iterator helper -----------------------------------------------//
template< typename ForwardIteratorT, typename PredicateT >
inline ForwardIteratorT trim_end_iter_select(
ForwardIteratorT InBegin,
ForwardIteratorT InEnd,
PredicateT IsSpace,
std::forward_iterator_tag )
{
ForwardIteratorT TrimIt=InBegin;
for( ForwardIteratorT It=InBegin; It!=InEnd; ++It )
{
if ( !IsSpace(*It) )
{
TrimIt=It;
++TrimIt;
}
}
return TrimIt;
}
template< typename ForwardIteratorT, typename PredicateT >
inline ForwardIteratorT trim_end_iter_select(
ForwardIteratorT InBegin,
ForwardIteratorT InEnd,
PredicateT IsSpace,
std::bidirectional_iterator_tag )
{
for( ForwardIteratorT It=InEnd; It!=InBegin; )
{
if ( !IsSpace(*(--It)) )
return ++It;
}
return InBegin;
}
// Search for first non matching character from the beginning of the sequence
template< typename ForwardIteratorT, typename PredicateT >
inline ForwardIteratorT trim_begin(
ForwardIteratorT InBegin,
ForwardIteratorT InEnd,
PredicateT IsSpace )
{
ForwardIteratorT It=InBegin;
for(; It!=InEnd; ++It )
{
if (!IsSpace(*It))
return It;
}
return It;
}
// Search for first non matching character from the end of the sequence
template< typename ForwardIteratorT, typename PredicateT >
inline ForwardIteratorT trim_end(
ForwardIteratorT InBegin,
ForwardIteratorT InEnd,
PredicateT IsSpace )
{
typedef BOOST_STRING_TYPENAME boost::detail::
iterator_traits<ForwardIteratorT>::iterator_category category;
return ::boost::algorithm::detail::trim_end_iter_select( InBegin, InEnd, IsSpace, category() );
}
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_TRIM_DETAIL_HPP

View File

@ -1,107 +0,0 @@
// Boost string_algo library util.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_UTIL_DETAIL_HPP
#define BOOST_STRING_UTIL_DETAIL_HPP
#include <boost/algorithm/string/config.hpp>
#include <functional>
#include <boost/range/iterator_range_core.hpp>
namespace boost {
namespace algorithm {
namespace detail {
// empty container -----------------------------------------------//
// empty_container
/*
This class represents always empty container,
containing elements of type CharT.
It is supposed to be used in a const version only
*/
template< typename CharT >
struct empty_container
{
typedef empty_container<CharT> type;
typedef CharT value_type;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef const value_type& reference;
typedef const value_type& const_reference;
typedef const value_type* iterator;
typedef const value_type* const_iterator;
// Operations
const_iterator begin() const
{
return reinterpret_cast<const_iterator>(0);
}
const_iterator end() const
{
return reinterpret_cast<const_iterator>(0);
}
bool empty() const
{
return false;
}
size_type size() const
{
return 0;
}
};
// bounded copy algorithm -----------------------------------------------//
// Bounded version of the std::copy algorithm
template<typename InputIteratorT, typename OutputIteratorT>
inline OutputIteratorT bounded_copy(
InputIteratorT First,
InputIteratorT Last,
OutputIteratorT DestFirst,
OutputIteratorT DestLast )
{
InputIteratorT InputIt=First;
OutputIteratorT OutputIt=DestFirst;
for(; InputIt!=Last && OutputIt!=DestLast; InputIt++, OutputIt++ )
{
*OutputIt=*InputIt;
}
return OutputIt;
}
// iterator range utilities -----------------------------------------//
// copy range functor
template<
typename SeqT,
typename IteratorT=BOOST_STRING_TYPENAME SeqT::const_iterator >
struct copy_iterator_rangeF
{
typedef iterator_range<IteratorT> argument_type;
typedef SeqT result_type;
SeqT operator()( const iterator_range<IteratorT>& Range ) const
{
return copy_range<SeqT>(Range);
}
};
} // namespace detail
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_UTIL_DETAIL_HPP

View File

@ -1,844 +0,0 @@
// Boost string_algo library erase.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_ERASE_HPP
#define BOOST_STRING_ERASE_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/algorithm/string/find_format.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/formatter.hpp>
/*! \file
Defines various erase algorithms. Each algorithm removes
part(s) of the input according to a searching criteria.
*/
namespace boost {
namespace algorithm {
// erase_range -------------------------------------------------------//
//! Erase range algorithm
/*!
Remove the given range from the input. The result is a modified copy of
the input. It is returned as a sequence or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input sequence
\param SearchRange A range in the input to be removed
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<typename OutputIteratorT, typename RangeT>
inline OutputIteratorT erase_range_copy(
OutputIteratorT Output,
const RangeT& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type>& SearchRange )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase range algorithm
/*!
\overload
*/
template<typename SequenceT>
inline SequenceT erase_range_copy(
const SequenceT& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_const_iterator<SequenceT>::type>& SearchRange )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase range algorithm
/*!
Remove the given range from the input.
The input sequence is modified in-place.
\param Input An input sequence
\param SearchRange A range in the input to be removed
*/
template<typename SequenceT>
inline void erase_range(
SequenceT& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_iterator<SequenceT>::type>& SearchRange )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::empty_formatter(Input) );
}
// erase_first --------------------------------------------------------//
//! Erase first algorithm
/*!
Remove the first occurrence of the substring from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT erase_first_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase first algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT erase_first_copy(
const SequenceT& Input,
const RangeT& Search )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase first algorithm
/*!
Remove the first occurrence of the substring from the input.
The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for.
*/
template<typename SequenceT, typename RangeT>
inline void erase_first(
SequenceT& Input,
const RangeT& Search )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
// erase_first ( case insensitive ) ------------------------------------//
//! Erase first algorithm ( case insensitive )
/*!
Remove the first occurrence of the substring from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT ierase_first_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase first algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT ierase_first_copy(
const SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase first algorithm ( case insensitive )
/*!
Remove the first occurrence of the substring from the input.
The input sequence is modified in-place. Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename RangeT>
inline void ierase_first(
SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
// erase_last --------------------------------------------------------//
//! Erase last algorithm
/*!
Remove the last occurrence of the substring from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for.
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT erase_last_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase last algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT erase_last_copy(
const SequenceT& Input,
const RangeT& Search )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase last algorithm
/*!
Remove the last occurrence of the substring from the input.
The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for
*/
template<typename SequenceT, typename RangeT>
inline void erase_last(
SequenceT& Input,
const RangeT& Search )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
// erase_last ( case insensitive ) ------------------------------------//
//! Erase last algorithm ( case insensitive )
/*!
Remove the last occurrence of the substring from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT ierase_last_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase last algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT ierase_last_copy(
const SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase last algorithm ( case insensitive )
/*!
Remove the last occurrence of the substring from the input.
The input sequence is modified in-place. Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename RangeT>
inline void ierase_last(
SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
// erase_nth --------------------------------------------------------------------//
//! Erase nth algorithm
/*!
Remove the Nth occurrence of the substring in the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT erase_nth_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
int Nth )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase nth algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT erase_nth_copy(
const SequenceT& Input,
const RangeT& Search,
int Nth )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase nth algorithm
/*!
Remove the Nth occurrence of the substring in the input.
The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for.
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
*/
template<typename SequenceT, typename RangeT>
inline void erase_nth(
SequenceT& Input,
const RangeT& Search,
int Nth )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::empty_formatter(Input) );
}
// erase_nth ( case insensitive ) ---------------------------------------------//
//! Erase nth algorithm ( case insensitive )
/*!
Remove the Nth occurrence of the substring in the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for.
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT ierase_nth_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
int Nth,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase nth algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT ierase_nth_copy(
const SequenceT& Input,
const RangeT& Search,
int Nth,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc)),
empty_formatter(Input) );
}
//! Erase nth algorithm
/*!
Remove the Nth occurrence of the substring in the input.
The input sequence is modified in-place. Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for.
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename RangeT>
inline void ierase_nth(
SequenceT& Input,
const RangeT& Search,
int Nth,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
// erase_all --------------------------------------------------------//
//! Erase all algorithm
/*!
Remove all the occurrences of the string from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input sequence
\param Search A substring to be searched for.
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT erase_all_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search )
{
return ::boost::algorithm::find_format_all_copy(
Output,
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase all algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT erase_all_copy(
const SequenceT& Input,
const RangeT& Search )
{
return ::boost::algorithm::find_format_all_copy(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase all algorithm
/*!
Remove all the occurrences of the string from the input.
The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for.
*/
template<typename SequenceT, typename RangeT>
inline void erase_all(
SequenceT& Input,
const RangeT& Search )
{
::boost::algorithm::find_format_all(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::empty_formatter(Input) );
}
// erase_all ( case insensitive ) ------------------------------------//
//! Erase all algorithm ( case insensitive )
/*!
Remove all the occurrences of the string from the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT ierase_all_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_all_copy(
Output,
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase all algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT ierase_all_copy(
const SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_all_copy(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
//! Erase all algorithm ( case insensitive )
/*!
Remove all the occurrences of the string from the input.
The input sequence is modified in-place. Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for.
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename RangeT>
inline void ierase_all(
SequenceT& Input,
const RangeT& Search,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format_all(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::empty_formatter(Input) );
}
// erase_head --------------------------------------------------------------------//
//! Erase head algorithm
/*!
Remove the head from the input. The head is a prefix of a sequence of given size.
If the sequence is shorter then required, the whole string is
considered to be the head. The result is a modified copy of the input.
It is returned as a sequence or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param N Length of the head.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename RangeT>
inline OutputIteratorT erase_head_copy(
OutputIteratorT Output,
const RangeT& Input,
int N )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
//! Erase head algorithm
/*!
\overload
*/
template<typename SequenceT>
inline SequenceT erase_head_copy(
const SequenceT& Input,
int N )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
//! Erase head algorithm
/*!
Remove the head from the input. The head is a prefix of a sequence of given size.
If the sequence is shorter then required, the whole string is
considered to be the head. The input sequence is modified in-place.
\param Input An input string
\param N Length of the head
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
*/
template<typename SequenceT>
inline void erase_head(
SequenceT& Input,
int N )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
// erase_tail --------------------------------------------------------------------//
//! Erase tail algorithm
/*!
Remove the tail from the input. The tail is a suffix of a sequence of given size.
If the sequence is shorter then required, the whole string is
considered to be the tail.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param N Length of the tail.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename RangeT>
inline OutputIteratorT erase_tail_copy(
OutputIteratorT Output,
const RangeT& Input,
int N )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
//! Erase tail algorithm
/*!
\overload
*/
template<typename SequenceT>
inline SequenceT erase_tail_copy(
const SequenceT& Input,
int N )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
//! Erase tail algorithm
/*!
Remove the tail from the input. The tail is a suffix of a sequence of given size.
If the sequence is shorter then required, the whole string is
considered to be the tail. The input sequence is modified in-place.
\param Input An input string
\param N Length of the tail
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
*/
template<typename SequenceT>
inline void erase_tail(
SequenceT& Input,
int N )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::empty_formatter( Input ) );
}
} // namespace algorithm
// pull names into the boost namespace
using algorithm::erase_range_copy;
using algorithm::erase_range;
using algorithm::erase_first_copy;
using algorithm::erase_first;
using algorithm::ierase_first_copy;
using algorithm::ierase_first;
using algorithm::erase_last_copy;
using algorithm::erase_last;
using algorithm::ierase_last_copy;
using algorithm::ierase_last;
using algorithm::erase_nth_copy;
using algorithm::erase_nth;
using algorithm::ierase_nth_copy;
using algorithm::ierase_nth;
using algorithm::erase_all_copy;
using algorithm::erase_all;
using algorithm::ierase_all_copy;
using algorithm::ierase_all;
using algorithm::erase_head_copy;
using algorithm::erase_head;
using algorithm::erase_tail_copy;
using algorithm::erase_tail;
} // namespace boost
#endif // BOOST_ERASE_HPP

View File

@ -1,334 +0,0 @@
// Boost string_algo library find.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_HPP
#define BOOST_STRING_FIND_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <boost/algorithm/string/constants.hpp>
/*! \file
Defines a set of find algorithms. The algorithms are searching
for a substring of the input. The result is given as an \c iterator_range
delimiting the substring.
*/
namespace boost {
namespace algorithm {
// Generic find -----------------------------------------------//
//! Generic find algorithm
/*!
Search the input using the given finder.
\param Input A string which will be searched.
\param Finder Finder object used for searching.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c RangeT::iterator or
\c RangeT::const_iterator, depending on the constness of
the input parameter.
*/
template<typename RangeT, typename FinderT>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
find(
RangeT& Input,
const FinderT& Finder)
{
iterator_range<BOOST_STRING_TYPENAME range_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
return Finder(::boost::begin(lit_input),::boost::end(lit_input));
}
// find_first -----------------------------------------------//
//! Find first algorithm
/*!
Search for the first occurrence of the substring in the input.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c RangeT::iterator or
\c RangeT::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
find_first(
Range1T& Input,
const Range2T& Search)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::first_finder(Search));
}
//! Find first algorithm ( case insensitive )
/*!
Search for the first occurrence of the substring in the input.
Searching is case insensitive.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\param Loc A locale used for case insensitive comparison
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
ifind_first(
Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::find(Input, ::boost::algorithm::first_finder(Search,is_iequal(Loc)));
}
// find_last -----------------------------------------------//
//! Find last algorithm
/*!
Search for the last occurrence of the substring in the input.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
find_last(
Range1T& Input,
const Range2T& Search)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::last_finder(Search));
}
//! Find last algorithm ( case insensitive )
/*!
Search for the last match a string in the input.
Searching is case insensitive.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\param Loc A locale used for case insensitive comparison
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
ifind_last(
Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::find(Input, ::boost::algorithm::last_finder(Search, is_iequal(Loc)));
}
// find_nth ----------------------------------------------------------------------//
//! Find n-th algorithm
/*!
Search for the n-th (zero-indexed) occurrence of the substring in the
input.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\param Nth An index (zero-indexed) of the match to be found.
For negative N, the matches are counted from the end of string.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
find_nth(
Range1T& Input,
const Range2T& Search,
int Nth)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::nth_finder(Search,Nth));
}
//! Find n-th algorithm ( case insensitive ).
/*!
Search for the n-th (zero-indexed) occurrence of the substring in the
input. Searching is case insensitive.
\param Input A string which will be searched.
\param Search A substring to be searched for.
\param Nth An index (zero-indexed) of the match to be found.
For negative N, the matches are counted from the end of string.
\param Loc A locale used for case insensitive comparison
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<Range1T>::type>
ifind_nth(
Range1T& Input,
const Range2T& Search,
int Nth,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::find(Input, ::boost::algorithm::nth_finder(Search,Nth,is_iequal(Loc)));
}
// find_head ----------------------------------------------------------------------//
//! Find head algorithm
/*!
Get the head of the input. Head is a prefix of the string of the
given size. If the input is shorter then required, whole input is considered
to be the head.
\param Input An input string
\param N Length of the head
For N>=0, at most N characters are extracted.
For N<0, at most size(Input)-|N| characters are extracted.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c Range1T::iterator or
\c Range1T::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename RangeT>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
find_head(
RangeT& Input,
int N)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::head_finder(N));
}
// find_tail ----------------------------------------------------------------------//
//! Find tail algorithm
/*!
Get the tail of the input. Tail is a suffix of the string of the
given size. If the input is shorter then required, whole input is considered
to be the tail.
\param Input An input string
\param N Length of the tail.
For N>=0, at most N characters are extracted.
For N<0, at most size(Input)-|N| characters are extracted.
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c RangeT::iterator or
\c RangeT::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename RangeT>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
find_tail(
RangeT& Input,
int N)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::tail_finder(N));
}
// find_token --------------------------------------------------------------------//
//! Find token algorithm
/*!
Look for a given token in the string. Token is a character that matches the
given predicate.
If the "token compress mode" is enabled, adjacent tokens are considered to be one match.
\param Input A input string.
\param Pred A unary predicate to identify a token
\param eCompress Enable/Disable compressing of adjacent tokens
\return
An \c iterator_range delimiting the match.
Returned iterator is either \c RangeT::iterator or
\c RangeT::const_iterator, depending on the constness of
the input parameter.
\note This function provides the strong exception-safety guarantee
*/
template<typename RangeT, typename PredicateT>
inline iterator_range<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
find_token(
RangeT& Input,
PredicateT Pred,
token_compress_mode_type eCompress=token_compress_off)
{
return ::boost::algorithm::find(Input, ::boost::algorithm::token_finder(Pred, eCompress));
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::find;
using algorithm::find_first;
using algorithm::ifind_first;
using algorithm::find_last;
using algorithm::ifind_last;
using algorithm::find_nth;
using algorithm::ifind_nth;
using algorithm::find_head;
using algorithm::find_tail;
using algorithm::find_token;
} // namespace boost
#endif // BOOST_STRING_FIND_HPP

View File

@ -1,287 +0,0 @@
// Boost string_algo library find_format.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_FORMAT_HPP
#define BOOST_STRING_FIND_FORMAT_HPP
#include <deque>
#include <boost/detail/iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/concept.hpp>
#include <boost/algorithm/string/detail/find_format.hpp>
#include <boost/algorithm/string/detail/find_format_all.hpp>
/*! \file
Defines generic replace algorithms. Each algorithm replaces
part(s) of the input. The part to be replaced is looked up using a Finder object.
Result of finding is then used by a Formatter object to generate the replacement.
*/
namespace boost {
namespace algorithm {
// generic replace -----------------------------------------------------------------//
//! Generic replace algorithm
/*!
Use the Finder to search for a substring. Use the Formatter to format
this substring and replace it in the input.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input sequence
\param Finder A Finder object used to search for a match to be replaced
\param Formatter A Formatter object used to format a match
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename RangeT,
typename FinderT,
typename FormatterT>
inline OutputIteratorT find_format_copy(
OutputIteratorT Output,
const RangeT& Input,
FinderT Finder,
FormatterT Formatter )
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type>
));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
return detail::find_format_copy_impl(
Output,
lit_input,
Formatter,
Finder( ::boost::begin(lit_input), ::boost::end(lit_input) ) );
}
//! Generic replace algorithm
/*!
\overload
*/
template<
typename SequenceT,
typename FinderT,
typename FormatterT>
inline SequenceT find_format_copy(
const SequenceT& Input,
FinderT Finder,
FormatterT Formatter )
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
return detail::find_format_copy_impl(
Input,
Formatter,
Finder(::boost::begin(Input), ::boost::end(Input)));
}
//! Generic replace algorithm
/*!
Use the Finder to search for a substring. Use the Formatter to format
this substring and replace it in the input. The input is modified in-place.
\param Input An input sequence
\param Finder A Finder object used to search for a match to be replaced
\param Formatter A Formatter object used to format a match
*/
template<
typename SequenceT,
typename FinderT,
typename FormatterT>
inline void find_format(
SequenceT& Input,
FinderT Finder,
FormatterT Formatter)
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
detail::find_format_impl(
Input,
Formatter,
Finder(::boost::begin(Input), ::boost::end(Input)));
}
// find_format_all generic ----------------------------------------------------------------//
//! Generic replace all algorithm
/*!
Use the Finder to search for a substring. Use the Formatter to format
this substring and replace it in the input. Repeat this for all matching
substrings.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input sequence
\param Finder A Finder object used to search for a match to be replaced
\param Formatter A Formatter object used to format a match
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename RangeT,
typename FinderT,
typename FormatterT>
inline OutputIteratorT find_format_all_copy(
OutputIteratorT Output,
const RangeT& Input,
FinderT Finder,
FormatterT Formatter)
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type>
));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
return detail::find_format_all_copy_impl(
Output,
lit_input,
Finder,
Formatter,
Finder(::boost::begin(lit_input), ::boost::end(lit_input)));
}
//! Generic replace all algorithm
/*!
\overload
*/
template<
typename SequenceT,
typename FinderT,
typename FormatterT >
inline SequenceT find_format_all_copy(
const SequenceT& Input,
FinderT Finder,
FormatterT Formatter )
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
return detail::find_format_all_copy_impl(
Input,
Finder,
Formatter,
Finder( ::boost::begin(Input), ::boost::end(Input) ) );
}
//! Generic replace all algorithm
/*!
Use the Finder to search for a substring. Use the Formatter to format
this substring and replace it in the input. Repeat this for all matching
substrings.The input is modified in-place.
\param Input An input sequence
\param Finder A Finder object used to search for a match to be replaced
\param Formatter A Formatter object used to format a match
*/
template<
typename SequenceT,
typename FinderT,
typename FormatterT >
inline void find_format_all(
SequenceT& Input,
FinderT Finder,
FormatterT Formatter )
{
// Concept check
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
BOOST_CONCEPT_ASSERT((
FormatterConcept<
FormatterT,
FinderT,BOOST_STRING_TYPENAME range_const_iterator<SequenceT>::type>
));
detail::find_format_all_impl(
Input,
Finder,
Formatter,
Finder(::boost::begin(Input), ::boost::end(Input)));
}
} // namespace algorithm
// pull the names to the boost namespace
using algorithm::find_format_copy;
using algorithm::find_format;
using algorithm::find_format_all_copy;
using algorithm::find_format_all;
} // namespace boost
#endif // BOOST_STRING_FIND_FORMAT_HPP

View File

@ -1,388 +0,0 @@
// Boost string_algo library find_iterator.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2004.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FIND_ITERATOR_HPP
#define BOOST_STRING_FIND_ITERATOR_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/iterator/iterator_categories.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/detail/find_iterator.hpp>
/*! \file
Defines find iterator classes. Find iterator repeatedly applies a Finder
to the specified input string to search for matches. Dereferencing
the iterator yields the current match or a range between the last and the current
match depending on the iterator used.
*/
namespace boost {
namespace algorithm {
// find_iterator -----------------------------------------------//
//! find_iterator
/*!
Find iterator encapsulates a Finder and allows
for incremental searching in a string.
Each increment moves the iterator to the next match.
Find iterator is a readable forward traversal iterator.
Dereferencing the iterator yields an iterator_range delimiting
the current match.
*/
template<typename IteratorT>
class find_iterator :
public iterator_facade<
find_iterator<IteratorT>,
const iterator_range<IteratorT>,
forward_traversal_tag >,
private detail::find_iterator_base<IteratorT>
{
private:
// facade support
friend class ::boost::iterator_core_access;
private:
// typedefs
typedef detail::find_iterator_base<IteratorT> base_type;
typedef BOOST_STRING_TYPENAME
base_type::input_iterator_type input_iterator_type;
typedef BOOST_STRING_TYPENAME
base_type::match_type match_type;
public:
//! Default constructor
/*!
Construct null iterator. All null iterators are equal.
\post eof()==true
*/
find_iterator() {}
//! Copy constructor
/*!
Construct a copy of the find_iterator
*/
find_iterator( const find_iterator& Other ) :
base_type(Other),
m_Match(Other.m_Match),
m_End(Other.m_End) {}
//! Constructor
/*!
Construct new find_iterator for a given finder
and a range.
*/
template<typename FinderT>
find_iterator(
IteratorT Begin,
IteratorT End,
FinderT Finder ) :
detail::find_iterator_base<IteratorT>(Finder,0),
m_Match(Begin,Begin),
m_End(End)
{
increment();
}
//! Constructor
/*!
Construct new find_iterator for a given finder
and a range.
*/
template<typename FinderT, typename RangeT>
find_iterator(
RangeT& Col,
FinderT Finder ) :
detail::find_iterator_base<IteratorT>(Finder,0)
{
iterator_range<BOOST_STRING_TYPENAME range_iterator<RangeT>::type> lit_col(::boost::as_literal(Col));
m_Match=::boost::make_iterator_range(::boost::begin(lit_col), ::boost::begin(lit_col));
m_End=::boost::end(lit_col);
increment();
}
private:
// iterator operations
// dereference
const match_type& dereference() const
{
return m_Match;
}
// increment
void increment()
{
m_Match=this->do_find(m_Match.end(),m_End);
}
// comparison
bool equal( const find_iterator& Other ) const
{
bool bEof=eof();
bool bOtherEof=Other.eof();
return bEof || bOtherEof ? bEof==bOtherEof :
(
m_Match==Other.m_Match &&
m_End==Other.m_End
);
}
public:
// operations
//! Eof check
/*!
Check the eof condition. Eof condition means that
there is nothing more to be searched i.e. find_iterator
is after the last match.
*/
bool eof() const
{
return
this->is_null() ||
(
m_Match.begin() == m_End &&
m_Match.end() == m_End
);
}
private:
// Attributes
match_type m_Match;
input_iterator_type m_End;
};
//! find iterator construction helper
/*!
* Construct a find iterator to iterate through the specified string
*/
template<typename RangeT, typename FinderT>
inline find_iterator<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
make_find_iterator(
RangeT& Collection,
FinderT Finder)
{
return find_iterator<BOOST_STRING_TYPENAME range_iterator<RangeT>::type>(
Collection, Finder);
}
// split iterator -----------------------------------------------//
//! split_iterator
/*!
Split iterator encapsulates a Finder and allows
for incremental searching in a string.
Unlike the find iterator, split iterator iterates
through gaps between matches.
Find iterator is a readable forward traversal iterator.
Dereferencing the iterator yields an iterator_range delimiting
the current match.
*/
template<typename IteratorT>
class split_iterator :
public iterator_facade<
split_iterator<IteratorT>,
const iterator_range<IteratorT>,
forward_traversal_tag >,
private detail::find_iterator_base<IteratorT>
{
private:
// facade support
friend class ::boost::iterator_core_access;
private:
// typedefs
typedef detail::find_iterator_base<IteratorT> base_type;
typedef BOOST_STRING_TYPENAME
base_type::input_iterator_type input_iterator_type;
typedef BOOST_STRING_TYPENAME
base_type::match_type match_type;
public:
//! Default constructor
/*!
Construct null iterator. All null iterators are equal.
\post eof()==true
*/
split_iterator() :
m_Next(),
m_End(),
m_bEof(true)
{}
//! Copy constructor
/*!
Construct a copy of the split_iterator
*/
split_iterator( const split_iterator& Other ) :
base_type(Other),
m_Match(Other.m_Match),
m_Next(Other.m_Next),
m_End(Other.m_End),
m_bEof(Other.m_bEof)
{}
//! Constructor
/*!
Construct new split_iterator for a given finder
and a range.
*/
template<typename FinderT>
split_iterator(
IteratorT Begin,
IteratorT End,
FinderT Finder ) :
detail::find_iterator_base<IteratorT>(Finder,0),
m_Match(Begin,Begin),
m_Next(Begin),
m_End(End),
m_bEof(false)
{
// force the correct behavior for empty sequences and yield at least one token
if(Begin!=End)
{
increment();
}
}
//! Constructor
/*!
Construct new split_iterator for a given finder
and a collection.
*/
template<typename FinderT, typename RangeT>
split_iterator(
RangeT& Col,
FinderT Finder ) :
detail::find_iterator_base<IteratorT>(Finder,0),
m_bEof(false)
{
iterator_range<BOOST_STRING_TYPENAME range_iterator<RangeT>::type> lit_col(::boost::as_literal(Col));
m_Match=make_iterator_range(::boost::begin(lit_col), ::boost::begin(lit_col));
m_Next=::boost::begin(lit_col);
m_End=::boost::end(lit_col);
// force the correct behavior for empty sequences and yield at least one token
if(m_Next!=m_End)
{
increment();
}
}
private:
// iterator operations
// dereference
const match_type& dereference() const
{
return m_Match;
}
// increment
void increment()
{
match_type FindMatch=this->do_find( m_Next, m_End );
if(FindMatch.begin()==m_End && FindMatch.end()==m_End)
{
if(m_Match.end()==m_End)
{
// Mark iterator as eof
m_bEof=true;
}
}
m_Match=match_type( m_Next, FindMatch.begin() );
m_Next=FindMatch.end();
}
// comparison
bool equal( const split_iterator& Other ) const
{
bool bEof=eof();
bool bOtherEof=Other.eof();
return bEof || bOtherEof ? bEof==bOtherEof :
(
m_Match==Other.m_Match &&
m_Next==Other.m_Next &&
m_End==Other.m_End
);
}
public:
// operations
//! Eof check
/*!
Check the eof condition. Eof condition means that
there is nothing more to be searched i.e. find_iterator
is after the last match.
*/
bool eof() const
{
return this->is_null() || m_bEof;
}
private:
// Attributes
match_type m_Match;
input_iterator_type m_Next;
input_iterator_type m_End;
bool m_bEof;
};
//! split iterator construction helper
/*!
* Construct a split iterator to iterate through the specified collection
*/
template<typename RangeT, typename FinderT>
inline split_iterator<
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
make_split_iterator(
RangeT& Collection,
FinderT Finder)
{
return split_iterator<BOOST_STRING_TYPENAME range_iterator<RangeT>::type>(
Collection, Finder);
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::find_iterator;
using algorithm::make_find_iterator;
using algorithm::split_iterator;
using algorithm::make_split_iterator;
} // namespace boost
#endif // BOOST_STRING_FIND_ITERATOR_HPP

View File

@ -1,266 +0,0 @@
// Boost string_algo library finder.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FINDER_HPP
#define BOOST_STRING_FINDER_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/algorithm/string/constants.hpp>
#include <boost/algorithm/string/detail/finder.hpp>
#include <boost/algorithm/string/compare.hpp>
/*! \file
Defines Finder generators. Finder object is a functor which is able to
find a substring matching a specific criteria in the input.
Finders are used as a pluggable components for replace, find
and split facilities. This header contains generator functions
for finders provided in this library.
*/
namespace boost {
namespace algorithm {
// Finder generators ------------------------------------------//
//! "First" finder
/*!
Construct the \c first_finder. The finder searches for the first
occurrence of the string in a given input.
The result is given as an \c iterator_range delimiting the match.
\param Search A substring to be searched for.
\return An instance of the \c first_finder object
*/
template<typename RangeT>
inline detail::first_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
is_equal>
first_finder( const RangeT& Search )
{
return
detail::first_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
is_equal>( ::boost::as_literal(Search), is_equal() ) ;
}
//! "First" finder
/*!
\overload
*/
template<typename RangeT,typename PredicateT>
inline detail::first_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
PredicateT>
first_finder(
const RangeT& Search, PredicateT Comp )
{
return
detail::first_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
PredicateT>( ::boost::as_literal(Search), Comp );
}
//! "Last" finder
/*!
Construct the \c last_finder. The finder searches for the last
occurrence of the string in a given input.
The result is given as an \c iterator_range delimiting the match.
\param Search A substring to be searched for.
\return An instance of the \c last_finder object
*/
template<typename RangeT>
inline detail::last_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
is_equal>
last_finder( const RangeT& Search )
{
return
detail::last_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
is_equal>( ::boost::as_literal(Search), is_equal() );
}
//! "Last" finder
/*!
\overload
*/
template<typename RangeT, typename PredicateT>
inline detail::last_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
PredicateT>
last_finder( const RangeT& Search, PredicateT Comp )
{
return
detail::last_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
PredicateT>( ::boost::as_literal(Search), Comp ) ;
}
//! "Nth" finder
/*!
Construct the \c nth_finder. The finder searches for the n-th (zero-indexed)
occurrence of the string in a given input.
The result is given as an \c iterator_range delimiting the match.
\param Search A substring to be searched for.
\param Nth An index of the match to be find
\return An instance of the \c nth_finder object
*/
template<typename RangeT>
inline detail::nth_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
is_equal>
nth_finder(
const RangeT& Search,
int Nth)
{
return
detail::nth_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
is_equal>( ::boost::as_literal(Search), Nth, is_equal() ) ;
}
//! "Nth" finder
/*!
\overload
*/
template<typename RangeT, typename PredicateT>
inline detail::nth_finderF<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type,
PredicateT>
nth_finder(
const RangeT& Search,
int Nth,
PredicateT Comp )
{
return
detail::nth_finderF<
BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type,
PredicateT>( ::boost::as_literal(Search), Nth, Comp );
}
//! "Head" finder
/*!
Construct the \c head_finder. The finder returns a head of a given
input. The head is a prefix of a string up to n elements in
size. If an input has less then n elements, whole input is
considered a head.
The result is given as an \c iterator_range delimiting the match.
\param N The size of the head
\return An instance of the \c head_finder object
*/
inline detail::head_finderF
head_finder( int N )
{
return detail::head_finderF(N);
}
//! "Tail" finder
/*!
Construct the \c tail_finder. The finder returns a tail of a given
input. The tail is a suffix of a string up to n elements in
size. If an input has less then n elements, whole input is
considered a head.
The result is given as an \c iterator_range delimiting the match.
\param N The size of the head
\return An instance of the \c tail_finder object
*/
inline detail::tail_finderF
tail_finder( int N )
{
return detail::tail_finderF(N);
}
//! "Token" finder
/*!
Construct the \c token_finder. The finder searches for a token
specified by a predicate. It is similar to std::find_if
algorithm, with an exception that it return a range of
instead of a single iterator.
If "compress token mode" is enabled, adjacent matching tokens are
concatenated into one match. Thus the finder can be used to
search for continuous segments of characters satisfying the
given predicate.
The result is given as an \c iterator_range delimiting the match.
\param Pred An element selection predicate
\param eCompress Compress flag
\return An instance of the \c token_finder object
*/
template< typename PredicateT >
inline detail::token_finderF<PredicateT>
token_finder(
PredicateT Pred,
token_compress_mode_type eCompress=token_compress_off )
{
return detail::token_finderF<PredicateT>( Pred, eCompress );
}
//! "Range" finder
/*!
Construct the \c range_finder. The finder does not perform
any operation. It simply returns the given range for
any input.
\param Begin Beginning of the range
\param End End of the range
\return An instance of the \c range_finger object
*/
template< typename ForwardIteratorT >
inline detail::range_finderF<ForwardIteratorT>
range_finder(
ForwardIteratorT Begin,
ForwardIteratorT End )
{
return detail::range_finderF<ForwardIteratorT>( Begin, End );
}
//! "Range" finder
/*!
\overload
*/
template< typename ForwardIteratorT >
inline detail::range_finderF<ForwardIteratorT>
range_finder( iterator_range<ForwardIteratorT> Range )
{
return detail::range_finderF<ForwardIteratorT>( Range );
}
} // namespace algorithm
// pull the names to the boost namespace
using algorithm::first_finder;
using algorithm::last_finder;
using algorithm::nth_finder;
using algorithm::head_finder;
using algorithm::tail_finder;
using algorithm::token_finder;
using algorithm::range_finder;
} // namespace boost
#endif // BOOST_STRING_FINDER_HPP

View File

@ -1,120 +0,0 @@
// Boost string_algo library formatter.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_FORMATTER_HPP
#define BOOST_STRING_FORMATTER_HPP
#include <boost/detail/iterator.hpp>
#include <boost/range/value_type.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/detail/formatter.hpp>
/*! \file
Defines Formatter generators. Formatter is a functor which formats
a string according to given parameters. A Formatter works
in conjunction with a Finder. A Finder can provide additional information
for a specific Formatter. An example of such a cooperation is regex_finder
and regex_formatter.
Formatters are used as pluggable components for replace facilities.
This header contains generator functions for the Formatters provided in this library.
*/
namespace boost {
namespace algorithm {
// generic formatters ---------------------------------------------------------------//
//! Constant formatter
/*!
Constructs a \c const_formatter. Const formatter always returns
the same value, regardless of the parameter.
\param Format A predefined value used as a result for formatting
\return An instance of the \c const_formatter object.
*/
template<typename RangeT>
inline detail::const_formatF<
iterator_range<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> >
const_formatter(const RangeT& Format)
{
return detail::const_formatF<
iterator_range<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> >(::boost::as_literal(Format));
}
//! Identity formatter
/*!
Constructs an \c identity_formatter. Identity formatter always returns
the parameter.
\return An instance of the \c identity_formatter object.
*/
template<typename RangeT>
inline detail::identity_formatF<
iterator_range<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> >
identity_formatter()
{
return detail::identity_formatF<
iterator_range<
BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> >();
}
//! Empty formatter
/*!
Constructs an \c empty_formatter. Empty formatter always returns an empty
sequence.
\param Input container used to select a correct value_type for the
resulting empty_container<>.
\return An instance of the \c empty_formatter object.
*/
template<typename RangeT>
inline detail::empty_formatF<
BOOST_STRING_TYPENAME range_value<RangeT>::type>
empty_formatter(const RangeT&)
{
return detail::empty_formatF<
BOOST_STRING_TYPENAME range_value<RangeT>::type>();
}
//! Empty formatter
/*!
Constructs a \c dissect_formatter. Dissect formatter uses a specified finder
to extract a portion of the formatted sequence. The first finder's match is returned
as a result
\param Finder a finder used to select a portion of the formatted sequence
\return An instance of the \c dissect_formatter object.
*/
template<typename FinderT>
inline detail::dissect_formatF< FinderT >
dissect_formatter(const FinderT& Finder)
{
return detail::dissect_formatF<FinderT>(Finder);
}
} // namespace algorithm
// pull the names to the boost namespace
using algorithm::const_formatter;
using algorithm::identity_formatter;
using algorithm::empty_formatter;
using algorithm::dissect_formatter;
} // namespace boost
#endif // BOOST_FORMATTER_HPP

View File

@ -1,193 +0,0 @@
// Boost string_algo library iter_find.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_ITER_FIND_HPP
#define BOOST_STRING_ITER_FIND_HPP
#include <boost/algorithm/string/config.hpp>
#include <algorithm>
#include <iterator>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/value_type.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/algorithm/string/concept.hpp>
#include <boost/algorithm/string/find_iterator.hpp>
#include <boost/algorithm/string/detail/util.hpp>
/*! \file
Defines generic split algorithms. Split algorithms can be
used to divide a sequence into several part according
to a given criteria. Result is given as a 'container
of containers' where elements are copies or references
to extracted parts.
There are two algorithms provided. One iterates over matching
substrings, the other one over the gaps between these matches.
*/
namespace boost {
namespace algorithm {
// iterate find ---------------------------------------------------//
//! Iter find algorithm
/*!
This algorithm executes a given finder in iteration on the input,
until the end of input is reached, or no match is found.
Iteration is done using built-in find_iterator, so the real
searching is performed only when needed.
In each iteration new match is found and added to the result.
\param Result A 'container container' to contain the result of search.
Both outer and inner container must have constructor taking a pair
of iterators as an argument.
Typical type of the result is
\c std::vector<boost::iterator_range<iterator>>
(each element of such a vector will container a range delimiting
a match).
\param Input A container which will be searched.
\param Finder A Finder object used for searching
\return A reference to the result
\note Prior content of the result will be overwritten.
*/
template<
typename SequenceSequenceT,
typename RangeT,
typename FinderT >
inline SequenceSequenceT&
iter_find(
SequenceSequenceT& Result,
RangeT& Input,
FinderT Finder )
{
BOOST_CONCEPT_ASSERT((
FinderConcept<
FinderT,
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
));
iterator_range<BOOST_STRING_TYPENAME range_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
typedef BOOST_STRING_TYPENAME
range_iterator<RangeT>::type input_iterator_type;
typedef find_iterator<input_iterator_type> find_iterator_type;
typedef detail::copy_iterator_rangeF<
BOOST_STRING_TYPENAME
range_value<SequenceSequenceT>::type,
input_iterator_type> copy_range_type;
input_iterator_type InputEnd=::boost::end(lit_input);
typedef transform_iterator<copy_range_type, find_iterator_type>
transform_iter_type;
transform_iter_type itBegin=
::boost::make_transform_iterator(
find_iterator_type( ::boost::begin(lit_input), InputEnd, Finder ),
copy_range_type());
transform_iter_type itEnd=
::boost::make_transform_iterator(
find_iterator_type(),
copy_range_type());
SequenceSequenceT Tmp(itBegin, itEnd);
Result.swap(Tmp);
return Result;
}
// iterate split ---------------------------------------------------//
//! Split find algorithm
/*!
This algorithm executes a given finder in iteration on the input,
until the end of input is reached, or no match is found.
Iteration is done using built-in find_iterator, so the real
searching is performed only when needed.
Each match is used as a separator of segments. These segments are then
returned in the result.
\param Result A 'container container' to contain the result of search.
Both outer and inner container must have constructor taking a pair
of iterators as an argument.
Typical type of the result is
\c std::vector<boost::iterator_range<iterator>>
(each element of such a vector will container a range delimiting
a match).
\param Input A container which will be searched.
\param Finder A finder object used for searching
\return A reference to the result
\note Prior content of the result will be overwritten.
*/
template<
typename SequenceSequenceT,
typename RangeT,
typename FinderT >
inline SequenceSequenceT&
iter_split(
SequenceSequenceT& Result,
RangeT& Input,
FinderT Finder )
{
BOOST_CONCEPT_ASSERT((
FinderConcept<FinderT,
BOOST_STRING_TYPENAME range_iterator<RangeT>::type>
));
iterator_range<BOOST_STRING_TYPENAME range_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
typedef BOOST_STRING_TYPENAME
range_iterator<RangeT>::type input_iterator_type;
typedef split_iterator<input_iterator_type> find_iterator_type;
typedef detail::copy_iterator_rangeF<
BOOST_STRING_TYPENAME
range_value<SequenceSequenceT>::type,
input_iterator_type> copy_range_type;
input_iterator_type InputEnd=::boost::end(lit_input);
typedef transform_iterator<copy_range_type, find_iterator_type>
transform_iter_type;
transform_iter_type itBegin=
::boost::make_transform_iterator(
find_iterator_type( ::boost::begin(lit_input), InputEnd, Finder ),
copy_range_type() );
transform_iter_type itEnd=
::boost::make_transform_iterator(
find_iterator_type(),
copy_range_type() );
SequenceSequenceT Tmp(itBegin, itEnd);
Result.swap(Tmp);
return Result;
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::iter_find;
using algorithm::iter_split;
} // namespace boost
#endif // BOOST_STRING_ITER_FIND_HPP

View File

@ -1,145 +0,0 @@
// Boost string_algo library join.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_JOIN_HPP
#define BOOST_STRING_JOIN_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/algorithm/string/detail/sequence.hpp>
#include <boost/range/value_type.hpp>
#include <boost/range/as_literal.hpp>
/*! \file
Defines join algorithm.
Join algorithm is a counterpart to split algorithms.
It joins strings from a 'list' by adding user defined separator.
Additionally there is a version that allows simple filtering
by providing a predicate.
*/
namespace boost {
namespace algorithm {
// join --------------------------------------------------------------//
//! Join algorithm
/*!
This algorithm joins all strings in a 'list' into one long string.
Segments are concatenated by given separator.
\param Input A container that holds the input strings. It must be a container-of-containers.
\param Separator A string that will separate the joined segments.
\return Concatenated string.
\note This function provides the strong exception-safety guarantee
*/
template< typename SequenceSequenceT, typename Range1T>
inline typename range_value<SequenceSequenceT>::type
join(
const SequenceSequenceT& Input,
const Range1T& Separator)
{
// Define working types
typedef typename range_value<SequenceSequenceT>::type ResultT;
typedef typename range_const_iterator<SequenceSequenceT>::type InputIteratorT;
// Parse input
InputIteratorT itBegin=::boost::begin(Input);
InputIteratorT itEnd=::boost::end(Input);
// Construct container to hold the result
ResultT Result;
// Append first element
if(itBegin!=itEnd)
{
detail::insert(Result, ::boost::end(Result), *itBegin);
++itBegin;
}
for(;itBegin!=itEnd; ++itBegin)
{
// Add separator
detail::insert(Result, ::boost::end(Result), ::boost::as_literal(Separator));
// Add element
detail::insert(Result, ::boost::end(Result), *itBegin);
}
return Result;
}
// join_if ----------------------------------------------------------//
//! Conditional join algorithm
/*!
This algorithm joins all strings in a 'list' into one long string.
Segments are concatenated by given separator. Only segments that
satisfy the predicate will be added to the result.
\param Input A container that holds the input strings. It must be a container-of-containers.
\param Separator A string that will separate the joined segments.
\param Pred A segment selection predicate
\return Concatenated string.
\note This function provides the strong exception-safety guarantee
*/
template< typename SequenceSequenceT, typename Range1T, typename PredicateT>
inline typename range_value<SequenceSequenceT>::type
join_if(
const SequenceSequenceT& Input,
const Range1T& Separator,
PredicateT Pred)
{
// Define working types
typedef typename range_value<SequenceSequenceT>::type ResultT;
typedef typename range_const_iterator<SequenceSequenceT>::type InputIteratorT;
// Parse input
InputIteratorT itBegin=::boost::begin(Input);
InputIteratorT itEnd=::boost::end(Input);
// Construct container to hold the result
ResultT Result;
// Roll to the first element that will be added
while(itBegin!=itEnd && !Pred(*itBegin)) ++itBegin;
// Add this element
if(itBegin!=itEnd)
{
detail::insert(Result, ::boost::end(Result), *itBegin);
++itBegin;
}
for(;itBegin!=itEnd; ++itBegin)
{
if(Pred(*itBegin))
{
// Add separator
detail::insert(Result, ::boost::end(Result), ::boost::as_literal(Separator));
// Add element
detail::insert(Result, ::boost::end(Result), *itBegin);
}
}
return Result;
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::join;
using algorithm::join_if;
} // namespace boost
#endif // BOOST_STRING_JOIN_HPP

View File

@ -1,475 +0,0 @@
// Boost string_algo library predicate.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_PREDICATE_HPP
#define BOOST_STRING_PREDICATE_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/range/as_literal.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/algorithm/string/compare.hpp>
#include <boost/algorithm/string/find.hpp>
#include <boost/algorithm/string/detail/predicate.hpp>
/*! \file boost/algorithm/string/predicate.hpp
Defines string-related predicates.
The predicates determine whether a substring is contained in the input string
under various conditions: a string starts with the substring, ends with the
substring, simply contains the substring or if both strings are equal.
Additionaly the algorithm \c all() checks all elements of a container to satisfy a
condition.
All predicates provide the strong exception guarantee.
*/
namespace boost {
namespace algorithm {
// starts_with predicate -----------------------------------------------//
//! 'Starts with' predicate
/*!
This predicate holds when the test string is a prefix of the Input.
In other words, if the input starts with the test.
When the optional predicate is specified, it is used for character-wise
comparison.
\param Input An input sequence
\param Test A test sequence
\param Comp An element comparison predicate
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T, typename PredicateT>
inline bool starts_with(
const Range1T& Input,
const Range2T& Test,
PredicateT Comp)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range1T>::type> lit_input(::boost::as_literal(Input));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range2T>::type> lit_test(::boost::as_literal(Test));
typedef BOOST_STRING_TYPENAME
range_const_iterator<Range1T>::type Iterator1T;
typedef BOOST_STRING_TYPENAME
range_const_iterator<Range2T>::type Iterator2T;
Iterator1T InputEnd=::boost::end(lit_input);
Iterator2T TestEnd=::boost::end(lit_test);
Iterator1T it=::boost::begin(lit_input);
Iterator2T pit=::boost::begin(lit_test);
for(;
it!=InputEnd && pit!=TestEnd;
++it,++pit)
{
if( !(Comp(*it,*pit)) )
return false;
}
return pit==TestEnd;
}
//! 'Starts with' predicate
/*!
\overload
*/
template<typename Range1T, typename Range2T>
inline bool starts_with(
const Range1T& Input,
const Range2T& Test)
{
return ::boost::algorithm::starts_with(Input, Test, is_equal());
}
//! 'Starts with' predicate ( case insensitive )
/*!
This predicate holds when the test string is a prefix of the Input.
In other words, if the input starts with the test.
Elements are compared case insensitively.
\param Input An input sequence
\param Test A test sequence
\param Loc A locale used for case insensitive comparison
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline bool istarts_with(
const Range1T& Input,
const Range2T& Test,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::starts_with(Input, Test, is_iequal(Loc));
}
// ends_with predicate -----------------------------------------------//
//! 'Ends with' predicate
/*!
This predicate holds when the test string is a suffix of the Input.
In other words, if the input ends with the test.
When the optional predicate is specified, it is used for character-wise
comparison.
\param Input An input sequence
\param Test A test sequence
\param Comp An element comparison predicate
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T, typename PredicateT>
inline bool ends_with(
const Range1T& Input,
const Range2T& Test,
PredicateT Comp)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range1T>::type> lit_input(::boost::as_literal(Input));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range2T>::type> lit_test(::boost::as_literal(Test));
typedef BOOST_STRING_TYPENAME
range_const_iterator<Range1T>::type Iterator1T;
typedef BOOST_STRING_TYPENAME boost::detail::
iterator_traits<Iterator1T>::iterator_category category;
return detail::
ends_with_iter_select(
::boost::begin(lit_input),
::boost::end(lit_input),
::boost::begin(lit_test),
::boost::end(lit_test),
Comp,
category());
}
//! 'Ends with' predicate
/*!
\overload
*/
template<typename Range1T, typename Range2T>
inline bool ends_with(
const Range1T& Input,
const Range2T& Test)
{
return ::boost::algorithm::ends_with(Input, Test, is_equal());
}
//! 'Ends with' predicate ( case insensitive )
/*!
This predicate holds when the test container is a suffix of the Input.
In other words, if the input ends with the test.
Elements are compared case insensitively.
\param Input An input sequence
\param Test A test sequence
\param Loc A locale used for case insensitive comparison
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline bool iends_with(
const Range1T& Input,
const Range2T& Test,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::ends_with(Input, Test, is_iequal(Loc));
}
// contains predicate -----------------------------------------------//
//! 'Contains' predicate
/*!
This predicate holds when the test container is contained in the Input.
When the optional predicate is specified, it is used for character-wise
comparison.
\param Input An input sequence
\param Test A test sequence
\param Comp An element comparison predicate
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T, typename PredicateT>
inline bool contains(
const Range1T& Input,
const Range2T& Test,
PredicateT Comp)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range1T>::type> lit_input(::boost::as_literal(Input));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range2T>::type> lit_test(::boost::as_literal(Test));
if (::boost::empty(lit_test))
{
// Empty range is contained always
return true;
}
// Use the temporary variable to make VACPP happy
bool bResult=(::boost::algorithm::first_finder(lit_test,Comp)(::boost::begin(lit_input), ::boost::end(lit_input)));
return bResult;
}
//! 'Contains' predicate
/*!
\overload
*/
template<typename Range1T, typename Range2T>
inline bool contains(
const Range1T& Input,
const Range2T& Test)
{
return ::boost::algorithm::contains(Input, Test, is_equal());
}
//! 'Contains' predicate ( case insensitive )
/*!
This predicate holds when the test container is contained in the Input.
Elements are compared case insensitively.
\param Input An input sequence
\param Test A test sequence
\param Loc A locale used for case insensitive comparison
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline bool icontains(
const Range1T& Input,
const Range2T& Test,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::contains(Input, Test, is_iequal(Loc));
}
// equals predicate -----------------------------------------------//
//! 'Equals' predicate
/*!
This predicate holds when the test container is equal to the
input container i.e. all elements in both containers are same.
When the optional predicate is specified, it is used for character-wise
comparison.
\param Input An input sequence
\param Test A test sequence
\param Comp An element comparison predicate
\return The result of the test
\note This is a two-way version of \c std::equal algorithm
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T, typename PredicateT>
inline bool equals(
const Range1T& Input,
const Range2T& Test,
PredicateT Comp)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range1T>::type> lit_input(::boost::as_literal(Input));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range2T>::type> lit_test(::boost::as_literal(Test));
typedef BOOST_STRING_TYPENAME
range_const_iterator<Range1T>::type Iterator1T;
typedef BOOST_STRING_TYPENAME
range_const_iterator<Range2T>::type Iterator2T;
Iterator1T InputEnd=::boost::end(lit_input);
Iterator2T TestEnd=::boost::end(lit_test);
Iterator1T it=::boost::begin(lit_input);
Iterator2T pit=::boost::begin(lit_test);
for(;
it!=InputEnd && pit!=TestEnd;
++it,++pit)
{
if( !(Comp(*it,*pit)) )
return false;
}
return (pit==TestEnd) && (it==InputEnd);
}
//! 'Equals' predicate
/*!
\overload
*/
template<typename Range1T, typename Range2T>
inline bool equals(
const Range1T& Input,
const Range2T& Test)
{
return ::boost::algorithm::equals(Input, Test, is_equal());
}
//! 'Equals' predicate ( case insensitive )
/*!
This predicate holds when the test container is equal to the
input container i.e. all elements in both containers are same.
Elements are compared case insensitively.
\param Input An input sequence
\param Test A test sequence
\param Loc A locale used for case insensitive comparison
\return The result of the test
\note This is a two-way version of \c std::equal algorithm
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline bool iequals(
const Range1T& Input,
const Range2T& Test,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::equals(Input, Test, is_iequal(Loc));
}
// lexicographical_compare predicate -----------------------------//
//! Lexicographical compare predicate
/*!
This predicate is an overload of std::lexicographical_compare
for range arguments
It check whether the first argument is lexicographically less
then the second one.
If the optional predicate is specified, it is used for character-wise
comparison
\param Arg1 First argument
\param Arg2 Second argument
\param Pred Comparison predicate
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T, typename PredicateT>
inline bool lexicographical_compare(
const Range1T& Arg1,
const Range2T& Arg2,
PredicateT Pred)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range1T>::type> lit_arg1(::boost::as_literal(Arg1));
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<Range2T>::type> lit_arg2(::boost::as_literal(Arg2));
return std::lexicographical_compare(
::boost::begin(lit_arg1),
::boost::end(lit_arg1),
::boost::begin(lit_arg2),
::boost::end(lit_arg2),
Pred);
}
//! Lexicographical compare predicate
/*!
\overload
*/
template<typename Range1T, typename Range2T>
inline bool lexicographical_compare(
const Range1T& Arg1,
const Range2T& Arg2)
{
return ::boost::algorithm::lexicographical_compare(Arg1, Arg2, is_less());
}
//! Lexicographical compare predicate (case-insensitive)
/*!
This predicate is an overload of std::lexicographical_compare
for range arguments.
It check whether the first argument is lexicographically less
then the second one.
Elements are compared case insensitively
\param Arg1 First argument
\param Arg2 Second argument
\param Loc A locale used for case insensitive comparison
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename Range1T, typename Range2T>
inline bool ilexicographical_compare(
const Range1T& Arg1,
const Range2T& Arg2,
const std::locale& Loc=std::locale())
{
return ::boost::algorithm::lexicographical_compare(Arg1, Arg2, is_iless(Loc));
}
// all predicate -----------------------------------------------//
//! 'All' predicate
/*!
This predicate holds it all its elements satisfy a given
condition, represented by the predicate.
\param Input An input sequence
\param Pred A predicate
\return The result of the test
\note This function provides the strong exception-safety guarantee
*/
template<typename RangeT, typename PredicateT>
inline bool all(
const RangeT& Input,
PredicateT Pred)
{
iterator_range<BOOST_STRING_TYPENAME range_const_iterator<RangeT>::type> lit_input(::boost::as_literal(Input));
typedef BOOST_STRING_TYPENAME
range_const_iterator<RangeT>::type Iterator1T;
Iterator1T InputEnd=::boost::end(lit_input);
for( Iterator1T It=::boost::begin(lit_input); It!=InputEnd; ++It)
{
if (!Pred(*It))
return false;
}
return true;
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::starts_with;
using algorithm::istarts_with;
using algorithm::ends_with;
using algorithm::iends_with;
using algorithm::contains;
using algorithm::icontains;
using algorithm::equals;
using algorithm::iequals;
using algorithm::all;
using algorithm::lexicographical_compare;
using algorithm::ilexicographical_compare;
} // namespace boost
#endif // BOOST_STRING_PREDICATE_HPP

View File

@ -1,42 +0,0 @@
// Boost string_algo library predicate_facade.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_PREDICATE_FACADE_HPP
#define BOOST_STRING_PREDICATE_FACADE_HPP
#include <boost/algorithm/string/config.hpp>
/*
\file boost/algorith/string/predicate_facade.hpp
This file contains predicate_facade definition. This template class is used
to identify classification predicates, so they can be combined using
composition operators.
*/
namespace boost {
namespace algorithm {
// predicate facade ------------------------------------------------------//
//! Predicate facade
/*!
This class allows to recognize classification
predicates, so that they can be combined using
composition operators.
Every classification predicate must be derived from this class.
*/
template<typename Derived>
struct predicate_facade {};
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_CLASSIFICATION_DETAIL_HPP

View File

@ -1,926 +0,0 @@
// Boost string_algo library replace.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_REPLACE_HPP
#define BOOST_STRING_REPLACE_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/iterator.hpp>
#include <boost/range/const_iterator.hpp>
#include <boost/algorithm/string/find_format.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/formatter.hpp>
#include <boost/algorithm/string/compare.hpp>
/*! \file
Defines various replace algorithms. Each algorithm replaces
part(s) of the input according to set of searching and replace criteria.
*/
namespace boost {
namespace algorithm {
// replace_range --------------------------------------------------------------------//
//! Replace range algorithm
/*!
Replace the given range in the input string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param SearchRange A range in the input to be substituted
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT replace_range_copy(
OutputIteratorT Output,
const Range1T& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_const_iterator<Range1T>::type>& SearchRange,
const Range2T& Format)
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::const_formatter(Format));
}
//! Replace range algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT replace_range_copy(
const SequenceT& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_const_iterator<SequenceT>::type>& SearchRange,
const RangeT& Format)
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::const_formatter(Format));
}
//! Replace range algorithm
/*!
Replace the given range in the input string.
The input sequence is modified in-place.
\param Input An input string
\param SearchRange A range in the input to be substituted
\param Format A substitute string
*/
template<typename SequenceT, typename RangeT>
inline void replace_range(
SequenceT& Input,
const iterator_range<
BOOST_STRING_TYPENAME
range_iterator<SequenceT>::type>& SearchRange,
const RangeT& Format)
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::range_finder(SearchRange),
::boost::algorithm::const_formatter(Format));
}
// replace_first --------------------------------------------------------------------//
//! Replace first algorithm
/*!
Replace the first match of the search substring in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT replace_first_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format)
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace first algorithm
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT replace_first_copy(
const SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace first algorithm
/*!
replace the first match of the search substring in the input
with the format string. The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void replace_first(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
// replace_first ( case insensitive ) ---------------------------------------------//
//! Replace first algorithm ( case insensitive )
/*!
Replace the first match of the search substring in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT ireplace_first_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace first algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename Range2T, typename Range1T>
inline SequenceT ireplace_first_copy(
const SequenceT& Input,
const Range2T& Search,
const Range1T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace first algorithm ( case insensitive )
/*!
Replace the first match of the search substring in the input
with the format string. Input sequence is modified in-place.
Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void ireplace_first(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
// replace_last --------------------------------------------------------------------//
//! Replace last algorithm
/*!
Replace the last match of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT replace_last_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace last algorithm
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT replace_last_copy(
const SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace last algorithm
/*!
Replace the last match of the search string in the input
with the format string. Input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void replace_last(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::last_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
// replace_last ( case insensitive ) -----------------------------------------------//
//! Replace last algorithm ( case insensitive )
/*!
Replace the last match of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT ireplace_last_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace last algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT ireplace_last_copy(
const SequenceT& Input,
const Range1T& Search,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace last algorithm ( case insensitive )
/*!
Replace the last match of the search string in the input
with the format string.The input sequence is modified in-place.
Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void ireplace_last(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::last_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
// replace_nth --------------------------------------------------------------------//
//! Replace nth algorithm
/*!
Replace an Nth (zero-indexed) match of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT replace_nth_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
int Nth,
const Range3T& Format )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::const_formatter(Format) );
}
//! Replace nth algorithm
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT replace_nth_copy(
const SequenceT& Input,
const Range1T& Search,
int Nth,
const Range2T& Format )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::const_formatter(Format) );
}
//! Replace nth algorithm
/*!
Replace an Nth (zero-indexed) match of the search string in the input
with the format string. Input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Format A substitute string
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void replace_nth(
SequenceT& Input,
const Range1T& Search,
int Nth,
const Range2T& Format )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::nth_finder(Search, Nth),
::boost::algorithm::const_formatter(Format) );
}
// replace_nth ( case insensitive ) -----------------------------------------------//
//! Replace nth algorithm ( case insensitive )
/*!
Replace an Nth (zero-indexed) match of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT ireplace_nth_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
int Nth,
const Range3T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc) ),
::boost::algorithm::const_formatter(Format) );
}
//! Replace nth algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT ireplace_nth_copy(
const SequenceT& Input,
const Range1T& Search,
int Nth,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace nth algorithm ( case insensitive )
/*!
Replace an Nth (zero-indexed) match of the search string in the input
with the format string. Input sequence is modified in-place.
Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Nth An index of the match to be replaced. The index is 0-based.
For negative N, matches are counted from the end of string.
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void ireplace_nth(
SequenceT& Input,
const Range1T& Search,
int Nth,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::nth_finder(Search, Nth, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
// replace_all --------------------------------------------------------------------//
//! Replace all algorithm
/*!
Replace all occurrences of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT replace_all_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format )
{
return ::boost::algorithm::find_format_all_copy(
Output,
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace all algorithm
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT replace_all_copy(
const SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
return ::boost::algorithm::find_format_all_copy(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
//! Replace all algorithm
/*!
Replace all occurrences of the search string in the input
with the format string. The input sequence is modified in-place.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void replace_all(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format )
{
::boost::algorithm::find_format_all(
Input,
::boost::algorithm::first_finder(Search),
::boost::algorithm::const_formatter(Format) );
}
// replace_all ( case insensitive ) -----------------------------------------------//
//! Replace all algorithm ( case insensitive )
/*!
Replace all occurrences of the search string in the input
with the format string.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
Searching is case insensitive.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T,
typename Range3T>
inline OutputIteratorT ireplace_all_copy(
OutputIteratorT Output,
const Range1T& Input,
const Range2T& Search,
const Range3T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_all_copy(
Output,
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace all algorithm ( case insensitive )
/*!
\overload
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline SequenceT ireplace_all_copy(
const SequenceT& Input,
const Range1T& Search,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::find_format_all_copy(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
//! Replace all algorithm ( case insensitive )
/*!
Replace all occurrences of the search string in the input
with the format string.The input sequence is modified in-place.
Searching is case insensitive.
\param Input An input string
\param Search A substring to be searched for
\param Format A substitute string
\param Loc A locale used for case insensitive comparison
*/
template<typename SequenceT, typename Range1T, typename Range2T>
inline void ireplace_all(
SequenceT& Input,
const Range1T& Search,
const Range2T& Format,
const std::locale& Loc=std::locale() )
{
::boost::algorithm::find_format_all(
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc)),
::boost::algorithm::const_formatter(Format) );
}
// replace_head --------------------------------------------------------------------//
//! Replace head algorithm
/*!
Replace the head of the input with the given format string.
The head is a prefix of a string of given size.
If the sequence is shorter then required, whole string if
considered to be the head.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param N Length of the head.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT replace_head_copy(
OutputIteratorT Output,
const Range1T& Input,
int N,
const Range2T& Format )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::const_formatter(Format) );
}
//! Replace head algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT replace_head_copy(
const SequenceT& Input,
int N,
const RangeT& Format )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::const_formatter(Format) );
}
//! Replace head algorithm
/*!
Replace the head of the input with the given format string.
The head is a prefix of a string of given size.
If the sequence is shorter then required, the whole string is
considered to be the head. The input sequence is modified in-place.
\param Input An input string
\param N Length of the head.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\param Format A substitute string
*/
template<typename SequenceT, typename RangeT>
inline void replace_head(
SequenceT& Input,
int N,
const RangeT& Format )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::head_finder(N),
::boost::algorithm::const_formatter(Format) );
}
// replace_tail --------------------------------------------------------------------//
//! Replace tail algorithm
/*!
Replace the tail of the input with the given format string.
The tail is a suffix of a string of given size.
If the sequence is shorter then required, whole string is
considered to be the tail.
The result is a modified copy of the input. It is returned as a sequence
or copied to the output iterator.
\param Output An output iterator to which the result will be copied
\param Input An input string
\param N Length of the tail.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\param Format A substitute string
\return An output iterator pointing just after the last inserted character or
a modified copy of the input
\note The second variant of this function provides the strong exception-safety guarantee
*/
template<
typename OutputIteratorT,
typename Range1T,
typename Range2T>
inline OutputIteratorT replace_tail_copy(
OutputIteratorT Output,
const Range1T& Input,
int N,
const Range2T& Format )
{
return ::boost::algorithm::find_format_copy(
Output,
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::const_formatter(Format) );
}
//! Replace tail algorithm
/*!
\overload
*/
template<typename SequenceT, typename RangeT>
inline SequenceT replace_tail_copy(
const SequenceT& Input,
int N,
const RangeT& Format )
{
return ::boost::algorithm::find_format_copy(
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::const_formatter(Format) );
}
//! Replace tail algorithm
/*!
Replace the tail of the input with the given format sequence.
The tail is a suffix of a string of given size.
If the sequence is shorter then required, the whole string is
considered to be the tail. The input sequence is modified in-place.
\param Input An input string
\param N Length of the tail.
For N>=0, at most N characters are extracted.
For N<0, size(Input)-|N| characters are extracted.
\param Format A substitute string
*/
template<typename SequenceT, typename RangeT>
inline void replace_tail(
SequenceT& Input,
int N,
const RangeT& Format )
{
::boost::algorithm::find_format(
Input,
::boost::algorithm::tail_finder(N),
::boost::algorithm::const_formatter(Format) );
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::replace_range_copy;
using algorithm::replace_range;
using algorithm::replace_first_copy;
using algorithm::replace_first;
using algorithm::ireplace_first_copy;
using algorithm::ireplace_first;
using algorithm::replace_last_copy;
using algorithm::replace_last;
using algorithm::ireplace_last_copy;
using algorithm::ireplace_last;
using algorithm::replace_nth_copy;
using algorithm::replace_nth;
using algorithm::ireplace_nth_copy;
using algorithm::ireplace_nth;
using algorithm::replace_all_copy;
using algorithm::replace_all;
using algorithm::ireplace_all_copy;
using algorithm::ireplace_all;
using algorithm::replace_head_copy;
using algorithm::replace_head;
using algorithm::replace_tail_copy;
using algorithm::replace_tail;
} // namespace boost
#endif // BOOST_REPLACE_HPP

View File

@ -1,120 +0,0 @@
// Boost string_algo library sequence_traits.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_SEQUENCE_TRAITS_HPP
#define BOOST_STRING_SEQUENCE_TRAITS_HPP
#include <boost/config.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/algorithm/string/yes_no_type.hpp>
/*! \file
Traits defined in this header are used by various algorithms to achieve
better performance for specific containers.
Traits provide fail-safe defaults. If a container supports some of these
features, it is possible to specialize the specific trait for this container.
For lacking compilers, it is possible of define an override for a specific tester
function.
Due to a language restriction, it is not currently possible to define specializations for
stl containers without including the corresponding header. To decrease the overhead
needed by this inclusion, user can selectively include a specialization
header for a specific container. They are located in boost/algorithm/string/stl
directory. Alternatively she can include boost/algorithm/string/std_collection_traits.hpp
header which contains specializations for all stl containers.
*/
namespace boost {
namespace algorithm {
// sequence traits -----------------------------------------------//
//! Native replace trait
/*!
This trait specifies that the sequence has \c std::string like replace method
*/
template< typename T >
class has_native_replace
{
public:
# if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = false };
# else
BOOST_STATIC_CONSTANT(bool, value=false);
# endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_native_replace<T>::value> type;
};
//! Stable iterators trait
/*!
This trait specifies that the sequence has stable iterators. It means
that operations like insert/erase/replace do not invalidate iterators.
*/
template< typename T >
class has_stable_iterators
{
public:
# if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = false };
# else
BOOST_STATIC_CONSTANT(bool, value=false);
# endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_stable_iterators<T>::value> type;
};
//! Const time insert trait
/*!
This trait specifies that the sequence's insert method has
constant time complexity.
*/
template< typename T >
class has_const_time_insert
{
public:
# if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = false };
# else
BOOST_STATIC_CONSTANT(bool, value=false);
# endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_insert<T>::value> type;
};
//! Const time erase trait
/*!
This trait specifies that the sequence's erase method has
constant time complexity.
*/
template< typename T >
class has_const_time_erase
{
public:
# if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = false };
# else
BOOST_STATIC_CONSTANT(bool, value=false);
# endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_erase<T>::value> type;
};
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_SEQUENCE_TRAITS_HPP

View File

@ -1,163 +0,0 @@
// Boost string_algo library split.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2006.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_SPLIT_HPP
#define BOOST_STRING_SPLIT_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/algorithm/string/iter_find.hpp>
#include <boost/algorithm/string/finder.hpp>
#include <boost/algorithm/string/compare.hpp>
/*! \file
Defines basic split algorithms.
Split algorithms can be used to divide a string
into several parts according to given criteria.
Each part is copied and added as a new element to the
output container.
Thus the result container must be able to hold copies
of the matches (in a compatible structure like std::string) or
a reference to it (e.g. using the iterator range class).
Examples of such a container are \c std::vector<std::string>
or \c std::list<boost::iterator_range<std::string::iterator>>
*/
namespace boost {
namespace algorithm {
// find_all ------------------------------------------------------------//
//! Find all algorithm
/*!
This algorithm finds all occurrences of the search string
in the input.
Each part is copied and added as a new element to the
output container.
Thus the result container must be able to hold copies
of the matches (in a compatible structure like std::string) or
a reference to it (e.g. using the iterator range class).
Examples of such a container are \c std::vector<std::string>
or \c std::list<boost::iterator_range<std::string::iterator>>
\param Result A container that can hold copies of references to the substrings
\param Input A container which will be searched.
\param Search A substring to be searched for.
\return A reference the result
\note Prior content of the result will be overwritten.
\note This function provides the strong exception-safety guarantee
*/
template< typename SequenceSequenceT, typename Range1T, typename Range2T >
inline SequenceSequenceT& find_all(
SequenceSequenceT& Result,
Range1T& Input,
const Range2T& Search)
{
return ::boost::algorithm::iter_find(
Result,
Input,
::boost::algorithm::first_finder(Search) );
}
//! Find all algorithm ( case insensitive )
/*!
This algorithm finds all occurrences of the search string
in the input.
Each part is copied and added as a new element to the
output container. Thus the result container must be able to hold copies
of the matches (in a compatible structure like std::string) or
a reference to it (e.g. using the iterator range class).
Examples of such a container are \c std::vector<std::string>
or \c std::list<boost::iterator_range<std::string::iterator>>
Searching is case insensitive.
\param Result A container that can hold copies of references to the substrings
\param Input A container which will be searched.
\param Search A substring to be searched for.
\param Loc A locale used for case insensitive comparison
\return A reference the result
\note Prior content of the result will be overwritten.
\note This function provides the strong exception-safety guarantee
*/
template< typename SequenceSequenceT, typename Range1T, typename Range2T >
inline SequenceSequenceT& ifind_all(
SequenceSequenceT& Result,
Range1T& Input,
const Range2T& Search,
const std::locale& Loc=std::locale() )
{
return ::boost::algorithm::iter_find(
Result,
Input,
::boost::algorithm::first_finder(Search, is_iequal(Loc) ) );
}
// tokenize -------------------------------------------------------------//
//! Split algorithm
/*!
Tokenize expression. This function is equivalent to C strtok. Input
sequence is split into tokens, separated by separators. Separators
are given by means of the predicate.
Each part is copied and added as a new element to the
output container.
Thus the result container must be able to hold copies
of the matches (in a compatible structure like std::string) or
a reference to it (e.g. using the iterator range class).
Examples of such a container are \c std::vector<std::string>
or \c std::list<boost::iterator_range<std::string::iterator>>
\param Result A container that can hold copies of references to the substrings
\param Input A container which will be searched.
\param Pred A predicate to identify separators. This predicate is
supposed to return true if a given element is a separator.
\param eCompress If eCompress argument is set to token_compress_on, adjacent
separators are merged together. Otherwise, every two separators
delimit a token.
\return A reference the result
\note Prior content of the result will be overwritten.
\note This function provides the strong exception-safety guarantee
*/
template< typename SequenceSequenceT, typename RangeT, typename PredicateT >
inline SequenceSequenceT& split(
SequenceSequenceT& Result,
RangeT& Input,
PredicateT Pred,
token_compress_mode_type eCompress=token_compress_off )
{
return ::boost::algorithm::iter_split(
Result,
Input,
::boost::algorithm::token_finder( Pred, eCompress ) );
}
} // namespace algorithm
// pull names to the boost namespace
using algorithm::find_all;
using algorithm::ifind_all;
using algorithm::split;
} // namespace boost
#endif // BOOST_STRING_SPLIT_HPP

View File

@ -1,68 +0,0 @@
// Boost string_algo library list_traits.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_STD_LIST_TRAITS_HPP
#define BOOST_STRING_STD_LIST_TRAITS_HPP
#include <boost/algorithm/string/yes_no_type.hpp>
#include <list>
#include <boost/algorithm/string/sequence_traits.hpp>
namespace boost {
namespace algorithm {
// std::list<> traits -----------------------------------------------//
// stable iterators trait
template<typename T, typename AllocT>
class has_stable_iterators< ::std::list<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_stable_iterators<T>::value> type;
};
// const time insert trait
template<typename T, typename AllocT>
class has_const_time_insert< ::std::list<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_insert<T>::value> type;
};
// const time erase trait
template<typename T, typename AllocT>
class has_const_time_erase< ::std::list<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_erase<T>::value> type;
};
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_STD_LIST_TRAITS_HPP

View File

@ -1,69 +0,0 @@
// Boost string_algo library slist_traits.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_STD_SLIST_TRAITS_HPP
#define BOOST_STRING_STD_SLIST_TRAITS_HPP
#include <boost/algorithm/string/config.hpp>
#include <boost/algorithm/string/yes_no_type.hpp>
#include BOOST_SLIST_HEADER
#include <boost/algorithm/string/sequence_traits.hpp>
namespace boost {
namespace algorithm {
// SGI's std::slist<> traits -----------------------------------------------//
// stable iterators trait
template<typename T, typename AllocT>
class has_stable_iterators< BOOST_STD_EXTENSION_NAMESPACE::slist<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_stable_iterators<T>::value> type;
};
// const time insert trait
template<typename T, typename AllocT>
class has_const_time_insert< BOOST_STD_EXTENSION_NAMESPACE::slist<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_insert<T>::value> type;
};
// const time erase trait
template<typename T, typename AllocT>
class has_const_time_erase< BOOST_STD_EXTENSION_NAMESPACE::slist<T,AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true };
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_const_time_erase<T>::value> type;
};
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_STD_LIST_TRAITS_HPP

View File

@ -1,44 +0,0 @@
// Boost string_algo library string_traits.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_STD_STRING_TRAITS_HPP
#define BOOST_STRING_STD_STRING_TRAITS_HPP
#include <boost/algorithm/string/yes_no_type.hpp>
#include <string>
#include <boost/algorithm/string/sequence_traits.hpp>
namespace boost {
namespace algorithm {
// std::basic_string<> traits -----------------------------------------------//
// native replace trait
template<typename T, typename TraitsT, typename AllocT>
class has_native_replace< std::basic_string<T, TraitsT, AllocT> >
{
public:
#if BOOST_WORKAROUND( __IBMCPP__, <= 600 )
enum { value = true } ;
#else
BOOST_STATIC_CONSTANT(bool, value=true);
#endif // BOOST_WORKAROUND( __IBMCPP__, <= 600 )
typedef mpl::bool_<has_native_replace<T>::value> type;
};
} // namespace algorithm
} // namespace boost
#endif // BOOST_STRING_LIST_TRAITS_HPP

View File

@ -1,26 +0,0 @@
// Boost string_algo library std_containers_traits.hpp header file ---------------------------//
// Copyright Pavol Droba 2002-2003.
//
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// See http://www.boost.org/ for updates, documentation, and revision history.
#ifndef BOOST_STRING_STD_CONTAINERS_TRAITS_HPP
#define BOOST_STRING_STD_CONTAINERS_TRAITS_HPP
/*!\file
This file includes sequence traits for stl containers.
*/
#include <boost/config.hpp>
#include <boost/algorithm/string/std/string_traits.hpp>
#include <boost/algorithm/string/std/list_traits.hpp>
#ifdef BOOST_HAS_SLIST
# include <boost/algorithm/string/std/slist_traits.hpp>
#endif
#endif // BOOST_STRING_STD_CONTAINERS_TRAITS_HPP

Some files were not shown because too many files have changed in this diff Show More