mirror of
https://github.com/saitohirga/WSJT-X.git
synced 2024-11-25 13:48:42 -05:00
ee2badb86f
The UDP decode and reply message have been augmented with a boolean flag denoting a low confidence decode when set. Existing clients can safely use the reply message without passing the flag as the default value will still action messages that have high confidence. If low confidence decodes are to be passed back via the reply message then the low confidence flag must be included and correctly set to match the original decode. See NetworkMessage.hpp for message fields and meanings. git-svn-id: svn+ssh://svn.code.sf.net/p/wsjt/wsjt/branches/wsjtx@7957 ab8295b8-cf94-4d9e-aec4-7959e3be5d79
270 lines
9.6 KiB
C++
270 lines
9.6 KiB
C++
#include "ClientWidget.hpp"
|
|
|
|
#include <QRegExp>
|
|
#include <QColor>
|
|
|
|
namespace
|
|
{
|
|
//QRegExp message_alphabet {"[- A-Za-z0-9+./?]*"};
|
|
QRegExp message_alphabet {"[- @A-Za-z0-9+./?#<>]*"};
|
|
QRegularExpression cq_re {"(CQ|CQDX|QRZ)[^A-Z0-9/]+"};
|
|
|
|
void update_dynamic_property (QWidget * widget, char const * property, QVariant const& value)
|
|
{
|
|
widget->setProperty (property, value);
|
|
widget->style ()->unpolish (widget);
|
|
widget->style ()->polish (widget);
|
|
widget->update ();
|
|
}
|
|
}
|
|
|
|
ClientWidget::IdFilterModel::IdFilterModel (QString const& client_id)
|
|
: client_id_ {client_id}
|
|
, rx_df_ (-1)
|
|
{
|
|
}
|
|
|
|
QVariant ClientWidget::IdFilterModel::data (QModelIndex const& proxy_index, int role) const
|
|
{
|
|
if (role == Qt::BackgroundRole)
|
|
{
|
|
switch (proxy_index.column ())
|
|
{
|
|
case 6: // message
|
|
{
|
|
auto message = QSortFilterProxyModel::data (proxy_index).toString ();
|
|
if (base_call_re_.pattern ().size ()
|
|
&& message.contains (base_call_re_))
|
|
{
|
|
return QColor {255,200,200};
|
|
}
|
|
if (message.contains (cq_re))
|
|
{
|
|
return QColor {200, 255, 200};
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 4: // DF
|
|
if (qAbs (QSortFilterProxyModel::data (proxy_index).toInt () - rx_df_) <= 10)
|
|
{
|
|
return QColor {255, 200, 200};
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return QSortFilterProxyModel::data (proxy_index, role);
|
|
}
|
|
|
|
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_;
|
|
}
|
|
|
|
void ClientWidget::IdFilterModel::de_call (QString const& call)
|
|
{
|
|
if (call != call_)
|
|
{
|
|
beginResetModel ();
|
|
if (call.size ())
|
|
{
|
|
base_call_re_.setPattern ("[^A-Z0-9]*" + Radio::base_callsign (call) + "[^A-Z0-9]*");
|
|
}
|
|
else
|
|
{
|
|
base_call_re_.setPattern (QString {});
|
|
}
|
|
call_ = call;
|
|
endResetModel ();
|
|
}
|
|
}
|
|
|
|
void ClientWidget::IdFilterModel::rx_df (int df)
|
|
{
|
|
if (df != rx_df_)
|
|
{
|
|
beginResetModel ();
|
|
rx_df_ = df;
|
|
endResetModel ();
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
QString make_title (QString const& id, QString const& version, QString const& revision)
|
|
{
|
|
QString title {id};
|
|
if (version.size ())
|
|
{
|
|
title += QString {" v%1"}.arg (version);
|
|
}
|
|
if (revision.size ())
|
|
{
|
|
title += QString {" (%1)"}.arg (revision);
|
|
}
|
|
return title;
|
|
}
|
|
}
|
|
|
|
ClientWidget::ClientWidget (QAbstractItemModel * decodes_model, QAbstractItemModel * beacons_model
|
|
, QString const& id, QString const& version, QString const& revision
|
|
, QWidget * parent)
|
|
: QDockWidget {make_title (id, version, revision), parent}
|
|
, id_ {id}
|
|
, decodes_proxy_model_ {id_}
|
|
, decodes_table_view_ {new QTableView}
|
|
, beacons_table_view_ {new QTableView}
|
|
, message_line_edit_ {new QLineEdit}
|
|
, decodes_stack_ {new QStackedLayout}
|
|
, auto_off_button_ {new QPushButton {tr ("&Auto Off")}}
|
|
, halt_tx_button_ {new QPushButton {tr ("&Halt Tx")}}
|
|
, mode_label_ {new QLabel}
|
|
, fast_mode_ {false}
|
|
, frequency_label_ {new QLabel}
|
|
, dx_label_ {new QLabel}
|
|
, rx_df_label_ {new QLabel}
|
|
, tx_df_label_ {new QLabel}
|
|
, report_label_ {new QLabel}
|
|
{
|
|
// set up widgets
|
|
decodes_proxy_model_.setSourceModel (decodes_model);
|
|
decodes_table_view_->setModel (&decodes_proxy_model_);
|
|
decodes_table_view_->verticalHeader ()->hide ();
|
|
decodes_table_view_->hideColumn (0);
|
|
decodes_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
|
|
|
auto form_layout = new QFormLayout;
|
|
form_layout->addRow (tr ("Free text:"), message_line_edit_);
|
|
message_line_edit_->setValidator (new QRegExpValidator {message_alphabet, this});
|
|
connect (message_line_edit_, &QLineEdit::textEdited, [this] (QString const& text) {
|
|
Q_EMIT do_free_text (id_, text, false);
|
|
});
|
|
connect (message_line_edit_, &QLineEdit::editingFinished, [this] () {
|
|
Q_EMIT do_free_text (id_, message_line_edit_->text (), true);
|
|
});
|
|
|
|
auto decodes_page = new QWidget;
|
|
auto decodes_layout = new QVBoxLayout {decodes_page};
|
|
decodes_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
|
decodes_layout->addWidget (decodes_table_view_);
|
|
decodes_layout->addLayout (form_layout);
|
|
|
|
auto beacons_proxy_model = new IdFilterModel {id_};
|
|
beacons_proxy_model->setSourceModel (beacons_model);
|
|
beacons_table_view_->setModel (beacons_proxy_model);
|
|
beacons_table_view_->verticalHeader ()->hide ();
|
|
beacons_table_view_->hideColumn (0);
|
|
beacons_table_view_->horizontalHeader ()->setStretchLastSection (true);
|
|
|
|
auto beacons_page = new QWidget;
|
|
auto beacons_layout = new QVBoxLayout {beacons_page};
|
|
beacons_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
|
beacons_layout->addWidget (beacons_table_view_);
|
|
|
|
decodes_stack_->addWidget (decodes_page);
|
|
decodes_stack_->addWidget (beacons_page);
|
|
|
|
// stack alternative views
|
|
auto content_layout = new QVBoxLayout;
|
|
content_layout->setContentsMargins (QMargins {2, 2, 2, 2});
|
|
content_layout->addLayout (decodes_stack_);
|
|
|
|
// set up controls
|
|
auto control_button_box = new QDialogButtonBox;
|
|
control_button_box->addButton (auto_off_button_, QDialogButtonBox::ActionRole);
|
|
control_button_box->addButton (halt_tx_button_, QDialogButtonBox::ActionRole);
|
|
connect (auto_off_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
|
Q_EMIT do_halt_tx (id_, true);
|
|
});
|
|
connect (halt_tx_button_, &QAbstractButton::clicked, [this] (bool /* checked */) {
|
|
Q_EMIT do_halt_tx (id_, false);
|
|
});
|
|
content_layout->addWidget (control_button_box);
|
|
|
|
// set up status area
|
|
auto status_bar = new QStatusBar;
|
|
status_bar->addPermanentWidget (mode_label_);
|
|
status_bar->addPermanentWidget (frequency_label_);
|
|
status_bar->addPermanentWidget (dx_label_);
|
|
status_bar->addPermanentWidget (rx_df_label_);
|
|
status_bar->addPermanentWidget (tx_df_label_);
|
|
status_bar->addPermanentWidget (report_label_);
|
|
content_layout->addWidget (status_bar);
|
|
connect (this, &ClientWidget::topLevelChanged, status_bar, &QStatusBar::setSizeGripEnabled);
|
|
|
|
// set up central widget
|
|
auto content_widget = new QFrame;
|
|
content_widget->setFrameStyle (QFrame::StyledPanel | QFrame::Sunken);
|
|
content_widget->setLayout (content_layout);
|
|
setWidget (content_widget);
|
|
// setMinimumSize (QSize {550, 0});
|
|
setFeatures (DockWidgetMovable | DockWidgetFloatable);
|
|
setAllowedAreas (Qt::BottomDockWidgetArea);
|
|
|
|
// connect up table view signals
|
|
connect (decodes_table_view_, &QTableView::doubleClicked, this, [this] (QModelIndex const& index) {
|
|
Q_EMIT do_reply (decodes_proxy_model_.mapToSource (index));
|
|
});
|
|
}
|
|
|
|
void ClientWidget::update_status (QString const& id, 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
|
|
, bool watchdog_timeout, QString const& sub_mode, bool fast_mode)
|
|
{
|
|
if (id == id_)
|
|
{
|
|
fast_mode_ = fast_mode;
|
|
decodes_proxy_model_.de_call (de_call);
|
|
decodes_proxy_model_.rx_df (rx_df);
|
|
mode_label_->setText (QString {"Mode: %1%2%3%4"}
|
|
.arg (mode)
|
|
.arg (sub_mode)
|
|
.arg (fast_mode && !mode.contains (QRegularExpression {R"(ISCAT|MSK144)"}) ? "fast" : "")
|
|
.arg (tx_mode.isEmpty () || tx_mode == mode ? "" : '(' + tx_mode + ')'));
|
|
frequency_label_->setText ("QRG: " + Radio::pretty_frequency_MHz_string (f));
|
|
dx_label_->setText (dx_call.size () >= 0 ? QString {"DX: %1%2"}.arg (dx_call)
|
|
.arg (dx_grid.size () ? '(' + dx_grid + ')' : QString {}) : QString {});
|
|
rx_df_label_->setText (rx_df >= 0 ? QString {"Rx: %1"}.arg (rx_df) : "");
|
|
tx_df_label_->setText (tx_df >= 0 ? QString {"Tx: %1"}.arg (tx_df) : "");
|
|
report_label_->setText ("SNR: " + report);
|
|
update_dynamic_property (frequency_label_, "transmitting", transmitting);
|
|
auto_off_button_->setEnabled (tx_enabled);
|
|
halt_tx_button_->setEnabled (transmitting);
|
|
update_dynamic_property (mode_label_, "decoding", decoding);
|
|
update_dynamic_property (tx_df_label_, "watchdog_timeout", watchdog_timeout);
|
|
}
|
|
}
|
|
|
|
void ClientWidget::decode_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
|
, float /*delta_time*/, quint32 /*delta_frequency*/, QString const& /*mode*/
|
|
, QString const& /*message*/, bool /*low_confidence*/)
|
|
{
|
|
if (client_id == id_)
|
|
{
|
|
decodes_stack_->setCurrentIndex (0);
|
|
decodes_table_view_->resizeColumnsToContents ();
|
|
decodes_table_view_->scrollToBottom ();
|
|
}
|
|
}
|
|
|
|
void ClientWidget::beacon_spot_added (bool /*is_new*/, QString const& client_id, QTime /*time*/, qint32 /*snr*/
|
|
, float /*delta_time*/, Frequency /*delta_frequency*/, qint32 /*drift*/
|
|
, QString const& /*callsign*/, QString const& /*grid*/, qint32 /*power*/)
|
|
{
|
|
if (client_id == id_)
|
|
{
|
|
decodes_stack_->setCurrentIndex (1);
|
|
beacons_table_view_->resizeColumnsToContents ();
|
|
beacons_table_view_->scrollToBottom ();
|
|
}
|
|
}
|
|
|
|
#include "moc_ClientWidget.cpp"
|