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