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);
476 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
478 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
482 void OnFileDialog() {
484 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
485 wxFileDialog dlg(m_parent, _(
"Select logfile"),
486 m_logger.GetDefaultLogfile().parent_path().string(),
487 m_logger.GetDefaultLogfile().stem().string(),
488 m_logger.GetFileDlgTypes(),
options);
489 if (dlg.ShowModal() == wxID_CANCEL)
return;
490 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
491 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
492 file_label->SetLabel(dlg.GetPath());
496 SetFormatFunc m_set_logtype;
498 const int kFilenameLabelId;
503 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
504 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
505 auto flags = wxSizerFlags(0).Border();
508 auto buttons =
new wxStdDialogButtonSizer();
509 auto close_btn =
new wxButton(
this, wxID_CLOSE);
510 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
511 [&](wxCommandEvent& ev) { EndModal(0); });
512 buttons->AddButton(close_btn);
514 buttons->Fit(parent);
517 auto panel =
new ThePanel(
this, set_logtype, logger);
518 auto vbox =
new wxBoxSizer(wxVERTICAL);
519 vbox->Add(panel, flags.Expand());
520 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
521 vbox->Add(buttons, flags.Expand());
532 enum class Id :
char {
542 : m_parent(parent), m_logger(logger) {
543 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
544 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
545 auto filters =
new wxMenu(
"");
546 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
547 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
548 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
549 AppendSubMenu(filters, _(
"Filters..."));
550 if (IsUserFilter(m_filter))
551 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
553 Bind(wxEVT_MENU, [&](wxCommandEvent& ev) {
554 switch (
static_cast<Id
>(ev.GetId())) {
559 case Id::kViewStdColors:
560 SetColor(static_cast<int>(Id::kViewStdColors));
564 CreateFilterDlg(parent);
567 case Id::kEditFilter:
568 EditFilterDlg(wxTheApp->GetTopWindow());
571 case Id::kEditActiveFilter:
572 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
575 case Id::kDeleteFilter:
576 RemoveFilterDlg(parent);
580 Check(
static_cast<int>(Id::kViewStdColors),
true);
583 void SetFilterName(
const std::string& filter) {
584 int id =
static_cast<int>(Id::kEditActiveFilter);
585 if (FindItem(
id)) Delete(
id);
586 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
590 void ConfigureLogging() {
593 [&](DataLogger::Format f, std::string s) { SetLogFormat(f, s); },
596 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
602 wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
603 return root->Append(
static_cast<int>(
id), label);
606 void SetLogFormat(DataLogger::Format format,
const std::string& label) {
607 m_logger.SetFormat(format);
608 std::string extension =
609 format == DataLogger::Format::kDefault ?
".log" :
".csv";
610 fs::path path = m_logger.GetLogfile();
611 path = path.parent_path() / (path.stem().string() + extension);
612 m_logger.SetLogfile(path);
615 void SetColor(
int id) {
616 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
617 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
618 if (!tty_scroll)
return;
620 wxMenuItem* item = FindItem(
id);
622 if (item->IsCheck() && item->IsChecked())
623 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
625 tty_scroll->SetColors(
626 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
631 std::string m_filter;
638 : wxButton(parent, wxID_ANY),
643 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
648 void UpdateTooltip() {
650 SetToolTip(_(
"Click to stop logging"));
652 SetToolTip(_(
"Click to start logging"));
661 void OnClick(
bool ctor =
false) {
662 if (!m_is_inited && !ctor) {
663 m_menu.ConfigureLogging();
666 is_logging = !is_logging;
667 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
669 m_logger.SetLogging(is_logging);
678 SetToolTip(_(
"Copy to clipboard"));
679 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
681 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
691 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
692 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
697 wxWindow* m_quick_filter;
701 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
702 m_show_filter = !m_show_filter;
703 m_quick_filter->Show(m_show_filter);
704 SetToolTip(m_show_filter ? _(
"Close quick filter")
705 : _(
"Open quick filter"));
706 GetGrandParent()->Layout();
714 std::function<std::string()> get_current_filter)
717 m_get_current_filter(std::move(get_current_filter)) {
719 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
720 SetToolTip(_(
"Open menu"));
725 std::function<std::string()> m_get_current_filter;
728 m_menu.SetFilterName(m_get_current_filter());
737 std::function<
void(
bool)> on_stop, std::function<
void()> on_hide,
742 m_menu(
this, logger),
743 m_log_button(
new LogButton(
this, logger, m_menu)) {
745 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
746 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
748 auto flags = wxSizerFlags(0).Border();
749 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
750 wbox->Add(m_log_button, flags);
753 wbox->Add(GetCharWidth() * 5, 0, 1);
754 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
755 wbox->Add(m_filter_choice, flags);
758 auto get_current_filter = [&] {
759 return m_filter_choice->GetStringSelection().ToStdString();
762 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
764 wbox->Add(
new CloseButton(
this, std::move(on_hide)), flags);
770 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
774 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
775 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
780 void OnContextClick() {
781 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
788 wxSize DoGetBestClientSize()
const override {
790 return wxSize(-1, -1);
792 return wxSize(85 * GetCharWidth(), 2.5 * GetCharHeight());
797 wxChoice* m_filter_choice;
799 wxButton* m_log_button;
802DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
805 m_stream(path, std::ios_base::app),
807 m_format(Format::kDefault),
808 m_log_start(NavmsgClock::now()) {}
810DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
812void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
814void DataLogger::SetLogfile(
const fs::path& path) {
815 m_stream = std::ofstream(path);
816 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
817 const auto now = std::chrono::system_clock::now();
818 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
819 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
820 m_stream <<
"received_at,protocol,msg_type,source,raw_data\n";
821 m_stream << std::flush;
826void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
828fs::path DataLogger::NullLogfile() {
829 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
835fs::path DataLogger::GetDefaultLogfile() {
836 if (m_path.stem() != NullLogfile().stem())
return m_path;
837 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
839 path += (m_format == Format::kDefault ?
".log" :
".csv");
843std::string DataLogger::GetFileDlgTypes() {
844 if (m_format == Format::kDefault)
845 return _(
"Log file (*.log)|*.log");
847 return _(
"Spreadsheet csv file(*.csv)|*.csv");
850void DataLogger::Add(
const Logline& ll) {
851 if (!m_is_logging || !ll.navmsg)
return;
852 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
853 if (m_format == DataLogger::Format::kVdr)
854 AddVdrLogline(ll, m_stream);
856 AddStdLogline(ll, m_stream,
857 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
861DataMonitor::DataMonitor(wxWindow* parent)
862 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxPoint(0, 0), wxDefaultSize,
863 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
864 kDataMonitorWindowName),
865 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
866 auto msg = std::dynamic_pointer_cast<const Nmea0183Msg>(navmsg);
869 m_quick_filter(
nullptr),
871 auto vbox =
new wxBoxSizer(wxVERTICAL);
872 auto tty_panel =
new TtyPanel(
this, 12);
873 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
874 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
876 auto on_quick_filter_evt = [&, tty_panel] {
878 assert(quick_filter);
879 std::string value = quick_filter->GetValue();
880 tty_panel->SetQuickFilter(value);
883 vbox->Add(m_quick_filter, wxSizerFlags());
885 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
886 auto on_close = [&,
this]() { this->OnHide(); };
887 auto status_line =
new StatusLine(
this, m_quick_filter, tty_panel, on_stop,
889 vbox->Add(status_line, wxSizerFlags().Expand());
894 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
895 tty_panel->SetQuickFilter(GetLabel().ToStdString());
897 m_quick_filter->Hide();
898 tty_panel->SetOnRightClick(
899 [&, status_line] { status_line->OnContextClick(); });
901 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
902 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
903 status_line->OnContextClick();
906 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
908 m_filter_update_lstnr.Init(
909 FilterEvents::GetInstance().filter_update,
910 [&](
ObservedEvt& ev) { OnFilterUpdate(ev.GetString().ToStdString()); });
912 m_filter_apply_lstnr.Init(
913 FilterEvents::GetInstance().filter_apply,
914 [&](
ObservedEvt& ev) { OnFilterApply(ev.GetString().ToStdString()); });
923 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
924 assert(w &&
"No TtyPanel found");
925 return w->IsShownOnScreen();
928void DataMonitor::OnFilterListChange() {
929 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
932 assert(filter_choice &&
"Wrong FilterChoice type (!)");
933 filter_choice->OnFilterListChange();
936void DataMonitor::OnFilterUpdate(
const std::string& name) {
937 if (name != m_current_filter)
return;
938 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
940 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
941 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
942 tty_scroll->SetFilter(filters_on_disk::Read(name));
945void DataMonitor::OnFilterApply(
const std::string& name) {
946 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
949 assert(filter_choice &&
"Wrong FilterChoice type (!)");
950 m_current_filter = name;
951 filter_choice->OnApply(name);
954void DataMonitor::OnHide() { Hide(); }
956#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.