OpenCPN Partial API docs
Loading...
Searching...
No Matches
send_to_peer_dlg.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 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
26#include <wx/statline.h>
27
28#include <curl/curl.h>
29
30#include "gl_headers.h" // Must be included before anything using GL stuff
31
32#include "send_to_peer_dlg.h"
33
34#include "model/cmdline.h"
35#include "model/config_vars.h"
36#include "model/mdns_cache.h"
37#include "model/mdns_query.h"
38#include "model/ocpn_utils.h"
39#include "model/peer_client.h"
40#include "model/route.h"
41#include "model/route_point.h"
42
43#include "gui_lib.h"
44#include "ocpn_platform.h"
45#include "peer_client_dlg.h"
46#include "route_gui.h"
47#include "route_point_gui.h"
48#include "ocpn_plugin.h"
49
50#ifdef __ANDROID__
51#include "androidUTIL.h"
52#endif
53
54#define TIMER_AUTOSCAN 94522
55#define TIMER_SCANTICK 94523
56
57static PeerDlgResult ConfirmWriteDlg() {
58 std::string msg(_("Objects exists on server. OK to overwrite?"));
59 long style = wxYES | wxNO | wxNO_DEFAULT | wxICON_QUESTION;
60 OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"), style);
61 int reply = dlg.ShowModal();
62 return reply == wxID_YES ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
63}
64
65static PeerDlgResult RunStatusDlg(PeerDlg kind, int status) {
66 switch (kind) {
67 case PeerDlg::InvalidHttpResponse: {
68 std::stringstream ss;
69 if (status >= 0) {
70 ss << _("Server HTTP response is :") << status;
71 } else {
72 ss << _("Curl transfer error: ")
73 << curl_easy_strerror(static_cast<CURLcode>(-status));
74 }
75 OCPNMessageDialog dlg(NULL, ss.str(), _("OpenCPN Info"),
76 wxICON_ERROR | wxOK | wxCANCEL);
77 int r = dlg.ShowModal();
78 return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
79 }
80 case PeerDlg::ErrorReturn: {
81 std::stringstream ss;
82 ss << _("Server internal error response:") << status;
83 OCPNMessageDialog dlg(NULL, ss.str(), _("OpenCPN Info"),
84 wxICON_ERROR | wxOK | wxCANCEL);
85 int r = dlg.ShowModal();
86 return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
87 }
88 case PeerDlg::TransferOk: {
89 std::stringstream ss;
90 std::string msg(_("Transfer successfully completed"));
91 OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
92 wxICON_INFORMATION | wxOK);
93 dlg.ShowModal();
94 return PeerDlgResult::Ok;
95 }
96 case PeerDlg::JsonParseError: {
97 std::string msg(_("Cannot parse server reply"));
98 OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
99 wxICON_ERROR | wxOK | wxCANCEL);
100 int r = dlg.ShowModal();
101 return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
102 }
103 case PeerDlg::BadPincode: {
104 std::string msg(_("Pincode not accepted"));
105 OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
106 wxICON_ERROR | wxOK | wxCANCEL);
107 int r = dlg.ShowModal();
108 return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
109 }
110 case PeerDlg::ActivateUnsupported: {
111 std::string msg(_("Server does not support activation"));
112 OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
113 wxICON_ERROR | wxOK | wxCANCEL);
114
115 int r = dlg.ShowModal();
116 return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
117 }
118 case PeerDlg::PinConfirm:
119 assert(false && "Illegal PinConfirm result dialog");
120 }
121 return PeerDlgResult::Cancel; // For the compiler, not reached
122}
123
125static void ParsePeer(const wxString& ui_value, PeerData& peer_data) {
126 wxString server_name = ui_value.BeforeFirst('{').Trim();
127 wxString peer_ip = ui_value;
128 int tail = ui_value.Find('{');
129 if (tail != wxNOT_FOUND) peer_ip = peer_ip.Mid(tail + 1);
130 peer_ip = peer_ip.BeforeFirst('}') + ":";
131 // Is the destination a portable? Detect by string inspection.
132 peer_ip += server_name.BeforeFirst('-') == "Portable" ? "8444" : "8443";
133 peer_data.server_name = server_name.ToStdString();
134 peer_data.dest_ip_address = peer_ip.ToStdString();
135}
136
137std::pair<PeerDlgResult, std::string> RunPincodeDlg() {
138 PinConfirmDlg dlg(wxTheApp->GetTopWindow(), wxID_ANY,
139 _("OpenCPN Server Message"), "", wxDefaultPosition,
140 wxDefaultSize, SYMBOL_PCD_STYLE);
141
142 static const char* const msg =
143 _("A server pin is needed.\n"
144 "Please enter PIN number from server to pair with this device");
145
146 dlg.SetMessage(msg);
147 dlg.SetPincodeText("");
148 if (dlg.ShowModal() == wxID_OK) {
149 auto pin = dlg.GetPincodeText().Trim().Trim(false);
150 return {PeerDlgResult::HasPincode, pin.ToStdString()};
151 }
152 return {PeerDlgResult::Cancel, ""};
153}
154
155BEGIN_EVENT_TABLE(SendToPeerDlg, wxDialog)
156EVT_BUTTON(ID_STP_CANCEL, SendToPeerDlg::OnCancelClick)
157EVT_BUTTON(ID_STP_OK, SendToPeerDlg::OnSendClick)
158EVT_BUTTON(ID_STP_SCAN, SendToPeerDlg::OnScanClick)
159EVT_TIMER(TIMER_AUTOSCAN, SendToPeerDlg::OnTimerAutoscan)
160EVT_TIMER(TIMER_SCANTICK, SendToPeerDlg::OnTimerScanTick)
161END_EVENT_TABLE()
162
164 m_PeerListBox = NULL;
165 m_pgauge = NULL;
166 m_SendButton = NULL;
167 m_CancelButton = NULL;
168 premtext = NULL;
169 m_scanTime = 5; // default, seconds
170 m_bScanOnCreate = false;
171
172 // Get our own local ipv4 address, for filtering
173 m_own_ip_addresses = get_local_ipv4_addresses();
174
175#ifdef __ANDROID__
176 androidDisableRotation();
177#endif
178}
179
180SendToPeerDlg::~SendToPeerDlg() {
181 delete m_PeerListBox;
182 delete m_pgauge;
183 delete m_SendButton;
184 delete m_CancelButton;
185#ifdef __ANDROID__
186 androidEnableRotation();
187#endif
188}
189
190bool SendToPeerDlg::Create(wxWindow* parent, wxWindowID id,
191 const wxString& caption, const wxString& hint,
192 const wxPoint& pos, const wxSize& size, long style) {
193 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
194 wxFont* pF = OCPNGetFont(_("Dialog"));
195 SetFont(*pF);
196
197 wxDialog::Create(parent, id, caption, pos, size, style);
198
199 CreateControls(hint);
200 GetSizer()->Fit(this);
201 GetSizer()->SetSizeHints(this);
202 Centre();
203
204 if (m_bScanOnCreate) {
205 m_autoScanTimer.SetOwner(this, TIMER_AUTOSCAN);
206 m_autoScanTimer.Start(500, wxTIMER_ONE_SHOT);
207 }
208 m_ScanTickTimer.SetOwner(this, TIMER_SCANTICK);
209
210 auto action = [&](ObservedEvt& evt) { m_pgauge->SetValue(evt.GetInt()); };
211 progress_listener.Init(progress, action);
212#ifdef __ANDROID__
213 androidDisableRotation();
214#endif
215 return true;
216}
217
218bool SendToPeerDlg::IsOwnAddress(const std::string& address) const {
219 const auto& v = m_own_ip_addresses;
220 return std::find(v.begin(), v.end(), address) != v.end();
221}
222
223bool SendToPeerDlg::EnableActivateChkbox() {
224 return m_RouteList.size() == 1 && m_RoutePointList.empty() &&
225 m_TrackList.empty();
226}
227
228void SendToPeerDlg::CreateControls(const wxString&) {
229 SendToPeerDlg* itemDialog1 = this;
230
231 wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
232 itemDialog1->SetSizer(itemBoxSizer2);
233
234 // Create the ScrollBox list of available com ports in a labeled static
235 // box
236 wxStaticBox* comm_box =
237 new wxStaticBox(this, wxID_ANY, _("Detected OpenCPN peer instances"));
238
239 wxStaticBoxSizer* comm_box_sizer = new wxStaticBoxSizer(comm_box, wxVERTICAL);
240 itemBoxSizer2->Add(comm_box_sizer, 0, wxEXPAND | wxALL, 5);
241
242 m_PeerListBox = new wxComboBox(this, ID_STP_CHOICE_PEER);
243
244 // Fill in the wxComboBox with all detected peers
245 for (auto& entry : MdnsCache::GetInstance().GetCache()) {
246 wxString item(entry.hostname.c_str());
247
248 // skip "self"
249 if (g_hostname == item.BeforeFirst('.')) continue;
250 if (IsOwnAddress(entry.ip)) continue;
251 item += std::string(" {") + entry.ip.c_str() + "}";
252 m_PeerListBox->Append(item);
253 }
254
255 if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
256 m_PeerListBox->Bind(wxEVT_TEXT, [&](wxCommandEvent&) {
257 m_SendButton->Enable(m_PeerListBox->GetValue() != "");
258 });
259 m_PeerListBox->Enable(!m_bScanOnCreate);
260 comm_box_sizer->Add(m_PeerListBox, 0, wxEXPAND | wxALL, 5);
261
262 wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxVERTICAL);
263 itemBoxSizer2->Add(itemBoxSizer3, 0, wxEXPAND | wxALL, 5);
264
265 m_RescanButton = new wxButton(itemDialog1, ID_STP_SCAN, _("Scan again"),
266 wxDefaultPosition, wxDefaultSize, 0);
267 itemBoxSizer3->Add(m_RescanButton, 0, wxALL, 5);
268
269 m_pgauge = new wxGauge(itemDialog1, -1, m_scanTime * 2, wxDefaultPosition,
270 wxSize(-1, GetCharHeight()));
271 itemBoxSizer3->Add(m_pgauge, 0, wxEXPAND | wxALL, 20);
272
273 itemBoxSizer2->AddSpacer(30);
274 itemBoxSizer2->Add(new wxStaticLine(this), wxSizerFlags(0).Expand());
275 m_activate_chkbox =
276 new wxCheckBox(this, wxID_ANY, _("Activate after transfer"),
277 wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
278 itemBoxSizer2->Add(m_activate_chkbox, 0, wxALIGN_RIGHT | wxALL, 10);
279 if (!EnableActivateChkbox()) m_activate_chkbox->Disable();
280
281 // OK/Cancel/etc.
282 wxBoxSizer* itemBoxSizer16 = new wxBoxSizer(wxHORIZONTAL);
283 itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5);
284
285 m_CancelButton = new wxButton(itemDialog1, ID_STP_CANCEL, _("Cancel"),
286 wxDefaultPosition, wxDefaultSize, 0);
287 itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
288
289 m_SendButton = new wxButton(itemDialog1, ID_STP_OK, _("Send"),
290 wxDefaultPosition, wxDefaultSize, 0);
291 itemBoxSizer16->Add(m_SendButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
292 m_SendButton->SetDefault();
293 m_SendButton->Enable(!m_PeerListBox->IsListEmpty());
294}
295
296void SendToPeerDlg::SetMessage(wxString msg) {
297 if (premtext) {
298 premtext->SetLabel(msg);
299 premtext->Refresh(true);
300 }
301}
302
303void SendToPeerDlg::OnSendClick(wxCommandEvent&) {
304 if (m_RouteList.empty() && m_TrackList.empty() && m_RoutePointList.empty()) {
305 Close();
306 return;
307 }
308 // Set up transfer data
309 PeerData peer_data(progress);
310 ParsePeer(m_PeerListBox->GetValue(), peer_data);
311 auto addr_port = ocpn::split(peer_data.dest_ip_address, ":");
312 if (addr_port.size() == 1) addr_port.push_back("8443");
313 MdnsCache::GetInstance().Add(addr_port[0], addr_port[1]);
314 peer_data.routes = m_RouteList;
315 peer_data.tracks = m_TrackList;
316 peer_data.routepoints = m_RoutePointList;
317 peer_data.run_status_dlg = RunStatusDlg;
318 peer_data.run_pincode_dlg = RunPincodeDlg;
319 peer_data.activate = m_activate_chkbox->GetValue();
320
321 // And send it out
322 m_pgauge->SetRange(100);
323 m_pgauge->SetValue(0);
324 m_pgauge->Show();
325
326 GetApiVersion(peer_data);
327 if (peer_data.api_version < SemanticVersion(5, 9)) {
328 SendNavobjects(peer_data);
329 } else {
330 bool is_writable = CheckNavObjects(peer_data);
331 if (is_writable || ConfirmWriteDlg() == PeerDlgResult::Ok) {
332 peer_data.overwrite = true;
333 SendNavobjects(peer_data);
334 }
335 }
336 m_pgauge->Hide();
337 Close();
338}
339
340void SendToPeerDlg::OnScanClick(wxCommandEvent&) { DoScan(); }
341
342void SendToPeerDlg::OnTimerAutoscan(wxTimerEvent&) { DoScan(); }
343
344void SendToPeerDlg::OnTimerScanTick(wxTimerEvent&) {
345 m_tick--;
346 if (m_pgauge) {
347 int v = m_pgauge->GetValue();
348 if (v + 1 <= m_pgauge->GetRange()) m_pgauge->SetValue(v + 1);
349 }
350
351 if (m_tick == 0) {
352 // Housekeeping
353 m_ScanTickTimer.Stop();
354 g_Platform->HideBusySpinner();
355 m_RescanButton->Enable();
356 m_SendButton->SetDefault();
357 m_pgauge->Hide();
358 m_PeerListBox->Enable(true);
359 m_bScanOnCreate = false;
360
361 m_PeerListBox->Clear();
362
363 // Fill in the wxComboBox with all detected peers besides own host
364 using namespace ocpn;
365 for (const MdnsCache::Entry& e : MdnsCache::GetInstance().GetCache()) {
366 if (g_hostname != split(e.hostname, ".")[0] || !IsOwnAddress(e.ip)) {
367 m_PeerListBox->Append(e.hostname + " {" + e.ip.c_str() + "}");
368 }
369 }
370 if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
371 m_SendButton->Enable(m_PeerListBox->GetCount() > 0);
372 }
373}
374
375void SendToPeerDlg::DoScan() {
376 m_RescanButton->Disable();
377 m_SendButton->Disable();
378 g_Platform->ShowBusySpinner();
379 m_pgauge->SetRange(m_scanTime);
380 m_pgauge->SetValue(0);
381 m_pgauge->Show();
382
383 FindAllOCPNServers(m_scanTime);
384
385 m_tick = m_scanTime * 2;
386 m_ScanTickTimer.Start(500, wxTIMER_CONTINUOUS);
387}
388
389void SendToPeerDlg::OnCancelClick(wxCommandEvent&) {
390 g_Platform->HideBusySpinner();
391 Close();
392}
Singleton cache for hosts looked up using mdns.
Definition mdns_cache.h:40
bool Add(const Entry &entry)
Add new entry to the cache.
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Definition observable.h:295
Custom event class for OpenCPN's notification system.
Dialog for sending navigation objects to peer devices.
Global variables reflecting command line options and arguments.
Global variables stored in configuration file.
Platform independent GL includes.
General purpose GUI support.
mDNS host lookups cache.
mDNS lookup wrappers.
Standard, mostly strings utilities.
Definition datetime.cpp:39
std::vector< std::string > split(const char *token_string, const std::string &delimiter)
Return vector of items in s separated by delimiter.
OpenCPN Platform specific support utilities.
PlugIn Object Definition/API.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
Miscellaneous utilities, many of which string related.
bool SendNavobjects(PeerData &peer_data)
Send data to server peer.
bool CheckNavObjects(PeerData &peer_data)
Check if server peer deems that writing these objects can be accepted i.
Peer client non-gui abstraction.
Confirm peer transfer PIN code dialog.
Route abstraction.
Route drawing stuff.
Waypoint or mark abstraction.
Purpose: Track and Trackpoint drawing stuff.
Send Route/Waypoint/Track to peer dialog.
bool activate
API parameter, activate route after transfer.
Definition peer_client.h:58
std::function< std::pair< PeerDlgResult, std::string >()> run_pincode_dlg
Pin confirm dialog, returns new {0, user_pin} or {error_code, error msg)
Definition peer_client.h:70
SemanticVersion api_version
server API version
Definition peer_client.h:53
std::function< PeerDlgResult(PeerDlg, int)> run_status_dlg
Dialog displaying status (good, bad, ...)
Definition peer_client.h:64
bool overwrite
API parameter, force overwrite w/o server dialogs.
Definition peer_client.h:57
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.