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 std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
174 if (ipv4_addrs.size()) m_ownipAddr = ipv4_addrs[0];
175
176#ifdef __ANDROID__
177 androidDisableRotation();
178#endif
179}
180
181SendToPeerDlg::~SendToPeerDlg() {
182 delete m_PeerListBox;
183 delete m_pgauge;
184 delete m_SendButton;
185 delete m_CancelButton;
186#ifdef __ANDROID__
187 androidEnableRotation();
188#endif
189}
190
191bool SendToPeerDlg::Create(wxWindow* parent, wxWindowID id,
192 const wxString& caption, const wxString& hint,
193 const wxPoint& pos, const wxSize& size, long style) {
194 SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
195 wxFont* pF = OCPNGetFont(_("Dialog"));
196 SetFont(*pF);
197
198 wxDialog::Create(parent, id, caption, pos, size, style);
199
200 CreateControls(hint);
201 GetSizer()->Fit(this);
202 GetSizer()->SetSizeHints(this);
203 Centre();
204
205 if (m_bScanOnCreate) {
206 m_autoScanTimer.SetOwner(this, TIMER_AUTOSCAN);
207 m_autoScanTimer.Start(500, wxTIMER_ONE_SHOT);
208 }
209 m_ScanTickTimer.SetOwner(this, TIMER_SCANTICK);
210
211 auto action = [&](ObservedEvt& evt) { m_pgauge->SetValue(evt.GetInt()); };
212 progress_listener.Init(progress, action);
213#ifdef __ANDROID__
214 androidDisableRotation();
215#endif
216 return true;
217}
218
219bool SendToPeerDlg::EnableActivateChkbox() {
220 return m_RouteList.size() == 1 && m_RoutePointList.empty() &&
221 m_TrackList.empty();
222}
223
224void SendToPeerDlg::CreateControls(const wxString&) {
225 SendToPeerDlg* itemDialog1 = this;
226
227 wxBoxSizer* itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
228 itemDialog1->SetSizer(itemBoxSizer2);
229
230 // Create the ScrollBox list of available com ports in a labeled static
231 // box
232 wxStaticBox* comm_box =
233 new wxStaticBox(this, wxID_ANY, _("Detected OpenCPN peer instances"));
234
235 wxStaticBoxSizer* comm_box_sizer = new wxStaticBoxSizer(comm_box, wxVERTICAL);
236 itemBoxSizer2->Add(comm_box_sizer, 0, wxEXPAND | wxALL, 5);
237
238 m_PeerListBox = new wxComboBox(this, ID_STP_CHOICE_PEER);
239
240 // Fill in the wxComboBox with all detected peers
241 for (auto& entry : MdnsCache::GetInstance().GetCache()) {
242 wxString item(entry.hostname.c_str());
243
244 // skip "self"
245 if (!g_hostname.IsSameAs(item.BeforeFirst('.')) ||
246 (m_ownipAddr != entry.ip)) {
247 item += " {";
248 item += entry.ip.c_str();
249 item += "}";
250 m_PeerListBox->Append(item);
251 }
252 }
253
254 if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
255 m_PeerListBox->Bind(wxEVT_TEXT, [&](wxCommandEvent&) {
256 m_SendButton->Enable(m_PeerListBox->GetValue() != "");
257 });
258 m_PeerListBox->Enable(!m_bScanOnCreate);
259 comm_box_sizer->Add(m_PeerListBox, 0, wxEXPAND | wxALL, 5);
260
261 wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxVERTICAL);
262 itemBoxSizer2->Add(itemBoxSizer3, 0, wxEXPAND | wxALL, 5);
263
264 m_RescanButton = new wxButton(itemDialog1, ID_STP_SCAN, _("Scan again"),
265 wxDefaultPosition, wxDefaultSize, 0);
266 itemBoxSizer3->Add(m_RescanButton, 0, wxALL, 5);
267
268 m_pgauge = new wxGauge(itemDialog1, -1, m_scanTime * 2, wxDefaultPosition,
269 wxSize(-1, GetCharHeight()));
270 itemBoxSizer3->Add(m_pgauge, 0, wxEXPAND | wxALL, 20);
271
272 itemBoxSizer2->AddSpacer(30);
273 itemBoxSizer2->Add(new wxStaticLine(this), wxSizerFlags(0).Expand());
274 m_activate_chkbox =
275 new wxCheckBox(this, wxID_ANY, _("Activate after transfer"),
276 wxDefaultPosition, wxDefaultSize, wxALIGN_RIGHT);
277 itemBoxSizer2->Add(m_activate_chkbox, 0, wxALIGN_RIGHT | wxALL, 10);
278 if (!EnableActivateChkbox()) m_activate_chkbox->Disable();
279
280 // OK/Cancel/etc.
281 wxBoxSizer* itemBoxSizer16 = new wxBoxSizer(wxHORIZONTAL);
282 itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5);
283
284 m_CancelButton = new wxButton(itemDialog1, ID_STP_CANCEL, _("Cancel"),
285 wxDefaultPosition, wxDefaultSize, 0);
286 itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
287
288 m_SendButton = new wxButton(itemDialog1, ID_STP_OK, _("Send"),
289 wxDefaultPosition, wxDefaultSize, 0);
290 itemBoxSizer16->Add(m_SendButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
291 m_SendButton->SetDefault();
292 m_SendButton->Enable(!m_PeerListBox->IsListEmpty());
293}
294
295void SendToPeerDlg::SetMessage(wxString msg) {
296 if (premtext) {
297 premtext->SetLabel(msg);
298 premtext->Refresh(true);
299 }
300}
301
302void SendToPeerDlg::OnSendClick(wxCommandEvent&) {
303 if (m_RouteList.empty() && m_TrackList.empty() && m_RoutePointList.empty()) {
304 Close();
305 return;
306 }
307 // Set up transfer data
308 PeerData peer_data(progress);
309 ParsePeer(m_PeerListBox->GetValue(), peer_data);
310 auto addr_port = ocpn::split(peer_data.dest_ip_address, ":");
311 if (addr_port.size() == 1) addr_port.push_back("8443");
312 MdnsCache::GetInstance().Add(addr_port[0], addr_port[1]);
313 peer_data.routes = m_RouteList;
314 peer_data.tracks = m_TrackList;
315 peer_data.routepoints = m_RoutePointList;
316 peer_data.run_status_dlg = RunStatusDlg;
317 peer_data.run_pincode_dlg = RunPincodeDlg;
318 peer_data.activate = m_activate_chkbox->GetValue();
319
320 // And send it out
321 m_pgauge->SetRange(100);
322 m_pgauge->SetValue(0);
323 m_pgauge->Show();
324
325 GetApiVersion(peer_data);
326 if (peer_data.api_version < SemanticVersion(5, 9)) {
327 SendNavobjects(peer_data);
328 } else {
329 bool is_writable = CheckNavObjects(peer_data);
330 if (is_writable || ConfirmWriteDlg() == PeerDlgResult::Ok) {
331 peer_data.overwrite = true;
332 SendNavobjects(peer_data);
333 }
334 }
335 m_pgauge->Hide();
336 Close();
337}
338
339void SendToPeerDlg::OnScanClick(wxCommandEvent&) { DoScan(); }
340
341void SendToPeerDlg::OnTimerAutoscan(wxTimerEvent&) { DoScan(); }
342
343void SendToPeerDlg::OnTimerScanTick(wxTimerEvent&) {
344 m_tick--;
345 if (m_pgauge) {
346 int v = m_pgauge->GetValue();
347 if (v + 1 <= m_pgauge->GetRange()) m_pgauge->SetValue(v + 1);
348 }
349
350 if (m_tick == 0) {
351 // Housekeeping
352 m_ScanTickTimer.Stop();
353 g_Platform->HideBusySpinner();
354 m_RescanButton->Enable();
355 m_SendButton->SetDefault();
356 m_pgauge->Hide();
357 m_PeerListBox->Enable(true);
358 m_bScanOnCreate = false;
359
360 m_PeerListBox->Clear();
361
362 // Fill in the wxComboBox with all detected peers besides own host
363 using namespace ocpn;
364 for (const MdnsCache::Entry& e : MdnsCache::GetInstance().GetCache()) {
365 if (g_hostname != split(e.hostname, ".")[0] || m_ownipAddr != e.ip) {
366 m_PeerListBox->Append(e.hostname + " {" + e.ip.c_str() + "}");
367 }
368 }
369 if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
370 m_SendButton->Enable(m_PeerListBox->GetCount() > 0);
371 }
372}
373
374void SendToPeerDlg::DoScan() {
375 m_RescanButton->Disable();
376 m_SendButton->Disable();
377 g_Platform->ShowBusySpinner();
378 m_pgauge->SetRange(m_scanTime);
379 m_pgauge->SetValue(0);
380 m_pgauge->Show();
381
382 FindAllOCPNServers(m_scanTime);
383
384 m_tick = m_scanTime * 2;
385 m_ScanTickTimer.Start(500, wxTIMER_CONTINUOUS);
386}
387
388void SendToPeerDlg::OnCancelClick(wxCommandEvent&) {
389 g_Platform->HideBusySpinner();
390 Close();
391}
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.