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
60extern wxString GetLayerName(int id);
61
62extern Routeman* g_pRouteMan;
63extern MyConfig* pConfig;
64extern ColorScheme global_color_scheme;
65extern RouteList* pRouteList;
66extern MyFrame* gFrame;
67extern RouteManagerDialog* pRouteManagerDialog;
68extern TCMgr* ptcmgr;
69
70int g_route_prop_x, g_route_prop_y, g_route_prop_sx, g_route_prop_sy;
71
72// Sunrise/twilight calculation for route properties.
73// limitations: latitude below 60, year between 2000 and 2100
74// riset is +1 for rise -1 for set
75// adapted by author's permission from QBASIC source as published at
76// http://www.stargazing.net/kepler
77
78#ifndef PI
79#define PI (4. * atan(1.0))
80#endif
81#define TPI (2. * PI)
82#define DEGS (180. / PI)
83#define RADS (PI / 180.)
84
85#define MOTWILIGHT \
86 1 // in some languages there may be a distinction between morning/evening
87#define SUNRISE 2
88#define DAY 3
89#define SUNSET 4
90#define EVTWILIGHT 5
91#define NIGHT 6
92
93static wxString GetDaylightString(int index) {
94 switch (index) {
95 case 0:
96 return _T(" - ");
97 case 1:
98 return _("MoTwilight");
99 case 2:
100 return _("Sunrise");
101 case 3:
102 return _("Daytime");
103 case 4:
104 return _("Sunset");
105 case 5:
106 return _("EvTwilight");
107 case 6:
108 return _("Nighttime");
109
110 default:
111 return _T("");
112 }
113}
114
115static double sign(double x) {
116 if (x < 0.)
117 return -1.;
118 else
119 return 1.;
120}
121
122static double FNipart(double x) { return (sign(x) * (int)(fabs(x))); }
123
124static double FNday(int y, int m, int d, int h) {
125 long fd = (367 * y - 7 * (y + (m + 9) / 12) / 4 + 275 * m / 9 + d);
126 return ((double)fd - 730531.5 + h / 24.);
127}
128
129static double FNrange(double x) {
130 double b = x / TPI;
131 double a = TPI * (b - FNipart(b));
132 if (a < 0.) a = TPI + a;
133 return (a);
134}
135
136static double getDaylightEvent(double glat, double glong, int riset,
137 double altitude, int y, int m, int d) {
138 double day = FNday(y, m, d, 0);
139 double days, correction;
140 double utold = PI;
141 double utnew = 0.;
142 double sinalt =
143 sin(altitude * RADS); // go for the sunrise/sunset altitude first
144 double sinphi = sin(glat * RADS);
145 double cosphi = cos(glat * RADS);
146 double g = glong * RADS;
147 double t, L, G, ec, lambda, E, obl, delta, GHA, cosc;
148 int limit = 12;
149 while ((fabs(utold - utnew) > .001)) {
150 if (limit-- <= 0) return (-1.);
151 days = day + utnew / TPI;
152 t = days / 36525.;
153 // get arguments of Sun's orbit
154 L = FNrange(4.8949504201433 + 628.331969753199 * t);
155 G = FNrange(6.2400408 + 628.3019501 * t);
156 ec = .033423 * sin(G) + .00034907 * sin(2 * G);
157 lambda = L + ec;
158 E = -1. * ec + .0430398 * sin(2 * lambda) - .00092502 * sin(4. * lambda);
159 obl = .409093 - .0002269 * t;
160 delta = asin(sin(obl) * sin(lambda));
161 GHA = utold - PI + E;
162 cosc = (sinalt - sinphi * sin(delta)) / (cosphi * cos(delta));
163 if (cosc > 1.)
164 correction = 0.;
165 else if (cosc < -1.)
166 correction = PI;
167 else
168 correction = acos(cosc);
169 double tmp = utnew;
170 utnew = FNrange(utold - (GHA + g + riset * correction));
171 utold = tmp;
172 }
173 return (utnew * DEGS / 15.); // returns decimal hours UTC
174}
175
176static double getLMT(double ut, double lon) {
177 double t = ut + lon / 15.;
178 if (t >= 0.)
179 if (t <= 24.)
180 return (t);
181 else
182 return (t - 24.);
183 else
184 return (t + 24.);
185}
186
190static wxString getDatetimeTimezoneSelector(int selection) {
191 switch (selection) {
192 case UTCINPUT:
193 return "UTC";
194 case LTINPUT:
195 return "Local Time";
196 case LMTINPUT:
197 return "LMT";
198 case GLOBAL_SETTINGS_INPUT:
199 default:
200 return wxEmptyString;
201 }
202}
203
204static int getDaylightStatus(double lat, double lon, wxDateTime utcDateTime) {
205 if (fabs(lat) > 60.) return (0);
206 int y = utcDateTime.GetYear();
207 int m = utcDateTime.GetMonth() + 1; // wxBug? months seem to run 0..11 ?
208 int d = utcDateTime.GetDay();
209 int h = utcDateTime.GetHour();
210 int n = utcDateTime.GetMinute();
211 int s = utcDateTime.GetSecond();
212 if (y < 2000 || y > 2100) return (0);
213
214 double ut = (double)h + (double)n / 60. + (double)s / 3600.;
215 double lt = getLMT(ut, lon);
216 double rsalt = -0.833;
217 double twalt = -12.;
218
219 if (lt <= 12.) {
220 double sunrise = getDaylightEvent(lat, lon, +1, rsalt, y, m, d);
221 if (sunrise < 0.)
222 return (0);
223 else
224 sunrise = getLMT(sunrise, lon);
225
226 if (fabs(lt - sunrise) < 0.15) return (SUNRISE);
227 if (lt > sunrise) return (DAY);
228 double twilight = getDaylightEvent(lat, lon, +1, twalt, y, m, d);
229 if (twilight < 0.)
230 return (0);
231 else
232 twilight = getLMT(twilight, lon);
233 if (lt > twilight)
234 return (MOTWILIGHT);
235 else
236 return (NIGHT);
237 } else {
238 double sunset = getDaylightEvent(lat, lon, -1, rsalt, y, m, d);
239 if (sunset < 0.)
240 return (0);
241 else
242 sunset = getLMT(sunset, lon);
243 if (fabs(lt - sunset) < 0.15) return (SUNSET);
244 if (lt < sunset) return (DAY);
245 double twilight = getDaylightEvent(lat, lon, -1, twalt, y, m, d);
246 if (twilight < 0.)
247 return (0);
248 else
249 twilight = getLMT(twilight, lon);
250 if (lt < twilight)
251 return (EVTWILIGHT);
252 else
253 return (NIGHT);
254 }
255}
256
257RoutePropDlgImpl::RoutePropDlgImpl(wxWindow* parent, wxWindowID id,
258 const wxString& title, const wxPoint& pos,
259 const wxSize& size, long style)
260 : RoutePropDlg(parent, id, title, pos, size, style) {
261 m_pRoute = nullptr;
262
263 SetColorScheme(global_color_scheme);
264
265 if (g_route_prop_sx > 0 && g_route_prop_sy > 0 &&
266 g_route_prop_sx < wxGetDisplaySize().x &&
267 g_route_prop_sy < wxGetDisplaySize().y) {
268 SetSize(g_route_prop_sx, g_route_prop_sy);
269 }
270
271 if (g_route_prop_x > 0 && g_route_prop_y > 0 &&
272 g_route_prop_x < wxGetDisplaySize().x &&
273 g_route_prop_y < wxGetDisplaySize().y) {
274 SetPosition(wxPoint(10, 10));
275 }
276 RecalculateSize();
277
278 Connect(wxEVT_COMMAND_MENU_SELECTED,
279 wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
280 NULL, this);
281
282#ifdef __WXOSX__
283 Connect(wxEVT_ACTIVATE, wxActivateEventHandler(RoutePropDlgImpl::OnActivate),
284 NULL, this);
285#endif
286}
287
288RoutePropDlgImpl::~RoutePropDlgImpl() {
289 Disconnect(wxEVT_COMMAND_MENU_SELECTED,
290 wxCommandEventHandler(RoutePropDlgImpl::OnRoutePropMenuSelected),
291 NULL, this);
292 instanceFlag = false;
293}
294
295bool RoutePropDlgImpl::instanceFlag = false;
296bool RoutePropDlgImpl::getInstanceFlag() {
297 return RoutePropDlgImpl::instanceFlag;
298}
299
300RoutePropDlgImpl* RoutePropDlgImpl::single = NULL;
301RoutePropDlgImpl* RoutePropDlgImpl::getInstance(wxWindow* parent) {
302 if (!instanceFlag) {
303 single = new RoutePropDlgImpl(parent);
304 instanceFlag = true;
305 }
306 return single;
307}
308
309void RoutePropDlgImpl::OnActivate(wxActivateEvent& event) {
310 auto pWin = dynamic_cast<wxFrame*>(event.GetEventObject());
311 long int style = pWin->GetWindowStyle();
312 if (event.GetActive())
313 pWin->SetWindowStyle(style | wxSTAY_ON_TOP);
314 else
315 pWin->SetWindowStyle(style ^ wxSTAY_ON_TOP);
316}
317
318void RoutePropDlgImpl::RecalculateSize(void) {
319 wxSize esize;
320 esize.x = GetCharWidth() * 110;
321 esize.y = GetCharHeight() * 40;
322
323 wxSize dsize = GetParent()->GetSize(); // GetClientSize();
324 esize.y = wxMin(esize.y, dsize.y - 0 /*(2 * GetCharHeight())*/);
325 esize.x = wxMin(esize.x, dsize.x - 0 /*(2 * GetCharHeight())*/);
326 SetSize(esize);
327
328 wxSize fsize = GetSize();
329 wxSize canvas_size = GetParent()->GetSize();
330 wxPoint screen_pos = GetParent()->GetScreenPosition();
331 int xp = (canvas_size.x - fsize.x) / 2;
332 int yp = (canvas_size.y - fsize.y) / 2;
333 Move(screen_pos.x + xp, screen_pos.y + yp);
334}
335
336void RoutePropDlgImpl::UpdatePoints() {
337 if (!m_pRoute) return;
338 wxDataViewItem selection = m_dvlcWaypoints->GetSelection();
339 int selected_row = m_dvlcWaypoints->GetSelectedRow();
340 m_dvlcWaypoints->DeleteAllItems();
341
342 wxVector<wxVariant> data;
343
344 m_pRoute->UpdateSegmentDistances(
345 m_pRoute->m_PlannedSpeed); // to fix ETA properties
346 m_tcDistance->SetValue(
347 wxString::Format(wxT("%5.1f ") + getUsrDistanceUnit(),
348 toUsrDistance(m_pRoute->m_route_length)));
349 m_tcEnroute->SetValue(formatTimeDelta(wxLongLong(m_pRoute->m_route_time)));
350 // Iterate on Route Points, inserting blank fields starting with index 0
351 wxRoutePointListNode* pnode = m_pRoute->pRoutePointList->GetFirst();
352 int in = 0;
353 wxString slen, eta, ete;
354 double bearing, distance, speed;
355 double totalDistance = 0;
356 wxDateTime eta_dt = wxInvalidDateTime;
357 while (pnode) {
358 speed = pnode->GetData()->GetPlannedSpeed();
359 if (speed < .1) {
360 speed = m_pRoute->m_PlannedSpeed;
361 }
362 if (in == 0) {
363 DistanceBearingMercator(pnode->GetData()->GetLatitude(),
364 pnode->GetData()->GetLongitude(), gLat, gLon,
365 &bearing, &distance);
366 if (m_pRoute->m_PlannedDeparture.IsValid()) {
369 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
370 .SetLongitude(pnode->GetData()->m_lon);
371 eta = wxString::Format(
372 "Start: %s", ocpn::toUsrDateTimeFormat(
373 m_pRoute->m_PlannedDeparture.FromUTC(), opts));
374 eta.Append(wxString::Format(
375 _T(" (%s)"),
376 GetDaylightString(getDaylightStatus(pnode->GetData()->m_lat,
377 pnode->GetData()->m_lon,
378 m_pRoute->m_PlannedDeparture))
379 .c_str()));
380 eta_dt = m_pRoute->m_PlannedDeparture;
381 } else {
382 eta = _("N/A");
383 }
384 if (speed > .1) {
385 ete = formatTimeDelta(wxLongLong(3600. * distance / speed));
386 } else {
387 ete = _("N/A");
388 }
389 } else {
390 distance = pnode->GetData()->GetDistance();
391 bearing = pnode->GetData()->GetCourse();
392 if (pnode->GetData()->GetETA().IsValid()) {
395 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
396 .SetLongitude(pnode->GetData()->m_lon);
397 eta = ocpn::toUsrDateTimeFormat(pnode->GetData()->GetETA().FromUTC(),
398 opts);
399 eta.Append(wxString::Format(
400 _T(" (%s)"),
401 GetDaylightString(getDaylightStatus(pnode->GetData()->m_lat,
402 pnode->GetData()->m_lon,
403 pnode->GetData()->GetETA()))
404 .c_str()));
405 eta_dt = pnode->GetData()->GetETA();
406 } else {
407 eta = wxEmptyString;
408 }
409 ete = pnode->GetData()->GetETE();
410 totalDistance += distance;
411 }
412 wxString name = pnode->GetData()->GetName();
413 double lat = pnode->GetData()->GetLatitude();
414 double lon = pnode->GetData()->GetLongitude();
415 wxString tide_station = pnode->GetData()->m_TideStation;
416 wxString desc = pnode->GetData()->GetDescription();
417 wxString etd;
418 if (pnode->GetData()->GetManualETD().IsValid()) {
419 // GetManualETD() returns time in UTC, always. So use it as such.
420 RoutePoint* rt = pnode->GetData();
423 .SetTimezone(getDatetimeTimezoneSelector(m_tz_selection))
424 .SetLongitude(rt->m_lon);
425 etd = ocpn::toUsrDateTimeFormat(rt->GetManualETD().FromUTC(), opts);
426 if (rt->GetManualETD().IsValid() && rt->GetETA().IsValid() &&
427 rt->GetManualETD() < rt->GetETA()) {
428 etd.Prepend(
429 _T("!! ")); // Manually entered ETD is before we arrive here!
430 }
431 } else {
432 etd = wxEmptyString;
433 }
434 pnode = pnode->GetNext();
435 wxString crs;
436 if (pnode) {
437 crs = formatAngle(pnode->GetData()->GetCourse());
438 } else {
439 crs = _("Arrived");
440 }
441
442 if (in == 0)
443 data.push_back(wxVariant("---"));
444 else {
445 std::ostringstream stm;
446 stm << in;
447 data.push_back(wxVariant(stm.str()));
448 }
449
450 wxString schar = wxEmptyString;
451#ifdef __ANDROID__
452 schar = wxString(" ");
453#endif
454 data.push_back(wxVariant(name + schar)); // To
455 slen.Printf(wxT("%5.1f ") + getUsrDistanceUnit(), toUsrDistance(distance));
456 data.push_back(wxVariant(schar + slen + schar)); // Distance
457 data.push_back(wxVariant(schar + formatAngle(bearing))); // Bearing
458 slen.Printf(wxT("%5.1f ") + getUsrDistanceUnit(),
459 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(_T("%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(" (") + _T(ETA_FORMAT_STR) + _T(")")
789 << tab << 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") + _T("..."));
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(_T("LW: ")); // High Water
1180 } else if (ev == 2) {
1181 tide_form.Append(_T("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(_T("#")) != wxNOT_FOUND) {
1377 wxRegKey RegKey(
1378 wxString(_T("HKEY_CLASSES_ROOT\\HTTP\\shell\\open\\command")));
1379 if (RegKey.Exists()) {
1380 wxString command_line;
1381 RegKey.QueryValue(wxString(_T("")), command_line);
1382
1383 // Remove "
1384 command_line.Replace(wxString(_T("\"")), wxString(_T("")));
1385
1386 // Strip arguments
1387 int l = command_line.Find(_T(".exe"));
1388 if (wxNOT_FOUND == l) l = command_line.Find(_T(".EXE"));
1389
1390 if (wxNOT_FOUND != l) {
1391 wxString cl = command_line.Mid(0, l + 4);
1392 cl += _T(" ");
1393 cc.Prepend(_T("\""));
1394 cc.Append(_T("\""));
1395 cl += cc;
1396 wxExecute(cl); // Async, so Fire and Forget...
1397 }
1398 }
1399 } else
1400 event.Skip();
1401#else
1402 wxString url = event.GetURL();
1403 url.Replace(_T(" "), _T("%20"));
1404 ::wxLaunchDefaultBrowser(url);
1405#endif
1406}
1407
1408void RoutePropDlgImpl::HyperlinkContextMenu(wxMouseEvent& event) {
1409 m_pEditedLink = (wxHyperlinkCtrl*)event.GetEventObject();
1410 m_scrolledWindowLinks->PopupMenu(
1411 m_menuLink, m_pEditedLink->GetPosition().x + event.GetPosition().x,
1412 m_pEditedLink->GetPosition().y + event.GetPosition().y);
1413}
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:194
bool DeleteRoute(Route *pRoute)
Definition routeman.cpp:856
Definition tcmgr.h:88
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.
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.