OpenCPN Partial API docs
Loading...
Searching...
No Matches
filter_dlg.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2025 Alec Leamas *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
23#include <functional>
24#include <memory>
25#include <string>
26
27#include <wx/app.h>
28#include <wx/arrstr.h>
29#include <wx/button.h>
30#include <wx/checkbox.h>
31#include <wx/checklst.h>
32#include <wx/choicdlg.h>
33#include <wx/frame.h>
34#include <wx/log.h>
35#include <wx/msgdlg.h>
36#include <wx/panel.h>
37#include <wx/radiobox.h>
38#include <wx/radiobut.h>
39#include <wx/sizer.h>
40#include <wx/statline.h>
41#include <wx/stattext.h>
42#include <wx/textdlg.h>
43#include <wx/wrapsizer.h>
44
45#ifndef ocpnUSE_wxBitmapBundle
46#include <wx/sstream.h>
47#include <wxSVG/svg.h>
48#endif
49
50#include "libgui/edit_button.h"
51
55#include "filter_dlg.h"
56#include "std_filesystem.h"
57#include "svg_icons.h"
58
59// Make _() return const char* instead of wxString;
60#undef _
61#if wxCHECK_VERSION(3, 2, 0)
62#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString().c_str()
63#else
64#define _(s) wxGetTranslation((s)).ToStdString().c_str()
65#endif
66
67static const std::string kEditFilterFrameName("EditFilterFrame::");
68
69static const char* const kFilterExists =
70 R"( Filter already exists. Please use Edit to update
71or Remove to delete it prior to create.)";
72
73static const std::unordered_map<NavmsgStatus::Direction, std::string>
74 kStringByDirection = {{NavmsgStatus::Direction::kInput, "Input"},
75 {NavmsgStatus::Direction::kHandled, "Handled"},
76 {NavmsgStatus::Direction::kOutput, "Output"},
77 {NavmsgStatus::Direction::kInternal, "Internal"}};
78
80template <typename T>
81T* GetWindowById(int id) {
82 return dynamic_cast<T*>(wxWindow::FindWindowById(id));
83};
84
85static NavmsgStatus::Direction StringToDirection(const std::string& s) {
86 for (const auto& it : kStringByDirection)
87 if (it.second == s) return it.first;
88 return NavmsgStatus::Direction::kNone;
89}
90
91static wxArrayString GetUserFilters() {
92 auto std_filters = filters_on_disk::List(false);
93 wxArrayString wx_filters;
94 for (auto& f : std_filters) wx_filters.Add(f);
95 return wx_filters;
96}
97
99static std::vector<std::string> GetActiveMessages() {
100 auto v = NavMsgBus::GetInstance().GetActiveMessages();
101 std::vector<std::string> result;
102 for (const auto& m : NavMsgBus::GetInstance().GetActiveMessages()) {
103 size_t pos = m.find("::");
104 if (pos == std::string::npos) {
105 result.push_back(m);
106 } else {
107 result.push_back(m.substr(pos + 2));
108 }
109 }
110 return result;
111}
112
113class NewFilterDlg : public wxTextEntryDialog {
114public:
115 NewFilterDlg(wxWindow*)
116 : wxTextEntryDialog(wxTheApp->GetTopWindow(), _("New filter name")) {}
117};
118
119class DeleteFilterDlg : public wxSingleChoiceDialog {
120public:
121 DeleteFilterDlg(wxWindow*)
122 : wxSingleChoiceDialog(wxTheApp->GetTopWindow(),
123 _("Remove filter (name):"), _("Remove filter"),
124 GetUserFilters()) {}
125};
126
127class SelectFilterDlg : public wxSingleChoiceDialog {
128public:
129 SelectFilterDlg(wxWindow*)
130 : wxSingleChoiceDialog(wxTheApp->GetTopWindow(), _("Edit filter (name):"),
131 _("Edit filter"), GetUserFilters()) {}
132};
133
134class RenameFilterChoiceDlg : public wxSingleChoiceDialog {
135public:
136 RenameFilterChoiceDlg(wxWindow*)
137 : wxSingleChoiceDialog(wxTheApp->GetTopWindow(),
138 _("Rename filter (name):"), _("Rename filter"),
139 GetUserFilters()) {}
140};
141
142class BadFilterNameDlg : public wxMessageDialog {
143public:
144 BadFilterNameDlg(wxWindow* parent)
145 : wxMessageDialog(parent, _(kFilterExists)) {}
146};
147
152class MsgTypePanel : public wxPanel {
153public:
154 MsgTypePanel(wxWindow* parent, NavmsgFilter& filter,
155 std::function<void()> on_update)
156 : wxPanel(parent, wxID_ANY),
157 m_filter(filter),
158 m_on_update(std::move(on_update)),
159 kIncludeBtnId(wxWindow::NewControlId()),
160 kExcludeBtnId(wxWindow::NewControlId()),
161 kEditBtnId(wxWindow::NewControlId()),
162 kListboxId(wxWindow::NewControlId()),
163 kSummaryId(wxWindow::NewControlId()),
164 kSetBtnId(wxWindow::NewControlId()),
165 kClearBtnId(wxWindow::NewControlId()) {
166 auto flags = wxSizerFlags(0).Border();
167 auto vbox = new wxStaticBoxSizer(wxVERTICAL, this, _("Message Types"));
168 auto hbox = new wxBoxSizer(wxHORIZONTAL);
169
170 auto radiobox = new wxBoxSizer(wxVERTICAL);
171 radiobox->Add(new wxRadioButton(this, kIncludeBtnId, _("Include messages"),
172 wxDefaultPosition, wxDefaultSize,
173 wxRB_GROUP));
174 radiobox->Add(
175 new wxRadioButton(this, kExcludeBtnId, _("Exclude messages")));
176 radiobox->Add(1, 1, 1); // Expanding space, align buttons to bottom
177 radiobox->Add(new wxButton(this, kSetBtnId, _("Check all")), flags);
178 radiobox->Add(new wxButton(this, kClearBtnId, _("Clear all")), flags);
179 hbox->Add(radiobox, flags.Expand());
180
181 hbox->Add(1, 1, 1); // Expanding space, keep button/summary right aligned
182 wxArrayString choices;
183 auto msg_types = GetActiveMessages();
184 for (const auto& msg_type : msg_types) choices.Add(msg_type);
185 auto listbox = new wxCheckListBox(this, kListboxId, wxDefaultPosition,
186 wxDefaultSize, choices, 1);
187 hbox->Add(new wxStaticText(this, kSummaryId, ""), flags);
188 hbox->Add(listbox, flags);
189
190 auto edit_box = new wxBoxSizer(wxVERTICAL);
191 auto edit_btn = new EditButton(this, kEditBtnId, [&] { OnEditClick(); });
192 edit_box->Add(edit_btn, wxSizerFlags());
193 hbox->Add(edit_box, flags.Expand());
194
195 wxWindow* btn = GetWindowById<wxWindow>(kSetBtnId);
196 btn->Hide();
197 btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { SetAllItems(true); });
198 btn = GetWindowById<wxWindow>(kClearBtnId);
199 btn->Hide();
200 btn->Bind(wxEVT_BUTTON, [&](wxCommandEvent&) { SetAllItems(false); });
201
202 vbox->Add(hbox, flags.Expand());
203 SetSizer(vbox);
204 ImportFromFilter();
205 listbox->Hide();
206 GetWindowById<wxStaticText>(kSummaryId)->SetLabel(GetSummary());
207 Fit();
208
209 listbox->Bind(wxEVT_CHECKLISTBOX,
210 [&](wxCommandEvent& ev) { OnItemCheck(ev.GetInt()); });
211 }
212
213private:
214 std::string GetListboxItem(wxCheckListBox* listbox, unsigned i) const {
215 return listbox->GetString(i).ToStdString();
216 }
217
218 void SetAllItems(bool value) {
219 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
220 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
221 listbox->Check(i, value);
222 }
223 ExportToFilter();
224 }
225
226 std::string GetSummary() {
227 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
228
229 if (m_filter.exclude_msg.empty() && m_filter.include_msg.empty())
230 return _("All");
231 bool excluded = !m_filter.exclude_msg.empty();
232 size_t checked =
233 excluded ? m_filter.exclude_msg.size() : m_filter.include_msg.size();
234 size_t all = listbox->GetCount();
235 if (all == checked) return _("All");
236
237 std::stringstream ss;
238 ss << "[" << checked << _(" of ") << all << "]";
239 return ss.str();
240 }
241
246 void OnEditClick() {
247 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
248 auto summary = GetWindowById<wxStaticText>(kSummaryId);
249 auto edit_button = GetWindowById<EditButton>(kEditBtnId);
250 bool is_editing = listbox->IsShown();
251 listbox->Show(!is_editing);
252 summary->Show(is_editing);
253 summary->SetLabel(GetSummary());
254 edit_button->SetIcon(!is_editing);
255 GetWindowById<wxWindow>(kSetBtnId)->Show(!is_editing);
256 GetWindowById<wxWindow>(kClearBtnId)->Show(!is_editing);
257 GetParent()->Fit();
258 ExportToFilter();
259 }
260
265 void OnItemCheck(int ix) {
266 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
267 int checked = 0;
268 for (unsigned i = 0; i < listbox->GetCount(); i += 1)
269 if (listbox->IsChecked(i)) checked += 1;
270 if (checked == 0) {
271 // Refuse to create a filter with no interfaces.
272 listbox->Check(ix);
273 return;
274 }
275 ExportToFilter();
276 }
277
279 void ImportFromFilter() {
280 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
281 auto exclude_btn = GetWindowById<wxRadioButton>(kExcludeBtnId);
282 auto include_btn = GetWindowById<wxRadioButton>(kIncludeBtnId);
283
284 exclude_btn->SetValue(m_filter.exclude_msg.size() > 0);
285 if (include_btn->GetValue()) {
286 if (m_filter.include_msg.empty()) {
287 for (unsigned i = 0; i < listbox->GetCount(); i++) listbox->Check(i);
288 } else {
289 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
290 std::string item = GetListboxItem(listbox, i);
291 if (m_filter.include_msg.count(item)) listbox->Check(i);
292 }
293 }
294 } else {
295 if (m_filter.exclude_msg.empty()) {
296 for (unsigned i = 0; i < listbox->GetCount(); i++) listbox->Check(i);
297 } else {
298 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
299 std::string item = GetListboxItem(listbox, i);
300 if (m_filter.exclude_msg.count(item)) listbox->Check(i);
301 }
302 }
303 }
304 }
305
307 void ExportToFilter() {
308 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
309 auto include_btn = GetWindowById<wxRadioButton>(kIncludeBtnId);
310 m_filter.include_msg.clear();
311 m_filter.exclude_msg.clear();
312 auto& the_set =
313 include_btn->GetValue() ? m_filter.include_msg : m_filter.exclude_msg;
314 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
315 if (!listbox->IsChecked(i)) continue;
316 std::string item = GetListboxItem(listbox, i);
317 the_set.insert(item);
318 }
319 if (the_set.size() == listbox->GetCount()) {
320 m_filter.include_msg.clear();
321 m_filter.exclude_msg.clear();
322 }
323 m_on_update();
324 }
325
326 NavmsgFilter& m_filter;
327 std::function<void()> m_on_update;
328 const int kIncludeBtnId;
329 const int kExcludeBtnId;
330 const int kEditBtnId;
331 const int kListboxId;
332 const int kSummaryId;
333 const int kSetBtnId;
334 const int kClearBtnId;
335};
336
345template <typename T>
346class SetPanel : public wxPanel {
347public:
348 SetPanel(wxWindow* parent, std::set<T>& set, std::function<void()> on_update,
349 const std::string& label, std::function<wxArrayString()> get_choices)
350 : wxPanel(),
351 m_set(set),
352 m_on_update(std::move(on_update)),
353 kEditBtnId(wxWindow::NewControlId()),
354 kListboxId(wxWindow::NewControlId()),
355 kListLabelId(wxWindow::NewControlId()) {
356 wxPanel::Create(parent, wxID_ANY);
357 auto flags = wxSizerFlags(0).Border();
358 auto vbox = new wxBoxSizer(wxVERTICAL);
359 auto hbox = new wxBoxSizer(wxHORIZONTAL);
360 hbox->Add(new wxStaticText(this, wxID_ANY, label), flags);
361
362 // Pre-compute required size of wxCheckListBox
363 // to avoid sizer errors on MacOS
364 wxArrayString array = get_choices();
365 int max_char = 0;
366 for (auto string : array) max_char = wxMax(max_char, string.Length());
367 int hsize = (max_char + 8) * wxWindow::GetCharWidth();
368
369 hbox->Add(1, 1, 1);
370 auto listbox = new wxCheckListBox(this, kListboxId, wxDefaultPosition,
371 wxSize(hsize, -1), get_choices());
372 hbox->Add(listbox, flags);
373 auto list_label = new wxStaticText(this, kListLabelId, "");
374 hbox->Add(list_label, flags);
375 auto edit_btn = new EditButton(this, kEditBtnId, [&] { OnEditClick(); });
376 hbox->Add(edit_btn, flags);
377 vbox->Add(hbox, flags.Expand());
378 SetSizer(vbox);
379
380 ImportFromFilter();
381 list_label->SetLabel(GetListLabel());
382
383 listbox->Hide();
384 list_label->Show();
385
386 // Execute a round-trip of OnEditClick() to force MacOS sizers to populate
387 // the listbox.
388 OnEditClick();
389 Layout();
390 OnEditClick();
391
392 listbox->Bind(wxEVT_CHECKLISTBOX,
393 [&](wxCommandEvent& ev) { OnItemCheck(ev.GetInt()); });
394 }
395
396private:
398 T GetListboxItem(wxCheckListBox* listbox, unsigned i) const {
399 if constexpr (std::is_same<T, std::string>::value)
400 return listbox->GetString(i).ToStdString();
401 else if constexpr (std::is_same<T, NavAddr::Bus>::value)
402 return NavAddr::StringToBus(listbox->GetString(i).ToStdString());
403 else if constexpr (std::is_same<T, NavmsgStatus::Direction>::value)
404 return StringToDirection(listbox->GetString(i).ToStdString());
405 else if constexpr (std::is_same<T, NavmsgStatus::Accepted>::value)
407 listbox->GetString(i).ToStdString());
408 else
409 assert(false && "bad type...");
410 }
411
413 std::string GetListLabel() {
414 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
415 if (m_set.empty() || m_set.size() == listbox->GetCount()) return _("All");
416 std::stringstream ss;
417 ss << "[" << m_set.size() << _(" of ") << listbox->GetCount() << "]";
418 return ss.str();
419 }
420
425 void OnEditClick() {
426 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
427 listbox->Layout();
428 auto list_label = GetWindowById<wxStaticText>(kListLabelId);
429 auto edit_button = GetWindowById<EditButton>(kEditBtnId);
430 bool is_editing = listbox->IsShown();
431 listbox->Show(!is_editing);
432 list_label->Show(is_editing);
433 list_label->SetLabel(GetListLabel());
434 edit_button->SetIcon(!is_editing);
435 GetParent()->Fit();
436 }
437
442 void OnItemCheck(int ix) {
443 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
444 auto list_label = GetWindowById<wxStaticText>(kListLabelId);
445 int checked = 0;
446 for (unsigned i = 0; i < listbox->GetCount(); i += 1)
447 if (listbox->IsChecked(i)) checked += 1;
448 if (checked == 0) {
449 listbox->Check(ix);
450 return;
451 }
452 list_label->SetLabel(GetListLabel());
453 ExportToFilter();
454 }
455
457 void ImportFromFilter() {
458 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
459 if (m_set.empty()) {
460 for (unsigned i = 0; i < listbox->GetCount(); i++) listbox->Check(i);
461 } else {
462 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
463 T item = GetListboxItem(listbox, i);
464 if (m_set.count(item) > 0) listbox->Check(i);
465 }
466 }
467 }
468
470 void ExportToFilter() {
471 auto listbox = GetWindowById<wxCheckListBox>(kListboxId);
472 m_set.clear();
473 for (unsigned i = 0; i < listbox->GetCount(); i += 1) {
474 if (!listbox->IsChecked(i)) continue;
475 T item = GetListboxItem(listbox, i);
476 m_set.insert(item);
477 }
478 if (m_set.size() == listbox->GetCount()) m_set.clear();
479 m_on_update();
480 }
481
482 std::set<T>& m_set;
483 std::function<void()> m_on_update;
484 const int kEditBtnId;
485 const int kListboxId;
486 const int kListLabelId;
487};
488
490class IfacePanel : public SetPanel<std::string> {
491public:
492 IfacePanel(wxWindow* parent, NavmsgFilter& filter,
493 std::function<void()> on_update)
494 : SetPanel<std::string>(parent, filter.interfaces, std::move(on_update),
495 _("Use interfaces"),
496 [&]() { return IfacePanel::GetChoices(); }) {}
497
498private:
499 wxArrayString GetChoices() const {
500 wxArrayString choices;
501 for (auto& driver : CommDriverRegistry::GetInstance().GetDrivers())
502 choices.Add(driver->iface);
503 choices.Add("Internal");
504 return choices;
505 }
506};
507
512class BusPanel : public SetPanel<NavAddr::Bus> {
513public:
514 BusPanel(wxWindow* parent, NavmsgFilter& filter,
515 std::function<void()> on_update)
516 : SetPanel<NavAddr::Bus>(parent, filter.buses, std::move(on_update),
517 _("Use message buses"),
518 [&]() { return BusPanel::GetChoices(); }) {}
519
520private:
521 wxArrayString GetChoices() const {
522 static const char* choices[] = {"nmea0183", "nmea2000", "SignalK", "Onenet",
523 "Plugin"};
524 return {5, choices};
525 }
526};
527
532class DirectionPanel : public SetPanel<NavmsgStatus::Direction> {
533public:
534 DirectionPanel(wxWindow* parent, NavmsgFilter& filter,
535 std::function<void()> on_update)
537 parent, filter.directions, std::move(on_update),
538 _("Use directions"), [&] { return DirectionPanel::GetChoices(); }) {
539 }
540
541private:
542 wxArrayString GetChoices() const {
543 static const char* choices[] = {"Input", "Handled", "Output", "Internal"};
544 return {4, choices};
545 }
546};
547
548class AcceptedPanel : public SetPanel<NavmsgStatus::Accepted> {
549public:
550 AcceptedPanel(wxWindow* parent, NavmsgFilter& filter,
551 std::function<void()> on_update)
553 parent, filter.accepted, std::move(on_update),
554 _("Use Accepted states"),
555 [&]() { return AcceptedPanel::GetChoices(); }) {}
556
557private:
558 wxArrayString GetChoices() const {
559 static const char* choices[] = {"Ok", "FilteredNoOutput",
560 "FilteredDropped"};
561 return {3, choices};
562 }
563};
564
565class EditFilterFrame : public wxFrame {
566 class Buttons : public wxPanel {
567 public:
568 Buttons(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
569 auto vbox = new wxBoxSizer(wxVERTICAL);
570 auto buttons = new wxStdDialogButtonSizer();
571 buttons->AddButton(new wxButton(this, wxID_CLOSE));
572 buttons->AddButton(new wxButton(this, wxID_APPLY));
573 vbox->Add(buttons, wxSizerFlags().Expand());
574 buttons->Realize();
575 SetSizer(vbox);
576 Layout();
577 }
578 };
579
580public:
581 EditFilterFrame(wxWindow* parent, const std::string& name,
582 std::function<void(const std::string&)> on_update,
583 std::function<void(const std::string&)> on_apply)
584 : wxFrame(parent, wxID_ANY,
585 [name] { return _("Edit filter: ") + name; }()),
586 m_on_update(std::move(on_update)),
587 m_on_apply(std::move(on_apply)),
588 m_name(name) {
589 SetName(kEditFilterFrameName + name);
590 m_filter = filters_on_disk::Read(m_name);
591
592 auto flags = wxSizerFlags().Border().Expand();
593 auto vbox = new wxBoxSizer(wxVERTICAL);
594 SetSizer(vbox);
595 vbox->Add(new IfacePanel(this, m_filter, [&] { Update(); }), flags);
596 vbox->Add(new wxStaticLine(this), flags.Expand());
597 auto buspanel = new BusPanel(this, m_filter, [&] { Update(); });
598 vbox->Add(buspanel, flags);
599 vbox->Add(new wxStaticLine(this), flags.Expand());
600 vbox->Add(new DirectionPanel(this, m_filter, [&] { Update(); }), flags);
601 vbox->Add(new wxStaticLine(this), flags.Expand());
602 vbox->Add(new AcceptedPanel(this, m_filter, [&] { Update(); }), flags);
603 vbox->Add(new MsgTypePanel(this, m_filter, [&] { Update(); }), flags);
604 vbox->Add(new Buttons(this), flags);
605 Fit();
606 Hide();
607
608 Bind(wxEVT_CLOSE_WINDOW, [&](wxCloseEvent&) { Destroy(); });
609 Bind(wxEVT_BUTTON, [&](wxCommandEvent& evt) { OnButtonEvent(evt); });
610 }
611
612private:
613 void Update() {
614 filters_on_disk::Write(m_filter, m_name);
615 m_on_update(m_name);
616 }
617
618 void OnButtonEvent(wxCommandEvent& evt) {
619 if (evt.GetId() == wxID_CLOSE) {
620 Destroy();
621 evt.Skip();
622 } else if (evt.GetId() == wxID_APPLY) {
623 m_on_apply(m_name);
624 evt.Skip();
625 }
626 }
627
628 std::function<void(const std::string&)> m_on_update;
629 std::function<void(const std::string&)> m_on_apply;
630 std::string m_name;
631 NavmsgFilter m_filter;
632};
633
634void CreateFilterDlg(wxWindow* parent) {
635 NewFilterDlg dlg(parent);
636 dlg.ShowModal();
637 auto name = dlg.GetValue().ToStdString();
638 if (name.empty()) {
639 wxMessageDialog msg_dlg(wxTheApp->GetTopWindow(), _("Illegal name"));
640 msg_dlg.ShowModal();
641 return;
642 }
643 if (filters_on_disk::Exists(name)) {
644 BadFilterNameDlg(wxTheApp->GetTopWindow()).ShowModal();
645 return;
646 }
647 NavmsgFilter filter;
648 filter.m_name = name;
649 filters_on_disk::Write(filter, name);
650 FilterEvents::GetInstance().filter_list_change.Notify();
651}
652
653void RemoveFilterDlg(wxWindow* parent) {
654 if (GetUserFilters().empty()) {
655 wxMessageDialog dlg(wxTheApp->GetTopWindow(), _("No filters created"));
656 dlg.ShowModal();
657 return;
658 }
659 DeleteFilterDlg dlg(parent);
660 int sts = dlg.ShowModal();
661 if (sts != wxID_OK) return;
662
663 fs::path path(dlg.GetStringSelection().ToStdString());
664 if (filters_on_disk::Remove(path.stem().string())) {
665 FilterEvents::GetInstance().filter_list_change.Notify();
666 wxMessageDialog msg_dlg(wxTheApp->GetTopWindow(), _("Filter removed"));
667 msg_dlg.ShowModal();
668 } else {
669 wxMessageDialog msg_dlg(wxTheApp->GetTopWindow(),
670 _("Cannot remove filter"));
671 msg_dlg.ShowModal();
672 }
673}
674
675void RenameFilterDlg(wxWindow* parent) {
676 if (GetUserFilters().empty()) {
677 wxMessageDialog dlg(wxTheApp->GetTopWindow(), _("No filters to rename"));
678 dlg.ShowModal();
679 return;
680 }
681 RenameFilterChoiceDlg dlg(parent);
682 int sts = dlg.ShowModal();
683 if (sts != wxID_OK) return;
684
685 fs::path old_name(dlg.GetStringSelection().ToStdString());
686 wxString caption = wxString(_("Renaming ")) + old_name.string();
687 auto new_name_dlg = new wxTextEntryDialog(parent, _("New name:"), caption);
688 int result = new_name_dlg->ShowModal();
689 if (result != wxID_OK) return;
690 filters_on_disk::Rename(old_name.string(),
691 new_name_dlg->GetValue().ToStdString());
692}
693
694void EditOneFilterDlg(wxWindow* parent, const std::string& filter) {
695 std::string window_name = kEditFilterFrameName + filter;
696 wxWindow* frame = wxWindow::FindWindowByName(window_name);
697 if (frame) {
698 frame->Raise();
699 return;
700 }
701 auto on_update = [](const std::string& _name) {
702 FilterEvents::GetInstance().filter_update.Notify(_name);
703 };
704 auto on_apply = [](const std::string& _name) {
705 FilterEvents::GetInstance().filter_apply.Notify(_name);
706 };
707 new EditFilterFrame(parent, filter, on_update, on_apply);
708 frame = wxWindow::FindWindowByName(window_name);
709 assert(frame && "Cannot create EditFilter frame");
710 frame->Show();
711}
712
713void EditFilterDlg(wxWindow* parent) {
714 if (GetUserFilters().empty()) {
715 wxMessageDialog dlg(wxTheApp->GetTopWindow(), _("No filters created"));
716 dlg.ShowModal();
717 return;
718 }
719 SelectFilterDlg dlg(parent);
720 int sts = dlg.ShowModal();
721 if (sts != wxID_OK) return;
722
723 EditOneFilterDlg(parent, dlg.GetStringSelection().ToStdString());
724};
The bus panel, filter w r t message types (nmea2000, nmea1083, etc.).
const std::vector< DriverPtr > & GetDrivers() const
Direction panel, set up filter w r t direction (input, output, etc..) Bound to filter....
Two state button showing either an edit.
Definition edit_button.h:36
obs::EventVar filter_apply
Notified with filter name when applied by user.
Definition filter_dlg.h:49
obs::EventVar filter_list_change
Notified without data when user creates or removes a filter.
Definition filter_dlg.h:43
obs::EventVar filter_update
Notified with filter name when filter is updated on disk.
Definition filter_dlg.h:46
Interfaces filtering panel, bound to filter.interfaces.
Message type filter setup GUI bound to filter.include_msg and filter.exclude_msg.
The raw message layer, a singleton.
const std::set< std::string > & GetActiveMessages()
Return list of message types sent or received.
static Accepted StringToAccepted(const std::string &s)
Return Accepted value corresponding to argument s.
Common base for displaying a filter set bound to the set ctor parameter.
void Notify() override
Notify all listeners, no data supplied.
Definition evtvar.h:83
Driver registration container, a singleton.
Raw messages layer, supports sending and recieving navmsg messages.
T * GetWindowById(int id)
Return window with given id (which must exist) cast to T*.
Dialogs handing user defined filters.
std::vector< std::string > List(bool include_system)
Return list of filters, possibly including also the system ones.
bool Rename(const std::string &old_name, const std::string &new_name)
Rename old_name on disk to new.
bool Write(const NavmsgFilter &filter, const std::string &name)
Write contents for given filter to disk.
Data Monitor filter storage routines.