OpenCPN Partial API docs
Loading...
Searching...
No Matches
connections_dlg.cpp
1#include <array>
2#include <algorithm>
3#include <sstream>
4#include <string>
5#include <vector>
6
7#include <wx/arrstr.h>
8#include <wx/bitmap.h>
9#include <wx/grid.h>
10#include <wx/panel.h>
11#include <wx/sizer.h>
12#include <wx/spinctrl.h>
13#include <wx/textctrl.h>
14#include <wx/window.h>
15
16#include "model/base_platform.h"
17#include "model/comm_drv_factory.h"
19#include "model/comm_util.h"
20#include "model/config_vars.h"
21#include "model/conn_params.h"
22#include "model/conn_states.h"
23
24#include "connections_dlg.h"
25
26#include "color_handler.h"
27#include "connection_edit.h"
28#include "conn_params_panel.h"
29#include "gui_lib.h"
30#include "navutil.h"
31#include "priority_gui.h"
32#include "std_filesystem.h"
33
34#ifdef __ANDROID__
35#include "androidUTIL.h"
36#endif
37
38static const auto kUtfArrowDown = wxString::FromUTF8(u8"\u25bc");
39static const auto kUtfArrowRight = wxString::FromUTF8(u8"\u25ba");
40static const auto kUtfCheckmark = wxString::FromUTF8(u8"\u2713");
41static const auto kUtfCircle = wxString::FromUTF8(u8"\u3007");
42static const auto kUtfCrossmark = wxString::FromUTF8(u8"\u274c");
43static const auto kUtfDegrees = wxString::FromUTF8(u8"\u00B0");
44static const auto kUtfExclamationMark = wxString::FromUTF8("!");
45static const auto kUtfFilledCircle = wxString::FromUTF8(u8"\u2b24");
46static const auto kUtfFisheye = wxString::FromUTF8(u8"\u25c9");
47static const auto kUtfGear = wxString::FromUTF8(u8"\u2699");
48static const auto kUtfMultiplyX = wxString::FromUTF8(u8"\u2715");
49static const auto kUtfTrashbin = wxString::FromUTF8(u8"\U0001f5d1");
50
51static inline bool IsWindows() {
52 return wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS;
53}
54
57public:
58 ConnCompare(int col) : m_col(col) {}
59
60 bool operator()(ConnectionParams* p1, ConnectionParams* p2) {
61 switch (m_col) {
62 case 0:
63 return int(p1->bEnabled) > int(p2->bEnabled);
64 case 1:
65 return p1->GetCommProtocol() < p2->GetCommProtocol();
66 case 2:
67 return p1->GetIOTypeValueStr() < p2->GetIOTypeValueStr();
68 case 3:
69 return p1->GetStrippedDSPort() < p2->GetStrippedDSPort();
70 default:
71 return false;
72 }
73 }
74
75private:
76 const int m_col;
77};
78
83public:
85 virtual void Apply() = 0;
86
90 virtual void Cancel() = 0;
91};
92
94class AddConnectionButton : public wxButton {
95public:
96 AddConnectionButton(wxWindow* parent, EventVar& evt_add_connection)
97 : wxButton(parent, wxID_ANY, _("Add new connection...")),
98 m_evt_add_connection(evt_add_connection) {
99 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
100 [&](wxCommandEvent& ev) { OnAddConnection(); });
101 }
102
103private:
104 void OnAddConnection() {
105 ConnectionEditDialog dialog(this);
106 dialog.SetPropsLabel(_("Configure new connection"));
107 dialog.SetDefaultConnectionParams();
108 wxWindow* options = wxWindow::FindWindowByName("Options");
109 assert(options && "Null Options window!");
110 dialog.SetSize(wxSize(options->GetSize().x, options->GetSize().y * 8 / 10));
111 auto rv = dialog.ShowModal();
112 if (rv == wxID_OK) {
113 ConnectionParams* cp = dialog.GetParamsFromControls();
114 if (cp) {
115 cp->b_IsSetup = false; // Trigger new stream
116 TheConnectionParams().push_back(cp);
117 }
118 UpdateDatastreams();
119 m_evt_add_connection.Notify();
120 }
121#ifdef __ANDROID__
122 androidEnableRotation();
123#endif
124 }
125
126 EventVar& m_evt_add_connection;
127};
128
130class ScrolledWindow : public wxScrolledWindow {
131public:
132 ScrolledWindow(wxWindow* parent)
133 : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
134 wxVSCROLL) {}
135
137 void AddClient(wxWindow* client, wxSize max_size, wxSize min_size) {
138 auto vbox = new wxBoxSizer(wxVERTICAL);
139 vbox->Add(client, wxSizerFlags().Border());
140 SetSizer(vbox);
141 SetMinClientSize(min_size);
142 SetMaxSize(max_size);
143 vbox->Layout();
144 SetScrollRate(0, 10);
145 }
146};
147
149class Connections : public wxGrid {
150public:
151 Connections(wxWindow* parent,
152 const std::vector<ConnectionParams*>& connections,
153 EventVar& on_conn_update)
154 : wxGrid(parent, wxID_ANY),
155 m_connections(connections),
156 m_on_conn_delete(on_conn_update),
157 m_last_tooltip_cell(100) {
158 SetTable(new wxGridStringTable(), false);
159 GetTable()->AppendCols(8);
160 HideCol(7);
161 static const std::array<wxString, 7> headers = {
162 "", _("Protocol"), _("In/Out"), _("Data port"), _("Status"), "", ""};
163 for (auto hdr = headers.begin(); hdr != headers.end(); hdr++)
164 SetColLabelValue(static_cast<int>(hdr - headers.begin()), *hdr);
165 if (IsWindows()) {
166 SetLabelBackgroundColour(GetGlobalColor("DILG1"));
167 SetLabelTextColour(GetGlobalColor("DILG3"));
168 }
169 HideRowLabels();
170 SetColAttributes(parent);
171 ReloadGrid(connections);
172 DisableDragColSize();
173 DisableDragRowSize();
174 wxWindow* options = wxWindow::FindWindowByName("Options");
175 assert(options && "Null Options window!");
176 SetSize(wxSize(options->GetSize().x, options->GetSize().y * 8 / 10));
177 wxWindow::Show(GetNumberRows() > 0);
178
179 GetGridWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& ev) {
180 OnMouseMove(ev);
181 ev.Skip();
182 });
183
184 GetGridWindow()->Bind(wxEVT_MOUSEWHEEL, [&](wxMouseEvent& ev) {
185 OnWheel(ev);
186 ev.Skip();
187 });
188
189 Bind(wxEVT_GRID_LABEL_LEFT_CLICK,
190 [&](wxGridEvent& ev) { HandleSort(ev.GetCol()); });
191 Bind(wxEVT_GRID_CELL_LEFT_CLICK,
192 [&](wxGridEvent& ev) { OnClickCell(ev.GetRow(), ev.GetCol()); });
193 Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) {
194 SetColAttributes(static_cast<wxWindow*>(ev.GetEventObject()));
195 ev.Skip();
196 });
197 conn_change_lstnr.Init(
198 m_conn_states.evt_conn_status_change,
199 [&](ObservedEvt&) { OnConnectionChange(m_connections); });
200 }
201
202 void OnWheel(wxMouseEvent& ev) {
203 auto p = GetParent();
204 auto psw = static_cast<ScrolledWindow*>(p);
205 int dir = ev.GetWheelRotation();
206 int xpos, ypos;
207 psw->GetViewStart(&xpos, &ypos);
208 int xsu, ysu;
209 psw->GetScrollPixelsPerUnit(&xsu, &ysu);
210 // Not sure where the factor "4" comes from...
211 psw->Scroll(-1, ypos - (dir / ysu) / 4);
212 }
213
215 void ReloadGrid(const std::vector<ConnectionParams*>& connections) {
216 ClearGrid();
217 for (auto it = connections.begin(); it != connections.end(); it++) {
218 auto row = static_cast<int>(it - connections.begin());
219 EnsureRows(row);
220 SetCellValue(row, 0, (*it)->bEnabled ? "1" : "");
221 if ((*it)->bEnabled)
222 m_tooltips[row][0] = _("Enabled, click to disable");
223 else
224 m_tooltips[row][0] = _("Disabled, click to enable");
225 std::string protocol = NavAddr::BusToString((*it)->GetCommProtocol());
226 SetCellValue(row, 1, protocol);
227 SetCellValue(row, 2, (*it)->GetIOTypeValueStr());
228 SetCellValue(row, 3, (*it)->GetStrippedDSPort());
229 m_tooltips[row][3] = (*it)->UserComment;
230 SetCellValue(row, 5, kUtfGear); // ⚙
231 m_tooltips[row][5] = _("Edit connection");
232 SetCellValue(row, 6, kUtfTrashbin); // 🗑
233 m_tooltips[row][6] = _("Delete connection");
234 SetCellValue(row, 7, (*it)->GetKey());
235 }
236 OnConnectionChange(m_connections);
237 AutoSize();
238 }
239
240 wxSize GetGridMaxSize() const {
241 return wxSize(GetCharWidth() * 120,
242 std::min(GetNumberRows() + 3, 10) * 2 * GetCharHeight());
243 }
244
245 wxSize GetGridMinSize() const {
246 return wxSize(GetCharWidth() * 80,
247 std::min(GetNumberRows() + 3, 6) * 2 * GetCharHeight());
248 }
249
252 public:
253 ConnStateCompare(Connections* connections) : m_conns(connections) {}
254 bool operator()(ConnectionParams* p1, ConnectionParams* p2) {
255 int row1 = m_conns->FindConnectionIndex(p1);
256 int row2 = m_conns->FindConnectionIndex(p2);
257 if (row1 == -1 && row2 == -1) return false;
258 if (row1 == -1) return false;
259 if (row2 == -1) return true;
260 int v1 = static_cast<int>(m_conns->GetCellValue(row1, 4)[0]);
261 int v2 = static_cast<int>(m_conns->GetCellValue(row2, 4)[0]);
262 return v1 < v2;
263 }
264 Connections* m_conns;
265 };
266
267private:
272 ConnectionParams* FindRowConnection(int row) {
273 auto found = find_if(m_connections.begin(), m_connections.end(),
274 [&](ConnectionParams* p) {
275 return GetCellValue(row, 7) == p->GetKey();
276 });
277 return found != m_connections.end() ? *found : nullptr;
278 }
279
284 int FindConnectionIndex(ConnectionParams* cp) {
285 using namespace std;
286 auto key = cp->GetKey();
287 auto found =
288 find_if(m_connections.begin(), m_connections.end(),
289 [key](ConnectionParams* cp) { return cp->GetKey() == key; });
290 if (found == m_connections.end()) return -1;
291 return static_cast<int>(found - m_connections.begin());
292 }
293
298 void EnsureRows(size_t rows) {
299 while (m_tooltips.size() <= rows)
300 m_tooltips.push_back(std::vector<std::string>(7));
301 while (GetNumberRows() <= static_cast<int>(rows)) AppendRows(1, false);
302 }
303
305 void SetColAttributes(wxWindow* parent) {
306 if (IsWindows()) {
307 // Set all cells to global color scheme
308 SetDefaultCellBackgroundColour(GetGlobalColor("DILG1"));
309 }
310 auto enable_attr = new wxGridCellAttr();
311 enable_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
312 enable_attr->SetRenderer(new wxGridCellBoolRenderer());
313 enable_attr->SetEditor(new wxGridCellBoolEditor());
314 if (IsWindows()) enable_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
315 SetColAttr(0, enable_attr);
316
317 auto protocol_attr = new wxGridCellAttr();
318 protocol_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
319 protocol_attr->SetReadOnly(true);
320 if (IsWindows())
321 protocol_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
322 SetColAttr(1, protocol_attr);
323
324 auto in_out_attr = new wxGridCellAttr();
325 in_out_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
326 in_out_attr->SetReadOnly(true);
327 if (IsWindows()) in_out_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
328 SetColAttr(2, in_out_attr);
329
330 auto port_attr = new wxGridCellAttr();
331 port_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
332 port_attr->SetReadOnly(true);
333 if (IsWindows()) port_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
334 SetColAttr(3, port_attr);
335
336 auto status_attr = new wxGridCellAttr();
337 status_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
338 status_attr->SetReadOnly(true);
339 status_attr->SetFont(parent->GetFont().Scale(1.3));
340 if (IsWindows()) status_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
341 SetColAttr(4, status_attr);
342
343 auto edit_attr = new wxGridCellAttr();
344 edit_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
345 edit_attr->SetFont(parent->GetFont().Scale(1.3));
346 edit_attr->SetReadOnly(true);
347 if (IsWindows()) edit_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
348 SetColAttr(5, edit_attr);
349
350 auto delete_attr = new wxGridCellAttr();
351 delete_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
352 delete_attr->SetFont(parent->GetFont().Scale(1.3));
353 delete_attr->SetTextColour(*wxRED);
354 delete_attr->SetReadOnly(true);
355 if (IsWindows()) delete_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
356 SetColAttr(6, delete_attr);
357 }
358
360 void OnClickCell(int row, int col) {
361 if (col == 0)
362 HandleEnable(row);
363 else if (col == 5) {
364 HandleEdit(row);
365 } else if (col == 6) {
366 HandleDelete(row);
367 }
368 }
370 void OnMouseMove(wxMouseEvent& ev) {
371 wxPoint pt = ev.GetPosition();
372 int row = YToRow(pt.y);
373 int col = XToCol(pt.x);
374 if (col < 0 || col >= 7 || row < 0 || row >= GetNumberRows()) return;
375 if (row * 7 + col == m_last_tooltip_cell) return;
376 m_last_tooltip_cell = row * 7 + col;
377 GetGridWindow()->SetToolTip(m_tooltips[row][col]);
378 }
379
381 void OnConnectionChange(const std::vector<ConnectionParams*>& connections) {
382 for (auto it = connections.begin(); it != connections.end(); it++) {
383 ConnState state = m_conn_states.GetDriverState(
384 (*it)->GetCommProtocol(), (*it)->GetStrippedDSPort());
385 if (!(*it)->bEnabled) state = ConnState::Disabled;
386 auto row = static_cast<int>(it - connections.begin());
387 EnsureRows(row);
388 switch (state) {
389 case ConnState::Disabled:
390 SetCellValue(row, 4, kUtfFilledCircle);
391 m_tooltips[row][4] = _("Disabled");
392 break;
393 case ConnState::NoStats:
394 SetCellValue(row, 4, kUtfCircle);
395 m_tooltips[row][4] = _("No driver statistics available");
396 break;
397 case ConnState::NoData:
398 SetCellValue(row, 4, kUtfExclamationMark);
399 m_tooltips[row][4] = _("No data flowing through connection");
400 break;
401 case ConnState::Unavailable:
402 SetCellValue(row, 4, kUtfMultiplyX);
403 m_tooltips[row][4] = _("The device is unavailable");
404 break;
405 case ConnState::Ok:
406 SetCellValue(row, 4, kUtfCheckmark);
407 m_tooltips[row][4] = _("Data is flowing");
408 break;
409 }
410 }
411 }
412
413 void SetSortingColumn(int col) {
414 if (GetSortingColumn() != wxNOT_FOUND) {
415 int old_col = GetSortingColumn();
416 auto label = GetColLabelValue(old_col);
417 if (label[0] == kUtfArrowDown[0])
418 SetColLabelValue(old_col, label.substr(2));
419 }
420 auto label = GetColLabelValue(col);
421 if (label[0] != kUtfArrowDown[0])
422 SetColLabelValue(col, kUtfArrowDown + " " + label);
423 wxGrid::SetSortingColumn(col);
424 Fit();
425 }
426
428 void HandleSort(int col) {
429 if (col > 4) return;
430 auto& params = TheConnectionParams();
431 if (col < 4)
432 std::sort(params.begin(), params.end(), ConnCompare(col));
433 else // col == 4
434 std::sort(params.begin(), params.end(), ConnStateCompare(this));
435 ReloadGrid(TheConnectionParams());
436 SetSortingColumn(col);
437 }
438
440 void HandleEnable(int row) {
441 ConnectionParams* cp = FindRowConnection(row);
442 if (!cp) return;
443 cp->bEnabled = !cp->bEnabled;
444 cp->b_IsSetup = FALSE; // trigger a rebuild/takedown of the connection
445 SetCellValue(row, 0, cp->bEnabled ? "1" : "");
446 if (cp->bEnabled)
447 m_tooltips[row][0] = _("Enabled, click to disable");
448 else
449 m_tooltips[row][0] = _("Disabled, click to enable");
450 DriverStats stats;
451 stats.driver_iface = cp->GetStrippedDSPort();
452 stats.driver_bus = cp->GetCommProtocol();
453 m_conn_states.HandleDriverStats(stats);
454 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
455 if (cp->bEnabled) MakeCommDriver(cp);
456 cp->b_IsSetup = true;
457 if (!cp->bEnabled) SetCellValue(row, 4, kUtfFilledCircle);
458 }
459
461 void HandleEdit(int row) {
462 ConnectionParams* cp = FindRowConnection(row);
463 if (cp) {
464 ConnectionEditDialog dialog(this);
465 DimeControl(&dialog);
466 dialog.SetPropsLabel(_("Edit Selected Connection"));
467 dialog.PreloadControls(cp);
468 wxWindow* options = wxWindow::FindWindowByName("Options");
469 assert(options && "Null Options window!");
470 dialog.SetSize(
471 wxSize(options->GetSize().x, options->GetSize().y * 8 / 10));
472 Show(GetNumberRows() > 0);
473
474 auto rv = dialog.ShowModal();
475 if (rv == wxID_OK) {
476 ConnectionParams* cp_edited = dialog.GetParamsFromControls();
477 delete cp->m_optionsPanel;
478 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
479 int index = FindConnectionIndex(cp);
480 assert(index != -1 && "Cannot look up connection index");
481 TheConnectionParams()[index] = cp_edited;
482 cp_edited->b_IsSetup = false; // Trigger new stream
483 ReloadGrid(m_connections);
484 UpdateDatastreams();
485 }
486 }
487 }
488
490 void HandleDelete(int row) {
491 ConnectionParams* cp = FindRowConnection(row);
492 auto found = std::find(m_connections.begin(), m_connections.end(), cp);
493 if (found != m_connections.end()) {
494 std::stringstream ss;
495 ss << _("Ok to delete connection on ") << (*found)->GetStrippedDSPort();
496 int rcode = OCPNMessageBox(this, ss.str(), _("Delete connection?"),
497 wxOK | wxCANCEL);
498 if (rcode != wxID_OK && rcode != wxID_YES) return;
499 delete (*found)->m_optionsPanel;
500 StopAndRemoveCommDriver((*found)->GetStrippedDSPort(),
501 (*found)->GetCommProtocol());
502 TheConnectionParams().erase(found);
503 if (GetNumberRows() > static_cast<int>(m_connections.size()))
504 DeleteRows(GetNumberRows() - 1);
505 m_on_conn_delete.Notify();
506 }
507 }
508
509 ObsListener conn_change_lstnr;
510 std::vector<std::vector<std::string>> m_tooltips;
511 ConnStates m_conn_states;
512 const std::vector<ConnectionParams*>& m_connections;
513 EventVar& m_on_conn_delete;
514 int m_last_tooltip_cell;
515};
516
518class GeneralPanel : public wxPanel {
519public:
520 explicit GeneralPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
521 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("General"));
522 SetSizer(sizer);
523 auto flags = wxSizerFlags().Border();
524 auto hbox = new wxBoxSizer(wxHORIZONTAL);
525 hbox->Add(new wxStaticText(this, wxID_ANY, _("Upload format:")), flags);
526 hbox->Add(new UploadOptionsChoice(this), flags);
527 sizer->Add(hbox, flags);
528 }
529
530private:
532 class UploadOptionsChoice : public wxChoice, public ApplyCancel {
533 public:
534 explicit UploadOptionsChoice(wxWindow* parent) : wxChoice() {
535 wxArrayString wx_choices;
536 for (auto& c : choices) wx_choices.Add(c);
537 Create(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wx_choices);
538 Cancel();
539 }
540
541 void Cancel() {
542 if (g_bGarminHostUpload)
543 SetSelection(1);
544 else if (g_GPS_Ident == "FurunoGP3X")
545 SetSelection(2);
546 else
547 SetSelection(0);
548 }
549
550 void Apply() {
551 switch (GetSelection()) {
552 case 0:
553 g_bGarminHostUpload = false;
554 g_GPS_Ident = "Generic";
555 break;
556 case 1:
557 g_bGarminHostUpload = true;
558 g_GPS_Ident = "Generic";
559 break;
560 case 2:
561 g_bGarminHostUpload = false;
562 g_GPS_Ident = "FurunoGP3X";
563 break;
564 }
565 }
566
567 const std::array<wxString, 3> choices = {
568 _("Generic"), _("Use Garmin GRMN (Host) mode for uploads"),
569 _("Format uploads for Furuno GP4X")};
570 };
571};
572
574class ShowAdvanced : public wxStaticText {
575public:
576 ShowAdvanced(wxWindow* parent, std::function<void(bool)> on_toggle)
577 : wxStaticText(parent, wxID_ANY, ""),
578 m_on_toggle(on_toggle),
579 m_show_advanced(true) {
580 Toggle();
581 Bind(wxEVT_LEFT_DOWN, [&](wxMouseEvent& ev) { Toggle(); });
582 }
583
584private:
585 wxString m_label = _("Advanced ");
586 std::function<void(bool)> m_on_toggle;
587 bool m_show_advanced;
588
589 void Toggle() {
590 m_show_advanced = !m_show_advanced;
591 m_label = _("Advanced ");
592 m_label += (m_show_advanced ? kUtfArrowDown : kUtfArrowRight);
593 SetLabelText(m_label);
594 m_on_toggle(m_show_advanced);
595 }
596};
597
599class AdvancedPanel : public wxPanel {
600public:
601 explicit AdvancedPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
602 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, "");
603 sizer->Add(new BearingsCheckbox(this), wxSizerFlags().Expand());
604 sizer->Add(new NmeaFilterRow(this), wxSizerFlags().Expand());
605 sizer->Add(new TalkerIdRow(this), wxSizerFlags().Expand());
606 sizer->Add(new NetmaskRow(this), wxSizerFlags().Expand());
607 sizer->Add(new PrioritiesBtn(this), wxSizerFlags().Border());
608 SetSizer(sizer);
609 }
610
611private:
613 class BearingsCheckbox : public wxCheckBox, public ApplyCancel {
614 public:
615 BearingsCheckbox(wxWindow* parent)
616 : wxCheckBox(parent, wxID_ANY,
617 _("Use magnetic bearing in output sentence APB")) {
618 SetValue(g_bMagneticAPB);
619 wxCheckBox::SetValue(g_bMagneticAPB);
620 }
621
622 void Apply() override { g_bMagneticAPB = GetValue(); }
623 void Cancel() override { SetValue(g_bMagneticAPB); }
624 };
625
627 class NmeaFilterRow : public wxPanel, public ApplyCancel {
628 wxCheckBox* checkbox;
629 wxTextCtrl* filter_period;
630
631 public:
632 NmeaFilterRow(wxWindow* parent) : wxPanel(parent) {
633 auto hbox = new wxBoxSizer(wxHORIZONTAL);
634 checkbox = new wxCheckBox(
635 this, wxID_ANY,
636 _("Filter NMEA course and speed data. Filter period: "));
637 checkbox->SetValue(g_bfilter_cogsog);
638 hbox->Add(checkbox, wxSizerFlags().Align(wxALIGN_CENTRE));
639 filter_period =
640 new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition,
641 wxSize(50, 3 * wxWindow::GetCharWidth()), 0);
642 filter_period->SetValue(std::to_string(g_COGFilterSec));
643 hbox->Add(filter_period, wxSizerFlags().Border());
644 SetSizer(hbox);
645 Cancel();
646 }
647
648 void Apply() override {
649 std::stringstream ss;
650 ss << filter_period->GetValue();
651 ss >> g_COGFilterSec;
652 g_bfilter_cogsog = checkbox->GetValue();
653 }
654
655 void Cancel() override {
656 std::stringstream ss;
657 ss << g_COGFilterSec;
658 filter_period->SetValue(ss.str());
659 checkbox->SetValue(g_bfilter_cogsog);
660 }
661 };
662
664 class TalkerIdRow : public wxPanel, public ApplyCancel {
665 wxTextCtrl* text_ctrl;
666
667 public:
668 TalkerIdRow(wxWindow* parent) : wxPanel(parent) {
669 auto hbox = new wxBoxSizer(wxHORIZONTAL);
670 hbox->Add(new wxStaticText(this, wxID_ANY, _("NMEA 0183 Talker Id: ")),
671 wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border());
672 text_ctrl = new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition,
673 wxSize(50, 3 * wxWindow::GetCharWidth()));
674 text_ctrl->SetValue(g_TalkerIdText);
675 hbox->Add(text_ctrl, wxSizerFlags().Border());
676 SetSizer(hbox);
677 Cancel();
678 }
679
680 void Apply() override { g_TalkerIdText = text_ctrl->GetValue(); }
681 void Cancel() override { text_ctrl->SetValue(g_TalkerIdText); }
682 };
683
685 class NetmaskRow : public wxPanel, public ApplyCancel {
686 public:
687 NetmaskRow(wxWindow* parent)
688 : wxPanel(parent),
689 m_spin_ctrl(new wxSpinCtrl(this, wxID_ANY)),
690 m_text(new wxStaticText(this, wxID_ANY, "")) {
691 m_spin_ctrl->SetRange(8, 32);
692 auto hbox = new wxBoxSizer(wxHORIZONTAL);
693 auto flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();
694 hbox->Add(new wxStaticText(this, wxID_ANY, _("Netmask: ")), flags);
695 hbox->Add(m_text, flags);
696 hbox->Add(new wxStaticText(this, wxID_ANY, _("length (bits): ")), flags);
697 hbox->Add(m_spin_ctrl, flags);
698 SetSizer(hbox);
699 Cancel();
700
701 Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev) {
702 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
703 Layout();
704 });
705 }
706
707 void Apply() override { g_netmask_bits = m_spin_ctrl->GetValue(); }
708
709 void Cancel() override {
710 m_spin_ctrl->SetValue(g_netmask_bits);
711 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
712 Layout();
713 }
714
715 private:
716 wxSpinCtrl* m_spin_ctrl;
717 wxStaticText* m_text;
718
719 std::string BitsToDottedMask(unsigned bits) {
720 uint32_t mask = 0xffffffff << (32 - bits);
721 std::stringstream ss;
722 ss << ((mask & 0xff000000) >> 24) << ".";
723 ss << ((mask & 0x00ff0000) >> 16) << ".";
724 ss << ((mask & 0x0000ff00) >> 8) << ".";
725 ss << (mask & 0x000000ff);
726 return ss.str();
727 }
728 };
729
731 class PrioritiesBtn : public wxButton {
732 public:
733 PrioritiesBtn(wxWindow* parent)
734 : wxButton(parent, wxID_ANY, _("Adjust communication priorities...")) {
735 Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
736 PriorityDlg dialog(this);
737 dialog.ShowModal();
738 });
739 }
740 };
741};
742
745 wxWindow* parent, const std::vector<ConnectionParams*>& connections)
746 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
747 wxTAB_TRAVERSAL, "ConnectionsDlg"),
748 m_connections(connections) {
749 auto vbox = new wxBoxSizer(wxVERTICAL);
750 auto scrolled_window = new ScrolledWindow(this);
751 auto conn_grid =
752 new Connections(scrolled_window, m_connections, m_evt_add_connection);
753 scrolled_window->AddClient(conn_grid, conn_grid->GetGridMaxSize(),
754 conn_grid->GetGridMinSize());
755 vbox->Add(scrolled_window, wxSizerFlags(5).Expand().Border());
756 vbox->Add(new AddConnectionButton(this, m_evt_add_connection),
757 wxSizerFlags().Border());
758 vbox->Add(0, wxWindow::GetCharHeight(), 1); // Expanding spacer
759 auto panel_flags = wxSizerFlags().Border(wxLEFT | wxDOWN | wxRIGHT).Expand();
760 vbox->Add(new GeneralPanel(this), panel_flags);
761
762 auto advanced_panel = new AdvancedPanel(this);
763 auto on_toggle = [&, advanced_panel, vbox](bool show) {
764 advanced_panel->Show(show);
765 vbox->SetSizeHints(this);
766 vbox->Fit(this);
767 };
768 vbox->Add(new ShowAdvanced(this, on_toggle), panel_flags);
769 vbox->Add(advanced_panel, panel_flags.ReserveSpaceEvenIfHidden());
770
771 SetSizer(vbox);
772 SetAutoLayout(true);
773 wxWindow::Fit();
774
775 auto on_evt_update_connections = [&, conn_grid,
776 scrolled_window](ObservedEvt&) {
777 conn_grid->ReloadGrid(TheConnectionParams());
778 conn_grid->Show(conn_grid->GetNumberRows() > 0);
779 scrolled_window->SetMinClientSize(conn_grid->GetGridMinSize());
780 scrolled_window->SetMaxSize(conn_grid->GetGridMaxSize());
781 Layout();
782 };
783 m_add_connection_lstnr.Init(m_evt_add_connection, on_evt_update_connections);
784};
785
786void ConnectionsDlg::OnResize() {
787 Layout();
788 Refresh();
789 Update();
790}
791
792void ConnectionsDlg::DoApply(wxWindow* root) {
793 for (wxWindow* child : root->GetChildren()) {
794 auto widget = dynamic_cast<ApplyCancel*>(child);
795 if (widget) widget->Apply();
796 DoApply(child);
797 }
798}
799
800void ConnectionsDlg::DoCancel(wxWindow* root) {
801 for (wxWindow* child : root->GetChildren()) {
802 auto widget = dynamic_cast<ApplyCancel*>(child);
803 if (widget) widget->Cancel();
804 DoCancel(child);
805 }
806}
807
808void ConnectionsDlg::ApplySettings() { DoApply(this); }
809
810void ConnectionsDlg::CancelSettings() { DoCancel(this); }
The "Add new connection" button.
Indeed: The "Advanced" panel.
Interface implemented by widgets supporting Apply and Cancel.
virtual void Apply()=0
Make values set by user actually being used.
virtual void Cancel()=0
Restore values modified by user to their pristine state, often in a global.
std::sort support: Compare two ConnectionParams w r t given column
Filter reading driver driver_stats status reports from CommDriverRegistry transforming these to a str...
Definition conn_states.h:65
EventVar evt_conn_status_change
Notified with a shared_ptr<ConnData> when the state is changed.
Definition conn_states.h:71
Dialog for editing connection parameters.
std::string GetKey() const
Return string unique for each instance.
void ApplySettings()
Traverse root's children and invoke Apply if they implement ApplyCancel.
ConnectionsDlg(wxWindow *parent, const std::vector< ConnectionParams * > &connections)
Main window: connections grid, "Add new connection", general options.
void CancelSettings()
Traverse root's children and invoke Cancel if they implement ApplyCancel.
std::sort support: Compare two ConnectionParams w r t state.
Grid with existing connections: type, port, status, etc.
void ReloadGrid(const std::vector< ConnectionParams * > &connections)
Reload grid using data from given list of connections.
Generic event handling between MVC Model and Controller based on a shared EventVar variable.
const void Notify()
Notify all listeners, no data supplied.
Indeed: the General panel.
Define an action to be performed when a KeyProvider is notified.
Definition observable.h:228
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Definition observable.h:255
Adds a std::shared<void> element to wxCommandEvent.
Scrollable window wrapping the client i.
void AddClient(wxWindow *client, wxSize max_size, wxSize min_size)
Set contents and size limits for scrollable area.
The "Show advanced" text + right/down triangle and handler.
Driver registration container, a singleton.
Runtime connection/driver state definitions.
Dialog and support code for editing a connection.
General purpose GUI support.
Driver statistics report.