OpenCPN Partial API docs
Loading...
Searching...
No Matches
route_gui.cpp
1
2#include <wx/colour.h>
3#include <wx/gdicmn.h>
4#include <wx/pen.h>
5#include <wx/string.h>
6#include <wx/utils.h>
7
8#include "color_handler.h"
9#include "chartbase.h"
10#include "comm_n0183_output.h"
11#include "georef.h"
12#include "gui_lib.h"
13#include "navutil.h"
14#include "own_ship.h"
15#include "routeman.h"
16#include "route_gui.h"
17#include "route_point_gui.h"
18#include "glChartCanvas.h"
19#include "line_clip.h"
20#include "route.h"
21
22extern Routeman* g_pRouteMan;
23extern wxColour g_colourTrackLineColour;
24extern int g_route_line_width;
25
26extern wxColor GetDimColor(wxColor c);
27extern bool g_bHighliteTracks;
28extern float g_GLMinSymbolLineWidth;
29extern bool g_bAllowShipToActive;
30
31extern ocpnGLOptions g_GLOptions;
32
33extern int s_arrow_icon[];
34
35static void TestLongitude(double lon, double min, double max, bool &lonl,
36 bool &lonr) {
37 double clon = (min + max) / 2;
38 if (min - lon > 180) lon += 360;
39
40 lonl = lonr = false;
41 if (lon < min) {
42 if (lon < clon - 180)
43 lonr = true;
44 else
45 lonl = true;
46 } else if (lon > max) {
47 if (lon > clon + 180)
48 lonl = true;
49 else
50 lonr = true;
51 }
52}
53
54void RouteGui::Draw(ocpnDC &dc, ChartCanvas *canvas, const LLBBox &box) {
55 if (m_route.pRoutePointList->empty()) return;
56
57 ViewPort vp = canvas->GetVP();
58
59 LLBBox test_box = m_route.GetBBox();
60 if (box.IntersectOut(test_box)) // Route is wholly outside window
61 return;
62
63 int width = g_route_line_width;
64 if (m_route.m_width != WIDTH_UNDEFINED) width = m_route.m_width;
65
66 if (m_route.m_bVisible && m_route.m_bRtIsSelected) {
67 wxPen spen = *g_pRouteMan->GetSelectedRoutePen();
68 spen.SetWidth(width);
69 dc.SetPen(spen);
70 dc.SetBrush(*g_pRouteMan->GetSelectedRouteBrush());
71 } else if (m_route.m_bVisible) {
72 wxPenStyle style = wxPENSTYLE_SOLID;
73 wxColour col;
74 if (m_route.m_style != wxPENSTYLE_INVALID) style = m_route.m_style;
75 if (m_route.m_Colour == wxEmptyString) {
76 col = g_pRouteMan->GetRoutePen()->GetColour();
77 } else {
78 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
79 i++) {
80 if (m_route.m_Colour == ::GpxxColorNames[i]) {
81 col = ::GpxxColors[i];
82 break;
83 }
84 }
85 }
86 dc.SetPen(*wxThePenList->FindOrCreatePen(col, width, style));
87 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
88 }
89
90 if (m_route.m_bVisible && m_route.m_bRtIsActive) {
91 wxPen spen = *g_pRouteMan->GetActiveRoutePen();
92 spen.SetWidth(width);
93 dc.SetPen(spen);
94 dc.SetBrush(*g_pRouteMan->GetActiveRouteBrush());
95 }
96
97 wxPoint rpt1, rpt2;
98 if (m_route.m_bVisible) DrawPointWhich(dc, canvas, 1, &rpt1);
99
100 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
101 RoutePoint *prp1 = node->GetData();
102 node = node->GetNext();
103
104 if (m_route.m_bVisible || prp1->IsShared()) RoutePointGui(*prp1).Draw(dc, canvas, NULL); //prp1->Draw(dc, canvas, NULL);
105
106 while (node) {
107 RoutePoint *prp2 = node->GetData();
108
109 bool draw_arrow = !(prp2->m_bIsActive && g_bAllowShipToActive);
110
111 if (m_route.m_bVisible || prp2->IsShared()) RoutePointGui(*prp2).Draw(dc, canvas, &rpt2); //prp2->Draw(dc, canvas, &rpt2);
112
113 if (m_route.m_bVisible) {
114 // Handle offscreen points
115 bool b_2_on = vp.GetBBox().Contains(prp2->m_lat, prp2->m_lon);
116 bool b_1_on = vp.GetBBox().Contains(prp1->m_lat, prp1->m_lon);
117
118 // Simple case
119 if (b_1_on && b_2_on)
120 RenderSegment(dc, rpt1.x, rpt1.y, rpt2.x, rpt2.y, vp, draw_arrow,
121 m_route.m_hiliteWidth); // with arrows
122
123 // In the cases where one point is on, and one off
124 // we must decide which way to go in longitude
125 // Arbitrarily, we will go the shortest way
126
127 double pix_full_circle = WGS84_semimajor_axis_meters * mercator_k0 * 2 *
128 PI * vp.view_scale_ppm;
129 double dp =
130 pow((double)(rpt1.x - rpt2.x), 2) + pow((double)(rpt1.y - rpt2.y), 2);
131 double dtest;
132 int adder;
133 if (b_1_on && !b_2_on) {
134 if (rpt2.x < rpt1.x)
135 adder = (int)pix_full_circle;
136 else
137 adder = -(int)pix_full_circle;
138
139 dtest = pow((double)(rpt1.x - (rpt2.x + adder)), 2) +
140 pow((double)(rpt1.y - rpt2.y), 2);
141
142 if (dp < dtest) adder = 0;
143
144 RenderSegment(dc, rpt1.x, rpt1.y, rpt2.x + adder, rpt2.y, vp,
145 draw_arrow, m_route.m_hiliteWidth);
146 } else if (!b_1_on) {
147 if (rpt1.x < rpt2.x)
148 adder = (int)pix_full_circle;
149 else
150 adder = -(int)pix_full_circle;
151
152 float rxd = rpt2.x - (rpt1.x + adder);
153 float ryd = rpt1.y - rpt2.y;
154 dtest = rxd * rxd + ryd * ryd;
155
156 if (dp < dtest) adder = 0;
157
158 RenderSegment(dc, rpt1.x + adder, rpt1.y, rpt2.x, rpt2.y, vp,
159 draw_arrow, m_route.m_hiliteWidth);
160 }
161 }
162
163 rpt1 = rpt2;
164 prp1 = prp2;
165
166 node = node->GetNext();
167 }
168}
169
170void RouteGui::RenderSegment(ocpnDC &dc, int xa, int ya, int xb, int yb,
171 ViewPort &vp, bool bdraw_arrow, int hilite_width) {
172 // Get the dc boundary
173 int sx, sy;
174 dc.GetSize(&sx, &sy);
175
176 // Try to exit early if the segment is nowhere near the screen
177 wxRect r(0, 0, sx, sy);
178 wxRect s(xa, ya, 1, 1);
179 wxRect t(xb, yb, 1, 1);
180 s.Union(t);
181 if (!r.Intersects(s)) return;
182
183 // Clip the line segment to the dc boundary
184 int x0 = xa;
185 int y0 = ya;
186 int x1 = xb;
187 int y1 = yb;
188
189 // If hilite is desired, use a Native Graphics context to render alpha
190 // colours That is, if wxGraphicsContext is available.....
191
192 if (hilite_width) {
193 if (Visible ==
194 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy)) {
195 wxPen psave = dc.GetPen();
196
197 wxColour y = GetGlobalColor(_T ( "YELO1" ));
198 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
199
200 wxPen HiPen(hilt, hilite_width, wxPENSTYLE_SOLID);
201
202 dc.SetPen(HiPen);
203 dc.StrokeLine(x0, y0, x1, y1);
204
205 dc.SetPen(psave);
206 dc.StrokeLine(x0, y0, x1, y1);
207 }
208 } else {
209 if (Visible ==
210 cohen_sutherland_line_clip_i(&x0, &y0, &x1, &y1, 0, sx, 0, sy))
211 dc.StrokeLine(x0, y0, x1, y1);
212 }
213
214 if (bdraw_arrow) {
215 // Draw a direction arrow
216
217 double theta = atan2((double)(yb - ya), (double)(xb - xa));
218 theta -= PI / 2.;
219
220 wxPoint icon[10];
221 double icon_scale_factor = 100 * vp.view_scale_ppm;
222 icon_scale_factor = fmin(icon_scale_factor, 1.5); // Sets the max size
223 icon_scale_factor = fmax(icon_scale_factor, .10);
224
225 // Get the absolute line length
226 // and constrain the arrow to be no more than xx% of the line length
227 double nom_arrow_size = 20.;
228 double max_arrow_to_leg = .20;
229 double lpp = sqrt(pow((double)(xa - xb), 2) + pow((double)(ya - yb), 2));
230
231 double icon_size = icon_scale_factor * nom_arrow_size;
232 if (icon_size > (lpp * max_arrow_to_leg))
233 icon_scale_factor = (lpp * max_arrow_to_leg) / nom_arrow_size;
234
235 for (int i = 0; i < 7; i++) {
236 int j = i * 2;
237 double pxa = (double)(s_arrow_icon[j]);
238 double pya = (double)(s_arrow_icon[j + 1]);
239
240 pya *= icon_scale_factor;
241 pxa *= icon_scale_factor;
242
243 double px = (pxa * sin(theta)) + (pya * cos(theta));
244 double py = (pya * sin(theta)) - (pxa * cos(theta));
245
246 icon[i].x = (int)(px) + xb;
247 icon[i].y = (int)(py) + yb;
248 }
249 wxPen savePen = dc.GetPen();
250 dc.SetPen(*wxTRANSPARENT_PEN);
251 dc.StrokePolygon(6, &icon[0], 0, 0);
252 dc.SetPen(savePen);
253 }
254}
255
256void RouteGui::RenderSegmentArrowsGL(ocpnDC &dc, int xa, int ya, int xb, int yb,
257 ViewPort &vp) {
258#ifdef ocpnUSE_GL
259 // Draw a direction arrow
260 float icon_scale_factor = 100 * vp.view_scale_ppm;
261 icon_scale_factor = fmin(icon_scale_factor, 1.5); // Sets the max size
262 icon_scale_factor = fmax(icon_scale_factor, .10);
263
264 // Get the absolute line length
265 // and constrain the arrow to be no more than xx% of the line length
266 float nom_arrow_size = 20.;
267 float max_arrow_to_leg = (float).20;
268 float lpp = sqrtf(powf((float)(xa - xb), 2) + powf((float)(ya - yb), 2));
269
270 float icon_size = icon_scale_factor * nom_arrow_size;
271 if (icon_size > (lpp * max_arrow_to_leg))
272 icon_scale_factor = (lpp * max_arrow_to_leg) / nom_arrow_size;
273
274 float theta = atan2f((float)yb - ya, (float)xb - xa);
275 theta -= (float)PI;
276
277 // icon_scale_factor = 5;
278 wxPoint pts[3];
279 // 0
280 pts[0].x = s_arrow_icon[0];
281 pts[0].y = s_arrow_icon[1];
282 pts[1].x = s_arrow_icon[2];
283 pts[1].y = s_arrow_icon[3];
284 pts[2].x = s_arrow_icon[6];
285 pts[2].y = s_arrow_icon[7];
286
287 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
288
289 // 1
290 pts[0].x = s_arrow_icon[2];
291 pts[0].y = s_arrow_icon[3];
292 pts[1].x = s_arrow_icon[4];
293 pts[1].y = s_arrow_icon[5];
294 pts[2].x = s_arrow_icon[6];
295 pts[2].y = s_arrow_icon[7];
296 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
297
298 // 2
299 pts[0].x = s_arrow_icon[0];
300 pts[0].y = -s_arrow_icon[1];
301 pts[1].x = s_arrow_icon[2];
302 pts[1].y = -s_arrow_icon[3];
303 pts[2].x = s_arrow_icon[6];
304 pts[2].y = -s_arrow_icon[7];
305 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
306
307 // 3
308 pts[0].x = s_arrow_icon[2];
309 pts[0].y = -s_arrow_icon[3];
310 pts[1].x = s_arrow_icon[4];
311 pts[1].y = -s_arrow_icon[5];
312 pts[2].x = s_arrow_icon[6];
313 pts[2].y = -s_arrow_icon[7];
314 dc.DrawPolygon(3, pts, xb, yb, icon_scale_factor, theta);
315
316
317#endif
318}
319
320void RouteGui::DrawPointWhich(ocpnDC &dc, ChartCanvas *canvas, int iPoint,
321 wxPoint *rpn) {
322 if (iPoint <= m_route.GetnPoints())
323 RoutePointGui(*m_route.GetPoint(iPoint)).Draw(dc, canvas, rpn);
324}
325
326void RouteGui::DrawSegment(ocpnDC &dc, ChartCanvas *canvas, wxPoint *rp1,
327 wxPoint *rp2, ViewPort &vp, bool bdraw_arrow) {
328 if (m_route.m_bRtIsSelected)
329 dc.SetPen(*g_pRouteMan->GetSelectedRoutePen());
330 else if (m_route.m_bRtIsActive)
331 dc.SetPen(*g_pRouteMan->GetActiveRoutePen());
332 else
333 dc.SetPen(*g_pRouteMan->GetRoutePen());
334
335 RenderSegment(dc, rp1->x, rp1->y, rp2->x, rp2->y, vp, bdraw_arrow);
336}
337
338void RouteGui::DrawGL(ViewPort &vp, ChartCanvas *canvas, ocpnDC &dc) {
339#ifdef ocpnUSE_GL
340 if (m_route.pRoutePointList->empty()) return;
341
342 if (!vp.GetBBox().IntersectOut(m_route.GetBBox()) && m_route.m_bVisible)
343 DrawGLRouteLines(vp, canvas, dc);
344
345 /* Route points */
346 for (wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst(); node;
347 node = node->GetNext()) {
348 RoutePoint *prp = node->GetData();
349 // Inflate the bounding box a bit to ensure full drawing in accelerated pan
350 // mode.
351 // TODO this is a little extravagant, assumming a mark is always a large
352 // fixed lat/lon extent.
353 // Maybe better to use the mark's drawn box, once it is known.
354 if (vp.GetBBox().ContainsMarge(prp->m_lat, prp->m_lon, .5)) {
355 if (m_route.m_bVisible || prp->IsShared()) RoutePointGui(*prp).DrawGL(vp, canvas, dc);
356 }
357 }
358#endif
359}
360
361void RouteGui::DrawGLRouteLines(ViewPort &vp, ChartCanvas *canvas, ocpnDC &dc) {
362#ifdef ocpnUSE_GL
363 // Hiliting first
364 // Being special case to draw something for a 1 point route....
365 if (m_route.m_hiliteWidth) {
366 wxColour y = GetGlobalColor(_T ( "YELO1" ));
367 wxColour hilt(y.Red(), y.Green(), y.Blue(), 128);
368
369 wxPen HiPen(hilt, m_route.m_hiliteWidth, wxPENSTYLE_SOLID);
370
371 dc.SetPen(HiPen);
372
373 DrawGLLines(vp, &dc, canvas);
374 }
375
376 /* determine color and width */
377 wxColour col;
378
379 int width = g_pRouteMan->GetRoutePen()->GetWidth(); // g_route_line_width;
380 if (m_route.m_width != wxPENSTYLE_INVALID) width = m_route.m_width;
381
382 if (m_route.m_bRtIsActive) {
383 col = g_pRouteMan->GetActiveRoutePen()->GetColour();
384 } else if (m_route.m_bRtIsSelected) {
385 col = g_pRouteMan->GetSelectedRoutePen()->GetColour();
386 } else {
387 if (m_route.m_Colour == wxEmptyString) {
388 col = g_pRouteMan->GetRoutePen()->GetColour();
389 } else {
390 for (unsigned int i = 0; i < sizeof(::GpxxColorNames) / sizeof(wxString);
391 i++) {
392 if (m_route.m_Colour == ::GpxxColorNames[i]) {
393 col = ::GpxxColors[i];
394 break;
395 }
396 }
397 }
398 }
399
400 wxPenStyle style = wxPENSTYLE_SOLID;
401 if (m_route.m_style != wxPENSTYLE_INVALID) style = m_route.m_style;
402 dc.SetPen(*wxThePenList->FindOrCreatePen(col, width, style));
403 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(col, wxBRUSHSTYLE_SOLID));
404
405 glLineWidth(wxMax(g_GLMinSymbolLineWidth, width));
406
407 dc.SetGLStipple();
408
409 DrawGLLines(vp, &dc, canvas);
410
411 glDisable(GL_LINE_STIPPLE);
412
413 /* direction arrows.. could probably be further optimized for opengl */
414 dc.SetPen(*wxThePenList->FindOrCreatePen(col, 1, wxPENSTYLE_SOLID));
415
416 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
417 wxPoint rpt1, rpt2;
418 while (node) {
419 RoutePoint *prp = node->GetData();
420 canvas->GetCanvasPointPix(prp->m_lat, prp->m_lon, &rpt2);
421 if (node != m_route.pRoutePointList->GetFirst()) {
422 if (!prp->m_bIsActive || !g_bAllowShipToActive)
423 RenderSegmentArrowsGL(dc, rpt1.x, rpt1.y, rpt2.x, rpt2.y, vp);
424 }
425 rpt1 = rpt2;
426 node = node->GetNext();
427 }
428#endif
429}
430
431void RouteGui::DrawGLLines(ViewPort &vp, ocpnDC *dc, ChartCanvas *canvas) {
432#ifdef ocpnUSE_GL
433 float pix_full_circle =
434 WGS84_semimajor_axis_meters * mercator_k0 * 2 * PI * vp.view_scale_ppm;
435
436 bool r1valid = false;
437 wxPoint2DDouble r1;
438 wxPoint2DDouble lastpoint;
439
440 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
441 RoutePoint *prp2 = node->GetData();
442 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &lastpoint);
443
444 // single point.. make sure it shows up for highlighting
445 if (m_route.GetnPoints() == 1 && dc) {
446 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &r1);
447 dc->DrawLine(r1.m_x, r1.m_y, r1.m_x + 2, r1.m_y + 2);
448 return;
449 }
450
451 // Handle offscreen points
452 LLBBox bbox = vp.GetBBox();
453
454 // dc is passed for thicker highlighted lines (performance not very important)
455
456 for (node = node->GetNext(); node; node = node->GetNext()) {
457 RoutePoint *prp1 = prp2;
458 prp2 = node->GetData();
459
460 // Provisional, to properly set status of last point in route
461 prp2->m_pos_on_screen = false;
462 {
463 wxPoint2DDouble r2;
464 canvas->GetDoubleCanvasPointPix(prp2->m_lat, prp2->m_lon, &r2);
465 if (std::isnan(r2.m_x)) {
466 r1valid = false;
467 continue;
468 }
469
470 lastpoint = r2; // For active track segment to ownship
471
472 // don't need to perform calculations or render segment
473 // if both points are past any edge of the vp
474 // TODO: use these optimizations for dc mode
475 bool lat1l = prp1->m_lat < bbox.GetMinLat(),
476 lat2l = prp2->m_lat < bbox.GetMinLat();
477 bool lat1r = prp1->m_lat > bbox.GetMaxLat(),
478 lat2r = prp2->m_lat > bbox.GetMaxLat();
479 if ((lat1l && lat2l) || (lat1r && lat2r)) {
480 r1valid = false;
481 prp1->m_pos_on_screen = false;
482 continue;
483 }
484
485 bool lon1l, lon1r, lon2l, lon2r;
486 TestLongitude(prp1->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon1l,
487 lon1r);
488 TestLongitude(prp2->m_lon, bbox.GetMinLon(), bbox.GetMaxLon(), lon2l,
489 lon2r);
490 if ((lon1l && lon2l) || (lon1r && lon2r)) {
491 r1valid = false;
492 prp1->m_pos_on_screen = false;
493 continue;
494 }
495
496 if (!r1valid) {
497 canvas->GetDoubleCanvasPointPix(prp1->m_lat, prp1->m_lon, &r1);
498 if (std::isnan(r1.m_x)) continue;
499 }
500
501 // we must decide which way to go in longitude
502 // for projections which wrap, in this case, we will render two lines
503 // (one may often be off screen which would be nice to fix but complicate
504 // things here anyway, in some cases both points are on screen, but the
505 // route wraps to either side so two lines are needed to draw this
506 // properly
507
508 double adder = 0;
509 if ((vp.m_projection_type == PROJECTION_MERCATOR ||
510 vp.m_projection_type == PROJECTION_EQUIRECTANGULAR)) {
511 float olon = vp.clon > 0 ? vp.clon - 180 : vp.clon + 180;
512
513 if (prp1->m_lon < prp2->m_lon) {
514 if (prp2->m_lon - prp1->m_lon < 180) {
515 if (olon > prp1->m_lon && olon < prp2->m_lon)
516 adder = pix_full_circle;
517 } else if (olon < prp1->m_lon || olon > prp2->m_lon)
518 adder = -pix_full_circle;
519 } else if (prp1->m_lon - prp2->m_lon < 180) {
520 if (olon < prp1->m_lon && olon > prp2->m_lon)
521 adder = -pix_full_circle;
522 } else if (olon > prp1->m_lon || olon < prp2->m_lon)
523 adder = pix_full_circle;
524 }
525
526 if (dc)
527 if (adder) {
528 float adderc = cos(vp.rotation) * adder,
529 adders = sin(vp.rotation) * adder;
530 dc->DrawLine(r1.m_x, r1.m_y, r2.m_x + adderc, r2.m_y + adders);
531 dc->DrawLine(r1.m_x - adderc, r1.m_y - adders, r2.m_x, r2.m_y);
532 } else
533 dc->DrawLine(r1.m_x, r1.m_y, r2.m_x, r2.m_y);
534 else {
535 }
536
537 r1 = r2;
538 r1valid = true;
539 }
540 }
541
542
543#endif
544}
545
546void RouteGui::CalculateDCRect(wxDC &dc_route, ChartCanvas *canvas,
547 wxRect *prect) {
548 dc_route.ResetBoundingBox();
549 dc_route.DestroyClippingRegion();
550
551 wxRect update_rect;
552
553 // Draw the route in skeleton form on the dc
554 // That is, draw only the route points, assuming that the segements will
555 // always be fully contained within the resulting rectangle.
556 // Can we prove this?
557 if (m_route.m_bVisible) {
558 wxRoutePointListNode *node = m_route.pRoutePointList->GetFirst();
559 while (node) {
560 RoutePoint *prp2 = node->GetData();
561 bool blink_save = prp2->m_bBlink;
562 prp2->m_bBlink = false;
563 ocpnDC odc_route(dc_route);
564 RoutePointGui(*prp2).Draw(odc_route, canvas, NULL);
565 prp2->m_bBlink = blink_save;
566
567 wxRect r = prp2->CurrentRect_in_DC;
568 // allow for large hilite circles at segment ends
569 r.Inflate(m_route.m_hiliteWidth, m_route.m_hiliteWidth);
570
571 update_rect.Union(r);
572 node = node->GetNext();
573 }
574 }
575
576 *prect = update_rect;
577}
578
579
580int RouteGui::SendToGPS(const wxString& com_name, bool bsend_waypoints,
581 SendToGpsDlg* dialog) {
582 int result = 0;
583
584 ::wxBeginBusyCursor();
585 result = SendRouteToGPS_N0183(&m_route, com_name, bsend_waypoints);
586 ::wxEndBusyCursor();
587
588 wxString msg;
589 if (0 == result)
590 msg = _("Route Transmitted.");
591 else {
592 if (result == ERR_GARMIN_INITIALIZE)
593 msg = _("Error on Route Upload. Garmin GPS not connected");
594 else
595 msg = _("Error on Route Upload. Please check logfiles...");
596
597 OCPNMessageBox(NULL, msg, _("OpenCPN Info"), wxOK | wxICON_INFORMATION);
598 }
599 return (result == 0);
600}
Route "Send to GPS..." Dialog Definition.
Definition: SendToGpsDlg.h:56
Definition: ocpndc.h:55