OpenCPN Partial API docs
SendToPeerDlg.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2010 by David S. Register *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the *
20  * Free Software Foundation, Inc., *
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
22  ***************************************************************************
23  */
24 #include <memory>
25 
26 #include <wx/statline.h>
27 
28 #include <curl/curl.h>
29 
30 #include "model/cmdline.h"
31 #include "model/config_vars.h"
32 #include "model/mdns_cache.h"
33 #include "model/mDNS_query.h"
34 #include "model/ocpn_utils.h"
35 #include "model/peer_client.h"
36 #include "model/route.h"
37 #include "model/route_point.h"
38 
39 #include "gui_lib.h"
40 #include "OCPNPlatform.h"
41 #include "ocpn_frame.h"
42 #include "peer_client_dlg.h"
43 #include "route_gui.h"
44 #include "route_point_gui.h"
45 
46 #include "SendToPeerDlg.h"
47 #include "ocpn_plugin.h"
48 
49 #ifdef __ANDROID__
50 #include "androidUTIL.h"
51 #endif
52 
53 #define TIMER_AUTOSCAN 94522
54 #define TIMER_SCANTICK 94523
55 
56 extern MyFrame* gFrame;
57 extern OCPNPlatform* g_Platform;
58 
59 static PeerDlgResult ConfirmWriteDlg() {
60  std::string msg(_("Objects exists on server. OK to overwrite?"));
61  long style = wxYES | wxNO | wxNO_DEFAULT | wxICON_QUESTION;
62  OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"), style);
63  int reply = dlg.ShowModal();
64  return reply == wxID_YES ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
65 }
66 
67 static PeerDlgResult RunStatusDlg(PeerDlg kind, int status) {
68  switch (kind) {
69  case PeerDlg::InvalidHttpResponse: {
70  std::stringstream ss;
71  if (status >= 0) {
72  ss << _("Server HTTP response is :") << status;
73  } else {
74  ss << _("Curl transfer error: ")
75  << curl_easy_strerror(static_cast<CURLcode>(-status));
76  }
77  OCPNMessageDialog dlg(NULL, ss.str(), _("OpenCPN Info"),
78  wxICON_ERROR | wxOK | wxCANCEL);
79  int r = dlg.ShowModal();
80  return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
81  }
82  case PeerDlg::ErrorReturn: {
83  std::stringstream ss;
84  ss << _("Server internal error response:") << status;
85  OCPNMessageDialog dlg(NULL, ss.str(), _("OpenCPN Info"),
86  wxICON_ERROR | wxOK | wxCANCEL);
87  int r = dlg.ShowModal();
88  return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
89  }
90  case PeerDlg::TransferOk: {
91  std::stringstream ss;
92  std::string msg(_("Transfer successfully completed"));
93  OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
94  wxICON_INFORMATION | wxOK);
95  dlg.ShowModal();
96  return PeerDlgResult::Ok;
97  }
98  case PeerDlg::JsonParseError: {
99  std::string msg(_("Cannot parse server reply"));
100  OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
101  wxICON_ERROR | wxOK | wxCANCEL);
102  int r = dlg.ShowModal();
103  return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
104  }
105  case PeerDlg::BadPincode: {
106  std::string msg(_("Pincode not accepted"));
107  OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
108  wxICON_ERROR | wxOK | wxCANCEL);
109  int r = dlg.ShowModal();
110  return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
111  }
112  case PeerDlg::ActivateUnsupported: {
113  std::string msg(_("Server does not support activation"));
114  OCPNMessageDialog dlg(NULL, msg, _("OpenCPN Info"),
115  wxICON_ERROR | wxOK | wxCANCEL);
116 
117  int r = dlg.ShowModal();
118  return r == wxID_OK ? PeerDlgResult::Ok : PeerDlgResult::Cancel;
119  }
120  case PeerDlg::PinConfirm:
121  assert(false && "Illegal PinConfirm result dialog");
122  }
123  return PeerDlgResult::Cancel; // For the compiler, not reached
124 }
125 
126 std::pair<PeerDlgResult, std::string> RunPincodeDlg() {
127  PinConfirmDlg dlg(gFrame, wxID_ANY, _("OpenCPN Server Message"), "",
128  wxDefaultPosition, wxDefaultSize, SYMBOL_PCD_STYLE);
129 
130  static const char* const msg =
131  _("A server pin is needed.\n"
132  "Please enter PIN number from server to pair with this device");
133 
134  dlg.SetMessage(msg);
135  dlg.SetPincodeText("");
136  if (dlg.ShowModal() == wxID_OK) {
137  auto pin = dlg.GetPincodeText().Trim().Trim(false);
138  return {PeerDlgResult::HasPincode, pin.ToStdString()};
139  }
140  return {PeerDlgResult::Cancel, ""};
141 }
142 
144 static void ParsePeer(const wxString& ui_value, PeerData& peer_data) {
145  wxString server_name = ui_value.BeforeFirst('{').Trim();
146  wxString peer_ip = ui_value;
147  int tail = ui_value.Find('{');
148  if (tail != wxNOT_FOUND) peer_ip = peer_ip.Mid(tail + 1);
149  peer_ip = peer_ip.BeforeFirst('}') + ":";
150  // Is the destination a portable? Detect by string inspection.
151  peer_ip += server_name.BeforeFirst('-') == "Portable" ? "8444" : "8443";
152  peer_data.server_name = server_name.ToStdString();
153  peer_data.dest_ip_address = peer_ip.ToStdString();
154 }
155 
156 IMPLEMENT_DYNAMIC_CLASS(SendToPeerDlg, wxDialog)
157 
158 BEGIN_EVENT_TABLE(SendToPeerDlg, wxDialog)
159 EVT_BUTTON(ID_STP_CANCEL, SendToPeerDlg::OnCancelClick)
160 EVT_BUTTON(ID_STP_OK, SendToPeerDlg::OnSendClick)
161 EVT_BUTTON(ID_STP_SCAN, SendToPeerDlg::OnScanClick)
162 EVT_TIMER(TIMER_AUTOSCAN, SendToPeerDlg::OnTimerAutoscan)
163 EVT_TIMER(TIMER_SCANTICK, SendToPeerDlg::OnTimerScanTick)
164 END_EVENT_TABLE()
165 
167  m_PeerListBox = NULL;
168  m_pgauge = NULL;
169  m_SendButton = NULL;
170  m_CancelButton = NULL;
171  premtext = NULL;
172  m_scanTime = 5; // default, seconds
173  m_bScanOnCreate = false;
174 
175  // Get our own local ipv4 address, for filtering
176  std::vector<std::string> ipv4_addrs = get_local_ipv4_addresses();
177  if (ipv4_addrs.size())
178  m_ownipAddr = ipv4_addrs[0];
179 
180 #ifdef __ANDROID__
181  androidDisableRotation();
182 #endif
183 }
184 
185 SendToPeerDlg::~SendToPeerDlg() {
186  delete m_PeerListBox;
187  delete m_pgauge;
188  delete m_SendButton;
189  delete m_CancelButton;
190 #ifdef __ANDROID__
191  androidEnableRotation();
192 #endif
193 }
194 
195 bool SendToPeerDlg::Create(wxWindow* parent, wxWindowID id,
196  const wxString& caption, const wxString& hint,
197  const wxPoint& pos, const wxSize& size, long style) {
198  SetExtraStyle(GetExtraStyle() | wxWS_EX_BLOCK_EVENTS);
199  wxFont* pF = OCPNGetFont(_T("Dialog"), 0);
200  SetFont(*pF);
201 
202  wxDialog::Create(parent, id, caption, pos, size, style);
203 
204  CreateControls(hint);
205  GetSizer()->Fit(this);
206  GetSizer()->SetSizeHints(this);
207  Centre();
208 
209  if (m_bScanOnCreate) {
210  m_autoScanTimer.SetOwner(this, TIMER_AUTOSCAN);
211  m_autoScanTimer.Start(500, wxTIMER_ONE_SHOT);
212  }
213  m_ScanTickTimer.SetOwner(this, TIMER_SCANTICK);
214 
215  auto action = [&](ObservedEvt& evt) { m_pgauge->SetValue(evt.GetInt()); };
216  progress_listener.Init(progress, action);
217 #ifdef __ANDROID__
218  androidDisableRotation();
219 #endif
220  return true;
221 }
222 
223 bool SendToPeerDlg::EnableActivateChkbox() {
224  return m_RouteList.size() == 1 && m_RoutePointList.empty() &&
225  m_TrackList.empty();
226 }
227 
228 void 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.IsSameAs(item.BeforeFirst('.')) ||
250  (m_ownipAddr != entry.ip)) {
251  item += " {";
252  item += entry.ip.c_str();
253  item += "}";
254  m_PeerListBox->Append(item);
255  }
256  }
257 
258  if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
259  m_PeerListBox->Bind(
260  wxEVT_TEXT,
261  [&](wxCommandEvent&) {
262  m_SendButton->Enable(m_PeerListBox->GetValue() != ""); });
263  m_PeerListBox->Enable(!m_bScanOnCreate);
264  comm_box_sizer->Add(m_PeerListBox, 0, wxEXPAND | wxALL, 5);
265 
266  wxBoxSizer* itemBoxSizer3 = new wxBoxSizer(wxVERTICAL);
267  itemBoxSizer2->Add(itemBoxSizer3, 0, wxEXPAND | wxALL, 5);
268 
269  m_RescanButton = new wxButton(itemDialog1, ID_STP_SCAN, _("Scan again"),
270  wxDefaultPosition, wxDefaultSize, 0);
271  itemBoxSizer3->Add(m_RescanButton, 0, wxALL, 5);
272 
273  m_pgauge = new wxGauge(itemDialog1, -1, m_scanTime * 2, wxDefaultPosition,
274  wxSize(-1, GetCharHeight()));
275  itemBoxSizer3->Add(m_pgauge, 0, wxEXPAND | wxALL, 20);
276 
277  itemBoxSizer2->AddSpacer(30);
278  itemBoxSizer2->Add(new wxStaticLine(this), wxSizerFlags(0).Expand());
279  m_activate_chkbox = new wxCheckBox(this, wxID_ANY,
280  _("Activate after transfer"),
281  wxDefaultPosition, wxDefaultSize,
282  wxALIGN_RIGHT);
283  itemBoxSizer2->Add(m_activate_chkbox, 0,
284  wxALIGN_RIGHT | wxALL, 10);
285  if (!EnableActivateChkbox()) m_activate_chkbox->Disable();
286 
287 
288  // OK/Cancel/etc.
289  wxBoxSizer* itemBoxSizer16 = new wxBoxSizer(wxHORIZONTAL);
290  itemBoxSizer2->Add(itemBoxSizer16, 0, wxALIGN_RIGHT | wxALL, 5);
291 
292  m_CancelButton = new wxButton(itemDialog1, ID_STP_CANCEL, _("Cancel"),
293  wxDefaultPosition, wxDefaultSize, 0);
294  itemBoxSizer16->Add(m_CancelButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
295 
296  m_SendButton = new wxButton(itemDialog1, ID_STP_OK, _("Send"),
297  wxDefaultPosition, wxDefaultSize, 0);
298  itemBoxSizer16->Add(m_SendButton, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
299  m_SendButton->SetDefault();
300  m_SendButton->Enable(!m_PeerListBox->IsListEmpty());
301 }
302 
303 void SendToPeerDlg::SetMessage(wxString msg) {
304  if (premtext) {
305  premtext->SetLabel(msg);
306  premtext->Refresh(true);
307  }
308 }
309 
310 void SendToPeerDlg::OnSendClick(wxCommandEvent&) {
311  if (m_RouteList.empty() && m_TrackList.empty() && m_RoutePointList.empty()) {
312  Close();
313  return;
314  }
315  // Set up transfer data
316  PeerData peer_data(progress);
317  ParsePeer(m_PeerListBox->GetValue(), peer_data);
318  auto addr_port = ocpn::split(peer_data.dest_ip_address, ":");
319  if (addr_port.size() == 1) addr_port.push_back("8443");
320  MdnsCache::GetInstance().Add(addr_port[0], addr_port[1]);
321  peer_data.routes = m_RouteList;
322  peer_data.tracks = m_TrackList;
323  peer_data.routepoints = m_RoutePointList;
324  peer_data.run_status_dlg = RunStatusDlg;
325  peer_data.run_pincode_dlg = RunPincodeDlg;
326  peer_data.activate = m_activate_chkbox->GetValue();
327 
328  // And send it out
329  m_pgauge->SetRange(100);
330  m_pgauge->SetValue(0);
331  m_pgauge->Show();
332 
333  GetApiVersion(peer_data);
334  if (peer_data.api_version < SemanticVersion(5, 9)) {
335  SendNavobjects(peer_data);
336  } else {
337  bool is_writable = CheckNavObjects(peer_data);
338  if (is_writable || ConfirmWriteDlg() == PeerDlgResult::Ok) {
339  peer_data.overwrite = true;
340  SendNavobjects(peer_data);
341  }
342  }
343  m_pgauge->Hide();
344  Close();
345 }
346 
347 void SendToPeerDlg::OnScanClick(wxCommandEvent&) { DoScan(); }
348 
349 void SendToPeerDlg::OnTimerAutoscan(wxTimerEvent&) { DoScan(); }
350 
351 void SendToPeerDlg::OnTimerScanTick(wxTimerEvent&) {
352  m_tick--;
353  if (m_pgauge) {
354  int v = m_pgauge->GetValue();
355  if (v + 1 <= m_pgauge->GetRange()) m_pgauge->SetValue(v + 1);
356  }
357 
358  if (m_tick == 0) {
359  // Housekeeping
360  m_ScanTickTimer.Stop();
361  g_Platform->HideBusySpinner();
362  m_RescanButton->Enable();
363  m_SendButton->SetDefault();
364  m_pgauge->Hide();
365  m_PeerListBox->Enable(true);
366  m_bScanOnCreate = false;
367 
368  // Clear the combo box
369  m_PeerListBox->Clear();
370 
371  // Fill in the wxComboBox with all detected peers
372  for (auto& entry : MdnsCache::GetInstance().GetCache()) {
373  wxString item(entry.hostname.c_str());
374 
375  // skip "self"
376  if (!g_hostname.IsSameAs(item.BeforeFirst('.')) ||
377  (m_ownipAddr != entry.ip)) {
378  item += " {";
379  item += entry.ip.c_str();
380  item += "}";
381  m_PeerListBox->Append(item);
382  }
383  }
384  if (m_PeerListBox->GetCount()) m_PeerListBox->SetSelection(0);
385  m_SendButton->Enable(m_PeerListBox->GetCount() > 0);
386  }
387 }
388 
389 void SendToPeerDlg::DoScan() {
390  m_RescanButton->Disable();
391  m_SendButton->Disable();
392  g_Platform->ShowBusySpinner();
393  m_pgauge->SetRange(m_scanTime);
394  m_pgauge->SetValue(0);
395  m_pgauge->Show();
396 
397  FindAllOCPNServers(m_scanTime);
398 
399  m_tick = m_scanTime * 2;
400  m_ScanTickTimer.Start(500, wxTIMER_CONTINUOUS);
401 }
402 
403 void SendToPeerDlg::OnCancelClick(wxCommandEvent&) {
404  g_Platform->HideBusySpinner();
405  Close();
406 }
bool Add(const Entry &entry)
Add new entry to the cache.
Definition: mdns_cache.cpp:51
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Definition: observable.h:227
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1652
Route "Send to Peer..." Dialog Definition.
Definition: SendToPeerDlg.h:71
Global variables reflecting command line options and arguments.
General purpose GUI support.
bool activate
API parameter, activate route after transfer.
Definition: peer_client.h:60
SemanticVersion api_version
server API version
Definition: peer_client.h:55
std::function< PeerDlgResult(PeerDlg, int)> run_status_dlg
Dialog displaying status (good, bad, ...)
Definition: peer_client.h:66
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:72
bool overwrite
API parameter, force overwrite w/o server dialogs.
Definition: peer_client.h:59
Versions uses a modified semantic versioning scheme: major.minor.revision.post-tag+build.
Definition: semantic_vers.h:51