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