31#include <wx/filedlg.h>
35#include <wx/statline.h>
36#include <wx/stattext.h>
37#include <wx/translation.h>
38#include <wx/wrapsizer.h>
41#include "androidUTIL.h"
44#include "model/base_platform.h"
52#include "std_filesystem.h"
53#include "svg_button.h"
58#pragma clang diagnostic push
59#pragma ide diagnostic ignored "UnreachableCode"
63#if wxCHECK_VERSION(3, 2, 0)
64#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString()
66#define _(s) wxGetTranslation((s)).ToStdString()
69using SetFormatFunc = std::function<void(DataLogger::Format, std::string)>;
74 return dynamic_cast<T*
>(wxWindow::FindWindowById(
id));
77static const char*
const kFilterChoiceName =
"FilterChoiceWindow";
80static const std::unordered_map<NavAddr::Bus, std::string> kSourceByBus = {
81 {NavAddr::Bus::N0183,
"NMEA0183"},
82 {NavAddr::Bus::N2000,
"NMEA2000"},
83 {NavAddr::Bus::Signalk,
"SignalK"}};
86static bool IsUserFilter(
const std::string& filter_name) {
88 auto found = std::find(filters.begin(), filters.end(), filter_name);
89 if (found != filters.end())
return true;
91 filters.begin(), filters.end(),
92 [filter_name](
const std::string& f) { return f == filter_name; });
96static std::string TimeStamp(
const NavmsgTimePoint& when,
97 const NavmsgTimePoint& since) {
98 using namespace std::chrono;
101 auto duration = when - since;
102 std::stringstream ss;
103 auto hrs = duration_cast<hours>(duration) % 24;
104 duration -= duration_cast<hours>(duration) / 24;
105 auto mins = duration_cast<minutes>(duration) % 60;
106 duration -= duration_cast<minutes>(duration) / 60;
107 auto secs = duration_cast<seconds>(duration) % 60;
108 duration -= duration_cast<seconds>(duration) / 60;
109 const auto msecs = duration_cast<milliseconds>(duration);
110 ss << setw(2) << setfill(
'0') << hrs.count() <<
":" << setw(2) << mins.count()
111 <<
":" << setw(2) << secs.count() <<
"." << setw(3)
112 << msecs.count() % 1000;
116static fs::path NullLogfile() {
117 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
128static std::string VdrQuote(
const std::string& arg) {
129 auto static constexpr npos = std::string::npos;
130 if (arg.find(
',') == npos && arg.find(
'"') == npos)
return arg;
132 for (
const auto c : arg) {
138 return "\"" + s +
"\"";
145static void AddVdrLogline(
const Logline& ll, std::ostream& stream) {
146 if (kSourceByBus.find(ll.navmsg->bus) == kSourceByBus.end())
return;
148 using namespace std::chrono;
149 const auto now = system_clock::now();
150 const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count();
153 stream << kSourceByBus.at(ll.navmsg->bus) <<
",";
154 stream << ll.navmsg->source->iface <<
",";
155 switch (ll.navmsg->bus) {
156 case NavAddr::Bus::N0183: {
157 auto msg0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(ll.navmsg);
158 stream << msg0183->talker << msg0183->type <<
",";
160 case NavAddr::Bus::N2000: {
161 auto msg2000 = std::dynamic_pointer_cast<const Nmea2000Msg>(ll.navmsg);
162 stream << msg2000->PGN.to_string() <<
",";
164 case NavAddr::Bus::Signalk: {
165 auto msgSignalK = std::dynamic_pointer_cast<const SignalkMsg>(ll.navmsg);
166 stream <<
"\"" << msgSignalK->context_self <<
"\",";
169 assert(
false &&
"Illegal message type");
171 stream << VdrQuote(ll.navmsg->to_vdr()) <<
"\n";
175static void AddStdLogline(
const Logline& ll, std::ostream& stream,
char fs,
176 const NavmsgTimePoint log_start) {
177 if (!ll.navmsg)
return;
179 ws << TimeStamp(ll.navmsg->created_at, log_start) << fs;
180 if (ll.state.direction == NavmsgStatus::Direction::kOutput)
181 ws << kUtfRightArrow << fs;
182 else if (ll.state.direction == NavmsgStatus::Direction::kInput)
183 ws << kUtfLeftwardsArrowToBar << fs;
184 else if (ll.state.direction == NavmsgStatus::Direction::kInternal)
185 ws << kUtfLeftRightArrow << fs;
187 ws << kUtfLeftArrow << fs;
188 if (ll.state.status != NavmsgStatus::State::kOk)
189 ws << kUtfMultiplicationX << fs;
190 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredNoOutput)
191 ws << kUtfFallingDiagonal << fs;
192 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredDropped)
193 ws << kUtfCircledDivisionSlash << fs;
195 ws << kUtfCheckMark << fs;
197 ws << ll.navmsg->source->iface << fs;
198 ws << NavAddr::BusToString(ll.navmsg->bus) << fs;
199 if (ll.state.status != NavmsgStatus::State::kOk)
200 ws << (!ll.error_msg.empty() ? ll.error_msg :
"Unknown error");
203 ws << fs << ll.message <<
"\n";
210 TtyPanel(wxWindow* parent,
size_t lines)
211 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
212 wxTAB_TRAVERSAL,
"TtyPanel"),
213 m_tty_scroll(
nullptr),
214 m_filter(
this, wxID_ANY),
216 m_on_right_click([] {}) {
217 const auto vbox =
new wxBoxSizer(wxVERTICAL);
218 m_tty_scroll =
new TtyScroll(
this,
static_cast<int>(m_lines));
219 m_tty_scroll->Bind(wxEVT_RIGHT_UP,
220 [&](wxMouseEvent&) { m_on_right_click(); });
221 vbox->
Add(m_tty_scroll, wxSizerFlags(1).Expand().Border());
229 bool IsVisible()
const override {
return IsShownOnScreen(); }
231 void OnStop(
bool stop)
const {
232 m_tty_scroll->
Pause(stop);
234 m_tty_scroll->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT);
236 m_tty_scroll->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
241 void SetQuickFilter(
const std::string& filter)
const {
245 void SetOnRightClick(std::function<
void()> f) {
246 m_on_right_click = std::move(f);
251 auto window = wxWindow::FindWindowByName(
"TtyPanel");
253 auto tty_panel =
dynamic_cast<TtyPanel*
>(window);
254 if (tty_panel) tty_panel->
Add(ll);
258 wxSize DoGetBestClientSize()
const override {
259 return {1,
static_cast<int>(m_lines * GetCharHeight())};
266 std::function<void()> m_on_right_click;
274 m_text_ctrl(
new wxTextCtrl(
this, wxID_ANY)),
275 m_on_text_evt(std::move(on_text_evt)) {
276 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
277 auto flags = wxSizerFlags(0).Border();
278 auto label_box =
new wxBoxSizer(wxVERTICAL);
279 label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Quick filter:")));
280 hbox->Add(label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
281 hbox->Add(m_text_ctrl, flags);
285 m_text_ctrl->Bind(wxEVT_TEXT, [&](wxCommandEvent&) { m_on_text_evt(); });
288 bool Show(
bool show)
override {
289 if (!show) m_text_ctrl->SetValue(
"");
290 return wxWindow::Show(show);
293 [[nodiscard]] std::string GetValue()
const {
294 return m_text_ctrl->GetValue().ToStdString();
298 wxTextCtrl* m_text_ctrl;
299 std::function<void()> m_on_text_evt;
306 : wxChoice(parent, wxID_ANY), m_tty_panel(tty_panel) {
307 wxWindow::SetName(kFilterChoiceName);
308 Bind(wxEVT_CHOICE, [&](wxCommandEvent&) { OnChoice(); });
309 OnFilterListChange();
310 const int ix = wxChoice::FindString(kLabels.at(
"default"));
311 if (ix != wxNOT_FOUND) wxChoice::SetSelection(ix);
312 NavmsgFilter filter = filters_on_disk::Read(
"default.filter");
313 m_tty_panel->SetFilter(filter);
316 void OnFilterListChange() {
318 int select_ix = GetSelection();
319 std::string selected;
320 if (select_ix != wxNOT_FOUND) selected = GetString(select_ix).ToStdString();
322 for (
auto& filter : m_filters) {
324 Append(kLabels.at(filter.m_name));
325 }
catch (std::out_of_range&) {
326 if (filter.m_description.empty())
327 Append(filter.m_name);
329 Append(filter.m_description);
332 if (!selected.empty()) {
333 int ix = FindString(selected);
334 SetSelection(ix == wxNOT_FOUND ? 0 : ix);
338 void OnFilterUpdate(
const std::string& name) {
340 int select_ix = GetSelection();
341 if (select_ix == wxNOT_FOUND)
return;
343 std::string selected = GetString(select_ix).ToStdString();
344 if (selected != name)
return;
347 m_tty_panel->SetFilter(filter);
350 void OnApply(
const std::string& name) {
351 int found = FindString(name);
352 if (found == wxNOT_FOUND) {
353 for (
auto& filter : m_filters) {
354 if (filter.m_name == name) {
355 found = FindString(filter.m_description);
360 if (found == wxNOT_FOUND)
return;
363 OnFilterUpdate(name);
369 const std::unordered_map<std::string, std::string> kLabels = {
370 {
"all-data", _(
"All data")},
371 {
"all-nmea", _(
"All NMEA data")},
372 {
"default", _(
"Default settings")},
373 {
"malformed", _(
"Malformed messages")},
374 {
"nmea-input", _(
"NMEA input data")},
375 {
"nmea-output", _(
"NMEA output data")},
376 {
"plugins", _(
"Messages to plugins")},
379 std::vector<NavmsgFilter> m_filters;
383 wxString label = GetString(GetSelection());
384 NavmsgFilter filter = FilterByLabel(label.ToStdString());
385 m_tty_panel->SetFilter(filter);
389 std::string name = label;
390 for (
const auto& kv : kLabels) {
391 if (kv.second == label) {
397 for (
auto& f : m_filters)
398 if (f.m_name == name)
return f;
400 for (
auto& f : m_filters)
401 if (f.m_description == label)
return f;
411 : wxButton(parent, wxID_ANY),
413 m_on_stop(std::move(on_stop)) {
414 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
420 std::function<void(
bool)> m_on_stop;
423 is_paused = !is_paused;
424 m_on_stop(is_paused);
425 SetLabel(is_paused ? _(
"Resume") : _(
"Pause"));
432 CloseButton(wxWindow* parent, std::function<
void()> on_close)
433 : wxButton(parent, wxID_ANY), m_on_close(std::move(on_close)) {
434 wxButton::SetLabel(_(
"Close"));
435 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
440 std::function<void()> m_on_close;
442 void OnClick()
const { m_on_close(); }
454 m_set_logtype(std::move(set_logtype)),
456 kFilenameLabelId(wxWindow::NewControlId()) {
457 auto flags = wxSizerFlags(0).Border();
460 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
461 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
462 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
464 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
465 default_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
466 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
468 default_btn->SetValue(
true);
469 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
470 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
471 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
473 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
474 left_vbox->Add(default_btn, flags.DoubleBorder());
475 left_vbox->Add(vdr_btn, flags);
476 left_vbox->Add(csv_btn, flags);
479 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
480 auto label =
new wxStaticText(
this, kFilenameLabelId,
481 m_logger.GetDefaultLogfile().string());
482 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
483 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
485 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
486 force_box->Bind(wxEVT_CHECKBOX, [&](
const wxCommandEvent& e) {
487 m_overwrite = e.IsChecked();
489 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
490 right_vbox->Add(label, flags);
491 right_vbox->Add(path_btn, flags);
492 right_vbox->Add(force_box, flags);
495 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
496 hbox->Add(left_vbox, flags);
497 hbox->Add(wxWindow::GetCharWidth() * 10, 0, 1);
498 hbox->Add(right_vbox, flags);
504 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
508 void OnFileDialog()
const {
510 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
511 wxFileDialog dlg(m_parent, _(
"Select logfile"),
512 m_logger.GetDefaultLogfile().parent_path().string(),
513 m_logger.GetDefaultLogfile().stem().string(),
514 m_logger.GetFileDlgTypes(),
options);
515 if (dlg.ShowModal() == wxID_CANCEL)
return;
516 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
517 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
518 file_label->SetLabel(dlg.GetPath());
522 SetFormatFunc m_set_logtype;
524 const int kFilenameLabelId;
529 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
530 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
531 auto flags = wxSizerFlags(0).Border();
534 auto buttons =
new wxStdDialogButtonSizer();
535 auto close_btn =
new wxButton(
this, wxID_CLOSE);
536 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
537 [&](wxCommandEvent& ev) { EndModal(0); });
538 buttons->AddButton(close_btn);
540 buttons->Fit(parent);
543 auto panel =
new ThePanel(
this, std::move(set_logtype), logger);
544 auto vbox =
new wxBoxSizer(wxVERTICAL);
545 vbox->Add(panel, flags.Expand());
546 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
547 vbox->Add(buttons, flags.Expand());
558 enum class Id :
char {
568 : m_parent(parent), m_logger(logger) {
569 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
570 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
571 auto filters =
new wxMenu(
"");
572 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
573 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
574 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
575 AppendSubMenu(filters, _(
"Filters..."));
576 if (IsUserFilter(m_filter))
577 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
579 Bind(wxEVT_MENU, [&](
const wxCommandEvent& ev) {
580 switch (
static_cast<Id
>(ev.GetId())) {
585 case Id::kViewStdColors:
586 SetColor(static_cast<int>(Id::kViewStdColors));
590 CreateFilterDlg(parent);
593 case Id::kEditFilter:
594 EditFilterDlg(wxTheApp->GetTopWindow());
597 case Id::kEditActiveFilter:
598 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
601 case Id::kDeleteFilter:
602 RemoveFilterDlg(parent);
606 Check(
static_cast<int>(Id::kViewStdColors),
true);
609 void SetFilterName(
const std::string& filter) {
610 int id =
static_cast<int>(Id::kEditActiveFilter);
611 if (FindItem(
id)) Delete(
id);
612 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
616 void ConfigureLogging()
const {
619 [&](DataLogger::Format f,
const std::string& s) { SetLogFormat(f, s); },
622 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
628 static wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
629 return root->Append(
static_cast<int>(
id), label);
632 void SetLogFormat(DataLogger::Format format,
const std::string& label)
const {
633 m_logger.SetFormat(format);
634 std::string extension =
635 format == DataLogger::Format::kDefault ?
".log" :
".csv";
636 fs::path path = m_logger.GetLogfile();
637 path = path.parent_path() / (path.stem().string() + extension);
638 m_logger.SetLogfile(path);
641 void SetColor(
int id)
const {
642 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
643 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
644 if (!tty_scroll)
return;
646 wxMenuItem* item = FindItem(
id);
648 if (item->IsCheck() && item->IsChecked())
649 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
651 tty_scroll->SetColors(
652 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
657 std::string m_filter;
664 : wxButton(parent, wxID_ANY),
669 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
674 void UpdateTooltip() {
676 SetToolTip(_(
"Click to stop logging"));
678 SetToolTip(_(
"Click to start logging"));
687 void OnClick(
bool ctor =
false) {
688 if (!m_is_inited && !ctor) {
689 m_menu.ConfigureLogging();
692 is_logging = !is_logging;
693 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
695 m_logger.SetLogging(is_logging);
704 SetToolTip(_(
"Copy to clipboard"));
705 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
707 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
717 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
718 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
723 wxWindow* m_quick_filter;
727 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
728 m_show_filter = !m_show_filter;
729 m_quick_filter->Show(m_show_filter);
730 SetToolTip(m_show_filter ? _(
"Close quick filter")
731 : _(
"Open quick filter"));
732 GetGrandParent()->Layout();
740 std::function<std::string()> get_current_filter)
743 m_get_current_filter(std::move(get_current_filter)) {
745 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
746 SetToolTip(_(
"Open menu"));
751 std::function<std::string()> m_get_current_filter;
754 m_menu.SetFilterName(m_get_current_filter());
763 std::function<
void(
bool)> on_stop,
764 const std::function<
void()>& on_hide,
DataLogger& logger)
768 m_menu(
this, logger),
769 m_log_button(
new LogButton(
this, logger, m_menu)) {
771 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
772 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
774 auto flags = wxSizerFlags(0).Border();
775 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
776 wbox->Add(m_log_button, flags);
779 wbox->Add(wxWindow::GetCharWidth() * 5, 0, 1);
780 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
781 wbox->Add(m_filter_choice, flags);
784 auto get_current_filter = [&] {
785 return m_filter_choice->GetStringSelection().ToStdString();
788 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
790 wbox->Add(
new CloseButton(
this, std::move(on_hide)), flags);
796 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
800 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
801 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
806 void OnContextClick() {
807 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
814 [[nodiscard]] wxSize DoGetBestClientSize()
const override {
818 return {85 * GetCharWidth(), 5 * GetCharHeight() / 2};
823 wxChoice* m_filter_choice;
825 wxButton* m_log_button;
828DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
831 m_stream(path, std::ios_base::app),
833 m_format(Format::kDefault),
834 m_log_start(NavmsgClock::now()) {}
836DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
838void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
840void DataLogger::SetLogfile(
const fs::path& path) {
841 m_stream = std::ofstream(path);
842 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
843 const auto now = std::chrono::system_clock::now();
844 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
845 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
846 m_stream <<
"received_at,protocol,source,msg_type,raw_data\n";
847 m_stream << std::flush;
852void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
854fs::path DataLogger::GetDefaultLogfile() {
855 if (m_path.stem() != NullLogfile().stem())
return m_path;
856 fs::path path(g_BasePlatform->GetHomeDir().ToStdString());
858 path += (m_format == Format::kDefault ?
".log" :
".csv");
862std::string DataLogger::GetFileDlgTypes()
const {
863 if (m_format == Format::kDefault)
864 return _(
"Log file (*.log)|*.log");
866 return _(
"Spreadsheet csv file(*.csv)|*.csv");
869void DataLogger::Add(
const Logline& ll) {
870 if (!m_is_logging || !ll.navmsg)
return;
871 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
872 if (m_format == DataLogger::Format::kVdr)
873 AddVdrLogline(ll, m_stream);
875 AddStdLogline(ll, m_stream,
876 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
880DataMonitor::DataMonitor(wxWindow* parent)
881 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxPoint(0, 0), wxDefaultSize,
882 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
883 kDataMonitorWindowName),
884 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
887 m_quick_filter(
nullptr),
889 auto vbox =
new wxBoxSizer(wxVERTICAL);
890 auto tty_panel =
new TtyPanel(
this, 12);
891 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
892 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
894 auto on_quick_filter_evt = [&, tty_panel] {
896 assert(quick_filter);
897 std::string value = quick_filter->GetValue();
898 tty_panel->SetQuickFilter(value);
901 vbox->Add(m_quick_filter, wxSizerFlags());
903 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
904 auto on_close = [&,
this]() { this->OnHide(); };
905 auto status_line =
new StatusLine(
this, m_quick_filter, tty_panel, on_stop,
907 vbox->Add(status_line, wxSizerFlags().Expand());
912 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
913 tty_panel->SetQuickFilter(GetLabel().ToStdString());
915 m_quick_filter->Hide();
916 tty_panel->SetOnRightClick(
917 [&, status_line] { status_line->OnContextClick(); });
919 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
920 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
921 status_line->OnContextClick();
924 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
926 m_filter_update_lstnr.Init(FilterEvents::GetInstance().filter_update,
928 OnFilterUpdate(ev.GetString().ToStdString());
931 m_filter_apply_lstnr.Init(FilterEvents::GetInstance().filter_apply,
933 OnFilterApply(ev.GetString().ToStdString());
943 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
944 assert(w &&
"No TtyPanel found");
945 return w->IsShownOnScreen();
948void DataMonitor::OnFilterListChange() {
949 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
952 assert(filter_choice &&
"Wrong FilterChoice type (!)");
953 filter_choice->OnFilterListChange();
956void DataMonitor::OnFilterUpdate(
const std::string& name)
const {
957 if (name != m_current_filter)
return;
958 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
960 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
961 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
962 tty_scroll->SetFilter(filters_on_disk::Read(name));
965void DataMonitor::OnFilterApply(
const std::string& name) {
966 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
969 assert(filter_choice &&
"Wrong FilterChoice type (!)");
970 m_current_filter = name;
971 filter_choice->OnApply(name);
974void DataMonitor::OnHide() { Hide(); }
976#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().
T * GetWindowById(int id)
Return window with given id (which must exist) cast to T*.
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,...