258 const std::vector<ConnectionParams*>& connections,
260 : wxGrid(parent, wxID_ANY),
261 m_connections(connections),
262 m_on_conn_delete(on_conn_update),
263 m_last_tooltip_cell(100),
265 ShowScrollbars(wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
266 SetTable(
new wxGridStringTable(),
false);
267 GetTable()->AppendCols(8);
270 SetDefaultRowSize(wxWindow::GetCharHeight() * 2);
271 SetColLabelSize(wxWindow::GetCharHeight() * 2);
273 static const std::array<wxString, 7> headers = {
274 "", _(
"Protocol"), _(
"In/Out"), _(
"Data port"), _(
"Status"),
"",
""};
276 for (
auto hdr = headers.begin(); hdr != headers.end(); hdr++, ic++) {
277 SetColLabelValue(
static_cast<int>(hdr - headers.begin()), *hdr);
278 int col_width = (*hdr).Length() * GetCharWidth();
279 col_width = wxMax(col_width, 6 * GetCharWidth());
280 header_column_widths[ic] = col_width;
281 SetColSize(ic, col_width);
285 SetLabelBackgroundColour(GetGlobalColor(
"DILG1"));
286 SetLabelTextColour(GetGlobalColor(
"DILG3"));
289 SetColAttributes(parent);
292 DisableDragColSize();
293 DisableDragRowSize();
294 wxWindow::Show(GetNumberRows() > 0);
296 GetGridWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& ev) {
300 GetGridWindow()->Bind(wxEVT_MOUSEWHEEL,
301 [&](
const wxMouseEvent& ev) {
OnWheel(ev); });
303 Bind(wxEVT_GRID_LABEL_LEFT_CLICK,
304 [&](wxGridEvent& ev) { HandleSort(ev.GetCol()); });
305 Bind(wxEVT_GRID_CELL_LEFT_CLICK,
306 [&](wxGridEvent& ev) { OnClickCell(ev.GetRow(), ev.GetCol()); });
307 Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) {
308 SetColAttributes(
static_cast<wxWindow*
>(ev.GetEventObject()));
311 conn_change_lstnr.
Init(
313 [&](
ObservedEvt&) { OnConnectionChange(m_connections); });
316 wxSize GetEstimatedSize() {
318 for (
auto s : header_column_widths) rs += s;
319 return wxSize(rs, -1);
324 auto w =
static_cast<wxScrolledWindow*
>(
325 wxWindow::FindWindowByName(TopScrollWindowName));
326 assert(w &&
"No TopScroll window found");
329 w->GetViewStart(&xpos, &ypos);
332 w->GetScrollPixelsPerUnit(&x, &y);
334 int dir = ev.GetWheelRotation();
335 w->Scroll(-1, ypos - dir / y / 4);
339 void ReloadGrid(
const std::vector<ConnectionParams*>& connections) {
341 m_renderer_status_vector.clear();
343 for (
auto it = connections.begin(); it != connections.end(); it++) {
344 auto row =
static_cast<int>(it - connections.begin());
346 SetCellValue(row, 0, (*it)->bEnabled ?
"1" :
"");
348 m_tooltips[row][0] = _(
"Enabled, click to disable");
350 m_tooltips[row][0] = _(
"Disabled, click to enable");
351 std::string protocol = NavAddr::BusToString((*it)->GetCommProtocol());
352 SetCellValue(row, 1, protocol);
353 SetCellValue(row, 2, (*it)->GetIOTypeValueStr());
354 SetCellValue(row, 3, (*it)->GetStrippedDSPort());
355 m_tooltips[row][3] = (*it)->UserComment;
357 m_tooltips[row][5] = _(
"Edit connection");
359 m_tooltips[row][6] = _(
"Delete connection");
360 SetCellValue(row, 7, (*it)->GetKey());
363 stat_renderer->status = ConnState::Disabled;
364 m_renderer_status_vector.push_back(stat_renderer);
365 SetCellRenderer(row, 4, stat_renderer);
367 wxString sp(protocol);
368 int ssize = sp.Length() * wxWindow::GetCharWidth();
369 header_column_widths[1] = wxMax(header_column_widths[1], ssize);
370 ssize = (*it)->GetIOTypeValueStr().Length() * wxWindow::GetCharWidth();
371 header_column_widths[2] = wxMax(header_column_widths[2], ssize);
372 sp = wxString((*it)->GetStrippedDSPort());
373 ssize = sp.Length() * wxWindow::GetCharWidth();
374 header_column_widths[3] = wxMax(header_column_widths[3], ssize);
377 OnConnectionChange(m_connections);
381 for (
auto val : header_column_widths) {
394 int row1 = m_conns->FindConnectionIndex(p1);
395 int row2 = m_conns->FindConnectionIndex(p2);
396 if (row1 == -1 && row2 == -1)
return false;
397 if (row1 == -1)
return false;
398 if (row2 == -1)
return true;
399 int v1 =
static_cast<int>(m_conns->GetCellValue(row1, 4)[0]);
400 int v2 =
static_cast<int>(m_conns->GetCellValue(row2, 4)[0]);
412 auto found = find_if(m_connections.begin(), m_connections.end(),
414 return GetCellValue(row, 7) == p->GetKey();
416 return found != m_connections.end() ? *found :
nullptr;
426 auto found = find_if(
427 m_connections.begin(), m_connections.end(),
429 if (found == m_connections.end())
return -1;
430 return static_cast<int>(found - m_connections.begin());
437 void EnsureRows(
size_t rows) {
438 while (m_tooltips.size() <= rows)
439 m_tooltips.push_back(std::vector<std::string>(7));
440 while (GetNumberRows() <=
static_cast<int>(rows)) AppendRows(1,
false);
444 void SetColAttributes(
const wxWindow* parent) {
447 SetDefaultCellBackgroundColour(GetGlobalColor(
"DILG1"));
449 auto enable_attr =
new wxGridCellAttr();
450 enable_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
451 enable_attr->SetRenderer(
new wxGridCellBoolRenderer());
452 enable_attr->SetEditor(
new wxGridCellBoolEditor());
453 if (IsWindows()) enable_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
454 SetColAttr(0, enable_attr);
456 auto protocol_attr =
new wxGridCellAttr();
457 protocol_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
458 protocol_attr->SetReadOnly(
true);
460 protocol_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
461 SetColAttr(1, protocol_attr);
463 auto in_out_attr =
new wxGridCellAttr();
464 in_out_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
465 in_out_attr->SetReadOnly(
true);
466 if (IsWindows()) in_out_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
467 SetColAttr(2, in_out_attr);
469 auto port_attr =
new wxGridCellAttr();
470 port_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
471 port_attr->SetReadOnly(
true);
472 if (IsWindows()) port_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
473 SetColAttr(3, port_attr);
475 auto status_attr =
new wxGridCellAttr();
476 status_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
477 status_attr->SetReadOnly(
true);
478 status_attr->SetFont(parent->GetFont().Scale(1.3));
479 if (IsWindows()) status_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
480 SetColAttr(4, status_attr);
482 auto edit_attr =
new wxGridCellAttr();
483 edit_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
484 edit_attr->SetFont(parent->GetFont().Scale(1.3));
485 edit_attr->SetReadOnly(
true);
486 if (IsWindows()) edit_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
487 SetColAttr(5, edit_attr);
489 auto delete_attr =
new wxGridCellAttr();
490 delete_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
491 delete_attr->SetFont(parent->GetFont().Scale(1.3));
492 delete_attr->SetTextColour(*wxRED);
493 delete_attr->SetReadOnly(
true);
494 if (IsWindows()) delete_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
495 SetColAttr(6, delete_attr);
499 void OnClickCell(
int row,
int col) {
504 }
else if (col == 6) {
510 void OnMouseMove(
const wxMouseEvent& ev) {
511 wxPoint pt = ev.GetPosition();
512 int row = YToRow(pt.y);
513 int col = XToCol(pt.x);
514 if (col < 0 || col >= 7 || row < 0 || row >= GetNumberRows())
return;
515 if (row * 7 + col == m_last_tooltip_cell)
return;
516 m_last_tooltip_cell = row * 7 + col;
517 GetGridWindow()->SetToolTip(m_tooltips[row][col]);
521 void OnConnectionChange(
const std::vector<ConnectionParams*>& connections) {
522 bool refresh_needed =
false;
523 for (
auto it = connections.begin(); it != connections.end(); it++) {
524 ConnState state = m_conn_states.GetDriverState(
525 (*it)->GetCommProtocol(), (*it)->GetStrippedDSPort());
526 if (!(*it)->bEnabled) state = ConnState::Disabled;
527 auto row =
static_cast<int>(it - connections.begin());
529 if (m_renderer_status_vector.size() <
static_cast<size_t>(row + 1))
532 case ConnState::Disabled:
533 if (m_renderer_status_vector[row]->status != ConnState::Disabled) {
534 m_renderer_status_vector[row]->SetBitmap(m_icons.filled_circle);
535 m_renderer_status_vector[row]->status = ConnState::Disabled;
536 refresh_needed =
true;
538 m_tooltips[row][4] = _(
"Disabled");
540 case ConnState::NoStats:
541 if (m_renderer_status_vector[row]->status != ConnState::NoStats) {
542 m_renderer_status_vector[row]->SetBitmap(m_icons.open_circle);
543 m_renderer_status_vector[row]->status = ConnState::NoStats;
544 refresh_needed =
true;
546 m_tooltips[row][4] = _(
"No driver statistics available");
548 case ConnState::NoData:
549 if (m_renderer_status_vector[row]->status != ConnState::NoData) {
550 m_renderer_status_vector[row]->SetBitmap(m_icons.exclaim_mark);
551 m_renderer_status_vector[row]->status = ConnState::NoData;
552 refresh_needed =
true;
554 m_tooltips[row][4] = _(
"No data flowing through connection");
556 case ConnState::Unavailable:
557 if (m_renderer_status_vector[row]->status != ConnState::Unavailable) {
558 m_renderer_status_vector[row]->SetBitmap(m_icons.x_mult);
559 m_renderer_status_vector[row]->status = ConnState::Unavailable;
560 refresh_needed =
true;
562 m_tooltips[row][4] = _(
"The device is unavailable");
565 if (m_renderer_status_vector[row]->status != ConnState::Ok) {
566 m_renderer_status_vector[row]->SetBitmap(m_icons.check_mark);
567 m_renderer_status_vector[row]->status = ConnState::Ok;
568 refresh_needed =
true;
570 m_tooltips[row][4] = _(
"Data is flowing");
574 if (refresh_needed) ForceRefresh();
578 void SetSortingColumn(
int col) {
579 if (GetSortingColumn() != wxNOT_FOUND) {
580 int old_col = GetSortingColumn();
581 auto label = GetColLabelValue(old_col);
582 if (label[0] == kUtfArrowDown[0])
583 SetColLabelValue(old_col, label.substr(2));
585 auto label = GetColLabelValue(col);
586 if (label[0] != kUtfArrowDown[0])
587 SetColLabelValue(col, kUtfArrowDown +
" " + label);
588 wxGrid::SetSortingColumn(col);
593 void HandleSort(
int col) {
595 auto& params = TheConnectionParams();
597 std::sort(params.begin(), params.end(),
ConnCompare(col));
599 std::sort(params.begin(), params.end(), ConnStateCompare(
this));
601 SetSortingColumn(col);
605 void HandleEnable(
int row) {
608 cp->bEnabled = !cp->bEnabled;
609 cp->b_IsSetup = FALSE;
610 SetCellValue(row, 0, cp->bEnabled ?
"1" :
"");
612 m_tooltips[row][0] = _(
"Enabled, click to disable");
614 m_tooltips[row][0] = _(
"Disabled, click to enable");
616 stats.driver_iface = cp->GetStrippedDSPort();
617 stats.driver_bus = cp->GetCommProtocol();
618 m_conn_states.HandleDriverStats(stats);
619 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
620 if (cp->bEnabled) MakeCommDriver(cp);
621 cp->b_IsSetup =
true;
623 SetCellValue(row, 4, kUtfFilledCircle);
629 void HandleEdit(
int row) {
632 DimeControl(&dialog);
633 dialog.SetPropsLabel(_(
"Edit Selected Connection"));
634 dialog.PreloadControls(cp);
635 wxWindow*
options = wxWindow::FindWindowByName(
"Options");
636 assert(
options &&
"Null Options window!");
639 Show(GetNumberRows() > 0);
641 auto rv = dialog.ShowModal();
644 delete cp->m_optionsPanel;
645 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
646 int index = FindConnectionIndex(cp);
647 assert(index != -1 &&
"Cannot look up connection index");
648 TheConnectionParams()[index] = cp_edited;
649 cp_edited->b_IsSetup =
false;
657 void HandleDelete(
int row) {
659 auto found = std::find(m_connections.begin(), m_connections.end(), cp);
660 if (found != m_connections.end()) {
661 std::stringstream ss;
662 ss << _(
"Ok to delete connection on ") << (*found)->GetStrippedDSPort();
663 int rcode = OCPNMessageBox(
this, ss.str(), _(
"Delete connection?"),
665 if (rcode != wxID_OK && rcode != wxID_YES)
return;
666 delete (*found)->m_optionsPanel;
667 StopAndRemoveCommDriver((*found)->GetStrippedDSPort(),
668 (*found)->GetCommProtocol());
669 TheConnectionParams().erase(found);
670 if (GetNumberRows() >
static_cast<int>(m_connections.size()))
671 DeleteRows(GetNumberRows() - 1);
672 m_on_conn_delete.
Notify();
677 std::vector<std::vector<std::string>> m_tooltips;
679 const std::vector<ConnectionParams*>& m_connections;
681 int m_last_tooltip_cell;
683 std::vector<BitmapCellRenderer*> m_renderer_status_vector;
684 std::array<int, 7> header_column_widths;
787 : wxPanel(parent, wxID_ANY) {
788 auto sizer =
new wxStaticBoxSizer(wxVERTICAL,
this,
"");
789 sizer->Add(
new BearingsCheckbox(
this), wxSizerFlags().Expand());
790 sizer->Add(
new NmeaFilterRow(
this), wxSizerFlags().Expand());
791 sizer->Add(
new TalkerIdRow(
this), wxSizerFlags().Expand());
792 sizer->Add(
new NetmaskRow(
this), wxSizerFlags().Expand());
794 wxWindow::SetMaxSize(max_size);
799 class BearingsCheckbox :
public wxCheckBox,
public ApplyCancel {
801 BearingsCheckbox(wxWindow* parent)
802 : wxCheckBox(parent, wxID_ANY,
803 _(
"Use magnetic bearing in output sentence APB")) {
804 wxCheckBox::SetValue(g_bMagneticAPB);
807 void Apply()
override { g_bMagneticAPB = GetValue(); }
808 void Cancel()
override { SetValue(g_bMagneticAPB); }
812 class NmeaFilterRow :
public wxPanel,
public ApplyCancel {
813 wxCheckBox* checkbox;
814 wxTextCtrl* filter_period;
817 NmeaFilterRow(wxWindow* parent) : wxPanel(parent) {
818 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
819 checkbox =
new wxCheckBox(
821 _(
"Filter NMEA course and speed data. Filter period: "));
822 checkbox->SetValue(g_bfilter_cogsog);
823 hbox->Add(checkbox, wxSizerFlags().Align(wxALIGN_CENTRE));
825 new wxTextCtrl(
this, wxID_ANY,
"", wxDefaultPosition,
826 wxSize(50, 3 * wxWindow::GetCharWidth()), 0);
827 filter_period->SetValue(std::to_string(g_COGFilterSec));
828 hbox->Add(filter_period, wxSizerFlags().Border());
830 NmeaFilterRow::Cancel();
833 void Apply()
override {
834 std::stringstream ss;
835 ss << filter_period->GetValue();
836 ss >> g_COGFilterSec;
837 g_bfilter_cogsog = checkbox->GetValue();
840 void Cancel()
override {
841 std::stringstream ss;
842 ss << g_COGFilterSec;
843 filter_period->SetValue(ss.str());
844 checkbox->SetValue(g_bfilter_cogsog);
849 class TalkerIdRow :
public wxPanel,
public ApplyCancel {
850 wxTextCtrl* text_ctrl;
853 TalkerIdRow(wxWindow* parent) : wxPanel(parent) {
854 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
855 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"NMEA 0183 Talker Id: ")),
856 wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border());
857 text_ctrl =
new wxTextCtrl(
this, wxID_ANY,
"", wxDefaultPosition,
858 wxSize(50, 3 * wxWindow::GetCharWidth()));
859 text_ctrl->SetToolTip(
860 _(
"Enter a two-letter talker ID to override the default ID in NMEA "
861 "sentences generated by OpenCPN (e.g., GP, HC, EC). This affects "
862 "only sentences created by OpenCPN, not those forwarded from other "
864 text_ctrl->SetValue(g_TalkerIdText);
865 hbox->Add(text_ctrl, wxSizerFlags().Border());
867 TalkerIdRow::Cancel();
870 void Apply()
override { g_TalkerIdText = text_ctrl->GetValue(); }
871 void Cancel()
override { text_ctrl->SetValue(g_TalkerIdText); }
875 class NetmaskRow :
public wxPanel,
public ApplyCancel {
877 NetmaskRow(wxWindow* parent)
879 m_spin_ctrl(
new wxSpinCtrl(
this, wxID_ANY)),
880 m_text(
new wxStaticText(
this, wxID_ANY,
"")) {
881 m_spin_ctrl->SetRange(8, 32);
882 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
883 auto flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();
884 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"Netmask: ")), flags);
885 hbox->Add(m_text, flags);
886 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"length (bits): ")), flags);
887 hbox->Add(m_spin_ctrl, flags);
889 NetmaskRow::Cancel();
891 Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev) {
892 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
897 void Apply()
override { g_netmask_bits = m_spin_ctrl->GetValue(); }
899 void Cancel()
override {
900 m_spin_ctrl->SetValue(g_netmask_bits);
901 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
906 wxSpinCtrl* m_spin_ctrl;
907 wxStaticText* m_text;