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