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