12#include <wx/statline.h>
13#include <wx/stattext.h>
14#include <wx/translation.h>
15#include <wx/wrapsizer.h>
18#include "androidUTIL.h"
21#include "model/base_platform.h"
29#include "std_filesystem.h"
30#include "svg_button.h"
35#pragma clang diagnostic push
36#pragma ide diagnostic ignored "UnreachableCode"
40#if wxCHECK_VERSION(3, 2, 0)
41#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString()
43#define _(s) wxGetTranslation((s)).ToStdString()
46using SetFormatFunc = std::function<void(DataLogger::Format, std::string)>;
50T* GetWindowById(
int id) {
51 return dynamic_cast<T*
>(wxWindow::FindWindowById(
id));
54static const char*
const kFilterChoiceName =
"FilterChoiceWindow";
57static const std::unordered_map<NavAddr::Bus, std::string> kSourceByBus = {
58 {NavAddr::Bus::N0183,
"NMEA0183"},
59 {NavAddr::Bus::N2000,
"NMEA2000"},
60 {NavAddr::Bus::Signalk,
"SignalK"}};
63static bool IsUserFilter(
const std::string& filter_name) {
65 auto found = std::find(filters.begin(), filters.end(), filter_name);
66 if (found != filters.end())
return true;
68 filters.begin(), filters.end(),
69 [filter_name](
const std::string& f) { return f == filter_name; });
73static std::string TimeStamp(
const NavmsgTimePoint& when,
74 const NavmsgTimePoint& since) {
75 using namespace std::chrono;
78 auto duration = when - since;
80 auto hrs = duration_cast<hours>(duration) % 24;
81 duration -= duration_cast<hours>(duration) / 24;
82 auto mins = duration_cast<minutes>(duration) % 60;
83 duration -= duration_cast<minutes>(duration) / 60;
84 auto secs = duration_cast<seconds>(duration) % 60;
85 duration -= duration_cast<seconds>(duration) / 60;
86 const auto msecs = duration_cast<milliseconds>(duration);
87 ss << setw(2) << setfill(
'0') << hrs.count() <<
":" << setw(2) << mins.count()
88 <<
":" << setw(2) << secs.count() <<
"." << setw(3)
89 << msecs.count() % 1000;
93static fs::path NullLogfile() {
94 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
105static std::string VdrQuote(
const std::string& arg) {
106 auto static constexpr npos = std::string::npos;
107 if (arg.find(
',') == npos && arg.find(
'"') == npos)
return arg;
109 for (
const auto c : arg) {
115 return "\"" + s +
"\"";
122static void AddVdrLogline(
const Logline& ll, std::ostream& stream) {
123 if (kSourceByBus.find(ll.navmsg->bus) == kSourceByBus.end())
return;
125 using namespace std::chrono;
126 const auto now = system_clock::now();
127 const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count();
130 stream << kSourceByBus.at(ll.navmsg->bus) <<
",";
131 stream << ll.navmsg->source->iface <<
",";
132 switch (ll.navmsg->bus) {
133 case NavAddr::Bus::N0183: {
134 auto msg0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(ll.navmsg);
135 stream << msg0183->talker << msg0183->type <<
",";
137 case NavAddr::Bus::N2000: {
138 auto msg2000 = std::dynamic_pointer_cast<const Nmea2000Msg>(ll.navmsg);
139 stream << msg2000->PGN.to_string() <<
",";
141 case NavAddr::Bus::Signalk: {
142 auto msgSignalK = std::dynamic_pointer_cast<const SignalkMsg>(ll.navmsg);
143 stream <<
"\"" << msgSignalK->context_self <<
"\",";
146 assert(
false &&
"Illegal message type");
148 stream << VdrQuote(ll.navmsg->to_vdr()) <<
"\n";
152static void AddStdLogline(
const Logline& ll, std::ostream& stream,
char fs,
153 const NavmsgTimePoint log_start) {
154 if (!ll.navmsg)
return;
156 ws << TimeStamp(ll.navmsg->created_at, log_start) << fs;
157 if (ll.state.direction == NavmsgStatus::Direction::kOutput)
158 ws << kUtfRightArrow << fs;
159 else if (ll.state.direction == NavmsgStatus::Direction::kInput)
160 ws << kUtfLeftwardsArrowToBar << fs;
161 else if (ll.state.direction == NavmsgStatus::Direction::kInternal)
162 ws << kUtfLeftRightArrow << fs;
164 ws << kUtfLeftArrow << fs;
165 if (ll.state.status != NavmsgStatus::State::kOk)
166 ws << kUtfMultiplicationX << fs;
167 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredNoOutput)
168 ws << kUtfFallingDiagonal << fs;
169 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredDropped)
170 ws << kUtfCircledDivisionSlash << fs;
172 ws << kUtfCheckMark << fs;
174 ws << ll.navmsg->source->iface << fs;
175 ws << NavAddr::BusToString(ll.navmsg->bus) << fs;
176 if (ll.state.status != NavmsgStatus::State::kOk)
177 ws << (!ll.error_msg.empty() ? ll.error_msg :
"Unknown error");
180 ws << fs << ll.message <<
"\n";
187 TtyPanel(wxWindow* parent,
size_t lines)
188 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
189 wxTAB_TRAVERSAL,
"TtyPanel"),
190 m_tty_scroll(
nullptr),
191 m_filter(
this, wxID_ANY),
193 m_on_right_click([] {}) {
194 const auto vbox =
new wxBoxSizer(wxVERTICAL);
195 m_tty_scroll =
new TtyScroll(
this,
static_cast<int>(m_lines));
196 m_tty_scroll->Bind(wxEVT_RIGHT_UP,
197 [&](wxMouseEvent&) { m_on_right_click(); });
198 vbox->
Add(m_tty_scroll, wxSizerFlags(1).Expand().Border());
206 bool IsVisible()
const override {
return IsShownOnScreen(); }
208 void OnStop(
bool stop)
const {
209 m_tty_scroll->
Pause(stop);
211 m_tty_scroll->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT);
213 m_tty_scroll->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
218 void SetQuickFilter(
const std::string& filter)
const {
222 void SetOnRightClick(std::function<
void()> f) {
223 m_on_right_click = std::move(f);
228 auto window = wxWindow::FindWindowByName(
"TtyPanel");
230 auto tty_panel =
dynamic_cast<TtyPanel*
>(window);
231 if (tty_panel) tty_panel->
Add(ll);
235 wxSize DoGetBestClientSize()
const override {
236 return {1,
static_cast<int>(m_lines * GetCharHeight())};
243 std::function<void()> m_on_right_click;
251 m_text_ctrl(
new wxTextCtrl(
this, wxID_ANY)),
252 m_on_text_evt(std::move(on_text_evt)) {
253 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
254 auto flags = wxSizerFlags(0).Border();
255 auto label_box =
new wxBoxSizer(wxVERTICAL);
256 label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Quick filter:")));
257 hbox->Add(label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
258 hbox->Add(m_text_ctrl, flags);
262 m_text_ctrl->Bind(wxEVT_TEXT, [&](wxCommandEvent&) { m_on_text_evt(); });
265 bool Show(
bool show)
override {
266 if (!show) m_text_ctrl->SetValue(
"");
267 return wxWindow::Show(show);
270 [[nodiscard]] std::string GetValue()
const {
271 return m_text_ctrl->GetValue().ToStdString();
275 wxTextCtrl* m_text_ctrl;
276 std::function<void()> m_on_text_evt;
283 : wxChoice(parent, wxID_ANY), m_tty_panel(tty_panel) {
284 wxWindow::SetName(kFilterChoiceName);
285 Bind(wxEVT_CHOICE, [&](wxCommandEvent&) { OnChoice(); });
286 OnFilterListChange();
287 const int ix = wxChoice::FindString(kLabels.at(
"default"));
288 if (ix != wxNOT_FOUND) wxChoice::SetSelection(ix);
289 NavmsgFilter filter = filters_on_disk::Read(
"default.filter");
290 m_tty_panel->SetFilter(filter);
293 void OnFilterListChange() {
295 int select_ix = GetSelection();
296 std::string selected;
297 if (select_ix != wxNOT_FOUND) selected = GetString(select_ix).ToStdString();
299 for (
auto& filter : m_filters) {
301 Append(kLabels.at(filter.m_name));
302 }
catch (std::out_of_range&) {
303 if (filter.m_description.empty())
304 Append(filter.m_name);
306 Append(filter.m_description);
309 if (!selected.empty()) {
310 int ix = FindString(selected);
311 SetSelection(ix == wxNOT_FOUND ? 0 : ix);
315 void OnFilterUpdate(
const std::string& name) {
317 int select_ix = GetSelection();
318 if (select_ix == wxNOT_FOUND)
return;
320 std::string selected = GetString(select_ix).ToStdString();
321 if (selected != name)
return;
324 m_tty_panel->SetFilter(filter);
327 void OnApply(
const std::string& name) {
328 int found = FindString(name);
329 if (found == wxNOT_FOUND) {
330 for (
auto& filter : m_filters) {
331 if (filter.m_name == name) {
332 found = FindString(filter.m_description);
337 if (found == wxNOT_FOUND)
return;
340 OnFilterUpdate(name);
346 const std::unordered_map<std::string, std::string> kLabels = {
347 {
"all-data", _(
"All data")},
348 {
"all-nmea", _(
"All NMEA data")},
349 {
"default", _(
"Default settings")},
350 {
"malformed", _(
"Malformed messages")},
351 {
"nmea-input", _(
"NMEA input data")},
352 {
"nmea-output", _(
"NMEA output data")},
353 {
"plugins", _(
"Messages to plugins")},
356 std::vector<NavmsgFilter> m_filters;
360 wxString label = GetString(GetSelection());
361 NavmsgFilter filter = FilterByLabel(label.ToStdString());
362 m_tty_panel->SetFilter(filter);
366 std::string name = label;
367 for (
const auto& kv : kLabels) {
368 if (kv.second == label) {
374 for (
auto& f : m_filters)
375 if (f.m_name == name)
return f;
377 for (
auto& f : m_filters)
378 if (f.m_description == label)
return f;
388 : wxButton(parent, wxID_ANY),
390 m_on_stop(std::move(on_stop)) {
391 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
397 std::function<void(
bool)> m_on_stop;
400 is_paused = !is_paused;
401 m_on_stop(is_paused);
402 SetLabel(is_paused ? _(
"Resume") : _(
"Pause"));
409 CloseButton(wxWindow* parent, std::function<
void()> on_close)
410 : wxButton(parent, wxID_ANY), m_on_close(std::move(on_close)) {
411 wxButton::SetLabel(_(
"Close"));
412 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
417 std::function<void()> m_on_close;
419 void OnClick()
const { m_on_close(); }
431 m_set_logtype(std::move(set_logtype)),
433 kFilenameLabelId(wxWindow::NewControlId()) {
434 auto flags = wxSizerFlags(0).Border();
437 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
438 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
439 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
441 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
442 default_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
443 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
445 default_btn->SetValue(
true);
446 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
447 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
448 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
450 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
451 left_vbox->Add(default_btn, flags.DoubleBorder());
452 left_vbox->Add(vdr_btn, flags);
453 left_vbox->Add(csv_btn, flags);
456 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
457 auto label =
new wxStaticText(
this, kFilenameLabelId,
458 m_logger.GetDefaultLogfile().string());
459 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
460 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
462 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
463 force_box->Bind(wxEVT_CHECKBOX, [&](
const wxCommandEvent& e) {
464 m_overwrite = e.IsChecked();
466 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
467 right_vbox->Add(label, flags);
468 right_vbox->Add(path_btn, flags);
469 right_vbox->Add(force_box, flags);
472 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
473 hbox->Add(left_vbox, flags);
474 hbox->Add(wxWindow::GetCharWidth() * 10, 0, 1);
475 hbox->Add(right_vbox, flags);
481 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
485 void OnFileDialog()
const {
487 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
488 wxFileDialog dlg(m_parent, _(
"Select logfile"),
489 m_logger.GetDefaultLogfile().parent_path().string(),
490 m_logger.GetDefaultLogfile().stem().string(),
491 m_logger.GetFileDlgTypes(),
options);
492 if (dlg.ShowModal() == wxID_CANCEL)
return;
493 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
494 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
495 file_label->SetLabel(dlg.GetPath());
499 SetFormatFunc m_set_logtype;
501 const int kFilenameLabelId;
506 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
507 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
508 auto flags = wxSizerFlags(0).Border();
511 auto buttons =
new wxStdDialogButtonSizer();
512 auto close_btn =
new wxButton(
this, wxID_CLOSE);
513 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
514 [&](wxCommandEvent& ev) { EndModal(0); });
515 buttons->AddButton(close_btn);
517 buttons->Fit(parent);
520 auto panel =
new ThePanel(
this, std::move(set_logtype), logger);
521 auto vbox =
new wxBoxSizer(wxVERTICAL);
522 vbox->Add(panel, flags.Expand());
523 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
524 vbox->Add(buttons, flags.Expand());
535 enum class Id :
char {
545 : m_parent(parent), m_logger(logger) {
546 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
547 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
548 auto filters =
new wxMenu(
"");
549 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
550 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
551 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
552 AppendSubMenu(filters, _(
"Filters..."));
553 if (IsUserFilter(m_filter))
554 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
556 Bind(wxEVT_MENU, [&](
const wxCommandEvent& ev) {
557 switch (
static_cast<Id
>(ev.GetId())) {
562 case Id::kViewStdColors:
563 SetColor(static_cast<int>(Id::kViewStdColors));
567 CreateFilterDlg(parent);
570 case Id::kEditFilter:
571 EditFilterDlg(wxTheApp->GetTopWindow());
574 case Id::kEditActiveFilter:
575 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
578 case Id::kDeleteFilter:
579 RemoveFilterDlg(parent);
583 Check(
static_cast<int>(Id::kViewStdColors),
true);
586 void SetFilterName(
const std::string& filter) {
587 int id =
static_cast<int>(Id::kEditActiveFilter);
588 if (FindItem(
id)) Delete(
id);
589 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
593 void ConfigureLogging()
const {
596 [&](DataLogger::Format f,
const std::string& s) { SetLogFormat(f, s); },
599 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
605 static wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
606 return root->Append(
static_cast<int>(
id), label);
609 void SetLogFormat(DataLogger::Format format,
const std::string& label)
const {
610 m_logger.SetFormat(format);
611 std::string extension =
612 format == DataLogger::Format::kDefault ?
".log" :
".csv";
613 fs::path path = m_logger.GetLogfile();
614 path = path.parent_path() / (path.stem().string() + extension);
615 m_logger.SetLogfile(path);
618 void SetColor(
int id)
const {
619 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
620 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
621 if (!tty_scroll)
return;
623 wxMenuItem* item = FindItem(
id);
625 if (item->IsCheck() && item->IsChecked())
626 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
628 tty_scroll->SetColors(
629 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
634 std::string m_filter;
641 : wxButton(parent, wxID_ANY),
646 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
651 void UpdateTooltip() {
653 SetToolTip(_(
"Click to stop logging"));
655 SetToolTip(_(
"Click to start logging"));
664 void OnClick(
bool ctor =
false) {
665 if (!m_is_inited && !ctor) {
666 m_menu.ConfigureLogging();
669 is_logging = !is_logging;
670 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
672 m_logger.SetLogging(is_logging);
681 SetToolTip(_(
"Copy to clipboard"));
682 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
684 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
694 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
695 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
700 wxWindow* m_quick_filter;
704 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
705 m_show_filter = !m_show_filter;
706 m_quick_filter->Show(m_show_filter);
707 SetToolTip(m_show_filter ? _(
"Close quick filter")
708 : _(
"Open quick filter"));
709 GetGrandParent()->Layout();
717 std::function<std::string()> get_current_filter)
720 m_get_current_filter(std::move(get_current_filter)) {
722 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
723 SetToolTip(_(
"Open menu"));
728 std::function<std::string()> m_get_current_filter;
731 m_menu.SetFilterName(m_get_current_filter());
740 std::function<
void(
bool)> on_stop,
741 const std::function<
void()>& on_hide,
DataLogger& logger)
745 m_menu(
this, logger),
746 m_log_button(
new LogButton(
this, logger, m_menu)) {
748 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
749 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
751 auto flags = wxSizerFlags(0).Border();
752 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
753 wbox->Add(m_log_button, flags);
756 wbox->Add(wxWindow::GetCharWidth() * 5, 0, 1);
757 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
758 wbox->Add(m_filter_choice, flags);
761 auto get_current_filter = [&] {
762 return m_filter_choice->GetStringSelection().ToStdString();
765 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
767 wbox->Add(
new CloseButton(
this, std::move(on_hide)), flags);
773 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
777 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
778 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
783 void OnContextClick() {
784 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
791 [[nodiscard]] wxSize DoGetBestClientSize()
const override {
795 return {85 * GetCharWidth(), 5 * GetCharHeight() / 2};
800 wxChoice* m_filter_choice;
802 wxButton* m_log_button;
805DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
808 m_stream(path, std::ios_base::app),
810 m_format(Format::kDefault),
811 m_log_start(NavmsgClock::now()) {}
813DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
815void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
817void DataLogger::SetLogfile(
const fs::path& path) {
818 m_stream = std::ofstream(path);
819 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
820 const auto now = std::chrono::system_clock::now();
821 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
822 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
823 m_stream <<
"received_at,protocol,source,msg_type,raw_data\n";
824 m_stream << std::flush;
829void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
831fs::path DataLogger::GetDefaultLogfile() {
832 if (m_path.stem() != NullLogfile().stem())
return m_path;
833 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
835 path += (m_format == Format::kDefault ?
".log" :
".csv");
839std::string DataLogger::GetFileDlgTypes()
const {
840 if (m_format == Format::kDefault)
841 return _(
"Log file (*.log)|*.log");
843 return _(
"Spreadsheet csv file(*.csv)|*.csv");
846void DataLogger::Add(
const Logline& ll) {
847 if (!m_is_logging || !ll.navmsg)
return;
848 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
849 if (m_format == DataLogger::Format::kVdr)
850 AddVdrLogline(ll, m_stream);
852 AddStdLogline(ll, m_stream,
853 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
857DataMonitor::DataMonitor(wxWindow* parent)
858 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxPoint(0, 0), wxDefaultSize,
859 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
860 kDataMonitorWindowName),
861 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
864 m_quick_filter(
nullptr),
866 auto vbox =
new wxBoxSizer(wxVERTICAL);
867 auto tty_panel =
new TtyPanel(
this, 12);
868 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
869 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
871 auto on_quick_filter_evt = [&, tty_panel] {
873 assert(quick_filter);
874 std::string value = quick_filter->GetValue();
875 tty_panel->SetQuickFilter(value);
878 vbox->Add(m_quick_filter, wxSizerFlags());
880 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
881 auto on_close = [&,
this]() { this->OnHide(); };
882 auto status_line =
new StatusLine(
this, m_quick_filter, tty_panel, on_stop,
884 vbox->Add(status_line, wxSizerFlags().Expand());
889 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
890 tty_panel->SetQuickFilter(GetLabel().ToStdString());
892 m_quick_filter->Hide();
893 tty_panel->SetOnRightClick(
894 [&, status_line] { status_line->OnContextClick(); });
896 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
897 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
898 status_line->OnContextClick();
901 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
903 m_filter_update_lstnr.Init(FilterEvents::GetInstance().filter_update,
905 OnFilterUpdate(ev.GetString().ToStdString());
908 m_filter_apply_lstnr.Init(FilterEvents::GetInstance().filter_apply,
910 OnFilterApply(ev.GetString().ToStdString());
920 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
921 assert(w &&
"No TtyPanel found");
922 return w->IsShownOnScreen();
925void DataMonitor::OnFilterListChange() {
926 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
929 assert(filter_choice &&
"Wrong FilterChoice type (!)");
930 filter_choice->OnFilterListChange();
933void DataMonitor::OnFilterUpdate(
const std::string& name)
const {
934 if (name != m_current_filter)
return;
935 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
937 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
938 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
939 tty_scroll->SetFilter(filters_on_disk::Read(name));
942void DataMonitor::OnFilterApply(
const std::string& name) {
943 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
946 assert(filter_choice &&
"Wrong FilterChoice type (!)");
947 m_current_filter = name;
948 filter_choice->OnApply(name);
951void DataMonitor::OnHide() { Hide(); }
953#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.
bool IsVisible() const override
Return true if log is visible i.e., if it's any point using Add().
void Notify() override
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, const 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 a 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.e., if it's any point using Add().
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.
Hooks into gui available in model.
Data monitor filter definitions.
Basic DataMonitor logging interface: LogLine (reflects a line in the log) and NmeaLog,...