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