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