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
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()) {
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()) {
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);
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 (unsigned int i = 0; i < kids.GetCount(); i++) {
525 wxWindowListNode* node = kids.Item(i);
526 wxWindow* win = node->GetData();
527 auto link_win = dynamic_cast<wxHyperlinkCtrl*>(win);
528 if (link_win) {
529 link_win->Disconnect(
530 wxEVT_COMMAND_HYPERLINK,
531 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick));
532 link_win->Disconnect(
533 wxEVT_RIGHT_DOWN,
534 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu));
535 win->Destroy();
536 }
537 }
538 int NbrOfLinks = m_pRoute->m_HyperlinkList->size();
539 HyperlinkList* list = m_pRoute->m_HyperlinkList;
540 for (Hyperlink* link : *m_pRoute->m_HyperlinkList) {
541 wxString Link = link->Link;
542 wxString Descr = link->DescrText;
543
544 wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
545 m_scrolledWindowLinks, wxID_ANY, Descr, Link, wxDefaultPosition,
546 wxDefaultSize, wxHL_DEFAULT_STYLE);
547 ctrl->Connect(
548 wxEVT_COMMAND_HYPERLINK,
549 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick), NULL,
550 this);
551 if (!m_pRoute->m_bIsInLayer) {
552 ctrl->Connect(
553 wxEVT_RIGHT_DOWN,
554 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu), NULL,
555 this);
556 }
557 bSizerLinks->Add(ctrl, 0, wxALL, 5);
558 }
559 m_scrolledWindowLinks->InvalidateBestSize();
560 m_scrolledWindowLinks->Layout();
561 bSizerLinks->Layout();
562 }
563
564 m_choiceTimezone->SetSelection(m_tz_selection);
565
566 // Reorganize dialog for route or track display
567 m_tcName->SetValue(m_pRoute->m_RouteNameString);
568 m_tcFrom->SetValue(m_pRoute->m_RouteStartString);
569 m_tcTo->SetValue(m_pRoute->m_RouteEndString);
570 m_tcDescription->SetValue(m_pRoute->m_RouteDescription);
571
572 m_tcName->SetFocus();
573 if (m_pRoute->m_PlannedDeparture.IsValid() &&
574 m_pRoute->m_PlannedDeparture.GetValue() > 0) {
575 wxDateTime t =
576 toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
577 (*m_pRoute->pRoutePointList->begin())->m_lon);
578 m_dpDepartureDate->SetValue(t.GetDateOnly());
579 m_tpDepartureTime->SetValue(t);
580 } else {
581 wxDateTime t =
582 toUsrDateTime(wxDateTime::Now().ToUTC(), m_tz_selection,
583 (*m_pRoute->pRoutePointList->begin())->m_lon);
584 m_dpDepartureDate->SetValue(t.GetDateOnly());
585 m_tpDepartureTime->SetValue(t);
586 }
587 }
588
589 m_btnSplit->Enable(false);
590 if (!m_pRoute) return;
591
592 if (m_pRoute->m_Colour == "") {
593 m_choiceColor->Select(0);
594 } else {
595 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
596 i++) {
597 if (m_pRoute->m_Colour == ::GpxxColorNames[i]) {
598 m_choiceColor->Select(i + 1);
599 break;
600 }
601 }
602 }
603
604 for (unsigned int i = 0; i < sizeof(::StyleValues) / sizeof(int); i++) {
605 if (m_pRoute->m_style == ::StyleValues[i]) {
606 m_choiceStyle->Select(i);
607 break;
608 }
609 }
610
611 for (unsigned int i = 0; i < sizeof(::WidthValues) / sizeof(int); i++) {
612 if (m_pRoute->m_width == ::WidthValues[i]) {
613 m_choiceWidth->Select(i);
614 break;
615 }
616 }
617
618 UpdatePoints();
619
620 m_btnExtend->Enable(IsThisRouteExtendable());
621}
622
623void RoutePropDlgImpl::DepartureDateOnDateChanged(wxDateEvent& event) {
624 if (!m_pRoute) return;
625 m_pRoute->SetDepartureDate(GetDepartureTS());
626 UpdatePoints();
627 event.Skip();
628}
629
630void RoutePropDlgImpl::DepartureTimeOnTimeChanged(wxDateEvent& event) {
631 if (!m_pRoute) return;
632 m_pRoute->SetDepartureDate(GetDepartureTS());
633 UpdatePoints();
634 event.Skip();
635}
636
637void RoutePropDlgImpl::TimezoneOnChoice(wxCommandEvent& event) {
638 if (!m_pRoute) return;
639 m_tz_selection = m_choiceTimezone->GetSelection();
640 wxDateTime t = toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
641 (*m_pRoute->pRoutePointList->begin())->m_lon);
642 m_dpDepartureDate->SetValue(t.GetDateOnly());
643 m_tpDepartureTime->SetValue(t);
644 UpdatePoints();
645 event.Skip();
646}
647
648void RoutePropDlgImpl::PlanSpeedOnTextEnter(wxCommandEvent& event) {
649 if (!m_pRoute) return;
650 double spd;
651 if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
652 if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
653 m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
654 UpdatePoints();
655 }
656 } else {
657 m_tcPlanSpeed->SetValue(
658 wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
659 }
660}
661
662void RoutePropDlgImpl::PlanSpeedOnKillFocus(wxFocusEvent& event) {
663 if (!m_pRoute) return;
664 double spd;
665 if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
666 if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
667 m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
668 UpdatePoints();
669 }
670 } else {
671 m_tcPlanSpeed->SetValue(
672 wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
673 }
674 event.Skip();
675}
676
677static int ev_col;
678void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemEditingDone(
679 wxDataViewEvent& event) {
680 // There is a bug in wxWidgets, the EDITING_DONE event does not contain the
681 // new value, so we must save the data and do the work later in the value
682 // changed event.
683 ev_col = event.GetColumn();
684}
685
686void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemValueChanged(
687 wxDataViewEvent& event) {
688#if wxCHECK_VERSION(3, 1, 2)
689 // wx 3.0.x crashes in the below code
690 if (!m_pRoute) return;
691 wxDataViewModel* const model = event.GetModel();
692 wxVariant value;
693 model->GetValue(value, event.GetItem(), ev_col);
694 RoutePoint* p = m_pRoute->GetPoint(
695 static_cast<int>(reinterpret_cast<long long>(event.GetItem().GetID())));
696 if (ev_col == COLUMN_PLANNED_SPEED) {
697 double spd;
698 if (!value.GetString().ToDouble(&spd)) {
699 spd = 0.0;
700 }
701 p->SetPlannedSpeed(fromUsrSpeed(spd));
702 } else if (ev_col == COLUMN_ETD) {
703 wxString::const_iterator end;
704 wxDateTime etd;
705
706 wxString ts = value.GetString();
707 if (ts.StartsWith("!")) {
708 ts.Replace("!", "", true);
709 }
710 ts.Trim(true);
711 ts.Trim(false);
712
713 if (!ts.IsEmpty()) {
714 if (!etd.ParseDateTime(ts, &end)) {
715 p->SetETD(wxInvalidDateTime);
716 } else {
717 p->SetETD(
718 fromUsrDateTime(etd, m_tz_selection, p->m_lon).FormatISOCombined());
719 }
720 } else {
721 p->SetETD(wxInvalidDateTime);
722 }
723 }
724 UpdatePoints();
725#endif
726}
727
728void RoutePropDlgImpl::WaypointsOnDataViewListCtrlSelectionChanged(
729 wxDataViewEvent& event) {
730 long selected_row = m_dvlcWaypoints->GetSelectedRow();
731 if (selected_row > 0 && selected_row < m_dvlcWaypoints->GetItemCount() - 1) {
732 m_btnSplit->Enable(true);
733 } else {
734 m_btnSplit->Enable(false);
735 }
736 if (IsThisRouteExtendable()) {
737 m_btnExtend->Enable(true);
738 } else {
739 m_btnExtend->Enable(false);
740 }
741 if (selected_row >= 0 && selected_row < m_dvlcWaypoints->GetItemCount()) {
742 RoutePoint* prp = m_pRoute->GetPoint(selected_row + 1);
743 if (prp) {
744 top_frame::Get()->JumpToPosition(prp->m_lat, prp->m_lon);
745#ifdef __WXMSW__
746 if (m_dvlcWaypoints) m_dvlcWaypoints->SetFocus();
747#endif
748 }
749 }
750}
751
753 wxDateTime dt = m_dpDepartureDate->GetValue();
754 dt.SetHour(m_tpDepartureTime->GetValue().GetHour());
755 dt.SetMinute(m_tpDepartureTime->GetValue().GetMinute());
756 dt.SetSecond(m_tpDepartureTime->GetValue().GetSecond());
757 return fromUsrDateTime(dt, m_tz_selection,
758 (*m_pRoute->pRoutePointList->begin())->m_lon);
759 ;
760}
761
762void RoutePropDlgImpl::OnRoutepropCopyTxtClick(wxCommandEvent& event) {
763 wxString tab("\t", wxConvUTF8);
764 wxString eol("\n", wxConvUTF8);
765 wxString csvString;
766
767 csvString << this->GetTitle() << eol << _("Name") << tab
768 << m_pRoute->m_RouteNameString << eol << _("Depart From") << tab
769 << m_pRoute->m_RouteStartString << eol << _("Destination") << tab
770 << m_pRoute->m_RouteEndString << eol << _("Total distance") << tab
771 << m_tcDistance->GetValue() << eol << _("Speed (Kts)") << tab
772 << m_tcPlanSpeed->GetValue() << eol
773 << _("Departure Time") + " (" + ETA_FORMAT_STR + ")" << tab
774 << GetDepartureTS().Format(ETA_FORMAT_STR) << eol
775 << _("Time enroute") << tab << m_tcEnroute->GetValue() << eol
776 << eol;
777
778 int noCols;
779 int noRows;
780 noCols = m_dvlcWaypoints->GetColumnCount();
781 noRows = m_dvlcWaypoints->GetItemCount();
782 wxListItem item;
783 item.SetMask(wxLIST_MASK_TEXT);
784
785 for (int i = 0; i < noCols; i++) {
786 wxDataViewColumn* col = m_dvlcWaypoints->GetColumn(i);
787 csvString << col->GetTitle() << tab;
788 }
789 csvString << eol;
790
791 wxVariant value;
792 for (int j = 0; j < noRows; j++) {
793 for (int i = 0; i < noCols; i++) {
794 m_dvlcWaypoints->GetValue(value, j, i);
795 csvString << value.MakeString() << tab;
796 }
797 csvString << eol;
798 }
799
800 if (wxTheClipboard->Open()) {
801 wxTextDataObject* data = new wxTextDataObject;
802 data->SetText(csvString);
803 wxTheClipboard->SetData(data);
804 wxTheClipboard->Close();
805 }
806}
807
808void RoutePropDlgImpl::OnRoutePropMenuSelected(wxCommandEvent& event) {
809 bool moveup = false;
810 switch (event.GetId()) {
811 case ID_RCLK_MENU_COPY_TEXT: {
812 OnRoutepropCopyTxtClick(event);
813 break;
814 }
815 case ID_RCLK_MENU_MOVEUP_WP: {
816 moveup = true;
817 }
818 case ID_RCLK_MENU_MOVEDOWN_WP: {
819 wxString mess =
820 moveup ? _("Are you sure you want to move Up this waypoint?")
821 : _("Are you sure you want to move Down this waypoint?");
822 int dlg_return =
823 OCPNMessageBox(this, mess, _("OpenCPN Move Waypoint"),
824 (long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
825
826 if (dlg_return == wxID_YES) {
827 wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
828 RoutePoint* pRP = m_pRoute->GetPoint(
829 static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
830 auto& list = m_pRoute->pRoutePointList;
831 auto pos = std::find(list->begin(), list->end(), pRP);
832
833 pSelect->DeleteAllSelectableRoutePoints(m_pRoute);
834 pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
835
836 m_pRoute->pRoutePointList->erase(pos);
837 pos += moveup ? -1 : 1;
838 m_pRoute->pRoutePointList->insert(pos, pRP);
839
840 pSelect->AddAllSelectableRouteSegments(m_pRoute);
841 pSelect->AddAllSelectableRoutePoints(m_pRoute);
842
843 // pConfig->UpdateRoute(m_pRoute);
844 NavObj_dB::GetInstance().UpdateRoute(m_pRoute);
845
846 m_pRoute->FinalizeForRendering();
847 m_pRoute->UpdateSegmentDistances();
848 ;
849
850 top_frame::Get()->InvalidateAllGL();
851
852 m_dvlcWaypoints->SelectRow(pos - list->begin());
853
854 SetRouteAndUpdate(m_pRoute, true);
855 }
856 break;
857 }
858 case ID_RCLK_MENU_DELETE: {
859 int dlg_return = OCPNMessageBox(
860 this, _("Are you sure you want to remove this waypoint?"),
861 _("OpenCPN Remove Waypoint"),
862 (long)wxYES_NO | wxCANCEL | wxYES_DEFAULT);
863
864 if (dlg_return == wxID_YES) {
865 int sel = m_dvlcWaypoints->GetSelectedRow();
866 m_dvlcWaypoints->SelectRow(sel);
867
868 wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
869 RoutePoint* pRP = m_pRoute->GetPoint(
870 static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
871
872 g_pRouteMan->RemovePointFromRoute(pRP, m_pRoute, 0);
873
874 top_frame::Get()->InvalidateAllGL();
875 UpdatePoints();
876 }
877 break;
878 }
879 case ID_RCLK_MENU_EDIT_WP: {
880 wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
881 RoutePoint* pRP = m_pRoute->GetPoint(
882 static_cast<int>(reinterpret_cast<long long>(selection.GetID())));
883
884 RouteManagerDialog::WptShowPropertiesDialog(pRP, this);
885 break;
886 }
887 }
888}
889
890void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemContextMenu(
891 wxDataViewEvent& event) {
892 wxMenu menu;
893 if (!m_pRoute->m_bIsInLayer) {
894 wxMenuItem* editItem = new wxMenuItem(&menu, ID_RCLK_MENU_EDIT_WP,
895 _("Waypoint Properties") + "...");
896 wxMenuItem* moveUpItem =
897 new wxMenuItem(&menu, ID_RCLK_MENU_MOVEUP_WP, _("Move Up"));
898 wxMenuItem* moveDownItem =
899 new wxMenuItem(&menu, ID_RCLK_MENU_MOVEDOWN_WP, _("Move Down"));
900 wxMenuItem* delItem =
901 new wxMenuItem(&menu, ID_RCLK_MENU_DELETE, _("Remove Selected"));
902#ifdef __ANDROID__
903 wxFont* pf = OCPNGetFont(_("Menu"));
904 editItem->SetFont(*pf);
905 moveUpItem->SetFont(*pf);
906 moveDownItem->SetFont(*pf);
907 delItem->SetFont(*pf);
908#endif
909#if defined(__WXMSW__)
910 wxFont* pf = GetOCPNScaledFont(_("Menu"));
911 editItem->SetFont(*pf);
912 moveUpItem->SetFont(*pf);
913 moveDownItem->SetFont(*pf);
914 delItem->SetFont(*pf);
915#endif
916
917 menu.Append(editItem);
918 if (g_btouch) menu.AppendSeparator();
919 menu.Append(moveUpItem);
920 if (g_btouch) menu.AppendSeparator();
921 menu.Append(moveDownItem);
922 if (g_btouch) menu.AppendSeparator();
923 menu.Append(delItem);
924
925 editItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0);
926 moveUpItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 1 &&
927 m_dvlcWaypoints->GetItemCount() > 2);
928 moveDownItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0 &&
929 m_dvlcWaypoints->GetSelectedRow() <
930 m_dvlcWaypoints->GetItemCount() - 1 &&
931 m_dvlcWaypoints->GetItemCount() > 2);
932 delItem->Enable(m_dvlcWaypoints->GetSelectedRow() >= 0 &&
933 m_dvlcWaypoints->GetItemCount() > 2);
934 }
935#ifndef __WXQT__
936 wxMenuItem* copyItem =
937 new wxMenuItem(&menu, ID_RCLK_MENU_COPY_TEXT, _("&Copy all as text"));
938
939#if defined(__WXMSW__)
940 wxFont* qFont = GetOCPNScaledFont(_("Menu"));
941 copyItem->SetFont(*qFont);
942#endif
943
944 if (g_btouch) menu.AppendSeparator();
945 menu.Append(copyItem);
946#endif
947
948 PopupMenu(&menu);
949}
950
951void RoutePropDlgImpl::ResetChanges() {
952 if (!m_pRoute) return;
953 m_pRoute->m_PlannedSpeed = m_OrigRoute.m_PlannedSpeed;
954 m_pRoute->m_PlannedDeparture = m_OrigRoute.m_PlannedDeparture;
955 m_pRoute = nullptr;
956}
957
958void RoutePropDlgImpl::SaveChanges() {
959 if (m_pRoute && !m_pRoute->m_bIsInLayer) {
960 // Get User input Text Fields
961 m_pRoute->m_RouteNameString = m_tcName->GetValue();
962 m_pRoute->m_RouteStartString = m_tcFrom->GetValue();
963 m_pRoute->m_RouteEndString = m_tcTo->GetValue();
964 m_pRoute->m_RouteDescription = m_tcDescription->GetValue();
965 if (m_choiceColor->GetSelection() == 0) {
966 m_pRoute->m_Colour = "";
967 } else {
968 m_pRoute->m_Colour = ::GpxxColorNames[m_choiceColor->GetSelection() - 1];
969 }
970 m_pRoute->m_style =
971 (wxPenStyle)::StyleValues[m_choiceStyle->GetSelection()];
972 m_pRoute->m_width = ::WidthValues[m_choiceWidth->GetSelection()];
973 switch (m_tz_selection) {
974 case LTINPUT:
975 m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_PC;
976 break;
977 case LMTINPUT:
978 m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_LOCAL;
979 break;
981 m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_GLOBAL;
982 break;
983 case UTCINPUT:
984 default:
985 m_pRoute->m_TimeDisplayFormat = RTE_TIME_DISP_UTC;
986 }
987
988 // pConfig->UpdateRoute(m_pRoute);
989 NavObj_dB::GetInstance().UpdateRoute(m_pRoute);
990 pConfig->UpdateSettings();
991 m_pRoute = nullptr;
992 }
993}
994
995void RoutePropDlgImpl::SetColorScheme(ColorScheme cs) { DimeControl(this); }
996
997void RoutePropDlgImpl::SaveGeometry() {
998 GetSize(&g_route_prop_sx, &g_route_prop_sy);
999 GetPosition(&g_route_prop_x, &g_route_prop_y);
1000}
1001
1002void RoutePropDlgImpl::BtnsOnOKButtonClick(wxCommandEvent& event) {
1003 SaveChanges();
1004 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
1005 pRouteManagerDialog->UpdateRouteListCtrl();
1006 }
1007 Hide();
1008 SaveGeometry();
1009}
1010
1011void RoutePropDlgImpl::SplitOnButtonClick(wxCommandEvent& event) {
1012 m_btnSplit->Enable(false);
1013
1014 if (m_pRoute->m_bIsInLayer) return;
1015
1016 int nSelected = m_dvlcWaypoints->GetSelectedRow() + 1;
1017 if ((nSelected > 1) && (nSelected < m_pRoute->GetnPoints())) {
1018 m_pHead = new Route();
1019 m_pTail = new Route();
1020 m_pHead->CloneRoute(m_pRoute, 1, nSelected, _("_A"));
1021 m_pTail->CloneRoute(m_pRoute, nSelected, m_pRoute->GetnPoints(), _("_B"),
1022 true);
1023 pRouteList->push_back(m_pHead);
1024 // pConfig->AddNewRoute(m_pHead);
1025 NavObj_dB::GetInstance().InsertRoute(m_pHead);
1026
1027 pRouteList->push_back(m_pTail);
1028 // pConfig->AddNewRoute(m_pTail);
1029 NavObj_dB::GetInstance().InsertRoute(m_pTail);
1030
1031 // pConfig->DeleteConfigRoute(m_pRoute);
1032 NavObj_dB::GetInstance().DeleteRoute(m_pRoute);
1033
1034 pSelect->DeleteAllSelectableRoutePoints(m_pRoute);
1035 pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
1036 g_pRouteMan->DeleteRoute(m_pRoute);
1037 pSelect->AddAllSelectableRouteSegments(m_pTail);
1038 pSelect->AddAllSelectableRoutePoints(m_pTail);
1039 pSelect->AddAllSelectableRouteSegments(m_pHead);
1040 pSelect->AddAllSelectableRoutePoints(m_pHead);
1041
1042 SetRouteAndUpdate(m_pTail);
1043 UpdatePoints();
1044
1045 if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
1046 pRouteManagerDialog->UpdateRouteListCtrl();
1047 }
1048}
1049
1050void RoutePropDlgImpl::PrintOnButtonClick(wxCommandEvent& event) {
1051 static std::set<int> s_options; // keep selected options
1052 RoutePrintDialog dlg(this, s_options);
1053 int result = dlg.ShowModal();
1054
1055 if (result == wxID_OK) {
1056 dlg.GetSelected(s_options);
1057 RoutePrintout printout(m_pRoute, s_options, m_tz_selection);
1058 auto& printer = PrintDialog::GetInstance();
1059 printer.Initialize(wxPORTRAIT);
1060 printer.EnablePageNumbers(true);
1061 printer.Print(this, &printout);
1062 }
1063
1064 event.Skip();
1065}
1066
1067void RoutePropDlgImpl::ExtendOnButtonClick(wxCommandEvent& event) {
1068 m_btnExtend->Enable(false);
1069
1070 if (IsThisRouteExtendable()) {
1071 int extend_idx = m_pExtendRoute->GetIndexOf(m_pExtendPoint);
1072 if (extend_idx < 0) {
1073 m_btnExtend->Enable(true);
1074 return;
1075 }
1076 int fm = extend_idx + 1;
1077 int to = m_pExtendRoute->GetnPoints();
1078 if (fm <= to) {
1079 pSelect->DeleteAllSelectableRouteSegments(m_pRoute);
1080 m_pRoute->CloneRoute(m_pExtendRoute, fm, to, _("_plus"));
1081 pSelect->AddAllSelectableRouteSegments(m_pRoute);
1082 SetRouteAndUpdate(m_pRoute);
1083 UpdatePoints();
1084 }
1085 }
1086 m_btnExtend->Enable(true);
1087}
1088
1089bool RoutePropDlgImpl::IsThisRouteExtendable() {
1090 m_pExtendRoute = NULL;
1091 m_pExtendPoint = NULL;
1092 if (!m_pRoute || m_pRoute->m_bRtIsActive || m_pRoute->m_bIsInLayer)
1093 return false;
1094
1095 RoutePoint* pLastPoint = m_pRoute->GetLastPoint();
1096 wxArrayPtrVoid* pEditRouteArray;
1097
1098 pEditRouteArray = g_pRouteMan->GetRouteArrayContaining(pLastPoint);
1099 // remove invisible & own routes from choices
1100 int i;
1101 for (i = pEditRouteArray->GetCount(); i > 0; i--) {
1102 Route* p = (Route*)pEditRouteArray->Item(i - 1);
1103 if (!p->IsVisible() || (p->m_GUID == m_pRoute->m_GUID))
1104 pEditRouteArray->RemoveAt(i - 1);
1105 }
1106 if (pEditRouteArray->GetCount() == 1) {
1107 m_pExtendPoint = pLastPoint;
1108 } else {
1109 if (pEditRouteArray->GetCount() == 0) {
1110 int nearby_radius_meters =
1111 (int)(8. / top_frame::Get()->GetCanvasTrueScale());
1112 double rlat = pLastPoint->m_lat;
1113 double rlon = pLastPoint->m_lon;
1114
1115 m_pExtendPoint = pWayPointMan->GetOtherNearbyWaypoint(
1116 rlat, rlon, nearby_radius_meters, pLastPoint->m_GUID);
1117 if (m_pExtendPoint) {
1118 wxArrayPtrVoid* pCloseWPRouteArray =
1119 g_pRouteMan->GetRouteArrayContaining(m_pExtendPoint);
1120 if (pCloseWPRouteArray) {
1121 pEditRouteArray = pCloseWPRouteArray;
1122
1123 // remove invisible & own routes from choices
1124 for (i = pEditRouteArray->GetCount(); i > 0; i--) {
1125 Route* p = (Route*)pEditRouteArray->Item(i - 1);
1126 if (!p->IsVisible() || (p->m_GUID == m_pRoute->m_GUID))
1127 pEditRouteArray->RemoveAt(i - 1);
1128 }
1129 }
1130 }
1131 }
1132 }
1133 if (pEditRouteArray->GetCount() == 1) {
1134 Route* p = (Route*)pEditRouteArray->Item(0);
1135 int extend_idx = p->GetIndexOf(m_pExtendPoint);
1136 if (extend_idx < 0) {
1137 delete pEditRouteArray;
1138 return false;
1139 }
1140 int fm = extend_idx + 1;
1141 int to = p->GetnPoints();
1142 if (fm <= to) {
1143 m_pExtendRoute = p;
1144 delete pEditRouteArray;
1145 return true;
1146 }
1147 }
1148 delete pEditRouteArray;
1149
1150 return false;
1151}
1152
1153wxString RoutePropDlgImpl::MakeTideInfo(wxString stationName, double lat,
1154 double lon, wxDateTime utcTime) {
1155 if (stationName.Find("lind") != wxNOT_FOUND) int yyp = 4;
1156
1157 if (stationName.IsEmpty()) {
1158 return "";
1159 }
1160 if (!utcTime.IsValid()) {
1161 return _("Invalid date/time!");
1162 }
1163 int stationID = ptcmgr->GetStationIDXbyName(stationName, lat, lon);
1164 if (stationID == 0) {
1165 return _("Unknown station!");
1166 }
1167 time_t dtmtt = utcTime.FromUTC().GetTicks();
1168 int ev = ptcmgr->GetNextBigEvent(&dtmtt, stationID);
1169
1170 wxDateTime dtm;
1171 dtm.Set(dtmtt).MakeUTC();
1172
1173 wxString tide_form = "";
1174
1175 if (ev == 1) {
1176 tide_form.Append("LW: "); // High Water
1177 } else if (ev == 2) {
1178 tide_form.Append("HW: "); // Low Water
1179 } else if (ev == 0) {
1180 tide_form.Append(_("Unavailable: "));
1181 }
1182
1183 int offset =
1184 ptcmgr->GetStationTimeOffset((IDX_entry*)ptcmgr->GetIDX_entry(stationID));
1187 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
1188 .SetLongitude(lon);
1189 wxString tideDateTime = ocpn::toUsrDateTimeFormat(dtm.FromUTC(), opts);
1190 tide_form.Append(tideDateTime);
1191 dtm.Add(wxTimeSpan(0, offset, 0));
1192 // Write next tide event using station timezone, formatted with explicit HH:MM
1193 // offset from UTC.
1194 tide_form.Append(wxString::Format(" (" + _("Local") + ": %s%+03d:%02d) @ %s",
1195 dtm.Format("%a %x %H:%M:%S"), (offset / 60),
1196 abs(offset) % 60, stationName.c_str()));
1197 return tide_form;
1198}
1199
1200void RoutePropDlgImpl::ItemEditOnMenuSelection(wxCommandEvent& event) {
1201 wxString findurl = m_pEditedLink->GetURL();
1202 wxString findlabel = m_pEditedLink->GetLabel();
1203
1204 LinkPropImpl* LinkPropDlg = new LinkPropImpl(this);
1205 LinkPropDlg->m_textCtrlLinkDescription->SetValue(findlabel);
1206 LinkPropDlg->m_textCtrlLinkUrl->SetValue(findurl);
1207 DimeControl(LinkPropDlg);
1208 LinkPropDlg->ShowWindowModalThenDo([this, LinkPropDlg, findurl,
1209 findlabel](int retcode) {
1210 if (retcode == wxID_OK) {
1211 for (Hyperlink* link : *m_pRoute->m_HyperlinkList) {
1212 wxString Link = link->Link;
1213 wxString Descr = link->DescrText;
1214 if (Link == findurl &&
1215 (Descr == findlabel || (Link == findlabel && Descr == ""))) {
1216 link->Link = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1217 link->DescrText = LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1218 wxHyperlinkCtrl* h =
1219 (wxHyperlinkCtrl*)m_scrolledWindowLinks->FindWindowByLabel(
1220 findlabel);
1221 if (h) {
1222 h->SetLabel(LinkPropDlg->m_textCtrlLinkDescription->GetValue());
1223 h->SetURL(LinkPropDlg->m_textCtrlLinkUrl->GetValue());
1224 }
1225 }
1226 }
1227
1228 m_scrolledWindowLinks->InvalidateBestSize();
1229 m_scrolledWindowLinks->Layout();
1230 bSizerLinks->Layout();
1231 }
1232 });
1233 event.Skip();
1234}
1235
1236void RoutePropDlgImpl::ItemAddOnMenuSelection(wxCommandEvent& event) {
1237 AddLinkOnButtonClick(event);
1238}
1239
1241 wxString findurl = m_pEditedLink->GetURL();
1242 wxString findlabel = m_pEditedLink->GetLabel();
1243
1244 wxWindowList kids = m_scrolledWindowLinks->GetChildren();
1245 for (unsigned int i = 0; i < kids.GetCount(); i++) {
1246 wxWindowListNode* node = kids.Item(i);
1247 wxWindow* win = node->GetData();
1248
1249 auto link_win = dynamic_cast<wxHyperlinkCtrl*>(win);
1250 if (link_win) {
1251 link_win->Disconnect(
1252 wxEVT_COMMAND_HYPERLINK,
1253 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick));
1254 link_win->Disconnect(
1255 wxEVT_RIGHT_DOWN,
1256 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu));
1257 win->Destroy();
1258 }
1259 }
1260
1262 int NbrOfLinks = m_pRoute->m_HyperlinkList->size();
1263 HyperlinkList* hyperlinklist = m_pRoute->m_HyperlinkList;
1264 // int len = 0;
1265 auto nodeToDelete = hyperlinklist->end();
1266 if (NbrOfLinks > 0) {
1267 auto it = hyperlinklist->begin();
1268 while (it != hyperlinklist->end()) {
1269 Hyperlink* link = *it;
1270 wxString Link = link->Link;
1271 wxString Descr = link->DescrText;
1272 if (Link == findurl &&
1273 (Descr == findlabel || (Link == findlabel && Descr == "")))
1274 nodeToDelete = it;
1275 else {
1276 wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
1277 m_scrolledWindowLinks, wxID_ANY, Descr, Link, wxDefaultPosition,
1278 wxDefaultSize, wxHL_DEFAULT_STYLE);
1279 ctrl->Connect(
1280 wxEVT_COMMAND_HYPERLINK,
1281 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick), NULL,
1282 this);
1283 ctrl->Connect(
1284 wxEVT_RIGHT_DOWN,
1285 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu), NULL,
1286 this);
1287
1288 bSizerLinks->Add(ctrl, 0, wxALL, 5);
1289 }
1290 it++;
1291 }
1292 }
1293 if (nodeToDelete != hyperlinklist->end()) {
1294 hyperlinklist->erase(nodeToDelete);
1295 }
1296 m_scrolledWindowLinks->InvalidateBestSize();
1297 m_scrolledWindowLinks->Layout();
1298 bSizerLinks->Layout();
1299 event.Skip();
1300}
1301
1302void RoutePropDlgImpl::AddLinkOnButtonClick(wxCommandEvent& event) {
1303 LinkPropImpl* LinkPropDlg = new LinkPropImpl(this);
1304 LinkPropDlg->m_textCtrlLinkDescription->SetValue("");
1305 LinkPropDlg->m_textCtrlLinkUrl->SetValue("");
1306 DimeControl(LinkPropDlg);
1307 LinkPropDlg->ShowWindowModalThenDo([this, LinkPropDlg](int retcode) {
1308 if (retcode == wxID_OK) {
1309 wxString desc = LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1310 if (desc == "") desc = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1311 wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
1312 m_scrolledWindowLinks, wxID_ANY, desc,
1313 LinkPropDlg->m_textCtrlLinkUrl->GetValue(), wxDefaultPosition,
1314 wxDefaultSize, wxHL_DEFAULT_STYLE);
1315 ctrl->Connect(wxEVT_COMMAND_HYPERLINK,
1316 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick),
1317 NULL, this);
1318 ctrl->Connect(wxEVT_RIGHT_DOWN,
1319 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu),
1320 NULL, this);
1321
1322 bSizerLinks->Add(ctrl, 0, wxALL, 5);
1323 m_scrolledWindowLinks->InvalidateBestSize();
1324 m_scrolledWindowLinks->Layout();
1325 bSizerLinks->Layout();
1326
1327 Hyperlink* h = new Hyperlink();
1328 h->DescrText = LinkPropDlg->m_textCtrlLinkDescription->GetValue();
1329 h->Link = LinkPropDlg->m_textCtrlLinkUrl->GetValue();
1330 h->LType = "";
1331 m_pRoute->m_HyperlinkList->push_back(h);
1332 }
1333 });
1334}
1335
1336void RoutePropDlgImpl::BtnEditOnToggleButton(wxCommandEvent& event) {
1337 if (m_toggleBtnEdit->GetValue()) {
1338 m_stEditEnabled->SetLabel(_("Links are opened for editing."));
1339 } else {
1340 m_stEditEnabled->SetLabel(_("Links are opened in the default browser."));
1341 }
1342 event.Skip();
1343}
1344
1345void RoutePropDlgImpl::OnHyperlinkClick(wxHyperlinkEvent& event) {
1346 if (m_toggleBtnEdit->GetValue()) {
1347 m_pEditedLink = (wxHyperlinkCtrl*)event.GetEventObject();
1348 ItemEditOnMenuSelection(event);
1349 event.Skip(false);
1350 return;
1351 }
1352 // Windows has trouble handling local file URLs with embedded anchor
1353 // points, e.g file://testfile.html#point1 The trouble is with the
1354 // wxLaunchDefaultBrowser with verb "open" Workaround is to probe the
1355 // registry to get the default browser, and open directly
1356 //
1357 // But, we will do this only if the URL contains the anchor point character
1358 // '#' What a hack......
1359
1360#ifdef __WXMSW__
1361 wxString cc = event.GetURL();
1362 if (cc.Find("#") != wxNOT_FOUND) {
1363 wxRegKey RegKey(wxString("HKEY_CLASSES_ROOT\\HTTP\\shell\\open\\command"));
1364 if (RegKey.Exists()) {
1365 wxString command_line;
1366 RegKey.QueryValue(wxString(""), command_line);
1367
1368 // Remove "
1369 command_line.Replace(wxString("\""), wxString(""));
1370
1371 // Strip arguments
1372 int l = command_line.Find(".exe");
1373 if (wxNOT_FOUND == l) l = command_line.Find(".EXE");
1374
1375 if (wxNOT_FOUND != l) {
1376 wxString cl = command_line.Mid(0, l + 4);
1377 cl += " ";
1378 cc.Prepend("\"");
1379 cc.Append("\"");
1380 cl += cc;
1381 wxExecute(cl); // Async, so Fire and Forget...
1382 }
1383 }
1384 } else
1385 event.Skip();
1386#else
1387 wxString url = event.GetURL();
1388 url.Replace(" ", "%20");
1389 ::wxLaunchDefaultBrowser(url);
1390#endif
1391}
1392
1393void RoutePropDlgImpl::HyperlinkContextMenu(wxMouseEvent& event) {
1394 m_pEditedLink = (wxHyperlinkCtrl*)event.GetEventObject();
1395 m_scrolledWindowLinks->PopupMenu(
1396 m_menuLink, m_pEditedLink->GetPosition().x + event.GetPosition().x,
1397 m_pEditedLink->GetPosition().y + event.GetPosition().y);
1398}
Generic Chart canvas base.
Represents an index entry for tidal and current data.
Definition idx_entry.h:48
Class LinkPropImpl.
static PrintDialog & GetInstance()
Get instance to handle the print process,.
Represents a waypoint or mark within the navigation system.
Definition route_point.h:71
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.
Input dialog with route print selection.
Printout route information and a table with selected route point information.
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_RouteDescription
Additional descriptive information about the route.
Definition route.h:262
RoutePointList * pRoutePointList
Ordered list of waypoints (RoutePoints) that make up this route.
Definition route.h:336
double m_route_length
Total length of the route in nautical miles, calculated using rhumb line (Mercator) distances.
Definition route.h:237
bool m_bRtIsActive
Flag indicating whether this route is currently active for navigation.
Definition route.h:208
wxString m_Colour
Color name for rendering the route on the chart.
Definition route.h:346
wxString m_RouteEndString
Name or description of the route's ending point.
Definition route.h:257
wxPenStyle m_style
Style of the route line when rendered on the chart.
Definition route.h:293
wxString m_TimeDisplayFormat
Format for displaying times in the UI.
Definition route.h:331
int m_width
Width of the route line in pixels when rendered on the chart.
Definition route.h:288
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
double m_route_time
Total estimated time to complete the route in seconds.
Definition route.h:242
void SetDepartureDate(const wxDateTime &dt)
Set the departure time of the route.
Definition route.h:184
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:765
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:2937
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:2977
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./*#end#*‍/.
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.
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.
TCMgr * ptcmgr
Global instance.
Definition tcmgr.cpp:42
Tide and Current Manager @TODO Add original author copyright.
Abstract gFrame/MyFrame interface.