OpenCPN Partial API docs
Loading...
Searching...
No Matches
route_prop_dlg_impl.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2013 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
24#include <wx/clipbrd.h>
25
26#include "gl_headers.h" // Must be included before anything using GL stuff
27
28#include "model/georef.h"
29#include "model/own_ship.h"
30#include "model/routeman.h"
31#include "model/select.h"
32
33#include "model/navobj_db.h"
34#include "model/navutil_base.h"
35
36#include "chcanv.h"
37#include "gui_lib.h"
38#include "mark_info.h"
39#include "navutil.h"
40#include "ocpn_plugin.h"
41#include "print_dialog.h"
42#include "routemanagerdialog.h"
43#include "route_printout.h"
44#include "route_prop_dlg_impl.h"
45#include "tcmgr.h"
46#include "top_frame.h"
47
48#define UTCINPUT 0
49#define LTINPUT \
50 1
51#define LMTINPUT 2
53#define GLOBAL_SETTINGS_INPUT 3
54
55#define ID_RCLK_MENU_COPY_TEXT 7013
56#define ID_RCLK_MENU_EDIT_WP 7014
57#define ID_RCLK_MENU_DELETE 7015
58#define ID_RCLK_MENU_MOVEUP_WP 7026
59#define ID_RCLK_MENU_MOVEDOWN_WP 7027
60
61#define COLUMN_PLANNED_SPEED 9
62#define COLUMN_ETD 13
63
64#ifndef PI
65#define PI (4. * atan(1.0))
66#endif
67#define TPI (2. * PI)
68#define DEGS (180. / PI)
69#define RADS (PI / 180.)
70
71#define MOTWILIGHT \
72 1 // in some languages there may be a distinction between morning/evening
73#define SUNRISE 2
74#define DAY 3
75#define SUNSET 4
76#define EVTWILIGHT 5
77#define NIGHT 6
78
80
81extern wxString GetLayerName(int id); // in ocpn_frame FIXME leamas)
82
83// Sunrise/twilight calculation for route properties.
84// limitations: latitude below 60, year between 2000 and 2100
85// riset is +1 for rise -1 for set
86// adapted by author's permission from QBASIC source as published at
87// http://www.stargazing.net/kepler
88
89static wxString GetDaylightString(int index) {
90 switch (index) {
91 case 0:
92 return " - ";
93 case 1:
94 return _("MoTwilight");
95 case 2:
96 return _("Sunrise");
97 case 3:
98 return _("Daytime");
99 case 4:
100 return _("Sunset");
101 case 5:
102 return _("EvTwilight");
103 case 6:
104 return _("Nighttime");
105
106 default:
107 return "";
108 }
109}
110
111static double sign(double x) {
112 if (x < 0.)
113 return -1.;
114 else
115 return 1.;
116}
117
118static double FNipart(double x) { return (sign(x) * (int)(fabs(x))); }
119
120static double FNday(int y, int m, int d, int h) {
121 long fd = (367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + d);
122 return ((double)fd - 730531.5 + h / 24.);
123}
124
125static double FNrange(double x) {
126 double b = x / TPI;
127 double a = TPI * (b - FNipart(b));
128 if (a < 0.) a = TPI + a;
129 return (a);
130}
131
132static double getDaylightEvent(double glat, double glong, int riset,
133 double altitude, int y, int m, int d) {
134 double day = FNday(y, m, d, 0);
135 double days, correction;
136 double utold = PI;
137 double utnew = 0.;
138 double sinalt =
139 sin(altitude * RADS); // go for the sunrise/sunset altitude first
140 double sinphi = sin(glat * RADS);
141 double cosphi = cos(glat * RADS);
142 double g = glong * RADS;
143 double t, L, G, ec, lambda, E, obl, delta, GHA, cosc;
144 int limit = 12;
145 while ((fabs(utold - utnew) > .001)) {
146 if (limit-- <= 0) return (-1.);
147 days = day + utnew / TPI;
148 t = days / 36525.;
149 // get arguments of Sun's orbit
150 L = FNrange(4.8949504201433 + 628.331969753199 * t);
151 G = FNrange(6.2400408 + 628.3019501 * t);
152 ec = .033423 * sin(G) + .00034907 * sin(2 * G);
153 lambda = L + ec;
154 E = -1. * ec + .0430398 * sin(2 * lambda) - .00092502 * sin(4. * lambda);
155 obl = .409093 - .0002269 * t;
156 delta = asin(sin(obl) * sin(lambda));
157 GHA = utold - PI + E;
158 cosc = (sinalt - sinphi * sin(delta)) / (cosphi * cos(delta));
159 if (cosc > 1.)
160 correction = 0.;
161 else if (cosc < -1.)
162 correction = PI;
163 else
164 correction = acos(cosc);
165 double tmp = utnew;
166 utnew = FNrange(utold - (GHA + g + riset * correction));
167 utold = tmp;
168 }
169 return (utnew * DEGS / 15.); // returns decimal hours UTC
170}
171
172static double getLMT(double ut, double lon) {
173 double t = ut + lon / 15.;
174 if (t >= 0.)
175 if (t <= 24.)
176 return (t);
177 else
178 return (t - 24.);
179 else
180 return (t + 24.);
181}
182
186static wxString getDatetimeTimezoneSelector(int selection) {
187 switch (selection) {
188 case UTCINPUT:
189 return "UTC";
190 case LTINPUT:
191 return "Local Time";
192 case LMTINPUT:
193 return "LMT";
195 default:
196 return "";
197 }
198}
199
200static int getDaylightStatus(double lat, double lon, wxDateTime utcDateTime) {
201 if (fabs(lat) > 60.) return (0);
202 int y = utcDateTime.GetYear();
203 int m = utcDateTime.GetMonth() + 1; // wxBug? months seem to run 0..11 ?
204 int d = utcDateTime.GetDay();
205 int h = utcDateTime.GetHour();
206 int n = utcDateTime.GetMinute();
207 int s = utcDateTime.GetSecond();
208 if (y < 2000 || y > 2100) return (0);
209
210 double ut = (double)h + (double)n / 60. + (double)s / 3600.;
211 double lt = getLMT(ut, lon);
212 double rsalt = -0.833;
213 double twalt = -12.;
214
215 if (lt <= 12.) {
216 double sunrise = getDaylightEvent(lat, lon, +1, rsalt, y, m, d);
217 if (sunrise < 0.)
218 return (0);
219 else
220 sunrise = getLMT(sunrise, lon);
221
222 if (fabs(lt - sunrise) < 0.15) return (SUNRISE);
223 if (lt > sunrise) return (DAY);
224 double twilight = getDaylightEvent(lat, lon, +1, twalt, y, m, d);
225 if (twilight < 0.)
226 return (0);
227 else
228 twilight = getLMT(twilight, lon);
229 if (lt > twilight)
230 return (MOTWILIGHT);
231 else
232 return (NIGHT);
233 } else {
234 double sunset = getDaylightEvent(lat, lon, -1, rsalt, y, m, d);
235 if (sunset < 0.)
236 return (0);
237 else
238 sunset = getLMT(sunset, lon);
239 if (fabs(lt - sunset) < 0.15) return (SUNSET);
240 if (lt < sunset) return (DAY);
241 double twilight = getDaylightEvent(lat, lon, -1, twalt, y, m, d);
242 if (twilight < 0.)
243 return (0);
244 else
245 twilight = getLMT(twilight, lon);
246 if (lt < twilight)
247 return (EVTWILIGHT);
248 else
249 return (NIGHT);
250 }
251}
252
253RoutePropDlgImpl::RoutePropDlgImpl(wxWindow* parent, wxWindowID id,
254 const wxString& title, const wxPoint& pos,
255 const wxSize& size, long style)
256 : RoutePropDlg(parent, id, title, pos, size, style) {
257 m_pRoute = nullptr;
258
259 SetColorScheme(global_color_scheme);
260
261 if (g_route_prop_sx > 0 && g_route_prop_sy > 0 &&
262 g_route_prop_sx < wxGetDisplaySize().x &&
263 g_route_prop_sy < wxGetDisplaySize().y) {
264 SetSize(g_route_prop_sx, g_route_prop_sy);
265 }
266
267 if (g_route_prop_x > 0 && g_route_prop_y > 0 &&
268 g_route_prop_x < wxGetDisplaySize().x &&
269 g_route_prop_y < wxGetDisplaySize().y) {
270 SetPosition(wxPoint(10, 10));
271 }
272 RecalculateSize();
273
274 Connect(wxEVT_COMMAND_MENU_SELECTED,
275 wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
276 NULL, this);
277
278#ifdef __WXOSX__
279 Connect(wxEVT_ACTIVATE, wxActivateEventHandler(RoutePropDlgImpl::OnActivate),
280 NULL, this);
281#endif
282}
283
284RoutePropDlgImpl::~RoutePropDlgImpl() {
285 Disconnect(wxEVT_COMMAND_MENU_SELECTED,
286 wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
287 NULL, this);
288 instanceFlag = false;
289}
290
291bool RoutePropDlgImpl::instanceFlag = false;
292bool RoutePropDlgImpl::getInstanceFlag() {
293 return RoutePropDlgImpl::instanceFlag;
294}
295
296RoutePropDlgImpl* RoutePropDlgImpl::single = NULL;
297RoutePropDlgImpl* RoutePropDlgImpl::getInstance(wxWindow* parent) {
298 if (!instanceFlag) {
299 single = new RoutePropDlgImpl(parent);
300 instanceFlag = true;
301 }
302 return single;
303}
304
305void RoutePropDlgImpl::OnActivate(wxActivateEvent& event) {
306 auto pWin = dynamic_cast<wxFrame*>(event.GetEventObject());
307 long int style = pWin->GetWindowStyle();
308 if (event.GetActive())
309 pWin->SetWindowStyle(style | wxSTAY_ON_TOP);
310 else
311 pWin->SetWindowStyle(style ^ wxSTAY_ON_TOP);
312}
313
314void RoutePropDlgImpl::RecalculateSize() {
315 wxSize esize;
316 esize.x = GetCharWidth() * 110;
317 esize.y = GetCharHeight() * 40;
318
319 wxSize dsize = GetParent()->GetSize(); // GetClientSize();
320 esize.y = wxMin(esize.y, dsize.y - 0 /*(2 * GetCharHeight())*/);
321 esize.x = wxMin(esize.x, dsize.x - 0 /*(2 * GetCharHeight())*/);
322 SetSize(esize);
323
324 wxSize fsize = GetSize();
325 wxSize canvas_size = GetParent()->GetSize();
326 wxPoint screen_pos = GetParent()->GetScreenPosition();
327 int xp = (canvas_size.x - fsize.x) / 2;
328 int yp = (canvas_size.y - fsize.y) / 2;
329 Move(screen_pos.x + xp, screen_pos.y + yp);
330}
331
332void RoutePropDlgImpl::UpdatePoints() {
333 if (!m_pRoute) return;
334 wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
335 int selected_row = m_dvlcWaypoints->GetSelectedRow();
336 m_dvlcWaypoints->DeleteAllItems();
337
338 wxVector<wxVariant> data;
339
340 m_pRoute->UpdateSegmentDistances(
341 m_pRoute->m_PlannedSpeed); // to fix ETA properties
342 m_tcDistance->SetValue(
343 wxString::Format("%5.1f " + getUsrDistanceUnit(),
344 toUsrDistance(m_pRoute->m_route_length)));
345 m_tcEnroute->SetValue(formatTimeDelta(wxLongLong(m_pRoute->m_route_time)));
346 // Iterate on Route Points, inserting blank fields starting with index 0
347 int in = 0;
348 wxString slen, eta, ete;
349 double bearing, distance, speed;
350 double totalDistance = 0;
351 wxDateTime eta_dt = wxInvalidDateTime;
352 auto pnode = m_pRoute->pRoutePointList->begin();
353 while (pnode != m_pRoute->pRoutePointList->end()) {
354 speed = (*pnode)->GetPlannedSpeed();
355 if (speed < .1) {
356 speed = m_pRoute->m_PlannedSpeed;
357 }
358 if (in == 0) {
359 DistanceBearingMercator((*pnode)->GetLatitude(), (*pnode)->GetLongitude(),
360 gLat, gLon, &bearing, &distance);
361 if (m_pRoute->m_PlannedDeparture.IsValid()) {
364 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
365 .SetLongitude((*pnode)->m_lon);
366 eta = wxString::Format(
367 "Start: %s", ocpn::toUsrDateTimeFormat(
368 m_pRoute->m_PlannedDeparture.FromUTC(), opts));
369 eta.Append(wxString::Format(
370 " (%s)", GetDaylightString(
371 getDaylightStatus((*pnode)->m_lat, (*pnode)->m_lon,
372 m_pRoute->m_PlannedDeparture))
373 .c_str()));
374 eta_dt = m_pRoute->m_PlannedDeparture;
375 } else {
376 eta = _("N/A");
377 }
378 if (speed > .1) {
379 ete = formatTimeDelta(wxLongLong(3600. * distance / speed));
380 } else {
381 ete = _("N/A");
382 }
383 } else {
384 distance = (*pnode)->GetDistance();
385 bearing = (*pnode)->GetCourse();
386 if ((*pnode)->GetETA().IsValid()) {
389 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
390 .SetLongitude((*pnode)->m_lon);
391 eta = ocpn::toUsrDateTimeFormat((*pnode)->GetETA().FromUTC(), opts);
392 eta.Append(wxString::Format(
393 " (%s)", GetDaylightString(getDaylightStatus((*pnode)->m_lat,
394 (*pnode)->m_lon,
395 (*pnode)->GetETA()))
396 .c_str()));
397 eta_dt = (*pnode)->GetETA();
398 } else {
399 eta = "";
400 }
401 ete = (*pnode)->GetETE();
402 totalDistance += distance;
403 }
404 wxString name = (*pnode)->GetName();
405 double lat = (*pnode)->GetLatitude();
406 double lon = (*pnode)->GetLongitude();
407 wxString tide_station = (*pnode)->m_TideStation;
408 wxString desc = (*pnode)->GetDescription();
409 wxString etd;
410 if ((*pnode)->GetManualETD().IsValid()) {
411 // GetManualETD() returns time in UTC, always. So use it as such.
412 RoutePoint* rt = (*pnode);
415 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
416 .SetLongitude(rt->m_lon);
417 etd = ocpn::toUsrDateTimeFormat(rt->GetManualETD().FromUTC(), opts);
418 if (rt->GetManualETD().IsValid() && rt->GetETA().IsValid() &&
419 rt->GetManualETD() < rt->GetETA()) {
420 etd.Prepend("!! "); // Manually entered ETD is before we arrive here!
421 }
422 } else {
423 etd = "";
424 }
425 ++pnode;
426 wxString crs;
427 if (pnode != m_pRoute->pRoutePointList->end()) {
428 crs = formatAngle((*pnode)->GetCourse());
429 } else {
430 crs = _("Arrived");
431 }
432
433 if (in == 0)
434 data.push_back(wxVariant("---"));
435 else {
436 std::ostringstream stm;
437 stm << in;
438 data.push_back(wxVariant(stm.str()));
439 }
440
441 wxString schar = "";
442#ifdef __ANDROID__
443 schar = wxString(" ");
444#endif
445 data.push_back(wxVariant(name + schar)); // To
446 slen.Printf("%5.1f " + getUsrDistanceUnit(), toUsrDistance(distance));
447 data.push_back(wxVariant(schar + slen + schar)); // Distance
448 data.push_back(wxVariant(schar + formatAngle(bearing))); // Bearing
449 slen.Printf("%5.1f " + getUsrDistanceUnit(), toUsrDistance(totalDistance));
450 data.push_back(wxVariant(schar + slen + schar)); // Total Distance
451 data.push_back(wxVariant(schar + ::toSDMM(1, lat, FALSE) + schar)); // Lat
452 data.push_back(wxVariant(schar + ::toSDMM(2, lon, FALSE) + schar)); // Lon
453 data.push_back(wxVariant(schar + ete + schar)); // ETE
454 data.push_back(schar + eta + schar); // ETA
455 data.push_back(
456 wxVariant(wxString::FromDouble(toUsrSpeed(speed)))); // Speed
457 data.push_back(wxVariant(
458 MakeTideInfo(tide_station, lat, lon, eta_dt))); // Next Tide event
459 data.push_back(wxVariant(desc)); // Description
460 data.push_back(wxVariant(crs));
461 data.push_back(wxVariant(etd));
462 data.push_back(
463 wxVariant("")); // Empty column to fill the remaining space (Usually
464 // gets squeezed to zero, even if not empty)
465 m_dvlcWaypoints->AppendItem(data);
466 data.clear();
467 in++;
468 }
469 if (selected_row > 0) {
470 m_dvlcWaypoints->SelectRow(selected_row);
471 m_dvlcWaypoints->EnsureVisible(selection);
472 }
473}
474
475void RoutePropDlgImpl::SetRouteAndUpdate(Route* pR, bool only_points) {
476 if (NULL == pR) return;
477
478 if (m_pRoute &&
479 m_pRoute != pR) // We had unsaved changes, but now display another route
480 ResetChanges();
481
482 m_OrigRoute.m_PlannedDeparture = pR->m_PlannedDeparture;
483 m_OrigRoute.m_PlannedSpeed = pR->m_PlannedSpeed;
484
485 wxString title = pR->GetName() == "" ? _("Route Properties") : pR->GetName();
486 if (!pR->m_bIsInLayer)
487 SetTitle(title);
488 else {
489 wxString caption(wxString::Format("%s, %s: %s", title, _("Layer"),
490 GetLayerName(pR->m_LayerID)));
491 SetTitle(caption);
492 }
493
494 // Fetch any config file values
495 if (!only_points) {
496 if (!pR->m_PlannedDeparture.IsValid()) {
497 pR->m_PlannedDeparture = wxDateTime::Now().ToUTC();
498 }
499
500 m_tz_selection = GLOBAL_SETTINGS_INPUT; // Honor global setting by default
501 if (pR != m_pRoute) {
502 if (pR->m_TimeDisplayFormat == RTE_TIME_DISP_UTC)
503 m_tz_selection = UTCINPUT;
504 else if (pR->m_TimeDisplayFormat == RTE_TIME_DISP_LOCAL)
505 m_tz_selection = LMTINPUT;
506 m_pEnroutePoint = NULL;
507 m_bStartNow = false;
508 }
509
510 m_pRoute = pR;
511
512 m_tcPlanSpeed->SetValue(
513 wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
514
515 if (m_scrolledWindowLinks) {
516 wxWindowList kids = m_scrolledWindowLinks->GetChildren();
517 for (unsigned int i = 0; i < kids.GetCount(); i++) {
518 wxWindowListNode* node = kids.Item(i);
519 wxWindow* win = node->GetData();
520 auto link_win = dynamic_cast<wxHyperlinkCtrl*>(win);
521 if (link_win) {
522 link_win->Disconnect(
523 wxEVT_COMMAND_HYPERLINK,
524 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick));
525 link_win->Disconnect(
526 wxEVT_RIGHT_DOWN,
527 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu));
528 win->Destroy();
529 }
530 }
531 int NbrOfLinks = m_pRoute->m_HyperlinkList->size();
532 HyperlinkList* list = m_pRoute->m_HyperlinkList;
533 for (Hyperlink* link : *m_pRoute->m_HyperlinkList) {
534 wxString Link = link->Link;
535 wxString Descr = link->DescrText;
536
537 wxHyperlinkCtrl* ctrl = new wxHyperlinkCtrl(
538 m_scrolledWindowLinks, wxID_ANY, Descr, Link, wxDefaultPosition,
539 wxDefaultSize, wxHL_DEFAULT_STYLE);
540 ctrl->Connect(
541 wxEVT_COMMAND_HYPERLINK,
542 wxHyperlinkEventHandler(RoutePropDlgImpl::OnHyperlinkClick), NULL,
543 this);
544 if (!m_pRoute->m_bIsInLayer) {
545 ctrl->Connect(
546 wxEVT_RIGHT_DOWN,
547 wxMouseEventHandler(RoutePropDlgImpl::HyperlinkContextMenu), NULL,
548 this);
549 }
550 bSizerLinks->Add(ctrl, 0, wxALL, 5);
551 }
552 m_scrolledWindowLinks->InvalidateBestSize();
553 m_scrolledWindowLinks->Layout();
554 bSizerLinks->Layout();
555 }
556
557 m_choiceTimezone->SetSelection(m_tz_selection);
558
559 // Reorganize dialog for route or track display
560 m_tcName->SetValue(m_pRoute->m_RouteNameString);
561 m_tcFrom->SetValue(m_pRoute->m_RouteStartString);
562 m_tcTo->SetValue(m_pRoute->m_RouteEndString);
563 m_tcDescription->SetValue(m_pRoute->m_RouteDescription);
564
565 m_tcName->SetFocus();
566 if (m_pRoute->m_PlannedDeparture.IsValid() &&
567 m_pRoute->m_PlannedDeparture.GetValue() > 0) {
568 wxDateTime t =
569 toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
570 (*m_pRoute->pRoutePointList->begin())->m_lon);
571 m_dpDepartureDate->SetValue(t.GetDateOnly());
572 m_tpDepartureTime->SetValue(t);
573 } else {
574 wxDateTime t =
575 toUsrDateTime(wxDateTime::Now().ToUTC(), m_tz_selection,
576 (*m_pRoute->pRoutePointList->begin())->m_lon);
577 m_dpDepartureDate->SetValue(t.GetDateOnly());
578 m_tpDepartureTime->SetValue(t);
579 }
580 }
581
582 m_btnSplit->Enable(false);
583 if (!m_pRoute) return;
584
585 if (m_pRoute->m_Colour == "") {
586 m_choiceColor->Select(0);
587 } else {
588 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
589 i++) {
590 if (m_pRoute->m_Colour == ::GpxxColorNames[i]) {
591 m_choiceColor->Select(i + 1);
592 break;
593 }
594 }
595 }
596
597 for (unsigned int i = 0; i < sizeof(::StyleValues) / sizeof(int); i++) {
598 if (m_pRoute->m_style == ::StyleValues[i]) {
599 m_choiceStyle->Select(i);
600 break;
601 }
602 }
603
604 for (unsigned int i = 0; i < sizeof(::WidthValues) / sizeof(int); i++) {
605 if (m_pRoute->m_width == ::WidthValues[i]) {
606 m_choiceWidth->Select(i);
607 break;
608 }
609 }
610
611 UpdatePoints();
612
613 m_btnExtend->Enable(IsThisRouteExtendable());
614}
615
616void RoutePropDlgImpl::DepartureDateOnDateChanged(wxDateEvent& event) {
617 if (!m_pRoute) return;
618 m_pRoute->SetDepartureDate(GetDepartureTS());
619 UpdatePoints();
620 event.Skip();
621}
622
623void RoutePropDlgImpl::DepartureTimeOnTimeChanged(wxDateEvent& event) {
624 if (!m_pRoute) return;
625 m_pRoute->SetDepartureDate(GetDepartureTS());
626 UpdatePoints();
627 event.Skip();
628}
629
630void RoutePropDlgImpl::TimezoneOnChoice(wxCommandEvent& event) {
631 if (!m_pRoute) return;
632 m_tz_selection = m_choiceTimezone->GetSelection();
633 wxDateTime t = toUsrDateTime(m_pRoute->m_PlannedDeparture, m_tz_selection,
634 (*m_pRoute->pRoutePointList->begin())->m_lon);
635 m_dpDepartureDate->SetValue(t.GetDateOnly());
636 m_tpDepartureTime->SetValue(t);
637 UpdatePoints();
638 event.Skip();
639}
640
641void RoutePropDlgImpl::PlanSpeedOnTextEnter(wxCommandEvent& event) {
642 if (!m_pRoute) return;
643 double spd;
644 if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
645 if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
646 m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
647 UpdatePoints();
648 }
649 } else {
650 m_tcPlanSpeed->SetValue(
651 wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
652 }
653}
654
655void RoutePropDlgImpl::PlanSpeedOnKillFocus(wxFocusEvent& event) {
656 if (!m_pRoute) return;
657 double spd;
658 if (m_tcPlanSpeed->GetValue().ToDouble(&spd)) {
659 if (m_pRoute->m_PlannedSpeed != fromUsrSpeed(spd)) {
660 m_pRoute->m_PlannedSpeed = fromUsrSpeed(spd);
661 UpdatePoints();
662 }
663 } else {
664 m_tcPlanSpeed->SetValue(
665 wxString::FromDouble(toUsrSpeed(m_pRoute->m_PlannedSpeed)));
666 }
667 event.Skip();
668}
669
670static int ev_col;
671void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemEditingDone(
672 wxDataViewEvent& event) {
673 // There is a bug in wxWidgets, the EDITING_DONE event does not contain the
674 // new value, so we must save the data and do the work later in the value
675 // changed event.
676 ev_col = event.GetColumn();
677}
678
679void RoutePropDlgImpl::WaypointsOnDataViewListCtrlItemValueChanged(
680 wxDataViewEvent& event) {
681#if wxCHECK_VERSION(3, 1, 2)
682 // wx 3.0.x crashes in the below code
683 if (!m_pRoute) return;
684 wxDataViewModel* const model = event.GetModel();
685 wxVariant value;
686 model->GetValue(value, event.GetItem(), ev_col);
687 RoutePoint* p = m_pRoute->GetPoint(
688 static_cast<int>(reinterpret_cast<long long>(event.GetItem().GetID())));
689 if (ev_col == COLUMN_PLANNED_SPEED) {
690 double spd;
691 if (!value.GetString().ToDouble(&spd)) {
692 spd = 0.0;
693 }
694 p->SetPlannedSpeed(fromUsrSpeed(spd));
695 } else if (ev_col == COLUMN_ETD) {
696 wxString::const_iterator end;
697 wxDateTime etd;
698
699 wxString ts = value.GetString();
700 if (ts.StartsWith("!")) {
701 ts.Replace("!", "", true);
702 }
703 ts.Trim(true);
704 ts.Trim(false);
705
706 if (!ts.IsEmpty()) {
707 if (!etd.ParseDateTime(ts, &end)) {
708 p->SetETD(wxInvalidDateTime);
709 } else {
710 p->SetETD(
711 fromUsrDateTime(etd, m_tz_selection, p->m_lon).FormatISOCombined());
712 }
713 } else {
714 p->SetETD(wxInvalidDateTime);
715 }
716 }
717 UpdatePoints();
718#endif
719}
720
721void RoutePropDlgImpl::WaypointsOnDataViewListCtrlSelectionChanged(
722 wxDataViewEvent& event) {
723 long selected_row = m_dvlcWaypoints->GetSelectedRow();
724 if (selected_row > 0 && selected_row < m_dvlcWaypoints->GetItemCount() - 1) {
725 m_btnSplit->Enable(true);
726 } else {
727 m_btnSplit->Enable(false);
728 }
729 if (IsThisRouteExtendable()) {
730 m_btnExtend->Enable(true);
731 } else {
732 m_btnExtend->Enable(false);
733 }
734 if (selected_row >= 0 && selected_row < m_dvlcWaypoints->GetItemCount()) {
735 RoutePoint* prp = m_pRoute->GetPoint(selected_row + 1);
736 if (prp) {
737 top_frame::Get()->JumpToPosition(prp->m_lat, prp->m_lon);
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 top_frame::Get()->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 top_frame::Get()->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. / top_frame::Get()->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.
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.
MyConfig * pConfig
Global instance.
Definition navutil.cpp:118
wxDateTime toUsrDateTime(const wxDateTime ts, const int format, const double lon)
Converts a timestamp from UTC to the user's preferred time format.
Definition navutil.cpp:2923
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:2963
Utility functions.
double toUsrDistance(double nm_distance, int unit)
Convert a distance from nautical miles (NMi) to user display units.
Navigation Utility Functions without GUI dependencies.
PlugIn Object Definition/API.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
double gLat
Vessel's current latitude in decimal degrees.
Definition own_ship.cpp:26
double gLon
Vessel's current longitude in decimal degrees.
Definition own_ship.cpp:27
Position, course, speed, etc.
Generic, styled prit dialog.
Route print dialog.
#define LMTINPUT
Format date/time using the remote location LMT time.
#define GLOBAL_SETTINGS_INPUT
Format date/time according to global OpenCPN settings.
RoutePropDlgImpl * pRoutePropDialog
Global instance.
#define UTCINPUT
Format date/time in UTC.
#define LTINPUT
Format date/time using timezone configured in the operating system./*#end#*‍/.
Route properties dialog.
Routeman * g_pRouteMan
Global instance.
Definition routeman.cpp:60
RouteList * pRouteList
Global instance.
Definition routeman.cpp:66
Route Manager.
RouteManagerDialog * pRouteManagerDialog
Global instance.
Manage routes dialog.
Select * pSelect
Global instance.
Definition select.cpp:36
Selected route, segment, waypoint, etc.
Configuration options for date and time formatting.
DateTimeFormatOptions & SetTimezone(const wxString &tz)
Sets the timezone mode for date/time display.
DateTimeFormatOptions & SetLongitude(double lon)
Sets the reference longitude for Local Mean Time (LMT) calculations.
TCMgr * ptcmgr
Global instance.
Definition tcmgr.cpp:42
Tide and Current Manager @TODO Add original author copyright.
Abstract gFrame/MyFrame interface.