OpenCPN Partial API docs
Loading...
Searching...
No Matches
connections_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
24#include <array>
25#include <algorithm>
26#include <sstream>
27#include <string>
28#include <vector>
29
30#include "gl_headers.h" // Must be included before anything using GL stuff
31
32#include <wx/arrstr.h>
33#include <wx/bitmap.h>
34#include <wx/grid.h>
35#include <wx/panel.h>
36#include <wx/sizer.h>
37#include <wx/spinctrl.h>
38#include <wx/statbox.h>
39#include <wx/statline.h>
40#include <wx/textctrl.h>
41#include <wx/window.h>
42
43#include "model/base_platform.h"
45#include "model/comm_util.h"
46#include "model/config_vars.h"
47#include "model/conn_params.h"
48#include "model/conn_states.h"
49#include "model/gui_events.h"
51
52#include "connections_dlg.h"
53
54#include "color_handler.h"
55#include "color_types.h"
56#include "connection_edit.h"
57#include "conn_params_panel.h"
58#include "gui_lib.h"
59#include "navutil.h"
60#include "ocpn_platform.h"
61#include "options.h"
62#include "priority_gui.h"
63#include "std_filesystem.h"
64#include "model/svg_utils.h"
65
66static constexpr auto TopScrollWindowName = "TopScroll";
67
68static const char* kInfoHeader = _("OpenCPN help").c_str();
69static const char* kInfo = _(R"---(
70Normally OpenCPN sends the RMB (distance and heading to waypoint
71etc) and RMC (position, heading etc.) sentences when there is an
72active waypoint. The talker ID is by default EC.
73
74In certain cases these messages (ECRMB and ECRMC) are required
75also when there is no active waypoint. This option enables this.
76
77Obviously, this option carries risks for vessels which have an
78autopilot which is not engaged manually -- the "fake" RMB messages
79could possibly initiate the autopilot in dangerous ways.
80
81Devices needing this includes the NASA Clipper GPS Repeater. In this
82case the output must also be filtered so that ECRMB and ECRMC are
83the only transmitted messages.
84)---")
85 .c_str();
86
87static const char* kInterfaceExistsMessage =
88 _("Warning: A driver using this interface already exists.").c_str();
89
90static wxString UtfArrowDown() {
91 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u25bc"));
92}
93static wxString UtfArrowRight() {
94 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u25ba"));
95}
96static wxString UtfCheckMark() {
97 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u2713"));
98}
99static wxString UtfGear() {
100 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u2699"));
101}
102static wxString UtfFilledCircle() {
103 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u25cf"));
104}
105static wxString UtfOpenCircle() {
106 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\u25c8"));
107}
108static wxString UtfWastebasket() {
109 return wxString::FromUTF8(reinterpret_cast<const char*>(u8"\U0001f5d1"));
110}
111
112static bool IsWindows() {
113 return wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS;
114}
115
116static bool IsAndroid() {
117#ifdef ANDROID
118 return true;
119#else
120 return false;
121#endif
122}
123
124static std::string BitsToDottedMask(const unsigned bits) {
125 const uint32_t mask = 0xffffffff << (32 - bits);
126 std::stringstream ss;
127 ss << ((mask & 0xff000000) >> 24) << ".";
128 ss << ((mask & 0x00ff0000) >> 16) << ".";
129 ss << ((mask & 0x0000ff00) >> 8) << ".";
130 ss << (mask & 0x000000ff);
131 return ss.str();
132}
133
134static ConnectionParams* FindConnectionByIface(const ConnectionParams* new_cp) {
135 for (const auto& cp : TheConnectionParams()) {
136 if (cp->type != new_cp->type) continue;
137 switch (cp->type) {
138 case SERIAL:
139 if (cp->serial_port != new_cp->serial_port) continue;
140 return cp;
141 break;
142 case NETWORK:
143 if (cp->net_protocol != new_cp->net_protocol) continue;
144 if (cp->network_address != new_cp->network_address) continue;
145 if (cp->network_port != new_cp->network_port) continue;
146 return cp;
147 break;
148 case INTERNAL_GPS:
149 return cp;
150 break;
151 case INTERNAL_BT:
152 return cp;
153 break;
154 case SOCKETCAN:
155 if (cp->socket_can_port != new_cp->socket_can_port) continue;
156 return cp;
157 break;
158 default:
159 continue;
160 }
161 }
162 return nullptr;
163}
164
169class BitmapEnableCellRenderer : public wxGridCellBoolRenderer {
170public:
171 BitmapEnableCellRenderer(int _size, ColorScheme cs) : size(_size), m_cs(cs) {}
172
173 [[nodiscard]] BitmapEnableCellRenderer* Clone() const override {
174 return new BitmapEnableCellRenderer(size, m_cs);
175 }
176
177 wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row,
178 int col) override {
179 return {size, size};
180 }
181
182private:
183 int size;
184 ColorScheme m_cs;
185};
186
190public:
191 explicit ConnCompare(int col) : m_col(col) {}
192
193 bool operator()(const ConnectionParams* p1,
194 const ConnectionParams* p2) const {
195 switch (m_col) {
196 case 0:
197 return static_cast<int>(p1->is_enabled) >
198 static_cast<int>(p2->is_enabled);
199 case 1:
200 return p1->GetCommProtocol() < p2->GetCommProtocol();
201 case 2:
203 case 3:
204 return p1->GetStrippedDSPort() < p2->GetStrippedDSPort();
205 default:
206 return false;
207 }
208 }
209
210private:
211 const int m_col;
212};
213
218public:
222 virtual ~ApplyCancel() = default;
223
225 virtual void Apply() = 0;
226
230 virtual void Cancel() = 0;
231};
232
234class AddConnectionButton final : public wxButton {
235public:
236 AddConnectionButton(wxWindow* parent, obs::EventVar& evt_add_connection,
237 const std::function<void(ConnectionParams* p,
238 bool editing)>& _start_edit_conn)
239 : wxButton(parent, wxID_ANY, _("Add new connection...")),
240 m_evt_add_connection(evt_add_connection),
241 m_start_edit_conn(_start_edit_conn) {
242 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
243 [&](wxCommandEvent&) { OnAddConnection(); });
244 }
245
246private:
247 void OnAddConnection() const { m_start_edit_conn(nullptr, false); }
248
249 obs::EventVar& m_evt_add_connection;
250 std::function<void(ConnectionParams* p, bool editing)> m_start_edit_conn;
251};
252
254class Connections final : public wxGrid {
255public:
256 Connections(wxWindow* parent,
257 const std::vector<ConnectionParams*>& connections,
258 obs::EventVar& on_conn_update,
259 const std::function<void(ConnectionParams* p, bool editing)>&
260 on_edit_conn)
261 : wxGrid(parent, wxID_ANY),
262 m_connections(connections),
263 m_header_column_widths({0, 0, 0, 0, 0, 0, 0}),
264 m_last_tooltip_cell(100),
265 m_cs(GLOBAL_COLOR_SCHEME_DAY),
266 m_on_conn_delete(on_conn_update),
267 m_on_edit_conn(on_edit_conn) {
268 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
269 SetTable(new wxGridStringTable(), true);
270 GetTable()->AppendCols(8);
271 HideCol(7);
272 if (IsAndroid()) {
273 SetDefaultRowSize(wxWindow::GetCharHeight() * 2);
274 SetColLabelSize(wxWindow::GetCharHeight() * 2);
275 }
276 static const std::array<wxString, 7> headers = {
277 "", _("Protocol") + " ", _("In/Out"), _("Data port"), _("Status"), "",
278 ""};
279 int ic = 0;
280 for (auto hdr = headers.begin(); hdr != headers.end(); hdr++, ic++) {
281 SetColLabelValue(static_cast<int>(hdr - headers.begin()), *hdr);
282 unsigned col_width = hdr->Length() * wxWindow::GetCharWidth();
283 col_width = wxMax(col_width, 6 * wxWindow::GetCharWidth());
284 m_header_column_widths[ic] = static_cast<int>(col_width);
285 SetColSize(ic, static_cast<int>(col_width));
286 }
287
288 if (IsWindows()) {
289 SetLabelBackgroundColour(GetGlobalColor("DILG1"));
290 SetLabelTextColour(GetGlobalColor("DILG3"));
291 }
292 HideRowLabels();
293 SetColAttributes(parent);
294
295 OnColorChange();
296 ReloadGrid(connections);
297 DisableDragColSize();
298 DisableDragRowSize();
299 wxWindow::Show(GetNumberRows() > 0);
300
301 GetGridWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& ev) {
302 OnMouseMove(ev);
303 ev.Skip();
304 });
305 GetGridWindow()->Bind(wxEVT_MOUSEWHEEL,
306 [&](const wxMouseEvent& ev) { OnWheel(ev); });
307 // wxGridEvent.GetCol() and GetRow() are not const until wxWidgets 3.2
308 Bind(wxEVT_GRID_LABEL_LEFT_CLICK,
309 [&](wxGridEvent& ev) { HandleSort(ev.GetCol()); });
310 Bind(wxEVT_GRID_CELL_LEFT_CLICK,
311 [&](wxGridEvent& ev) { OnClickCell(ev.GetRow(), ev.GetCol()); });
312 Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) {
313 SetColAttributes(dynamic_cast<const wxWindow*>(ev.GetEventObject()));
314 ev.Skip();
315 });
316 Bind(wxEVT_SYS_COLOUR_CHANGED,
317 [&](wxSysColourChangedEvent& ev) { OnColorChange(ev); });
318 conn_change_lstnr.Init(
319 m_conn_states.evt_conn_status_change,
320 [&](ObservedEvt&) { OnConnectionChange(m_connections); });
321 }
322
323 void SetColorScheme(const ColorScheme cs) {
324 m_cs = cs;
325 ReloadGrid(m_connections);
326 }
327
328 void OnColorChange() {
329 auto foreground = wxSystemSettings::GetColour(wxSYS_COLOUR_MENUTEXT);
330 auto background = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
331 SetLabelBackgroundColour(background);
332 SetLabelTextColour(foreground);
333 for (int row = 0; row <= GetNumberRows(); row++) {
334 for (unsigned col = 0; col < 7; col++) {
335 SetCellBackgroundColour(row, col, background);
336 SetCellTextColour(row, col, foreground);
337 }
338 }
339 }
340
341 void OnColorChange(wxSysColourChangedEvent& ev) {
342 OnColorChange();
343 ev.Skip();
344 }
345
346 wxSize GetEstimatedSize() const {
347 unsigned rs = 0;
348 for (auto s : m_header_column_widths) rs += s;
349 return {static_cast<int>(rs), -1};
350 }
351
353 static void OnWheel(const wxMouseEvent& ev) {
354 auto w =
355 dynamic_cast<wxScrolledWindow*>(FindWindowByName(TopScrollWindowName));
356 assert(w && "No TopScroll window found");
357 int xpos;
358 int ypos;
359 w->GetViewStart(&xpos, &ypos);
360 int x;
361 int y;
362 w->GetScrollPixelsPerUnit(&x, &y);
363 // Not sure where the factor "4" comes from...
364 const int dir = ev.GetWheelRotation();
365 w->Scroll(-1, ypos - dir / y / 4);
366 }
367
369 void ReloadGrid(const std::vector<ConnectionParams*>& connections) {
370 ClearGrid();
371
372 for (auto it = connections.begin(); it != connections.end(); ++it) {
373 const auto row = static_cast<int>(it - connections.begin());
374 EnsureRows(row);
375 SetCellValue(row, 0, (*it)->is_enabled ? "1" : "");
376 if ((*it)->is_enabled)
377 m_tooltips[row][0] = _("Enabled, click to disable");
378 else
379 m_tooltips[row][0] = _("Disabled, click to enable");
380 std::string protocol = NavAddr::BusToString((*it)->GetCommProtocol());
381 SetCellValue(row, 1, protocol);
382 SetCellValue(row, 2, (*it)->GetPortDirectionValueStr());
383 SetCellValue(row, 3, (*it)->GetStrippedDSPort());
384 m_tooltips[row][3] = (*it)->user_comment;
385 SetCellValue(row, 5, UtfGear());
386 m_tooltips[row][5] = _("Edit connection");
387 SetCellValue(row, 6, UtfWastebasket());
388 m_tooltips[row][6] = _("Delete connection");
389 SetCellValue(row, 7, (*it)->GetKey());
390
391 if (IsAndroid()) {
392 wxString sp(protocol);
393 unsigned size = sp.Length() * wxWindow::GetCharWidth();
394 m_header_column_widths[1] = std::max(m_header_column_widths[1], size);
395 size = (*it)->GetPortDirectionValueStr().Length() *
396 wxWindow::GetCharWidth();
397 m_header_column_widths[2] = std::max(m_header_column_widths[2], size);
398 sp = wxString((*it)->GetStrippedDSPort());
399 size = sp.Length() * wxWindow::GetCharWidth();
400 m_header_column_widths[3] = std::max(m_header_column_widths[3], size);
401 }
402 }
403 OnConnectionChange(m_connections);
404 OnColorChange();
405
406 if (IsAndroid()) {
407 int ic = 0;
408 for (auto val : m_header_column_widths) {
409 SetColSize(ic, static_cast<int>(val));
410 ic++;
411 }
412 } else
413 AutoSize();
414 }
415
418 public:
419 explicit ConnStateCompare(Connections* connections)
420 : m_conns(connections) {}
421 bool operator()(const ConnectionParams* p1,
422 const ConnectionParams* p2) const {
423 int row1 = m_conns->FindConnectionIndex(p1);
424 int row2 = m_conns->FindConnectionIndex(p2);
425 if (row1 == -1 && row2 == -1) return false;
426 if (row1 == -1) return false;
427 if (row2 == -1) return true;
428 const int v1 = m_conns->GetCellValue(row1, 4)[0];
429 const int v2 = m_conns->GetCellValue(row2, 4)[0];
430 return v1 < v2;
431 }
432 Connections* m_conns;
433 };
434
440 using namespace std;
441 auto key = cp->GetKey();
442 auto found = find_if(
443 m_connections.begin(), m_connections.end(),
444 [key](const ConnectionParams* cp) { return cp->GetKey() == key; });
445 if (found == m_connections.end()) return -1;
446 return static_cast<int>(found - m_connections.begin());
447 }
448
449private:
454 ConnectionParams* FindRowConnection(const int row) const {
455 auto found = find_if(m_connections.begin(), m_connections.end(),
456 [&](const ConnectionParams* p) {
457 return GetCellValue(row, 7) == p->GetKey();
458 });
459 return found != m_connections.end() ? *found : nullptr;
460 }
461
466 void EnsureRows(size_t rows) {
467 for (unsigned i = m_tooltips.size(); i <= rows; i++)
468 m_tooltips.emplace_back(std::vector<std::string>(7));
469 for (unsigned i = GetNumberRows(); i <= rows; i++) AppendRows(1, false);
470 }
471
473 void SetColAttributes(const wxWindow* parent) {
474 if (IsWindows()) {
475 // Set all cells to global color scheme
476 SetDefaultCellBackgroundColour(GetGlobalColor("DILG1"));
477 }
478 auto enable_attr = new wxGridCellAttr();
479 enable_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
480
481 if (IsAndroid())
482 enable_attr->SetRenderer(
483 new BitmapEnableCellRenderer(wxWindow::GetCharWidth() * 3 / 2, m_cs));
484 else
485 enable_attr->SetRenderer(new wxGridCellBoolRenderer());
486
487 enable_attr->SetEditor(new wxGridCellBoolEditor());
488 if (IsWindows()) enable_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
489 SetColAttr(0, enable_attr);
490
491 auto protocol_attr = new wxGridCellAttr();
492 protocol_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
493 protocol_attr->SetReadOnly(true);
494 if (IsWindows())
495 protocol_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
496 SetColAttr(1, protocol_attr);
497
498 auto in_out_attr = new wxGridCellAttr();
499 in_out_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
500 in_out_attr->SetReadOnly(true);
501 if (IsWindows()) in_out_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
502 SetColAttr(2, in_out_attr);
503
504 auto port_attr = new wxGridCellAttr();
505 port_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
506 port_attr->SetReadOnly(true);
507 if (IsWindows()) port_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
508 SetColAttr(3, port_attr);
509
510 auto status_attr = new wxGridCellAttr();
511 status_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
512 status_attr->SetReadOnly(true);
513 status_attr->SetFont(parent->GetFont().Scale(1.3));
514 if (IsWindows()) status_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
515 SetColAttr(4, status_attr);
516
517 auto edit_attr = new wxGridCellAttr();
518 edit_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
519 edit_attr->SetFont(parent->GetFont().Scale(1.3));
520 edit_attr->SetReadOnly(true);
521 if (IsWindows()) edit_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
522 SetColAttr(5, edit_attr);
523
524 auto delete_attr = new wxGridCellAttr();
525 delete_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
526 delete_attr->SetFont(parent->GetFont().Scale(1.3));
527 delete_attr->SetTextColour(*wxRED);
528 delete_attr->SetReadOnly(true);
529 if (IsWindows()) delete_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
530 SetColAttr(6, delete_attr);
531 }
532
534 void OnClickCell(int row, int col) {
535 if (col == 0)
536 HandleEnable(row);
537 else if (col == 5) {
538 HandleEdit(row);
539 } else if (col == 6) {
540 HandleDelete(row);
541 }
542 }
543
545 void OnMouseMove(const wxMouseEvent& ev) {
546 const wxPoint pt = ev.GetPosition();
547 int row = YToRow(pt.y);
548 int col = XToCol(pt.x);
549 if (col < 0 || col >= 7 || row < 0 || row >= GetNumberRows()) return;
550 if (row * 7 + col == m_last_tooltip_cell) return;
551 m_last_tooltip_cell = row * 7 + col;
552 GetGridWindow()->SetToolTip(m_tooltips[row][col]);
553 }
554
556 void OnConnectionChange(const std::vector<ConnectionParams*>& connections) {
557 bool refresh_needed = false;
558 for (auto it = connections.begin(); it != connections.end(); ++it) {
559 ConnState state = m_conn_states.GetDriverState(
560 (*it)->GetCommProtocol(), (*it)->GetStrippedDSPort());
561 if (!(*it)->is_enabled) state = ConnState::Disabled;
562 auto row = static_cast<int>(it - connections.begin());
563 EnsureRows(row);
564 switch (state) {
565 case ConnState::Disabled:
566 SetCellValue(row, 4, UtfFilledCircle());
567 m_tooltips[row][4] = _("Disabled");
568 refresh_needed = true;
569 break;
570 case ConnState::NoStats:
571 SetCellValue(row, 4, UtfOpenCircle());
572 m_tooltips[row][4] = _("No driver statistics available");
573 refresh_needed = true;
574 break;
575 case ConnState::NoData:
576 SetCellValue(row, 4, "!");
577 m_tooltips[row][4] = _("No data flowing through connection");
578 refresh_needed = true;
579 break;
580 case ConnState::Unavailable:
581 SetCellValue(row, 4, "X");
582 m_tooltips[row][4] = _("The device is unavailable");
583 refresh_needed = true;
584 break;
585 case ConnState::Ok:
586 SetCellValue(row, 4, UtfCheckMark());
587 m_tooltips[row][4] = _("Data is flowing");
588 refresh_needed = true;
589 break;
590 }
591 }
592 if (refresh_needed) ForceRefresh();
593 }
594
596 void SetSortingColumn(int col) {
597 if (GetSortingColumn() != wxNOT_FOUND) {
598 int old_col = GetSortingColumn();
599 auto label = GetColLabelValue(old_col);
600 if (label[0] == UtfArrowDown()[0])
601 SetColLabelValue(old_col, label.substr(2));
602 }
603 auto label = GetColLabelValue(col);
604 if (label[0] != UtfArrowDown()[0])
605 SetColLabelValue(col, UtfArrowDown() + " " + label);
606 wxGrid::SetSortingColumn(col);
607 Fit();
608 }
609
611 void HandleSort(int col) {
612 if (col > 4) return;
613 auto& params = TheConnectionParams();
614 if (col < 4)
615 std::sort(params.begin(), params.end(), ConnCompare(col));
616 else // col == 4
617 std::sort(params.begin(), params.end(), ConnStateCompare(this));
619 SetSortingColumn(col);
620 }
621
623 void HandleEnable(int row) {
624 ConnectionParams* cp = FindRowConnection(row);
625 if (!cp) return;
626 cp->is_enabled = !cp->is_enabled;
627 cp->is_setup = FALSE; // trigger a rebuild/takedown of the connection
628 SetCellValue(row, 0, cp->is_enabled ? "1" : "");
629 if (cp->is_enabled)
630 m_tooltips[row][0] = _("Enabled, click to disable");
631 else
632 m_tooltips[row][0] = _("Disabled, click to enable");
633 DriverStats stats;
634 stats.driver_iface = cp->GetStrippedDSPort();
635 stats.driver_bus = cp->GetCommProtocol();
636 m_conn_states.HandleDriverStats(stats);
637 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
638 if (cp->is_enabled) MakeCommDriver(cp);
639 cp->is_setup = true;
640 if (!cp->is_enabled) {
641 SetCellValue(row, 4, UtfFilledCircle());
642 // ForceRefresh() apparently broken, see #4648
644 }
645 }
646
648 void HandleEdit(int row) {
649 if (ConnectionParams* cp = FindRowConnection(row)) {
650 Show(GetNumberRows() > 0);
651 m_on_edit_conn(cp, true);
652 }
653 }
654
656 void HandleDelete(int row) {
657 ConnectionParams* cp = FindRowConnection(row);
658 auto found = std::find(m_connections.begin(), m_connections.end(), cp);
659 if (found != m_connections.end()) {
660 std::stringstream ss;
661 ss << _("Ok to delete connection on ") << (*found)->GetStrippedDSPort();
662 int rcode = OCPNMessageBox(this, ss.str(), _("Delete connection?"),
663 wxOK | wxCANCEL);
664 if (rcode != wxID_OK && rcode != wxID_YES) return;
665 delete (*found)->options_panel;
666 StopAndRemoveCommDriver((*found)->GetStrippedDSPort(),
667 (*found)->GetCommProtocol());
668 TheConnectionParams().erase(found);
669 if (GetNumberRows() > static_cast<int>(m_connections.size()))
670 DeleteRows(GetNumberRows() - 1);
671 m_on_conn_delete.Notify();
672 }
673 }
674
675 obs::Listener conn_change_lstnr;
676 std::vector<std::vector<std::string>> m_tooltips;
677 ConnStates m_conn_states;
678 const std::vector<ConnectionParams*>& m_connections;
679 std::array<unsigned, 7> m_header_column_widths;
680 int m_last_tooltip_cell;
681 ColorScheme m_cs;
682 obs::EventVar& m_on_conn_delete;
683 std::function<void(ConnectionParams* p, bool editing)> m_on_edit_conn;
684};
685
687class GeneralPanel : public wxPanel {
688public:
689 explicit GeneralPanel(wxWindow* parent, wxSize max_size)
690 : wxPanel(parent, wxID_ANY) {
691 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("General"));
692 SetSizer(sizer);
693 auto flags = wxSizerFlags().Border();
694 m_upload_options = new UploadOptionsChoice(this);
695 sizer->Add(m_upload_options, flags);
696 sizer->Add(new PrioritiesBtn(this), flags);
697 wxWindow::SetMaxSize(max_size);
698 }
699 void SetColorScheme(ColorScheme) {
700 DimeControl(m_upload_options);
701 Refresh();
702 }
703
704private:
706 class PrioritiesBtn final : public wxButton {
707 public:
708 explicit PrioritiesBtn(wxWindow* parent)
709 : wxButton(parent, wxID_ANY, _("Adjust Nav data priorities...")) {
710 Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
711 PriorityDlg dialog(this->GetParent());
712 dialog.ShowModal();
713 });
714 }
715 };
716
718 class UploadOptionsChoice final : public wxRadioBox, public ApplyCancel {
719 public:
720 explicit UploadOptionsChoice(wxWindow* parent) : wxRadioBox() {
721 wxArrayString wx_choices;
722 for (auto& c : choices) wx_choices.Add(c);
723 Create(parent, wxID_ANY, _("Upload Format"), wxDefaultPosition,
724 wxDefaultSize, wx_choices, 0, wxRA_SPECIFY_ROWS);
725 DimeControl(this);
726 UploadOptionsChoice::Cancel();
727 }
728
729 void Cancel() override {
730 if (g_bGarminHostUpload)
731 SetSelection(1);
732 else if (g_GPS_Ident == "FurunoGP3X")
733 SetSelection(2);
734 else
735 SetSelection(0);
736 }
737
738 void Apply() override {
739 switch (GetSelection()) {
740 case 0:
741 g_bGarminHostUpload = false;
742 g_GPS_Ident = "Generic";
743 break;
744 case 1:
745 g_bGarminHostUpload = true;
746 g_GPS_Ident = "Generic";
747 break;
748 case 2:
749 g_bGarminHostUpload = false;
750 g_GPS_Ident = "FurunoGP3X";
751 break;
752 default:
753 assert(false && "Invalid upload case option");
754 }
755 }
756
757 const std::array<wxString, 3> choices = {
758 _("Generic NMEA 0183"), _("Garmin Host mode"), _("Furuno GP4X")};
759 };
760
761 UploadOptionsChoice* m_upload_options;
762};
763
765class ShowAdvanced : public wxStaticText {
766public:
767 ShowAdvanced(wxWindow* parent, std::function<void(bool)> on_toggle)
768 : wxStaticText(parent, wxID_ANY, ""),
769 m_on_toggle(std::move(on_toggle)),
770 m_show_advanced(true) {
771 Toggle();
772 Bind(wxEVT_LEFT_DOWN, [&](const wxMouseEvent& ev) { Toggle(); });
773 }
774
775private:
776 wxString m_label = _("Advanced ");
777 std::function<void(bool)> m_on_toggle;
778 bool m_show_advanced;
779
780 void Toggle() {
781 m_show_advanced = !m_show_advanced;
782 m_label = _("Advanced ");
783 m_label += m_show_advanced ? UtfArrowDown() : UtfArrowRight();
784 SetLabelText(m_label);
785 m_on_toggle(m_show_advanced);
786 }
787};
788
790class AdvancedPanel : public wxPanel {
791public:
792 explicit AdvancedPanel(wxWindow* parent, wxSize max_size)
793 : wxPanel(parent, wxID_ANY) {
794 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, "");
795 sizer->Add(new BearingsCheckbox(this), wxSizerFlags().Expand());
796 sizer->Add(new ExtraRmbRmcLine(this), wxSizerFlags().Expand());
797 sizer->Add(new NmeaFilterRow(this), wxSizerFlags().Expand());
798 sizer->Add(new TalkerIdRow(this), wxSizerFlags().Expand());
799 sizer->Add(new NetmaskRow(this), wxSizerFlags().Expand());
800 SetSizer(sizer);
801 wxWindow::SetMaxSize(max_size);
802 }
803
804private:
806 class BearingsCheckbox : public wxCheckBox, public ApplyCancel {
807 public:
808 explicit BearingsCheckbox(wxWindow* parent)
809 : wxCheckBox(parent, wxID_ANY,
810 _("Use magnetic bearing in output sentence APB")) {
811 wxCheckBox::SetValue(g_bMagneticAPB);
812 }
813
814 void Apply() override { g_bMagneticAPB = GetValue(); }
815 void Cancel() override { SetValue(g_bMagneticAPB); }
816 };
817
818 class ExtraRmbRmcLine : public wxPanel {
819 class RmbRmcCheckbox : public wxCheckBox, public ApplyCancel {
820 public:
821 explicit RmbRmcCheckbox(wxWindow* parent)
822 : wxCheckBox(parent, wxID_ANY,
823 _("Always send RMB and RMC NMEA0183 data")) {
824 wxCheckBox::SetValue(g_always_send_rmb_rmc);
825 }
826
827 void Apply() override { g_always_send_rmb_rmc = GetValue(); }
828 void Cancel() override { SetValue(g_always_send_rmb_rmc); }
829 };
830
831 public:
832 explicit ExtraRmbRmcLine(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
833 auto hbox = new wxBoxSizer(wxHORIZONTAL);
834 hbox->Add(new RmbRmcCheckbox(this), wxSizerFlags().Expand());
835 hbox->Add(1, 1, 1, wxEXPAND);
836 hbox->Add(new InfoButton(this, g_btouch, kInfoHeader, kInfo),
837 wxSizerFlags().Border());
838 SetSizer(hbox);
839 wxWindow::Layout();
840 wxWindow::Show();
841 }
842 };
843 class ExtraRmbRmcCheckbox final : public wxCheckBox, public ApplyCancel {
844 public:
845 explicit ExtraRmbRmcCheckbox(wxWindow* parent)
846 : wxCheckBox(parent, wxID_ANY,
847 _("Always send RMB and RMC NMEA0183 data")) {
848 wxCheckBox::SetValue(g_always_send_rmb_rmc);
849 }
850
851 void Apply() override { g_always_send_rmb_rmc = GetValue(); }
852 void Cancel() override { SetValue(g_always_send_rmb_rmc); }
853 };
854
856 class NmeaFilterRow : public wxPanel, public ApplyCancel {
857 wxCheckBox* checkbox;
858 wxTextCtrl* filter_period;
859
860 public:
861 explicit NmeaFilterRow(wxWindow* parent) : wxPanel(parent) {
862 auto hbox = new wxBoxSizer(wxHORIZONTAL);
863 checkbox = new wxCheckBox(
864 this, wxID_ANY,
865 _("Filter NMEA course and speed data. Filter period: "));
866 checkbox->SetValue(g_bfilter_cogsog);
867 hbox->Add(checkbox, wxSizerFlags().Align(wxALIGN_CENTRE));
868 filter_period =
869 new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition,
870 wxSize(50, 3 * wxWindow::GetCharWidth()), 0);
871 filter_period->SetValue(std::to_string(g_COGFilterSec));
872 hbox->Add(filter_period, wxSizerFlags().Border());
873 SetSizer(hbox);
874 NmeaFilterRow::Cancel();
875 }
876
877 void Apply() override {
878 std::stringstream ss;
879 ss << filter_period->GetValue();
880 ss >> g_COGFilterSec;
881 g_bfilter_cogsog = checkbox->GetValue();
882 }
883
884 void Cancel() override {
885 std::stringstream ss;
886 ss << g_COGFilterSec;
887 filter_period->SetValue(ss.str());
888 checkbox->SetValue(g_bfilter_cogsog);
889 }
890 };
891
893 class TalkerIdRow : public wxPanel, public ApplyCancel {
894 wxTextCtrl* text_ctrl;
895
896 public:
897 explicit TalkerIdRow(wxWindow* parent) : wxPanel(parent) {
898 auto hbox = new wxBoxSizer(wxHORIZONTAL);
899 hbox->Add(new wxStaticText(this, wxID_ANY, _("NMEA 0183 Talker Id: ")),
900 wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border());
901 text_ctrl = new wxTextCtrl(
902 this, wxID_ANY, "", wxDefaultPosition,
903 wxSize(4 * wxWindow::GetCharWidth(), 3 * wxWindow::GetCharWidth()));
904 text_ctrl->SetToolTip(
905 _("Enter a two-letter talker ID to override the default ID in NMEA "
906 "sentences generated by OpenCPN (e.g., GP, HC, EC). This affects "
907 "only sentences created by OpenCPN, not those forwarded from other "
908 "devices."));
909 text_ctrl->SetValue(g_TalkerIdText);
910 hbox->Add(text_ctrl, wxSizerFlags().Border());
911 SetSizer(hbox);
912 TalkerIdRow::Cancel();
913 }
914
915 void Apply() override { g_TalkerIdText = text_ctrl->GetValue(); }
916 void Cancel() override { text_ctrl->SetValue(g_TalkerIdText); }
917 };
918
920 class NetmaskRow : public wxPanel, public ApplyCancel {
921 public:
922 explicit NetmaskRow(wxWindow* parent)
923 : wxPanel(parent),
924 m_spin_ctrl(new wxSpinCtrl(this, wxID_ANY)),
925 m_text(new wxStaticText(this, wxID_ANY, "")) {
926 m_spin_ctrl->SetRange(8, 32);
927 auto hbox = new wxBoxSizer(wxHORIZONTAL);
928 auto flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();
929 hbox->Add(new wxStaticText(this, wxID_ANY, _("Netmask: ")), flags);
930 hbox->Add(m_text, flags);
931 hbox->Add(new wxStaticText(this, wxID_ANY, _("length (bits): ")), flags);
932 hbox->Add(m_spin_ctrl, flags);
933 SetSizer(hbox);
934 NetmaskRow::Cancel();
935
936 Bind(wxEVT_SPINCTRL, [&](wxSpinEvent&) {
937 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
938 Layout();
939 });
940 }
941
942 void Apply() override { g_netmask_bits = m_spin_ctrl->GetValue(); }
943
944 void Cancel() override {
945 m_spin_ctrl->SetValue(g_netmask_bits);
946 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
947 Layout();
948 }
949
950 private:
951 wxSpinCtrl* m_spin_ctrl;
952 wxStaticText* m_text;
953 };
954};
955
957class TopPanel : public wxPanel {
958public:
959 TopPanel(wxWindow* parent, const std::vector<ConnectionParams*>& connections,
960 obs::EventVar& evt_add_connection,
961 const std::function<void(ConnectionParams* p, bool editing)>&
962 on_edit_conn)
963 : wxPanel(parent, wxID_ANY),
964 m_evt_add_connection(evt_add_connection),
965 m_connections(connections) {
966 auto vbox = new wxBoxSizer(wxVERTICAL);
967 auto conn_grid = new Connections(this, m_connections, m_evt_add_connection,
968 on_edit_conn);
969 wxSize panel_max_size(conn_grid->GetEstimatedSize());
970 vbox->AddSpacer(wxWindow::GetCharHeight());
971 auto conn_flags = wxSizerFlags().Border();
972 if (IsAndroid()) conn_flags = wxSizerFlags().Border().Expand();
973 vbox->Add(conn_grid, conn_flags);
974 vbox->Add(new AddConnectionButton(this, m_evt_add_connection, on_edit_conn),
975 wxSizerFlags().Border());
976 vbox->Add(0, wxWindow::GetCharHeight()); // Expanding spacer
977 auto panel_flags =
978 wxSizerFlags().Border(wxLEFT | wxDOWN | wxRIGHT).Expand();
979 m_general_panel = new GeneralPanel(this, panel_max_size);
980 vbox->Add(m_general_panel, panel_flags);
981
982 auto advanced_panel = new AdvancedPanel(this, panel_max_size);
983 m_advanced_panel = advanced_panel;
984 auto on_toggle = [&, advanced_panel, vbox](bool show) {
985 // FIXME advanced_panel->SetMaxSize({conn_grid->GetSize().x, -1});
986 advanced_panel->Show(show);
987 vbox->SetSizeHints(this);
988 vbox->Fit(this);
989 };
990 vbox->Add(new ShowAdvanced(this, on_toggle), panel_flags);
991 vbox->Add(advanced_panel, panel_flags.ReserveSpaceEvenIfHidden());
992
993 SetSizer(vbox);
994 vbox->SetSizeHints(this);
995 vbox->Fit(this);
996 wxWindow::Fit();
997 wxWindow::Show();
998
999 auto on_evt_update_connections = [&, conn_grid](ObservedEvt&) {
1000 conn_grid->ReloadGrid(TheConnectionParams());
1001 conn_grid->Show(conn_grid->GetNumberRows() > 0);
1002 Layout();
1003 };
1004 m_add_connection_lstnr.Init(m_evt_add_connection,
1005 on_evt_update_connections);
1006 m_conn_grid = conn_grid;
1007 }
1008
1009 void SetColorScheme(const ColorScheme cs) const {
1010 m_conn_grid->SetColorScheme(cs);
1011 m_general_panel->SetColorScheme(cs);
1012 }
1013 [[nodiscard]] Connections* GetConnectionsGrid() const { return m_conn_grid; }
1014
1015 obs::EventVar& m_evt_add_connection;
1016
1017private:
1018 const std::vector<ConnectionParams*>& m_connections;
1019 obs::Listener m_add_connection_lstnr;
1020 Connections* m_conn_grid;
1021 GeneralPanel* m_general_panel;
1022 AdvancedPanel* m_advanced_panel;
1023};
1024
1026class TopScroll : public wxScrolledWindow {
1027public:
1028 TopScroll(wxWindow* parent, const std::vector<ConnectionParams*>& connections,
1029 obs::EventVar& evt_add_connection)
1030 : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
1031 wxVSCROLL | wxHSCROLL, TopScrollWindowName) {
1032 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
1033 auto vbox = new wxBoxSizer(wxVERTICAL);
1034 SetSizer(vbox);
1035
1036 auto on_edit_connection = [&](ConnectionParams* p, bool editing) {
1037 if (editing)
1038 HandleEdit(p);
1039 else
1040 HandleNew(p);
1041 };
1042
1043 top_panel =
1044 new TopPanel(this, connections, evt_add_connection, on_edit_connection);
1045 vbox->Add(top_panel, wxSizerFlags(1).Expand());
1046
1047 auto on_edit_click = [&](const ConnectionParams* p, bool new_mode,
1048 bool ok_cancel) {
1049 HandleEditFinish(p, new_mode, ok_cancel);
1050 };
1051
1052 m_edit_panel = new ConnectionEditDialog(this, on_edit_click);
1053 m_edit_panel->SetPropsLabel(_("Edit Selected Connection"));
1054 wxWindow* options = FindWindowByName("Options");
1055 assert(options && "Null Options window!");
1056 int fraction = 9;
1057#ifdef ANDROID
1058 fraction = 10;
1059#endif
1060 m_edit_panel->SetSize(
1061 wxSize(options->GetSize().x, options->GetSize().y * fraction / 10));
1062 vbox->Add(m_edit_panel, wxSizerFlags(0).Expand());
1063 m_edit_panel->Hide();
1064
1065 SetScrollRate(0, 10);
1066 if (IsAndroid()) SetScrollRate(1, 1);
1067
1068 SetScrollRate(0, 10);
1069 if (IsAndroid()) SetScrollRate(1, 1);
1070 }
1071
1072 void SetColorScheme(ColorScheme cs) const {
1073 if (top_panel) top_panel->SetColorScheme(cs);
1074 }
1075
1076 void HandleEdit(ConnectionParams* p) {
1077 m_edit_panel->SetPropsLabel(_("Edit Selected Connection"));
1078 m_edit_panel->SetNewMode(false);
1079 m_edit_panel->PreloadControls(p);
1080 m_edit_panel->AddOKCancelButtons();
1081 SwitchToEditor();
1082 }
1083
1084 void HandleNew(ConnectionParams*) {
1085 m_edit_panel->SetPropsLabel(_("Configure new connection"));
1086 m_edit_panel->SetDefaultConnectionParams();
1087 m_edit_panel->SetNewMode(true);
1088 m_edit_panel->AddOKCancelButtons();
1089 m_edit_panel->InitiateNewConnection();
1090 SwitchToEditor();
1091 }
1092
1093 void SwitchToEditor() {
1094 top_panel->Hide(); // TopPanel
1095 Scroll(0, 0);
1096 DimeControl(m_edit_panel);
1097 m_edit_panel->Show();
1098 g_options->ShowOKButtons(false);
1099 Layout();
1100 }
1101
1102 void SwitchToGrid() {
1103 g_options->ShowOKButtons(true);
1104 m_edit_panel->Hide();
1105 top_panel->Show();
1106 top_panel->GetConnectionsGrid()->ReloadGrid(TheConnectionParams());
1107 Layout();
1108 Scroll(0, 0);
1109 }
1110
1111 void HandleEditFinish(const ConnectionParams* cp_orig, bool new_mode,
1112 bool ok_cancel) {
1113 if (!ok_cancel) {
1114 SwitchToGrid();
1115 return;
1116 }
1117 // OK from EDIT mode
1118 if (!new_mode) {
1119 ConnectionParams* cp_edited = m_edit_panel->GetParamsFromControls();
1120 delete cp_orig->options_panel;
1121 StopAndRemoveCommDriver(cp_orig->GetStrippedDSPort(),
1122 cp_orig->GetCommProtocol());
1123 int index = top_panel->GetConnectionsGrid()->FindConnectionIndex(cp_orig);
1124 assert(index != -1 && "Cannot look up connection index");
1125 TheConnectionParams()[index] = cp_edited;
1126 cp_edited->is_setup = false; // Trigger new stream
1127 }
1128 // OK from NEW mode
1129 else {
1130 ConnectionParams* cp = m_edit_panel->GetParamsFromControls();
1131 if (cp) {
1132 if (FindConnectionByIface(cp)) {
1133 wxMessageDialog dialog(this, kInterfaceExistsMessage,
1134 _("Driver creation warning"),
1135 wxOK | wxCENTRE | wxICON_WARNING);
1136 dialog.ShowModal();
1137 }
1138 if (cp->IsPortValid()) {
1139 // Trigger new stream
1140 cp->is_setup = false;
1141 // transfer ownership FIXME use smart pointers
1142 TheConnectionParams().push_back(cp);
1143 } else {
1144 wxString msg =
1145 _("Unable to create a connection as configured. "
1146 "Connected port or address was missing.");
1147 auto& noteman = NotificationManager::GetInstance();
1148 noteman.AddNotification(NotificationSeverity::kWarning,
1149 msg.ToStdString(), 60);
1150 delete cp;
1151 }
1152 }
1153 UpdateDatastreams();
1154 top_panel->m_evt_add_connection.Notify();
1155 }
1156
1157 SwitchToGrid();
1158 UpdateDatastreams();
1159 }
1160
1161private:
1162 TopPanel* top_panel;
1163 ConnectionEditDialog* m_edit_panel;
1164};
1165
1168 wxWindow* parent, const std::vector<ConnectionParams*>& connections)
1169 : wxPanel(parent, wxID_ANY), m_connections(connections) {
1170 const auto vbox = new wxBoxSizer(wxVERTICAL);
1171 vbox->Add(new TopScroll(this, connections, m_evt_add_connection),
1172 wxSizerFlags(1).Expand());
1173 SetSizer(vbox);
1174 wxWindow::Fit();
1175 wxWindow::Show();
1176}
1177
1178void ConnectionsDlg::OnResize(const wxSize& size) {
1179 auto w = FindWindowByName(TopScrollWindowName);
1180 if (!w) return;
1181 w->SetMinSize(size);
1182 Fit();
1183}
1184
1185void ConnectionsDlg::DoApply(wxWindow* root) {
1186 for (wxWindow* child : root->GetChildren()) {
1187 if (auto widget = dynamic_cast<ApplyCancel*>(child)) widget->Apply();
1188 DoApply(child);
1189 }
1190}
1191
1192void ConnectionsDlg::DoCancel(wxWindow* root) {
1193 for (wxWindow* child : root->GetChildren()) {
1194 if (auto widget = dynamic_cast<ApplyCancel*>(child)) widget->Cancel();
1195 DoCancel(child);
1196 }
1197}
1198
1199void ConnectionsDlg::ApplySettings() { DoApply(this); }
1200
1201void ConnectionsDlg::CancelSettings() { DoCancel(this); }
1202
1203void ConnectionsDlg::SetColorScheme(ColorScheme cs) {
1204 auto w = dynamic_cast<TopScroll*>(FindWindowByName(TopScrollWindowName));
1205 if (w) w->SetColorScheme(cs);
1206}
Basic platform specific support utilities without GUI deps.
The "Add new connection" button.
Indeed: The "Advanced" panel.
Interface implemented by widgets supporting Apply and Cancel.
virtual ~ApplyCancel()=default
Destroy the Apply Cancel object.
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.
Custom renderer class for rendering ENABLE in a grid cell.
std::sort support: Compare twSetInitialSettingso ConnectionParams w r t given column
Filter reading driver driver_stats status reports from CommDriverRegistry transforming these to a str...
Definition conn_states.h:64
obs::EventVar evt_conn_status_change
Notified with a shared_ptr<ConnData> when the state is changed.
Definition conn_states.h:70
Dialog for editing connection parameters.
ConnectionParams * GetParamsFromControls()
Return parameters instance populated from UI elements owned by caller.
Connection data container close to a POD struct.
Definition conn_params.h:75
bool IsPortValid() const
Return true if port data like com port or network address are sane.
std::string GetStrippedDSPort() const
Return port string with possible windows extra data removed, in some cases empty.
std::string GetKey() const
Return string unique for each instance.
wxString GetPortDirectionValueStr() const
Return translated name for direction, like _("Input").
NavAddr::Bus GetCommProtocol() const
Return NavAddr::Bus corresponding to network address/port or serial parameters.
void ApplySettings()
Make dialog's settings the active ones, usually by updating globals.
void OnResize(const wxSize &size)
Resize the connections tab.
ConnectionsDlg(wxWindow *parent, const std::vector< ConnectionParams * > &connections)
Create a new ConnectionsDlg instance.
void CancelSettings()
Restore dialog settings from currently used values, usually globals.
std::sort support: Compare two ConnectionParams w r t state.
Grid with existing connections: type, port, status, etc.
static void OnWheel(const wxMouseEvent &ev)
Mouse wheel: scroll the TopScroll window.
void ReloadGrid(const std::vector< ConnectionParams * > &connections)
Reload grid using data from given list of connections.
int FindConnectionIndex(const ConnectionParams *cp) const
Find index in m_connections for given pointer.
Indeed: the General panel.
Clickable, small info icon which displays a help text Typical usage:
Definition gui_lib.h:146
Custom event class for OpenCPN's notification system.
The "Show advanced" text + right/down triangle and handler.
Top panel: connections grid, "Add new connection", general options.
Top scroll window, adds scrollbars to TopPanel.
Generic event handling between for example MVC Model and Controller based on a shared EventVar variab...
Definition evtvar.h:78
void Notify() override
Notify all listeners, no data supplied.
Definition evtvar.h:83
Global color handling by name.
void MakeCommDriver(const ConnectionParams *params)
Create and register a driver for given connection.
Communication drivers factory and support.
Misc driver utilities.
bool g_always_send_rmb_rmc
Always send RMB and RMC n0183 messages even if there is no active route.
Global variables stored in configuration file.
std::vector< ConnectionParams * > & TheConnectionParams()
Return global list of connections.
Connection parameters.
Panel for editing a connection.
Runtime connection/driver state definitions.
Dialog and support code for editing a connection.
Options | Connections GUI tab managing connections
Platform independent GL includes.
Misc GUI event vars, a singleton.
General purpose GUI support.
Utility functions.
User notifications manager.
OpenCPN Platform specific support utilities.
options * g_options
Global instance.
Definition options.cpp:181
Options dialog.
Input priorities management dialog.
Driver statistics report.
SVG utilities.