11#include <wx/platinfo.h>
13#include <wx/statline.h>
14#include <wx/stattext.h>
15#include <wx/translation.h>
16#include <wx/wrapsizer.h>
19#include "androidUTIL.h"
22#include "model/base_platform.h"
26#include "model/nmea_log.h"
30#include "std_filesystem.h"
31#include "svg_button.h"
36#pragma clang diagnostic push
37#pragma ide diagnostic ignored "UnreachableCode"
41#if wxCHECK_VERSION(3, 2, 0)
42#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString()
44#define _(s) wxGetTranslation((s)).ToStdString()
47using SetFormatFunc = std::function<void(DataLogger::Format, std::string)>;
53T* GetWindowById(
int id) {
54 return dynamic_cast<T*
>(wxWindow::FindWindowById(
id));
57static const char*
const kFilterChoiceName =
"FilterChoiceWindow";
60static const std::unordered_map<NavAddr::Bus, std::string> kSourceByBus = {
61 {NavAddr::Bus::N0183,
"NMEA0183"},
62 {NavAddr::Bus::N2000,
"NMEA2000"},
63 {NavAddr::Bus::Signalk,
"SignalK"}};
66static bool IsUserFilter(
const std::string& filter_name) {
68 auto found = std::find(filters.begin(), filters.end(), filter_name);
69 if (found != filters.end())
return true;
70 for (
auto& f : filters) {
72 if (nf.m_description == filter_name)
return true;
78static std::string TimeStamp(
const NavmsgTimePoint& when,
79 const NavmsgTimePoint& since) {
80 using namespace std::chrono;
83 auto duration = when - since;
85 auto hrs = duration_cast<hours>(duration) % 24;
86 duration -= duration_cast<hours>(duration) / 24;
87 auto mins = duration_cast<minutes>(duration) % 60;
88 duration -= duration_cast<minutes>(duration) / 60;
89 auto secs = duration_cast<seconds>(duration) % 60;
90 duration -= duration_cast<seconds>(duration) / 60;
91 auto msecs = duration_cast<milliseconds>(duration);
92 ss << setw(2) << setfill(
'0') << hrs.count() <<
":" << setw(2) << mins.count()
93 <<
":" << setw(2) << secs.count() <<
"." << setw(3)
94 << msecs.count() % 1000;
104static std::string VdrQuote(
const std::string& arg) {
105 auto static const npos = std::string::npos;
106 if (arg.find(
',') == npos && arg.find(
'"') == npos)
return arg;
114 return "\"" + s +
"\"";
121static void AddVdrLogline(
const Logline& ll, std::ostream& stream) {
122 if (kSourceByBus.find(ll.navmsg->bus) == kSourceByBus.end())
return;
124 using namespace std::chrono;
125 auto now = steady_clock::now();
126 auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count();
129 stream << kSourceByBus.at(ll.navmsg->bus) <<
",";
130 stream << ll.navmsg->source->iface <<
",";
131 switch (ll.navmsg->bus) {
132 case NavAddr::Bus::N0183: {
133 auto msg0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(ll.navmsg);
134 stream << msg0183->talker << msg0183->type <<
",";
136 case NavAddr::Bus::N2000: {
137 auto msg2000 = std::dynamic_pointer_cast<const Nmea2000Msg>(ll.navmsg);
138 stream << msg2000->PGN.to_string() <<
",";
140 case NavAddr::Bus::Signalk: {
141 auto msgSignalK = std::dynamic_pointer_cast<const SignalkMsg>(ll.navmsg);
142 stream <<
"\"" << msgSignalK->context_self <<
"\",";
145 assert(
false &&
"Illegal message type");
147 stream << VdrQuote(ll.navmsg->to_vdr()) <<
"\n";
151static void AddStdLogline(
const Logline& ll, std::ostream& stream,
char fs,
152 const NavmsgTimePoint log_start) {
153 if (!ll.navmsg)
return;
155 ws << TimeStamp(ll.navmsg->created_at, log_start) << fs;
156 if (ll.state.direction == NavmsgStatus::Direction::kOutput)
157 ws << kUtfRightArrow << fs;
158 else if (ll.state.direction == NavmsgStatus::Direction::kInput)
159 ws << kUtfLeftwardsArrowToBar << fs;
160 else if (ll.state.direction == NavmsgStatus::Direction::kInternal)
161 ws << kUtfLeftRightArrow << fs;
163 ws << kUtfLeftArrow << fs;
164 if (ll.state.status != NavmsgStatus::State::kOk)
165 ws << kUtfMultiplicationX << fs;
166 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredNoOutput)
167 ws << kUtfFallingDiagonal << fs;
168 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredDropped)
169 ws << kUtfCircledDivisionSlash << fs;
171 ws << kUtfCheckMark << fs;
173 ws << ll.navmsg->source->iface << fs;
174 ws << NavAddr::BusToString(ll.navmsg->bus) << fs;
175 if (ll.state.status != NavmsgStatus::State::kOk)
176 ws << (!ll.error_msg.empty() ? ll.error_msg :
"Unknown errror");
179 ws << fs << ll.message <<
"\n";
186 TtyPanel(wxWindow* parent,
size_t lines)
187 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
188 wxTAB_TRAVERSAL,
"TtyPanel"),
189 m_tty_scroll(
nullptr),
190 m_filter(
this, wxID_ANY),
192 m_on_right_click([] {}) {
193 auto vbox =
new wxBoxSizer(wxVERTICAL);
194 m_tty_scroll =
new TtyScroll(
this, m_lines);
195 m_tty_scroll->Bind(wxEVT_RIGHT_UP,
196 [&](wxMouseEvent&) { m_on_right_click(); });
197 vbox->
Add(m_tty_scroll, wxSizerFlags(1).Expand().Border());
205 bool IsVisible()
const override {
return IsShownOnScreen(); }
207 void OnStop(
bool stop) {
208 m_tty_scroll->
Pause(stop);
210 m_tty_scroll->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT);
212 m_tty_scroll->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
217 void SetQuickFilter(
const std::string& filter) {
221 void SetOnRightClick(std::function<
void()> f) {
222 m_on_right_click = std::move(f);
227 auto window = wxWindow::FindWindowByName(
"TtyPanel");
229 auto tty_panel =
dynamic_cast<TtyPanel*
>(window);
230 if (tty_panel) tty_panel->
Add(ll);
234 wxSize DoGetBestClientSize()
const override {
235 return wxSize(-1, m_lines * GetCharHeight());
242 std::function<void()> m_on_right_click;
250 m_text_ctrl(
new wxTextCtrl(
this, wxID_ANY)),
251 m_on_text_evt(std::move(on_text_evt)) {
252 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
253 auto flags = wxSizerFlags(0).Border();
254 auto label_box =
new wxBoxSizer(wxVERTICAL);
255 label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Quick filter:")));
256 hbox->Add(label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
257 hbox->Add(m_text_ctrl, flags);
261 m_text_ctrl->Bind(wxEVT_TEXT, [&](wxCommandEvent&) { m_on_text_evt(); });
264 bool Show(
bool show =
true)
override {
265 if (!show) m_text_ctrl->SetValue(
"");
266 return wxWindow::Show(show);
269 std::string GetValue() {
return m_text_ctrl->GetValue().ToStdString(); }
272 wxTextCtrl* m_text_ctrl;
273 std::function<void()> m_on_text_evt;
280 : wxButton(parent, wxID_ANY), is_logging(
true), m_logger(logger) {
281 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
288 void UpdateTooltip() {
289 if (!IsThisEnabled())
290 SetToolTip(_(
"Set log file using menu to enable"));
292 SetToolTip(_(
"Click to stop logging"));
294 SetToolTip(_(
"Click to start logging"));
297 bool Enable(
bool enable)
override {
298 bool result = wxWindow::Enable(enable);
308 is_logging = !is_logging;
309 SetLabel(is_logging ? _(
"Stop") : _(
"Start"));
311 m_logger.SetLogging(is_logging);
319 : wxChoice(parent, wxID_ANY), m_tty_panel(tty_panel) {
320 SetName(kFilterChoiceName);
321 Bind(wxEVT_CHOICE, [&](wxCommandEvent&) { OnChoice(); });
322 OnFilterListChange();
323 int ix = FindString(
"Default settings");
324 if (ix != wxNOT_FOUND) SetSelection(ix);
325 NavmsgFilter filter = filters_on_disk::Read(
"default.filter");
326 m_tty_panel->SetFilter(filter);
329 void OnFilterListChange() {
331 int select_ix = GetSelection();
332 std::string selected;
333 if (select_ix != wxNOT_FOUND) selected = GetString(select_ix).ToStdString();
335 for (
auto& filter : m_filters) {
337 Append(kLabels.at(filter.m_name));
338 }
catch (std::out_of_range&) {
339 if (filter.m_description.empty())
340 Append(filter.m_name);
342 Append(filter.m_description);
345 if (!selected.empty()) {
346 int ix = FindString(selected);
347 SetSelection(ix == wxNOT_FOUND ? 0 : ix);
351 void OnFilterUpdate(
const std::string& name) {
353 int select_ix = GetSelection();
354 if (select_ix == wxNOT_FOUND)
return;
356 std::string selected = GetString(select_ix).ToStdString();
357 if (selected != name)
return;
360 m_tty_panel->SetFilter(filter);
363 void OnApply(
const std::string& name) {
364 int found = FindString(name);
365 if (found == wxNOT_FOUND) {
366 for (
auto& filter : m_filters) {
367 if (filter.m_name == name) {
368 found = FindString(filter.m_description);
373 if (found == wxNOT_FOUND)
return;
376 OnFilterUpdate(name);
382 const std::unordered_map<std::string, std::string> kLabels = {
383 {
"all-data", _(
"All data")},
384 {
"all-nmea", _(
"All NMEA data")},
385 {
"malformed", _(
"Malformed messages")},
386 {
"nmea-input", _(
"NMEA input data")},
387 {
"nmea-output", _(
"NMEA output data")},
388 {
"plugins", _(
"Messages to plugins")},
391 std::vector<NavmsgFilter> m_filters;
395 wxString label = GetString(GetSelection());
396 NavmsgFilter filter = FilterByLabel(label.ToStdString());
397 m_tty_panel->SetFilter(filter);
402 for (
const auto& kv : kLabels) {
403 if (kv.second == label) {
409 for (
auto& f : m_filters)
410 if (f.m_name == name)
return f;
412 for (
auto& f : m_filters)
413 if (f.m_description == label)
return f;
423 : wxButton(parent, wxID_ANY),
425 m_on_stop(std::move(on_stop)) {
426 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
432 std::function<void(
bool)> m_on_stop;
435 is_paused = !is_paused;
436 m_on_stop(is_paused);
437 SetLabel(is_paused ? _(
"Resume") : _(
"Pause"));
450 m_set_logtype(set_logtype),
452 kFilenameLabelId(wxWindow::NewControlId()) {
453 auto flags = wxSizerFlags(0).Border();
456 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
457 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
458 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
460 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
461 default_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
462 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
464 default_btn->SetValue(
true);
465 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
466 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
467 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
469 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
470 left_vbox->Add(default_btn, flags.DoubleBorder());
471 left_vbox->Add(vdr_btn, flags);
472 left_vbox->Add(csv_btn, flags);
475 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
476 auto label =
new wxStaticText(
this, kFilenameLabelId,
477 m_logger.GetDefaultLogfile().string());
478 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
479 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
481 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
482 force_box->Bind(wxEVT_CHECKBOX,
483 [&](wxCommandEvent& e) { m_overwrite = e.IsChecked(); });
484 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
485 right_vbox->Add(label, flags);
486 right_vbox->Add(path_btn, flags);
487 right_vbox->Add(force_box, flags);
490 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
491 hbox->Add(left_vbox, flags);
492 hbox->Add(GetCharWidth() * 10, 0, 1);
493 hbox->Add(right_vbox, flags);
498 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
500 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
504 void OnFileDialog() {
506 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
507 wxFileDialog dlg(m_parent, _(
"Select logfile"),
508 m_logger.GetDefaultLogfile().parent_path().string(),
509 m_logger.GetDefaultLogfile().stem().string(),
510 m_logger.GetFileDlgTypes(),
options);
511 if (dlg.ShowModal() == wxID_CANCEL)
return;
512 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
513 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
514 file_label->SetLabel(dlg.GetPath());
518 SetFormatFunc m_set_logtype;
520 const int kFilenameLabelId;
525 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
526 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
527 auto flags = wxSizerFlags(0).Border();
530 auto buttons =
new wxStdDialogButtonSizer();
531 auto close_btn =
new wxButton(
this, wxID_CLOSE);
532 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
533 [&](wxCommandEvent& ev) { Destroy(); });
534 buttons->AddButton(close_btn);
536 buttons->Fit(parent);
539 auto panel =
new ThePanel(
this, set_logtype, logger);
540 auto vbox =
new wxBoxSizer(wxVERTICAL);
541 vbox->Add(panel, flags.Expand());
542 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
543 vbox->Add(buttons, flags.Expand());
554 enum class Id :
char {
564 wxWindow* log_button)
566 m_log_button(log_button),
568 m_log_label(log_label) {
569 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
570 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
571 auto filters =
new wxMenu(
"");
572 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
573 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
574 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
575 AppendSubMenu(filters, _(
"Filters..."));
576 if (IsUserFilter(m_filter))
577 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
579 Bind(wxEVT_MENU, [&](wxCommandEvent& ev) {
580 switch (
static_cast<Id
>(ev.GetId())) {
585 case Id::kViewStdColors:
586 SetColor(static_cast<int>(Id::kViewStdColors));
590 CreateFilterDlg(parent);
593 case Id::kEditFilter:
594 EditFilterDlg(wxTheApp->GetTopWindow());
597 case Id::kEditActiveFilter:
598 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
601 case Id::kDeleteFilter:
602 RemoveFilterDlg(parent);
606 Check(
static_cast<int>(Id::kViewStdColors),
true);
609 void SetFilterName(
const std::string& filter) {
610 int id =
static_cast<int>(Id::kEditActiveFilter);
611 if (FindItem(
id)) Delete(
id);
612 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
617 wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
618 return root->Append(
static_cast<int>(
id), label);
621 void SetLogFormat(DataLogger::Format format,
const std::string& label) {
622 m_log_label->SetLabel(_(
"Log type: ") + label);
623 m_logger.SetFormat(format);
624 m_log_button->Enable();
625 std::string extension =
626 format == DataLogger::Format::kDefault ?
".log" :
".csv";
627 fs::path path = m_logger.GetLogfile();
628 path = path.parent_path() / (path.stem().string() + extension);
629 m_logger.SetLogfile(path);
635 [&](DataLogger::Format f, std::string s) { SetLogFormat(f, s); },
638 m_parent->GetParent()->Layout();
641 void SetColor(
int id) {
642 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
643 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
644 if (!tty_scroll)
return;
646 wxMenuItem* item = FindItem(
id);
648 if (item->IsCheck() && item->IsChecked())
649 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
651 tty_scroll->SetColors(
652 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
656 wxWindow* m_log_button;
658 wxStaticText* m_log_label;
659 std::string m_filter;
667 SetToolTip(_(
"Copy to clipboard"));
668 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
670 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
680 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
681 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
686 wxWindow* m_quick_filter;
690 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
691 m_show_filter = !m_show_filter;
692 m_quick_filter->Show(m_show_filter);
693 SetToolTip(m_show_filter ? _(
"Close quick filter")
694 : _(
"Open quick filter"));
695 GetGrandParent()->Layout();
703 std::function<std::string()> get_current_filter)
704 : wxButton(parent, wxID_ANY, wxEmptyString, wxDefaultPosition,
705 wxDefaultSize, wxBU_EXACTFIT),
707 m_get_current_filter(std::move(get_current_filter)) {
708 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
709 SetLabel(kUtfIdenticalTo);
710 SetToolTip(_(
"Open menu"));
715 std::function<std::string()> m_get_current_filter;
718 m_menu.SetFilterName(m_get_current_filter());
727 std::function<
void(
bool)> on_stop,
DataLogger& logger)
730 m_log_button(
new LogButton(
this, logger)),
731 m_log_label(
new wxStaticText(
this, wxID_ANY, _(
"Logging: Default"))),
733 m_menu(
this, m_log_label, logger, m_log_button) {
735 auto log_label_box =
new wxBoxSizer(wxVERTICAL);
736 log_label_box->Add(m_log_label);
737 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
738 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"View")));
740 auto flags = wxSizerFlags(0).Border();
741 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
742 wbox->Add(log_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
743 wbox->Add(m_log_button, flags);
746 wbox->Add(GetCharWidth() * 2, 0, 1);
747 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
748 wbox->Add(m_filter_choice, flags);
751 auto get_current_filter = [&] {
752 return m_filter_choice->GetStringSelection().ToStdString();
755 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
760 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
764 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
765 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
770 void OnContextClick() {
771 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
778 wxSize DoGetBestClientSize()
const override {
780 return wxSize(-1, -1);
782 return wxSize(85 * GetCharWidth(), 2.5 * GetCharHeight());
787 wxButton* m_log_button;
788 wxStaticText* m_log_label;
789 wxChoice* m_filter_choice;
793DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
796 m_stream(path, std::ios_base::app),
798 m_format(Format::kDefault),
799 m_log_start(std::chrono::steady_clock::now()) {}
801DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
803void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
805void DataLogger::SetLogfile(
const fs::path& path) {
806 m_stream = std::ofstream(path);
807 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
808 const auto now = std::chrono::system_clock::now();
809 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
810 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
811 m_stream <<
"received_at,protocol,msg_type,source,raw_data\n";
812 m_stream << std::flush;
817void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
819fs::path DataLogger::NullLogfile() {
820 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
826fs::path DataLogger::GetDefaultLogfile() {
827 if (m_path.stem() != NullLogfile().stem())
return m_path;
828 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
830 path += (m_format == Format::kDefault ?
".log" :
".csv");
834std::string DataLogger::GetFileDlgTypes() {
835 if (m_format == Format::kDefault)
836 return _(
"Log file (*.log)|*.log");
838 return _(
"Spreadsheet csv file(*.csv)|*.csv");
841void DataLogger::Add(
const Logline& ll) {
842 if (!m_is_logging || !ll.navmsg)
return;
843 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
844 if (m_format == DataLogger::Format::kVdr)
845 AddVdrLogline(ll, m_stream);
847 AddStdLogline(ll, m_stream,
848 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
852DataMonitor::DataMonitor(wxWindow* parent)
853 : wxFrame(parent, wxID_ANY,
"Data Monitor", wxDefaultPosition,
854 wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
855 kDataMonitorWindowName),
856 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
857 auto msg = std::dynamic_pointer_cast<const Nmea0183Msg>(navmsg);
860 m_quick_filter(
nullptr),
862 auto vbox =
new wxBoxSizer(wxVERTICAL);
863 auto tty_panel =
new TtyPanel(
this, 12);
864 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
865 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
867 auto on_quick_filter_evt = [&, tty_panel] {
869 assert(quick_filter);
870 std::string value = quick_filter->GetValue();
871 tty_panel->SetQuickFilter(value);
874 vbox->Add(m_quick_filter, wxSizerFlags());
876 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
878 new StatusLine(
this, m_quick_filter, tty_panel, on_stop, m_logger);
879 vbox->Add(status_line, wxSizerFlags().Expand());
884 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
885 tty_panel->SetQuickFilter(GetLabel().ToStdString());
887 m_quick_filter->Hide();
888 tty_panel->SetOnRightClick(
889 [&, status_line] { status_line->OnContextClick(); });
891 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
892 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
893 status_line->OnContextClick();
896 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
898 m_filter_update_lstnr.Init(
899 FilterEvents::GetInstance().filter_update,
900 [&](
ObservedEvt& ev) { OnFilterUpdate(ev.GetString().ToStdString()); });
902 m_filter_apply_lstnr.Init(
903 FilterEvents::GetInstance().filter_apply,
904 [&](
ObservedEvt& ev) { OnFilterApply(ev.GetString().ToStdString()); });
913 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
914 assert(w &&
"No TtyPanel found");
915 return w->IsShownOnScreen();
918void DataMonitor::OnFilterListChange() {
919 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
922 assert(filter_choice &&
"Wrong FilterChoice type (!)");
923 filter_choice->OnFilterListChange();
926void DataMonitor::OnFilterUpdate(
const std::string& name) {
927 if (name != m_current_filter)
return;
928 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
930 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
931 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
932 tty_scroll->SetFilter(filters_on_disk::Read(name));
935void DataMonitor::OnFilterApply(
const std::string& name) {
936 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
939 assert(filter_choice &&
"Wrong FilterChoice type (!)");
940 m_current_filter = name;
941 filter_choice->OnApply(name);
944#pragma clang diagnostic pop
EventVar OnNewLogfile
Notified with new path on filename change.
void Add(const Logline &ll) override
Add an input line to log output.
virtual bool IsVisible() const override
Return true if log is visible i.
const void Notify()
Notify all listeners, no data supplied.
Offer user to select current filter.
Log setup window invoked from menu "Logging" item.
Actual data sent between application and transport layer.
static std::vector< NavmsgFilter > GetAllFilters()
Return list of all filters, system + user defined.
Define an action to be performed when a KeyProvider is notified.
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Custom event class for OpenCPN's notification system.
The quick filter above the status line, invoked by funnel button.
Overall bottom status line.
Main window, a rolling log of messages.
void Add(const Logline &ll) override
Add an formatted string to log output.
static void AddIfExists(const Logline &ll)
Invoke Add(s) for possibly existing instance.
bool IsVisible() const override
Return true if log is visible i.
New NMEA Debugger successor main window.
Provide a data stream of input messages for the Data Monitor.
Dialogs handing user defined filters.
std::vector< std::string > List(bool include_system)
Return list of filters, possibly including also the system ones.
NavmsgFilter Read(const std::string &name)
Read filter with given name from disk.
Hooks into gui available in model.
Data monitor filter definitions.