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