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"
52#include "std_filesystem.h"
53#include "svg_button.h"
56#include "user_colors_dlg.h"
59#pragma clang diagnostic push
60#pragma ide diagnostic ignored "UnreachableCode"
64#if wxCHECK_VERSION(3, 2, 0)
65#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString()
67#define _(s) wxGetTranslation((s)).ToStdString()
70using SetFormatFunc = std::function<void(DataLogger::Format, std::string)>;
75 return dynamic_cast<T*
>(wxWindow::FindWindowById(
id));
78static const char*
const kFilterChoiceName =
"FilterChoiceWindow";
81static const std::unordered_map<NavAddr::Bus, std::string> kSourceByBus = {
82 {NavAddr::Bus::N0183,
"NMEA0183"},
83 {NavAddr::Bus::N2000,
"NMEA2000"},
84 {NavAddr::Bus::Signalk,
"SignalK"}};
87static bool IsUserFilter(
const std::string& filter_name) {
89 auto found = std::find(filters.begin(), filters.end(), filter_name);
90 if (found != filters.end())
return true;
92 filters.begin(), filters.end(),
93 [filter_name](
const std::string& f) { return f == filter_name; });
97static std::string TimeStamp(
const NavmsgTimePoint& when,
98 const NavmsgTimePoint& since) {
99 using namespace std::chrono;
102 auto duration = when - since;
103 std::stringstream ss;
104 auto hrs = duration_cast<hours>(duration) % 24;
105 duration -= duration_cast<hours>(duration) / 24;
106 auto mins = duration_cast<minutes>(duration) % 60;
107 duration -= duration_cast<minutes>(duration) / 60;
108 auto secs = duration_cast<seconds>(duration) % 60;
109 duration -= duration_cast<seconds>(duration) / 60;
110 const auto msecs = duration_cast<milliseconds>(duration);
111 ss << setw(2) << setfill(
'0') << hrs.count() <<
":" << setw(2) << mins.count()
112 <<
":" << setw(2) << secs.count() <<
"." << setw(3)
113 << msecs.count() % 1000;
117static fs::path NullLogfile() {
118 if (wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS)
129static std::string VdrQuote(
const std::string& arg) {
130 auto static constexpr npos = std::string::npos;
131 if (arg.find(
',') == npos && arg.find(
'"') == npos)
return arg;
133 for (
const auto c : arg) {
139 return "\"" + s +
"\"";
146static void AddVdrLogline(
const Logline& ll, std::ostream& stream) {
147 if (kSourceByBus.find(ll.navmsg->bus) == kSourceByBus.end())
return;
149 using namespace std::chrono;
150 const auto now = system_clock::now();
151 const auto ms = duration_cast<milliseconds>(now.time_since_epoch()).count();
154 stream << kSourceByBus.at(ll.navmsg->bus) <<
",";
155 stream << ll.navmsg->source->iface <<
",";
156 switch (ll.navmsg->bus) {
157 case NavAddr::Bus::N0183: {
158 auto msg0183 = std::dynamic_pointer_cast<const Nmea0183Msg>(ll.navmsg);
159 stream << msg0183->talker << msg0183->type <<
",";
161 case NavAddr::Bus::N2000: {
162 auto msg2000 = std::dynamic_pointer_cast<const Nmea2000Msg>(ll.navmsg);
163 stream << msg2000->PGN.to_string() <<
",";
165 case NavAddr::Bus::Signalk: {
166 auto msgSignalK = std::dynamic_pointer_cast<const SignalkMsg>(ll.navmsg);
167 stream <<
"\"" << msgSignalK->context_self <<
"\",";
170 assert(
false &&
"Illegal message type");
172 stream << VdrQuote(ll.navmsg->to_vdr()) <<
"\n";
176static void AddStdLogline(
const Logline& ll, std::ostream& stream,
char fs,
177 const NavmsgTimePoint log_start) {
178 if (!ll.navmsg)
return;
180 ws << TimeStamp(ll.navmsg->created_at, log_start) << fs;
181 if (ll.state.direction == NavmsgStatus::Direction::kOutput)
182 ws << kUtfRightArrow << fs;
183 else if (ll.state.direction == NavmsgStatus::Direction::kInput)
184 ws << kUtfLeftwardsArrowToBar << fs;
185 else if (ll.state.direction == NavmsgStatus::Direction::kInternal)
186 ws << kUtfLeftRightArrow << fs;
188 ws << kUtfLeftArrow << fs;
189 if (ll.state.status != NavmsgStatus::State::kOk)
190 ws << kUtfMultiplicationX << fs;
191 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredNoOutput)
192 ws << kUtfFallingDiagonal << fs;
193 else if (ll.state.accepted == NavmsgStatus::Accepted::kFilteredDropped)
194 ws << kUtfCircledDivisionSlash << fs;
196 ws << kUtfCheckMark << fs;
198 ws << ll.navmsg->source->iface << fs;
199 ws << NavAddr::BusToString(ll.navmsg->bus) << fs;
200 if (ll.state.status != NavmsgStatus::State::kOk)
201 ws << (!ll.error_msg.empty() ? ll.error_msg :
"Unknown error");
204 ws << fs << ll.message <<
"\n";
211 TtyPanel(wxWindow* parent,
size_t lines)
212 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
213 wxTAB_TRAVERSAL,
"TtyPanel"),
214 m_tty_scroll(
nullptr),
215 m_filter(
this, wxID_ANY),
217 m_on_right_click([] {}) {
218 const auto vbox =
new wxBoxSizer(wxVERTICAL);
219 m_tty_scroll =
new TtyScroll(
this,
static_cast<int>(m_lines));
220 m_tty_scroll->Bind(wxEVT_RIGHT_UP,
221 [&](wxMouseEvent&) { m_on_right_click(); });
222 vbox->
Add(m_tty_scroll, wxSizerFlags(1).Expand().Border());
230 bool IsVisible()
const override {
return IsShownOnScreen(); }
232 void OnStop(
bool stop)
const {
233 m_tty_scroll->
Pause(stop);
235 m_tty_scroll->ShowScrollbars(wxSHOW_SB_DEFAULT, wxSHOW_SB_DEFAULT);
237 m_tty_scroll->ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
242 void SetQuickFilter(
const std::string& filter)
const {
246 void SetOnRightClick(std::function<
void()> f) {
247 m_on_right_click = std::move(f);
252 auto window = wxWindow::FindWindowByName(
"TtyPanel");
254 auto tty_panel =
dynamic_cast<TtyPanel*
>(window);
255 if (tty_panel) tty_panel->
Add(ll);
259 wxSize DoGetBestClientSize()
const override {
260 return {1,
static_cast<int>(m_lines * GetCharHeight())};
267 std::function<void()> m_on_right_click;
275 m_text_ctrl(
new wxTextCtrl(
this, wxID_ANY)),
276 m_on_text_evt(std::move(on_text_evt)) {
277 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
278 auto flags = wxSizerFlags(0).Border();
279 auto label_box =
new wxBoxSizer(wxVERTICAL);
280 label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Quick filter:")));
281 hbox->Add(label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
282 hbox->Add(m_text_ctrl, flags);
286 m_text_ctrl->Bind(wxEVT_TEXT, [&](wxCommandEvent&) { m_on_text_evt(); });
289 bool Show(
bool show)
override {
290 if (!show) m_text_ctrl->SetValue(
"");
291 return wxWindow::Show(show);
294 [[nodiscard]] std::string GetValue()
const {
295 return m_text_ctrl->GetValue().ToStdString();
299 wxTextCtrl* m_text_ctrl;
300 std::function<void()> m_on_text_evt;
307 : wxChoice(parent, wxID_ANY), m_tty_panel(tty_panel) {
308 wxWindow::SetName(kFilterChoiceName);
309 Bind(wxEVT_CHOICE, [&](wxCommandEvent&) { OnChoice(); });
310 OnFilterListChange();
311 const int ix = wxChoice::FindString(kLabels.at(
"default"));
312 if (ix != wxNOT_FOUND) wxChoice::SetSelection(ix);
313 NavmsgFilter filter = filters_on_disk::Read(
"default.filter");
314 m_tty_panel->SetFilter(filter);
317 void OnFilterListChange() {
319 int select_ix = GetSelection();
320 std::string selected;
321 if (select_ix != wxNOT_FOUND) selected = GetString(select_ix).ToStdString();
323 for (
auto& filter : m_filters) {
325 Append(kLabels.at(filter.m_name));
326 }
catch (std::out_of_range&) {
327 if (filter.m_description.empty())
328 Append(filter.m_name);
330 Append(filter.m_description);
333 if (!selected.empty()) {
334 int ix = FindString(selected);
335 SetSelection(ix == wxNOT_FOUND ? 0 : ix);
339 void OnFilterUpdate(
const std::string& name) {
341 int select_ix = GetSelection();
342 if (select_ix == wxNOT_FOUND)
return;
344 std::string selected = GetString(select_ix).ToStdString();
345 if (selected != name)
return;
348 m_tty_panel->SetFilter(filter);
351 void OnApply(
const std::string& name) {
352 int found = FindString(name);
353 if (found == wxNOT_FOUND) {
354 for (
auto& filter : m_filters) {
355 if (filter.m_name == name) {
356 found = FindString(filter.m_description);
361 if (found == wxNOT_FOUND)
return;
364 OnFilterUpdate(name);
370 const std::unordered_map<std::string, std::string> kLabels = {
371 {
"all-data", _(
"All data")},
372 {
"all-nmea", _(
"All NMEA data")},
373 {
"default", _(
"Default settings")},
374 {
"malformed", _(
"Malformed messages")},
375 {
"nmea-input", _(
"NMEA input data")},
376 {
"nmea-output", _(
"NMEA output data")},
377 {
"plugins", _(
"Messages to plugins")},
380 std::vector<NavmsgFilter> m_filters;
384 wxString label = GetString(GetSelection());
385 NavmsgFilter filter = FilterByLabel(label.ToStdString());
386 m_tty_panel->SetFilter(filter);
390 std::string name = label;
391 for (
const auto& kv : kLabels) {
392 if (kv.second == label) {
398 for (
auto& f : m_filters)
399 if (f.m_name == name)
return f;
401 for (
auto& f : m_filters)
402 if (f.m_description == label)
return f;
412 : wxButton(parent, wxID_ANY),
414 m_on_stop(std::move(on_stop)) {
415 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
421 std::function<void(
bool)> m_on_stop;
424 is_paused = !is_paused;
425 m_on_stop(is_paused);
426 SetLabel(is_paused ? _(
"Resume") : _(
"Pause"));
433 CloseButton(wxWindow* parent, std::function<
void()> on_close)
434 : wxButton(parent, wxID_ANY), m_on_close(std::move(on_close)) {
435 wxButton::SetLabel(_(
"Close"));
436 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
441 std::function<void()> m_on_close;
443 void OnClick()
const { m_on_close(); }
455 m_set_logtype(std::move(set_logtype)),
457 kFilenameLabelId(wxWindow::NewControlId()) {
458 auto flags = wxSizerFlags(0).Border();
461 auto vdr_btn =
new wxRadioButton(
this, wxID_ANY,
"VDR");
462 vdr_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
463 m_set_logtype(DataLogger::Format::kVdr,
"VDR");
465 auto default_btn =
new wxRadioButton(
this, wxID_ANY,
"Default");
466 default_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
467 m_set_logtype(DataLogger::Format::kDefault, _(
"Default"));
469 default_btn->SetValue(
true);
470 auto csv_btn =
new wxRadioButton(
this, wxID_ANY,
"CSV");
471 csv_btn->Bind(wxEVT_RADIOBUTTON, [&](
const wxCommandEvent& e) {
472 m_set_logtype(DataLogger::Format::kCsv,
"CSV");
474 auto left_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log format"));
475 left_vbox->Add(default_btn, flags.DoubleBorder());
476 left_vbox->Add(vdr_btn, flags);
477 left_vbox->Add(csv_btn, flags);
480 m_logger.SetLogfile(m_logger.GetDefaultLogfile());
481 auto label =
new wxStaticText(
this, kFilenameLabelId,
482 m_logger.GetDefaultLogfile().string());
483 auto path_btn =
new wxButton(
this, wxID_ANY, _(
"Change..."));
484 path_btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnFileDialog(); });
486 new wxCheckBox(
this, wxID_ANY, _(
"Overwrite existing file"));
487 force_box->Bind(wxEVT_CHECKBOX, [&](
const wxCommandEvent& e) {
488 m_overwrite = e.IsChecked();
490 auto right_vbox =
new wxStaticBoxSizer(wxVERTICAL,
this, _(
"Log file"));
491 right_vbox->Add(label, flags);
492 right_vbox->Add(path_btn, flags);
493 right_vbox->Add(force_box, flags);
496 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
497 hbox->Add(left_vbox, flags);
498 hbox->Add(wxWindow::GetCharWidth() * 10, 0, 1);
499 hbox->Add(right_vbox, flags);
505 GetWindowById<wxStaticText>(kFilenameLabelId)->SetLabel(ev.GetString());
509 void OnFileDialog()
const {
511 if (!m_overwrite)
options |= wxFD_OVERWRITE_PROMPT;
512 wxFileDialog dlg(m_parent, _(
"Select logfile"),
513 m_logger.GetDefaultLogfile().parent_path().string(),
514 m_logger.GetDefaultLogfile().stem().string(),
515 m_logger.GetFileDlgTypes(),
options);
516 if (dlg.ShowModal() == wxID_CANCEL)
return;
517 m_logger.SetLogfile(fs::path(dlg.GetPath().ToStdString()));
518 auto file_label = GetWindowById<wxStaticText>(kFilenameLabelId);
519 file_label->SetLabel(dlg.GetPath());
523 SetFormatFunc m_set_logtype;
525 const int kFilenameLabelId;
530 : wxDialog(parent, wxID_ANY, _(
"Logging setup"), wxDefaultPosition,
531 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
532 auto flags = wxSizerFlags(0).Border();
535 auto buttons =
new wxStdDialogButtonSizer();
536 auto close_btn =
new wxButton(
this, wxID_CLOSE);
537 close_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
538 [&](wxCommandEvent& ev) { EndModal(0); });
539 buttons->AddButton(close_btn);
541 buttons->Fit(parent);
544 auto panel =
new ThePanel(
this, std::move(set_logtype), logger);
545 auto vbox =
new wxBoxSizer(wxVERTICAL);
546 vbox->Add(panel, flags.Expand());
547 vbox->Add(
new wxStaticLine(
this, wxID_ANY), flags.Expand());
548 vbox->Add(buttons, flags.Expand());
559 enum class Id :
char {
570 : m_parent(parent), m_logger(logger) {
571 AppendCheckItem(
static_cast<int>(Id::kViewStdColors), _(
"Use colors"));
572 Append(
static_cast<int>(Id::kUserColors), _(
"Colors..."));
573 Append(
static_cast<int>(Id::kLogSetup), _(
"Logging..."));
574 auto filters =
new wxMenu(
"");
575 AppendId(filters, Id::kNewFilter, _(
"Create new..."));
576 AppendId(filters, Id::kEditFilter, _(
"Edit..."));
577 AppendId(filters, Id::kDeleteFilter, _(
"Delete..."));
578 AppendSubMenu(filters, _(
"Filters..."));
579 if (IsUserFilter(m_filter))
580 Append(
static_cast<int>(Id::kEditActiveFilter), _(
"Edit active filter"));
582 Bind(wxEVT_MENU, [&](
const wxCommandEvent& ev) {
583 switch (
static_cast<Id
>(ev.GetId())) {
588 case Id::kViewStdColors:
589 SetColor(static_cast<int>(Id::kViewStdColors));
593 CreateFilterDlg(parent);
596 case Id::kEditFilter:
597 EditFilterDlg(wxTheApp->GetTopWindow());
600 case Id::kEditActiveFilter:
601 EditOneFilterDlg(wxTheApp->GetTopWindow(), m_filter);
604 case Id::kDeleteFilter:
605 RemoveFilterDlg(parent);
608 case Id::kUserColors:
609 UserColorsDlg(wxTheApp->GetTopWindow());
613 Check(
static_cast<int>(Id::kViewStdColors),
true);
616 void SetFilterName(
const std::string& filter) {
617 int id =
static_cast<int>(Id::kEditActiveFilter);
618 if (FindItem(
id)) Delete(
id);
619 if (IsUserFilter(filter)) Append(
id, _(
"Edit active filter"));
623 void ConfigureLogging()
const {
626 [&](DataLogger::Format f,
const std::string& s) { SetLogFormat(f, s); },
629 auto monitor = wxWindow::FindWindowByName(kDataMonitorWindowName);
635 static wxMenuItem* AppendId(wxMenu* root, Id
id,
const wxString& label) {
636 return root->Append(
static_cast<int>(
id), label);
639 void SetLogFormat(DataLogger::Format format,
const std::string& label)
const {
640 m_logger.SetFormat(format);
641 std::string extension =
642 format == DataLogger::Format::kDefault ?
".log" :
".csv";
643 fs::path path = m_logger.GetLogfile();
644 path = path.parent_path() / (path.stem().string() + extension);
645 m_logger.SetLogfile(path);
648 void SetColor(
int id)
const {
649 auto* w = wxWindow::FindWindowByName(
"TtyScroll");
650 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
651 if (!tty_scroll)
return;
653 wxMenuItem* item = FindItem(
id);
655 if (item->IsCheck() && item->IsChecked())
656 tty_scroll->SetColors(std::make_unique<StdColorsByState>());
658 tty_scroll->SetColors(
659 std::make_unique<NoColorsByState>(tty_scroll->GetForegroundColour()));
664 std::string m_filter;
671 : wxButton(parent, wxID_ANY),
676 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
681 void UpdateTooltip() {
683 SetToolTip(_(
"Click to stop logging"));
685 SetToolTip(_(
"Click to start logging"));
694 void OnClick(
bool ctor =
false) {
695 if (!m_is_inited && !ctor) {
696 m_menu.ConfigureLogging();
699 is_logging = !is_logging;
700 SetLabel(is_logging ? _(
"Stop logging") : _(
"Start logging"));
702 m_logger.SetLogging(is_logging);
711 SetToolTip(_(
"Copy to clipboard"));
712 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) {
714 dynamic_cast<TtyScroll*
>(wxWindow::FindWindowByName(
"TtyScroll"));
724 :
SvgButton(parent), m_quick_filter(quick_filter), m_show_filter(
true) {
725 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
730 wxWindow* m_quick_filter;
734 LoadIcon(m_show_filter ? kFunnelSvg : kNoFunnelSvg);
735 m_show_filter = !m_show_filter;
736 m_quick_filter->Show(m_show_filter);
737 SetToolTip(m_show_filter ? _(
"Close quick filter")
738 : _(
"Open quick filter"));
739 GetGrandParent()->Layout();
747 std::function<std::string()> get_current_filter)
750 m_get_current_filter(std::move(get_current_filter)) {
752 Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { OnClick(); });
753 SetToolTip(_(
"Open menu"));
758 std::function<std::string()> m_get_current_filter;
761 m_menu.SetFilterName(m_get_current_filter());
770 std::function<
void(
bool)> on_stop,
771 const std::function<
void()>& on_hide,
DataLogger& logger)
775 m_menu(
this, logger),
776 m_log_button(
new LogButton(
this, logger, m_menu)) {
778 auto filter_label_box =
new wxBoxSizer(wxVERTICAL);
779 filter_label_box->Add(
new wxStaticText(
this, wxID_ANY, _(
"Filter")));
781 auto flags = wxSizerFlags(0).Border();
782 auto wbox =
new wxWrapSizer(wxHORIZONTAL);
783 wbox->Add(m_log_button, flags);
786 wbox->Add(wxWindow::GetCharWidth() * 5, 0, 1);
787 wbox->Add(filter_label_box, flags.Align(wxALIGN_CENTER_VERTICAL));
788 wbox->Add(m_filter_choice, flags);
791 auto get_current_filter = [&] {
792 return m_filter_choice->GetStringSelection().ToStdString();
795 wbox->Add(
new MenuButton(
this, m_menu, get_current_filter), flags);
797 wbox->Add(
new CloseButton(
this, std::move(on_hide)), flags);
803 Bind(wxEVT_SIZE, [&](wxSizeEvent& ev) {
807 Bind(wxEVT_RIGHT_UP, [&](wxMouseEvent& ev) {
808 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
813 void OnContextClick() {
814 m_menu.SetFilterName(m_filter_choice->GetStringSelection().ToStdString());
821 [[nodiscard]] wxSize DoGetBestClientSize()
const override {
825 return {85 * GetCharWidth(), 5 * GetCharHeight() / 2};
830 wxChoice* m_filter_choice;
832 wxButton* m_log_button;
835DataLogger::DataLogger(wxWindow* parent,
const fs::path& path)
838 m_stream(path, std::ios_base::app),
840 m_format(Format::kDefault),
841 m_log_start(NavmsgClock::now()) {}
843DataLogger::DataLogger(wxWindow* parent) :
DataLogger(parent, NullLogfile()) {}
845void DataLogger::SetLogging(
bool logging) { m_is_logging = logging; }
847void DataLogger::SetLogfile(
const fs::path& path) {
848 m_stream = std::ofstream(path);
849 m_stream <<
"# timestamp_format: EPOCH_MILLIS\n";
850 const auto now = std::chrono::system_clock::now();
851 const std::time_t t_c = std::chrono::system_clock::to_time_t(now);
852 m_stream <<
"# Created at: " << std::ctime(&t_c) <<
" \n";
853 m_stream <<
"received_at,protocol,source,msg_type,raw_data\n";
854 m_stream << std::flush;
859void DataLogger::SetFormat(DataLogger::Format format) { m_format = format; }
861fs::path DataLogger::GetDefaultLogfile() {
862 if (m_path.stem() != NullLogfile().stem())
return m_path;
865 path += (m_format == Format::kDefault ?
".log" :
".csv");
869std::string DataLogger::GetFileDlgTypes()
const {
870 if (m_format == Format::kDefault)
871 return _(
"Log file (*.log)|*.log");
873 return _(
"Spreadsheet csv file(*.csv)|*.csv");
876void DataLogger::Add(
const Logline& ll) {
877 if (!m_is_logging || !ll.navmsg)
return;
878 if (m_format == Format::kVdr && ll.navmsg->to_vdr().empty())
return;
879 if (m_format == DataLogger::Format::kVdr)
880 AddVdrLogline(ll, m_stream);
882 AddStdLogline(ll, m_stream,
883 m_format == DataLogger::Format::kCsv ?
'|' :
' ',
887DataMonitor::DataMonitor(wxWindow* parent)
888 : wxFrame(parent, wxID_ANY, _(
"Data Monitor"), wxPoint(0, 0), wxDefaultSize,
889 wxDEFAULT_FRAME_STYLE | wxFRAME_FLOAT_ON_PARENT,
890 kDataMonitorWindowName),
891 m_monitor_src([&](const std::shared_ptr<const
NavMsg>& navmsg) {
894 m_quick_filter(
nullptr),
896 auto vbox =
new wxBoxSizer(wxVERTICAL);
897 auto tty_panel =
new TtyPanel(
this, 12);
898 vbox->Add(tty_panel, wxSizerFlags(1).Expand().Border());
899 vbox->Add(
new wxStaticLine(
this), wxSizerFlags().Expand().Border());
901 auto on_quick_filter_evt = [&, tty_panel] {
903 assert(quick_filter);
904 std::string value = quick_filter->GetValue();
905 tty_panel->SetQuickFilter(value);
908 vbox->Add(m_quick_filter, wxSizerFlags());
910 auto on_stop = [&, tty_panel](
bool stop) { tty_panel->OnStop(stop); };
911 auto on_close = [&,
this]() { this->OnHide(); };
912 auto status_line =
new StatusLine(
this, m_quick_filter, tty_panel, on_stop,
914 vbox->Add(status_line, wxSizerFlags().Expand());
919 m_quick_filter->Bind(wxEVT_TEXT, [&, tty_panel](wxCommandEvent&) {
920 tty_panel->SetQuickFilter(GetLabel().ToStdString());
922 m_quick_filter->Hide();
923 tty_panel->SetOnRightClick(
924 [&, status_line] { status_line->OnContextClick(); });
926 Bind(wxEVT_CLOSE_WINDOW, [
this](wxCloseEvent& ev) { Hide(); });
927 Bind(wxEVT_RIGHT_UP, [status_line](wxMouseEvent& ev) {
928 status_line->OnContextClick();
931 m_filter_list_lstnr.Init(FilterEvents::GetInstance().filter_list_change,
933 m_filter_update_lstnr.Init(FilterEvents::GetInstance().filter_update,
935 OnFilterUpdate(ev.GetString().ToStdString());
938 m_filter_apply_lstnr.Init(FilterEvents::GetInstance().filter_apply,
940 OnFilterApply(ev.GetString().ToStdString());
950 wxWindow* w = wxWindow::FindWindowByName(
"TtyPanel");
951 assert(w &&
"No TtyPanel found");
952 return w->IsShownOnScreen();
955void DataMonitor::OnFilterListChange() {
956 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
959 assert(filter_choice &&
"Wrong FilterChoice type (!)");
960 filter_choice->OnFilterListChange();
963void DataMonitor::OnFilterUpdate(
const std::string& name)
const {
964 if (name != m_current_filter)
return;
965 wxWindow* w = wxWindow::FindWindowByName(
"TtyScroll");
967 auto tty_scroll =
dynamic_cast<TtyScroll*
>(w);
968 assert(tty_scroll &&
"Wrong TtyScroll type (!)");
969 tty_scroll->SetFilter(filters_on_disk::Read(name));
972void DataMonitor::OnFilterApply(
const std::string& name) {
973 wxWindow* w = wxWindow::FindWindowByName(kFilterChoiceName);
976 assert(filter_choice &&
"Wrong FilterChoice type (!)");
977 m_current_filter = name;
978 filter_choice->OnApply(name);
981void DataMonitor::OnHide() { Hide(); }
983#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.
Data Monitor filter storage routines.
Hooks into gui available in model.
Data monitor filter definitions.
Basic DataMonitor logging interface: LogLine (reflects a line in the log) and NmeaLog,...