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