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 = system_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 : wxChoice(parent, wxID_ANY), m_tty_panel(tty_panel) {
281 SetName(kFilterChoiceName);
282 Bind(wxEVT_CHOICE, [&](wxCommandEvent&) { OnChoice(); });
283 OnFilterListChange();
284 int ix = FindString(kLabels.at(
"default"));
285 if (ix != wxNOT_FOUND) SetSelection(ix);
286 NavmsgFilter filter = filters_on_disk::Read(
"default.filter");
287 m_tty_panel->SetFilter(filter);
290 void OnFilterListChange() {
292 int select_ix = GetSelection();
293 std::string selected;
294 if (select_ix != wxNOT_FOUND) selected = GetString(select_ix).ToStdString();
296 for (
auto& filter : m_filters) {
298 Append(kLabels.at(filter.m_name));
299 }
catch (std::out_of_range&) {
300 if (filter.m_description.empty())
301 Append(filter.m_name);
303 Append(filter.m_description);
306 if (!selected.empty()) {
307 int ix = FindString(selected);
308 SetSelection(ix == wxNOT_FOUND ? 0 : ix);
312 void OnFilterUpdate(
const std::string& name) {
314 int select_ix = GetSelection();
315 if (select_ix == wxNOT_FOUND)
return;
317 std::string selected = GetString(select_ix).ToStdString();
318 if (selected != name)
return;
321 m_tty_panel->SetFilter(filter);
324 void OnApply(
const std::string& name) {
325 int found = FindString(name);
326 if (found == wxNOT_FOUND) {
327 for (
auto& filter : m_filters) {
328 if (filter.m_name == name) {
329 found = FindString(filter.m_description);
334 if (found == wxNOT_FOUND)
return;
337 OnFilterUpdate(name);
343 const std::unordered_map<std::string, std::string> kLabels = {
344 {
"all-data", _(
"All data")},
345 {
"all-nmea", _(
"All NMEA data")},
346 {
"default", _(
"Default settings")},
347 {
"malformed", _(
"Malformed messages")},
348 {
"nmea-input", _(
"NMEA input data")},
349 {
"nmea-output", _(
"NMEA output data")},
350 {
"plugins", _(
"Messages to plugins")},
353 std::vector<NavmsgFilter> m_filters;
357 wxString label = GetString(GetSelection());
358 NavmsgFilter filter = FilterByLabel(label.ToStdString());
359 m_tty_panel->SetFilter(filter);
364 for (
const auto& kv : kLabels) {
365 if (kv.second == label) {
371 for (
auto& f : m_filters)
372 if (f.m_name == name)
return f;
374 for (
auto& f : m_filters)
375 if (f.m_description == label)
return f;
385 : wxButton(parent, wxID_ANY),
387 m_on_stop(std::move(on_stop)) {
388 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
394 std::function<void(
bool)> m_on_stop;
397 is_paused = !is_paused;
398 m_on_stop(is_paused);
399 SetLabel(is_paused ? _(
"Resume") : _(
"Pause"));
406 CloseButton(wxWindow* parent, std::function<
void()> on_close)
407 : wxButton(parent, wxID_ANY), m_on_close(std::move(on_close)) {
408 SetLabel(_(
"Close"));
409 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
414 std::function<void()> m_on_close;
416 void OnClick() { m_on_close(); }
428 m_set_logtype(set_logtype),
430 kFilenameLabelId(wxWindow::NewControlId()) {
431 auto flags = wxSizerFlags(0).Border();
434 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
435 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
436 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
438 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
439 default_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
440 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
442 default_btn->SetValue(
true);
443 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
444 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
445 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
447 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
448 left_vbox->Add(default_btn, flags.DoubleBorder());
449 left_vbox->Add(vdr_btn, flags);
450 left_vbox->Add(csv_btn, flags);
453 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
454 auto label =
new wxStaticText(
this, kFilenameLabelId,
455 m_logger.GetDefaultLogfile().string());
456 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
457 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
459 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
460 force_box->Bind(wxEVT_CHECKBOX,
461 [&](wxCommandEvent& e) { m_overwrite = e.IsChecked(); });
462 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
463 right_vbox->Add(label, flags);
464 right_vbox->Add(path_btn, flags);
465 right_vbox->Add(force_box, flags);
468 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
469 hbox->Add(left_vbox, flags);
470 hbox->Add(GetCharWidth() * 10, 0, 1);
471 hbox->Add(right_vbox, flags);
477 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
481 void OnFileDialog() {
483 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
484 wxFileDialog dlg(m_parent, _(
"Select logfile"),
485 m_logger.GetDefaultLogfile().parent_path().string(),
486 m_logger.GetDefaultLogfile().stem().string(),
487 m_logger.GetFileDlgTypes(),
options);
488 if (dlg.ShowModal() == wxID_CANCEL)
return;
489 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
490 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
491 file_label->SetLabel(dlg.GetPath());
495 SetFormatFunc m_set_logtype;
497 const int kFilenameLabelId;
502 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
503 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
504 auto flags = wxSizerFlags(0).Border();
507 auto buttons =
new wxStdDialogButtonSizer();
508 auto close_btn =
new wxButton(
this, wxID_CLOSE);
509 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
510 [&](wxCommandEvent& ev) { EndModal(0); });
511 buttons->AddButton(close_btn);
513 buttons->Fit(parent);
516 auto panel =
new ThePanel(
this, set_logtype, logger);
517 auto vbox =
new wxBoxSizer(wxVERTICAL);
518 vbox->Add(panel, flags.Expand());
519 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
520 vbox->Add(buttons, flags.Expand());
531 enum class Id :
char {
541 : m_parent(parent), m_logger(logger) {
542 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
543 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
544 auto filters =
new wxMenu(
"");
545 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
546 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
547 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
548 AppendSubMenu(filters, _(
"Filters..."));
549 if (IsUserFilter(m_filter))
550 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
552 Bind(wxEVT_MENU, [&](wxCommandEvent& ev) {
553 switch (
static_cast<Id
>(ev.GetId())) {
558 case Id::kViewStdColors:
559 SetColor(static_cast<int>(Id::kViewStdColors));
563 CreateFilterDlg(parent);
566 case Id::kEditFilter:
567 EditFilterDlg(wxTheApp->GetTopWindow());
570 case Id::kEditActiveFilter:
571 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
574 case Id::kDeleteFilter:
575 RemoveFilterDlg(parent);
579 Check(
static_cast<int>(Id::kViewStdColors),
true);
582 void SetFilterName(
const std::string& filter) {
583 int id =
static_cast<int>(Id::kEditActiveFilter);
584 if (FindItem(
id)) Delete(
id);
585 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
589 void ConfigureLogging() {
592 [&](DataLogger::Format f, std::string s) { SetLogFormat(f, s); },
595 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
601 wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
602 return root->Append(
static_cast<int>(
id), label);
605 void SetLogFormat(DataLogger::Format format,
const std::string& label) {
606 m_logger.SetFormat(format);
607 std::string extension =
608 format == DataLogger::Format::kDefault ?
".log" :
".csv";
609 fs::path path = m_logger.GetLogfile();
610 path = path.parent_path() / (path.stem().string() + extension);
611 m_logger.SetLogfile(path);
614 void SetColor(
int id) {
615 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
616 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
617 if (!tty_scroll)
return;
619 wxMenuItem* item = FindItem(
id);
621 if (item->IsCheck() && item->IsChecked())
622 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
624 tty_scroll->SetColors(
625 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
630 std::string m_filter;
637 : wxButton(parent, wxID_ANY),
642 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
647 void UpdateTooltip() {
649 SetToolTip(_(
"Click to stop logging"));
651 SetToolTip(_(
"Click to start logging"));
660 void OnClick(
bool ctor =
false) {
661 if (!m_is_inited && !ctor) {
662 m_menu.ConfigureLogging();
665 is_logging = !is_logging;
666 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
668 m_logger.SetLogging(is_logging);
677 SetToolTip(_(
"Copy to clipboard"));
678 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
680 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
690 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
691 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
696 wxWindow* m_quick_filter;
700 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
701 m_show_filter = !m_show_filter;
702 m_quick_filter->Show(m_show_filter);
703 SetToolTip(m_show_filter ? _(
"Close quick filter")
704 : _(
"Open quick filter"));
705 GetGrandParent()->Layout();
713 std::function<std::string()> get_current_filter)
716 m_get_current_filter(std::move(get_current_filter)) {
718 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
719 SetToolTip(_(
"Open menu"));
724 std::function<std::string()> m_get_current_filter;
727 m_menu.SetFilterName(m_get_current_filter());
736 std::function<
void(
bool)> on_stop, std::function<
void()> on_hide,
741 m_menu(
this, logger),
742 m_log_button(
new LogButton(
this, logger, m_menu)) {
744 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
745 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
747 auto flags = wxSizerFlags(0).Border();
748 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
749 wbox->Add(m_log_button, flags);
752 wbox->Add(GetCharWidth() * 5, 0, 1);
753 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
754 wbox->Add(m_filter_choice, flags);
757 auto get_current_filter = [&] {
758 return m_filter_choice->GetStringSelection().ToStdString();
761 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
763 wbox->Add(
new CloseButton(
this, std::move(on_hide)), flags);
769 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
773 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
774 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
779 void OnContextClick() {
780 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
787 wxSize DoGetBestClientSize()
const override {
789 return wxSize(-1, -1);
791 return wxSize(85 * GetCharWidth(), 2.5 * GetCharHeight());
796 wxChoice* m_filter_choice;
798 wxButton* m_log_button;
801DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
804 m_stream(path, std::ios_base::app),
806 m_format(Format::kDefault),
807 m_log_start(NavmsgClock::now()) {}
809DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
811void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
813void DataLogger::SetLogfile(
const fs::path& path) {
814 m_stream = std::ofstream(path);
815 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
816 const auto now = std::chrono::system_clock::now();
817 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
818 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
819 m_stream <<
"received_at,protocol,msg_type,source,raw_data\n";
820 m_stream << std::flush;
825void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
827fs::path DataLogger::NullLogfile() {
828 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
834fs::path DataLogger::GetDefaultLogfile() {
835 if (m_path.stem() != NullLogfile().stem())
return m_path;
836 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
838 path += (m_format == Format::kDefault ?
".log" :
".csv");
842std::string DataLogger::GetFileDlgTypes() {
843 if (m_format == Format::kDefault)
844 return _(
"Log file (*.log)|*.log");
846 return _(
"Spreadsheet csv file(*.csv)|*.csv");
849void DataLogger::Add(
const Logline& ll) {
850 if (!m_is_logging || !ll.navmsg)
return;
851 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
852 if (m_format == DataLogger::Format::kVdr)
853 AddVdrLogline(ll, m_stream);
855 AddStdLogline(ll, m_stream,
856 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
860DataMonitor::DataMonitor(wxWindow* parent)
861 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxPoint(0, 0), wxDefaultSize,
862 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
863 kDataMonitorWindowName),
864 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
865 auto msg = std::dynamic_pointer_cast<const Nmea0183Msg>(navmsg);
868 m_quick_filter(
nullptr),
870 auto vbox =
new wxBoxSizer(wxVERTICAL);
871 auto tty_panel =
new TtyPanel(
this, 12);
872 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
873 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
875 auto on_quick_filter_evt = [&, tty_panel] {
877 assert(quick_filter);
878 std::string value = quick_filter->GetValue();
879 tty_panel->SetQuickFilter(value);
882 vbox->Add(m_quick_filter, wxSizerFlags());
884 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
885 auto on_close = [&,
this]() { this->OnHide(); };
886 auto status_line =
new StatusLine(
this, m_quick_filter, tty_panel, on_stop,
888 vbox->Add(status_line, wxSizerFlags().Expand());
893 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
894 tty_panel->SetQuickFilter(GetLabel().ToStdString());
896 m_quick_filter->Hide();
897 tty_panel->SetOnRightClick(
898 [&, status_line] { status_line->OnContextClick(); });
900 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
901 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
902 status_line->OnContextClick();
905 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
907 m_filter_update_lstnr.Init(
908 FilterEvents::GetInstance().filter_update,
909 [&](
ObservedEvt& ev) { OnFilterUpdate(ev.GetString().ToStdString()); });
911 m_filter_apply_lstnr.Init(
912 FilterEvents::GetInstance().filter_apply,
913 [&](
ObservedEvt& ev) { OnFilterApply(ev.GetString().ToStdString()); });
922 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
923 assert(w &&
"No TtyPanel found");
924 return w->IsShownOnScreen();
927void DataMonitor::OnFilterListChange() {
928 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
931 assert(filter_choice &&
"Wrong FilterChoice type (!)");
932 filter_choice->OnFilterListChange();
935void DataMonitor::OnFilterUpdate(
const std::string& name) {
936 if (name != m_current_filter)
return;
937 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
939 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
940 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
941 tty_scroll->SetFilter(filters_on_disk::Read(name));
944void DataMonitor::OnFilterApply(
const std::string& name) {
945 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
948 assert(filter_choice &&
"Wrong FilterChoice type (!)");
949 m_current_filter = name;
950 filter_choice->OnApply(name);
953void DataMonitor::OnHide() { Hide(); }
955#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.