OpenCPN Partial API docs
RoutePropDlgImpl.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  *
5  ***************************************************************************
6  * Copyright (C) 2013 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 <wx/clipbrd.h>
25 
26 #include "model/georef.h"
27 #include "model/own_ship.h"
28 #include "model/routeman.h"
29 #include "model/select.h"
30 
31 #include "chcanv.h"
32 #include "gui_lib.h"
33 #include "MarkInfo.h"
34 #include "model/navutil_base.h"
35 #include "navutil.h"
36 #include "ocpn_plugin.h"
37 #include "routemanagerdialog.h"
38 #include "routeprintout.h"
39 #include "RoutePropDlgImpl.h"
40 #include "tcmgr.h"
41 
42 #define ID_RCLK_MENU_COPY_TEXT 7013
43 #define ID_RCLK_MENU_EDIT_WP 7014
44 #define ID_RCLK_MENU_DELETE 7015
45 #define ID_RCLK_MENU_MOVEUP_WP 7026
46 #define ID_RCLK_MENU_MOVEDOWN_WP 7027
47 
48 #define COLUMN_PLANNED_SPEED 9
49 #define COLUMN_ETD 13
50 
51 extern wxString GetLayerName(int id);
52 
53 extern Routeman* g_pRouteMan;
54 extern MyConfig* pConfig;
55 extern ColorScheme global_color_scheme;
56 extern RouteList* pRouteList;
57 extern MyFrame* gFrame;
58 extern RouteManagerDialog* pRouteManagerDialog;
59 extern TCMgr* ptcmgr;
60 
61 int g_route_prop_x, g_route_prop_y, g_route_prop_sx, g_route_prop_sy;
62 
63 // Sunrise/twilight calculation for route properties.
64 // limitations: latitude below 60, year between 2000 and 2100
65 // riset is +1 for rise -1 for set
66 // adapted by author's permission from QBASIC source as published at
67 // http://www.stargazing.net/kepler
68 
69 #ifndef PI
70 #define PI (4. * atan(1.0))
71 #endif
72 #define TPI (2. * PI)
73 #define DEGS (180. / PI)
74 #define RADS (PI / 180.)
75 
76 #define MOTWILIGHT \
77  1 // in some languages there may be a distinction between morning/evening
78 #define SUNRISE 2
79 #define DAY 3
80 #define SUNSET 4
81 #define EVTWILIGHT 5
82 #define NIGHT 6
83 
84 static wxString GetDaylightString(int index) {
85  switch (index) {
86  case 0:
87  return _T(" - ");
88  case 1:
89  return _("MoTwilight");
90  case 2:
91  return _("Sunrise");
92  case 3:
93  return _("Daytime");
94  case 4:
95  return _("Sunset");
96  case 5:
97  return _("EvTwilight");
98  case 6:
99  return _("Nighttime");
100 
101  default:
102  return _T("");
103  }
104 }
105 
106 static double sign(double x) {
107  if (x < 0.)
108  return -1.;
109  else
110  return 1.;
111 }
112 
113 static double FNipart(double x) { return (sign(x) * (int)(fabs(x))); }
114 
115 static double FNday(int y, int m, int d, int h) {
116  long fd = (367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + d);
117  return ((double)fd - 730531.5 + h / 24.);
118 }
119 
120 static double FNrange(double x) {
121  double b = x / TPI;
122  double a = TPI * (b - FNipart(b));
123  if (a < 0.) a = TPI + a;
124  return (a);
125 }
126 
127 static double getDaylightEvent(double glat, double glong, int riset,
128  double altitude, int y, int m, int d) {
129  double day = FNday(y, m, d, 0);
130  double days, correction;
131  double utold = PI;
132  double utnew = 0.;
133  double sinalt =
134  sin(altitude * RADS); // go for the sunrise/sunset altitude first
135  double sinphi = sin(glat * RADS);
136  double cosphi = cos(glat * RADS);
137  double g = glong * RADS;
138  double t, L, G, ec, lambda, E, obl, delta, GHA, cosc;
139  int limit = 12;
140  while ((fabs(utold - utnew) > .001)) {
141  if (limit-- <= 0) return (-1.);
142  days = day + utnew / TPI;
143  t = days / 36525.;
144  // get arguments of Sun's orbit
145  L = FNrange(4.8949504201433 + 628.331969753199 * t);
146  G = FNrange(6.2400408 + 628.3019501 * t);
147  ec = .033423 * sin(G) + .00034907 * sin(2 * G);
148  lambda = L + ec;
149  E = -1. * ec + .0430398 * sin(2 * lambda) - .00092502 * sin(4. * lambda);
150  obl = .409093 - .0002269 * t;
151  delta = asin(sin(obl) * sin(lambda));
152  GHA = utold - PI + E;
153  cosc = (sinalt - sinphi * sin(delta)) / (cosphi * cos(delta));
154  if (cosc > 1.)
155  correction = 0.;
156  else if (cosc < -1.)
157  correction = PI;
158  else
159  correction = acos(cosc);
160  double tmp = utnew;
161  utnew = FNrange(utold - (GHA + g + riset * correction));
162  utold = tmp;
163  }
164  return (utnew * DEGS / 15.); // returns decimal hours UTC
165 }
166 
167 static double getLMT(double ut, double lon) {
168  double t = ut + lon / 15.;
169  if (t >= 0.)
170  if (t <= 24.)
171  return (t);
172  else
173  return (t - 24.);
174  else
175  return (t + 24.);
176 }
177 
178 static int getDaylightStatus(double lat, double lon, wxDateTime utcDateTime) {
179  if (fabs(lat) > 60.) return (0);
180  int y = utcDateTime.GetYear();
181  int m = utcDateTime.GetMonth() + 1; // wxBug? months seem to run 0..11 ?
182  int d = utcDateTime.GetDay();
183  int h = utcDateTime.GetHour();
184  int n = utcDateTime.GetMinute();
185  int s = utcDateTime.GetSecond();
186  if (y < 2000 || y > 2100) return (0);
187 
188  double ut = (double)h + (double)n / 60. + (double)s / 3600.;
189  double lt = getLMT(ut, lon);
190  double rsalt = -0.833;
191  double twalt = -12.;
192 
193  if (lt <= 12.) {
194  double sunrise = getDaylightEvent(lat, lon, +1, rsalt, y, m, d);
195  if (sunrise < 0.)
196  return (0);
197  else
198  sunrise = getLMT(sunrise, lon);
199 
200  if (fabs(lt - sunrise) < 0.15) return (SUNRISE);
201  if (lt > sunrise) return (DAY);
202  double twilight = getDaylightEvent(lat, lon, +1, twalt, y, m, d);
203  if (twilight < 0.)
204  return (0);
205  else
206  twilight = getLMT(twilight, lon);
207  if (lt > twilight)
208  return (MOTWILIGHT);
209  else
210  return (NIGHT);
211  } else {
212  double sunset = getDaylightEvent(lat, lon, -1, rsalt, y, m, d);
213  if (sunset < 0.)
214  return (0);
215  else
216  sunset = getLMT(sunset, lon);
217  if (fabs(lt - sunset) < 0.15) return (SUNSET);
218  if (lt < sunset) return (DAY);
219  double twilight = getDaylightEvent(lat, lon, -1, twalt, y, m, d);
220  if (twilight < 0.)
221  return (0);
222  else
223  twilight = getLMT(twilight, lon);
224  if (lt < twilight)
225  return (EVTWILIGHT);
226  else
227  return (NIGHT);
228  }
229 }
230 
231 RoutePropDlgImpl::RoutePropDlgImpl(wxWindow* parent, wxWindowID id,
232  const wxString& title, const wxPoint& pos,
233  const wxSize& size, long style)
234  : RoutePropDlg(parent, id, title, pos, size, style) {
235  m_pRoute = NULL;
236 
237  SetColorScheme(global_color_scheme);
238 
239  if (g_route_prop_sx > 0 && g_route_prop_sy > 0 &&
240  g_route_prop_sx < wxGetDisplaySize().x &&
241  g_route_prop_sy < wxGetDisplaySize().y) {
242  SetSize(g_route_prop_sx, g_route_prop_sy);
243  }
244 
245  if (g_route_prop_x > 0 && g_route_prop_y > 0 &&
246  g_route_prop_x < wxGetDisplaySize().x &&
247  g_route_prop_y < wxGetDisplaySize().y) {
248  SetPosition(wxPoint(10, 10));
249  }
250  RecalculateSize();
251 
252  Connect(wxEVT_COMMAND_MENU_SELECTED,
253  wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
254  NULL, this);
255 
256 #ifdef __WXOSX__
257  Connect(wxEVT_ACTIVATE,
258  wxActivateEventHandler(RoutePropDlgImpl::OnActivate),
259  NULL, this);
260 #endif
261 }
262 
263 RoutePropDlgImpl::~RoutePropDlgImpl() {
264  Disconnect(wxEVT_COMMAND_MENU_SELECTED,
265  wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
266  NULL, this);
267  instanceFlag = false;
268 }
269 
270 bool RoutePropDlgImpl::instanceFlag = false;
271 bool RoutePropDlgImpl::getInstanceFlag() {
272  return RoutePropDlgImpl::instanceFlag;
273 }
274 
275 RoutePropDlgImpl* RoutePropDlgImpl::single = NULL;
276 RoutePropDlgImpl* RoutePropDlgImpl::getInstance(wxWindow* parent) {
277  if (!instanceFlag) {
278  single = new RoutePropDlgImpl(parent);
279  instanceFlag = true;
280  }
281  return single;
282 }
283 
284 void RoutePropDlgImpl::OnActivate(wxActivateEvent& event){
285  wxFrame* pWin = wxDynamicCast(event.GetEventObject(), wxFrame);
286  long int style = pWin->GetWindowStyle();
287  if (event.GetActive())
288  pWin->SetWindowStyle(style | wxSTAY_ON_TOP);
289  else
290  pWin->SetWindowStyle(style ^ wxSTAY_ON_TOP);
291 }
292 
293 void RoutePropDlgImpl::RecalculateSize(void) {
294  wxSize esize;
295  esize.x = GetCharWidth() * 110;
296  esize.y = GetCharHeight() * 40;
297 
298  wxSize dsize = GetParent()->GetSize(); // GetClientSize();
299  esize.y = wxMin(esize.y, dsize.y - 0 /*(2 * GetCharHeight())*/);
300  esize.x = wxMin(esize.x, dsize.x - 0 /*(2 * GetCharHeight())*/);
301  SetSize(esize);
302 
303  wxSize fsize = GetSize();
304  wxSize canvas_size = GetParent()->GetSize();
305  wxPoint screen_pos = GetParent()->GetScreenPosition();
306  int xp = (canvas_size.x - fsize.x) / 2;
307  int yp = (canvas_size.y - fsize.y) / 2;
308  Move(screen_pos.x + xp, screen_pos.y + yp);
309 }
310 
311 void RoutePropDlgImpl::UpdatePoints() {
312  if (!m_pRoute) return;
313  wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
314  int selected_row = m_dvlcWaypoints->GetSelectedRow();
315  m_dvlcWaypoints->DeleteAllItems();
316 
317  wxVector<wxVariant> data;
318 
319  m_pRoute->UpdateSegmentDistances(
320  m_pRoute->m_PlannedSpeed); // to fix ETA properties
321  m_tcDistance->SetValue(
322  wxString::Format(wxT("%5.1f ") + getUsrDistanceUnit(),
323  toUsrDistance(m_pRoute->m_route_length)));
324  m_tcEnroute->SetValue(formatTimeDelta(wxLongLong(m_pRoute->m_route_time)));
325  // Iterate on Route Points, inserting blank fields starting with index 0
326  wxRoutePointListNode* pnode = m_pRoute->pRoutePointList->GetFirst();
327  int in = 0;
328  wxString slen, eta, ete;
329  double bearing, distance, speed;
330  double totalDistance = 0;
331  wxDateTime eta_dt = wxInvalidDateTime;
332  while (pnode) {
333  speed = pnode->GetData()->GetPlannedSpeed();
334  if (speed < .1) {
335  speed = m_pRoute->m_PlannedSpeed;
336  }
337  if (in == 0) {
338  DistanceBearingMercator(pnode->GetData()->GetLatitude(),
339  pnode->GetData()->GetLongitude(), gLat, gLon,
340  &bearing, &distance);
341  if (m_pRoute->m_PlannedDeparture.IsValid()) {
342  eta = wxString::Format(
343  "Start: %s", toUsrDateTime(m_pRoute->m_PlannedDeparture,
344  m_tz_selection, pnode->GetData()->m_lon)
345  .Format(ETA_FORMAT_STR)
346  .c_str());
347  eta.Append(wxString::Format(
348  _T(" (%s)"),
349  GetDaylightString(getDaylightStatus(pnode->GetData()->m_lat,
350  pnode->GetData()->m_lon,
351  m_pRoute->m_PlannedDeparture))
352  .c_str()));
353  eta_dt = m_pRoute->m_PlannedDeparture;
354  } else {
355  eta = _("N/A");
356  }
357  if (speed > .1) {
358  ete = formatTimeDelta(wxLongLong(3600. * distance / speed));
359  } else {
360  ete = _("N/A");
361  }
362  } else {
363  distance = pnode->GetData()->GetDistance();
364  bearing = pnode->GetData()->GetCourse();
365  if (pnode->GetData()->GetETA().IsValid()) {
366  eta = toUsrDateTime(pnode->GetData()->GetETA(), m_tz_selection,
367  pnode->GetData()->m_lon)
368  .Format(ETA_FORMAT_STR);
369  eta.Append(wxString::Format(
370  _T(" (%s)"),
371  GetDaylightString(getDaylightStatus(pnode->GetData()->m_lat,
372  pnode->GetData()->m_lon,
373  pnode->GetData()->GetETA()))
374  .c_str()));
375  eta_dt = pnode->GetData()->GetETA();
376  } else {
377  eta = wxEmptyString;
378  }
379  ete = pnode->GetData()->GetETE();
380  totalDistance += distance;
381  }
382  wxString name = pnode->GetData()->GetName();
383  double lat = pnode->GetData()->GetLatitude();
384  double lon = pnode->GetData()->GetLongitude();
385  wxString tide_station = pnode->GetData()->m_TideStation;
386  wxString desc = pnode->GetData()->GetDescription();
387  wxString etd;
388  if (pnode->GetData()->GetManualETD().IsValid()) {
389  // GetManualETD() returns time in UTC, always. So use it as such.
390  etd = toUsrDateTime(pnode->GetData()->GetManualETD(),
391  0 /*m_tz_selection*/, pnode->GetData()->m_lon)
392  .Format(ETA_FORMAT_STR);
393  if (pnode->GetData()->GetManualETD().IsValid() &&
394  pnode->GetData()->GetETA().IsValid() &&
395  pnode->GetData()->GetManualETD() < pnode->GetData()->GetETA()) {
396  etd.Prepend(
397  _T("!! ")); // Manually entered ETD is before we arrive here!
398  }
399  } else {
400  etd = wxEmptyString;
401  }
402  pnode = pnode->GetNext();
403  wxString crs;
404  if (pnode) {
405  crs = formatAngle(pnode->GetData()->GetCourse());
406  } else {
407  crs = _("Arrived");
408  }
409 
410  if (in == 0)
411  data.push_back(wxVariant("---"));
412  else {
413  std::ostringstream stm;
414  stm << in;
415  data.push_back(wxVariant(stm.str()));
416  }
417 
418  wxString schar = wxEmptyString;
419 #ifdef __ANDROID__
420  schar = wxString(" ");
421 #endif
422  data.push_back(wxVariant(name + schar)); // To
423  slen.Printf(wxT("%5.1f ") + getUsrDistanceUnit(), toUsrDistance(distance));
424  data.push_back(wxVariant(schar + slen + schar)); // Distance
425  data.push_back(wxVariant(schar + formatAngle(bearing))); // Bearing
426  slen.Printf(wxT("%5.1f ") + getUsrDistanceUnit(),
427  toUsrDistance(totalDistance));
428  data.push_back(wxVariant(schar + slen + schar)); // Total Distance
429  data.push_back(wxVariant(schar + ::toSDMM(1, lat, FALSE) + schar)); // Lat
430  data.push_back(wxVariant(schar + ::toSDMM(2, lon, FALSE) + schar)); // Lon
431  data.push_back(wxVariant(schar + ete + schar)); // ETE
432  data.push_back(schar + eta + schar); // ETA
433  data.push_back(
434  wxVariant(wxString::FromDouble(toUsrSpeed(speed)))); // Speed
435  data.push_back(wxVariant(
436  MakeTideInfo(tide_station, lat, lon, eta_dt))); // Next Tide event
437  data.push_back(wxVariant(desc)); // Description
438  data.push_back(wxVariant(crs));
439  data.push_back(wxVariant(etd));
440  data.push_back(wxVariant(
441  wxEmptyString)); // Empty column to fill the remaining space (Usually
442  // gets squeezed to zero, even if not empty)
443  m_dvlcWaypoints->AppendItem(data);
444  data.clear();
445  in++;
446  }
447  if (selected_row > 0) {
448  m_dvlcWaypoints->SelectRow(selected_row);
449  m_dvlcWaypoints->EnsureVisible(selection);
450  }
451 }
452 
453 wxDateTime RoutePropDlgImpl::toUsrDateTime(const wxDateTime ts,
454  const int format, const double lon) {
455  if (!ts.IsValid()) {
456  return ts;
457  }
458  wxDateTime dt;
459  switch (m_tz_selection) {
460  case 2: // LMT@Location
461  if (std::isnan(lon)) {
462  dt = wxInvalidDateTime;
463  } else {
464  dt =
465  ts.Add(wxTimeSpan(wxTimeSpan(0, 0, wxLongLong(lon * 3600. / 15.))));
466  }
467  break;
468  case 1: // Local@PC
469  dt = ts.FromUTC();
470  break;
471  case 0: // UTC
472  dt = ts;
473  break;
474  }
475  return dt;
476 }
477 
478 wxDateTime RoutePropDlgImpl::fromUsrDateTime(const wxDateTime ts,
479  const int format,
480  const double lon) {
481  if (!ts.IsValid()) {
482  return ts;
483  }
484  wxDateTime dt;
485  switch (m_tz_selection) {
486  case 2: // LMT@Location
487  if (std::isnan(lon)) {
488  dt = wxInvalidDateTime;
489  } else {
490  dt = ts.Subtract(wxTimeSpan(0, 0, wxLongLong(lon * 3600. / 15.)));
491  }
492  break;
493  case 1: // Local@PC
494  dt = ts.ToUTC();
495  break;
496  case 0: // UTC
497  dt = ts;
498  break;
499  }
500  return dt;
501 }
502 
503 void RoutePropDlgImpl::SetRouteAndUpdate(Route* pR, bool only_points) {
504  if (NULL == pR) return;
505 
506  if (m_pRoute &&
507  m_pRoute != pR) // We had unsaved changes, but now display another route
508  ResetChanges();
509 
510  m_OrigRoute.m_PlannedDeparture = pR->m_PlannedDeparture;
511  m_OrigRoute.m_PlannedSpeed = pR->m_PlannedSpeed;
512 
513  wxString title =
514  pR->GetName() == wxEmptyString ? _("Route Properties") : pR->GetName();
515  if (!pR->m_bIsInLayer)
516  SetTitle(title);
517  else {
518  wxString caption(wxString::Format(_T("%s, %s: %s"), title, _("Layer"),
519  GetLayerName(pR->m_LayerID)));
520  SetTitle(caption);
521  }
522 
523  // Fetch any config file values
524  if (!only_points) {
525  if (!pR->m_PlannedDeparture.IsValid())
526  pR->m_PlannedDeparture = wxDateTime::Now().ToUTC();
527 
528  m_tz_selection = 1; // Local PC time by default
529  if (pR != m_pRoute) {
530  if (pR->m_TimeDisplayFormat == RTE_TIME_DISP_UTC)
531  m_tz_selection = 0;
532  else if (pR->m_TimeDisplayFormat == RTE_TIME_DISP_LOCAL)
533  m_tz_selection = 2;
534  m_pEnroutePoint = NULL;
535  m_bStartNow = false;
536  }
537 
538  m_pRoute = pR;
539 
540  m_tcPlanSpeed->SetValue(
541  wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
542 
543  if (m_scrolledWindowLinks) {
544  wxWindowList kids = m_scrolledWindowLinks->GetChildren();
545  for (unsigned int i = 0; i < kids.GetCount(); i++) {
546  wxWindowListNode* node = kids.Item(i);
547  wxWindow* win = node->GetData();
548  if (win->IsKindOf(CLASSINFO(wxHyperlinkCtrl))) {
549  ((wxHyperlinkCtrl*)win)
550  ->Disconnect(
551  wxEVT_COMMAND_HYPERLINK,
552  wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick));
553  ((wxHyperlinkCtrl*)win)
554  ->Disconnect(
555  wxEVT_RIGHT_DOWN,
556  wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu));
557  win->Destroy();
558  }
559  }
560  int NbrOfLinks = m_pRoute->m_HyperlinkList->GetCount();
561  HyperlinkList* hyperlinklist = m_pRoute->m_HyperlinkList;
562  if (NbrOfLinks > 0) {
563  wxHyperlinkListNode* linknode = hyperlinklist->GetFirst();
564  while (linknode) {
565  Hyperlink* link = linknode->GetData();
566  wxString Link = link->Link;
567  wxString Descr = link->DescrText;
568 
569  wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
570  m_scrolledWindowLinks, wxID_ANY, Descr, Link, wxDefaultPosition,
571  wxDefaultSize, wxHL_DEFAULT_STYLE);
572  ctrl->Connect(
573  wxEVT_COMMAND_HYPERLINK,
574  wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick), NULL,
575  this);
576  if (!m_pRoute->m_bIsInLayer) {
577  ctrl->Connect(
578  wxEVT_RIGHT_DOWN,
579  wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu),
580  NULL, this);
581  }
582  bSizerLinks->Add(ctrl, 0, wxALL, 5);
583 
584  linknode = linknode->GetNext();
585  }
586  }
587  m_scrolledWindowLinks->InvalidateBestSize();
588  m_scrolledWindowLinks->Layout();
589  bSizerLinks->Layout();
590  }
591 
592  m_choiceTimezone->SetSelection(m_tz_selection);
593 
594  // Reorganize dialog for route or track display
595  m_tcName->SetValue(m_pRoute->m_RouteNameString);
596  m_tcFrom->SetValue(m_pRoute->m_RouteStartString);
597  m_tcTo->SetValue(m_pRoute->m_RouteEndString);
598  m_tcDescription->SetValue(m_pRoute->m_RouteDescription);
599 
600  m_tcName->SetFocus();
601  if (m_pRoute->m_PlannedDeparture.IsValid() &&
602  m_pRoute->m_PlannedDeparture.GetValue() > 0) {
603  m_dpDepartureDate->SetValue(
604  toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
605  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon)
606  .GetDateOnly());
607  m_tpDepartureTime->SetValue(toUsrDateTime(
608  m_pRoute->m_PlannedDeparture, m_tz_selection,
609  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon));
610  } else {
611  m_dpDepartureDate->SetValue(
612  toUsrDateTime(wxDateTime::Now(), m_tz_selection,
613  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon)
614  .GetDateOnly());
615  m_tpDepartureTime->SetValue(toUsrDateTime(
616  wxDateTime::Now(), m_tz_selection,
617  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon));
618  }
619  }
620 
621  m_btnSplit->Enable(false);
622  if (!m_pRoute) return;
623 
624  if (m_pRoute->m_Colour == wxEmptyString) {
625  m_choiceColor->Select(0);
626  } else {
627  for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
628  i++) {
629  if (m_pRoute->m_Colour == ::GpxxColorNames[i]) {
630  m_choiceColor->Select(i + 1);
631  break;
632  }
633  }
634  }
635 
636  for (unsigned int i = 0; i < sizeof(::StyleValues) / sizeof(int); i++) {
637  if (m_pRoute->m_style == ::StyleValues[i]) {
638  m_choiceStyle->Select(i);
639  break;
640  }
641  }
642 
643  for (unsigned int i = 0; i < sizeof(::WidthValues) / sizeof(int); i++) {
644  if (m_pRoute->m_width == ::WidthValues[i]) {
645  m_choiceWidth->Select(i);
646  break;
647  }
648  }
649 
650  UpdatePoints();
651 
652  m_btnExtend->Enable(IsThisRouteExtendable());
653 }
654 
655 void RoutePropDlgImpl::DepartureDateOnDateChanged(wxDateEvent& event) {
656  if (!m_pRoute) return;
657  m_pRoute->SetDepartureDate(GetDepartureTS());
658  UpdatePoints();
659  event.Skip();
660 }
661 
662 void RoutePropDlgImpl::DepartureTimeOnTimeChanged(wxDateEvent& event) {
663  if (!m_pRoute) return;
664  m_pRoute->SetDepartureDate(GetDepartureTS());
665  UpdatePoints();
666  event.Skip();
667 }
668 
669 void RoutePropDlgImpl::TimezoneOnChoice(wxCommandEvent& event) {
670  m_tz_selection = m_choiceTimezone->GetSelection();
671  m_dpDepartureDate->SetValue(
672  toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
673  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon)
674  .GetDateOnly());
675  m_tpDepartureTime->SetValue(
676  toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
677  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon));
678  UpdatePoints();
679  event.Skip();
680 }
681 
682 void RoutePropDlgImpl::PlanSpeedOnTextEnter(wxCommandEvent& event) {
683  if (!m_pRoute) return;
684  double spd;
685  if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
686  if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
687  m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
688  UpdatePoints();
689  }
690  } else {
691  m_tcPlanSpeed->SetValue(
692  wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
693  }
694 }
695 
696 void RoutePropDlgImpl::PlanSpeedOnKillFocus(wxFocusEvent& event) {
697  if (!m_pRoute) return;
698  double spd;
699  if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
700  if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
701  m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
702  UpdatePoints();
703  }
704  } else {
705  m_tcPlanSpeed->SetValue(
706  wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
707  }
708  event.Skip();
709 }
710 
711 static int ev_col;
712 void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemEditingDone(
713  wxDataViewEvent& event) {
714  // There is a bug in wxWidgets, the EDITING_DONE event does not contain the
715  // new value, so we must save the data and do the work later in the value
716  // changed event.
717  ev_col = event.GetColumn();
718 }
719 
720 void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemValueChanged(
721  wxDataViewEvent& event) {
722 #if wxCHECK_VERSION(3, 1, 2)
723  // wx 3.0.x crashes in the bellow code
724  if (!m_pRoute) return;
725  wxDataViewModel* const model = event.GetModel();
726  wxVariant value;
727  model->GetValue(value, event.GetItem(), ev_col);
728  RoutePoint* p = m_pRoute->GetPoint(
729  static_cast<int>(reinterpret_cast<long long>(event.GetItem().GetID())));
730  if (ev_col == COLUMN_PLANNED_SPEED) {
731  double spd;
732  if (!value.GetString().ToDouble(&spd)) {
733  spd = 0.0;
734  }
735  p->SetPlannedSpeed(fromUsrSpeed(spd));
736  } else if (ev_col == COLUMN_ETD) {
737  wxString::const_iterator end;
738  wxDateTime etd;
739 
740  wxString ts = value.GetString();
741  if (ts.StartsWith("!")) {
742  ts.Replace("!", wxEmptyString, true);
743  }
744  ts.Trim(true);
745  ts.Trim(false);
746 
747  if (!ts.IsEmpty()) {
748  if (!etd.ParseDateTime(ts, &end)) {
749  p->SetETD(wxInvalidDateTime);
750  } else {
751  p->SetETD(
752  fromUsrDateTime(etd, m_tz_selection, p->m_lon).FormatISOCombined());
753  }
754  } else {
755  p->SetETD(wxInvalidDateTime);
756  }
757  }
758  UpdatePoints();
759 #endif
760 }
761 
762 void RoutePropDlgImpl::WaypointsOnDataViewListCtrlSelectionChanged(
763  wxDataViewEvent& event) {
764  long selected_row = m_dvlcWaypoints->GetSelectedRow();
765  if (selected_row > 0 && selected_row < m_dvlcWaypoints->GetItemCount() - 1) {
766  m_btnSplit->Enable(true);
767  } else {
768  m_btnSplit->Enable(false);
769  }
770  if (IsThisRouteExtendable()) {
771  m_btnExtend->Enable(true);
772  } else {
773  m_btnExtend->Enable(false);
774  }
775  if (selected_row >= 0 && selected_row < m_dvlcWaypoints->GetItemCount()) {
776  RoutePoint* prp = m_pRoute->GetPoint(selected_row + 1);
777  if (prp) {
778  if (gFrame->GetFocusCanvas()) {
779  gFrame->JumpToPosition(gFrame->GetFocusCanvas(), prp->m_lat,
780  prp->m_lon,
781  gFrame->GetFocusCanvas()->GetVPScale());
782  }
783 #ifdef __WXMSW__
784  if (m_dvlcWaypoints) m_dvlcWaypoints->SetFocus();
785 #endif
786  }
787  }
788 }
789 
790 wxDateTime RoutePropDlgImpl::GetDepartureTS() {
791  wxDateTime dt = m_dpDepartureDate->GetValue();
792  dt.SetHour(m_tpDepartureTime->GetValue().GetHour());
793  dt.SetMinute(m_tpDepartureTime->GetValue().GetMinute());
794  dt.SetSecond(m_tpDepartureTime->GetValue().GetSecond());
795  return fromUsrDateTime(
796  dt, m_tz_selection,
797  m_pRoute->pRoutePointList->GetFirst()->GetData()->m_lon);
798  ;
799 }
800 
801 void RoutePropDlgImpl::OnRoutepropCopyTxtClick(wxCommandEvent& event) {
802  wxString tab("\t", wxConvUTF8);
803  wxString eol("\n", wxConvUTF8);
804  wxString csvString;
805 
806  csvString << this->GetTitle() << eol << _("Name") << tab
807  << m_pRoute->m_RouteNameString << eol << _("Depart From") << tab
808  << m_pRoute->m_RouteStartString << eol << _("Destination") << tab
809  << m_pRoute->m_RouteEndString << eol << _("Total distance") << tab
810  << m_tcDistance->GetValue() << eol << _("Speed (Kts)") << tab
811  << m_tcPlanSpeed->GetValue() << eol
812  << _("Departure Time") + _T(" (") + _T(ETA_FORMAT_STR) + _T(")")
813  << tab << GetDepartureTS().Format(ETA_FORMAT_STR) << eol
814  << _("Time enroute") << tab << m_tcEnroute->GetValue() << eol
815  << eol;
816 
817  int noCols;
818  int noRows;
819  noCols = m_dvlcWaypoints->GetColumnCount();
820  noRows = m_dvlcWaypoints->GetItemCount();
821  wxListItem item;
822  item.SetMask(wxLIST_MASK_TEXT);
823 
824  for (int i = 0; i < noCols; i++) {
825  wxDataViewColumn* col = m_dvlcWaypoints->GetColumn(i);
826  csvString << col->GetTitle() << tab;
827  }
828  csvString << eol;
829 
830  wxVariant value;
831  for (int j = 0; j < noRows; j++) {
832  for (int i = 0; i < noCols; i++) {
833  m_dvlcWaypoints->GetValue(value, j, i);
834  csvString << value.MakeString() << tab;
835  }
836  csvString << eol;
837  }
838 
839  if (wxTheClipboard->Open()) {
840  wxTextDataObject* data = new wxTextDataObject;
841  data->SetText(csvString);
842  wxTheClipboard->SetData(data);
843  wxTheClipboard->Close();
844  }
845 }
846 
847 void RoutePropDlgImpl::OnRoutePropMenuSelected(wxCommandEvent& event) {
848  bool moveup = false;
849  switch (event.GetId()) {
850  case ID_RCLK_MENU_COPY_TEXT: {
851  OnRoutepropCopyTxtClick(event);
852  break;
853  }
854  case ID_RCLK_MENU_MOVEUP_WP: {
855  moveup = true;
856  }
857  case ID_RCLK_MENU_MOVEDOWN_WP: {
858  wxString mess =
859  moveup ? _("Are you sure you want to move Up this waypoint?")
860  : _("Are you sure you want to move Down this waypoint?");
861  int dlg_return =
862  OCPNMessageBox(this, mess, _("OpenCPN Move Waypoint"),
863  (long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
864 
865  if (dlg_return == wxID_YES) {
866  wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
867  RoutePoint* pRP = m_pRoute->GetPoint(
868  static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
869  int nRP = m_pRoute->pRoutePointList->IndexOf(pRP) + (moveup ? -1 : 1);
870 
871  pSelect->DeleteAllSelectableRoutePoints(m_pRoute);
872  pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
873 
874  m_pRoute->pRoutePointList->DeleteObject(pRP);
875  m_pRoute->pRoutePointList->Insert(nRP, pRP);
876 
877  pSelect->AddAllSelectableRouteSegments(m_pRoute);
878  pSelect->AddAllSelectableRoutePoints(m_pRoute);
879 
880  pConfig->UpdateRoute(m_pRoute);
881 
882  m_pRoute->FinalizeForRendering();
883  m_pRoute->UpdateSegmentDistances();
884  ;
885 
886  gFrame->InvalidateAllGL();
887 
888  m_dvlcWaypoints->SelectRow(nRP);
889 
890  SetRouteAndUpdate(m_pRoute, true);
891  }
892  break;
893  }
894  case ID_RCLK_MENU_DELETE: {
895  int dlg_return = OCPNMessageBox(
896  this, _("Are you sure you want to remove this waypoint?"),
897  _("OpenCPN Remove Waypoint"),
898  (long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
899 
900  if (dlg_return == wxID_YES) {
901  int sel = m_dvlcWaypoints->GetSelectedRow();
902  m_dvlcWaypoints->SelectRow(sel);
903 
904  wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
905  RoutePoint* pRP = m_pRoute->GetPoint(
906  static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
907 
908  g_pRouteMan->RemovePointFromRoute(pRP, m_pRoute, 0);
909  gFrame->InvalidateAllGL();
910  UpdatePoints();
911  }
912  break;
913  }
914  case ID_RCLK_MENU_EDIT_WP: {
915  wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
916  RoutePoint* pRP = m_pRoute->GetPoint(
917  static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
918 
919  RouteManagerDialog::WptShowPropertiesDialog(std::vector<RoutePoint*> {pRP}, this);
920  break;
921  }
922  }
923 }
924 
925 void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemContextMenu(
926  wxDataViewEvent& event) {
927  wxMenu menu;
928  if (!m_pRoute->m_bIsInLayer) {
929  wxMenuItem* editItem = new wxMenuItem(&menu, ID_RCLK_MENU_EDIT_WP,
930  _("Waypoint Properties") + _T("..."));
931  wxMenuItem* moveUpItem =
932  new wxMenuItem(&menu, ID_RCLK_MENU_MOVEUP_WP, _("Move Up"));
933  wxMenuItem* moveDownItem =
934  new wxMenuItem(&menu, ID_RCLK_MENU_MOVEDOWN_WP, _("Move Down"));
935  wxMenuItem* delItem =
936  new wxMenuItem(&menu, ID_RCLK_MENU_DELETE, _("Remove Selected"));
937 #ifdef __ANDROID__
938  wxFont* pf = OCPNGetFont(_T("Menu"), 0);
939  editItem->SetFont(*pf);
940  moveUpItem->SetFont(*pf);
941  moveDownItem->SetFont(*pf);
942  delItem->SetFont(*pf);
943 #endif
944 #if defined(__WXMSW__)
945  wxFont* pf = GetOCPNScaledFont(_T("Menu"));
946  editItem->SetFont(*pf);
947  moveUpItem->SetFont(*pf);
948  moveDownItem->SetFont(*pf);
949  delItem->SetFont(*pf);
950 #endif
951 
952  menu.Append(editItem);
953  if (g_btouch) menu.AppendSeparator();
954  menu.Append(moveUpItem);
955  if (g_btouch) menu.AppendSeparator();
956  menu.Append(moveDownItem);
957  if (g_btouch) menu.AppendSeparator();
958  menu.Append(delItem);
959 
960  editItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0);
961  moveUpItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 1 &&
962  m_dvlcWaypoints->GetItemCount() > 2);
963  moveDownItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0 &&
964  m_dvlcWaypoints->GetSelectedRow() <
965  m_dvlcWaypoints->GetItemCount() - 1 &&
966  m_dvlcWaypoints->GetItemCount() > 2);
967  delItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0 &&
968  m_dvlcWaypoints->GetItemCount() > 2);
969  }
970 #ifndef __WXQT__
971  wxMenuItem* copyItem =
972  new wxMenuItem(&menu, ID_RCLK_MENU_COPY_TEXT, _("&Copy all as text"));
973 
974 #if defined(__WXMSW__)
975  wxFont* qFont = GetOCPNScaledFont(_T("Menu"));
976  copyItem->SetFont(*qFont);
977 #endif
978 
979  if (g_btouch) menu.AppendSeparator();
980  menu.Append(copyItem);
981 #endif
982 
983  PopupMenu(&menu);
984 }
985 
986 void RoutePropDlgImpl::ResetChanges() {
987  if (!m_pRoute) return;
988  m_pRoute->m_PlannedSpeed = m_OrigRoute.m_PlannedSpeed;
989  m_pRoute->m_PlannedDeparture = m_OrigRoute.m_PlannedDeparture;
990  m_pRoute = NULL;
991 }
992 
993 void RoutePropDlgImpl::SaveChanges() {
994  if (m_pRoute && !m_pRoute->m_bIsInLayer) {
995  // Get User input Text Fields
996  m_pRoute->m_RouteNameString = m_tcName->GetValue();
997  m_pRoute->m_RouteStartString = m_tcFrom->GetValue();
998  m_pRoute->m_RouteEndString = m_tcTo->GetValue();
999  m_pRoute->m_RouteDescription = m_tcDescription->GetValue();
1000  if (m_choiceColor->GetSelection() == 0) {
1001  m_pRoute->m_Colour = wxEmptyString;
1002  } else {
1003  m_pRoute->m_Colour = ::GpxxColorNames[m_choiceColor->GetSelection() - 1];
1004  }
1005  m_pRoute->m_style =
1006  (wxPenStyle)::StyleValues[m_choiceStyle->GetSelection()];
1007  m_pRoute->m_width = ::WidthValues[m_choiceWidth->GetSelection()];
1008  switch (m_tz_selection) {
1009  case 1:
1010  m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_PC;
1011  break;
1012  case 2:
1013  m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_LOCAL;
1014  break;
1015  default:
1016  m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_UTC;
1017  }
1018 
1019  pConfig->UpdateRoute(m_pRoute);
1020  pConfig->UpdateSettings();
1021  m_pRoute = NULL;
1022  }
1023 }
1024 
1025 void RoutePropDlgImpl::SetColorScheme(ColorScheme cs) { DimeControl(this); }
1026 
1027 void RoutePropDlgImpl::SaveGeometry() {
1028  GetSize(&g_route_prop_sx, &g_route_prop_sy);
1029  GetPosition(&g_route_prop_x, &g_route_prop_y);
1030 }
1031 
1032 void RoutePropDlgImpl::BtnsOnOKButtonClick(wxCommandEvent& event) {
1033  SaveChanges();
1034  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
1035  pRouteManagerDialog->UpdateRouteListCtrl();
1036  }
1037  Hide();
1038  SaveGeometry();
1039 }
1040 
1041 void RoutePropDlgImpl::SplitOnButtonClick(wxCommandEvent& event) {
1042  m_btnSplit->Enable(false);
1043 
1044  if (m_pRoute->m_bIsInLayer) return;
1045 
1046  int nSelected = m_dvlcWaypoints->GetSelectedRow() + 1;
1047  if ((nSelected > 1) && (nSelected < m_pRoute->GetnPoints())) {
1048  m_pHead = new Route();
1049  m_pTail = new Route();
1050  m_pHead->CloneRoute(m_pRoute, 1, nSelected, _("_A"));
1051  m_pTail->CloneRoute(m_pRoute, nSelected, m_pRoute->GetnPoints(), _("_B"),
1052  true);
1053  pRouteList->Append(m_pHead);
1054  pConfig->AddNewRoute(m_pHead);
1055 
1056  pRouteList->Append(m_pTail);
1057  pConfig->AddNewRoute(m_pTail);
1058 
1059  pConfig->DeleteConfigRoute(m_pRoute);
1060 
1061  pSelect->DeleteAllSelectableRoutePoints(m_pRoute);
1062  pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
1063  g_pRouteMan->DeleteRoute(m_pRoute, NavObjectChanges::getInstance());
1064  pSelect->AddAllSelectableRouteSegments(m_pTail);
1065  pSelect->AddAllSelectableRoutePoints(m_pTail);
1066  pSelect->AddAllSelectableRouteSegments(m_pHead);
1067  pSelect->AddAllSelectableRoutePoints(m_pHead);
1068 
1069  SetRouteAndUpdate(m_pTail);
1070  UpdatePoints();
1071 
1072  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
1073  pRouteManagerDialog->UpdateRouteListCtrl();
1074  }
1075 }
1076 
1077 void RoutePropDlgImpl::PrintOnButtonClick(wxCommandEvent& event) {
1078  RoutePrintSelection* dlg = new RoutePrintSelection(this, m_pRoute);
1079  DimeControl(dlg);
1080  dlg->ShowWindowModalThenDo([this, dlg](int retcode) {
1081  if (retcode == wxID_OK) {
1082  }
1083  });
1084 }
1085 
1086 void RoutePropDlgImpl::ExtendOnButtonClick(wxCommandEvent& event) {
1087  m_btnExtend->Enable(false);
1088 
1089  if (IsThisRouteExtendable()) {
1090  int fm = m_pExtendRoute->GetIndexOf(m_pExtendPoint) + 1;
1091  int to = m_pExtendRoute->GetnPoints();
1092  if (fm <= to) {
1093  pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
1094  m_pRoute->CloneRoute(m_pExtendRoute, fm, to, _("_plus"));
1095  pSelect->AddAllSelectableRouteSegments(m_pRoute);
1096  SetRouteAndUpdate(m_pRoute);
1097  UpdatePoints();
1098  }
1099  }
1100  m_btnExtend->Enable(true);
1101 }
1102 
1103 bool RoutePropDlgImpl::IsThisRouteExtendable() {
1104  m_pExtendRoute = NULL;
1105  m_pExtendPoint = NULL;
1106  if (m_pRoute->m_bRtIsActive || m_pRoute->m_bIsInLayer) return false;
1107 
1108  RoutePoint* pLastPoint = m_pRoute->GetLastPoint();
1109  wxArrayPtrVoid* pEditRouteArray;
1110 
1111  pEditRouteArray = g_pRouteMan->GetRouteArrayContaining(pLastPoint);
1112  // remove invisible & own routes from choices
1113  int i;
1114  for (i = pEditRouteArray->GetCount(); i > 0; i--) {
1115  Route* p = (Route*)pEditRouteArray->Item(i - 1);
1116  if (!p->IsVisible() || (p->m_GUID == m_pRoute->m_GUID))
1117  pEditRouteArray->RemoveAt(i - 1);
1118  }
1119  if (pEditRouteArray->GetCount() == 1) {
1120  m_pExtendPoint = pLastPoint;
1121  } else {
1122  if (pEditRouteArray->GetCount() == 0) {
1123  int nearby_radius_meters =
1124  (int)(8. / gFrame->GetPrimaryCanvas()->GetCanvasTrueScale());
1125  double rlat = pLastPoint->m_lat;
1126  double rlon = pLastPoint->m_lon;
1127 
1128  m_pExtendPoint = pWayPointMan->GetOtherNearbyWaypoint(
1129  rlat, rlon, nearby_radius_meters, pLastPoint->m_GUID);
1130  if (m_pExtendPoint) {
1131  wxArrayPtrVoid* pCloseWPRouteArray =
1132  g_pRouteMan->GetRouteArrayContaining(m_pExtendPoint);
1133  if (pCloseWPRouteArray) {
1134  pEditRouteArray = pCloseWPRouteArray;
1135 
1136  // remove invisible & own routes from choices
1137  for (i = pEditRouteArray->GetCount(); i > 0; i--) {
1138  Route* p = (Route*)pEditRouteArray->Item(i - 1);
1139  if (!p->IsVisible() || (p->m_GUID == m_pRoute->m_GUID))
1140  pEditRouteArray->RemoveAt(i - 1);
1141  }
1142  }
1143  }
1144  }
1145  }
1146  if (pEditRouteArray->GetCount() == 1) {
1147  Route* p = (Route*)pEditRouteArray->Item(0);
1148  int fm = p->GetIndexOf(m_pExtendPoint) + 1;
1149  int to = p->GetnPoints();
1150  if (fm <= to) {
1151  m_pExtendRoute = p;
1152  delete pEditRouteArray;
1153  return true;
1154  }
1155  }
1156  delete pEditRouteArray;
1157 
1158  return false;
1159 }
1160 
1161 wxString RoutePropDlgImpl::MakeTideInfo(wxString stationName, double lat,
1162  double lon, wxDateTime utcTime) {
1163  if (stationName.Find("lind") != wxNOT_FOUND) int yyp = 4;
1164 
1165  if (stationName.IsEmpty()) {
1166  return wxEmptyString;
1167  }
1168  if (!utcTime.IsValid()) {
1169  return _("Invalid date/time!");
1170  }
1171  int stationID = ptcmgr->GetStationIDXbyName(stationName, lat, lon);
1172  if (stationID == 0) {
1173  return _("Unknown station!");
1174  }
1175  time_t dtmtt = utcTime.FromUTC().GetTicks();
1176  int ev = ptcmgr->GetNextBigEvent(&dtmtt, stationID);
1177 
1178  wxDateTime dtm;
1179  dtm.Set(dtmtt).MakeUTC();
1180 
1181  wxString tide_form = wxEmptyString;
1182 
1183  if (ev == 1) {
1184  tide_form.Append(_T("LW: "));
1185  } else if (ev == 2) {
1186  tide_form.Append(_T("HW: "));
1187  } else if (ev == 0) {
1188  tide_form.Append(_("Unavailable: "));
1189  }
1190 
1191  int offset =
1192  ptcmgr->GetStationTimeOffset((IDX_entry*)ptcmgr->GetIDX_entry(stationID));
1193 
1194  tide_form.Append(
1195  toUsrDateTime(dtm, m_tz_selection, lon).Format(ETA_FORMAT_STR));
1196  dtm.Add(wxTimeSpan(0, offset, 0));
1197  tide_form.Append(wxString::Format(_T(" (") + _("Local") + _T(": %s) @ %s"),
1198  dtm.Format(ETA_FORMAT_STR),
1199  stationName.c_str()));
1200 
1201  return tide_form;
1202 }
1203 
1204 void RoutePropDlgImpl::ItemEditOnMenuSelection(wxCommandEvent& event) {
1205  wxString findurl = m_pEditedLink->GetURL();
1206  wxString findlabel = m_pEditedLink->GetLabel();
1207 
1208  LinkPropImpl* LinkPropDlg = new LinkPropImpl(this);
1209  LinkPropDlg->m_textCtrlLinkDescription->SetValue(findlabel);
1210  LinkPropDlg->m_textCtrlLinkUrl->SetValue(findurl);
1211  DimeControl(LinkPropDlg);
1212  LinkPropDlg->ShowWindowModalThenDo([this, LinkPropDlg, findurl,
1213  findlabel](int retcode) {
1214  if (retcode == wxID_OK) {
1215  int NbrOfLinks = m_pRoute->m_HyperlinkList->GetCount();
1216  HyperlinkList* hyperlinklist = m_pRoute->m_HyperlinkList;
1217  // int len = 0;
1218  if (NbrOfLinks > 0) {
1219  wxHyperlinkListNode* linknode = hyperlinklist->GetFirst();
1220  while (linknode) {
1221  Hyperlink* link = linknode->GetData();
1222  wxString Link = link->Link;
1223  wxString Descr = link->DescrText;
1224  if (Link == findurl &&
1225  (Descr == findlabel ||
1226  (Link == findlabel && Descr == wxEmptyString))) {
1227  link->Link = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1228  link->DescrText =
1229  LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1230  wxHyperlinkCtrl* h =
1231  (wxHyperlinkCtrl*)m_scrolledWindowLinks->FindWindowByLabel(
1232  findlabel);
1233  if (h) {
1234  h->SetLabel(LinkPropDlg->m_textCtrlLinkDescription->GetValue());
1235  h->SetURL(LinkPropDlg->m_textCtrlLinkUrl->GetValue());
1236  }
1237  }
1238  linknode = linknode->GetNext();
1239  }
1240  }
1241 
1242  m_scrolledWindowLinks->InvalidateBestSize();
1243  m_scrolledWindowLinks->Layout();
1244  bSizerLinks->Layout();
1245  }
1246  });
1247  event.Skip();
1248 }
1249 
1250 void RoutePropDlgImpl::ItemAddOnMenuSelection(wxCommandEvent& event) {
1251  AddLinkOnButtonClick(event);
1252 }
1253 
1254 void RoutePropDlgImpl::ItemDeleteOnMenuSelection(wxCommandEvent& event) {
1255  wxHyperlinkListNode* nodeToDelete = NULL;
1256  wxString findurl = m_pEditedLink->GetURL();
1257  wxString findlabel = m_pEditedLink->GetLabel();
1258 
1259  wxWindowList kids = m_scrolledWindowLinks->GetChildren();
1260  for (unsigned int i = 0; i < kids.GetCount(); i++) {
1261  wxWindowListNode* node = kids.Item(i);
1262  wxWindow* win = node->GetData();
1263 
1264  if (win->IsKindOf(CLASSINFO(wxHyperlinkCtrl))) {
1265  ((wxHyperlinkCtrl*)win)
1266  ->Disconnect(
1267  wxEVT_COMMAND_HYPERLINK,
1268  wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick));
1269  ((wxHyperlinkCtrl*)win)
1270  ->Disconnect(
1271  wxEVT_RIGHT_DOWN,
1272  wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu));
1273  win->Destroy();
1274  }
1275  }
1276 
1278  int NbrOfLinks = m_pRoute->m_HyperlinkList->GetCount();
1279  HyperlinkList* hyperlinklist = m_pRoute->m_HyperlinkList;
1280  // int len = 0;
1281  if (NbrOfLinks > 0) {
1282  wxHyperlinkListNode* linknode = hyperlinklist->GetFirst();
1283  while (linknode) {
1284  Hyperlink* link = linknode->GetData();
1285  wxString Link = link->Link;
1286  wxString Descr = link->DescrText;
1287  if (Link == findurl &&
1288  (Descr == findlabel || (Link == findlabel && Descr == wxEmptyString)))
1289  nodeToDelete = linknode;
1290  else {
1291  wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
1292  m_scrolledWindowLinks, wxID_ANY, Descr, Link, wxDefaultPosition,
1293  wxDefaultSize, wxHL_DEFAULT_STYLE);
1294  ctrl->Connect(
1295  wxEVT_COMMAND_HYPERLINK,
1296  wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick), NULL,
1297  this);
1298  ctrl->Connect(
1299  wxEVT_RIGHT_DOWN,
1300  wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu), NULL,
1301  this);
1302 
1303  bSizerLinks->Add(ctrl, 0, wxALL, 5);
1304  }
1305  linknode = linknode->GetNext();
1306  }
1307  }
1308  if (nodeToDelete) {
1309  hyperlinklist->DeleteNode(nodeToDelete);
1310  }
1311  m_scrolledWindowLinks->InvalidateBestSize();
1312  m_scrolledWindowLinks->Layout();
1313  bSizerLinks->Layout();
1314  event.Skip();
1315 }
1316 
1317 void RoutePropDlgImpl::AddLinkOnButtonClick(wxCommandEvent& event) {
1318  LinkPropImpl* LinkPropDlg = new LinkPropImpl(this);
1319  LinkPropDlg->m_textCtrlLinkDescription->SetValue(wxEmptyString);
1320  LinkPropDlg->m_textCtrlLinkUrl->SetValue(wxEmptyString);
1321  DimeControl(LinkPropDlg);
1322  LinkPropDlg->ShowWindowModalThenDo([this, LinkPropDlg](int retcode) {
1323  if (retcode == wxID_OK) {
1324  wxString desc = LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1325  if (desc == wxEmptyString)
1326  desc = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1327  wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
1328  m_scrolledWindowLinks, wxID_ANY, desc,
1329  LinkPropDlg->m_textCtrlLinkUrl->GetValue(), wxDefaultPosition,
1330  wxDefaultSize, wxHL_DEFAULT_STYLE);
1331  ctrl->Connect(wxEVT_COMMAND_HYPERLINK,
1332  wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick),
1333  NULL, this);
1334  ctrl->Connect(wxEVT_RIGHT_DOWN,
1335  wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu),
1336  NULL, this);
1337 
1338  bSizerLinks->Add(ctrl, 0, wxALL, 5);
1339  m_scrolledWindowLinks->InvalidateBestSize();
1340  m_scrolledWindowLinks->Layout();
1341  bSizerLinks->Layout();
1342 
1343  Hyperlink* h = new Hyperlink();
1344  h->DescrText = LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1345  h->Link = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1346  h->LType = wxEmptyString;
1347  m_pRoute->m_HyperlinkList->Append(h);
1348  }
1349  });
1350 }
1351 
1352 void RoutePropDlgImpl::BtnEditOnToggleButton(wxCommandEvent& event) {
1353  if (m_toggleBtnEdit->GetValue()) {
1354  m_stEditEnabled->SetLabel(_("Links are opened for editing."));
1355  } else {
1356  m_stEditEnabled->SetLabel(_("Links are opened in the default browser."));
1357  }
1358  event.Skip();
1359 }
1360 
1361 void RoutePropDlgImpl::OnHyperlinkClick(wxHyperlinkEvent& event) {
1362  if (m_toggleBtnEdit->GetValue()) {
1363  m_pEditedLink = (wxHyperlinkCtrl*)event.GetEventObject();
1364  ItemEditOnMenuSelection(event);
1365  event.Skip(false);
1366  return;
1367  }
1368  // Windows has trouble handling local file URLs with embedded anchor
1369  // points, e.g file://testfile.html#point1 The trouble is with the
1370  // wxLaunchDefaultBrowser with verb "open" Workaround is to probe the
1371  // registry to get the default browser, and open directly
1372  //
1373  // But, we will do this only if the URL contains the anchor point character
1374  // '#' What a hack......
1375 
1376 #ifdef __WXMSW__
1377  wxString cc = event.GetURL();
1378  if (cc.Find(_T("#")) != wxNOT_FOUND) {
1379  wxRegKey RegKey(
1380  wxString(_T("HKEY_CLASSES_ROOT\\HTTP\\shell\\open\\command")));
1381  if (RegKey.Exists()) {
1382  wxString command_line;
1383  RegKey.QueryValue(wxString(_T("")), command_line);
1384 
1385  // Remove "
1386  command_line.Replace(wxString(_T("\"")), wxString(_T("")));
1387 
1388  // Strip arguments
1389  int l = command_line.Find(_T(".exe"));
1390  if (wxNOT_FOUND == l) l = command_line.Find(_T(".EXE"));
1391 
1392  if (wxNOT_FOUND != l) {
1393  wxString cl = command_line.Mid(0, l + 4);
1394  cl += _T(" ");
1395  cc.Prepend(_T("\""));
1396  cc.Append(_T("\""));
1397  cl += cc;
1398  wxExecute(cl); // Async, so Fire and Forget...
1399  }
1400  }
1401  } else
1402  event.Skip();
1403 #else
1404  wxString url = event.GetURL();
1405  url.Replace(_T(" "), _T("%20"));
1406  ::wxLaunchDefaultBrowser(url);
1407 #endif
1408 }
1409 
1410 void RoutePropDlgImpl::HyperlinkContextMenu(wxMouseEvent& event) {
1411  m_pEditedLink = (wxHyperlinkCtrl*)event.GetEventObject();
1412  m_scrolledWindowLinks->PopupMenu(
1413  m_menuLink, m_pEditedLink->GetPosition().x + event.GetPosition().x,
1414  m_pEditedLink->GetPosition().y + event.GetPosition().y);
1415 }
Definition: IDX_entry.h:41
Class LinkPropImpl.
Definition: LinkPropDlg.h:89
void ItemDeleteOnMenuSelection(wxCommandEvent &event)
Class RoutePropDlg.
Definition: RoutePropDlg.h:62
Definition: route.h:75
bool DeleteRoute(Route *pRoute, NavObjectChanges *nav_obj_changes)
Definition: routeman.cpp:751
Definition: tcmgr.h:86
General purpose GUI support.