152 const std::vector<ConnectionParams*>& connections,
154 : wxGrid(parent, wxID_ANY),
155 m_connections(connections),
156 m_on_conn_delete(on_conn_update),
157 m_last_tooltip_cell(100) {
158 SetTable(
new wxGridStringTable(),
false);
159 GetTable()->AppendCols(8);
161 static const std::array<wxString, 7> headers = {
162 "", _(
"Protocol"), _(
"In/Out"), _(
"Data port"), _(
"Status"),
"",
""};
163 for (
auto hdr = headers.begin(); hdr != headers.end(); hdr++)
164 SetColLabelValue(
static_cast<int>(hdr - headers.begin()), *hdr);
166 SetLabelBackgroundColour(GetGlobalColor(
"DILG1"));
167 SetLabelTextColour(GetGlobalColor(
"DILG3"));
170 SetColAttributes(parent);
172 DisableDragColSize();
173 DisableDragRowSize();
174 wxWindow*
options = wxWindow::FindWindowByName(
"Options");
175 assert(
options &&
"Null Options window!");
176 SetSize(wxSize(
options->GetSize().x,
options->GetSize().y * 8 / 10));
177 wxWindow::Show(GetNumberRows() > 0);
179 GetGridWindow()->Bind(wxEVT_MOTION, [&](wxMouseEvent& ev) {
184 GetGridWindow()->Bind(wxEVT_MOUSEWHEEL, [&](wxMouseEvent& ev) {
189 Bind(wxEVT_GRID_LABEL_LEFT_CLICK,
190 [&](wxGridEvent& ev) { HandleSort(ev.GetCol()); });
191 Bind(wxEVT_GRID_CELL_LEFT_CLICK,
192 [&](wxGridEvent& ev) { OnClickCell(ev.GetRow(), ev.GetCol()); });
193 Bind(wxEVT_PAINT, [&](wxPaintEvent& ev) {
194 SetColAttributes(
static_cast<wxWindow*
>(ev.GetEventObject()));
197 conn_change_lstnr.
Init(
199 [&](
ObservedEvt&) { OnConnectionChange(m_connections); });
202 void OnWheel(wxMouseEvent& ev) {
203 auto p = GetParent();
205 int dir = ev.GetWheelRotation();
207 psw->GetViewStart(&xpos, &ypos);
209 psw->GetScrollPixelsPerUnit(&xsu, &ysu);
211 psw->Scroll(-1, ypos - (dir / ysu) / 4);
215 void ReloadGrid(
const std::vector<ConnectionParams*>& connections) {
217 for (
auto it = connections.begin(); it != connections.end(); it++) {
218 auto row =
static_cast<int>(it - connections.begin());
220 SetCellValue(row, 0, (*it)->bEnabled ?
"1" :
"");
222 m_tooltips[row][0] = _(
"Enabled, click to disable");
224 m_tooltips[row][0] = _(
"Disabled, click to enable");
225 std::string protocol = NavAddr::BusToString((*it)->GetCommProtocol());
226 SetCellValue(row, 1, protocol);
227 SetCellValue(row, 2, (*it)->GetIOTypeValueStr());
228 SetCellValue(row, 3, (*it)->GetStrippedDSPort());
229 m_tooltips[row][3] = (*it)->UserComment;
230 SetCellValue(row, 5, kUtfGear);
231 m_tooltips[row][5] = _(
"Edit connection");
232 SetCellValue(row, 6, kUtfTrashbin);
233 m_tooltips[row][6] = _(
"Delete connection");
234 SetCellValue(row, 7, (*it)->GetKey());
236 OnConnectionChange(m_connections);
240 wxSize GetGridMaxSize()
const {
241 return wxSize(GetCharWidth() * 120,
242 std::min(GetNumberRows() + 3, 10) * 2 * GetCharHeight());
245 wxSize GetGridMinSize()
const {
246 return wxSize(GetCharWidth() * 80,
247 std::min(GetNumberRows() + 3, 6) * 2 * GetCharHeight());
255 int row1 = m_conns->FindConnectionIndex(p1);
256 int row2 = m_conns->FindConnectionIndex(p2);
257 if (row1 == -1 && row2 == -1)
return false;
258 if (row1 == -1)
return false;
259 if (row2 == -1)
return true;
260 int v1 =
static_cast<int>(m_conns->GetCellValue(row1, 4)[0]);
261 int v2 =
static_cast<int>(m_conns->GetCellValue(row2, 4)[0]);
273 auto found = find_if(m_connections.begin(), m_connections.end(),
275 return GetCellValue(row, 7) == p->GetKey();
277 return found != m_connections.end() ? *found :
nullptr;
288 find_if(m_connections.begin(), m_connections.end(),
290 if (found == m_connections.end())
return -1;
291 return static_cast<int>(found - m_connections.begin());
298 void EnsureRows(
size_t rows) {
299 while (m_tooltips.size() <= rows)
300 m_tooltips.push_back(std::vector<std::string>(7));
301 while (GetNumberRows() <=
static_cast<int>(rows)) AppendRows(1,
false);
305 void SetColAttributes(wxWindow* parent) {
308 SetDefaultCellBackgroundColour(GetGlobalColor(
"DILG1"));
310 auto enable_attr =
new wxGridCellAttr();
311 enable_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
312 enable_attr->SetRenderer(
new wxGridCellBoolRenderer());
313 enable_attr->SetEditor(
new wxGridCellBoolEditor());
314 if (IsWindows()) enable_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
315 SetColAttr(0, enable_attr);
317 auto protocol_attr =
new wxGridCellAttr();
318 protocol_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
319 protocol_attr->SetReadOnly(
true);
321 protocol_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
322 SetColAttr(1, protocol_attr);
324 auto in_out_attr =
new wxGridCellAttr();
325 in_out_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
326 in_out_attr->SetReadOnly(
true);
327 if (IsWindows()) in_out_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
328 SetColAttr(2, in_out_attr);
330 auto port_attr =
new wxGridCellAttr();
331 port_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
332 port_attr->SetReadOnly(
true);
333 if (IsWindows()) port_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
334 SetColAttr(3, port_attr);
336 auto status_attr =
new wxGridCellAttr();
337 status_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
338 status_attr->SetReadOnly(
true);
339 status_attr->SetFont(parent->GetFont().Scale(1.3));
340 if (IsWindows()) status_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
341 SetColAttr(4, status_attr);
343 auto edit_attr =
new wxGridCellAttr();
344 edit_attr->SetAlignment(wxALIGN_CENTRE, wxALIGN_CENTRE);
345 edit_attr->SetFont(parent->GetFont().Scale(1.3));
346 edit_attr->SetReadOnly(
true);
347 if (IsWindows()) edit_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
348 SetColAttr(5, edit_attr);
350 auto delete_attr =
new wxGridCellAttr();
351 delete_attr->SetAlignment(wxALIGN_LEFT, wxALIGN_CENTRE);
352 delete_attr->SetFont(parent->GetFont().Scale(1.3));
353 delete_attr->SetTextColour(*wxRED);
354 delete_attr->SetReadOnly(
true);
355 if (IsWindows()) delete_attr->SetBackgroundColour(GetGlobalColor(
"DILG1"));
356 SetColAttr(6, delete_attr);
360 void OnClickCell(
int row,
int col) {
365 }
else if (col == 6) {
370 void OnMouseMove(wxMouseEvent& ev) {
371 wxPoint pt = ev.GetPosition();
372 int row = YToRow(pt.y);
373 int col = XToCol(pt.x);
374 if (col < 0 || col >= 7 || row < 0 || row >= GetNumberRows())
return;
375 if (row * 7 + col == m_last_tooltip_cell)
return;
376 m_last_tooltip_cell = row * 7 + col;
377 GetGridWindow()->SetToolTip(m_tooltips[row][col]);
381 void OnConnectionChange(
const std::vector<ConnectionParams*>& connections) {
382 for (
auto it = connections.begin(); it != connections.end(); it++) {
383 ConnState state = m_conn_states.GetDriverState(
384 (*it)->GetCommProtocol(), (*it)->GetStrippedDSPort());
385 if (!(*it)->bEnabled) state = ConnState::Disabled;
386 auto row =
static_cast<int>(it - connections.begin());
389 case ConnState::Disabled:
390 SetCellValue(row, 4, kUtfFilledCircle);
391 m_tooltips[row][4] = _(
"Disabled");
393 case ConnState::NoStats:
394 SetCellValue(row, 4, kUtfCircle);
395 m_tooltips[row][4] = _(
"No driver statistics available");
397 case ConnState::NoData:
398 SetCellValue(row, 4, kUtfExclamationMark);
399 m_tooltips[row][4] = _(
"No data flowing through connection");
401 case ConnState::Unavailable:
402 SetCellValue(row, 4, kUtfMultiplyX);
403 m_tooltips[row][4] = _(
"The device is unavailable");
406 SetCellValue(row, 4, kUtfCheckmark);
407 m_tooltips[row][4] = _(
"Data is flowing");
413 void SetSortingColumn(
int col) {
414 if (GetSortingColumn() != wxNOT_FOUND) {
415 int old_col = GetSortingColumn();
416 auto label = GetColLabelValue(old_col);
417 if (label[0] == kUtfArrowDown[0])
418 SetColLabelValue(old_col, label.substr(2));
420 auto label = GetColLabelValue(col);
421 if (label[0] != kUtfArrowDown[0])
422 SetColLabelValue(col, kUtfArrowDown +
" " + label);
423 wxGrid::SetSortingColumn(col);
428 void HandleSort(
int col) {
430 auto& params = TheConnectionParams();
432 std::sort(params.begin(), params.end(),
ConnCompare(col));
434 std::sort(params.begin(), params.end(), ConnStateCompare(
this));
436 SetSortingColumn(col);
440 void HandleEnable(
int row) {
443 cp->bEnabled = !cp->bEnabled;
444 cp->b_IsSetup = FALSE;
445 SetCellValue(row, 0, cp->bEnabled ?
"1" :
"");
447 m_tooltips[row][0] = _(
"Enabled, click to disable");
449 m_tooltips[row][0] = _(
"Disabled, click to enable");
451 stats.driver_iface = cp->GetStrippedDSPort();
452 stats.driver_bus = cp->GetCommProtocol();
453 m_conn_states.HandleDriverStats(stats);
454 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
455 if (cp->bEnabled) MakeCommDriver(cp);
456 cp->b_IsSetup =
true;
457 if (!cp->bEnabled) SetCellValue(row, 4, kUtfFilledCircle);
461 void HandleEdit(
int row) {
465 DimeControl(&dialog);
466 dialog.SetPropsLabel(_(
"Edit Selected Connection"));
467 dialog.PreloadControls(cp);
468 wxWindow*
options = wxWindow::FindWindowByName(
"Options");
469 assert(
options &&
"Null Options window!");
472 Show(GetNumberRows() > 0);
474 auto rv = dialog.ShowModal();
477 delete cp->m_optionsPanel;
478 StopAndRemoveCommDriver(cp->GetStrippedDSPort(), cp->GetCommProtocol());
479 int index = FindConnectionIndex(cp);
480 assert(index != -1 &&
"Cannot look up connection index");
481 TheConnectionParams()[index] = cp_edited;
482 cp_edited->b_IsSetup =
false;
490 void HandleDelete(
int row) {
492 auto found = std::find(m_connections.begin(), m_connections.end(), cp);
493 if (found != m_connections.end()) {
494 std::stringstream ss;
495 ss << _(
"Ok to delete connection on ") << (*found)->GetStrippedDSPort();
496 int rcode = OCPNMessageBox(
this, ss.str(), _(
"Delete connection?"),
498 if (rcode != wxID_OK && rcode != wxID_YES)
return;
499 delete (*found)->m_optionsPanel;
500 StopAndRemoveCommDriver((*found)->GetStrippedDSPort(),
501 (*found)->GetCommProtocol());
502 TheConnectionParams().erase(found);
503 if (GetNumberRows() >
static_cast<int>(m_connections.size()))
504 DeleteRows(GetNumberRows() - 1);
505 m_on_conn_delete.
Notify();
510 std::vector<std::vector<std::string>> m_tooltips;
512 const std::vector<ConnectionParams*>& m_connections;
514 int m_last_tooltip_cell;
601 explicit AdvancedPanel(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
602 auto sizer =
new wxStaticBoxSizer(wxVERTICAL,
this,
"");
603 sizer->Add(
new BearingsCheckbox(
this), wxSizerFlags().Expand());
604 sizer->Add(
new NmeaFilterRow(
this), wxSizerFlags().Expand());
605 sizer->Add(
new TalkerIdRow(
this), wxSizerFlags().Expand());
606 sizer->Add(
new NetmaskRow(
this), wxSizerFlags().Expand());
607 sizer->Add(
new PrioritiesBtn(
this), wxSizerFlags().Border());
613 class BearingsCheckbox :
public wxCheckBox,
public ApplyCancel {
615 BearingsCheckbox(wxWindow* parent)
616 : wxCheckBox(parent, wxID_ANY,
617 _(
"Use magnetic bearing in output sentence APB")) {
618 SetValue(g_bMagneticAPB);
619 wxCheckBox::SetValue(g_bMagneticAPB);
622 void Apply()
override { g_bMagneticAPB = GetValue(); }
623 void Cancel()
override { SetValue(g_bMagneticAPB); }
627 class NmeaFilterRow :
public wxPanel,
public ApplyCancel {
628 wxCheckBox* checkbox;
629 wxTextCtrl* filter_period;
632 NmeaFilterRow(wxWindow* parent) : wxPanel(parent) {
633 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
634 checkbox =
new wxCheckBox(
636 _(
"Filter NMEA course and speed data. Filter period: "));
637 checkbox->SetValue(g_bfilter_cogsog);
638 hbox->Add(checkbox, wxSizerFlags().Align(wxALIGN_CENTRE));
640 new wxTextCtrl(
this, wxID_ANY,
"", wxDefaultPosition,
641 wxSize(50, 3 * wxWindow::GetCharWidth()), 0);
642 filter_period->SetValue(std::to_string(g_COGFilterSec));
643 hbox->Add(filter_period, wxSizerFlags().Border());
648 void Apply()
override {
649 std::stringstream ss;
650 ss << filter_period->GetValue();
651 ss >> g_COGFilterSec;
652 g_bfilter_cogsog = checkbox->GetValue();
655 void Cancel()
override {
656 std::stringstream ss;
657 ss << g_COGFilterSec;
658 filter_period->SetValue(ss.str());
659 checkbox->SetValue(g_bfilter_cogsog);
664 class TalkerIdRow :
public wxPanel,
public ApplyCancel {
665 wxTextCtrl* text_ctrl;
668 TalkerIdRow(wxWindow* parent) : wxPanel(parent) {
669 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
670 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"NMEA 0183 Talker Id: ")),
671 wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border());
672 text_ctrl =
new wxTextCtrl(
this, wxID_ANY,
"", wxDefaultPosition,
673 wxSize(50, 3 * wxWindow::GetCharWidth()));
674 text_ctrl->SetValue(g_TalkerIdText);
675 hbox->Add(text_ctrl, wxSizerFlags().Border());
680 void Apply()
override { g_TalkerIdText = text_ctrl->GetValue(); }
681 void Cancel()
override { text_ctrl->SetValue(g_TalkerIdText); }
685 class NetmaskRow :
public wxPanel,
public ApplyCancel {
687 NetmaskRow(wxWindow* parent)
689 m_spin_ctrl(
new wxSpinCtrl(
this, wxID_ANY)),
690 m_text(
new wxStaticText(
this, wxID_ANY,
"")) {
691 m_spin_ctrl->SetRange(8, 32);
692 auto hbox =
new wxBoxSizer(wxHORIZONTAL);
693 auto flags = wxSizerFlags().Align(wxALIGN_CENTRE_VERTICAL).Border();
694 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"Netmask: ")), flags);
695 hbox->Add(m_text, flags);
696 hbox->Add(
new wxStaticText(
this, wxID_ANY, _(
"length (bits): ")), flags);
697 hbox->Add(m_spin_ctrl, flags);
701 Bind(wxEVT_SPINCTRL, [&](wxSpinEvent& ev) {
702 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
707 void Apply()
override { g_netmask_bits = m_spin_ctrl->GetValue(); }
709 void Cancel()
override {
710 m_spin_ctrl->SetValue(g_netmask_bits);
711 m_text->SetLabel(BitsToDottedMask(m_spin_ctrl->GetValue()));
716 wxSpinCtrl* m_spin_ctrl;
717 wxStaticText* m_text;
719 std::string BitsToDottedMask(
unsigned bits) {
720 uint32_t mask = 0xffffffff << (32 - bits);
721 std::stringstream ss;
722 ss << ((mask & 0xff000000) >> 24) <<
".";
723 ss << ((mask & 0x00ff0000) >> 16) <<
".";
724 ss << ((mask & 0x0000ff00) >> 8) <<
".";
725 ss << (mask & 0x000000ff);
731 class PrioritiesBtn :
public wxButton {
733 PrioritiesBtn(wxWindow* parent)
734 : wxButton(parent, wxID_ANY, _(
"Adjust communication priorities...")) {
735 Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent&) {