OpenCPN Partial API docs
Loading...
Searching...
No Matches
connections_dlg.cpp
Go to the documentation of this file.
1
2/***************************************************************************
3 * Copyright (C) 2025 Alec Leamas *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
23#include <array>
24#include <algorithm>
25#include <sstream>
26#include <string>
27#include <vector>
28
29#include <wx/arrstr.h>
30#include <wx/bitmap.h>
31#include <wx/grid.h>
32#include <wx/panel.h>
33#include <wx/sizer.h>
34#include <wx/spinctrl.h>
35#include <wx/textctrl.h>
36#include <wx/window.h>
37
38#include "model/base_platform.h"
39#include "model/comm_drv_factory.h"
40#include "model/comm_util.h"
41#include "model/config_vars.h"
42#include "model/conn_params.h"
43#include "model/conn_states.h"
45
46#include "connections_dlg.h"
47
48#include "color_handler.h"
49#include "color_types.h"
50#include "connection_edit.h"
51#include "conn_params_panel.h"
52#include "gui_lib.h"
53#include "navutil.h"
54#include "OCPNPlatform.h"
55#include "options.h"
56#include "priority_gui.h"
57#include "std_filesystem.h"
58#include "svg_utils.h"
59
60extern OCPNPlatform* g_Platform;
61extern options* g_options;
62
63static const auto kUtfArrowDown = wxString::FromUTF8(u8"\u25bc");
64static const auto kUtfArrowRight = wxString::FromUTF8(u8"\u25ba");
65static const auto kUtfFilledCircle = wxString::FromUTF8(u8"\u2b24");
66
67static const char* const TopScrollWindowName = "TopScroll";
68
69static inline bool IsWindows() {
70 return wxPlatformInfo::Get().GetOperatingSystemId() & wxOS_WINDOWS;
71}
72
73static inline bool IsAndroid() {
74#ifdef ANDROID
75 return true;
76#else
77 return false;
78#endif
79}
80
81static std::string BitsToDottedMask(unsigned bits) {
82 uint32_t mask = 0xffffffff << (32 - bits);
83 std::stringstream ss;
84 ss << ((mask & 0xff000000) >> 24) << ".";
85 ss << ((mask & 0x00ff0000) >> 16) << ".";
86 ss << ((mask & 0x0000ff00) >> 8) << ".";
87 ss << (mask & 0x000000ff);
88 return ss.str();
89}
90
92class StdIcons {
93private:
94 const double m_size;
95 const fs::path m_svg_dir;
96 ColorScheme m_cs;
97
99 double GetSize(const wxWindow* parent) {
100 double size = parent->GetCharHeight() * (IsWindows() ? 1.3 : 1.0);
101#if wxCHECK_VERSION(3, 1, 2)
102 // Apply scale factor, mostly for Windows. Other platforms
103 // does this in the toolkits, ToDIP() is aware of this.
104 size *= static_cast<double>(parent->ToDIP(100)) / 100.;
105#endif
106 // Force minimum physical size for touch screens
107 if (g_btouch) {
108 double pixel_per_mm =
109 wxGetDisplaySize().x / g_Platform->GetDisplaySizeMM();
110 size = std::max(size, 7.0 * pixel_per_mm);
111 }
112 return size;
113 }
114
115 wxBitmap LoadIcon(const std::string& filename) const {
116 fs::path path = m_svg_dir / filename;
117 return LoadSVG(path.string(), m_size, m_size);
118 }
119
120 wxBitmap IconApplyColorScheme(const wxBitmap proto) const {
121 if (!proto.IsOk()) return wxNullBitmap;
122 if ((m_cs != GLOBAL_COLOR_SCHEME_DAY) &&
123 (m_cs != GLOBAL_COLOR_SCHEME_RGB)) {
124 // Assume the bitmap is monochrome, so simply invert the colors.
125 wxImage image = proto.ConvertToImage();
126 unsigned char* data = image.GetData();
127 unsigned char* p_idata = data;
128 for (int i = 0; i < image.GetSize().y; i++) {
129 for (int j = 0; j < image.GetSize().x; j++) {
130 unsigned char v = *p_idata;
131 v = 255 - v;
132 *p_idata++ = v;
133 v = *p_idata;
134 v = 255 - v;
135 *p_idata++ = v;
136 v = *p_idata;
137 v = 255 - v;
138 *p_idata++ = v;
139 }
140 }
141 return wxBitmap(image);
142 } else
143 return proto;
144 }
145
146public:
147 StdIcons(const wxWindow* parent)
148 : m_size(GetSize(parent)),
149 m_svg_dir(fs::path(g_Platform->GetSharedDataDir().ToStdString()) /
150 "uidata" / "MUI_flat"),
151 m_cs(GLOBAL_COLOR_SCHEME_RGB),
152 trashbin_proto(LoadIcon("trash_bin.svg")),
153 settings_proto(LoadIcon("setting_gear.svg")),
154 filled_circle_proto(LoadIcon("circle-on.svg")),
155 open_circle_proto(LoadIcon("circle-off.svg")),
156 exclaim_mark_proto(LoadIcon("exclaim_mark.svg")),
157 x_mult_proto(LoadIcon("X_mult.svg")),
158 check_mark_proto(LoadIcon("check_mark.svg")) {
159 trashbin = trashbin_proto;
160 settings = settings_proto;
161 filled_circle = filled_circle_proto;
162 open_circle = open_circle_proto;
163 exclaim_mark = exclaim_mark_proto;
164 x_mult = x_mult_proto;
165 check_mark = check_mark_proto;
166 }
167
168 void SetColorScheme(ColorScheme cs) {
169 if (m_cs != cs) {
170 m_cs = cs;
171 trashbin = IconApplyColorScheme(trashbin_proto);
172 settings = IconApplyColorScheme(settings_proto);
173 filled_circle = IconApplyColorScheme(filled_circle_proto);
174 open_circle = IconApplyColorScheme(open_circle_proto);
175 exclaim_mark = IconApplyColorScheme(exclaim_mark_proto);
176 x_mult = IconApplyColorScheme(x_mult_proto);
177 check_mark = IconApplyColorScheme(check_mark_proto);
178 }
179 }
180
181 const wxBitmap trashbin_proto;
182 const wxBitmap settings_proto;
183 const wxBitmap filled_circle_proto;
184 const wxBitmap open_circle_proto;
185 const wxBitmap exclaim_mark_proto;
186 const wxBitmap x_mult_proto;
187 const wxBitmap check_mark_proto;
188
189 wxBitmap trashbin;
190 wxBitmap settings;
191 wxBitmap filled_circle;
192 wxBitmap open_circle;
193 wxBitmap exclaim_mark;
194 wxBitmap x_mult;
195 wxBitmap check_mark;
196};
197
199class BitmapCellRenderer : public wxGridCellRenderer {
200public:
201 BitmapCellRenderer(const wxBitmap& bitmap, ColorScheme cs)
202 : status(ConnState::Disabled), m_bitmap(bitmap), m_cs(cs) {}
203
204 // Update the bitmap dynamically
205 void SetBitmap(const wxBitmap& bitmap) { m_bitmap = bitmap; }
206
207 void Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, const wxRect& rect,
208 int row, int col, bool isSelected) override {
209 dc.SetBrush(wxBrush(GetGlobalColor("DILG2")));
210 if ((m_cs != GLOBAL_COLOR_SCHEME_DAY) && (m_cs != GLOBAL_COLOR_SCHEME_RGB))
211 dc.SetBrush(wxBrush(GetDialogColor(DLG_BACKGROUND)));
212 if (IsWindows()) dc.SetBrush(wxBrush(GetGlobalColor("DILG1")));
213 dc.DrawRectangle(rect);
214
215 // Draw the bitmap centered in the cell
216 dc.DrawBitmap(m_bitmap, rect.x + (rect.width - m_bitmap.GetWidth()) / 2,
217 rect.y + (rect.height - m_bitmap.GetHeight()) / 2, true);
218 }
219
220 wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row,
221 int col) override {
222 // Return the size of the bitmap as the best size for the cell
223 return wxSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
224 }
225
226 BitmapCellRenderer* Clone() const override {
227 return new BitmapCellRenderer(m_bitmap, m_cs);
228 }
229 ConnState status;
230
231private:
232 wxBitmap m_bitmap;
233 ColorScheme m_cs;
234};
235
238class BitmapEnableCellRenderer : public wxGridCellBoolRenderer {
239public:
240 BitmapEnableCellRenderer(int _size, ColorScheme cs) : size(_size), m_cs(cs) {}
241
242 BitmapEnableCellRenderer* Clone() const override {
243 return new BitmapEnableCellRenderer(size, m_cs);
244 }
245
246 wxSize GetBestSize(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc, int row,
247 int col) override {
248 return wxSize(size, size);
249 }
250
251private:
252 int size;
253 ColorScheme m_cs;
254};
255
258public:
259 ConnCompare(int col) : m_col(col) {}
260
261 bool operator()(const ConnectionParams* p1, const ConnectionParams* p2) {
262 switch (m_col) {
263 case 0:
264 return static_cast<int>(p1->bEnabled) > static_cast<int>(p2->bEnabled);
265 case 1:
266 return p1->GetCommProtocol() < p2->GetCommProtocol();
267 case 2:
268 return p1->GetIOTypeValueStr() < p2->GetIOTypeValueStr();
269 case 3:
270 return p1->GetStrippedDSPort() < p2->GetStrippedDSPort();
271 default:
272 return false;
273 }
274 }
275
276private:
277 const int m_col;
278};
279
284public:
288 virtual ~ApplyCancel() = default;
289
291 virtual void Apply() = 0;
292
296 virtual void Cancel() = 0;
297};
298
300class AddConnectionButton : public wxButton {
301public:
303 wxWindow* parent, EventVar& evt_add_connection,
304 std::function<void(ConnectionParams* p, bool editing)> _start_edit_conn)
305 : wxButton(parent, wxID_ANY, _("Add new connection...")),
306 m_evt_add_connection(evt_add_connection),
307 m_start_edit_conn(_start_edit_conn) {
308 Bind(wxEVT_COMMAND_BUTTON_CLICKED,
309 [&](wxCommandEvent& ev) { OnAddConnection(); });
310 }
311
312private:
313 void OnAddConnection() { m_start_edit_conn(nullptr, false); }
314
315 EventVar& m_evt_add_connection;
316 std::function<void(ConnectionParams* p, bool editing)> m_start_edit_conn;
317};
318
320class Connections : public wxGrid {
321public:
323 wxWindow* parent, const std::vector<ConnectionParams*>& connections,
324 EventVar& on_conn_update,
325 std::function<void(ConnectionParams* p, bool editing)> on_edit_conn)
326 : wxGrid(parent, wxID_ANY),
327 m_connections(connections),
328 m_on_conn_delete(on_conn_update),
329 m_last_tooltip_cell(100),
330 m_icons(parent),
331 m_on_edit_conn(on_edit_conn) {
332 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
333 SetTable(new wxGridStringTable(), false);
334 GetTable()->AppendCols(8);
335 HideCol(7);
336 if (IsAndroid()) {
337 SetDefaultRowSize(wxWindow::GetCharHeight() * 2);
338 SetColLabelSize(wxWindow::GetCharHeight() * 2);
339 }
340 static const std::array<wxString, 7> headers = {
341 "", _("Protocol") + " ", _("In/Out"), _("Data port"), _("Status"), "",
342 ""};
343 int ic = 0;
344 for (auto hdr = headers.begin(); hdr != headers.end(); hdr++, ic++) {
345 SetColLabelValue(static_cast<int>(hdr - headers.begin()), *hdr);
346 int col_width = (*hdr).Length() * GetCharWidth();
347 col_width = wxMax(col_width, 6 * GetCharWidth());
348 header_column_widths[ic] = col_width;
349 SetColSize(ic, col_width);
350 }
351
352 if (IsWindows()) {
353 SetLabelBackgroundColour(GetGlobalColor("DILG1"));
354 SetLabelTextColour(GetGlobalColor("DILG3"));
355 }
356 HideRowLabels();
357 SetColAttributes(parent);
358
359 ReloadGrid(connections);
360 DisableDragColSize();
361 DisableDragRowSize();
362 wxWindow::Show(GetNumberRows() > 0);
363
364 GetGridWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& ev) {
365 OnMouseMove(ev);
366 ev.Skip();
367 });
368 GetGridWindow()->Bind(wxEVT_MOUSEWHEEL,
369 [&](const wxMouseEvent& ev) { OnWheel(ev); });
370 // wxGridEvent.GetCol() and GetRow() are not const until wxWidgets 3.2
371 Bind(wxEVT_GRID_LABEL_LEFT_CLICK,
372 [&](wxGridEvent& ev) { HandleSort(ev.GetCol()); });
373 Bind(wxEVT_GRID_CELL_LEFT_CLICK,
374 [&](wxGridEvent& ev) { OnClickCell(ev.GetRow(), ev.GetCol()); });
375 Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) {
376 SetColAttributes(static_cast<wxWindow*>(ev.GetEventObject()));
377 ev.Skip();
378 });
379 conn_change_lstnr.Init(
380 m_conn_states.evt_conn_status_change,
381 [&](ObservedEvt&) { OnConnectionChange(m_connections); });
382 }
383 void SetColorScheme(ColorScheme cs) {
384 m_cs = cs;
385 m_icons.SetColorScheme(cs);
386 ReloadGrid(m_connections);
387 }
388
389 wxSize GetEstimatedSize() {
390 int rs = 0;
391 for (auto s : header_column_widths) rs += s;
392 return wxSize(rs, -1);
393 }
394
396 void OnWheel(const wxMouseEvent& ev) {
397 auto w = static_cast<wxScrolledWindow*>(
398 wxWindow::FindWindowByName(TopScrollWindowName));
399 assert(w && "No TopScroll window found");
400 int xpos;
401 int ypos;
402 w->GetViewStart(&xpos, &ypos);
403 int x;
404 int y;
405 w->GetScrollPixelsPerUnit(&x, &y);
406 // Not sure where the factor "4" comes from...
407 int dir = ev.GetWheelRotation();
408 w->Scroll(-1, ypos - dir / y / 4);
409 }
410
412 void ReloadGrid(const std::vector<ConnectionParams*>& connections) {
413 ClearGrid();
414 m_renderer_status_vector.clear();
415
416 for (auto it = connections.begin(); it != connections.end(); it++) {
417 auto row = static_cast<int>(it - connections.begin());
418 EnsureRows(row);
419 SetCellValue(row, 0, (*it)->bEnabled ? "1" : "");
420 if ((*it)->bEnabled)
421 m_tooltips[row][0] = _("Enabled, click to disable");
422 else
423 m_tooltips[row][0] = _("Disabled, click to enable");
424 std::string protocol = NavAddr::BusToString((*it)->GetCommProtocol());
425 SetCellValue(row, 1, protocol);
426 SetCellValue(row, 2, (*it)->GetIOTypeValueStr());
427 SetCellValue(row, 3, (*it)->GetStrippedDSPort());
428 m_tooltips[row][3] = (*it)->UserComment;
429 SetCellRenderer(row, 5, new BitmapCellRenderer(m_icons.settings, m_cs));
430 m_tooltips[row][5] = _("Edit connection");
431 SetCellRenderer(row, 6, new BitmapCellRenderer(m_icons.trashbin, m_cs));
432 m_tooltips[row][6] = _("Delete connection");
433 SetCellValue(row, 7, (*it)->GetKey());
434
435 auto stat_renderer = new BitmapCellRenderer(m_icons.filled_circle, m_cs);
436 stat_renderer->status = ConnState::Disabled;
437 m_renderer_status_vector.push_back(stat_renderer);
438 SetCellRenderer(row, 4, stat_renderer);
439 if (IsAndroid()) {
440 wxString sp(protocol);
441 int ssize = sp.Length() * wxWindow::GetCharWidth();
442 header_column_widths[1] = wxMax(header_column_widths[1], ssize);
443 ssize = (*it)->GetIOTypeValueStr().Length() * wxWindow::GetCharWidth();
444 header_column_widths[2] = wxMax(header_column_widths[2], ssize);
445 sp = wxString((*it)->GetStrippedDSPort());
446 ssize = sp.Length() * wxWindow::GetCharWidth();
447 header_column_widths[3] = wxMax(header_column_widths[3], ssize);
448 }
449 }
450 OnConnectionChange(m_connections);
451
452 if (IsAndroid()) {
453 int ic = 0;
454 for (auto val : header_column_widths) {
455 SetColSize(ic, val);
456 ic++;
457 }
458 } else
459 AutoSize();
460 }
461
464 public:
465 ConnStateCompare(Connections* connections) : m_conns(connections) {}
466 bool operator()(const ConnectionParams* p1, const ConnectionParams* p2) {
467 int row1 = m_conns->FindConnectionIndex(p1);
468 int row2 = m_conns->FindConnectionIndex(p2);
469 if (row1 == -1 && row2 == -1) return false;
470 if (row1 == -1) return false;
471 if (row2 == -1) return true;
472 int v1 = static_cast<int>(m_conns->GetCellValue(row1, 4)[0]);
473 int v2 = static_cast<int>(m_conns->GetCellValue(row2, 4)[0]);
474 return v1 < v2;
475 }
476 Connections* m_conns;
477 };
478
484 using namespace std;
485 auto key = cp->GetKey();
486 auto found = find_if(
487 m_connections.begin(), m_connections.end(),
488 [key](const ConnectionParams* cp) { return cp->GetKey() == key; });
489 if (found == m_connections.end()) return -1;
490 return static_cast<int>(found - m_connections.begin());
491 }
492
493private:
498 ConnectionParams* FindRowConnection(int row) const {
499 auto found = find_if(m_connections.begin(), m_connections.end(),
500 [&](const ConnectionParams* p) {
501 return GetCellValue(row, 7) == p->GetKey();
502 });
503 return found != m_connections.end() ? *found : nullptr;
504 }
505
510 void EnsureRows(size_t rows) {
511 while (m_tooltips.size() <= rows)
512 m_tooltips.push_back(std::vector<std::string>(7));
513 while (GetNumberRows() <= static_cast<int>(rows)) AppendRows(1, false);
514 }
515
517 void SetColAttributes(const wxWindow* parent) {
518 if (IsWindows()) {
519 // Set all cells to global color scheme
520 SetDefaultCellBackgroundColour(GetGlobalColor("DILG1"));
521 }
522 auto enable_attr = new wxGridCellAttr();
523 enable_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
524
525 if (IsAndroid())
526 enable_attr->SetRenderer(
527 new BitmapEnableCellRenderer(wxWindow::GetCharWidth() * 3 / 2, m_cs));
528 else
529 enable_attr->SetRenderer(new wxGridCellBoolRenderer());
530
531 enable_attr->SetEditor(new wxGridCellBoolEditor());
532 if (IsWindows()) enable_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
533 SetColAttr(0, enable_attr);
534
535 auto protocol_attr = new wxGridCellAttr();
536 protocol_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
537 protocol_attr->SetReadOnly(true);
538 if (IsWindows())
539 protocol_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
540 SetColAttr(1, protocol_attr);
541
542 auto in_out_attr = new wxGridCellAttr();
543 in_out_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
544 in_out_attr->SetReadOnly(true);
545 if (IsWindows()) in_out_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
546 SetColAttr(2, in_out_attr);
547
548 auto port_attr = new wxGridCellAttr();
549 port_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
550 port_attr->SetReadOnly(true);
551 if (IsWindows()) port_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
552 SetColAttr(3, port_attr);
553
554 auto status_attr = new wxGridCellAttr();
555 status_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
556 status_attr->SetReadOnly(true);
557 status_attr->SetFont(parent->GetFont().Scale(1.3));
558 if (IsWindows()) status_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
559 SetColAttr(4, status_attr);
560
561 auto edit_attr = new wxGridCellAttr();
562 edit_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
563 edit_attr->SetFont(parent->GetFont().Scale(1.3));
564 edit_attr->SetReadOnly(true);
565 if (IsWindows()) edit_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
566 SetColAttr(5, edit_attr);
567
568 auto delete_attr = new wxGridCellAttr();
569 delete_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
570 delete_attr->SetFont(parent->GetFont().Scale(1.3));
571 delete_attr->SetTextColour(*wxRED);
572 delete_attr->SetReadOnly(true);
573 if (IsWindows()) delete_attr->SetBackgroundColour(GetGlobalColor("DILG1"));
574 SetColAttr(6, delete_attr);
575 }
576
578 void OnClickCell(int row, int col) {
579 if (col == 0)
580 HandleEnable(row);
581 else if (col == 5) {
582 HandleEdit(row);
583 } else if (col == 6) {
584 HandleDelete(row);
585 }
586 }
587
589 void OnMouseMove(const wxMouseEvent& ev) {
590 wxPoint pt = ev.GetPosition();
591 int row = YToRow(pt.y);
592 int col = XToCol(pt.x);
593 if (col < 0 || col >= 7 || row < 0 || row >= GetNumberRows()) return;
594 if (row * 7 + col == m_last_tooltip_cell) return;
595 m_last_tooltip_cell = row * 7 + col;
596 GetGridWindow()->SetToolTip(m_tooltips[row][col]);
597 }
598
600 void OnConnectionChange(const std::vector<ConnectionParams*>& connections) {
601 bool refresh_needed = false;
602 for (auto it = connections.begin(); it != connections.end(); it++) {
603 ConnState state = m_conn_states.GetDriverState(
604 (*it)->GetCommProtocol(), (*it)->GetStrippedDSPort());
605 if (!(*it)->bEnabled) state = ConnState::Disabled;
606 auto row = static_cast<int>(it - connections.begin());
607 EnsureRows(row);
608 if (m_renderer_status_vector.size() < static_cast<size_t>(row + 1))
609 continue;
610 switch (state) {
611 case ConnState::Disabled:
612 if (m_renderer_status_vector[row]->status != ConnState::Disabled) {
613 m_renderer_status_vector[row]->SetBitmap(m_icons.filled_circle);
614 m_renderer_status_vector[row]->status = ConnState::Disabled;
615 refresh_needed = true;
616 }
617 m_tooltips[row][4] = _("Disabled");
618 break;
619 case ConnState::NoStats:
620 if (m_renderer_status_vector[row]->status != ConnState::NoStats) {
621 m_renderer_status_vector[row]->SetBitmap(m_icons.open_circle);
622 m_renderer_status_vector[row]->status = ConnState::NoStats;
623 refresh_needed = true;
624 }
625 m_tooltips[row][4] = _("No driver statistics available");
626 break;
627 case ConnState::NoData:
628 if (m_renderer_status_vector[row]->status != ConnState::NoData) {
629 m_renderer_status_vector[row]->SetBitmap(m_icons.exclaim_mark);
630 m_renderer_status_vector[row]->status = ConnState::NoData;
631 refresh_needed = true;
632 }
633 m_tooltips[row][4] = _("No data flowing through connection");
634 break;
635 case ConnState::Unavailable:
636 if (m_renderer_status_vector[row]->status != ConnState::Unavailable) {
637 m_renderer_status_vector[row]->SetBitmap(m_icons.x_mult);
638 m_renderer_status_vector[row]->status = ConnState::Unavailable;
639 refresh_needed = true;
640 }
641 m_tooltips[row][4] = _("The device is unavailable");
642 break;
643 case ConnState::Ok:
644 if (m_renderer_status_vector[row]->status != ConnState::Ok) {
645 m_renderer_status_vector[row]->SetBitmap(m_icons.check_mark);
646 m_renderer_status_vector[row]->status = ConnState::Ok;
647 refresh_needed = true;
648 }
649 m_tooltips[row][4] = _("Data is flowing");
650 break;
651 }
652 }
653 if (refresh_needed) ForceRefresh();
654 }
655
657 void SetSortingColumn(int col) {
658 if (GetSortingColumn() != wxNOT_FOUND) {
659 int old_col = GetSortingColumn();
660 auto label = GetColLabelValue(old_col);
661 if (label[0] == kUtfArrowDown[0])
662 SetColLabelValue(old_col, label.substr(2));
663 }
664 auto label = GetColLabelValue(col);
665 if (label[0] != kUtfArrowDown[0])
666 SetColLabelValue(col, kUtfArrowDown + " " + label);
667 wxGrid::SetSortingColumn(col);
668 Fit();
669 }
670
672 void HandleSort(int col) {
673 if (col > 4) return;
674 auto& params = TheConnectionParams();
675 if (col < 4)
676 std::sort(params.begin(), params.end(), ConnCompare(col));
677 else // col == 4
678 std::sort(params.begin(), params.end(), ConnStateCompare(this));
679 ReloadGrid(TheConnectionParams());
680 SetSortingColumn(col);
681 }
682
684 void HandleEnable(int row) {
685 ConnectionParams* cp = FindRowConnection(row);
686 if (!cp) return;
687 cp->bEnabled = !cp->bEnabled;
688 cp->b_IsSetup = FALSE; // trigger a rebuild/takedown of the connection
689 SetCellValue(row, 0, cp->bEnabled ? "1" : "");
690 if (cp->bEnabled)
691 m_tooltips[row][0] = _("Enabled, click to disable");
692 else
693 m_tooltips[row][0] = _("Disabled, click to enable");
694 DriverStats stats;
695 stats.driver_iface = cp->GetStrippedDSPort();
696 stats.driver_bus = cp->GetCommProtocol();
697 m_conn_states.HandleDriverStats(stats);
698 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
699 if (cp->bEnabled) MakeCommDriver(cp);
700 cp->b_IsSetup = true;
701 if (!cp->bEnabled) {
702 SetCellValue(row, 4, kUtfFilledCircle);
703 // ForceRefresh() apparently broken, see #4648
704 ReloadGrid(TheConnectionParams());
705 }
706 }
707
709 void HandleEdit(int row) {
710 if (ConnectionParams* cp = FindRowConnection(row)) {
711 Show(GetNumberRows() > 0);
712 m_on_edit_conn(cp, true);
713 }
714 }
715
717 void HandleDelete(int row) {
718 ConnectionParams* cp = FindRowConnection(row);
719 auto found = std::find(m_connections.begin(), m_connections.end(), cp);
720 if (found != m_connections.end()) {
721 std::stringstream ss;
722 ss << _("Ok to delete connection on ") << (*found)->GetStrippedDSPort();
723 int rcode = OCPNMessageBox(this, ss.str(), _("Delete connection?"),
724 wxOK | wxCANCEL);
725 if (rcode != wxID_OK && rcode != wxID_YES) return;
726 delete (*found)->m_optionsPanel;
727 StopAndRemoveCommDriver((*found)->GetStrippedDSPort(),
728 (*found)->GetCommProtocol());
729 TheConnectionParams().erase(found);
730 if (GetNumberRows() > static_cast<int>(m_connections.size()))
731 DeleteRows(GetNumberRows() - 1);
732 m_on_conn_delete.Notify();
733 }
734 }
735
736 ObsListener conn_change_lstnr;
737 std::vector<std::vector<std::string>> m_tooltips;
738 ConnStates m_conn_states;
739 const std::vector<ConnectionParams*>& m_connections;
740 EventVar& m_on_conn_delete;
741 int m_last_tooltip_cell;
742 StdIcons m_icons;
743 std::vector<BitmapCellRenderer*> m_renderer_status_vector;
744 std::array<int, 7> header_column_widths;
745 ColorScheme m_cs;
746 std::function<void(ConnectionParams* p, bool editing)> m_on_edit_conn;
747};
748
750class GeneralPanel : public wxPanel {
751public:
752 explicit GeneralPanel(wxWindow* parent, wxSize max_size)
753 : wxPanel(parent, wxID_ANY) {
754 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, _("General"));
755 SetSizer(sizer);
756 auto flags = wxSizerFlags().Border();
757 m_upload_options = new UploadOptionsChoice(this);
758 sizer->Add(m_upload_options, flags);
759 sizer->Add(new PrioritiesBtn(this), flags);
760 wxWindow::SetMaxSize(max_size);
761 }
762 void SetColorScheme(ColorScheme cs) {
763 DimeControl(m_upload_options);
764 Refresh();
765 }
766
767private:
769 class PrioritiesBtn : public wxButton {
770 public:
771 PrioritiesBtn(wxWindow* parent)
772 : wxButton(parent, wxID_ANY, _("Adjust Nav data priorities...")) {
773 Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {
774 PriorityDlg dialog(this->GetParent());
775 dialog.ShowModal();
776 });
777 }
778 };
779
781 class UploadOptionsChoice : public wxRadioBox, public ApplyCancel {
782 public:
783 explicit UploadOptionsChoice(wxWindow* parent) : wxRadioBox() {
784 wxArrayString wx_choices;
785 for (auto& c : choices) wx_choices.Add(c);
786 Create(parent, wxID_ANY, _("Upload Format"), wxDefaultPosition,
787 wxDefaultSize, wx_choices, 0, wxRA_SPECIFY_ROWS);
788 DimeControl(this);
789 UploadOptionsChoice::Cancel();
790 }
791
792 void Cancel() override {
793 if (g_bGarminHostUpload)
794 SetSelection(1);
795 else if (g_GPS_Ident == "FurunoGP3X")
796 SetSelection(2);
797 else
798 SetSelection(0);
799 }
800
801 void Apply() override {
802 switch (GetSelection()) {
803 case 0:
804 g_bGarminHostUpload = false;
805 g_GPS_Ident = "Generic";
806 break;
807 case 1:
808 g_bGarminHostUpload = true;
809 g_GPS_Ident = "Generic";
810 break;
811 case 2:
812 g_bGarminHostUpload = false;
813 g_GPS_Ident = "FurunoGP3X";
814 break;
815 default:
816 assert(false && "Invalid upload case option");
817 }
818 }
819
820 const std::array<wxString, 3> choices = {
821 _("Generic NMEA 0183"), _("Garmin Host mode"), _("Furuno GP4X")};
822 };
823
824 UploadOptionsChoice* m_upload_options;
825};
826
828class ShowAdvanced : public wxStaticText {
829public:
830 ShowAdvanced(wxWindow* parent, std::function<void(bool)> on_toggle)
831 : wxStaticText(parent, wxID_ANY, ""),
832 m_on_toggle(on_toggle),
833 m_show_advanced(true) {
834 Toggle();
835 Bind(wxEVT_LEFT_DOWN, [&](wxMouseEvent& ev) { Toggle(); });
836 }
837
838private:
839 wxString m_label = _("Advanced ");
840 std::function<void(bool)> m_on_toggle;
841 bool m_show_advanced;
842
843 void Toggle() {
844 m_show_advanced = !m_show_advanced;
845 m_label = _("Advanced ");
846 m_label += (m_show_advanced ? kUtfArrowDown : kUtfArrowRight);
847 SetLabelText(m_label);
848 m_on_toggle(m_show_advanced);
849 }
850};
851
853class AdvancedPanel : public wxPanel {
854public:
855 explicit AdvancedPanel(wxWindow* parent, wxSize max_size)
856 : wxPanel(parent, wxID_ANY) {
857 auto sizer = new wxStaticBoxSizer(wxVERTICAL, this, "");
858 sizer->Add(new BearingsCheckbox(this), wxSizerFlags().Expand());
859 sizer->Add(new NmeaFilterRow(this), wxSizerFlags().Expand());
860 sizer->Add(new TalkerIdRow(this), wxSizerFlags().Expand());
861 sizer->Add(new NetmaskRow(this), wxSizerFlags().Expand());
862 SetSizer(sizer);
863 wxWindow::SetMaxSize(max_size);
864 }
865
866private:
868 class BearingsCheckbox : public wxCheckBox, public ApplyCancel {
869 public:
870 BearingsCheckbox(wxWindow* parent)
871 : wxCheckBox(parent, wxID_ANY,
872 _("Use magnetic bearing in output sentence APB")) {
873 wxCheckBox::SetValue(g_bMagneticAPB);
874 }
875
876 void Apply() override { g_bMagneticAPB = GetValue(); }
877 void Cancel() override { SetValue(g_bMagneticAPB); }
878 };
879
881 class NmeaFilterRow : public wxPanel, public ApplyCancel {
882 wxCheckBox* checkbox;
883 wxTextCtrl* filter_period;
884
885 public:
886 NmeaFilterRow(wxWindow* parent) : wxPanel(parent) {
887 auto hbox = new wxBoxSizer(wxHORIZONTAL);
888 checkbox = new wxCheckBox(
889 this, wxID_ANY,
890 _("Filter NMEA course and speed data. Filter period: "));
891 checkbox->SetValue(g_bfilter_cogsog);
892 hbox->Add(checkbox, wxSizerFlags().Align(wxALIGN_CENTRE));
893 filter_period =
894 new wxTextCtrl(this, wxID_ANY, "", wxDefaultPosition,
895 wxSize(50, 3 * wxWindow::GetCharWidth()), 0);
896 filter_period->SetValue(std::to_string(g_COGFilterSec));
897 hbox->Add(filter_period, wxSizerFlags().Border());
898 SetSizer(hbox);
899 NmeaFilterRow::Cancel();
900 }
901
902 void Apply() override {
903 std::stringstream ss;
904 ss << filter_period->GetValue();
905 ss >> g_COGFilterSec;
906 g_bfilter_cogsog = checkbox->GetValue();
907 }
908
909 void Cancel() override {
910 std::stringstream ss;
911 ss << g_COGFilterSec;
912 filter_period->SetValue(ss.str());
913 checkbox->SetValue(g_bfilter_cogsog);
914 }
915 };
916
918 class TalkerIdRow : public wxPanel, public ApplyCancel {
919 wxTextCtrl* text_ctrl;
920
921 public:
922 TalkerIdRow(wxWindow* parent) : wxPanel(parent) {
923 auto hbox = new wxBoxSizer(wxHORIZONTAL);
924 hbox->Add(new wxStaticText(this, wxID_ANY, _("NMEA 0183 Talker Id: ")),
925 wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border());
926 text_ctrl = new wxTextCtrl(
927 this, wxID_ANY, "", wxDefaultPosition,
928 wxSize(4 * wxWindow::GetCharWidth(), 3 * wxWindow::GetCharWidth()));
929 text_ctrl->SetToolTip(
930 _("Enter a two-letter talker ID to override the default ID in NMEA "
931 "sentences generated by OpenCPN (e.g., GP, HC, EC). This affects "
932 "only sentences created by OpenCPN, not those forwarded from other "
933 "devices."));
934 text_ctrl->SetValue(g_TalkerIdText);
935 hbox->Add(text_ctrl, wxSizerFlags().Border());
936 SetSizer(hbox);
937 TalkerIdRow::Cancel();
938 }
939
940 void Apply() override { g_TalkerIdText = text_ctrl->GetValue(); }
941 void Cancel() override { text_ctrl->SetValue(g_TalkerIdText); }
942 };
943
945 class NetmaskRow : public wxPanel, public ApplyCancel {
946 public:
947 NetmaskRow(wxWindow* parent)
948 : wxPanel(parent),
949 m_spin_ctrl(new wxSpinCtrl(this, wxID_ANY)),
950 m_text(new wxStaticText(this, wxID_ANY, "")) {
951 m_spin_ctrl->SetRange(8, 32);
952 auto hbox = new wxBoxSizer(wxHORIZONTAL);
953 auto flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();
954 hbox->Add(new wxStaticText(this, wxID_ANY, _("Netmask: ")), flags);
955 hbox->Add(m_text, flags);
956 hbox->Add(new wxStaticText(this, wxID_ANY, _("length (bits): ")), flags);
957 hbox->Add(m_spin_ctrl, flags);
958 SetSizer(hbox);
959 NetmaskRow::Cancel();
960
961 Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev) {
962 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
963 Layout();
964 });
965 }
966
967 void Apply() override { g_netmask_bits = m_spin_ctrl->GetValue(); }
968
969 void Cancel() override {
970 m_spin_ctrl->SetValue(g_netmask_bits);
971 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
972 Layout();
973 }
974
975 private:
976 wxSpinCtrl* m_spin_ctrl;
977 wxStaticText* m_text;
978 };
979};
980
982class TopPanel : public wxPanel {
983public:
984 TopPanel(wxWindow* parent, const std::vector<ConnectionParams*>& connections,
985 EventVar& evt_add_connection,
986 std::function<void(ConnectionParams* p, bool editing)> on_edit_conn)
987 : wxPanel(parent, wxID_ANY),
988 m_evt_add_connection(evt_add_connection),
989 m_connections(connections) {
990 auto vbox = new wxBoxSizer(wxVERTICAL);
991 auto conn_grid = new Connections(this, m_connections, m_evt_add_connection,
992 on_edit_conn);
993 wxSize panel_max_size(conn_grid->GetEstimatedSize());
994 vbox->AddSpacer(wxWindow::GetCharHeight());
995 auto conn_flags = wxSizerFlags().Border();
996 if (IsAndroid()) conn_flags = wxSizerFlags().Border().Expand();
997 vbox->Add(conn_grid, conn_flags);
998 vbox->Add(new AddConnectionButton(this, m_evt_add_connection, on_edit_conn),
999 wxSizerFlags().Border());
1000 vbox->Add(0, wxWindow::GetCharHeight()); // Expanding spacer
1001 auto panel_flags =
1002 wxSizerFlags().Border(wxLEFT | wxDOWN | wxRIGHT).Expand();
1003 m_general_panel = new GeneralPanel(this, panel_max_size);
1004 vbox->Add(m_general_panel, panel_flags);
1005
1006 auto advanced_panel = new AdvancedPanel(this, panel_max_size);
1007 m_advanced_panel = advanced_panel;
1008 auto on_toggle = [&, advanced_panel, vbox](bool show) {
1009 advanced_panel->Show(show);
1010 vbox->SetSizeHints(this);
1011 vbox->Fit(this);
1012 };
1013 vbox->Add(new ShowAdvanced(this, on_toggle), panel_flags);
1014 vbox->Add(advanced_panel, panel_flags.ReserveSpaceEvenIfHidden());
1015
1016 SetSizer(vbox);
1017 vbox->SetSizeHints(this);
1018 vbox->Fit(this);
1019 wxWindow::Fit();
1020 wxWindow::Show();
1021
1022 auto on_evt_update_connections = [&, conn_grid](ObservedEvt&) {
1023 conn_grid->ReloadGrid(TheConnectionParams());
1024 conn_grid->Show(conn_grid->GetNumberRows() > 0);
1025 Layout();
1026 };
1027 m_add_connection_lstnr.Init(m_evt_add_connection,
1028 on_evt_update_connections);
1029 m_conn_grid = conn_grid;
1030 }
1031
1032 void SetColorScheme(ColorScheme cs) {
1033 m_conn_grid->SetColorScheme(cs);
1034 m_general_panel->SetColorScheme(cs);
1035 }
1036 Connections* GetConnectionsGrid() { return m_conn_grid; }
1037
1038 EventVar& m_evt_add_connection;
1039
1040private:
1041 const std::vector<ConnectionParams*>& m_connections;
1042 ObsListener m_add_connection_lstnr;
1043 Connections* m_conn_grid;
1044 GeneralPanel* m_general_panel;
1045 AdvancedPanel* m_advanced_panel;
1046};
1047
1049class TopScroll : public wxScrolledWindow {
1050public:
1051 TopScroll(wxWindow* parent, const std::vector<ConnectionParams*>& connections,
1052 EventVar& evt_add_connection)
1053 : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
1054 wxVSCROLL | wxHSCROLL, TopScrollWindowName) {
1055 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_ALWAYS);
1056 auto vbox = new wxBoxSizer(wxVERTICAL);
1057 SetSizer(vbox);
1058
1059 auto on_edit_connection = [&](ConnectionParams* p, bool editing) {
1060 if (editing)
1061 HandleEdit(p);
1062 else
1063 HandleNew(p);
1064 };
1065
1066 top_panel =
1067 new TopPanel(this, connections, evt_add_connection, on_edit_connection);
1068 vbox->Add(top_panel, wxSizerFlags(1).Expand());
1069
1070 auto on_edit_click = [&](ConnectionParams* p, bool new_mode,
1071 bool ok_cancel) {
1072 HandleEditFinish(p, new_mode, ok_cancel);
1073 };
1074
1075 m_edit_panel = new ConnectionEditDialog(this, on_edit_click);
1076 m_edit_panel->SetPropsLabel(_("Edit Selected Connection"));
1077 wxWindow* options = wxWindow::FindWindowByName("Options");
1078 assert(options && "Null Options window!");
1079 int fraction = 9;
1080#ifdef ANDROID
1081 fraction = 10;
1082#endif
1083 m_edit_panel->SetSize(
1084 wxSize(options->GetSize().x, options->GetSize().y * fraction / 10));
1085 vbox->Add(m_edit_panel, wxSizerFlags(0).Expand());
1086 m_edit_panel->Hide();
1087
1088 SetScrollRate(0, 10);
1089 if (IsAndroid()) SetScrollRate(1, 1);
1090
1091 SetScrollRate(0, 10);
1092 if (IsAndroid()) SetScrollRate(1, 1);
1093 }
1094
1095 void SetColorScheme(ColorScheme cs) {
1096 if (top_panel) top_panel->SetColorScheme(cs);
1097 }
1098
1099 void HandleEdit(ConnectionParams* p) {
1100 m_edit_panel->SetPropsLabel(_("Edit Selected Connection"));
1101 m_edit_panel->SetNewMode(false);
1102 m_edit_panel->PreloadControls(p);
1103 m_edit_panel->AddOKCancelButtons();
1104 SwitchToEditor();
1105 }
1106
1107 void HandleNew(ConnectionParams* p) {
1108 m_edit_panel->SetPropsLabel(_("Configure new connection"));
1109 m_edit_panel->SetDefaultConnectionParams();
1110 m_edit_panel->SetNewMode(true);
1111 m_edit_panel->AddOKCancelButtons();
1112 SwitchToEditor();
1113 }
1114
1115 void SwitchToEditor() {
1116 top_panel->Hide(); // TopPanel
1117 Scroll(0, 0);
1118 DimeControl(m_edit_panel);
1119 m_edit_panel->Show();
1120 g_options->ShowOKButtons(false);
1121
1122 Layout();
1123 }
1124
1125 void SwitchToGrid() {
1126 g_options->ShowOKButtons(true);
1127 m_edit_panel->Hide();
1128 top_panel->Show();
1129 top_panel->GetConnectionsGrid()->ReloadGrid(TheConnectionParams());
1130 Layout();
1131 Scroll(0, 0);
1132 }
1133
1134 void HandleEditFinish(ConnectionParams* cp_orig, bool new_mode,
1135 bool ok_cancel) {
1136 if (!ok_cancel) {
1137 SwitchToGrid();
1138 return;
1139 }
1140 // OK from EDIT mode
1141 if (!new_mode) {
1142 ConnectionParams* cp_edited = m_edit_panel->GetParamsFromControls();
1143 delete cp_orig->m_optionsPanel;
1144 StopAndRemoveCommDriver(cp_orig->GetStrippedDSPort(),
1145 cp_orig->GetCommProtocol());
1146 int index = top_panel->GetConnectionsGrid()->FindConnectionIndex(cp_orig);
1147 assert(index != -1 && "Cannot look up connection index");
1148 TheConnectionParams()[index] = cp_edited;
1149 cp_edited->b_IsSetup = false; // Trigger new stream
1150 }
1151 // OK from NEW mode
1152 else {
1153 if (ConnectionParams* cp = m_edit_panel->GetParamsFromControls()) {
1154 if (cp->GetValidPort()) {
1155 cp->b_IsSetup = false; // Trigger new stream
1156 TheConnectionParams().push_back(cp);
1157 } else {
1158 wxString msg =
1159 _("Unable to create a connection as configured. "
1160 "Connected port or address was missing.");
1161 auto& noteman = NotificationManager::GetInstance();
1162 noteman.AddNotification(NotificationSeverity::kWarning,
1163 msg.ToStdString(), 60);
1164 }
1165 }
1166 UpdateDatastreams();
1167 top_panel->m_evt_add_connection.Notify();
1168 }
1169
1170 SwitchToGrid();
1171 UpdateDatastreams();
1172 }
1173
1174private:
1175 TopPanel* top_panel;
1176 ConnectionEditDialog* m_edit_panel;
1177};
1178
1181 wxWindow* parent, const std::vector<ConnectionParams*>& connections)
1182 : wxPanel(parent, wxID_ANY), m_connections(connections) {
1183 auto vbox = new wxBoxSizer(wxVERTICAL);
1184 vbox->Add(new TopScroll(this, connections, m_evt_add_connection),
1185 wxSizerFlags(1).Expand());
1186 SetSizer(vbox);
1187 wxWindow::Fit();
1188 wxWindow::Show();
1189};
1190
1191void ConnectionsDlg::OnResize(const wxSize& size) {
1192 auto w = wxWindow::FindWindowByName(TopScrollWindowName);
1193 if (!w) return;
1194 w->SetMinSize(size);
1195 Fit();
1196}
1197
1198void ConnectionsDlg::DoApply(wxWindow* root) {
1199 for (wxWindow* child : root->GetChildren()) {
1200 if (auto widget = dynamic_cast<ApplyCancel*>(child)) widget->Apply();
1201 DoApply(child);
1202 }
1203}
1204
1205void ConnectionsDlg::DoCancel(wxWindow* root) {
1206 for (wxWindow* child : root->GetChildren()) {
1207 if (auto widget = dynamic_cast<ApplyCancel*>(child)) widget->Cancel();
1208 DoCancel(child);
1209 }
1210}
1211
1212void ConnectionsDlg::ApplySettings() { DoApply(this); }
1213
1214void ConnectionsDlg::CancelSettings() { DoCancel(this); }
1215
1216void ConnectionsDlg::SetColorScheme(ColorScheme cs) {
1217 auto w =
1218 static_cast<TopScroll*>(wxWindow::FindWindowByName(TopScrollWindowName));
1219 if (w) w->SetColorScheme(cs);
1220}
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 bitmap in a grid cell.
Custom renderer class for rendering ENABLE in a grid cell.
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()
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.
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.
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.
Provides platform-specific support utilities for OpenCPN.
double GetDisplaySizeMM()
Get the width of the screen in millimeters.
Define an action to be performed when a KeyProvider is notified.
Definition observable.h:247
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Definition observable.h:274
Custom event class for OpenCPN's notification system.
The "Show advanced" text + right/down triangle and handler.
Standard icons bitmaps: settings gear, trash bin, etc.
Top panel: connections grid, "Add new connection", general options.
Top scroll window, adds scrollbars to TopPanel.
Runtime connection/driver state definitions.
Dialog and support code for editing a connection.
The ConnectionsDlg class.
General purpose GUI support.
Class NotificationManager.
Driver statistics report.