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"));
412 m_set_logtype(set_logtype),
414 kFilenameLabelId(wxWindow::NewControlId()) {
415 auto flags = wxSizerFlags(0).Border();
418 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
419 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
420 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
422 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
423 default_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
424 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
426 default_btn->SetValue(
true);
427 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
428 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](wxCommandEvent e) {
429 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
431 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
432 left_vbox->Add(default_btn, flags.DoubleBorder());
433 left_vbox->Add(vdr_btn, flags);
434 left_vbox->Add(csv_btn, flags);
437 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
438 auto label =
new wxStaticText(
this, kFilenameLabelId,
439 m_logger.GetDefaultLogfile().string());
440 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
441 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
443 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
444 force_box->Bind(wxEVT_CHECKBOX,
445 [&](wxCommandEvent& e) { m_overwrite = e.IsChecked(); });
446 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
447 right_vbox->Add(label, flags);
448 right_vbox->Add(path_btn, flags);
449 right_vbox->Add(force_box, flags);
452 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
453 hbox->Add(left_vbox, flags);
454 hbox->Add(GetCharWidth() * 10, 0, 1);
455 hbox->Add(right_vbox, flags);
460 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
462 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
466 void OnFileDialog() {
468 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
469 wxFileDialog dlg(m_parent, _(
"Select logfile"),
470 m_logger.GetDefaultLogfile().parent_path().string(),
471 m_logger.GetDefaultLogfile().stem().string(),
472 m_logger.GetFileDlgTypes(),
options);
473 if (dlg.ShowModal() == wxID_CANCEL)
return;
474 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
475 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
476 file_label->SetLabel(dlg.GetPath());
480 SetFormatFunc m_set_logtype;
482 const int kFilenameLabelId;
487 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
488 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
489 auto flags = wxSizerFlags(0).Border();
492 auto buttons =
new wxStdDialogButtonSizer();
493 auto close_btn =
new wxButton(
this, wxID_CLOSE);
494 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
495 [&](wxCommandEvent& ev) { EndModal(0); });
496 buttons->AddButton(close_btn);
498 buttons->Fit(parent);
501 auto panel =
new ThePanel(
this, set_logtype, logger);
502 auto vbox =
new wxBoxSizer(wxVERTICAL);
503 vbox->Add(panel, flags.Expand());
504 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
505 vbox->Add(buttons, flags.Expand());
516 enum class Id :
char {
526 : m_parent(parent), m_logger(logger) {
527 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
528 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
529 auto filters =
new wxMenu(
"");
530 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
531 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
532 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
533 AppendSubMenu(filters, _(
"Filters..."));
534 if (IsUserFilter(m_filter))
535 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
537 Bind(wxEVT_MENU, [&](wxCommandEvent& ev) {
538 switch (
static_cast<Id
>(ev.GetId())) {
543 case Id::kViewStdColors:
544 SetColor(static_cast<int>(Id::kViewStdColors));
548 CreateFilterDlg(parent);
551 case Id::kEditFilter:
552 EditFilterDlg(wxTheApp->GetTopWindow());
555 case Id::kEditActiveFilter:
556 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
559 case Id::kDeleteFilter:
560 RemoveFilterDlg(parent);
564 Check(
static_cast<int>(Id::kViewStdColors),
true);
567 void SetFilterName(
const std::string& filter) {
568 int id =
static_cast<int>(Id::kEditActiveFilter);
569 if (FindItem(
id)) Delete(
id);
570 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
574 void ConfigureLogging() {
577 [&](DataLogger::Format f, std::string s) { SetLogFormat(f, s); },
580 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
586 wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
587 return root->Append(
static_cast<int>(
id), label);
590 void SetLogFormat(DataLogger::Format format,
const std::string& label) {
591 m_logger.SetFormat(format);
592 std::string extension =
593 format == DataLogger::Format::kDefault ?
".log" :
".csv";
594 fs::path path = m_logger.GetLogfile();
595 path = path.parent_path() / (path.stem().string() + extension);
596 m_logger.SetLogfile(path);
599 void SetColor(
int id) {
600 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
601 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
602 if (!tty_scroll)
return;
604 wxMenuItem* item = FindItem(
id);
606 if (item->IsCheck() && item->IsChecked())
607 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
609 tty_scroll->SetColors(
610 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
615 std::string m_filter;
622 : wxButton(parent, wxID_ANY),
627 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
632 void UpdateTooltip() {
634 SetToolTip(_(
"Click to stop logging"));
636 SetToolTip(_(
"Click to start logging"));
645 void OnClick(
bool ctor =
false) {
646 if (!m_is_inited && !ctor) {
647 m_menu.ConfigureLogging();
650 is_logging = !is_logging;
651 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
653 m_logger.SetLogging(is_logging);
662 SetToolTip(_(
"Copy to clipboard"));
663 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
665 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
675 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
676 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
681 wxWindow* m_quick_filter;
685 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
686 m_show_filter = !m_show_filter;
687 m_quick_filter->Show(m_show_filter);
688 SetToolTip(m_show_filter ? _(
"Close quick filter")
689 : _(
"Open quick filter"));
690 GetGrandParent()->Layout();
698 std::function<std::string()> get_current_filter)
701 m_get_current_filter(std::move(get_current_filter)) {
703 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
704 SetToolTip(_(
"Open menu"));
709 std::function<std::string()> m_get_current_filter;
712 m_menu.SetFilterName(m_get_current_filter());
721 std::function<
void(
bool)> on_stop,
DataLogger& logger)
725 m_menu(
this, logger),
726 m_log_button(
new LogButton(
this, logger, m_menu)) {
728 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
729 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
731 auto flags = wxSizerFlags(0).Border();
732 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
733 wbox->Add(m_log_button, flags);
736 wbox->Add(GetCharWidth() * 5, 0, 1);
737 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
738 wbox->Add(m_filter_choice, flags);
741 auto get_current_filter = [&] {
742 return m_filter_choice->GetStringSelection().ToStdString();
745 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
750 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
754 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
755 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
760 void OnContextClick() {
761 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
768 wxSize DoGetBestClientSize()
const override {
770 return wxSize(-1, -1);
772 return wxSize(85 * GetCharWidth(), 2.5 * GetCharHeight());
777 wxChoice* m_filter_choice;
779 wxButton* m_log_button;
782DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
785 m_stream(path, std::ios_base::app),
787 m_format(Format::kDefault),
788 m_log_start(NavmsgClock::now()) {}
790DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
792void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
794void DataLogger::SetLogfile(
const fs::path& path) {
795 m_stream = std::ofstream(path);
796 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
797 const auto now = std::chrono::system_clock::now();
798 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
799 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
800 m_stream <<
"received_at,protocol,msg_type,source,raw_data\n";
801 m_stream << std::flush;
806void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
808fs::path DataLogger::NullLogfile() {
809 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
815fs::path DataLogger::GetDefaultLogfile() {
816 if (m_path.stem() != NullLogfile().stem())
return m_path;
817 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
819 path += (m_format == Format::kDefault ?
".log" :
".csv");
823std::string DataLogger::GetFileDlgTypes() {
824 if (m_format == Format::kDefault)
825 return _(
"Log file (*.log)|*.log");
827 return _(
"Spreadsheet csv file(*.csv)|*.csv");
830void DataLogger::Add(
const Logline& ll) {
831 if (!m_is_logging || !ll.navmsg)
return;
832 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
833 if (m_format == DataLogger::Format::kVdr)
834 AddVdrLogline(ll, m_stream);
836 AddStdLogline(ll, m_stream,
837 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
841DataMonitor::DataMonitor(wxWindow* parent)
842 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxDefaultPosition,
843 wxDefaultSize, wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
844 kDataMonitorWindowName),
845 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
846 auto msg = std::dynamic_pointer_cast<const Nmea0183Msg>(navmsg);
849 m_quick_filter(
nullptr),
851 auto vbox =
new wxBoxSizer(wxVERTICAL);
852 auto tty_panel =
new TtyPanel(
this, 12);
853 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
854 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
856 auto on_quick_filter_evt = [&, tty_panel] {
858 assert(quick_filter);
859 std::string value = quick_filter->GetValue();
860 tty_panel->SetQuickFilter(value);
863 vbox->Add(m_quick_filter, wxSizerFlags());
865 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
867 new StatusLine(
this, m_quick_filter, tty_panel, on_stop, m_logger);
868 vbox->Add(status_line, wxSizerFlags().Expand());
873 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
874 tty_panel->SetQuickFilter(GetLabel().ToStdString());
876 m_quick_filter->Hide();
877 tty_panel->SetOnRightClick(
878 [&, status_line] { status_line->OnContextClick(); });
880 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
881 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
882 status_line->OnContextClick();
885 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
887 m_filter_update_lstnr.Init(
888 FilterEvents::GetInstance().filter_update,
889 [&](
ObservedEvt& ev) { OnFilterUpdate(ev.GetString().ToStdString()); });
891 m_filter_apply_lstnr.Init(
892 FilterEvents::GetInstance().filter_apply,
893 [&](
ObservedEvt& ev) { OnFilterApply(ev.GetString().ToStdString()); });
902 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
903 assert(w &&
"No TtyPanel found");
904 return w->IsShownOnScreen();
907void DataMonitor::OnFilterListChange() {
908 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
911 assert(filter_choice &&
"Wrong FilterChoice type (!)");
912 filter_choice->OnFilterListChange();
915void DataMonitor::OnFilterUpdate(
const std::string& name) {
916 if (name != m_current_filter)
return;
917 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
919 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
920 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
921 tty_scroll->SetFilter(filters_on_disk::Read(name));
924void DataMonitor::OnFilterApply(
const std::string& name) {
925 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
928 assert(filter_choice &&
"Wrong FilterChoice type (!)");
929 m_current_filter = name;
930 filter_choice->OnApply(name);
933#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.