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