OpenCPN Partial API docs
Loading...
Searching...
No Matches
connection_edit.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2022 by David S. Register *
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 <memory>
25#include <set>
26#include <string>
27#include <vector>
28
29#include <wx/wxprec.h>
30
31#ifndef WX_PRECOMP
32#include <wx/wx.h>
33#endif
34
35#include "config.h"
36
37#include <wx/tokenzr.h>
38#include <wx/regex.h>
39
40#if defined(__linux__) && !defined(__ANDROID__)
41#include <linux/can.h>
42#include <net/if.h>
43#include <serial/serial.h>
44#include <sys/ioctl.h>
45#include <sys/socket.h>
46#include "dnet.h"
47#endif
48
49#ifdef __ANDROID__
50#include "androidUTIL.h"
51#include "qdebug.h"
52#endif
53
54#include "connection_edit.h"
55
56#include "libgui/text_ctrl_w_help.h"
57
59#include "model/config_vars.h"
60#include "model/ocpn_utils.h"
61#include "model/ser_ports.h"
62#include "model/sys_events.h"
63
64#include "conn_params_panel.h"
65#include "gui_lib.h"
66#include "nmea0183.h"
67#include "ocpn_platform.h"
68#include "ocpn_plugin.h" // FIXME for GetOCPNScaledFont_PlugIn
69#include "options.h"
70#include "priority_gui.h"
71#include "udev_rule_mgr.h"
72
73// Make _() return std::string instead of wxString;
74#undef _
75#if wxCHECK_VERSION(3, 2, 0)
76#define _(s) wxGetTranslation(wxASCII_STR(s)).ToStdString()
77#else
78#define _(s) wxGetTranslation((s)).ToStdString()
79#endif
80
81static const std::string kAddressDefaultHelp =
82 _("Enter IP address or hostname");
83static const std::string kAddressUdpHelp =
84 _("IP address or hostname, often 255.255.255.255");
85static const std::string kAddressMcastHelp =
86 _("Group address, usually 224.0.2.0 - 224.0.255.255");
87static const std::string kTcpDevice = _("Device using TCP");
88static const std::string kUdpReceive = _("Device sending UDP");
89static const std::string kUdpOutput = _("UDP send");
90static const std::string kGpsdDevice = _("Gpsd server");
91static const std::string kSignalkDevice = _("SignalK server");
92static const std::string kTcpClient = _("TCP client");
93static const std::string kUdpSend = _("Device(s) receiving UDP");
94static const std::string kGpsdClient = _("Gpsd client");
95static const std::string kSignalkClient = _("SignalK client");
96static const std::string kTcpServer = _("TCP Server");
97static const std::string kUdpInput = _("UDP Receive");
98static const std::string kMulticastServer = _("UDP Multicast Receive and Send");
99static const std::string kMulticastClient = _("UDP Multicast Send");
100
101static const std::vector<std::string> kBasicNetViews = {
102 kTcpDevice, kUdpReceive, kUdpSend, kGpsdDevice, kSignalkDevice};
103
104static const std::vector<std::string> kAdvancedNetViews = {
105 // First items matches kBasicNetViews
106 kTcpClient, kUdpInput, kUdpOutput, kGpsdClient,
107 kSignalkClient, kTcpServer, kMulticastClient, kMulticastServer};
108
109static wxString StringArrayToString(const wxArrayString& arr) {
110 wxString ret = wxEmptyString;
111 for (size_t i = 0; i < arr.Count(); i++) {
112 if (i > 0) ret.Append(",");
113 ret.Append(arr[i]);
114 }
115 return ret;
116}
117
118// Check available SocketCAN interfaces
119#if defined(__linux__) && !defined(__ANDROID__)
120static intf_t* intf;
121std::vector<std::string> can_if_candidates;
122static int print_intf(const struct intf_entry* entry, void* arg) {
123 std::string iface = entry->intf_name;
124 if (entry->intf_type == 1 && iface.find("can") != std::string::npos) {
125 can_if_candidates.push_back(entry->intf_name);
126 }
127 return 0;
128}
129#endif
130
131static bool IsAddressMultiCast(const wxString& ip) {
132 wxArrayString bytes = wxSplit(ip, '.');
133 if (bytes.size() != 4) {
134 return false;
135 }
136 unsigned long ipNum = (wxAtoi(bytes[0]) << 24) + (wxAtoi(bytes[1]) << 16) +
137 (wxAtoi(bytes[2]) << 8) + wxAtoi(bytes[3]);
138 unsigned long multicastStart = (224 << 24);
139 unsigned long multicastEnd = (239 << 24) + (255 << 16) + (255 << 8) + 255;
140 return ipNum >= multicastStart && ipNum <= multicastEnd;
141}
142
143static bool IsAddressListener(const std::string& address) {
144 return address.empty() || address == "0.0.0.0";
145}
146
148static std::string GetChoiceSelection(const wxChoice* choice) {
149 int selected = choice->GetSelection();
150 return choice->GetString(selected).ToStdString();
151}
152
158static std::string NetViewByConnection(const ConnectionParams* cp) {
159 bool is_server = IsAddressListener(cp->network_address.ToStdString());
160 if (IsAddressMultiCast(cp->network_address))
161 return is_server ? kMulticastServer : kMulticastClient;
162 switch (cp->net_protocol) {
163 case NetworkProtocol::GPSD:
164 return kGpsdDevice;
165 case NetworkProtocol::SIGNALK:
166 return kSignalkDevice;
167 case NetworkProtocol::UDP:
168 return is_server ? kUdpReceive : kUdpSend;
169 case NetworkProtocol::TCP:
170 return is_server ? kTcpServer : kTcpDevice;
171 default:
172 wxLogWarning("Cannot deduce connection params view type");
173 return "";
174 }
175 return ""; // for the compiler
176}
177
178static wxArrayString GetAvailableSocketCANInterfaces() {
179 wxArrayString rv;
180
181#if defined(__linux__) && !defined(__ANDROID__)
182 can_if_candidates.clear();
183
184 if ((intf = intf_open()) == nullptr) {
185 wxLogWarning("Error opening interface list");
186 return rv;
187 }
188
189 if (intf_loop(intf, print_intf, nullptr) < 0) {
190 wxLogWarning("Error looping over interface list");
191 }
192 intf_close(intf);
193
194 for (const auto& iface : can_if_candidates) {
195 int sock = socket(PF_CAN, SOCK_RAW, CAN_RAW);
196 if (sock < 0) {
197 continue;
198 }
199
200 // Get the interface index
201 struct ifreq if_request = {{0}};
202 strcpy(if_request.ifr_name, iface.c_str());
203 if (ioctl(sock, SIOCGIFINDEX, &if_request) < 0) {
204 continue;
205 }
206
207 // Check if interface is UP
208 struct sockaddr_can can_address = {0};
209 can_address.can_family = AF_CAN;
210 can_address.can_ifindex = if_request.ifr_ifindex;
211 if (ioctl(sock, SIOCGIFFLAGS, &if_request) < 0) {
212 continue;
213 }
214 if (if_request.ifr_flags & IFF_UP) {
215 rv.Add(iface);
216 } else {
217 continue;
218 }
219 }
220#endif
221 return rv;
222}
223
224static void LoadSerialPorts(wxComboBox* box) {
226 class PortSorter {
227 private:
228 [[nodiscard]] static std::string GetKey(const std::string& s) {
229 if (s.find("->") == std::string::npos) return s;
230 return ocpn::trim(ocpn::split(s, "->")[1]) + " link";
231 }
232
233 public:
234 bool operator()(const std::string& lhs, const std::string& rhs) const {
235 return GetKey(lhs) < GetKey(rhs);
236 }
237 } port_sorter;
238
239 std::set<std::string, PortSorter> sorted_ports(port_sorter);
240 std::unique_ptr<wxArrayString> ports(EnumerateSerialPorts());
241 for (size_t i = 0; i < ports->GetCount(); i++)
242 sorted_ports.insert((*ports)[i].ToStdString());
243
244 auto value = box->GetValue();
245 box->Clear();
246 for (auto& p : sorted_ports) box->Append(p);
247 if (!value.empty()) box->SetValue(value);
248}
249
250static bool CheckPort(wxWindow* parent, TextCtrlWithHelp& ctrl) {
251 if (ctrl.IsPristine() || ctrl.GetValue().empty()) {
252 auto dlg = wxMessageDialog(parent, _("Required field port is missing"),
253 _("OpenCPN error"), wxOK | wxICON_ERROR);
254 dlg.ShowModal();
255 return false;
256 };
257 int port = 0;
258 try {
259 port = std::stoi(ctrl.GetValue().ToStdString());
260 } catch (std::logic_error&) {
261 auto dlg = wxMessageDialog(parent, _("Invalid port number"),
262 _("OpenCPN error"), wxOK | wxICON_ERROR);
263 dlg.ShowModal();
264 return false;
265 }
266 if (port < 1024) {
267 static const std::string kMsg =
268 _(R"(Port numbers smaller than 1024 are reserved for use by the
269operating system and should normally not be used by OpenCPN)");
270 auto dlg = wxMessageDialog(parent, kMsg, _("OpenCPN warning"),
271 wxOK | wxICON_WARNING);
272 dlg.ShowModal();
273 return true;
274 }
275 return true;
276}
277
278bool CheckAddress(wxWindow* parent, TextCtrlWithHelp& ctrl) {
279 if (ctrl.IsPristine() || ctrl.GetValue().empty()) {
280 auto dlg = wxMessageDialog(parent, _("Required field address is missing"),
281 _("OpenCPN error"), wxOK | wxICON_ERROR);
282 dlg.ShowModal();
283 return false;
284 };
285 // Checking the address requires using gethostbyname() or so since it
286 // could be a hostname. Not worthwhile in this context.
287 return true;
288}
289
290bool CheckIoDirections(wxWindow* parent, const wxCheckBox* input,
291 const wxCheckBox* output) {
292 if (input->GetValue() || output->GetValue()) return true;
293
294 auto dlg =
295 wxMessageDialog(parent, _("Either input or output must be checked"),
296 _("OpenCPN error"), wxOK | wxICON_ERROR);
297 dlg.ShowModal();
298 return false;
299}
300
302static void SetupProtocolChoice(wxChoice* choice) {
303 choice->Clear();
304 choice->Append("NMEA 0183");
305 choice->Append("NMEA 2000");
306 choice->SetSelection(0);
307 choice->Enable();
308}
309//------------------------------------------------------------------------------
310// ConnectionEditDialog Implementation
311//------------------------------------------------------------------------------
312
313// Define constructors
314ConnectionEditDialog::ConnectionEditDialog()
315 : ConnectionEditDialog(nullptr, nullptr) {}
316
317ConnectionEditDialog::ConnectionEditDialog(
318 wxWindow* parent,
319 std::function<void(ConnectionParams* p, bool editing, bool ok_cancel)>
320 _on_edit_click)
321 : wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), 0,
322 "conn_edit"),
323 m_scroll_win_connections(nullptr),
324 m_on_edit_click(std::move(_on_edit_click)),
325 m_selected_conn_params(nullptr),
326 m_cp_original(nullptr),
327 m_is_conn_saved(false),
328 m_is_editing(false),
329 m_is_nmea_params_shown(false),
330 m_new_mode(false),
331 m_advanced(false),
332 m_conn_enabled(false),
333 m_accept_radiobtn(nullptr),
334 m_add_btn(nullptr),
335 m_advanced_chkbox(nullptr),
336 m_aps_magnetic_chkbox(nullptr),
337 m_auth_token_tctrl(nullptr),
338 m_auth_token_text(nullptr),
339 m_baud_rate_choice(nullptr),
340 m_bt_data_sources_choice(nullptr),
341 m_bt_last_result_count(0),
342 m_bt_no_change_counter(0),
343 m_bt_pairs_text(nullptr),
344 m_bt_scanning(0),
345 m_bt_scan_timer(nullptr),
346 m_can_props_sizer(nullptr),
347 m_can_source_choice(nullptr),
348 m_can_source_text(nullptr),
349 m_collapse_box(nullptr),
350 m_connection_props_sizer(nullptr),
351 m_connections_sizer(nullptr),
352 m_conn_edit_statbox(nullptr),
353 m_dlg_buttons_apply_btn(nullptr),
354 m_dlg_buttons_cancel_btn(nullptr),
355 m_dlg_buttons_ok_btn(nullptr),
356 m_dlg_cancel_btn(nullptr),
357 m_dlg_ok_btn(nullptr),
358 m_filter_cog_sog_chkbox(nullptr),
359 m_filter_sec_tctrl(nullptr),
360 m_filter_sec_text(nullptr),
361 m_furuno_gp3x_chkbox(nullptr),
362 m_garmin_host_chkbox(nullptr),
363 m_garmin_upload_host_chkbox(nullptr),
364 m_ignore_radiobtn(nullptr),
365 m_in_filter_sizer(nullptr),
366 m_input_chkbox(nullptr),
367 m_input_stc_list_btn(nullptr),
368 m_input_stc_tctrl(nullptr),
369 m_net_address_tctrl(nullptr),
370 m_net_addr_text(nullptr),
371 m_net_comment_tctrl(nullptr),
372 m_net_comment_text(nullptr),
373 m_net_data_protocol_choice(nullptr),
374 m_net_data_protocol_text(nullptr),
375 m_net_expert_box_text(nullptr),
376 m_net_expert_chkbox(nullptr),
377 m_net_port_tctrl(nullptr),
378 m_net_port_text(nullptr),
379 m_net_props_sizer(nullptr),
380 m_net_view_choice(nullptr),
381 m_net_type_choice_text(nullptr),
382 m_o_accept_radiobtn(nullptr),
383 m_o_ignore_radiobtn(nullptr),
384 m_out_filter_sizer(nullptr),
385 m_output_chkbox(nullptr),
386 m_output_stc_list_btn(nullptr),
387 m_output_stc_tctrl(nullptr),
388 m_parent(parent),
389 m_port_combo(nullptr),
390 m_precision_choice(nullptr),
391 m_precision_text(nullptr),
392 m_priority_choice(nullptr),
393 m_priority_dialog_btn(nullptr),
394 m_remove_btn(nullptr),
395 m_scan_bt_btn(nullptr),
396 m_ser_baudrate_text(nullptr),
397 m_ser_comment_text(nullptr),
398 m_serial_comment_tctrl(nullptr),
399 m_serial_protocol_choice(nullptr),
400 m_ser_port_text(nullptr),
401 m_ser_props_sizer(nullptr),
402 m_ser_protocol_text(nullptr),
403 m_sizer_box_btn(nullptr),
404 m_sk_check_discover_chkbox(nullptr),
405 m_sk_discover_btn(nullptr),
406 m_sk_server_status_text(nullptr),
407 m_std_dialog_btn_sizer(nullptr),
408 m_talker_id_text(nullptr),
409 m_type_can_radiobtn(nullptr),
410 m_type_internal_bt_radiobtn(nullptr),
411 m_type_internal_gps_radiobtn(nullptr),
412 m_type_net_radiobtn(nullptr),
413 m_type_serial_radiobtn(nullptr) {
414 Init();
415}
416
417ConnectionEditDialog::~ConnectionEditDialog() = default;
418
419void ConnectionEditDialog::AddOKCancelButtons() {
420#ifndef ANDROID
421 if (!m_std_dialog_btn_sizer) {
422 m_std_dialog_btn_sizer = new wxStdDialogButtonSizer();
423 m_dlg_ok_btn = new wxButton(this, wxID_OK);
424 m_dlg_cancel_btn = new wxButton(this, wxID_CANCEL, _("Cancel"));
425 m_std_dialog_btn_sizer->AddButton(m_dlg_ok_btn);
426 m_std_dialog_btn_sizer->AddButton(m_dlg_cancel_btn);
427 m_std_dialog_btn_sizer->Realize();
428 GetSizer()->Add(m_std_dialog_btn_sizer, 0, wxALL | wxEXPAND, 5);
429 m_std_dialog_btn_sizer->Show(true);
430 }
431#else
432 if (!m_std_dialog_btn_sizer) {
433 m_std_dialog_btn_sizer = new wxStdDialogButtonSizer();
434 m_dlg_ok_btn = new wxButton(this, wxID_OK);
435 m_dlg_cancel_btn = new wxButton(this, wxID_CANCEL, _("Cancel"));
436 m_std_dialog_btn_sizer->AddSpacer(wxWindow::GetCharWidth());
437 m_std_dialog_btn_sizer->Add(m_dlg_ok_btn, 0, wxALL, 5);
438 m_std_dialog_btn_sizer->Add(m_dlg_cancel_btn, 0, wxALL, 5);
439 GetSizer()->Add(m_std_dialog_btn_sizer, 0, wxALL | wxEXPAND, 5);
440 }
441#endif
442
443 m_dlg_ok_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
444 [&](wxCommandEvent& ev) { OnOKClick(); });
445 m_dlg_cancel_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED,
446 [&](wxCommandEvent& ev) { OnCancelClick(); });
447}
448void ConnectionEditDialog::InitiateNewConnection() {
449 m_net_view_choice->Clear();
450 for (const auto& view : kBasicNetViews) m_net_view_choice->Append(view);
451 m_net_view_choice->SetSelection(0);
452 m_net_expert_chkbox->SetValue(false);
453 m_net_comment_tctrl->Hide();
454 m_net_comment_text->Hide();
455 auto port_ctrl = dynamic_cast<TextCtrlWithHelp*>(m_net_port_tctrl);
456 if (port_ctrl) port_ctrl->RestoreHelp();
457 m_net_address_tctrl->Show();
458 m_net_address_tctrl->Enable();
459 auto addr_ctrl = dynamic_cast<TextCtrlWithHelp*>(m_net_address_tctrl);
460 if (addr_ctrl) addr_ctrl->SetHelp(kAddressDefaultHelp);
461 m_net_addr_text->Show();
462 SetupProtocolChoice(m_net_data_protocol_choice);
463 m_output_chkbox->SetValue(false);
464 m_input_chkbox->SetValue(true);
465}
466
468 int selection = m_net_view_choice->GetSelection();
469 if (selection == wxNOT_FOUND) return;
470 std::string view = GetChoiceSelection(m_net_view_choice);
471 ConfigureControlsForView(view);
472 OnExpertModeChange();
473}
474
475void ConnectionEditDialog::ConfigureControlsForView(const std::string& view) {
476 if (!m_type_net_radiobtn->GetValue()) return;
477 auto found = std::find(kBasicNetViews.begin(), kBasicNetViews.end(), view);
478 int selection = m_net_view_choice->GetSelection();
479 assert(selection != wxNOT_FOUND);
480 m_net_expert_chkbox->SetValue(found == kBasicNetViews.end());
481 m_net_address_tctrl->Enable();
482 auto net_addr_w_help = dynamic_cast<TextCtrlWithHelp*>(m_net_address_tctrl);
483 assert(net_addr_w_help);
484 auto net_port_w_help = dynamic_cast<TextCtrlWithHelp*>(m_net_port_tctrl);
485 assert(net_port_w_help);
486 m_net_addr_text->SetLabel(_("Server address"));
487 m_output_chkbox->Disable();
488 m_input_chkbox->Disable();
489 m_net_addr_text->Show();
490 m_net_address_tctrl->Show();
491 auto port = m_net_port_tctrl->GetValue();
492 if (port == kDefaultGpsdPort || port == kDefaultSignalkPort || port.empty())
493 net_port_w_help->RestoreHelp();
494 if (m_net_data_protocol_choice->GetCount() != 2)
495 SetupProtocolChoice(m_net_data_protocol_choice);
496 if (view == kTcpDevice || view == kTcpClient) {
497 m_net_addr_text->Show();
498 m_net_address_tctrl->Show();
499 if (m_net_address_tctrl->GetValue() == "0.0.0.0")
500 net_addr_w_help->RestoreHelp();
501 if (net_addr_w_help->IsPristine())
502 net_addr_w_help->SetHelp(kAddressDefaultHelp);
503 if (net_port_w_help->IsPristine())
504 net_port_w_help->SetHelp(_("Port number (1025 - 65535, often 10110)"));
505 m_input_chkbox->Enable();
506 m_output_chkbox->Enable();
507 } else if (view == kUdpReceive || view == kUdpInput) {
508 m_net_addr_text->Hide();
509 m_net_address_tctrl->ChangeValue("0.0.0.0");
510 m_net_address_tctrl->Disable();
511 m_net_address_tctrl->Hide();
512 if (net_addr_w_help->IsPristine())
513 net_addr_w_help->SetHelp(kAddressDefaultHelp);
514 if (net_port_w_help->IsPristine())
515 net_port_w_help->SetHelp(_("Port number (1025 - 65535, often 10110)"));
516 m_input_chkbox->SetValue(true);
517 m_output_chkbox->SetValue(false);
518 } else if (view == kUdpSend || view == kUdpOutput) {
519 if (m_net_address_tctrl->GetValue() == "0.0.0.0")
520 net_addr_w_help->RestoreHelp();
521 if (net_addr_w_help->IsPristine())
522 net_addr_w_help->SetHelp(kAddressUdpHelp);
523 m_input_chkbox->SetValue(false);
524 m_output_chkbox->SetValue(true);
525 } else if (view == kGpsdClient || view == kGpsdDevice) {
526 m_net_data_protocol_choice->Clear();
527 m_net_data_protocol_choice->Append("gpsd");
528 m_net_data_protocol_choice->SetSelection(0);
529 m_net_data_protocol_choice->Disable();
530 if (m_net_address_tctrl->GetValue() == "0.0.0.0")
531 net_addr_w_help->RestoreHelp();
532 if (net_addr_w_help->IsPristine())
533 net_addr_w_help->SetHelp(kAddressDefaultHelp);
534 if (net_port_w_help->IsPristine())
535 net_port_w_help->ChangeValue(kDefaultGpsdPort);
536 m_output_chkbox->SetValue(false);
537 m_input_chkbox->SetValue(true);
538 } else if (view == kSignalkClient || view == kSignalkDevice) {
539 m_net_data_protocol_choice->Clear();
540 m_net_data_protocol_choice->Append("SignalK");
541 m_net_data_protocol_choice->SetSelection(0);
542 m_net_data_protocol_choice->Disable();
543 if (m_net_address_tctrl->GetValue() == "0.0.0.0")
544 net_addr_w_help->RestoreHelp();
545 if (net_addr_w_help->IsPristine())
546 net_addr_w_help->SetHelp(kAddressDefaultHelp);
547 if (net_port_w_help->IsPristine())
548 net_port_w_help->ChangeValue(kDefaultSignalkPort);
549 m_input_chkbox->SetValue(true);
550 m_output_chkbox->SetValue(false);
551 } else if (view == kTcpServer) {
552 m_net_addr_text->SetLabel(_("Interface"));
553 m_net_address_tctrl->ChangeValue("0.0.0.0");
554 m_net_address_tctrl->Disable();
555 m_output_chkbox->Enable();
556 m_input_chkbox->Enable();
557 } else if (view == kMulticastClient || view == kMulticastServer) {
558 if (net_addr_w_help->GetValue().empty()) net_addr_w_help->RestoreHelp();
559 m_net_addr_text->SetLabel(_("Multicast group"));
560 if (net_port_w_help->IsPristine())
561 net_port_w_help->SetHelp(_("Port number, usually 49152 - 65535"));
562 if (net_addr_w_help->IsPristine())
563 net_addr_w_help->SetHelp(kAddressMcastHelp);
564 if (view == kMulticastClient) {
565 m_input_chkbox->SetValue(false);
566 m_output_chkbox->SetValue(true);
567 } else {
568 m_input_chkbox->SetValue(true);
569 m_output_chkbox->Enable();
570 }
571 } else {
572 wxLogError("Illegal view: %s", view.c_str());
573 assert(false && "Illegal view");
574 }
576}
577
579 if (!m_type_net_radiobtn->GetValue()) return;
580 if (m_garmin_host_chkbox) m_garmin_host_chkbox->Hide();
581 if (m_garmin_upload_host_chkbox) m_garmin_upload_host_chkbox->Hide();
582 const std::string view = GetChoiceSelection(m_net_view_choice);
583 bool show_auth = view == kSignalkDevice || view == kSignalkClient;
584 m_auth_token_tctrl->Show(show_auth && m_advanced);
585 m_auth_token_text->Show(show_auth && m_advanced);
586 bool show_apb_precision = m_output_chkbox->IsChecked();
587 m_precision_text->Show(show_apb_precision && m_advanced);
588 m_precision_choice->Show(show_apb_precision && m_advanced);
589 Layout();
590}
591
592void ConnectionEditDialog::Init() {
593 wxFont* qFont = GetOCPNScaledFont(_("Dialog"));
594 SetFont(*qFont);
595
596 // Setup some initial values
597
598 m_bt_scan_timer.SetOwner(this, ID_BT_SCANTIMER);
599 m_bt_scanning = 0;
600 wxSize displaySize = wxGetDisplaySize();
601
602 // Create the UI
603
604 auto* mainSizer = new wxBoxSizer(wxVERTICAL);
605 SetSizer(mainSizer);
606
607 wxFont* dFont = GetOCPNScaledFont_PlugIn(_("Dialog"));
608 double font_size = dFont->GetPointSize() * 17 / 16;
609 wxFont* bFont = wxTheFontList->FindOrCreateFont(
610 static_cast<int>(font_size), dFont->GetFamily(), dFont->GetStyle(),
611 wxFONTWEIGHT_BOLD);
612
613 // Connections Properties
614 m_conn_edit_statbox =
615 new wxStaticBox(this, wxID_ANY, _("Edit Selected Connection"));
616 m_conn_edit_statbox->SetFont(*bFont);
617
618 m_connection_props_sizer =
619 new wxStaticBoxSizer(m_conn_edit_statbox, wxVERTICAL);
620 GetSizer()->Add(m_connection_props_sizer, 1, wxALL | wxEXPAND, 5);
621
622 wxBoxSizer* bSizer15;
623 bSizer15 = new wxBoxSizer(wxHORIZONTAL);
624
625 m_connection_props_sizer->Add(bSizer15, 0, wxTOP | wxEXPAND, 5);
626
627 m_type_serial_radiobtn =
628 new wxRadioButton(this, wxID_ANY, _("Serial"), wxDefaultPosition,
629 wxDefaultSize, wxRB_GROUP);
630 m_type_serial_radiobtn->SetValue(true);
631 bSizer15->Add(m_type_serial_radiobtn, 0, wxALL, 5);
632
633 m_type_net_radiobtn = new wxRadioButton(this, wxID_ANY, _("Network"),
634 wxDefaultPosition, wxDefaultSize, 0);
635 bSizer15->Add(m_type_net_radiobtn, 0, wxALL, 5);
636
637 m_type_can_radiobtn = new wxRadioButton(this, wxID_ANY, "socketCAN",
638 wxDefaultPosition, wxDefaultSize, 0);
639#if defined(__linux__) && !defined(__ANDROID__) && !defined(__WXOSX__)
640 bSizer15->Add(m_type_can_radiobtn, 0, wxALL, 5);
641#else
642 m_type_can_radiobtn->Hide();
643#endif
644
645 auto* bSizer15a = new wxBoxSizer(wxHORIZONTAL);
646 m_connection_props_sizer->Add(bSizer15a, 0, wxEXPAND, 5);
647
648 if (OCPNPlatform::hasInternalGPS()) {
649 m_type_internal_gps_radiobtn = new wxRadioButton(
650 this, wxID_ANY, _("Built-in GPS"), wxDefaultPosition, wxDefaultSize, 0);
651 bSizer15a->Add(m_type_internal_gps_radiobtn, 0, wxALL, 5);
652 } else
653 m_type_internal_gps_radiobtn = nullptr;
654
655 // has built-in Bluetooth
656 if (OCPNPlatform::hasInternalBT()) {
657 m_type_internal_bt_radiobtn =
658 new wxRadioButton(this, wxID_ANY, _("Built-in Bluetooth SPP"),
659 wxDefaultPosition, wxDefaultSize, 0);
660 bSizer15a->Add(m_type_internal_bt_radiobtn, 0, wxALL, 5);
661
662 m_scan_bt_btn = new wxButton(this, wxID_ANY, _("BT Scan") + " ",
663 wxDefaultPosition, wxDefaultSize);
664 m_scan_bt_btn->Hide();
665
666 m_connection_props_sizer->Add(m_scan_bt_btn, 0, wxALL, 25);
667
668 m_bt_pairs_text =
669 new wxStaticText(this, wxID_ANY, _("Bluetooth Data Sources"),
670 wxDefaultPosition, wxDefaultSize, 0);
671 m_bt_pairs_text->Wrap(-1);
672 m_bt_pairs_text->Hide();
673 m_connection_props_sizer->Add(m_bt_pairs_text, 0, wxALL, 5);
674
675 wxArrayString mt;
676 mt.Add("unscanned");
677
678 int ref_size = this->GetCharWidth();
679 m_bt_data_sources_choice =
680 new wxChoice(this, wxID_ANY, wxDefaultPosition,
681 wxSize(40 * ref_size, 2 * ref_size), mt);
682 m_bt_data_sources_choice->SetSelection(0);
683 m_bt_data_sources_choice->Hide();
684 m_connection_props_sizer->Add(m_bt_data_sources_choice, 1, wxEXPAND | wxTOP,
685 25);
686
687 } else {
688 m_type_internal_bt_radiobtn = nullptr;
689 }
690
691 m_net_props_sizer = new wxFlexGridSizer(0, 2, 0, 0);
692
693 m_connection_props_sizer->Add(m_net_props_sizer, 0, wxEXPAND, 5);
694
695 // Optimize for Portrait mode handheld devices
696 if (displaySize.x < displaySize.y) {
697 wxBoxSizer* bSizer16a;
698 bSizer16a = new wxBoxSizer(wxHORIZONTAL);
699 m_net_props_sizer->AddSpacer(1);
700 m_net_props_sizer->Add(bSizer16a, 1, wxEXPAND, 5);
701 m_net_props_sizer->AddSpacer(1);
702 m_net_props_sizer->AddSpacer(1);
703 }
704 m_net_expert_box_text = new wxStaticText(this, wxID_ANY, _("Expert mode"));
705 m_net_props_sizer->Add(m_net_expert_box_text, 0, wxALL, 5);
706 m_net_expert_chkbox = new wxCheckBox(this, wxID_ANY, "");
707 m_net_props_sizer->Add(m_net_expert_chkbox, 0, wxALL, 5);
708 m_net_expert_chkbox->Bind(
709 wxEVT_CHECKBOX, [&](const wxCommandEvent&) { OnExpertModeChange(); });
710
711 m_net_type_choice_text =
712 new wxStaticText(this, wxID_ANY, _("Connection type"));
713 m_net_props_sizer->Add(m_net_type_choice_text, 0, wxALL, 5);
714 m_net_view_choice = new wxChoice(this, wxID_ANY);
715 m_net_view_choice->Append(kBasicNetViews[0]);
716 m_net_view_choice->SetSelection(0); // until OnConnectionTypeChanged()
717 m_net_view_choice->Bind(wxEVT_CHOICE,
718 [&](wxCommandEvent&) { OnConnectionTypeChange(); });
719 m_net_props_sizer->Add(m_net_view_choice, 0, wxTOP, 5);
720 m_net_data_protocol_text =
721 new wxStaticText(this, wxID_ANY, _("Data Protocol"));
722 m_net_data_protocol_text->Wrap(-1);
723 m_net_props_sizer->Add(m_net_data_protocol_text, 0, wxALL, 5);
724
725 m_net_data_protocol_choice = new wxChoice(this, wxID_ANY);
726 SetupProtocolChoice(m_net_data_protocol_choice);
727 m_net_props_sizer->Add(m_net_data_protocol_choice, 1, wxTOP, 5);
728 m_net_props_sizer->AddSpacer(1);
729 m_net_props_sizer->AddSpacer(1);
730
731 m_net_addr_text = new wxStaticText(this, wxID_ANY, _("Address"));
732 m_net_addr_text->Wrap(-1);
733 int column1width = 15 * GetCharWidth();
734 m_net_addr_text->SetMinSize(wxSize(column1width, -1));
735 m_net_props_sizer->Add(m_net_addr_text, 0, wxALL, 5);
736 m_net_address_tctrl = new TextCtrlWithHelp(this, kAddressDefaultHelp);
737 int column2width = 60 * GetCharWidth();
738 m_net_address_tctrl->SetMaxSize(wxSize(column2width, -1));
739 m_net_address_tctrl->SetMinSize(wxSize(column2width, -1));
740 m_net_address_tctrl->Bind(wxEVT_KILL_FOCUS,
741 [&](wxFocusEvent& ev) { OnAddressChange(ev); });
742
743 m_net_props_sizer->Add(m_net_address_tctrl, 0, wxEXPAND | wxTOP, 5);
744 m_net_props_sizer->AddSpacer(1);
745 m_net_props_sizer->AddSpacer(1);
746
747 m_net_port_text = new wxStaticText(this, wxID_ANY, _("Data Port"));
748 m_net_port_text->Wrap(-1);
749 m_net_props_sizer->Add(m_net_port_text, 0, wxALL, 5);
750
751 m_net_port_tctrl = new TextCtrlWithHelp(this, "Enter data source port");
752 m_net_port_tctrl->SetMaxSize(wxSize(column2width, -1));
753 m_net_port_tctrl->SetMinSize(wxSize(column2width, -1));
754 m_net_props_sizer->Add(m_net_port_tctrl, 1, wxEXPAND | wxTOP, 5);
755 m_net_port_tctrl->SetMaxSize(wxSize(column2width, -1));
756 m_net_port_tctrl->SetMinSize(wxSize(column2width, -1));
757
758 m_net_comment_text = new wxStaticText(this, wxID_ANY, _("User Comment"));
759 m_net_comment_text->Wrap(-1);
760 m_net_comment_text->SetMinSize({column1width, -1});
761 m_net_props_sizer->Add(m_net_comment_text, 0, wxALL, 5);
762 m_net_comment_text->Hide();
763
764 m_net_comment_tctrl = new wxTextCtrl(this, wxID_ANY);
765 m_net_comment_tctrl->SetMaxSize({column2width, -1});
766 m_net_comment_tctrl->SetMinSize({column2width, -1});
767 m_net_props_sizer->Add(m_net_comment_tctrl, 1, wxEXPAND | wxTOP, 5);
768 m_net_comment_tctrl->Hide();
769
770 m_net_props_sizer->AddSpacer(1);
771 m_net_props_sizer->AddSpacer(1);
772
773 m_can_props_sizer = new wxGridSizer(0, 1, 0, 0);
774 wxFlexGridSizer* fgSizer1C;
775 fgSizer1C = new wxFlexGridSizer(0, 2, 0, 0);
776
777 m_can_source_text = new wxStaticText(this, wxID_ANY, _("socketCAN Source"),
778 wxDefaultPosition, wxDefaultSize, 0);
779 m_can_source_text->Wrap(-1);
780 m_can_source_text->SetMinSize(wxSize(column1width, -1));
781 fgSizer1C->Add(m_can_source_text, 0, wxALL, 5);
782
783 wxArrayString choices = GetAvailableSocketCANInterfaces();
784 m_can_source_choice =
785 new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, choices);
786
787 m_can_source_choice->SetSelection(0);
788 m_can_source_choice->Enable(!choices.empty());
789 m_can_source_choice->SetMaxSize(wxSize(column2width, -1));
790 m_can_source_choice->SetMinSize(wxSize(column2width, -1));
791 fgSizer1C->Add(m_can_source_choice, 1, wxEXPAND | wxTOP, 5);
792
793 m_can_props_sizer->Add(fgSizer1C, 0, wxEXPAND, 5);
794
795 m_connection_props_sizer->Add(m_can_props_sizer, 0, wxEXPAND, 5);
796
797 m_ser_props_sizer = new wxGridSizer(0, 1, 0, 0);
798 m_connection_props_sizer->Add(m_ser_props_sizer, 0, wxEXPAND, 5);
799
800 wxFlexGridSizer* fgSizer1;
801 fgSizer1 = new wxFlexGridSizer(0, 4, 0, 0);
802 fgSizer1->SetFlexibleDirection(wxBOTH);
803 fgSizer1->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
804
805 m_ser_port_text =
806 new wxStaticText(this, wxID_ANY, _("Data port"), wxDefaultPosition,
807 wxDefaultSize, wxST_ELLIPSIZE_END);
808 m_ser_port_text->SetMinSize(wxSize(column1width, -1));
809 m_ser_port_text->Wrap(-1);
810
811 fgSizer1->Add(m_ser_port_text, 0, wxALL, 5);
812
813 m_port_combo =
814 new wxComboBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
815 wxDefaultSize, 0, nullptr, 0);
816
817 m_port_combo->SetMaxSize(wxSize(column2width, -1));
818 m_port_combo->SetMinSize(wxSize(column2width, -1));
819
820 fgSizer1->Add(m_port_combo, 0, wxEXPAND | wxTOP, 5);
821
822 m_ser_baudrate_text = new wxStaticText(this, wxID_ANY, _("Baudrate"),
823 wxDefaultPosition, wxDefaultSize, 0);
824 m_ser_baudrate_text->Wrap(-1);
825 fgSizer1->AddSpacer(1);
826 fgSizer1->AddSpacer(1);
827 fgSizer1->Add(m_ser_baudrate_text, 0, wxALL, 5);
828
829 wxString m_choiceBaudRateChoices[] = {
830 _("150"), _("300"), _("600"), _("1200"), _("2400"),
831 _("4800"), _("9600"), _("19200"), _("38400"), _("57600"),
832 _("115200"), _("230400"), _("460800"), _("921600")};
833 int m_choiceBaudRateNChoices =
834 sizeof(m_choiceBaudRateChoices) / sizeof(wxString);
835 m_baud_rate_choice =
836 new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
837 m_choiceBaudRateNChoices, m_choiceBaudRateChoices, 0);
838 // m_choiceBaudRate->Bind(wxEVT_MOUSEWHEEL,
839 // &ConnectionEditDialog::OnWheelChoice, this);
840
841 m_baud_rate_choice->SetSelection(0);
842
843 fgSizer1->Add(m_baud_rate_choice, 1, wxEXPAND | wxTOP, 5);
844 fgSizer1->AddSpacer(1);
845 fgSizer1->AddSpacer(1);
846
847 m_ser_protocol_text = new wxStaticText(this, wxID_ANY, _("Protocol"));
848 m_ser_protocol_text->Wrap(-1);
849 fgSizer1->Add(m_ser_protocol_text, 0, wxALL, 5);
850
851 wxString m_choiceSerialProtocolChoices[] = {_("NMEA 0183"), _("NMEA 2000")};
852 int m_choiceSerialProtocolNChoices =
853 sizeof(m_choiceSerialProtocolChoices) / sizeof(wxString);
854 m_serial_protocol_choice = new wxChoice(
855 this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
856 m_choiceSerialProtocolNChoices, m_choiceSerialProtocolChoices, 0);
857 // m_choiceSerialProtocol->Bind(wxEVT_MOUSEWHEEL,
858 // &ConnectionEditDialog::OnWheelChoice, this);
859
860 m_serial_protocol_choice->SetSelection(0);
861 m_serial_protocol_choice->Enable(true);
862 fgSizer1->Add(m_serial_protocol_choice, 1, wxEXPAND | wxTOP, 5);
863
864 m_ser_props_sizer->Add(fgSizer1, 0, wxEXPAND, 5);
865
866 // User Comments
867
868 auto* commentSizer = new wxFlexGridSizer(0, 2, 0, 0);
869 // sbSizerConnectionProps->Add(commentSizer, 0, wxEXPAND, 5);
870
871 // Serial User Comments
872 m_ser_comment_text = new wxStaticText(this, wxID_ANY, _("User Comment"));
873 m_ser_comment_text->Wrap(-1);
874 m_ser_comment_text->SetMinSize(wxSize(column1width, -1));
875 commentSizer->Add(m_ser_comment_text, 0, wxALL, 5);
876
877 m_serial_comment_tctrl = new wxTextCtrl(this, wxID_ANY);
878 m_serial_comment_tctrl->SetMaxSize(wxSize(column2width, -1));
879 m_serial_comment_tctrl->SetMinSize(wxSize(column2width, -1));
880
881 commentSizer->Add(m_serial_comment_tctrl, 1, wxTOP, 5);
882
883 m_connection_props_sizer->Add(commentSizer, 0, wxALL, 5);
884
885 wxFlexGridSizer* fgSizer5;
886 fgSizer5 = new wxFlexGridSizer(0, 2, 0, 0);
887 fgSizer5->SetFlexibleDirection(wxBOTH);
888 fgSizer5->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
889 m_connection_props_sizer->Add(fgSizer5, 0, wxEXPAND, 5);
890
891 m_input_chkbox =
892 new wxCheckBox(this, wxID_ANY, _("Receive Input on this Port"));
893 fgSizer5->Add(m_input_chkbox, 0, wxALL, 2);
894 fgSizer5->AddSpacer(1);
895
896 m_output_chkbox =
897 new wxCheckBox(this, wxID_ANY,
898 wxString::Format("%s (%s)", _("Output on this port"),
899 _("as autopilot or NMEA repeater")));
900 fgSizer5->Add(m_output_chkbox, 0, wxALL, 2);
901 fgSizer5->AddSpacer(1);
902
903 // Authentication token
904
905 auto flags = wxSizerFlags().Border();
906 m_collapse_box = new wxBoxSizer(wxHORIZONTAL);
907
908 m_collapse_box->Add(new wxStaticText(this, wxID_ANY, _("Advanced: ")), flags);
909 m_collapse_box->Add(
910 new ExpandableIcon(this,
911 [&](bool collapsed) { OnCollapsedToggle(collapsed); }),
912 flags);
913 fgSizer5->Add(m_collapse_box, wxSizerFlags());
914 fgSizer5->Add(new wxStaticText(this, wxID_ANY, ""));
915
916#ifndef USE_GARMINHOST
917 m_cbGarminHost->Hide();
918#endif
919
920 m_auth_token_text = new wxStaticText(this, wxID_ANY, _("Auth Token"));
921 m_auth_token_text->SetMinSize(wxSize(column1width, -1));
922 m_auth_token_text->Wrap(-1);
923 m_auth_token_text->SetMinSize(wxSize(column1width, -1));
924 fgSizer5->Add(m_auth_token_text, 0, wxALL, 5);
925 m_auth_token_text->Hide();
926
927 m_auth_token_tctrl = new wxTextCtrl(this, wxID_ANY, "");
928 m_auth_token_tctrl->SetMinSize(wxSize(column2width, -1));
929 fgSizer5->Add(m_auth_token_tctrl, 1, wxEXPAND | wxTOP, 5);
930 m_auth_token_tctrl->SetValue("orvar");
931 m_auth_token_tctrl->Hide();
932
933 fgSizer5->AddSpacer(1);
934 fgSizer5->Add(new wxStaticText(this, wxID_ANY, ""), 0, wxALL, 2);
935
936 m_precision_text =
937 new wxStaticText(this, wxID_ANY, _("APB bearing precision"));
938 m_precision_text->Wrap(-1);
939 m_precision_text->SetMinSize(wxSize(column1width, -1));
940 fgSizer5->Add(m_precision_text, 0, wxALL, 2);
941 m_precision_text->Hide();
942
943 wxString m_choicePrecisionChoices[] = {_("x"), _("x.x"), _("x.xx"),
944 _("x.xxx"), _("x.xxxx")};
945 int m_choicePrecisionNChoices =
946 sizeof(m_choicePrecisionChoices) / sizeof(wxString);
947 m_precision_choice =
948 new wxChoice(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
949 m_choicePrecisionNChoices, m_choicePrecisionChoices, 0);
950 // m_choicePrecision->Bind(wxEVT_MOUSEWHEEL,
951 // &ConnectionEditDialog::OnWheelChoice, this);
952
953 m_precision_choice->SetSelection(g_NMEAAPBPrecision);
954 fgSizer5->Add(m_precision_choice, 0, wxALL, 2);
955 m_precision_choice->Hide();
956 OnExpertModeChange();
957
958 m_garmin_host_chkbox =
959 new wxCheckBox(this, wxID_ANY, _("Use Garmin (GRMN) mode for input"));
960 m_garmin_host_chkbox->SetValue(false);
961 fgSizer5->Add(m_garmin_host_chkbox, 0, wxALL, 2);
962 fgSizer5->AddSpacer(1);
963
964 // signalK discovery enable
965 m_sk_check_discover_chkbox =
966 new wxCheckBox(this, wxID_ANY, _("Automatic server discovery"));
967 m_sk_check_discover_chkbox->SetValue(true);
968 m_sk_check_discover_chkbox->SetToolTip(
969 _("If checked, signal K server will be discovered automatically"));
970
971 fgSizer5->Add(m_sk_check_discover_chkbox, 0, wxALL, 2);
972
973 // signal K "Discover now" button
974 m_sk_discover_btn = new wxButton(this, wxID_ANY, _("Discover now..."));
975 m_sk_discover_btn->Hide();
976 fgSizer5->Add(m_sk_discover_btn, 0, wxALL, 2);
977
978 // signalK Server Status
979 m_sk_server_status_text = new wxStaticText(this, wxID_ANY, "");
980 fgSizer5->Add(m_sk_server_status_text, 0, wxALL, 2);
981
982 m_in_filter_sizer = new wxStaticBoxSizer(
983 new wxStaticBox(this, wxID_ANY, _("Input filtering")), wxVERTICAL);
984 m_connection_props_sizer->Add(m_in_filter_sizer,
985 wxSizerFlags().Expand().Border());
986
987 wxBoxSizer* bSizer9;
988 bSizer9 = new wxBoxSizer(wxHORIZONTAL);
989
990 m_accept_radiobtn =
991 new wxRadioButton(this, wxID_ANY, _("Accept only sentences"));
992 bSizer9->Add(m_accept_radiobtn, 0, wxALL, 5);
993
994 m_ignore_radiobtn = new wxRadioButton(this, wxID_ANY, _("Ignore sentences"));
995 bSizer9->Add(m_ignore_radiobtn, 0, wxALL, 5);
996
997 m_in_filter_sizer->Add(bSizer9, 0, wxEXPAND, 5);
998
999 wxBoxSizer* bSizer11;
1000 bSizer11 = new wxBoxSizer(wxHORIZONTAL);
1001 m_in_filter_sizer->Add(bSizer11, 0, wxEXPAND, 5);
1002
1003 m_input_stc_tctrl =
1004 new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
1005 wxDefaultSize, wxTE_READONLY);
1006 bSizer11->Add(m_input_stc_tctrl, 1, wxALL | wxEXPAND, 5);
1007
1008 m_input_stc_list_btn = new wxButton(this, wxID_ANY, "...", wxDefaultPosition,
1009 wxDefaultSize, wxBU_EXACTFIT);
1010 bSizer11->Add(m_input_stc_list_btn, 0, wxALL, 5);
1011
1012 bSizer11->AddSpacer(GetCharWidth() * 5);
1013
1014 m_out_filter_sizer = new wxStaticBoxSizer(
1015 new wxStaticBox(this, wxID_ANY, _("Output filtering")), wxVERTICAL);
1016 m_connection_props_sizer->Add(m_out_filter_sizer, 0, wxEXPAND, 5);
1017
1018 wxBoxSizer* bSizer10;
1019 bSizer10 = new wxBoxSizer(wxHORIZONTAL);
1020
1021 m_o_accept_radiobtn =
1022 new wxRadioButton(this, wxID_ANY, _("Transmit sentences"),
1023 wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
1024 bSizer10->Add(m_o_accept_radiobtn, 0, wxALL, 5);
1025
1026 m_o_ignore_radiobtn = new wxRadioButton(this, wxID_ANY, _("Drop sentences"),
1027 wxDefaultPosition, wxDefaultSize, 0);
1028 bSizer10->Add(m_o_ignore_radiobtn, 0, wxALL, 5);
1029
1030 m_out_filter_sizer->Add(bSizer10, 0, wxEXPAND, 5);
1031
1032 wxBoxSizer* bSizer12;
1033 bSizer12 = new wxBoxSizer(wxHORIZONTAL);
1034 m_out_filter_sizer->Add(bSizer12, 0, wxEXPAND, 5);
1035
1036 m_output_stc_tctrl =
1037 new wxTextCtrl(this, wxID_ANY, wxEmptyString, wxDefaultPosition,
1038 wxDefaultSize, wxTE_READONLY);
1039 bSizer12->Add(m_output_stc_tctrl, 1, wxALL | wxEXPAND, 5);
1040
1041 m_output_stc_list_btn = new wxButton(this, wxID_ANY, "...", wxDefaultPosition,
1042 wxDefaultSize, wxBU_EXACTFIT);
1043 bSizer12->Add(m_output_stc_list_btn, 0, wxALL, 5);
1044
1045 bSizer12->AddSpacer(GetCharWidth() * 5);
1046
1047 m_connection_props_sizer->AddSpacer(20);
1048
1049 ConnectControls();
1050
1051 SetInitialSettings();
1052
1053 ShowTypeCommon();
1054
1055 ShowNMEACommon(true);
1056 ShowNMEASerial(true);
1057 ShowNMEANet(false);
1058 ShowNMEACAN(false);
1060 m_is_conn_saved = true;
1061
1062 GetSizer()->Fit(this);
1063
1064 m_new_device_listener.Init(
1065 SystemEvents::GetInstance().evt_dev_change,
1066 [&](ObservedEvt&) { LoadSerialPorts(m_port_combo); });
1067}
1068
1069void ConnectionEditDialog::OnAddressChange(wxFocusEvent& ev) {
1070 int selection = m_net_view_choice->GetSelection();
1071 if (selection != wxNOT_FOUND) {
1072 std::string type = m_net_view_choice->GetString(selection).ToStdString();
1073 if (type == kMulticastClient || type == kMulticastServer) {
1074 auto address = m_net_address_tctrl->GetValue().ToStdString();
1075 if (!IsAddressMultiCast(address)) {
1076 auto dlg = wxMessageDialog(this, _("Illegal multicast address"),
1077 _("OpenCPN warning"), wxOK | wxICON_WARNING);
1078 dlg.ShowModal();
1079 }
1080 }
1081 }
1082 ev.Skip();
1083}
1084
1085void ConnectionEditDialog::OnExpertModeChange() {
1086 bool advanced = m_net_expert_chkbox->GetValue();
1087 int view = m_net_view_choice->GetSelection();
1088 m_net_view_choice->Clear();
1089 if (advanced) {
1090 for (const auto& choice : kAdvancedNetViews)
1091 m_net_view_choice->Append(choice);
1092 m_net_type_choice_text->SetLabel(_("Connection type"));
1093 if (m_net_comment_text) m_net_comment_text->Show();
1094 if (m_net_comment_tctrl) m_net_comment_tctrl->Show();
1095 } else {
1096 if (view != wxNOT_FOUND) {
1097 if (static_cast<size_t>(view) >= kBasicNetViews.size()) view = 0;
1098 }
1099 for (const auto& choice : kBasicNetViews) m_net_view_choice->Append(choice);
1100 m_net_type_choice_text->SetLabel(_("Connect to"));
1101 if (m_net_comment_text) m_net_comment_text->Hide();
1102 if (m_net_comment_tctrl) m_net_comment_tctrl->Hide();
1103 }
1104 m_net_view_choice->SetSelection(view);
1105 auto view_str = m_net_view_choice->GetStringSelection().ToStdString();
1106 if (!view_str.empty()) ConfigureControlsForView(view_str);
1107 Layout();
1108}
1109
1110void ConnectionEditDialog::OnCancelClick() {
1111 m_on_edit_click(nullptr, false, false);
1112}
1113
1114void ConnectionEditDialog::OnOKClick() {
1115 if (m_cp_original) {
1116 int selection = m_net_view_choice->GetSelection();
1117 if (selection != wxNOT_FOUND) {
1118 std::string selected =
1119 m_net_view_choice->GetString(selection).ToStdString();
1120 }
1121 }
1122 bool ok = true;
1123 if (m_net_address_tctrl->IsEnabled() &&
1124 m_net_address_tctrl->IsShownOnScreen()) {
1125 auto net_address = dynamic_cast<TextCtrlWithHelp*>(m_net_address_tctrl);
1126 if (net_address) ok = CheckAddress(this, *net_address);
1127 }
1128 if (m_net_port_tctrl->IsEnabled() && m_net_port_tctrl->IsShownOnScreen()) {
1129 auto net_port = dynamic_cast<TextCtrlWithHelp*>(m_net_port_tctrl);
1130 if (net_port) ok = ok && CheckPort(this, *net_port);
1131 }
1132 ok = ok && CheckIoDirections(this, m_output_chkbox, m_input_chkbox);
1133 if (ok) m_on_edit_click(m_cp_original, m_new_mode, true);
1134}
1135
1136void ConnectionEditDialog::SetInitialSettings() {
1137 LoadSerialPorts(m_port_combo);
1138}
1139
1140// void ConnectionEditDialog::OnWheelChoice(wxMouseEvent& event) {
1141// return;
1142// }
1143
1144void ConnectionEditDialog::SetSelectedConnectionPanel(
1145 ConnectionParamsPanel* panel) {
1146 // Only one panel can be selected at any time
1147 // Clear any selections
1148
1149 if (m_selected_conn_params && m_selected_conn_params->options_panel)
1150 m_selected_conn_params->options_panel->SetSelected(false);
1151
1152 if (panel) {
1153 m_selected_conn_params = panel->m_pConnectionParams;
1154 panel->SetSelected(true);
1155 SetConnectionParams(m_selected_conn_params);
1156 m_remove_btn->Enable();
1157 m_remove_btn->Show();
1158 m_add_btn->Disable();
1159 m_conn_edit_statbox->SetLabel(_("Edit Connection"));
1160
1161 } else {
1162 m_selected_conn_params = nullptr;
1163 m_remove_btn->Disable();
1164 m_add_btn->Enable();
1165 m_add_btn->Show();
1166 m_conn_edit_statbox->SetLabel("");
1167 ClearNMEAForm();
1168 }
1169
1170 // Scroll the panel to allow the user to see more of the NMEA parameter
1171 // settings area
1172 // wxPoint buttonPosition = m_buttonAdd->GetPosition();
1173 // this->Scroll(-1, buttonPosition.y / m_parent->GetScrollRate());
1174}
1175
1176void ConnectionEditDialog::SetPropsLabel(const wxString& label) {
1177 m_conn_edit_statbox->SetLabel(label);
1178}
1179
1180void ConnectionEditDialog::EnableConnection(ConnectionParams* conn,
1181 bool value) {
1182 if (conn) {
1183 // conn->bEnabled = value;
1184 conn->is_setup = false; // trigger a rebuild/takedown of the connection
1185 m_conn_enabled = conn->is_enabled;
1186 }
1187}
1188
1189void ConnectionEditDialog::OnValChange(wxCommandEvent& event) { event.Skip(); }
1190
1191void ConnectionEditDialog::OnScanBtClick(wxCommandEvent& event) {
1192 if (m_bt_scanning)
1193 StopBtScan();
1194 else {
1195 m_bt_no_change_counter = 0;
1196 m_bt_last_result_count = 0;
1197
1198 Bind(wxEVT_TIMER, &ConnectionEditDialog::OnBtScanTimer, this,
1199 ID_BT_SCANTIMER);
1200 m_bt_scan_timer.Start(1000, wxTIMER_CONTINUOUS);
1201 g_Platform->startBluetoothScan();
1202 m_bt_scanning = 1;
1203 if (m_scan_bt_btn) {
1204 m_scan_bt_btn->SetLabel(_("Stop Scan"));
1205 }
1206 }
1207}
1208
1209void ConnectionEditDialog::OnBtScanTimer(wxTimerEvent& event) {
1210 if (m_bt_scanning) {
1211 m_bt_scanning++;
1212
1213 m_bt_scan_results = g_Platform->getBluetoothScanResults();
1214
1215 m_bt_data_sources_choice->Clear();
1216 m_bt_data_sources_choice->Append(m_bt_scan_results[0]); // scan status
1217
1218 unsigned int i = 1;
1219 while ((i + 1) < m_bt_scan_results.GetCount()) {
1220 wxString item1 = m_bt_scan_results[i] + ";";
1221 wxString item2 = m_bt_scan_results.Item(i + 1);
1222 m_bt_data_sources_choice->Append(item1 + item2);
1223
1224 i += 2;
1225 }
1226
1227 if (m_bt_scan_results.GetCount() > 1) {
1228 m_bt_data_sources_choice->SetSelection(1);
1229 }
1230
1231 // Watch for changes. When no changes occur after n seconds, stop the
1232 // scan
1233 if (m_bt_no_change_counter > 5) StopBtScan();
1234
1235 if ((int)m_bt_scan_results.GetCount() == m_bt_last_result_count)
1236 m_bt_no_change_counter++;
1237 else
1238 m_bt_no_change_counter = 0;
1239
1240 m_bt_last_result_count = static_cast<int>(m_bt_scan_results.GetCount());
1241
1242 // Absolute fallback
1243 if (m_bt_scanning >= 15) {
1244 StopBtScan();
1245 }
1246 } else {
1247 }
1248}
1249
1250void ConnectionEditDialog::StopBtScan() {
1251 m_bt_scan_timer.Stop();
1252
1253 g_Platform->stopBluetoothScan();
1254
1255 m_bt_scanning = 0;
1256
1257 if (m_scan_bt_btn) {
1258 m_scan_bt_btn->SetLabel(_("BT Scan"));
1259 m_scan_bt_btn->Enable();
1260 }
1261}
1262
1263void ConnectionEditDialog::OnConnValChange(wxCommandEvent& event) {
1264 m_is_conn_saved = false;
1265 event.Skip();
1266}
1267
1268void ConnectionEditDialog::OnTypeSerialSelected(wxCommandEvent& event) {
1269 OnConnValChange(event);
1270 SetNMEAFormToSerial();
1271}
1272
1273void ConnectionEditDialog::OnTypeNetSelected(wxCommandEvent& event) {
1274 OnConnValChange(event);
1275 SetNMEAFormToNet();
1276}
1277
1278void ConnectionEditDialog::OnTypeCANSelected(wxCommandEvent& event) {
1279 OnConnValChange(event);
1280 SetNMEAFormToCAN();
1281}
1282
1283void ConnectionEditDialog::OnTypeGPSSelected(wxCommandEvent& event) {
1284 OnConnValChange(event);
1285 SetNMEAFormToGPS();
1286}
1287
1288void ConnectionEditDialog::OnTypeBTSelected(wxCommandEvent& event) {
1289 OnConnValChange(event);
1290 SetNMEAFormToBT();
1291}
1292
1293void ConnectionEditDialog::OnUploadFormatChange(wxCommandEvent& event) {
1294 if (event.GetEventObject() == m_garmin_upload_host_chkbox &&
1295 event.IsChecked())
1296 m_furuno_gp3x_chkbox->SetValue(false);
1297 else if (event.GetEventObject() == m_furuno_gp3x_chkbox && event.IsChecked())
1298 m_garmin_upload_host_chkbox->SetValue(false);
1299
1300 OnConnValChange(event);
1301 event.Skip();
1302}
1303
1304void ConnectionEditDialog::ShowTypeCommon(bool visible) {
1305 m_type_serial_radiobtn->Show(visible);
1306 m_type_net_radiobtn->Show(visible);
1307#if defined(__linux__) && !defined(__ANDROID__) && !defined(__WXOSX__)
1308 m_type_can_radiobtn->Show(visible);
1309#endif
1310 if (m_type_internal_gps_radiobtn) m_type_internal_gps_radiobtn->Show(visible);
1311 if (m_type_internal_bt_radiobtn) m_type_internal_bt_radiobtn->Show(visible);
1312}
1313
1314void ConnectionEditDialog::ShowNMEACommon(bool visible) {
1315 bool advanced = m_advanced;
1316 m_input_chkbox->Show(visible);
1317 m_output_chkbox->Show(visible);
1318 if (!visible) {
1319 m_out_filter_sizer->SetDimension(0, 0, 0, 0);
1320 m_in_filter_sizer->SetDimension(0, 0, 0, 0);
1321 m_connection_props_sizer->SetDimension(0, 0, 0, 0);
1322 m_conn_edit_statbox->SetLabel("");
1323 }
1324
1325 m_sk_check_discover_chkbox->Hide(); // Provisional
1326 m_sk_discover_btn->Hide();
1327
1328 const bool bin_enable = (m_input_chkbox->IsChecked() && advanced);
1329 ShowInFilter(visible && bin_enable);
1330 const bool bout_enable = (m_output_chkbox->IsChecked() && advanced);
1331 ShowOutFilter(visible && bout_enable);
1332
1333 m_is_nmea_params_shown = visible;
1334}
1335
1336void ConnectionEditDialog::ShowNMEANet(bool visible) {
1337 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1338
1339 m_net_addr_text->Show(visible);
1340 m_net_address_tctrl->Show(visible);
1341 m_net_data_protocol_text->Show(visible);
1342 m_net_port_text->Show(visible);
1343 m_net_data_protocol_choice->Show(visible);
1344 m_net_port_tctrl->Show(visible);
1345 m_net_expert_chkbox->Show(visible);
1346 if (m_net_expert_chkbox->GetValue()) {
1347 m_net_comment_text->Show(visible);
1348 m_net_comment_tctrl->Show(visible);
1349 }
1350 m_net_expert_box_text->Show(visible);
1351 m_net_type_choice_text->Show(visible);
1352 m_net_view_choice->Show(visible);
1353 m_garmin_host_chkbox->Hide();
1355}
1356
1357void ConnectionEditDialog::ShowNMEASerial(bool visible) {
1358 bool advanced = m_advanced;
1359 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1360
1361 m_ser_baudrate_text->Show(visible);
1362 m_baud_rate_choice->Show(visible);
1363 m_ser_port_text->Show(visible);
1364 m_port_combo->Show(visible);
1365 m_ser_protocol_text->Show(visible);
1366 m_serial_protocol_choice->Show(visible);
1367 m_garmin_host_chkbox->Show(visible && advanced);
1368 m_ser_comment_text->Show(visible);
1369 m_serial_comment_tctrl->Show(visible);
1370 m_net_comment_text->Hide();
1371 m_net_comment_tctrl->Hide();
1372}
1373
1374void ConnectionEditDialog::ShowNMEAGPS(bool visible) {
1375 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1376
1377 m_sk_check_discover_chkbox->Hide();
1378 m_sk_discover_btn->Hide();
1379 m_output_chkbox->Hide();
1380}
1381
1382void ConnectionEditDialog::ShowNMEACAN(bool visible) {
1383 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1384 m_can_source_text->Show(visible);
1385 m_can_source_choice->Show(visible);
1386 if (visible && m_dlg_ok_btn && m_can_source_choice->IsEmpty())
1387 m_dlg_ok_btn->Enable(false);
1388}
1389
1390void ConnectionEditDialog::ShowNMEABT(bool visible) {
1391 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1392
1393 if (visible) {
1394 if (m_scan_bt_btn) m_scan_bt_btn->Show();
1395 if (m_bt_pairs_text) m_bt_pairs_text->Show();
1396 if (m_bt_data_sources_choice) {
1397 if (m_bt_data_sources_choice->GetCount() > 1)
1398 m_bt_data_sources_choice->SetSelection(1);
1399 m_bt_data_sources_choice->Show();
1400 }
1401 } else {
1402 if (m_scan_bt_btn) m_scan_bt_btn->Hide();
1403 if (m_bt_pairs_text) m_bt_pairs_text->Hide();
1404 if (m_bt_data_sources_choice) m_bt_data_sources_choice->Hide();
1405 }
1406 m_sk_check_discover_chkbox->Hide();
1407 m_sk_check_discover_chkbox->Hide(); // Provisional
1408 m_sk_discover_btn->Hide();
1409 m_output_stc_tctrl->Show(visible);
1410 m_output_stc_list_btn->Show(visible);
1411 m_output_chkbox->Show(visible);
1412}
1413
1414void ConnectionEditDialog::SetNMEAFormToSerial() {
1415 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1416
1417 ShowNMEACommon(true);
1418 ShowNMEANet(false);
1419 ShowNMEAGPS(false);
1420 ShowNMEABT(false);
1421 ShowNMEASerial(true);
1422 ShowNMEACAN(false);
1423 SetDSFormRWStates();
1424 LayoutDialog();
1425}
1426
1427void ConnectionEditDialog::SetNMEAFormToNet() {
1428 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1429
1430 ShowNMEACommon(true);
1431 ShowNMEANet(true);
1432 ShowNMEAGPS(false);
1433 ShowNMEABT(false);
1434 ShowNMEASerial(false);
1435 ShowNMEACAN(false);
1436 SetDSFormRWStates();
1437
1438 LayoutDialog();
1439}
1440
1441void ConnectionEditDialog::SetNMEAFormToCAN() {
1442 if (m_dlg_ok_btn) m_dlg_ok_btn->Enable();
1443
1444 ShowNMEACommon(false);
1445 ShowNMEANet(false);
1446 ShowNMEAGPS(false);
1447 ShowNMEABT(false);
1448 ShowNMEASerial(false);
1449 ShowNMEACAN(true);
1450 m_in_filter_sizer->Show(false);
1451 m_out_filter_sizer->Show(false);
1452 SetDSFormRWStates();
1453
1454 LayoutDialog();
1455}
1456
1457void ConnectionEditDialog::SetNMEAFormToGPS() {
1458 ShowNMEACommon(true);
1459 ShowNMEANet(false);
1460 ShowNMEAGPS(true);
1461 ShowNMEABT(false);
1462 ShowNMEASerial(false);
1463 ShowNMEACAN(false);
1464
1465 // m_container->FitInside();
1466 // Fit();
1467 SetDSFormRWStates();
1468 LayoutDialog();
1469}
1470
1471void ConnectionEditDialog::SetNMEAFormToBT() {
1472 ShowNMEACommon(true);
1473 ShowNMEANet(false);
1474 ShowNMEAGPS(false);
1475 ShowNMEABT(true);
1476 ShowNMEASerial(false);
1477 ShowNMEACAN(false);
1478
1479 // m_container->FitInside();
1480 // Fit();
1481 SetDSFormRWStates();
1482 LayoutDialog();
1483}
1484
1485void ConnectionEditDialog::ClearNMEAForm() {
1486 ShowNMEACommon(false);
1487 ShowNMEANet(false);
1488 ShowNMEAGPS(false);
1489 ShowNMEABT(false);
1490 ShowNMEASerial(false);
1491 ShowNMEACAN(false);
1492
1493 // m_container->FitInside();
1494 // Fit();
1495}
1496
1497/*
1498 * Transitional: The network view is handled by OnConnectionTypeChange()
1499 * and RefreshAdvancedDetails(), remaining is handled here
1500 */
1501void ConnectionEditDialog::SetDSFormOptionVizStates() {
1502 bool advanced = m_advanced;
1503 m_collapse_box->ShowItems(true);
1504 m_input_chkbox->Show();
1505 m_output_chkbox->Show();
1506
1507 ShowInFilter(advanced);
1508 ShowOutFilter(advanced);
1509 // Discovery hidden until it works.
1510 // m_cbCheckSKDiscover->Show();
1511 // m_ButtonSKDiscover->Show();
1512 m_sk_server_status_text->Show(advanced);
1513
1514 if (m_type_serial_radiobtn->GetValue()) {
1515 m_sk_check_discover_chkbox->Hide();
1516 m_sk_discover_btn->Hide();
1517 m_sk_server_status_text->Hide();
1518 bool n0183ctlenabled =
1519 (DataProtocol)m_serial_protocol_choice->GetSelection() ==
1520 DataProtocol::PROTO_NMEA0183;
1521 bool n2kctlenabled =
1522 (DataProtocol)m_serial_protocol_choice->GetSelection() ==
1523 DataProtocol::PROTO_NMEA2000;
1524 if (!n0183ctlenabled) {
1525 if (n2kctlenabled) {
1526 m_input_chkbox->Show();
1527 m_output_chkbox->Show();
1528 } else {
1529 m_input_chkbox->Hide();
1530 m_output_chkbox->Hide();
1531 }
1532 ShowOutFilter(false);
1533 ShowInFilter(false);
1534 m_net_data_protocol_text->Hide();
1535 m_net_data_protocol_choice->Hide();
1536 m_net_expert_chkbox->Hide();
1537 m_net_type_choice_text->Hide();
1538 m_net_view_choice->Hide();
1539 m_net_expert_chkbox->Hide();
1540 m_net_type_choice_text->Hide();
1541 m_net_expert_box_text->Hide();
1542 m_net_view_choice->Hide();
1543 } else {
1544 m_input_chkbox->Show();
1545 m_input_chkbox->Enable();
1546
1547 ShowInFilter(m_input_chkbox->IsChecked() && advanced);
1548 ShowOutFilter(m_output_chkbox->IsChecked() && advanced);
1549
1550 m_garmin_host_chkbox->Show(m_input_chkbox->IsChecked() && advanced);
1551 }
1552 }
1553
1554 if (m_type_internal_gps_radiobtn &&
1555 m_type_internal_gps_radiobtn->GetValue()) {
1556 m_sk_check_discover_chkbox->Hide();
1557 m_sk_discover_btn->Hide();
1558 m_sk_server_status_text->Hide();
1559 m_output_chkbox->Hide();
1560 m_input_chkbox->Hide();
1561 ShowOutFilter(false);
1562 ShowInFilter(false);
1563 m_garmin_host_chkbox->Hide();
1564 m_collapse_box->ShowItems(false);
1565 }
1566
1567 if (m_type_internal_bt_radiobtn && m_type_internal_bt_radiobtn->GetValue()) {
1568 m_sk_check_discover_chkbox->Hide();
1569 m_sk_discover_btn->Hide();
1570 m_sk_server_status_text->Hide();
1571
1572 ShowInFilter(m_input_chkbox->IsChecked() && advanced);
1573 ShowOutFilter(m_output_chkbox->IsChecked() && advanced);
1574 }
1575
1576 if (m_type_can_radiobtn->GetValue()) {
1577 m_sk_check_discover_chkbox->Hide();
1578 m_sk_discover_btn->Hide();
1579 m_sk_server_status_text->Hide();
1580 m_garmin_host_chkbox->Hide();
1581 m_input_chkbox->Hide();
1582 m_output_chkbox->Hide();
1583
1584 ShowInFilter(false);
1585 ShowOutFilter(false);
1586
1587 m_net_data_protocol_text->Hide();
1588 m_net_data_protocol_choice->Hide();
1589 m_net_expert_chkbox->Hide();
1590 m_net_type_choice_text->Hide();
1591 m_net_expert_box_text->Hide();
1592 m_net_view_choice->Hide();
1593 m_collapse_box->Show(false);
1594 }
1595
1596 if (m_type_net_radiobtn->GetValue()) {
1597 if ((DataProtocol)m_net_data_protocol_choice->GetSelection() ==
1598 DataProtocol::PROTO_NMEA2000) {
1599 ShowInFilter(false);
1600 ShowOutFilter(false);
1601 }
1602 if ((DataProtocol)m_net_data_protocol_choice->GetSelection() ==
1603 DataProtocol::PROTO_NMEA0183) {
1604 ShowInFilter(m_input_chkbox->IsChecked() && advanced);
1605 ShowOutFilter(m_output_chkbox->IsChecked() && advanced);
1606 }
1607 }
1608}
1609
1610/*
1611 * Transitional: The network view is handled by OnConnectionTypeChange()
1612 * and RefreshAdvancedDetails(), remaining is handled here
1613 */
1614void ConnectionEditDialog::SetDSFormRWStates() {
1615 if (m_type_serial_radiobtn->GetValue()) {
1616 m_input_chkbox->Enable(true);
1617 m_output_chkbox->Enable(true);
1618 ShowInFilter();
1619 ShowOutFilter(m_output_chkbox->IsChecked());
1620 } else {
1621 m_o_accept_radiobtn->Enable(true);
1622 m_o_ignore_radiobtn->Enable(true);
1623 m_output_stc_list_btn->Enable(true);
1624 }
1625 SetDSFormOptionVizStates();
1626}
1627
1628void ConnectionEditDialog::ShowInFilter(bool bshow) {
1629 m_in_filter_sizer->GetStaticBox()->Show(bshow);
1630 m_accept_radiobtn->Show(bshow);
1631 m_ignore_radiobtn->Show(bshow);
1632 m_input_stc_tctrl->Show(bshow);
1633 m_input_stc_list_btn->Show(bshow);
1634}
1635
1636void ConnectionEditDialog::ShowOutFilter(bool bshow) {
1637 m_out_filter_sizer->GetStaticBox()->Show(bshow);
1638 m_o_accept_radiobtn->Show(bshow);
1639 m_o_ignore_radiobtn->Show(bshow);
1640 m_output_stc_tctrl->Show(bshow);
1641 m_output_stc_list_btn->Show(bshow);
1642}
1643
1644void ConnectionEditDialog::PreloadControls(ConnectionParams* cp) {
1645 m_cp_original = cp;
1646 SetConnectionParams(cp);
1647}
1648
1649void ConnectionEditDialog::SetConnectionParams(ConnectionParams* cp) {
1650 if (cp->type == NETWORK && cp->direction == PortDirection::kInput) {
1651 // work around buggy pre 5.16 configurations which have "localhost" or
1652 // 127.0.0.1 instead of the correct 0.0.0.0
1653 auto& address = cp->network_address;
1654 if (address == "127.0.0.1" || address == "localhost") address = "0.0.0.0";
1655 }
1656 const std::string view = NetViewByConnection(cp);
1657 auto found = std::find(kBasicNetViews.begin(), kBasicNetViews.end(), view);
1658 m_net_expert_chkbox->SetValue(found == kBasicNetViews.end());
1659 m_net_view_choice->Clear();
1660 if (found == kBasicNetViews.end())
1661 for (const auto& v : kAdvancedNetViews) m_net_view_choice->Append(v);
1662 else
1663 for (const auto& v : kBasicNetViews) m_net_view_choice->Append(v);
1664 std::vector<std::string> all_views = kBasicNetViews;
1665 for (const auto& v : kAdvancedNetViews) all_views.push_back(v);
1666 found = std::find(all_views.begin(), all_views.end(), view);
1667 if (found != all_views.end()) {
1668 int select_ix = m_net_view_choice->FindString(*found);
1669 if (select_ix != wxNOT_FOUND) m_net_view_choice->SetSelection(select_ix);
1670 }
1671 if (wxNOT_FOUND == m_port_combo->FindString(cp->serial_port))
1672 m_port_combo->Append(cp->serial_port);
1673
1674 m_port_combo->Select(m_port_combo->FindString(cp->serial_port));
1675
1676 m_garmin_host_chkbox->SetValue(cp->is_garmin);
1677 m_sk_check_discover_chkbox->SetValue(cp->auto_sk_discover);
1678 if (view == kUdpReceive || view == kUdpInput) {
1679 m_input_chkbox->SetValue(true);
1680 m_input_chkbox->Disable();
1681 m_output_chkbox->SetValue(false);
1682 m_output_chkbox->Disable();
1683 } else {
1684 m_input_chkbox->SetValue(cp->direction != PortDirection::kOutput);
1685 m_output_chkbox->SetValue(cp->direction != PortDirection::kInput);
1686 }
1687
1688 if (cp->input_sentence_list_type == WHITELIST)
1689 m_accept_radiobtn->SetValue(true);
1690 else
1691 m_ignore_radiobtn->SetValue(true);
1692 if (cp->output_sentence_list_type == WHITELIST)
1693 m_o_accept_radiobtn->SetValue(true);
1694 else
1695 m_o_ignore_radiobtn->SetValue(true);
1696 m_input_stc_tctrl->SetValue(StringArrayToString(cp->input_sentence_list));
1697 m_output_stc_tctrl->SetValue(StringArrayToString(cp->output_sentence_list));
1698 m_baud_rate_choice->Select(
1699 m_baud_rate_choice->FindString(wxString::Format("%d", cp->baudrate)));
1700 m_serial_protocol_choice->Select(cp->data_protocol); // TODO
1701 auto net_address = dynamic_cast<TextCtrlWithHelp*>(m_net_address_tctrl);
1702 if (net_address) m_net_address_tctrl->ChangeValue(cp->network_address);
1703
1704 m_net_data_protocol_choice->Select(cp->data_protocol); // TODO
1705
1706 if (cp->network_port == 0)
1707 m_net_port_tctrl->ChangeValue("");
1708 else
1709 m_net_port_tctrl->ChangeValue(std::to_string(cp->network_port));
1710
1711 if (cp->type == SERIAL) {
1712 m_type_serial_radiobtn->SetValue(true);
1713 SetNMEAFormToSerial();
1714 SetNMEAFormForSerialProtocol();
1715 } else if (cp->type == NETWORK) {
1716 m_type_net_radiobtn->SetValue(true);
1717 SetNMEAFormToNet();
1718 } else if (cp->type == SOCKETCAN) {
1719 m_type_can_radiobtn->SetValue(true);
1720 SetNMEAFormToCAN();
1721 } else if (cp->type == INTERNAL_GPS) {
1722 if (m_type_internal_gps_radiobtn)
1723 m_type_internal_gps_radiobtn->SetValue(true);
1724 SetNMEAFormToGPS();
1725 } else if (cp->type == INTERNAL_BT) {
1726 if (m_type_internal_bt_radiobtn)
1727 m_type_internal_bt_radiobtn->SetValue(true);
1728 SetNMEAFormToBT();
1729
1730 // Preset the source selector
1731 wxString bts = cp->network_address + ";" + cp->serial_port;
1732 m_bt_data_sources_choice->Clear();
1733 m_bt_data_sources_choice->Append(bts);
1734 m_bt_data_sources_choice->SetSelection(0);
1735 } else {
1736 ClearNMEAForm();
1737 }
1738
1739 if (cp->type == SERIAL) {
1740 m_serial_comment_tctrl->SetValue(cp->user_comment);
1741 } else if (cp->type == NETWORK) {
1742 m_net_comment_tctrl->SetValue(cp->user_comment);
1743 ConfigureControlsForView(view);
1744 OnExpertModeChange();
1745 }
1746
1747 m_auth_token_tctrl->SetValue(cp->auth_token);
1748
1749 m_conn_enabled = cp->is_enabled;
1750
1751 // Reset touch flag
1752 m_is_conn_saved = true;
1753}
1754
1755void ConnectionEditDialog::SetDefaultConnectionParams() {
1756 if (m_port_combo && !m_port_combo->IsListEmpty()) {
1757 m_port_combo->Select(0);
1758 m_port_combo->SetValue(wxEmptyString); // These two broke it
1759 }
1760 m_garmin_host_chkbox->SetValue(false);
1761 m_input_chkbox->SetValue(true);
1762 m_output_chkbox->SetValue(false);
1763 m_accept_radiobtn->SetValue(true);
1764 m_o_accept_radiobtn->SetValue(true);
1765 m_input_stc_tctrl->SetValue(wxEmptyString);
1766 m_output_stc_tctrl->SetValue(wxEmptyString);
1767 m_baud_rate_choice->Select(m_baud_rate_choice->FindString("4800"));
1768 // m_choiceSerialProtocol->Select( cp->Protocol ); // TODO
1769
1770 m_net_comment_tctrl->SetValue(wxEmptyString);
1771 m_serial_comment_tctrl->SetValue(wxEmptyString);
1772 m_auth_token_tctrl->SetValue(wxEmptyString);
1773 auto net_address = dynamic_cast<TextCtrlWithHelp*>(m_net_address_tctrl);
1774 if (net_address) net_address->RestoreHelp();
1775 auto net_port = dynamic_cast<TextCtrlWithHelp*>(m_net_port_tctrl);
1776 if (net_port) net_port->RestoreHelp();
1777 bool bserial = true;
1778#ifdef __WXGTK__
1779 bserial = false;
1780#endif
1781
1782#ifdef __WXOSX__
1783 bserial = false;
1784#endif
1785
1786#ifdef __ANDROID__
1787 if (m_type_internal_gps_radiobtn) {
1788 m_type_internal_gps_radiobtn->SetValue(true);
1789 SetNMEAFormToGPS();
1790 } else {
1791 m_type_net_radiobtn->SetValue(true);
1792 SetNMEAFormToNet();
1793 }
1794#else
1795 m_type_serial_radiobtn->SetValue(bserial);
1796 m_type_net_radiobtn->SetValue(!bserial);
1797 bserial ? SetNMEAFormToSerial() : SetNMEAFormToNet();
1798 m_type_can_radiobtn->SetValue(false);
1799#endif
1800
1801 m_conn_enabled = true;
1802
1803 // Reset touch flag
1804 m_is_conn_saved = false;
1805}
1806
1807void ConnectionEditDialog::LayoutDialog() {
1808 m_net_props_sizer->Layout();
1809 m_ser_props_sizer->Layout();
1810 this->Layout();
1811 this->Fit();
1812 GetSizer()->Layout();
1813}
1814
1815void ConnectionEditDialog::UpdateSourceList(bool bResort) {
1816 for (auto* cp : TheConnectionParams()) {
1817 ConnectionParamsPanel* panel = cp->options_panel;
1818 if (panel) panel->Update(cp);
1819 }
1820
1821 m_scroll_win_connections->Layout();
1822}
1823
1824void ConnectionEditDialog::OnSelectDatasource(wxListEvent& event) {
1825 SetConnectionParams(TheConnectionParams()[event.GetData()]);
1826 m_remove_btn->Enable();
1827 m_remove_btn->Show();
1828 event.Skip();
1829}
1830
1831void ConnectionEditDialog::OnDiscoverButton(wxCommandEvent& event) {
1832#if 0 // FIXME (dave)
1833 wxString ip;
1834 int port;
1835 std::string serviceIdent =
1836 std::string("_signalk-ws._tcp.local."); // Works for node.js server
1837
1838 g_Platform->ShowBusySpinner();
1839
1840 if (SignalKDataStream::DiscoverSKServer(serviceIdent, ip, port,
1841 1)) // 1 second scan
1842 {
1843 m_tNetAddress->SetValue(ip);
1844 m_tNetPort->SetValue(wxString::Format("%i", port));
1845 UpdateDiscoverStatus(_("Signal K server available."));
1846 } else {
1847 UpdateDiscoverStatus(_("Signal K server not found."));
1848 }
1849 g_Platform->HideBusySpinner();
1850#endif
1851 event.Skip();
1852}
1853
1854void ConnectionEditDialog::UpdateDiscoverStatus(const wxString& stat) {
1855 m_sk_server_status_text->SetLabel(stat);
1856}
1857
1858void ConnectionEditDialog::OnBtnIStcs(wxCommandEvent& event) {
1859 const ListType type = m_accept_radiobtn->GetValue() ? WHITELIST : BLACKLIST;
1860 const wxArrayString list =
1861 wxStringTokenize(m_input_stc_tctrl->GetValue(), ",");
1862 SentenceListDlg dlg(m_parent, FILTER_INPUT, type, list);
1863
1864 if (dlg.ShowModal() == wxID_OK)
1865 m_input_stc_tctrl->SetValue(dlg.GetSentences());
1866}
1867
1868void ConnectionEditDialog::OnBtnOStcs(wxCommandEvent& event) {
1869 const ListType type = m_o_accept_radiobtn->GetValue() ? WHITELIST : BLACKLIST;
1870 const wxArrayString list =
1871 wxStringTokenize(m_output_stc_tctrl->GetValue(), ",");
1872 SentenceListDlg dlg(m_parent, FILTER_OUTPUT, type, list);
1873
1874 if (dlg.ShowModal() == wxID_OK)
1875 m_output_stc_tctrl->SetValue(dlg.GetSentences());
1876}
1877
1878void ConnectionEditDialog::OnNetProtocolSelected(wxCommandEvent& event) {
1879 SetDSFormRWStates();
1880 LayoutDialog();
1881 OnConnValChange(event);
1882}
1883
1884void ConnectionEditDialog::OnRbAcceptInput(wxCommandEvent& event) {
1885 OnConnValChange(event);
1886}
1887void ConnectionEditDialog::OnRbIgnoreInput(wxCommandEvent& event) {
1888 OnConnValChange(event);
1889}
1890
1891void ConnectionEditDialog::OnRbOutput(wxCommandEvent& event) {
1892 OnConnValChange(event);
1893}
1894
1895void ConnectionEditDialog::OnCbInput(wxCommandEvent& event) {
1896 const bool checked = m_input_chkbox->IsChecked();
1897 ShowInFilter(checked);
1898 SetDSFormRWStates();
1899 LayoutDialog();
1900 OnConnValChange(event);
1901}
1902
1903void ConnectionEditDialog::OnCbOutput(wxCommandEvent& event) {
1904 OnConnValChange(event);
1905 const bool is_output_enabled = m_output_chkbox->IsChecked();
1906 ShowOutFilter(is_output_enabled);
1907
1908 int selection = m_net_view_choice->GetSelection();
1909 std::string view;
1910 if (selection != wxNOT_FOUND)
1911 view = m_net_view_choice->GetString(selection).ToStdString();
1912 if (view == kUdpInput || view == kMulticastServer) {
1913 if (is_output_enabled) {
1914 // Check for a UDP input connection on the same port
1915 NetworkProtocol proto = UDP;
1916 for (auto* cp : TheConnectionParams()) {
1917 if (cp->net_protocol == proto &&
1918 cp->network_port == wxAtoi(m_net_port_tctrl->GetValue()) &&
1919 cp->direction == PortDirection::kInput) {
1920 wxString mes;
1921 bool warn = false;
1922 if (cp->is_enabled) {
1923 mes =
1924 _("There is an enabled UDP input connection that uses the "
1925 "same data port.");
1926 mes << "\n"
1927 << _("Please apply a filter on both connections to avoid a "
1928 "feedback loop.");
1929 warn = true;
1930 } else {
1931 mes =
1932 _("There is a disabled UDP Input connection that uses the "
1933 "same Dataport.");
1934 mes << "\n"
1935 << _("If you enable that input please apply a filter on both "
1936 "connections to avoid a feedback loop.");
1937 }
1938 mes << "\n"
1939 << _("Or consider using a different data port for one of them");
1940 if (warn)
1941 OCPNMessageBox(this, mes, _("OpenCPN Warning"),
1942 wxOK | wxICON_EXCLAMATION, 60);
1943 else
1944 OCPNMessageBox(this, mes, _("OpenCPN info"),
1945 wxOK | wxICON_INFORMATION, 60);
1946 break;
1947 }
1948 }
1949 }
1950 }
1951 if (view == kUdpReceive || view == kUdpInput) {
1952 m_net_address_tctrl->Hide();
1953 m_net_address_tctrl->Disable();
1954 m_net_addr_text->Hide();
1955 m_net_addr_text->Disable();
1956 }
1957 SetDSFormRWStates();
1959 LayoutDialog();
1960}
1961
1962void ConnectionEditDialog::OnCollapsedToggle(bool collapsed) {
1963 m_advanced = !collapsed;
1964 if (m_type_net_radiobtn->GetValue())
1965 SetNMEAFormForNetProtocol();
1966 else
1967 SetNMEAFormForSerialProtocol();
1969 LayoutDialog();
1970}
1971
1972void ConnectionEditDialog::OnCbAdvanced(wxCommandEvent& event) {
1973 if (m_type_net_radiobtn->GetValue())
1974 SetNMEAFormForNetProtocol();
1975 else
1976 SetNMEAFormForSerialProtocol();
1977 LayoutDialog();
1978}
1979
1980void ConnectionEditDialog::SetNMEAFormForSerialProtocol() {
1981 bool n0183ctlenabled =
1982 (DataProtocol)m_serial_protocol_choice->GetSelection() ==
1983 DataProtocol::PROTO_NMEA0183;
1984 bool advanced = m_advanced;
1985 ShowNMEACommon(n0183ctlenabled && advanced);
1986 m_garmin_host_chkbox->Show(n0183ctlenabled && advanced);
1987
1988 SetDSFormRWStates();
1989 LayoutDialog();
1990}
1991
1992void ConnectionEditDialog::SetNMEAFormForNetProtocol() {
1993 bool n0183ctlenabled =
1994 (DataProtocol)m_net_data_protocol_choice->GetSelection() ==
1995 DataProtocol::PROTO_NMEA0183;
1996 bool advanced = m_advanced;
1997 ShowNMEACommon(n0183ctlenabled && advanced);
1998 m_garmin_host_chkbox->Show(n0183ctlenabled && advanced);
1999
2000 SetDSFormRWStates();
2001 LayoutDialog();
2002}
2003
2004void ConnectionEditDialog::OnProtocolChoice(wxCommandEvent& event) {
2005 if (m_type_net_radiobtn->GetValue())
2006 SetNMEAFormForNetProtocol();
2007 else
2008 SetNMEAFormForSerialProtocol();
2009 OnConnValChange(event);
2010}
2011
2013 auto* pConnectionParams = new ConnectionParams();
2014 UpdateConnectionParamsFromControls(pConnectionParams);
2015 return pConnectionParams;
2016}
2017
2018ConnectionParams* ConnectionEditDialog::UpdateConnectionParamsFromControls(
2019 ConnectionParams* pConnectionParams) {
2020 pConnectionParams->is_valid = true;
2021 int selection = m_net_view_choice->GetSelection();
2022 if (selection != wxNOT_FOUND) {
2023 std::string s = m_net_view_choice->GetString(selection).ToStdString();
2024 }
2025 if (m_type_serial_radiobtn->GetValue())
2026 pConnectionParams->type = SERIAL;
2027 else if (m_type_net_radiobtn->GetValue())
2028 pConnectionParams->type = NETWORK;
2029 else if (m_type_internal_gps_radiobtn &&
2030 m_type_internal_gps_radiobtn->GetValue())
2031 pConnectionParams->type = INTERNAL_GPS;
2032 else if (m_type_internal_bt_radiobtn &&
2033 m_type_internal_bt_radiobtn->GetValue())
2034 pConnectionParams->type = INTERNAL_BT;
2035 else if (m_type_can_radiobtn && m_type_can_radiobtn->GetValue())
2036 pConnectionParams->type = SOCKETCAN;
2037
2038 if (m_type_net_radiobtn->GetValue()) {
2039 // Save the existing addr/port to allow closing of existing port
2040 pConnectionParams->last_network_address =
2041 pConnectionParams->network_address;
2042 pConnectionParams->last_network_port = pConnectionParams->network_port;
2043 pConnectionParams->last_net_protocol = pConnectionParams->net_protocol;
2044 pConnectionParams->last_data_protocol = pConnectionParams->data_protocol;
2045
2046 pConnectionParams->network_address =
2047 m_net_address_tctrl->GetValue().Trim(false).Trim(true);
2048 pConnectionParams->network_port =
2049 wxAtoi(m_net_port_tctrl->GetValue().Trim(false).Trim(true));
2050 int net_select = m_net_view_choice->GetSelection();
2051 std::string net_type;
2052 if (net_select != wxNOT_FOUND)
2053 net_type = m_net_view_choice->GetString(net_select).ToStdString();
2054 if (net_type == kTcpClient || net_type == kTcpServer ||
2055 net_type == kTcpDevice) {
2056 pConnectionParams->net_protocol = TCP;
2057 pConnectionParams->data_protocol =
2058 static_cast<DataProtocol>(m_net_data_protocol_choice->GetSelection());
2059 } else if (net_type == kUdpSend || net_type == kUdpInput ||
2060 net_type == kUdpReceive || net_type == kMulticastClient ||
2061 net_type == kMulticastServer) {
2062 pConnectionParams->net_protocol = UDP;
2063 pConnectionParams->data_protocol =
2064 static_cast<DataProtocol>(m_net_data_protocol_choice->GetSelection());
2065 } else if (net_type == kGpsdClient || net_type == kGpsdDevice) {
2066 pConnectionParams->net_protocol = GPSD;
2067 } else if (net_type == kSignalkClient || net_type == kSignalkDevice) {
2068 pConnectionParams->net_protocol = SIGNALK;
2069 } else {
2070 pConnectionParams->net_protocol = PROTO_UNDEFINED;
2071 };
2072 }
2073 if (m_type_serial_radiobtn->GetValue())
2074 pConnectionParams->data_protocol =
2075 (DataProtocol)m_serial_protocol_choice->GetSelection();
2076 else if (m_type_net_radiobtn->GetValue())
2077 pConnectionParams->data_protocol =
2078 (DataProtocol)m_net_data_protocol_choice->GetSelection();
2079
2080 pConnectionParams->baudrate =
2081 wxAtoi(m_baud_rate_choice->GetStringSelection());
2082 pConnectionParams->checksum_check = true;
2083 pConnectionParams->auto_sk_discover = m_sk_check_discover_chkbox->GetValue();
2084 pConnectionParams->is_garmin = m_garmin_host_chkbox->GetValue();
2085 pConnectionParams->input_sentence_list =
2086 wxStringTokenize(m_input_stc_tctrl->GetValue(), ",");
2087 if (m_accept_radiobtn->GetValue())
2088 pConnectionParams->input_sentence_list_type = WHITELIST;
2089 else
2090 pConnectionParams->input_sentence_list_type = BLACKLIST;
2091 if (m_input_chkbox->GetValue()) {
2092 if (m_output_chkbox->GetValue()) {
2093 pConnectionParams->direction = PortDirection::kInOut;
2094 } else {
2095 pConnectionParams->direction = PortDirection::kInput;
2096 }
2097 } else
2098 pConnectionParams->direction = PortDirection::kOutput;
2099
2100 pConnectionParams->output_sentence_list =
2101 wxStringTokenize(m_output_stc_tctrl->GetValue(), ",");
2102 if (m_o_accept_radiobtn->GetValue())
2103 pConnectionParams->output_sentence_list_type = WHITELIST;
2104 else
2105 pConnectionParams->output_sentence_list_type = BLACKLIST;
2106 pConnectionParams->serial_port = m_port_combo->GetValue().BeforeFirst(' ');
2107#if defined(__linux__) && !defined(__ANDROID__)
2108 if (pConnectionParams->type == SERIAL)
2109 CheckSerialAccess(m_parent, pConnectionParams->serial_port.ToStdString());
2110#endif
2111
2112 if (m_type_can_radiobtn && m_type_can_radiobtn->GetValue())
2113 pConnectionParams->data_protocol = PROTO_NMEA2000;
2114
2115 pConnectionParams->is_enabled = m_conn_enabled;
2116 pConnectionParams->is_setup = false;
2117
2118 if (pConnectionParams->type == INTERNAL_GPS) {
2119 pConnectionParams->network_address = "";
2120 pConnectionParams->network_port = 0;
2121 pConnectionParams->net_protocol = PROTO_UNDEFINED;
2122 pConnectionParams->baudrate = 0;
2123 pConnectionParams->serial_port = "Internal GPS";
2124 }
2125
2126 if (pConnectionParams->type == INTERNAL_BT) {
2127 wxString parms = m_bt_data_sources_choice->GetStringSelection();
2128 wxStringTokenizer tkz(parms, ";");
2129 wxString name = tkz.GetNextToken();
2130 wxString mac = tkz.GetNextToken();
2131
2132 pConnectionParams->network_address = name;
2133 pConnectionParams->serial_port = mac;
2134 pConnectionParams->network_port = 0;
2135 pConnectionParams->net_protocol = PROTO_UNDEFINED;
2136 pConnectionParams->baudrate = 0;
2137 // pConnectionParams->SetAuxParameterStr(m_choiceBTDataSources->GetStringSelection());
2138 }
2139
2140 if (pConnectionParams->type == SOCKETCAN) {
2141 pConnectionParams->network_address = "";
2142 pConnectionParams->network_port = 0;
2143 pConnectionParams->net_protocol = PROTO_UNDEFINED;
2144 pConnectionParams->baudrate = 0;
2145 pConnectionParams->socket_can_port =
2146 m_can_source_choice->GetString(m_can_source_choice->GetSelection());
2147 }
2148 if (pConnectionParams->type == SERIAL) {
2149 pConnectionParams->user_comment = m_serial_comment_tctrl->GetValue();
2150 } else if (pConnectionParams->type == NETWORK) {
2151 pConnectionParams->user_comment = m_net_comment_tctrl->GetValue();
2152 }
2153 pConnectionParams->auth_token = m_auth_token_tctrl->GetValue();
2154
2155 return pConnectionParams;
2156}
2157
2158void ConnectionEditDialog::OnPriorityDialog(wxCommandEvent& event) {
2159 auto* pdlg = new PriorityDlg(m_parent);
2160 pdlg->ShowModal();
2161 delete pdlg;
2162}
2163void ConnectionEditDialog::ConnectControls() {
2164 // Connect controls
2165 m_type_serial_radiobtn->Connect(
2166 wxEVT_COMMAND_RADIOBUTTON_SELECTED,
2167 wxCommandEventHandler(ConnectionEditDialog::OnTypeSerialSelected),
2168 nullptr, this);
2169 m_type_net_radiobtn->Connect(
2170 wxEVT_COMMAND_RADIOBUTTON_SELECTED,
2171 wxCommandEventHandler(ConnectionEditDialog::OnTypeNetSelected), nullptr,
2172 this);
2173 m_type_can_radiobtn->Connect(
2174 wxEVT_COMMAND_RADIOBUTTON_SELECTED,
2175 wxCommandEventHandler(ConnectionEditDialog::OnTypeCANSelected), nullptr,
2176 this);
2177 if (m_type_internal_gps_radiobtn)
2178 m_type_internal_gps_radiobtn->Connect(
2179 wxEVT_COMMAND_RADIOBUTTON_SELECTED,
2180 wxCommandEventHandler(ConnectionEditDialog::OnTypeGPSSelected), nullptr,
2181 this);
2182 if (m_type_internal_bt_radiobtn)
2183 m_type_internal_bt_radiobtn->Connect(
2184 wxEVT_COMMAND_RADIOBUTTON_SELECTED,
2185 wxCommandEventHandler(ConnectionEditDialog::OnTypeBTSelected), nullptr,
2186 this);
2187
2188 m_net_data_protocol_choice->Connect(
2189 wxEVT_COMMAND_CHOICE_SELECTED,
2190 wxCommandEventHandler(ConnectionEditDialog::OnProtocolChoice), nullptr,
2191 this);
2192 m_serial_protocol_choice->Connect(
2193 wxEVT_COMMAND_CHOICE_SELECTED,
2194 wxCommandEventHandler(ConnectionEditDialog::OnProtocolChoice), nullptr,
2195 this);
2196
2197 // input/output control
2198 m_input_chkbox->Connect(
2199 wxEVT_COMMAND_CHECKBOX_CLICKED,
2200 wxCommandEventHandler(ConnectionEditDialog::OnCbInput), nullptr, this);
2201 m_output_chkbox->Connect(
2202 wxEVT_COMMAND_CHECKBOX_CLICKED,
2203 wxCommandEventHandler(ConnectionEditDialog::OnCbOutput), nullptr, this);
2204
2205 if (m_scan_bt_btn)
2206 m_scan_bt_btn->Connect(
2207 wxEVT_COMMAND_BUTTON_CLICKED,
2208 wxCommandEventHandler(ConnectionEditDialog::OnScanBtClick), nullptr,
2209 this);
2210
2211 m_input_stc_list_btn->Connect(
2212 wxEVT_COMMAND_BUTTON_CLICKED,
2213 wxCommandEventHandler(ConnectionEditDialog::OnBtnIStcs), nullptr, this);
2214
2215 // output filtering
2216 m_output_stc_list_btn->Connect(
2217 wxEVT_COMMAND_BUTTON_CLICKED,
2218 wxCommandEventHandler(ConnectionEditDialog::OnBtnOStcs), nullptr, this);
2219}
2220
2221SentenceListDlg::SentenceListDlg(wxWindow* parent, FilterDirection dir,
2222 ListType type, const wxArrayString& list)
2223 : wxDialog(parent, wxID_ANY, _("Sentence Filter"), wxDefaultPosition,
2224 wxSize(280, 420)),
2225 m_type(type),
2226 m_dir(dir),
2227 m_sentences(NMEA0183().GetRecognizedArray()) {
2228 m_sentences.Sort();
2229 auto* mainSizer = new wxBoxSizer(wxVERTICAL);
2230 auto* secondSizer = new wxBoxSizer(wxHORIZONTAL);
2231 auto* pclbBox = new wxStaticBox(this, wxID_ANY, GetBoxLabel());
2232 auto* stcSizer = new wxStaticBoxSizer(pclbBox, wxVERTICAL);
2233 m_sentences_clb = new wxCheckListBox(this, wxID_ANY, wxDefaultPosition,
2234 wxDefaultSize, m_sentences);
2235 auto* btnEntrySizer = new wxBoxSizer(wxVERTICAL);
2236 auto* btnCheckAll = new wxButton(this, wxID_ANY, _("Select All"));
2237 auto* btnClearAll = new wxButton(this, wxID_ANY, _("Clear All"));
2238 auto* btnAdd = new wxButton(this, wxID_ANY, _("Add"));
2239 m_del_btn = new wxButton(this, wxID_ANY, _("Delete"));
2240 m_del_btn->Disable();
2241 auto* btnSizer = new wxStdDialogButtonSizer();
2242 auto* btnOK = new wxButton(this, wxID_OK);
2243 auto* btnCancel = new wxButton(this, wxID_CANCEL, _("Cancel"));
2244
2245 secondSizer->Add(stcSizer, 1, wxALL | wxEXPAND, 5);
2246 stcSizer->Add(m_sentences_clb, 1, wxALL | wxEXPAND, 5);
2247 btnEntrySizer->Add(btnCheckAll, 0, wxALL, 5);
2248 btnEntrySizer->Add(btnClearAll, 0, wxALL, 5);
2249 btnEntrySizer->AddSpacer(1);
2250 btnEntrySizer->Add(btnAdd, 0, wxALL, 5);
2251 btnEntrySizer->Add(m_del_btn, 0, wxALL, 5);
2252 secondSizer->Add(btnEntrySizer, 0, wxALL | wxEXPAND, 5);
2253 mainSizer->Add(secondSizer, 1, wxEXPAND, 5);
2254 btnSizer->AddButton(btnOK);
2255 btnSizer->AddButton(btnCancel);
2256 btnSizer->Realize();
2257 mainSizer->Add(btnSizer, 0, wxALL | wxEXPAND, 5);
2258
2259 SetSizer(mainSizer);
2260 mainSizer->SetSizeHints(this);
2261 Centre();
2262
2263 btnAdd->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
2264 wxCommandEventHandler(SentenceListDlg::OnAddClick), nullptr,
2265 this);
2266 m_del_btn->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
2267 wxCommandEventHandler(SentenceListDlg::OnDeleteClick),
2268 nullptr, this);
2269 m_sentences_clb->Connect(wxEVT_COMMAND_LISTBOX_SELECTED,
2270 wxCommandEventHandler(SentenceListDlg::OnCLBSelect),
2271 nullptr, this);
2272 btnCheckAll->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
2273 wxCommandEventHandler(SentenceListDlg::OnCheckAllClick),
2274 nullptr, this);
2275 btnClearAll->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
2276 wxCommandEventHandler(SentenceListDlg::OnClearAllClick),
2277 nullptr, this);
2278 Populate(list);
2279}
2280
2281wxString SentenceListDlg::GetBoxLabel() const {
2282 if (m_dir == FILTER_OUTPUT)
2283 return m_type == WHITELIST ? _("Transmit sentences") : _("Drop sentences");
2284 else
2285 return m_type == WHITELIST ? _("Accept only sentences")
2286 : _("Ignore sentences");
2287}
2288
2289void SentenceListDlg::Populate(const wxArrayString& list) {
2290 if (m_dir == FILTER_OUTPUT) {
2291 wxString s;
2292 m_sentences.Add(g_TalkerIdText + wxString("RMB"));
2293 m_sentences.Add(g_TalkerIdText + wxString("RMC"));
2294 m_sentences.Add(g_TalkerIdText + wxString("APB"));
2295 m_sentences.Add(g_TalkerIdText + wxString("XTE"));
2296 }
2297 m_sentences.Add("AIVDM");
2298 m_sentences.Add("AIVDO");
2299 m_sentences.Add("FRPOS");
2300 m_sentences.Add(g_TalkerIdText);
2301 m_sentences.Add("CD");
2302 m_sentences.Sort();
2303 m_sentences_clb->Clear();
2304 m_sentences_clb->InsertItems(m_sentences, 0);
2305
2306 wxArrayString new_strings;
2307 if (list.Count() == 0) {
2308 for (size_t i = 0; i < m_sentences_clb->GetCount(); ++i)
2309 m_sentences_clb->Check(i, m_type == WHITELIST);
2310 } else {
2311 for (size_t i = 0; i < list.Count(); ++i) {
2312 int item = m_sentences_clb->FindString(list[i]);
2313 if (item != wxNOT_FOUND)
2314 m_sentences_clb->Check(item);
2315 else
2316 new_strings.Add(list[i]);
2317 }
2318 if (new_strings.GetCount()) {
2319 m_sentences_clb->InsertItems(new_strings, m_sentences_clb->GetCount());
2320 for (size_t i = 0; i < new_strings.GetCount(); ++i) {
2321 int item = m_sentences_clb->FindString(new_strings[i]);
2322 if (item != wxNOT_FOUND) m_sentences_clb->Check(item);
2323 }
2324 }
2325 }
2326}
2327
2328wxString SentenceListDlg::GetSentences() {
2329 wxArrayString retString;
2330 for (size_t i = 0; i < m_sentences_clb->GetCount(); i++) {
2331 if (m_sentences_clb->IsChecked(i))
2332 retString.Add(m_sentences_clb->GetString(i));
2333 }
2334 return StringArrayToString(retString);
2335}
2336
2337void SentenceListDlg::OnCLBSelect(wxCommandEvent& e) {
2338 // Only activate the "Delete" button if the selection is not in the standard
2339 // list
2340 m_del_btn->Enable(m_sentences.Index(e.GetString()) == wxNOT_FOUND);
2341}
2342
2343void SentenceListDlg::OnAddClick(wxCommandEvent& event) {
2344#ifdef __ANDROID__
2345 androidDisableRotation();
2346#endif
2347
2348 wxTextEntryDialog textdlg(
2349 this,
2350 _("Enter the NMEA sentence (2, 3 or 5 characters)\n or a valid REGEX "
2351 "expression (6 characters or longer)"),
2352 _("Enter the NMEA sentence"));
2353
2354 textdlg.SetTextValidator(wxFILTER_ASCII);
2355 int result = textdlg.ShowModal();
2356
2357#ifdef __ANDROID__
2358 androidEnableRotation();
2359#endif
2360
2361 if (result == wxID_CANCEL) return;
2362 wxString stc = textdlg.GetValue();
2363
2364 if (stc.Length() == 2 || stc.Length() == 3 || stc.Length() == 5) {
2365 m_sentences_clb->Append(stc);
2366 m_sentences_clb->Check(m_sentences_clb->FindString(stc));
2367 return;
2368 } else if (stc.Length() < 2) {
2369 OCPNMessageBox(
2370 this,
2371 _("An NMEA sentence is generally 3 characters long (like RMC, GGA etc.)\n \
2372 It can also have a two letter prefix identifying the source, or TALKER, of the message.\n \
2373 The whole sentences then looks like GPGGA or AITXT.\n \
2374 You may filter out all the sentences with certain TALKER prefix (like GP, AI etc.).\n \
2375 The filter also accepts Regular Expressions (REGEX) with 6 or more characters. \n\n"),
2376 _("OpenCPN Info"));
2377 return;
2378 }
2379
2380 else {
2381 // Verify that a longer text entry is a valid RegEx
2382 wxRegEx r(stc);
2383 if (r.IsValid()) {
2384 m_sentences_clb->Append(stc);
2385 m_sentences_clb->Check(m_sentences_clb->FindString(stc));
2386 return;
2387 } else {
2388 OCPNMessageBox(this, _("REGEX syntax error: \n") + stc,
2389 _("OpenCPN Info"));
2390 return;
2391 }
2392 }
2393}
2394
2395void SentenceListDlg::OnDeleteClick(wxCommandEvent& event) {
2396 m_sentences_clb->Delete(m_sentences_clb->GetSelection());
2397}
2398
2399void SentenceListDlg::OnClearAllClick(wxCommandEvent& event) {
2400 for (size_t i = 0; i < m_sentences_clb->GetCount(); i++)
2401 m_sentences_clb->Check(i, false);
2402}
2403
2404void SentenceListDlg::OnCheckAllClick(wxCommandEvent& event) {
2405 for (size_t i = 0; i < m_sentences_clb->GetCount(); i++)
2406 m_sentences_clb->Check(i, true);
2407}
Dialog for editing connection parameters.
void OnConnectionTypeChange()
Initiate a network connection view with new data.
void RefreshAdvancedDetails()
Refresh visible states in a network connection view.
ConnectionParams * GetParamsFromControls()
Return parameters instance populated from UI elements owned by caller.
Panel for displaying and editing connection parameters.
Connection data container close to a POD struct.
Definition conn_params.h:75
Simple panel showing either an "expand" or "collapse" icon, state switches when clicked.
Definition expand_icon.h:38
Custom event class for OpenCPN's notification system.
A wxTextCtrl with an initial italics help text, removed when user starts typing.
void SetHelp(const std::string &help_text)
Set help text, enter pristine state.
void RestoreHelp()
Restore help text to initial value, enter pristine state.
bool IsPristine() const
Return true if user has not entered anything.
Communication drivers factory and support.
Global variables stored in configuration file.
std::vector< ConnectionParams * > & TheConnectionParams()
Return global list of connections.
Panel for editing a connection.
Dialog and support code for editing a connection.
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:61
General purpose GUI support.
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
std::string trim(std::string s)
Strip possibly trailing and/or leading space characters in s.
OpenCPN Platform specific support utilities.
PlugIn Object Definition/API.
Miscellaneous utilities, many of which string related.
Options dialog.
Input priorities management dialog.
Serial ports support, notably enumeration.
wxArrayString * EnumerateSerialPorts(void)
Enumerate all serial ports.
Suspend/resume and new devices events exchange point.
bool CheckSerialAccess(wxWindow *parent, const std::string device)
Run checks and possible dialogs to ensure device is accessible.
Access checks for comm devices and dongle.