OpenCPN Partial API docs
ocpn_frame.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: OpenCPN Main wxWidgets Program
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  **************************************************************************/
25 #include "config.h"
26 
27 #ifdef __MINGW32__
28 #undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
29 #include <windows.h>
30 #endif
31 
32 #include <wx/wxprec.h>
33 
34 #ifndef WX_PRECOMP
35 #include <wx/wx.h>
36 #endif // precompiled headers
37 
38 #ifdef __WXMSW__
39 //#include "c:\\Program Files\\visual leak detector\\include\\vld.h"
40 #endif
41 
42 
43 #ifdef __WXMSW__
44 #include <math.h>
45 #include <psapi.h>
46 #include <stdlib.h>
47 #include <time.h>
48 #endif
49 
50 #ifdef OCPN_HAVE_X11
51 #include <X11/Xatom.h>
52 #include <X11/Xlib.h>
53 #endif
54 
55 #include <wx/stdpaths.h>
56 #include <wx/tokenzr.h>
57 #include <wx/display.h>
58 
59 #include "model/ais_decoder.h"
60 #include "model/ais_state_vars.h"
61 #include "model/ais_target_data.h"
62 #include "model/cmdline.h"
63 #include "model/comm_drv_factory.h" //FIXME(dave) this one goes away
64 #include "model/comm_drv_registry.h"
65 #include "model/comm_n0183_output.h"
66 #include "model/comm_navmsg_bus.h"
67 #include "model/comm_vars.h"
68 #include "model/config_vars.h"
69 #include "model/cutil.h"
70 #include "model/georef.h"
71 #include "model/gui.h"
72 #include "model/idents.h"
73 #include "model/local_api.h"
74 #include "model/logger.h"
75 #include "model/multiplexer.h"
76 #include "model/nav_object_database.h"
77 #include "model/navutil_base.h"
78 #include "model/own_ship.h"
79 #include "model/plugin_loader.h"
80 #include "model/routeman.h"
81 #include "model/select.h"
82 #include "model/sys_events.h"
83 #include "model/track.h"
84 
85 #include "AboutFrameImpl.h"
86 #include "about.h"
87 #include "ais.h"
88 #include "ais_info_gui.h"
89 #include "AISTargetAlertDialog.h"
90 #include "AISTargetListDialog.h"
91 #include "AISTargetQueryDialog.h"
92 #include "CanvasConfig.h"
93 #include "chartbase.h"
94 #include "chart_ctx_factory.h"
95 #include "chartdb.h"
96 #include "chcanv.h"
97 #include "cm93.h"
98 #include "color_handler.h"
99 #include "compass.h"
100 #include "concanv.h"
101 #include "ConfigMgr.h"
102 #include "displays.h"
103 #include "dychart.h"
104 #include "FontMgr.h"
105 #include "glChartCanvas.h"
106 #include "GoToPositionDialog.h"
107 #include "gui_lib.h"
108 #include "iENCToolbar.h"
109 #include "Layer.h"
110 #include "load_errors_dlg.h"
111 #include "MarkInfo.h"
112 #include "MUIBar.h"
113 #include "N2KParser.h"
114 #include "navutil.h"
115 #include "NMEALogWindow.h"
116 #include "ocpn_app.h"
117 #include "OCPN_AUIManager.h"
118 #include "ocpn_frame.h"
119 #include "OCPNPlatform.h"
120 #include "OCPN_Sound.h"
121 #include "options.h"
122 #include "pluginmanager.h"
123 #include "routemanagerdialog.h"
124 #include "routeman_gui.h"
125 #include "route_point_gui.h"
126 #include "RoutePropDlgImpl.h"
127 #include "s52plib.h"
128 #include "s57chart.h"
129 #include "S57QueryDialog.h"
130 #include "SystemCmdSound.h"
131 #include "tcmgr.h"
132 #include "timers.h"
133 #include "toolbar.h"
134 #include "TrackPropDlg.h"
135 #include "waypointman_gui.h"
136 #include "CanvasOptions.h"
137 #include "udev_rule_mgr.h"
138 
139 #ifdef __ANDROID__
140 #include "androidUTIL.h"
141 #endif
142 
143 //------------------------------------------------------------------------------
144 // Fwd Declarations
145 //------------------------------------------------------------------------------
146 WX_DEFINE_ARRAY_PTR(ChartCanvas *, arrayofCanvasPtr);
147 
148 //------------------------------------------------------------------------------
149 // Static variable definition
150 //------------------------------------------------------------------------------
151 //
152 extern OCPN_AUIManager *g_pauimgr;
153 extern MyConfig *pConfig;
154 extern arrayofCanvasPtr g_canvasArray;
155 extern MyFrame *gFrame;
156 extern AISTargetListDialog *g_pAISTargetList;
157 extern AISTargetQueryDialog *g_pais_query_dialog_active;
158 extern ConsoleCanvas *console;
159 extern RouteManagerDialog *pRouteManagerDialog;
160 extern Routeman *g_pRouteMan;
161 extern MarkInfoDlg *g_pMarkInfoDialog;
162 extern RoutePropDlgImpl *pRoutePropDialog;
163 extern TrackPropDlg *pTrackPropDialog;
164 extern GoToPositionDialog *pGoToPositionDialog;
165 extern CM93OffsetDialog *g_pCM93OffsetDialog;
166 extern S57QueryDialog *g_pObjectQueryDialog;
167 extern about *g_pAboutDlgLegacy;
168 extern AboutFrameImpl *g_pAboutDlg;
169 
170 extern double vLat, vLon;
171 extern wxString g_locale;
172 extern ColorScheme global_color_scheme;
173 extern options *g_pOptions;
174 extern options *g_options;
175 
176 #ifdef ocpnUSE_GL
177 GLenum g_texture_rectangle_format;
178 #endif
179 
180 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
181 extern wxLocale *plocale_def_lang;
182 #endif
183 
184 extern OCPNPlatform *g_Platform;
185 extern BasePlatform
186  *g_BasePlatform; // points to g_platform, handles brain-dead MS linker.
187 
188 extern s52plib *ps52plib;
189 extern ocpnFloatingToolbarDialog *g_MainToolbar;
190 extern PlugInManager *g_pi_manager;
191 
192 extern bool g_b_legacy_input_filter_behaviour;
193 extern bool g_bTrackActive;
194 extern ocpnStyle::StyleManager *g_StyleManager;
195 extern bool g_bmasterToolbarFull;
196 extern int g_nAutoHideToolbar;
197 extern bool g_bAutoHideToolbar;
198 extern bool g_bshowToolbar;
199 extern int g_maintoolbar_x;
200 extern int g_maintoolbar_y;
201 extern wxString g_toolbarConfig;
202 extern float g_toolbar_scalefactor;
203 extern float g_compass_scalefactor;
204 extern bool g_bShowMenuBar;
205 extern bool g_bShowCompassWin;
206 
207 extern bool g_benable_rotate;
208 extern int g_GUIScaleFactor;
209 extern int g_ChartScaleFactor;
210 extern int g_last_ChartScaleFactor;
211 extern int g_ShipScaleFactor;
212 extern float g_ShipScaleFactorExp;
213 extern int g_ENCTextScaleFactor;
214 
215 extern bool g_bShowTide;
216 extern bool g_bShowCurrent;
217 extern bool g_bUIexpert;
218 extern RouteList *pRouteList;
219 extern wxString g_default_wp_icon;
220 extern std::vector<std::string> TideCurrentDataSet;
221 extern wxString g_TCData_Dir;
222 extern TCMgr *ptcmgr;
223 extern char nmea_tick_chars[];
224 extern double AnchorPointMinDist;
225 extern bool AnchorAlertOn1, AnchorAlertOn2;
226 extern wxString g_AW1GUID;
227 extern wxString g_AW2GUID;
228 extern bool g_bCruising;
229 extern double g_COGAvg;
230 extern int g_COGAvgSec;
231 extern ActiveTrack *g_pActiveTrack;
232 extern std::vector<Track *> g_TrackList;
233 extern double gQueryVar;
234 extern wxPrintData *g_printData;
235 extern wxPageSetupData *g_pageSetupData;
236 extern int g_ChartUpdatePeriod;
237 extern int g_SkewCompUpdatePeriod;
238 extern bool g_bCourseUp;
239 extern bool g_bLookAhead;
240 extern bool g_bskew_comp;
241 extern bool g_bPauseTest;
242 extern bool g_bSleep;
243 extern bool g_bPlayShipsBells;
244 extern wxDateTime g_loglast_time;
245 extern int g_nAWDefault;
246 extern int g_nAWMax;
247 extern bool g_bDeferredStartTrack;
248 extern bool bDBUpdateInProgress;
249 extern int quitflag;
250 extern int g_tick;
251 extern ChartDB *ChartData;
252 extern bool g_boptionsactive;
253 extern bool g_bDeferredInitDone;
254 extern int options_lastPage;
255 extern int options_subpage;
256 extern bool b_reloadForPlugins;
257 extern ChartCanvas *g_focusCanvas;
258 extern int g_NeedDBUpdate;
259 extern bool g_bFullscreen;
260 extern wxString gWorldMapLocation, gDefaultWorldMapLocation;
261 extern ChartGroupArray *g_pGroupArray;
262 extern bool g_bEnableZoomToCursor;
263 extern double g_display_size_mm;
264 extern std::vector<size_t> g_config_display_size_mm;
265 extern wxString ChartListFileName;
266 extern bool g_bFullscreenToolbar;
267 extern arrayofCanvasPtr g_canvasArray;
268 extern wxString g_lastAppliedTemplateGUID;
269 extern wxPoint options_lastWindowPos;
270 extern wxSize options_lastWindowSize;
271 extern unsigned int g_canvasConfig;
272 extern bool g_bFullScreenQuilt;
273 extern bool g_bQuiltEnable;
274 extern wxString *pInit_Chart_Dir;
275 extern bool g_bShowOutlines;
276 extern bool g_bTempShowMenuBar;
277 extern bool g_bShowStatusBar;
278 extern bool g_FlushNavobjChanges;
279 extern int g_FlushNavobjChangesTimeout;
280 extern bool g_bShowChartBar;
281 extern double g_plus_minus_zoom_factor;
282 extern int g_nframewin_x;
283 extern int g_nframewin_y;
284 extern int g_nframewin_posx;
285 extern int g_nframewin_posy;
286 extern bool g_bframemax;
287 extern LayerList *pLayerList;
288 extern bool g_bAutoAnchorMark;
289 extern wxDateTime g_start_time;
290 extern bool g_bcompression_wait;
291 extern bool g_bquiting;
292 extern bool b_inCloseWindow;
293 extern bool b_inCompressAllCharts;
294 extern long g_maintoolbar_orient;
295 extern wxAuiDefaultDockArt *g_pauidockart;
296 extern int g_click_stop;
297 extern wxString g_CmdSoundString;
298 extern std::vector<OcpnSound *> bells_sound;
299 extern char bells_sound_file_name[2][12];
300 extern int g_sticky_chart;
301 extern int g_sticky_projection;
302 extern wxArrayPtrVoid *UserColourHashTableArray;
303 extern wxColorHashMap *pcurrent_user_color_hash;
304 
305 // probable move to ocpn_app
306 extern bool g_bfilter_cogsog;
307 extern int g_COGFilterSec;
308 extern int g_SOGFilterSec;
309 extern bool g_own_ship_sog_cog_calc;
310 extern int g_own_ship_sog_cog_calc_damp_sec;
311 extern bool g_bHasHwClock;
312 extern bool s_bSetSystemTime;
313 extern bool bVelocityValid;
314 extern int gHDx_Watchdog;
315 extern AisInfoGui *g_pAISGUI;
316 
317 extern bool g_bUseGLL;
318 extern int g_MemFootMB;
319 extern Multiplexer *g_pMUX;
320 extern int g_memUsed;
321 extern int g_chart_zoom_modifier_vector;
322 extern bool g_config_display_size_manual;
323 extern bool g_PrintingInProgress;
324 
325 #ifdef __WXMSW__
326 // System color control support
327 
328 typedef DWORD(WINAPI *SetSysColors_t)(DWORD, DWORD *, DWORD *);
329 typedef DWORD(WINAPI *GetSysColor_t)(DWORD);
330 
331 SetSysColors_t pSetSysColors;
332 GetSysColor_t pGetSysColor;
333 
334 void SaveSystemColors(void);
335 void RestoreSystemColors(void);
336 
337 DWORD color_3dface;
338 DWORD color_3dhilite;
339 DWORD color_3dshadow;
340 DWORD color_3ddkshadow;
341 DWORD color_3dlight;
342 DWORD color_activecaption;
343 DWORD color_gradientactivecaption;
344 DWORD color_captiontext;
345 DWORD color_windowframe;
346 DWORD color_inactiveborder;
347 
348 #endif
349 
350 
351 #ifdef __MSVC__
352 #define _CRTDBG_MAP_ALLOC
353 #include <stdlib.h>
354 #include <crtdbg.h>
355 #define DEBUG_NEW new (_NORMAL_BLOCK, __FILE__, __LINE__)
356 #define new DEBUG_NEW
357 #endif
358 
359 #if !defined(NAN)
360 static const long long lNaN = 0xfff8000000000000;
361 #define NAN (*(double *)&lNaN)
362 #endif
363 
364 static wxArrayPtrVoid *UserColorTableArray = 0;
365 
366 // Some static helpers
367 void appendOSDirSlash(wxString *pString);
368 
369 void InitializeUserColors(void);
370 void DeInitializeUserColors(void);
371 void SetSystemColors(ColorScheme cs);
372 
373 static bool LoadAllPlugIns(bool load_enabled) {
374  AbstractPlatform::ShowBusySpinner();
375  bool b = PluginLoader::getInstance()->LoadAllPlugIns(load_enabled);
376  AbstractPlatform::HideBusySpinner();
377  return b;
378 }
379 
380 //------------------------------------------------------------------------------
381 // PNG Icon resources
382 //------------------------------------------------------------------------------
383 
384 #if defined(__WXGTK__) || defined(__WXQT__)
385 #include "bitmaps/opencpn.xpm"
386 #endif
387 
388 //------------------------------------------------------------------------------
389 // Local constants
390 //------------------------------------------------------------------------------
391 // enum {
392 // ID_PIANO_DISABLE_QUILT_CHART = 32000, ID_PIANO_ENABLE_QUILT_CHART
393 // };
394 
395 //------------------------------------------------------------------------------
396 // Fwd Refs
397 //------------------------------------------------------------------------------
398 
399 iENCToolbar *g_iENCToolbar;
400 int g_iENCToolbarPosX;
401 int g_iENCToolbarPosY;
402 
403 void BuildiENCToolbar(bool bnew) {
404  if (g_bInlandEcdis) {
405  if (bnew) {
406  if (g_iENCToolbar) {
407  wxPoint locn = g_iENCToolbar->GetToolbarPosition();
408  wxPoint tbp_incanvas = locn; //gFrame->GetPrimaryCanvas()->ScreenToClient(locn);
409 
410  g_iENCToolbarPosY = tbp_incanvas.y;
411  g_iENCToolbarPosX = tbp_incanvas.x;
412 
413  delete g_iENCToolbar;
414  g_iENCToolbar = 0;
415  }
416  }
417 
418  if (!g_iENCToolbar) {
419  wxPoint posn(g_iENCToolbarPosX, g_iENCToolbarPosY);
420 
421  // Overlapping main toolbar?
422  if (g_MainToolbar) {
423  if ((g_iENCToolbarPosY > g_maintoolbar_y) &&
424  (g_iENCToolbarPosY < g_maintoolbar_y + g_MainToolbar->GetToolSize().y))
425  g_iENCToolbarPosY = -1; // force a reposition
426  }
427 
428  if ((g_iENCToolbarPosX < 0) || (g_iENCToolbarPosY < 0)) {
429  posn.x = 0;
430  posn.y = 100;
431 
432  if (g_MainToolbar)
433  posn = wxPoint(g_maintoolbar_x + g_MainToolbar->GetToolbarSize().x + 4,
434  g_maintoolbar_y);
435  }
436 
437  double tool_scale_factor =
438  g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor);
439 
440  g_iENCToolbar =
441  new iENCToolbar(gFrame, posn, wxTB_HORIZONTAL, tool_scale_factor);
442  g_iENCToolbar->SetColorScheme(global_color_scheme);
443  g_iENCToolbar->EnableSubmerge(false);
444  }
445  } else {
446  delete g_iENCToolbar;
447  g_iENCToolbar = NULL;
448  }
449 }
450 
451 int ShowNavWarning() {
452  wxString msg0(
453  _("\n\
454 OpenCPN is distributed in the hope that it will be useful, \
455 but WITHOUT ANY WARRANTY; without even the implied \
456 warranty of MERCHANTABILITY or FITNESS FOR A \
457 PARTICULAR PURPOSE.\n\n\
458 See the GNU General Public License for more details.\n\n\
459 OpenCPN must only be used in conjunction with approved \
460 paper charts and traditional methods of navigation.\n\n\
461 DO NOT rely upon OpenCPN for safety of life or property.\n\n\
462 Please click \"OK\" to agree and proceed, \"Cancel\" to quit.\n"));
463 
464  wxString vs = wxString::Format(wxT(" .. Version %s"), VERSION_FULL);
465 
466 #ifdef __ANDROID__
467  androidShowDisclaimer(_("OpenCPN for Android") + vs, msg0);
468  return true;
469 #else
470  wxColor fg = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
471  wxString msg1;
472  msg1.Printf(_T("<html><body><font color=#%02x%02x%02x><hr />"), fg.Red(),
473  fg.Green(), fg.Blue());
474 
475  for (unsigned int i = 0; i < msg0.Length(); i++) {
476  if (msg0[i] == '\n')
477  msg1 += _T("<br>");
478  else
479  msg1 += msg0[i];
480  }
481 
482  msg1 << _T("<hr /></font></body></html>");
483 
485  gFrame, msg1, _("Welcome to OpenCPN") + vs, -1, wxCANCEL | wxOK);
486 
487  infoDlg.ShowModal();
488 
489  return (infoDlg.GetReturnCode());
490 #endif
491 }
492 
493 bool isSingleChart(ChartBase *chart) {
494  if (chart == nullptr) return false;
495 
496  // ..For each canvas...
497  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
498  ChartCanvas *cc = g_canvasArray.Item(i);
499  if (cc && cc->m_singleChart == chart) {
500  return true;
501  }
502  }
503  return false;
504 }
505 
506 #if defined(__WXGTK__) && defined(OCPN_HAVE_X11)
507 
508 // Note: use XFree to free this pointer. Use unique_ptr in the future.
509 static char *get_X11_property(Display *disp, Window win, Atom xa_prop_type,
510  const char *prop_name) {
511  Atom xa_prop_name;
512  Atom xa_ret_type;
513  int ret_format;
514  unsigned long ret_nitems;
515  unsigned long ret_bytes_after;
516  unsigned char *ret_prop;
517 
518  xa_prop_name = XInternAtom(disp, prop_name, False);
519 
520  // For XGetWindowProperty source see
521  // https://github.com/mirror/libX11/blob/master/src/GetProp.c#L107
522  // it is quite tricky. Some notes.
523  // + Results are already NULL terminated.
524  // + 32 as a ret_format means sizeof(long) in the API...
525  // + but as xlib does the null termination we can just ignore the sizes.
526  if (XGetWindowProperty(disp, win, xa_prop_name, 0, 1024, False, xa_prop_type,
527  &xa_ret_type, &ret_format, &ret_nitems,
528  &ret_bytes_after, &ret_prop) != Success)
529  return NULL;
530 
531  if (xa_ret_type != xa_prop_type) {
532  XFree(ret_prop);
533  return NULL;
534  }
535  return (char *)ret_prop;
536 }
537 #endif
538 
539 // Determine if a transparent toolbar is possible under linux with opengl
540 static bool isTransparentToolbarInOpenGLOK(void) {
541 #ifdef __WXOSX__
542  return true;
543 #else
544  bool status = false;
545 #ifndef __WXQT__
546 #ifdef OCPN_HAVE_X11
547  if (!g_bdisable_opengl) {
548  Display *disp = XOpenDisplay(NULL);
549  Window *sup_window;
550  if ((sup_window = (Window *)get_X11_property(disp, DefaultRootWindow(disp),
551  XA_WINDOW,
552  "_NET_SUPPORTING_WM_CHECK")) ||
553  (sup_window = (Window *)get_X11_property(disp, DefaultRootWindow(disp),
554  XA_CARDINAL,
555  "_WIN_SUPPORTING_WM_CHECK"))) {
556  /* WM_NAME */
557  char *wm_name;
558  if ((wm_name = get_X11_property(disp, *sup_window,
559  XInternAtom(disp, "UTF8_STRING", False),
560  "_NET_WM_NAME")) ||
561  (wm_name = get_X11_property(disp, *sup_window, XA_STRING,
562  "_NET_WM_NAME"))) {
563  // we know it works in xfce4, add other checks as we can validate them
564  if (strstr(wm_name, "Xfwm4") || strstr(wm_name, "Compiz"))
565  status = true;
566 
567  XFree(wm_name);
568  }
569  XFree(sup_window);
570  }
571  XCloseDisplay(disp);
572  }
573 #endif
574 #endif
575  return status;
576 #endif
577 }
578 
579 //------------------------------------------------------------------------------
580 // MyFrame
581 //------------------------------------------------------------------------------
582 
583 // Frame implementation
584 // NOLINTBEGIN
585 wxDEFINE_EVENT(BELLS_PLAYED_EVTYPE, wxCommandEvent);
586 
587 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
588 EVT_CLOSE(MyFrame::OnCloseWindow)
589 EVT_MENU(wxID_EXIT, MyFrame::OnExit)
590 EVT_SIZE(MyFrame::OnSize)
591 EVT_MOVE(MyFrame::OnMove)
592 EVT_ICONIZE(MyFrame::OnIconize)
593 EVT_MENU(-1, MyFrame::OnToolLeftClick)
594 EVT_TIMER(INIT_TIMER, MyFrame::OnInitTimer)
595 EVT_TIMER(FRAME_TIMER_1, MyFrame::OnFrameTimer1)
596 EVT_TIMER(FRAME_TC_TIMER, MyFrame::OnFrameTCTimer)
597 EVT_TIMER(FRAME_COG_TIMER, MyFrame::OnFrameCOGTimer)
598 EVT_TIMER(MEMORY_FOOTPRINT_TIMER, MyFrame::OnMemFootTimer)
599 EVT_MAXIMIZE(MyFrame::OnMaximize)
600 EVT_COMMAND(wxID_ANY, wxEVT_COMMAND_TOOL_RCLICKED,
601  MyFrame::RequestNewToolbarArgEvent)
602 EVT_ERASE_BACKGROUND(MyFrame::OnEraseBackground)
603 // EVT_TIMER(RESIZE_TIMER, MyFrame::OnResizeTimer)
604 EVT_TIMER(RECAPTURE_TIMER, MyFrame::OnRecaptureTimer)
605 EVT_TIMER(TOOLBAR_ANIMATE_TIMER, MyFrame::OnToolbarAnimateTimer)
606 EVT_COMMAND(wxID_ANY, BELLS_PLAYED_EVTYPE, MyFrame::OnBellsFinished)
607 
608 #ifdef wxHAS_POWER_EVENTS
609 EVT_POWER_SUSPENDING(MyFrame::OnSuspending)
610 EVT_POWER_SUSPENDED(MyFrame::OnSuspended)
611 EVT_POWER_SUSPEND_CANCEL(MyFrame::OnSuspendCancel)
612 EVT_POWER_RESUME(MyFrame::OnResume)
613 #endif // wxHAS_POWER_EVENTS
614 
615 END_EVENT_TABLE()
616 
617 // NOLINTEND
618 
619 /*
620  * Direct callback from completed sound, possibly in an interrupt
621  * context. Just post an event to be processed in main thread.
622  */
623 static void onBellsFinishedCB(void *ptr) {
624  auto framePtr = static_cast<MyFrame *>(ptr);
625  if (framePtr) {
626  wxCommandEvent ev(BELLS_PLAYED_EVTYPE);
627  wxPostEvent(framePtr, ev);
628  }
629 }
630 
631 // My frame constructor
632 MyFrame::MyFrame(wxFrame *frame, const wxString &title, const wxPoint &pos,
633  const wxSize &size, long style)
634  : wxFrame(frame, -1, title, pos, size, style, kTopLevelWindowName),
635  comm_overflow_dlg(this) {
636 
637  g_current_monitor = wxDisplay::GetFromWindow(this);
638 #ifdef __WXOSX__
639  // On retina displays there is a difference between the physical size of the OpenGL canvas and the DIP
640  // This is not observed anywhere else so far, so g_current_monitor_dip_px_ratio cna be kept 1.0 everywhere else
641  if (g_bopengl) {
642  g_current_monitor_dip_px_ratio = g_monitor_info[g_current_monitor].width_px / g_monitor_info[g_current_monitor].width;
643  }
644 #endif
645  m_last_track_rotation_ts = 0;
646  m_ulLastNMEATicktime = 0;
647 
648  m_pStatusBar = NULL;
649  m_StatusBarFieldCount = g_Platform->GetStatusBarFieldCount();
650 
651  m_pMenuBar = NULL;
652  g_options = NULL;
653  m_load_errors_dlg_ctrl = std::make_unique<LoadErrorsDlgCtrl>(this);
654 
655  // Redirect the initialization timer to this frame
656  InitTimer.SetOwner(this, INIT_TIMER);
657  m_iInitCount = 0;
658  m_initializing = false;
659 
660  // Redirect the global heartbeat timer to this frame
661  FrameTimer1.SetOwner(this, FRAME_TIMER_1);
662 
663  // Redirect the Tide/Current update timer to this frame
664  FrameTCTimer.SetOwner(this, FRAME_TC_TIMER);
665 
666  // Redirect the COG Averager timer to this frame
667  FrameCOGTimer.SetOwner(this, FRAME_COG_TIMER);
668 
669  // Redirect the Memory Footprint Management timer to this frame
670  MemFootTimer.SetOwner(this, MEMORY_FOOTPRINT_TIMER);
671 
672  // Direct the Toolbar Animation timer to this frame
673  ToolbarAnimateTimer.SetOwner(this, TOOLBAR_ANIMATE_TIMER);
674 
675 #ifdef __ANDROID__
676 // m_PrefTimer.SetOwner( this, ANDROID_PREF_TIMER );
677 // Connect( m_PrefTimer.GetId(), wxEVT_TIMER, wxTimerEventHandler(
678 // MyFrame::OnPreferencesResultTimer ), NULL, this );
679 #endif
680 
681  // Set up some assorted member variables
682  m_bTimeIsSet = false;
683  nBlinkerTick = 0;
684 
685  m_bdefer_resize = false;
686 
687  // Clear the NMEA Filter tables
688  for (int i = 0; i < MAX_COGSOG_FILTER_SECONDS; i++) {
689  COGFilterTable[i] = NAN;
690  SOGFilterTable[i] = NAN;
691  }
692  m_last_bGPSValid = false;
693  m_last_bVelocityValid = false;
694 
695  gHdt = NAN;
696  gHdm = NAN;
697  gVar = NAN;
698  gSog = NAN;
699  gCog = NAN;
700 
701  for (int i = 0; i < MAX_COG_AVERAGE_SECONDS; i++) COGTable[i] = NAN;
702 
703  m_fixtime = -1;
704 
705  m_ChartUpdatePeriod = 1; // set the default (1 sec.) period
706  initIXNetSystem();
707 
708  // Establish my children
709  struct MuxLogCallbacks log_callbacks;
710  log_callbacks.log_is_active = []() { return NMEALogWindow::GetInstance().Active(); };
711  log_callbacks.log_message = [](const std::string& s) {
712  NMEALogWindow::GetInstance().Add(s); };
713  g_pMUX = new Multiplexer(log_callbacks, g_b_legacy_input_filter_behaviour);
714 
715  struct AisDecoderCallbacks ais_callbacks;
716  ais_callbacks.confirm_stop_track = []() {
717  int r = OCPNMessageBox(NULL,
718  _("This AIS target has Persistent tracking selected by MMSI properties\n"
719  "A Persistent track recording will therefore be restarted for this target.\n\n"
720  "Do you instead want to stop Persistent tracking for this target?"),
721  _("OpenCPN Info"), wxYES_NO | wxCENTER, 60);
722  return r == wxID_YES;
723  };
724  ais_callbacks.get_target_mmsi = []() {
725  auto alert_dlg_active =
726  dynamic_cast<AISTargetAlertDialog*>(g_pais_alert_dialog_active);
727  assert(alert_dlg_active);
728  return alert_dlg_active->Get_Dialog_MMSI();
729  };
730 
731 
732  g_pAIS = new AisDecoder(ais_callbacks);
733 
734  g_pAISGUI = new AisInfoGui();
735 
736  // Create/connect a dynamic event handler slot
737  wxLogMessage(" **** Connect stuff");
738 
739  b_autofind = false;
740 
741  // Create/connect a dynamic event handler slot for OCPN_MsgEvent(s) coming
742  // from PlugIn system
743  Connect(wxEVT_OCPN_MSG,
744  (wxObjectEventFunction)(wxEventFunction)&MyFrame::OnEvtPlugInMessage);
745 
746  //FIXME (dave)
747  //Connect(wxEVT_OCPN_THREADMSG,
748  // (wxObjectEventFunction)(wxEventFunction)&MyFrame::OnEvtTHREADMSG);
749 
750  // And from the thread SENC creator
751  Connect(wxEVT_OCPN_BUILDSENCTHREAD,
752  (wxObjectEventFunction)(wxEventFunction)&MyFrame::OnSENCEvtThread);
753  // Establish the system icons for the frame.
754 
755 #ifdef __WXMSW__
756  SetIcon(wxICON(
757  0)); // this grabs the first icon in the integrated MSW resource file
758 #endif
759 
760 #if defined(__WXGTK__) || defined(__WXQT__)
761  wxIcon app_icon(opencpn); // This comes from opencpn.xpm inclusion above
762  SetIcon(app_icon);
763 #endif
764 
765 #ifdef __WXMSW__
766 
767  // Establish the entry points in USER32.DLL for system color control
768 
769  wxDynamicLibrary dllUser32(_T("user32.dll"));
770 
771  pSetSysColors = (SetSysColors_t)dllUser32.GetSymbol(wxT("SetSysColors"));
772  pGetSysColor = (GetSysColor_t)dllUser32.GetSymbol(wxT("GetSysColor"));
773 
774  SaveSystemColors();
775 #endif
776 
777  m_next_available_plugin_tool_id = ID_PLUGIN_BASE;
778 
779  g_sticky_chart = -1;
780  g_sticky_projection = -1;
781  m_BellsToPlay = 0;
782 
783  m_resizeTimer.SetOwner(this, RESIZE_TIMER);
784  m_recaptureTimer.SetOwner(this, RECAPTURE_TIMER);
785  m_tick_idx = 0;
786  assert(g_pRouteMan != 0 && "g_pRouteMan not available");
787  m_routes_update_listener.Init(g_pRouteMan->on_routes_update,
788  [&](wxCommandEvent) { Refresh(); });
789 
790 
791 #ifdef __WXOSX__
792  // Enable native fullscreen on macOS
793  EnableFullScreenView();
794 #endif
795 }
796 
797 MyFrame::~MyFrame() {
798  FrameTimer1.Stop();
800 
801  delete ChartData;
802  // delete pCurrentStack;
803 
804  // Free the Route List
805  wxRouteListNode *node = pRouteList->GetFirst();
806 
807  while (node) {
808  Route *pRouteDelete = node->GetData();
809  delete pRouteDelete;
810 
811  node = node->GetNext();
812  }
813  delete pRouteList;
814  pRouteList = NULL;
815 
816  Disconnect(
817  wxEVT_OCPN_MSG,
818  (wxObjectEventFunction)(wxEventFunction)&MyFrame::OnEvtPlugInMessage);
819  //FIXME (dave) Was in some datastream file?
820  //Disconnect(wxEVT_OCPN_THREADMSG,
821  // (wxObjectEventFunction)(wxEventFunction)&MyFrame::OnEvtTHREADMSG);
822 }
823 
824 void MyFrame::OnSENCEvtThread(OCPN_BUILDSENC_ThreadEvent &event) {
825  s57chart *chart;
826  switch (event.type) {
827  case SENC_BUILD_STARTED:
828  // printf("Myframe SENC build started\n");
829  break;
830  case SENC_BUILD_DONE_NOERROR:
831  // printf("Myframe SENC build done no error\n");
832  chart = event.m_ticket->m_chart;
833  if (chart) {
834  chart->PostInit(FULL_INIT, global_color_scheme);
835  // ..For each canvas, force an S52PLIB reconfig...
836  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
837  ChartCanvas *cc = g_canvasArray.Item(i);
838  if (cc) cc->ClearS52PLIBStateHash(); // Force a S52 PLIB re-configure
839  }
840  }
841 
842  ReloadAllVP();
843  delete event.m_ticket;
844  break;
845  case SENC_BUILD_DONE_ERROR:
846  // printf("Myframe SENC build done ERROR\n");
847  break;
848  default:
849  break;
850  }
851 }
852 
853 void MyFrame::RebuildChartDatabase() {
854  bool b_SetInitialPoint = false;
855 
856  // Build the initial chart dir array
857  ArrayOfCDI ChartDirArray;
858  pConfig->LoadChartDirArray(ChartDirArray);
859 
860  if (ChartDirArray.GetCount()) {
861  // Create and Save a new Chart Database based on the hints
862  // given in the config file
863  if(g_NeedDBUpdate == 1) {
864  wxString msg1(
865  _("OpenCPN needs to update the chart database from config file "
866  "entries...."));
867 
868  OCPNMessageDialog mdlg(gFrame, msg1, wxString(_("OpenCPN Info")),
869  wxICON_INFORMATION | wxOK);
870  mdlg.ShowModal();
871  }
872 
873  delete ChartData;
874  ChartData = new ChartDB();
875 
876  wxString line(
877  _("Rebuilding chart database from configuration file entries..."));
878  /* The following 3 strings are embeded in wxProgressDialog but must be
879  * included by xgettext to be localized properly. See
880  * {wxWidgets}src/generic/progdlgg.cpp:190 */
881  wxString dummy1 = _("Elapsed time : ");
882  wxString dummy2 = _("Estimated time : ");
883  wxString dummy3 = _("Remaining time : ");
884  wxGenericProgressDialog *pprog = new wxGenericProgressDialog(
885  _("OpenCPN Chart Update"), line, 100, NULL,
886  wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
887  wxPD_REMAINING_TIME);
888 
889  ChartData->Create(ChartDirArray, pprog);
890  ChartData->SaveBinary(ChartListFileName);
891 
892  delete pprog;
893 
894  // Apply the inital Group Array structure to the chart database
895  ChartData->ApplyGroupArray(g_pGroupArray);
896  }
897 }
898 
899 // play an arbitrary number of bells by using 1 and 2 bell sounds
900 void MyFrame::OnBellsFinished(wxCommandEvent &event) {
901  int bells = wxMin(m_BellsToPlay, 2);
902  if (bells <= 0) return;
903 
904  wxString soundfile = _T("sounds");
905  appendOSDirSlash(&soundfile);
906  soundfile += wxString(bells_sound_file_name[bells - 1], wxConvUTF8);
907  soundfile.Prepend(g_Platform->GetSharedDataDir());
908  wxLogMessage(_T("Using bells sound file: ") + soundfile);
909 
910  OcpnSound *sound = bells_sound[bells - 1];
911  sound->SetFinishedCallback(onBellsFinishedCB, this);
912  auto cmd_sound = dynamic_cast<SystemCmdSound *>(sound);
913  if (cmd_sound) cmd_sound->SetCmd(g_CmdSoundString.mb_str(wxConvUTF8));
914  sound->Load(soundfile);
915  if (!sound->IsOk()) {
916  wxLogMessage(_T("Failed to load bells sound file: ") + soundfile);
917  return;
918  }
919  sound->Play();
920  m_BellsToPlay -= bells;
921 }
922 
923 void MyFrame::OnEraseBackground(wxEraseEvent &event) {}
924 
925 void MyFrame::OnMaximize(wxMaximizeEvent &event) {
926  g_click_stop = 0;
927 #ifdef __WXOSX__
928  event.Skip();
929 #endif
930 }
931 
932 ColorScheme GetColorScheme() { return global_color_scheme; }
933 
934 ColorScheme MyFrame::GetColorScheme() { return global_color_scheme; }
935 
936 void MyFrame::ReloadAllVP() {
937  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
938  ChartCanvas *cc = g_canvasArray.Item(i);
939  if (cc) cc->ReloadVP();
940  }
941 }
942 
943 void MyFrame::SetAndApplyColorScheme(ColorScheme cs) {
944  global_color_scheme = cs;
945 
946  wxString SchemeName;
947  switch (cs) {
948  case GLOBAL_COLOR_SCHEME_DAY:
949  SchemeName = _T("DAY");
950  break;
951  case GLOBAL_COLOR_SCHEME_DUSK:
952  SchemeName = _T("DUSK");
953  break;
954  case GLOBAL_COLOR_SCHEME_NIGHT:
955  SchemeName = _T("NIGHT");
956  break;
957  default:
958  SchemeName = _T("DAY");
959  break;
960  }
961 
962  g_pauidockart->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE, wxAUI_GRADIENT_NONE);
963 
964  g_pauidockart->SetColour(wxAUI_DOCKART_BORDER_COLOUR, wxColour(0, 0, 0));
965  g_pauidockart->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 1);
966  g_pauidockart->SetColour(wxAUI_DOCKART_SASH_COLOUR, wxColour(0, 0, 0));
967  g_pauidockart->SetMetric(wxAUI_DOCKART_SASH_SIZE, 0);
968  g_pauidockart->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR,
969  wxColour(0, 0, 0));
970  g_pauidockart->SetColour(wxAUI_DOCKART_BACKGROUND_COLOUR, wxColour(0, 0, 0));
971 
972  // if( cs == GLOBAL_COLOR_SCHEME_DUSK || cs == GLOBAL_COLOR_SCHEME_NIGHT )
973  // {
974  // g_pauidockart->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 0);
975  // g_pauidockart->SetColour(wxAUI_DOCKART_BACKGROUND_COLOUR,
976  // wxColour(0,0,0));
977  // g_pauidockart->SetColour(wxAUI_DOCKART_BORDER_COLOUR,
978  // wxColour(0,0,0));
979  // }
980 
981  // else{
982  // g_pauidockart->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE,
983  // g_grad_default);
984  // g_pauidockart->SetColour(wxAUI_DOCKART_BORDER_COLOUR,
985  // g_border_color_default);
986  // g_pauidockart->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE,
987  // g_border_size_default);
988  // g_pauidockart->SetColour(wxAUI_DOCKART_SASH_COLOUR,
989  // g_sash_color_default);
990  // g_pauidockart->SetMetric(wxAUI_DOCKART_SASH_SIZE,
991  // g_sash_size_default);
992  // g_pauidockart->SetColour(wxAUI_DOCKART_INACTIVE_CAPTION_COLOUR,
993  // g_caption_color_default);
994  // g_pauidockart->SetColour(wxAUI_DOCKART_BACKGROUND_COLOUR,
995  // g_background_color_default);
996  //
997  // }
998 
999  g_pauidockart->SetColour(wxAUI_DOCKART_SASH_COLOUR, wxColour(0, 0, 0));
1000  g_pauidockart->SetMetric(wxAUI_DOCKART_SASH_SIZE, 6);
1001 
1002  g_pauimgr->Update();
1003 
1004  g_StyleManager->GetCurrentStyle()->SetColorScheme(cs);
1005 
1006  // Search the user color table array to find the proper hash table
1007  unsigned Usercolortable_index = 0;
1008  for (unsigned int i = 0; i < UserColorTableArray->GetCount(); i++) {
1009  colTable *ct = (colTable *)UserColorTableArray->Item(i);
1010  if (SchemeName.IsSameAs(*ct->tableName)) {
1011  Usercolortable_index = i;
1012  break;
1013  }
1014  }
1015 
1016  if (ps52plib) ps52plib->SetPLIBColorScheme(SchemeName, ChartCtxFactory());
1017 
1018  // Set up a pointer to the proper hash table
1019  pcurrent_user_color_hash =
1020  (wxColorHashMap *)UserColourHashTableArray->Item(Usercolortable_index);
1021 
1022  SetSystemColors(cs);
1023 
1024  // ..For each canvas...
1025  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1026  ChartCanvas *cc = g_canvasArray.Item(i);
1027  if (cc) {
1028  cc->SetColorScheme(cs);
1029  cc->GetWorldBackgroundChart()->SetColorScheme(cs);
1030  cc->HideChartInfoWindow();
1031  cc->SetQuiltChartHiLiteIndex(-1);
1032  }
1033  }
1034 
1035  if (pWayPointMan)
1036  WayPointmanGui(*pWayPointMan).SetColorScheme(cs,
1037  g_Platform->GetDisplayDPmm());
1038  if (ChartData) ChartData->ApplyColorSchemeToCachedCharts(cs);
1039 
1040  if (g_options) {
1041  g_options->SetColorScheme(cs);
1042  }
1043 
1044  if (console) {
1045  console->SetColorScheme(cs);
1046  }
1047 
1048  if (g_pRouteMan) {
1049  g_pRouteMan->SetColorScheme(cs, g_Platform->GetDisplayDPmm());
1050  }
1051 
1052  if (g_pMarkInfoDialog) {
1053  g_pMarkInfoDialog->SetColorScheme(cs);
1054  }
1055 
1056  if (pRoutePropDialog) {
1057  pRoutePropDialog->SetColorScheme(cs);
1058  }
1059 
1060  // For the AIS target query dialog, we must rebuild it to incorporate the
1061  // style desired for the colorscheme selected
1062  if (g_pais_query_dialog_active) {
1063  bool b_isshown = g_pais_query_dialog_active->IsShown();
1064  int n_mmsi = g_pais_query_dialog_active->GetMMSI();
1065  if (b_isshown) g_pais_query_dialog_active->Show(false); // dismiss it
1066 
1067  g_pais_query_dialog_active->Close();
1068 
1069  g_pais_query_dialog_active = new AISTargetQueryDialog();
1070  g_pais_query_dialog_active->Create(
1071  this, -1, _("AIS Target Query"),
1072  wxPoint(g_ais_query_dialog_x, g_ais_query_dialog_y));
1073  g_pais_query_dialog_active->SetMMSI(n_mmsi);
1074  g_pais_query_dialog_active->UpdateText();
1075  if (b_isshown) g_pais_query_dialog_active->Show();
1076  }
1077 
1078  if (pRouteManagerDialog) pRouteManagerDialog->SetColorScheme();
1079 
1080  if (g_pAISTargetList) g_pAISTargetList->SetColorScheme();
1081 
1082  if (g_pObjectQueryDialog) g_pObjectQueryDialog->SetColorScheme();
1083 
1084  ApplyGlobalColorSchemetoStatusBar();
1085 
1086  UpdateAllToolbars(cs);
1087 
1088  if (g_MainToolbar) {
1089  if (g_MainToolbar->GetColorScheme() != cs) {
1090  // capture the current toolbar collapse state
1091  bool btoolbarFull = g_bmasterToolbarFull;
1092 
1093  g_MainToolbar->SetColorScheme(cs);
1094  // g_MainToolbar->DestroyToolBar();
1095  // CreateMasterToolbar();
1096 
1097  if (!btoolbarFull) {
1098  //g_MainToolbar->Hide();
1099  RequestNewMasterToolbar();
1100  g_MainToolbar->SetColorScheme(cs);
1101  CollapseGlobalToolbar();
1102  //g_MainToolbar->Show();
1103  } else {
1104  RequestNewMasterToolbar();
1105  g_MainToolbar->SetColorScheme(cs);
1106  }
1107  }
1108  }
1109 
1110  if (g_pi_manager) g_pi_manager->SetColorSchemeForAllPlugIns(cs);
1111 }
1112 
1113 void MyFrame::ApplyGlobalColorSchemetoStatusBar(void) {
1114  if (m_pStatusBar != NULL) {
1115  m_pStatusBar->SetBackgroundColour(GetGlobalColor(_T("UIBDR"))); // UINFF
1116  m_pStatusBar->ClearBackground();
1117  }
1118 }
1119 
1120 ChartCanvas *MyFrame::GetPrimaryCanvas() {
1121  if (g_canvasArray.GetCount() > 0)
1122  return g_canvasArray.Item(0);
1123  else
1124  return NULL;
1125 }
1126 void MyFrame::CancelAllMouseRoute() {
1127  // ..For each canvas...
1128  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1129  ChartCanvas *cc = g_canvasArray.Item(i);
1130  if (cc) cc->CancelMouseRoute();
1131  }
1132 }
1133 
1134 void MyFrame::NotifyChildrenResize() {}
1135 
1136 void MyFrame::CreateCanvasLayout(bool b_useStoredSize) {
1137  // Clear the cache, and thus close all charts to avoid memory leaks
1138  if (ChartData) ChartData->PurgeCache();
1139 
1140  // Detach all canvases from AUI manager
1141  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1142  ChartCanvas *cc = g_canvasArray[i];
1143  if (cc) {
1144  g_pauimgr->DetachPane(cc);
1145  }
1146  }
1147 
1148  // Destroy any existing canvases, except for Primary canvas
1149  for (unsigned int i = 1; i < g_canvasArray.GetCount(); i++) {
1150  ChartCanvas *cc = g_canvasArray.Item(i);
1151  if (cc) {
1152  // pthumbwin = NULL; // TODO
1153  //cc->DestroyToolbar();
1154  cc->Destroy();
1155  }
1156  }
1157 
1158  auto &config_array = ConfigMgr::Get().GetCanvasConfigArray();
1159 
1160  // Canvas pointers in config array are now invalid
1161  for (unsigned int i = 1; i < config_array.GetCount(); i++) {
1162  config_array.Item(i)->canvas = NULL;
1163  }
1164 
1165  // g_canvasArray.Clear();
1166 
1167  // Clear the canvas Array, except for Primary canvas
1168  for (unsigned int i = 1; i < g_canvasArray.GetCount(); i++) {
1169  g_canvasArray.RemoveAt(i);
1170  }
1171 
1172  ChartCanvas *cc = NULL;
1173  switch (g_canvasConfig) {
1174  default:
1175  case 0: // a single canvas
1176  if (!g_canvasArray.GetCount() || !config_array.Item(0)) {
1177  cc = new ChartCanvas(this, 0); // the chart display canvas
1178  g_canvasArray.Add(cc);
1179  } else {
1180  cc = g_canvasArray[0];
1181  }
1182 
1183 #ifdef ocpnUSE_GL
1184  // Verify that glCanvas is ready, if necessary
1185  if (g_bopengl) {
1186  if (!cc->GetglCanvas()) cc->SetupGlCanvas();
1187  cc->GetglCanvas()->Show();
1188  }
1189 #endif
1190  config_array.Item(0)->canvas = cc;
1191 
1192  cc->SetDisplaySizeMM(g_display_size_mm);
1193 
1194  cc->ApplyCanvasConfig(config_array.Item(0));
1195 
1196  // cc->SetToolbarPosition(wxPoint( g_maintoolbar_x,
1197  // g_maintoolbar_y ));
1198  cc->ConfigureChartBar();
1199  cc->SetColorScheme(global_color_scheme);
1200  cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
1201  cc->SetShowGPS(true);
1202 
1203  g_pauimgr->AddPane(cc);
1204  g_pauimgr->GetPane(cc).Name(_T("ChartCanvas"));
1205  g_pauimgr->GetPane(cc).Fixed();
1206  g_pauimgr->GetPane(cc).CaptionVisible(false);
1207  g_pauimgr->GetPane(cc).CenterPane();
1208 
1209  break;
1210 
1211  case 1: { // two canvas, horizontal
1212  if (!g_canvasArray.GetCount() || !g_canvasArray[0]) {
1213  cc = new ChartCanvas(this, 0); // the chart display canvas
1214  g_canvasArray.Add(cc);
1215  } else {
1216  cc = g_canvasArray[0];
1217  }
1218 
1219  // Verify that glCanvas is ready, if not already built
1220 #ifdef ocpnUSE_GL
1221  if (g_bopengl) {
1222  if (!cc->GetglCanvas()) cc->SetupGlCanvas();
1223  }
1224 #endif
1225  config_array.Item(0)->canvas = cc;
1226 
1227  cc->ApplyCanvasConfig(config_array.Item(0));
1228 
1229  cc->SetDisplaySizeMM(g_display_size_mm);
1230  cc->ConfigureChartBar();
1231  cc->SetColorScheme(global_color_scheme);
1232  cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
1233  cc->SetShowGPS(false);
1234 
1235  g_pauimgr->AddPane(cc);
1236  g_pauimgr->GetPane(cc).Name(_T("ChartCanvas"));
1237  g_pauimgr->GetPane(cc)
1238  .CaptionVisible(false)
1239  .PaneBorder(false)
1240  .CloseButton(false);
1241 
1242  g_pauimgr->GetPane(cc).CenterPane();
1243 
1244  cc = new ChartCanvas(this, 1); // the chart display canvas
1245  g_canvasArray.Add(cc);
1246 
1247  // There is not yet a config descriptor for canvas 2, so create one by
1248  // copy ctor from canvas {0}.
1249  if (config_array.GetCount() < 2) {
1250  canvasConfig *pcc = new canvasConfig(*config_array.Item(0));
1251  pcc->configIndex = 1;
1252 
1253  // Arbitrarily establish the initial size of the new canvas to be
1254  // half the screen width.
1255  pcc->canvasSize = wxSize(GetClientSize().x / 2, GetClientSize().y);
1256  config_array.Add(pcc);
1257  }
1258 
1259  config_array.Item(1)->canvas = cc;
1260 
1261  cc->ApplyCanvasConfig(config_array.Item(1));
1262 
1263  cc->SetDisplaySizeMM(g_display_size_mm);
1264  cc->ConfigureChartBar();
1265  cc->SetColorScheme(global_color_scheme);
1266  cc->SetShowGPS(true);
1267 
1268  g_pauimgr->AddPane(cc);
1269  g_pauimgr->GetPane(cc).Name(_T("ChartCanvas2"));
1270  g_pauimgr->GetPane(cc)
1271  .CaptionVisible(false)
1272  .PaneBorder(false)
1273  .CloseButton(false);
1274  g_pauimgr->GetPane(cc).RightDockable(true);
1275  g_pauimgr->GetPane(cc).Right();
1276 
1277 #ifdef __ANDROID__
1278  config_array.Item(1)->canvasSize =
1279  wxSize(GetClientSize().x / 2, GetClientSize().y);
1280  g_pauimgr->GetPane(cc).BestSize(GetClientSize().x / 2, GetClientSize().y);
1281 #endif
1282 
1283  // If switching fromsingle canvas to 2-canvas mode dynamically,
1284  // try to use the latest persisted size for the new second canvas.
1285  if (b_useStoredSize) {
1286  int ccw = config_array.Item(1)->canvasSize.x;
1287  int cch = config_array.Item(1)->canvasSize.y;
1288 
1289  // Check for undefined size, and set a nice default size if necessary.
1290  if (ccw < GetClientSize().x / 10) {
1291  ccw = GetClientSize().x / 2;
1292  cch = GetClientSize().y;
1293  }
1294 
1295  g_pauimgr->GetPane(cc).BestSize(ccw, cch);
1296  cc->SetSize(ccw, cch);
1297  }
1298 
1299  break;
1300  }
1301 
1302  case 2: // two canvas, vertical
1303 
1304  break;
1305  }
1306 
1307  g_focusCanvas = GetPrimaryCanvas();
1308 }
1309 
1310 void MyFrame::RequestNewToolbars(bool bforcenew) {
1311  if (b_inCloseWindow) {
1312  return;
1313  }
1314 
1315  BuildiENCToolbar(bforcenew);
1316  PositionIENCToolbar();
1317 
1318 #ifdef __ANDROID__
1319  DoChartUpdate();
1320 #endif
1321 }
1322 
1323 // Update inplace the various controls with bitmaps corresponding to the
1324 // current color scheme
1325 void MyFrame::UpdateAllToolbars(ColorScheme cs) {
1326  if (g_iENCToolbar) g_iENCToolbar->SetColorScheme(cs);
1327 
1328  return;
1329 }
1330 
1331 void MyFrame::SetAllToolbarScale() {
1332  g_toolbar_scalefactor = g_Platform->GetToolbarScaleFactor(g_GUIScaleFactor);
1333 }
1334 
1335 void MyFrame::SetGPSCompassScale() {
1336  g_compass_scalefactor = g_Platform->GetCompassScaleFactor(g_GUIScaleFactor);
1337 }
1338 
1339 ChartCanvas *MyFrame::GetCanvasUnderMouse() {
1340  wxPoint screenPoint = ::wxGetMousePosition();
1341  canvasConfig *cc;
1342 
1343  switch (g_canvasConfig) {
1344  case 1:
1345  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
1346  if (cc) {
1347  ChartCanvas *canvas = cc->canvas;
1348  if (canvas->GetScreenRect().Contains(
1349  /*canvas->ScreenToClient*/ (screenPoint)))
1350  return canvas;
1351  }
1352  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(1);
1353  if (cc) {
1354  ChartCanvas *canvas = cc->canvas;
1355  if (canvas->GetScreenRect().Contains(
1356  /*canvas->ScreenToClient*/ (screenPoint)))
1357  return canvas;
1358  }
1359  break;
1360 
1361  default:
1362  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
1363  if (cc) {
1364  ChartCanvas *canvas = cc->canvas;
1365  if (canvas->GetScreenRect().Contains(
1366  canvas->ScreenToClient(screenPoint)))
1367  return canvas;
1368  }
1369  }
1370 
1371  return NULL;
1372 }
1373 
1374 int MyFrame::GetCanvasIndexUnderMouse() {
1375  wxPoint screenPoint = ::wxGetMousePosition();
1376  canvasConfig *cc;
1377 
1378  switch (g_canvasConfig) {
1379  case 1:
1380  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
1381  if (cc) {
1382  ChartCanvas *canvas = cc->canvas;
1383  if (canvas->GetScreenRect().Contains(
1384  /*canvas->ScreenToClient*/ (screenPoint)))
1385  return 0;
1386  }
1387  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(1);
1388  if (cc) {
1389  ChartCanvas *canvas = cc->canvas;
1390  if (canvas->GetScreenRect().Contains(
1391  /*canvas->ScreenToClient*/ (screenPoint)))
1392  return 1;
1393  }
1394  break;
1395 
1396  default:
1397  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
1398  if (cc) {
1399  ChartCanvas *canvas = cc->canvas;
1400  if (canvas->GetScreenRect().Contains(
1401  canvas->ScreenToClient(screenPoint)))
1402  return 0;
1403  }
1404  }
1405 
1406  return -1;
1407 }
1408 
1409 bool MyFrame::DropMarker(bool atOwnShip) {
1410  double lat, lon;
1411  ChartCanvas *canvas = GetCanvasUnderMouse();
1412  if (atOwnShip) {
1413  lat = gLat;
1414  lon = gLon;
1415  } else {
1416  if (!canvas) return false;
1417 
1418  lat = canvas->m_cursor_lat;
1419  lon = canvas->m_cursor_lon;
1420  }
1421 
1422  RoutePoint *pWP =
1423  new RoutePoint(lat, lon, g_default_wp_icon, wxEmptyString, wxEmptyString);
1424  pWP->m_bIsolatedMark = true; // This is an isolated mark
1425  pSelect->AddSelectableRoutePoint(lat, lon, pWP);
1426  pConfig->AddNewWayPoint(pWP, -1); // use auto next num
1427  if (canvas)
1428  if (!RoutePointGui(*pWP).IsVisibleSelectable(canvas))
1429  RoutePointGui(*pWP).ShowScaleWarningMessage(canvas);
1430  if (pRouteManagerDialog && pRouteManagerDialog->IsShown())
1431  pRouteManagerDialog->UpdateWptListCtrl();
1432  // undo->BeforeUndoableAction( Undo_CreateWaypoint, pWP, Undo_HasParent,
1433  // NULL ); undo->AfterUndoableAction( NULL );
1434 
1435  InvalidateAllGL();
1436  RefreshAllCanvas(false);
1437 
1438  return true;
1439 }
1440 
1441 void MyFrame::SwitchKBFocus(ChartCanvas *pCanvas) {
1442  if (g_canvasConfig != 0) { // multi-canvas?
1443  canvasConfig *cc;
1444  int nTarget = -1;
1445  int nTargetGTK = -1;
1446  ChartCanvas *target;
1447  wxWindow *source = FindFocus();
1448  ChartCanvas *test = wxDynamicCast(source, ChartCanvas);
1449  if (!test) return;
1450 
1451  // On linux(GTK), the TAB key causes a loss of focus immediately
1452  // So the logic needs a switch
1453  switch (g_canvasConfig) {
1454  case 1:
1455  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
1456  if (cc) {
1457  ChartCanvas *canvas = cc->canvas;
1458  if (canvas && (canvas == test)) {
1459  nTarget = 1;
1460  nTargetGTK = 0;
1461  }
1462  }
1463  cc = ConfigMgr::Get().GetCanvasConfigArray().Item(1);
1464  if (cc) {
1465  ChartCanvas *canvas = cc->canvas;
1466  if (canvas && (canvas == test)) {
1467  nTarget = 0;
1468  nTargetGTK = 1;
1469  }
1470  }
1471 
1472  if (nTarget >= 0) {
1473  // printf("sw %d\n", nTarget);
1474  int nfinalTarget = nTarget;
1475 #ifdef __WXGTK__
1476  nfinalTarget = nTargetGTK;
1477 #endif
1478  target = ConfigMgr::Get().GetCanvasConfigArray().Item(nfinalTarget)->canvas;
1479  if (target) {
1480  wxWindow *win = wxDynamicCast(target, wxWindow);
1481  win->SetFocus();
1482  target->Refresh(true);
1483  }
1484  }
1485  break;
1486 
1487  default:
1488  break;
1489  }
1490  }
1491 }
1492 
1493 void MyFrame::FastClose() {
1494  FrameTimer1.Stop();
1495  quitflag++; // signal to the timer loop
1496  FrameTimer1.Start(1); // real quick now...
1497 }
1498 
1499 // Intercept menu commands
1500 void MyFrame::OnExit(wxCommandEvent &event) {
1501  quitflag++; // signal to the timer loop
1502 }
1503 
1504 void MyFrame::OnCloseWindow(wxCloseEvent &event) {
1505  // It is possible that double clicks on application exit box could cause
1506  // re-entrance here Not good, and don't need it anyway, so simply return.
1507  if (b_inCloseWindow) {
1508  // wxLogMessage(_T("opencpn::MyFrame re-entering
1509  // OnCloseWindow"));
1510  return;
1511  }
1512 
1513  // The Options dialog, and other deferred init items, are not fully
1514  // initialized. Best to just cancel the close request. This is probably only
1515  // reachable on slow hardware, or on Android life-cycle events...
1516 #ifndef __ANDROID__
1517  if (!g_bDeferredInitDone) return;
1518 #endif
1519 
1520 #ifndef __WXOSX__
1521  if (g_options) {
1522  delete g_options;
1523  g_options = NULL;
1524  g_pOptions = NULL;
1525  }
1526 #endif
1527 
1528  // If the multithread chart compressor engine is running, cancel the close
1529  // command
1530  if (b_inCompressAllCharts) {
1531  return;
1532  }
1533 
1534  if (bDBUpdateInProgress) return;
1535 
1536  b_inCloseWindow = true;
1537 
1538  ::wxSetCursor(wxCURSOR_WAIT);
1539 
1540  // If we happen to have the measure tool open on Ctrl-Q quit
1541  // ..For each canvas...
1542  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1543  ChartCanvas *cc = g_canvasArray.Item(i);
1544  if (cc && cc->IsMeasureActive()) {
1545  cc->CancelMeasureRoute();
1546  }
1547  }
1548 
1549  // We save perspective before closing to restore position next time
1550  // Pane is not closed so the child is not notified (OnPaneClose)
1551  if (g_pAISTargetList) {
1552  wxAuiPaneInfo &pane = g_pauimgr->GetPane(g_pAISTargetList);
1553  g_AisTargetList_perspective = g_pauimgr->SavePaneInfo(pane);
1554  g_pauimgr->DetachPane(g_pAISTargetList);
1555  }
1556 
1557  // Make sure the saved perspective minimum canvas sizes are essentially
1558  // undefined
1559  // for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){
1560  // ChartCanvas *cc = g_canvasArray.Item(i);
1561  // if(cc)
1562  // g_pauimgr->GetPane( cc ).MinSize(10,10);
1563  // }
1564 
1565  pConfig->SetPath(_T ( "/AUI" ));
1566  pConfig->Write(_T ( "AUIPerspective" ), g_pauimgr->SavePerspective());
1567 
1568  g_bquiting = true;
1569 
1570 #ifdef ocpnUSE_GL
1571  // cancel compression jobs
1572  if (g_bopengl) {
1573  if (g_glTextureManager) {
1574  g_glTextureManager->PurgeJobList();
1575 
1576  if (g_glTextureManager->GetRunningJobCount()) g_bcompression_wait = true;
1577  }
1578  }
1579 #endif
1580 
1581  SetCursor(wxCURSOR_WAIT);
1582 
1583  RefreshAllCanvas(true);
1584 
1585  // This yield is not necessary, since the Update() proceeds syncronously...
1586  // wxYield();
1587 
1588  // Save the saved Screen Brightness
1589  RestoreScreenBrightness();
1590 
1591  // Persist the toolbar locations
1592  // if (g_MainToolbar) {
1593  // g_MainToolbar->GetFrameRelativePosition(&g_maintoolbar_x, &g_maintoolbar_y);
1594  // }
1595 
1596 #if 0
1597  if (g_iENCToolbar) {
1598  wxPoint locn = g_iENCToolbar->GetPosition();
1599  wxPoint tbp_incanvas = GetPrimaryCanvas()->ScreenToClient(locn);
1600  g_iENCToolbarPosY = tbp_incanvas.y;
1601  g_iENCToolbarPosX = tbp_incanvas.x;
1602  }
1603 #endif
1604 
1605  g_bframemax = IsMaximized();
1606 
1607  FrameTimer1.Stop();
1608  FrameCOGTimer.Stop();
1609 
1610  TrackOff();
1611 
1612  /*
1613  Automatically drop an anchorage waypoint, if enabled
1614  On following conditions:
1615  1. In "Cruising" mode, meaning that speed has at some point exceeded 3.0 kts.
1616  2. Current speed is less than 0.5 kts.
1617  3. Opencpn has been up at least 30 minutes
1618  4. And, of course, opencpn is going down now.
1619  5. And if there is no anchor watch set on "anchor..." icon mark //
1620  pjotrc 2010.02.15
1621  */
1622  if (g_bAutoAnchorMark) {
1623  bool watching_anchor = false; // pjotrc 2010.02.15
1624  if (pAnchorWatchPoint1) // pjotrc 2010.02.15
1625  watching_anchor = (pAnchorWatchPoint1->GetIconName().StartsWith(
1626  _T("anchor"))); // pjotrc 2010.02.15
1627  if (pAnchorWatchPoint2) // pjotrc 2010.02.15
1628  watching_anchor |= (pAnchorWatchPoint2->GetIconName().StartsWith(
1629  _T("anchor"))); // pjotrc 2010.02.15
1630 
1631  wxDateTime now = wxDateTime::Now();
1632  wxTimeSpan uptime = now.Subtract(g_start_time);
1633 
1634  if (!watching_anchor && (g_bCruising) && (gSog < 0.5) &&
1635  (uptime.IsLongerThan(wxTimeSpan(0, 30, 0, 0)))) // pjotrc 2010.02.15
1636  {
1637  // First, delete any single anchorage waypoint closer than 0.25 NM from
1638  // this point This will prevent clutter and database congestion....
1639 
1640  wxRoutePointListNode *node = pWayPointMan->GetWaypointList()->GetFirst();
1641  while (node) {
1642  RoutePoint *pr = node->GetData();
1643  if (pr->GetName().StartsWith(_T("Anchorage"))) {
1644  double a = gLat - pr->m_lat;
1645  double b = gLon - pr->m_lon;
1646  double l = sqrt((a * a) + (b * b));
1647 
1648  // caveat: this is accurate only on the Equator
1649  if ((l * 60. * 1852.) < (.25 * 1852.)) {
1650  pConfig->DeleteWayPoint(pr);
1651  pSelect->DeleteSelectablePoint(pr, SELTYPE_ROUTEPOINT);
1652  delete pr;
1653  break;
1654  }
1655  }
1656 
1657  node = node->GetNext();
1658  }
1659 
1660  wxString name = now.Format();
1661  name.Prepend(_("Anchorage created "));
1662  RoutePoint *pWP =
1663  new RoutePoint(gLat, gLon, _T("anchorage"), name, wxEmptyString);
1664  pWP->m_bShowName = false;
1665  pWP->m_bIsolatedMark = true;
1666 
1667  pConfig->AddNewWayPoint(pWP, -1); // use auto next num
1668  }
1669  }
1670 
1671  // Provisionally save all settings before deactivating plugins
1672  pConfig->UpdateSettings();
1673 
1674  // Deactivate the PlugIns
1675  auto plugin_loader = PluginLoader::getInstance();
1676  if (plugin_loader) {
1677  plugin_loader->DeactivateAllPlugIns();
1678  }
1679 
1680  wxLogMessage(_T("opencpn::MyFrame exiting cleanly."));
1681 
1682  quitflag++;
1683 
1684  pConfig->UpdateNavObj();
1685 
1686  pConfig->m_pNavObjectChangesSet->reset();
1687 
1688  // Remove any leftover Routes and Waypoints from config file as they were
1689  // saved to navobj before
1690  pConfig->DeleteGroup(_T ( "/Routes" ));
1691  pConfig->DeleteGroup(_T ( "/Marks" ));
1692  pConfig->Flush();
1693 
1694  delete g_printData;
1695  delete g_pageSetupData;
1696 
1697  if (g_pAboutDlg) g_pAboutDlg->Destroy();
1698  if (g_pAboutDlgLegacy) g_pAboutDlgLegacy->Destroy();
1699 
1700  // Explicitely Close some children, especially the ones with event
1701  // handlers or that call GUI methods
1702 
1703  if (g_pCM93OffsetDialog) {
1704  g_pCM93OffsetDialog->Destroy();
1705  g_pCM93OffsetDialog = NULL;
1706  }
1707 
1708 #ifndef __ANDROID__
1709  //if (g_MainToolbar) g_MainToolbar->Destroy();
1710  //g_MainToolbar = NULL;
1711 #endif
1712 
1713  if (g_iENCToolbar) {
1714  //wxPoint locn = g_iENCToolbar->GetPosition();
1715  //g_iENCToolbarPosY = locn.y;
1716  //g_iENCToolbarPosX = locn.x;
1717  //g_iENCToolbar->Destroy();
1718  }
1719 
1720  if (g_pAISTargetList) {
1721  g_pAISTargetList->Disconnect_decoder();
1722  g_pAISTargetList->Destroy();
1723  }
1724 
1725 #ifndef __WXQT__
1726  SetStatusBar(NULL);
1727 #endif
1728 
1729  if (RouteManagerDialog::getInstanceFlag()) {
1730  if (pRouteManagerDialog) {
1731  pRouteManagerDialog->Destroy();
1732  pRouteManagerDialog = NULL;
1733  }
1734  }
1735 
1736  // Clear the cache, and thus close all charts to avoid memory leaks
1737  if (ChartData) ChartData->PurgeCache();
1738 
1739  // pthumbwin is a canvas child
1740  // pthumbwin = NULL;
1741 
1742  // Finally ready to destroy the canvases
1743  g_focusCanvas = NULL;
1744 
1745  // ..For each canvas...
1746  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1747  ChartCanvas *cc = g_canvasArray.Item(i);
1748  if (cc) cc->Destroy();
1749  }
1750 
1751  g_canvasArray.Clear();
1752 
1753  g_pauimgr->UnInit();
1754  delete g_pauimgr;
1755  g_pauimgr = NULL;
1756 
1757  // Unload the PlugIns
1758  // Note that we are waiting until after the canvas is destroyed,
1759  // since some PlugIns may have created children of canvas.
1760  // Such a PlugIn must stay intact for the canvas dtor to call
1761  // DestoryChildren()
1762 
1763  if (ChartData) ChartData->PurgeCachePlugins();
1764 
1765  if (PluginLoader::getInstance())
1766  PluginLoader::getInstance()->UnLoadAllPlugIns();
1767 
1768  if (g_pi_manager) {
1769  delete g_pi_manager;
1770  g_pi_manager = NULL;
1771  }
1772 
1773  MyApp& app = wxGetApp();
1774  app.m_comm_bridge.SaveConfig();
1775 
1776  delete pConfig; // All done
1777  pConfig = NULL;
1778  InitBaseConfig(0);
1779 
1780 
1781  if (g_pAIS) {
1782  delete g_pAIS;
1783  g_pAIS = NULL;
1784  }
1785 
1786  if (g_pAISGUI) {
1787  delete g_pAISGUI;
1788  g_pAISGUI = NULL;
1789  }
1790 
1791  delete g_pMUX;
1792  g_pMUX = NULL;
1793 
1794  // Close and delete all comm drivers
1795  auto& registry = CommDriverRegistry::GetInstance();
1796  registry.CloseAllDrivers();
1797 
1798  // Clear some global arrays, lists, and hash maps...
1799  for (size_t i = 0; i < TheConnectionParams()->Count(); i++) {
1800  ConnectionParams *cp = TheConnectionParams()->Item(i);
1801  delete cp;
1802  }
1803  delete TheConnectionParams();
1804 
1805  if (pLayerList) {
1806  LayerList::iterator it;
1807  while (pLayerList->GetCount()) {
1808  Layer *lay = pLayerList->GetFirst()->GetData();
1809  delete lay; // automatically removes the layer from list, see Layer dtor
1810  }
1811  }
1812 
1813  NMEALogWindow::Shutdown();
1814 
1815  ReleaseApiListeners();
1816 
1817  g_MainToolbar = NULL;
1818  g_bTempShowMenuBar = false;
1819 
1820 #define THREAD_WAIT_SECONDS 5
1821 #ifdef ocpnUSE_GL
1822  // The last thing we do is finish the compression threads.
1823  // This way the main window is already invisible and to the user
1824  // it appears to have finished rather than hanging for several seconds
1825  // while the compression threads exit
1826  if (g_bopengl && g_glTextureManager &&
1827  g_glTextureManager->GetRunningJobCount()) {
1828  g_glTextureManager->ClearAllRasterTextures();
1829 
1830  wxLogMessage(_T("Starting compressor pool drain"));
1831  wxDateTime now = wxDateTime::Now();
1832  time_t stall = now.GetTicks();
1833  time_t end = stall + THREAD_WAIT_SECONDS;
1834 
1835  int n_comploop = 0;
1836  while (stall < end) {
1837  wxDateTime later = wxDateTime::Now();
1838  stall = later.GetTicks();
1839 
1840  wxString msg;
1841  msg.Printf(_T("Time: %d Job Count: %d"), n_comploop,
1842  g_glTextureManager->GetRunningJobCount());
1843  wxLogMessage(msg);
1844  if (!g_glTextureManager->GetRunningJobCount()) break;
1845  wxYield();
1846  wxSleep(1);
1847  }
1848 
1849  wxString fmsg;
1850  fmsg.Printf(_T("Finished compressor pool drain..Time: %d Job Count: %d"),
1851  n_comploop, g_glTextureManager->GetRunningJobCount());
1852  wxLogMessage(fmsg);
1853  }
1854  delete g_glTextureManager;
1855 #endif
1856  uninitIXNetSystem();
1857  this->Destroy();
1858  gFrame = NULL;
1859 
1860  wxLogMessage(_T("gFrame destroyed."));
1861 
1862 #ifdef __ANDROID__
1863 #ifndef USE_ANDROID_GLES2
1864  qDebug() << "Calling OnExit()";
1865  wxTheApp->OnExit();
1866 #endif
1867 #endif
1868  wxTheApp->ExitMainLoop();
1869 }
1870 
1871 void MyFrame::OnMove(wxMoveEvent &event) {
1872  auto idx = wxDisplay::GetFromWindow(this);
1873  if (idx != wxNOT_FOUND && g_current_monitor != static_cast<size_t>(idx) && static_cast<size_t>(idx) < g_monitor_info.size()) {
1874  g_current_monitor = idx;
1875 #ifdef __WXOSX__
1876  // On retina displays there is a difference between the physical size of the OpenGL canvas and the DIP
1877  // This is not observed anywhere else so far, so g_current_monitor_dip_px_ratio cna be kept 1.0 everywhere else
1878  if (g_bopengl) {
1879  g_current_monitor_dip_px_ratio = g_monitor_info[idx].width_px / g_monitor_info[idx].width;
1880  }
1881 #endif
1882  DEBUG_LOG << "Moved to " << idx
1883 #if wxCHECK_VERSION(3, 1, 6)
1884  << " PPI: " << wxDisplay(idx).GetPPI().GetX() << "x" << wxDisplay(idx).GetPPI().GetY()
1885  << " SF wxDisplay: " << wxDisplay(idx).GetScaleFactor()
1886 #endif
1887  << " Size wxDisplay: " << wxDisplay(idx).GetGeometry().GetWidth() << "x" << wxDisplay(idx).GetGeometry().GetHeight()
1888  << " MM wxDisplay: " << wxGetDisplaySizeMM().GetX() << "x" << wxGetDisplaySizeMM().GetY()
1889  << " Name wxDisplay: " << wxDisplay(idx).GetName().c_str()
1890  << " Real: " << g_monitor_info[idx].width_mm << "x" << g_monitor_info[idx].height_mm << "mm "
1891  << g_monitor_info[idx].width_mm << "x" << g_monitor_info[idx].height_mm << "mm "
1892  << g_monitor_info[idx].width << "x" << g_monitor_info[idx].height << "DIP "
1893  << g_monitor_info[idx].width_px << "x" << g_monitor_info[idx].height_px << "px"
1894  << g_monitor_info[idx].scale << "%";
1895  if(g_config_display_size_manual) {
1896  if(g_config_display_size_mm.size() > static_cast<size_t>(idx)) {
1897  g_display_size_mm = g_config_display_size_mm[idx];
1898  } // Do nothing if the user did not set any value for this monitor
1899  } else {
1900  g_display_size_mm = g_monitor_info[idx].width_mm;
1901  }
1902  }
1903  // ..For each canvas...
1904  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
1905  ChartCanvas *cc = g_canvasArray.Item(i);
1906  if (cc) {
1907  cc->SetMUIBarPosition();
1908  cc->SetDisplaySizeMM(g_display_size_mm);
1909  }
1910  }
1911 
1912 #ifdef __WXOSX__
1913  SendSizeEvent();
1914 #endif
1915 
1916  UpdateGPSCompassStatusBoxes();
1917 
1918  if (console && console->IsShown()) PositionConsole();
1919 
1920  // If global toolbar is shown, reposition it...
1921  //if (g_MainToolbar) {
1922  //g_MainToolbar->RestoreRelativePosition(g_maintoolbar_x, g_maintoolbar_y);
1923  //g_MainToolbar->Realize();
1924  //}
1925 
1926  PositionIENCToolbar();
1927 
1928  // Somehow, this method does not work right on Windows....
1929  // g_nframewin_posx = event.GetPosition().x;
1930  // g_nframewin_posy = event.GetPosition().y;
1931 
1932  g_nframewin_posx = GetPosition().x;
1933  g_nframewin_posy = GetPosition().y;
1934 }
1935 
1936 void MyFrame::ProcessCanvasResize(void) {
1937  UpdateGPSCompassStatusBoxes(true);
1938 
1939  if (console && console->IsShown()) PositionConsole();
1940 
1941  PositionIENCToolbar();
1942 
1943 #ifndef __ANDROID__
1944  TriggerRecaptureTimer();
1945 #endif
1946 }
1947 
1948 void MyFrame::TriggerRecaptureTimer() {
1949  m_recaptureTimer.Start(
1950  1000, wxTIMER_ONE_SHOT); // One second seems enough, on average
1951 }
1952 
1953 void MyFrame::OnRecaptureTimer(wxTimerEvent &event) { Raise(); }
1954 
1955 void MyFrame::SetCanvasSizes(wxSize frameSize) {
1956  if (!g_canvasArray.GetCount()) return;
1957 
1958 #if 0
1959  int cccw = frameSize.x;
1960  int ccch = frameSize.y;
1961 #endif
1962 
1963  // .. for each canvas...
1964  switch (g_canvasConfig) {
1965  default:
1966  case 0:
1967 #if 0
1968  cc = g_canvasArray.Item(0);
1969  if( cc ) {
1970  cc->GetSize( &cur_width, &cur_height );
1971  if( ( cur_width != cccw ) || ( cur_height != ccch ) ) {
1972  if( g_pauimgr->GetPane( cc ).IsOk() )
1973  g_pauimgr->GetPane( cc ).BestSize( cccw, ccch );
1974  else
1975  cc->SetSize( 0, 0, cccw, ccch );
1976  }
1977  }
1978 #endif
1979  break;
1980 
1981  case 1:
1982 #if 0
1983  cc = g_canvasArray.Item(1);
1984  if( cc ) {
1985  int ccw = g_canvasConfigArray.Item(1)->canvasSize.x;
1986  int cch = g_canvasConfigArray.Item(1)->canvasSize.y;
1987 
1988  ccw = wxMin(ccw, cccw * 8 / 10);
1989  ccw = wxMax(ccw, cccw * 2 / 10);
1990  if(cccw < 100)
1991  ccw = 20;
1992 
1993  g_canvasConfigArray.Item(1)->canvasSize = wxSize(ccw, cch);
1994 // g_pauimgr->GetPane(cc).MinSize(cccw * 2 / 10, ccch);
1995 
1996 #if 1 // ndef __WXMSW__
1997  // wxAUI hack: This is needed to explicietly set a docked pane size
1998  // Set MinSize to desired value, then call wxAuiPaneInfo::Fixed() to
1999  // apply it
2000  g_pauimgr->GetPane(cc).MinSize(ccw, cch);
2001  g_pauimgr->GetPane(cc).Fixed();
2002  g_pauimgr->Update();
2003 
2004  //now make resizable again
2005  g_pauimgr->GetPane(cc).Resizable();
2007  //g_pauimgr->Update(); //Deferred
2008  //g_pauimgr->GetPane( cc ).BestSize( ccw, cch );
2009 #endif
2010  }
2011 #endif
2012 
2013  break;
2014  }
2015 }
2016 
2017 void MyFrame::OnIconize(wxIconizeEvent &event) {
2018 #if 0
2019  if (g_MainToolbar) {
2020  g_MainToolbar->Show(!event.IsIconized());
2021  }
2022  if (g_iENCToolbar) {
2023  g_iENCToolbar->Show(!event.IsIconized());
2024  }
2025 
2026  // .. for each canvas...
2027  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2028  ChartCanvas *cc = g_canvasArray.Item(i);
2029  if (cc && cc->GetMUIBar()) {
2030  if (cc->GetMUIBar()->GetCanvasOptions()) {
2031  if (cc->GetMUIBar()->GetCanvasOptions()->IsShown()) {
2032  cc->GetMUIBar()->PushCanvasOptions(); // hide it
2033  }
2034  }
2035  }
2036  }
2037 
2038 #endif
2039 }
2040 
2041 void MyFrame::OnSize(wxSizeEvent &event) { ODoSetSize(); }
2042 
2043 void MyFrame::ODoSetSize(void) {
2044  int x, y;
2045  GetClientSize(&x, &y);
2046  // Resize the children
2047 
2048  if (m_pStatusBar != NULL) {
2049  m_StatusBarFieldCount = g_Platform->GetStatusBarFieldCount();
2050  int currentCount = m_pStatusBar->GetFieldsCount();
2051  if (currentCount != m_StatusBarFieldCount) {
2052  if ((currentCount > 0) && (currentCount < 7)) {
2053  // reset the widths very small to avoid auto-resizing of the frame
2054  // The sizes will be reset later in this method
2055  int widths[] = {2, 2, 2, 2, 2, 2};
2056  m_pStatusBar->SetStatusWidths(currentCount, widths);
2057  }
2058 
2059  m_pStatusBar->SetFieldsCount(m_StatusBarFieldCount);
2060  }
2061 
2062  if (m_StatusBarFieldCount) {
2063  // If the status bar layout is "complex", meaning more than two columns,
2064  // then use custom crafted relative widths for the fields.
2065  // Otherwise, just split the frame client width into equal spaces
2066 
2067  if (m_StatusBarFieldCount > 2) {
2068  int widths[] = {-6, -5, -5, -6, -4};
2069  m_pStatusBar->SetStatusWidths(m_StatusBarFieldCount, widths);
2070  } else if (m_StatusBarFieldCount == 2) {
2071  int cwidth = x * 90 / 100;
2072  int widths[] = {100, 100};
2073  widths[0] = cwidth * 6.4 / 10.0;
2074  widths[1] = cwidth * 3.6 / 10.0;
2075  m_pStatusBar->SetStatusWidths(m_StatusBarFieldCount, widths);
2076  } else {
2077  int widths[] = {100, 100};
2078  widths[0] = x * 90 / 100;
2079  m_pStatusBar->SetStatusWidths(m_StatusBarFieldCount, widths);
2080  }
2081 
2082  int styles[] = {wxSB_FLAT, wxSB_FLAT, wxSB_FLAT,
2083  wxSB_FLAT, wxSB_FLAT, wxSB_FLAT};
2084  m_pStatusBar->SetStatusStyles(m_StatusBarFieldCount, styles);
2085 
2086  wxString sogcog(_T("SOG --- ") + getUsrSpeedUnit() + +_T(" ") +
2087  _T(" COG ---\u00B0"));
2088  m_pStatusBar->SetStatusText(sogcog, STAT_FIELD_SOGCOG);
2089  }
2090  }
2091 
2092  if (m_pStatusBar) {
2093  // Maybe resize the font so the text fits in the boxes
2094 
2095  wxRect stat_box;
2096  m_pStatusBar->GetFieldRect(0, stat_box);
2097  // maximum size is 1/28 of the box width, or the box height - whicever is
2098  // less
2099  int max_font_size = wxMin((stat_box.width / 28), (stat_box.height));
2100 
2101  wxFont sys_font = *wxNORMAL_FONT;
2102  int try_font_size = sys_font.GetPointSize();
2103 
2104 #ifdef __WXOSX__
2105  int min_font_size = 10; // much less than 10pt is unreadably small on OS X
2106  try_font_size += 1; // default to 1pt larger than system UI font
2107 #else
2108  int min_font_size =
2109  7; // on Win/Linux the text does not shrink quite so fast
2110  try_font_size += 2; // default to 2pt larger than system UI font
2111 #endif
2112 
2113  // get the user's preferred font, or if none set then the system default
2114  // with the size overridden
2115  wxFont *statusBarFont =
2116  FontMgr::Get().GetFont(_("StatusBar"), try_font_size);
2117  int font_size = statusBarFont->GetPointSize();
2118 
2119  font_size = wxMin(font_size,
2120  max_font_size); // maximum to fit in the statusbar boxes
2121  font_size =
2122  wxMax(font_size, min_font_size); // minimum to stop it being unreadable
2123 
2124 #ifdef __ANDROID__
2125  font_size = statusBarFont->GetPointSize();
2126 #endif
2127 
2128  // Accomodate HDPI displays
2129  font_size /= OCPN_GetDisplayContentScaleFactor();
2130 
2131  wxFont *pstat_font = FontMgr::Get().FindOrCreateFont(
2132  font_size, statusBarFont->GetFamily(), statusBarFont->GetStyle(),
2133  statusBarFont->GetWeight(), false, statusBarFont->GetFaceName());
2134 
2135  int min_height = stat_box.height;
2136 
2137  m_pStatusBar->SetFont(*pstat_font);
2138  m_pStatusBar->SetForegroundColour(
2139  FontMgr::Get().GetFontColor(_("StatusBar")));
2140 #ifdef __ANDROID__
2141  min_height = (pstat_font->GetPointSize() * getAndroidDisplayDensity()) + 10;
2142  min_height =
2143  (min_height >> 1) * 2; // force even number, makes GLCanvas happier...
2144  m_pStatusBar->SetMinHeight(min_height);
2145 // qDebug() <<"StatusBar min height:" << min_height << "StatusBar font
2146 // points:" << pstat_font->GetPointSize();
2147 #endif
2148  // wxString msg;
2149  // msg.Printf(_T("StatusBar min height: %d StatusBar font points:
2150  // %d"), min_height, pstat_font->GetPointSize()); wxLogMessage(msg);
2151  }
2152 
2153  SetCanvasSizes(GetClientSize());
2154 
2155  UpdateGPSCompassStatusBoxes(true);
2156 
2157  if (console) PositionConsole();
2158 
2159  // .. for each canvas...
2160  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2161  ChartCanvas *cc = g_canvasArray.Item(i);
2162  if (cc) cc->FormatPianoKeys();
2163  }
2164 
2165  // If global toolbar is shown, resize it...
2166  if (g_MainToolbar) {
2167  wxSize szBefore = g_MainToolbar->GetToolbarSize();
2168  g_MainToolbar->SetGeometry(GetPrimaryCanvas()->GetCompass()->IsShown(),
2169  GetPrimaryCanvas()->GetCompass()->GetRect());
2170  g_MainToolbar->Realize();
2171 
2172  if (szBefore != g_MainToolbar->GetToolbarSize())
2173  g_MainToolbar->RefreshToolbar();
2174  }
2175 
2176  // Update the stored window size
2177  GetSize(&x, &y);
2178  g_nframewin_x = x;
2179  g_nframewin_y = y;
2180 
2181  // Inform the PlugIns
2182  if (g_pi_manager) g_pi_manager->SendResizeEventToAllPlugIns(x, y);
2183 
2184  // Force redraw if in lookahead mode
2185  // TODO is this all right?
2186  // if( g_bLookAhead ) {
2187  // DoCOGSet();
2188  // DoChartUpdate();
2189  // }
2190 
2191  // FIXME (dave) Thumbwins are gone...
2192  // if (pthumbwin) pthumbwin->SetMaxSize(GetClientSize());
2193 
2194  // Reset the options dialog size logic
2195  options_lastWindowSize = wxSize(0, 0);
2196  options_lastWindowPos = wxPoint(0, 0);
2197 
2198 #ifdef __ANDROID__
2199  // If the options dialog is displayed, this will have the effect of
2200  // raising the dialog above the main and canvas-GUI toolbars.
2201  // If the dialog is not shown, no harm done
2202 
2203  if (!b_inCloseWindow) {
2204  if (g_options) g_options->Raise();
2205 
2206  resizeAndroidPersistents();
2207  }
2208 
2209 #endif
2210 
2211  if (g_pauimgr) g_pauimgr->Update();
2212 }
2213 
2214 void MyFrame::PositionConsole(void) {
2215  if (NULL == GetPrimaryCanvas()) return;
2216  // Reposition console based on its size and chartcanvas size
2217  int ccx, ccy, ccsx, ccsy, consx, consy;
2218  ChartCanvas *consoleHost = GetPrimaryCanvas();
2219  if (g_canvasConfig > 0) consoleHost = g_canvasArray[1];
2220 
2221  if (consoleHost) {
2222  consoleHost->GetSize(&ccsx, &ccsy);
2223  consoleHost->GetPosition(&ccx, &ccy);
2224  } else {
2225  GetPrimaryCanvas()->GetSize(&ccsx, &ccsy);
2226  GetPrimaryCanvas()->GetPosition(&ccx, &ccy);
2227  consoleHost = GetPrimaryCanvas();
2228  }
2229 
2230  int yOffset = 60;
2231  if (consoleHost) {
2232  if(consoleHost->GetCompass()){
2233  wxRect compass_rect = consoleHost->GetCompass()->GetRect();
2234  // Compass is normal upper right position.
2235  if(compass_rect.y < 100)
2236  yOffset = compass_rect.y + compass_rect.height + 45;
2237  }
2238  }
2239 
2240  console->GetSize(&consx, &consy);
2241 
2242  wxPoint screen_pos =
2243  ClientToScreen(wxPoint(ccx + ccsx - consx - 2, ccy + yOffset));
2244  console->Move(screen_pos);
2245 }
2246 
2247 void MyFrame::UpdateAllFonts() {
2248  if (console) {
2249  console->UpdateFonts();
2250  // Reposition console
2251  PositionConsole();
2252  }
2253 
2254  // Close and destroy any persistent dialogs, so that new fonts will be
2255  // utilized
2256  DestroyPersistentDialogs();
2257 
2258  if (pWayPointMan) pWayPointMan->ClearRoutePointFonts();
2259 
2260  RefreshAllCanvas();
2261 }
2262 
2263 void MyFrame::DestroyPersistentDialogs() {
2264  if (g_pais_query_dialog_active) {
2265  g_pais_query_dialog_active->Hide();
2266  g_pais_query_dialog_active->Destroy();
2267  g_pais_query_dialog_active = NULL;
2268  }
2269 
2270  if (RoutePropDlgImpl::getInstanceFlag() && pRoutePropDialog) {
2271  pRoutePropDialog->Hide();
2272  pRoutePropDialog->Destroy();
2273  pRoutePropDialog = NULL;
2274  }
2275 
2276  if (TrackPropDlg::getInstanceFlag() && pTrackPropDialog) {
2277  pTrackPropDialog->Hide();
2278  pTrackPropDialog->Destroy();
2279  pTrackPropDialog = NULL;
2280  }
2281 
2282  if (g_pMarkInfoDialog) {
2283  g_pMarkInfoDialog->Hide();
2284  g_pMarkInfoDialog->Destroy();
2285  g_pMarkInfoDialog = NULL;
2286  }
2287 
2288  if (g_pObjectQueryDialog) {
2289  g_pObjectQueryDialog->Hide();
2290  g_pObjectQueryDialog->Destroy();
2291  g_pObjectQueryDialog = NULL;
2292  }
2293 }
2294 
2295 void MyFrame::RefreshGroupIndices(void) {
2296  // ..For each canvas...
2297  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2298  ChartCanvas *cc = g_canvasArray.Item(i);
2299  if (cc) cc->canvasRefreshGroupIndex();
2300  }
2301 }
2302 
2303 void MyFrame::OnToolLeftClick(wxCommandEvent &event) {
2304  if (g_MainToolbar) g_MainToolbar->HideTooltip();
2305 
2306 
2307  switch (event.GetId()) {
2308  case ID_MENU_SCALE_OUT:
2309  DoStackDelta(GetPrimaryCanvas(), 1);
2310  DoChartUpdate();
2311  break;
2312 
2313  case ID_MENU_SCALE_IN:
2314  DoStackDelta(GetPrimaryCanvas(), -1);
2315  DoChartUpdate();
2316  break;
2317 
2318  case ID_MENU_ZOOM_IN: {
2319  if (GetFocusCanvas()) {
2320  GetFocusCanvas()->ZoomCanvas(g_plus_minus_zoom_factor, false);
2321  }
2322  break;
2323  }
2324 
2325  case ID_MENU_ZOOM_OUT: {
2326  if (GetFocusCanvas()) {
2327  GetFocusCanvas()->ZoomCanvas(1.0 / g_plus_minus_zoom_factor, false);
2328  }
2329  break;
2330  }
2331 
2332  case ID_MENU_ROUTE_NEW: {
2333  if (GetFocusCanvas()) {
2334  if (0 == GetFocusCanvas()->m_routeState) {
2335  GetFocusCanvas()->StartRoute();
2336  } else {
2337  GetFocusCanvas()->FinishRoute();
2338  }
2339  }
2340  break;
2341  }
2342 
2343  case ID_MENU_TOOL_MEASURE: {
2344  GetPrimaryCanvas()->StartMeasureRoute();
2345  break;
2346  }
2347 
2348  case ID_MENU_MARK_BOAT: {
2349  DropMarker(true);
2350  break;
2351  }
2352 
2353  case ID_MENU_MARK_CURSOR: {
2354  DropMarker(false);
2355  break;
2356  }
2357 
2358  case ID_MENU_NAV_FOLLOW: {
2359  if (gFrame->GetPrimaryCanvas())
2360  gFrame->GetPrimaryCanvas()->TogglebFollow();
2361  break;
2362  }
2363 
2364  case ID_MENU_CHART_OUTLINES: {
2365  ToggleChartOutlines(GetFocusCanvas());
2366  break;
2367  }
2368 
2369  case ID_MENU_CHART_QUILTING: {
2370  ToggleQuiltMode(GetFocusCanvas());
2371  break;
2372  }
2373 
2374  case ID_MENU_UI_CHARTBAR: {
2375  ToggleChartBar(GetFocusCanvas());
2376  break;
2377  }
2378 
2379  case ID_MENU_ENC_TEXT:
2380  case ID_ENC_TEXT: {
2381  ToggleENCText(GetFocusCanvas());
2382  break;
2383  }
2384  case ID_MENU_ENC_LIGHTS: {
2385  ToggleLights(GetFocusCanvas());
2386  break;
2387  }
2388  case ID_MENU_ENC_SOUNDINGS: {
2389  ToggleSoundings(GetFocusCanvas());
2390  break;
2391  }
2392  case ID_MENU_ENC_ANCHOR: {
2393  ToggleAnchor(GetFocusCanvas());
2394  break;
2395  }
2396  case ID_MENU_ENC_DATA_QUALITY: {
2397  ToggleDataQuality(GetFocusCanvas());
2398  break;
2399  }
2400  case ID_MENU_SHOW_NAVOBJECTS: {
2401  ToggleNavobjects(GetFocusCanvas());
2402  break;
2403  }
2404 
2405  case ID_MENU_AIS_TARGETS: {
2406  ToggleAISDisplay(GetFocusCanvas());
2407  break;
2408  }
2409  case ID_MENU_AIS_MOORED_TARGETS: {
2410  g_bHideMoored = !g_bHideMoored;
2411  break;
2412  }
2413  case ID_MENU_AIS_SCALED_TARGETS: {
2414  ToggleAISMinimizeTargets(GetFocusCanvas());
2415  break;
2416  }
2417 
2418  case ID_MENU_AIS_TARGETLIST: {
2419  if (GetPrimaryCanvas()) GetPrimaryCanvas()->ShowAISTargetList();
2420  break;
2421  }
2422 
2423  case ID_MENU_AIS_TRACKS: {
2424  g_bAISShowTracks = !g_bAISShowTracks;
2425  SetMenubarItemState(ID_MENU_AIS_TRACKS, g_bAISShowTracks);
2426  break;
2427  }
2428 
2429  case ID_MENU_AIS_CPADIALOG: {
2430  g_bAIS_CPA_Alert = !g_bAIS_CPA_Alert;
2431  SetMenubarItemState(ID_MENU_AIS_CPADIALOG, g_bAIS_CPA_Alert);
2432  m_pMenuBar->Enable(ID_MENU_AIS_CPASOUND, g_bAIS_CPA_Alert);
2433  if (g_bAIS_CPA_Alert){
2434  SetMenubarItemState(ID_MENU_AIS_CPASOUND, g_bAIS_CPA_Alert_Audio);
2435  }
2436  break;
2437  }
2438 
2439  case ID_MENU_AIS_CPASOUND: {
2440  g_bAIS_CPA_Alert_Audio = !g_bAIS_CPA_Alert_Audio;
2441  SetMenubarItemState(ID_MENU_AIS_CPASOUND, g_bAIS_CPA_Alert_Audio);
2442  break;
2443  }
2444 
2445  case ID_MENU_AIS_CPAWARNING: {
2446  if (GetPrimaryCanvas()) GetPrimaryCanvas()->ToggleCPAWarn();
2447  SetMenubarItemState(ID_MENU_AIS_CPAWARNING, g_bCPAWarn);
2448  break;
2449  }
2450 
2451  case wxID_PREFERENCES:
2452  case ID_SETTINGS: {
2453  g_MainToolbar->HideTooltip();
2454  DoSettings();
2455  break;
2456  }
2457 
2458  case ID_MENU_SETTINGS_BASIC: {
2459 #ifdef __ANDROID__
2461  androidDisableFullScreen();
2462  g_MainToolbar->HideTooltip();
2463  DoAndroidPreferences();
2464 #else
2465  DoSettings();
2466 #endif
2467  break;
2468  }
2469 
2470  case ID_MENU_UI_FULLSCREEN: {
2471  ToggleFullScreen();
2472  break;
2473  }
2474 
2475  case ID_MENU_SHOW_CURRENTS: {
2476  GetFocusCanvas()->ShowCurrents(!GetFocusCanvas()->GetbShowCurrent());
2477  GetFocusCanvas()->ReloadVP();
2478  GetFocusCanvas()->Refresh(false);
2479  break;
2480  }
2481 
2482  case ID_MENU_SHOW_TIDES: {
2483  GetFocusCanvas()->ShowTides(!GetFocusCanvas()->GetbShowTide());
2484  GetFocusCanvas()->ReloadVP();
2485  GetFocusCanvas()->Refresh(false);
2486  break;
2487  }
2488 
2489  case wxID_ABOUT:
2490  case ID_ABOUT: {
2491  g_Platform->DoHelpDialog();
2492  break;
2493  }
2494 
2495  case wxID_HELP: {
2496  g_Platform->LaunchLocalHelp();
2497  break;
2498  }
2499 
2500  case ID_PRINT: {
2501  DoPrint();
2502  break;
2503  }
2504 
2505  case ID_MENU_UI_COLSCHEME:
2506  case ID_COLSCHEME: {
2507  ToggleColorScheme();
2508  break;
2509  }
2510 
2511  case ID_TBEXIT: {
2512  Close();
2513  break;
2514  }
2515 
2516  case ID_MENU_OQUIT: {
2517  Close();
2518  break;
2519  }
2520 
2521  case ID_MENU_ROUTE_MANAGER:
2522  case ID_ROUTEMANAGER: {
2523  pRouteManagerDialog = RouteManagerDialog::getInstance(
2524  this); // There is one global instance of the Dialog
2525 
2526  if (pRouteManagerDialog->IsShown())
2527  pRouteManagerDialog->Hide();
2528  else {
2529  pRouteManagerDialog->UpdateRouteListCtrl();
2530  pRouteManagerDialog->UpdateTrkListCtrl();
2531  pRouteManagerDialog->UpdateWptListCtrl();
2532  pRouteManagerDialog->UpdateLayListCtrl();
2533 
2534  pRouteManagerDialog->Show();
2535 
2536  // Required if RMDialog is not STAY_ON_TOP
2537 #ifdef __WXOSX__
2538  pRouteManagerDialog->Centre();
2539  pRouteManagerDialog->Raise();
2540 #endif
2541  }
2542  break;
2543  }
2544 
2545  case ID_MENU_NAV_TRACK:
2546  case ID_TRACK: {
2547  if (!g_bTrackActive) {
2548  TrackOn();
2549  g_bTrackCarryOver = true;
2550  } else {
2551  TrackOff(true); // catch the last point
2552  if (pConfig && pConfig->IsChangesFileDirty()) {
2553  pConfig->UpdateNavObj(true);
2554  }
2555  g_bTrackCarryOver = false;
2556  RefreshAllCanvas(true);
2557  }
2558  break;
2559  }
2560 
2561  case ID_MENU_CHART_NORTHUP: {
2562  SetUpMode(GetPrimaryCanvas(), NORTH_UP_MODE);
2563  break;
2564  }
2565  case ID_MENU_CHART_COGUP: {
2566  SetUpMode(GetPrimaryCanvas(), COURSE_UP_MODE);
2567  break;
2568  }
2569  case ID_MENU_CHART_HEADUP: {
2570  SetUpMode(GetPrimaryCanvas(), HEAD_UP_MODE);
2571  break;
2572  }
2573 
2574  case ID_MENU_MARK_MOB:
2575  case ID_MOB: {
2576  ActivateMOB();
2577  break;
2578  }
2579 
2580  case ID_MASTERTOGGLE: {
2581  if (g_MainToolbar) {
2582  wxString tip = _("Show Toolbar");
2583  if (!g_bmasterToolbarFull) tip = _("Hide Toolbar");
2584  if (g_MainToolbar->GetToolbar())
2585  g_MainToolbar->GetToolbar()->SetToolShortHelp(ID_MASTERTOGGLE, tip);
2586 
2587  g_bmasterToolbarFull = !g_bmasterToolbarFull;
2588 
2589 #ifdef __WXOSX__
2590  if (g_bmasterToolbarFull)
2591  m_nMasterToolCountShown =
2592  g_MainToolbar->GetToolCount() -
2593  1; // TODO disable animation on OSX. Maybe use fade effect?
2594  else
2595  m_nMasterToolCountShown = 2;
2596 #else
2597  m_nMasterToolCountShown =
2598  g_MainToolbar->GetToolShowCount(); // Current state
2599 #endif
2600  ToolbarAnimateTimer.Start(10, wxTIMER_ONE_SHOT);
2601  }
2602  break;
2603  }
2604 
2605  // Various command events coming from (usually) other threads,
2606  // used to control OCPN modes in a thread-safe way.
2607 
2608  case ID_CMD_SELECT_CHART_TYPE: {
2609  selectChartDisplay(event.GetExtraLong(), -1);
2610  break;
2611  }
2612 
2613  case ID_CMD_SELECT_CHART_FAMILY: {
2614  selectChartDisplay(-1, event.GetExtraLong());
2615  break;
2616  }
2617 
2618  case ID_CMD_APPLY_SETTINGS: {
2619  applySettingsString(event.GetString());
2620 #ifdef __ANDROID__
2621  androidRestoreFullScreen();
2622 #endif
2623 
2624  break;
2625  }
2626 
2627  case ID_CMD_NULL_REFRESH: {
2628  Refresh(true);
2629  break;
2630  }
2631 
2632  case ID_CMD_SETVP: {
2633  setStringVP(event.GetString());
2634  break;
2635  }
2636 
2637  case ID_CMD_INVALIDATE: {
2638  InvalidateAllGL();
2639  Refresh(true);
2640  break;
2641  }
2642 
2643  case ID_CMD_POST_JSON_TO_PLUGINS: {
2644  // Extract the Message ID which is embedded in the JSON string passed in
2645  // the event
2646  wxJSONValue root;
2647  wxJSONReader reader;
2648 
2649  int numErrors = reader.Parse(event.GetString(), &root);
2650  if (numErrors == 0) {
2651  if (root[_T("MessageID")].IsString()) {
2652  wxString MsgID = root[_T("MessageID")].AsString();
2653  SendPluginMessage(MsgID, event.GetString()); // Send to all PlugIns
2654  }
2655  }
2656 
2657  break;
2658  }
2659 
2660  case ID_DENSITY:
2661  case ID_RMINUS:
2662  case ID_RPLUS: {
2663  if (g_iENCToolbar ){
2664  g_iENCToolbar->OnToolLeftClick(event);
2665  }
2666  break;
2667  }
2668 
2669  default: {
2670  // Look for PlugIn tools
2671  // If found, make the callback.
2672  // TODO Modify this to allow multiple tools per plugin
2673  if (g_pi_manager) {
2674  g_MainToolbar->HideTooltip();
2675 
2676  ArrayOfPlugInToolbarTools tool_array =
2677  g_pi_manager->GetPluginToolbarToolArray();
2678  for (unsigned int i = 0; i < tool_array.size(); i++) {
2679  PlugInToolbarToolContainer *pttc = tool_array[i];
2680  if (event.GetId() == pttc->id) {
2681  if (pttc->m_pplugin)
2682  pttc->m_pplugin->OnToolbarToolCallback(pttc->id);
2683  return; // required to prevent event.Skip() being called
2684  }
2685  }
2686  }
2687 
2688  // If we didn't handle the event, allow it to bubble up to other handlers.
2689  // This is required for the system menu items (Hide, etc.) on OS X to work.
2690  // This must only be called if we did NOT handle the event, otherwise it
2691  // stops the menu items from working on Windows.
2692  event.Skip();
2693 
2694  break;
2695  }
2696 
2697  } // switch
2698 
2699  // Finally, force a refresh of the main toolbar
2700  if (g_MainToolbar)
2701  g_MainToolbar->Realize();
2702 
2703 
2704 }
2705 
2706 bool MyFrame::SetGlobalToolbarViz(bool viz) {
2707  bool viz_now = g_bmasterToolbarFull;
2708 
2709  g_MainToolbar->HideTooltip();
2710  wxString tip = _("Show Toolbar");
2711  if (viz) {
2712  tip = _("Hide Toolbar");
2713  if (g_MainToolbar->GetToolbar())
2714  g_MainToolbar->GetToolbar()->SetToolShortHelp(ID_MASTERTOGGLE, tip);
2715  }
2716 
2717  bool toggle = false;
2718  if (viz && !g_bmasterToolbarFull)
2719  toggle = true;
2720 
2721  else if (!viz && g_bmasterToolbarFull)
2722  toggle = true;
2723 
2724  if (toggle) {
2725  g_bmasterToolbarFull = !g_bmasterToolbarFull;
2726 
2727 #ifdef __WXOSX__
2728  if (g_bmasterToolbarFull)
2729  m_nMasterToolCountShown =
2730  g_MainToolbar->GetToolCount() -
2731  1; // TODO disable animation on OSX. Maybe use fade effect?
2732  else
2733  m_nMasterToolCountShown = 2;
2734 #else
2735  m_nMasterToolCountShown =
2736  g_MainToolbar->GetToolShowCount(); // Current state
2737 #endif
2738  ToolbarAnimateTimer.Start(10, wxTIMER_ONE_SHOT);
2739  }
2740 
2741  return viz_now;
2742 }
2743 
2744 void MyFrame::ScheduleSettingsDialog() {
2745  wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2746  evt.SetId(ID_SETTINGS /*ID_MENU_SETTINGS_BASIC*/);
2747  GetEventHandler()->AddPendingEvent(evt);
2748 }
2749 
2750 ChartCanvas *MyFrame::GetFocusCanvas() {
2751  if ((g_canvasConfig != 0) && g_focusCanvas) // multi-canvas?
2752  return g_focusCanvas;
2753  else
2754  return GetPrimaryCanvas();
2755 }
2756 
2757 void MyFrame::OnToolbarAnimateTimer(wxTimerEvent &event) {
2758  if (g_bmasterToolbarFull) {
2759 #ifndef OCPN_TOOLBAR_ANIMATE
2760  m_nMasterToolCountShown = (int)g_MainToolbar->GetToolCount();
2761 #endif
2762 
2763  if (m_nMasterToolCountShown < (int)g_MainToolbar->GetToolCount()) {
2764  m_nMasterToolCountShown++;
2765  g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2766  g_MainToolbar->Realize();
2767  g_MainToolbar->RefreshToolbar();
2768 
2769  ToolbarAnimateTimer.Start(20, wxTIMER_ONE_SHOT);
2770  } else {
2771  g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2772  g_MainToolbar->GetToolbar()->InvalidateBitmaps();
2773  g_MainToolbar->Realize();
2774  g_MainToolbar->RefreshToolbar();
2775  }
2776  } else {
2777 #ifndef OCPN_TOOLBAR_ANIMATE
2778  m_nMasterToolCountShown = 1;
2779 #endif
2780  if (m_nMasterToolCountShown > 1) {
2781  m_nMasterToolCountShown--;
2782  g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2783  g_MainToolbar->Realize();
2784  g_MainToolbar->RefreshToolbar();
2785  ToolbarAnimateTimer.Start(10, wxTIMER_ONE_SHOT);
2786  } else {
2787  g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2788  g_MainToolbar->GetToolbar()->InvalidateBitmaps();
2789  g_MainToolbar->Realize();
2790  g_MainToolbar->RefreshToolbar();
2791  }
2792  }
2793 }
2794 
2795 void MyFrame::InvalidateAllGL() {
2796 #ifdef ocpnUSE_GL
2797  // For each canvas
2798  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2799  ChartCanvas *cc = g_canvasArray.Item(i);
2800  if (cc) {
2801  cc->InvalidateGL();
2802  cc->Refresh();
2803  }
2804  }
2805 #endif
2806 }
2807 
2808 void MyFrame::RefreshAllCanvas(bool bErase) {
2809  // For each canvas
2810  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2811  ChartCanvas *cc = g_canvasArray.Item(i);
2812  if (cc) {
2813  cc->Refresh(bErase);
2814  }
2815  }
2816 }
2817 
2818 void MyFrame::setStringVP(wxString VPS) {
2819  ChartCanvas *cc = GetPrimaryCanvas();
2820 
2821  if (!cc) return;
2822 
2823  wxStringTokenizer tkz(VPS, _T(";"));
2824 
2825  wxString token = tkz.GetNextToken();
2826  double lat = gLat;
2827  token.ToDouble(&lat);
2828 
2829  token = tkz.GetNextToken();
2830  double lon = gLon;
2831  token.ToDouble(&lon);
2832 
2833  token = tkz.GetNextToken();
2834  double scale_ppm = cc->GetVP().view_scale_ppm;
2835  token.ToDouble(&scale_ppm);
2836 
2837  cc->SetViewPoint(lat, lon, scale_ppm, 0, cc->GetVPRotation());
2838 }
2839 
2840 void MyFrame::DoSettings() {
2841  if (g_boptionsactive) return;
2842 
2843  bool bnewtoolbar = !(DoOptionsDialog() == 0);
2844 
2845  // Apply various system settings
2846  ApplyGlobalSettings(bnewtoolbar);
2847 
2848  // ..For each canvas...
2849  bool b_loadHarmonics = false;
2850  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2851  ChartCanvas *cc = g_canvasArray.Item(i);
2852  if (cc) {
2853  if (cc->GetbShowCurrent() || cc->GetbShowTide()) b_loadHarmonics = true;
2854  }
2855  }
2856  if (b_loadHarmonics) LoadHarmonics();
2857 
2858  // The chart display options may have changed, especially on S57 ENC,
2859  // So, flush the cache and redraw
2860  ReloadAllVP();
2861 }
2862 
2863 void MyFrame::ToggleChartBar(ChartCanvas *cc) {
2864  g_bShowChartBar = !g_bShowChartBar;
2865 
2866  if (g_bShowChartBar) cc->m_brepaint_piano = true;
2867 
2868  cc->ReloadVP(); // needed to set VP.pix_height
2869  Refresh();
2870 
2871  if (g_bShowChartBar) {
2872  DoChartUpdate();
2873  UpdateControlBar(cc);
2874  }
2875 
2876  SetMenubarItemState(ID_MENU_UI_CHARTBAR, g_bShowChartBar);
2877 }
2878 
2879 void MyFrame::ToggleColorScheme() {
2880  static bool lastIsNight;
2881  ColorScheme s = GetColorScheme();
2882  int is = (int)s;
2883  is++;
2884  if (lastIsNight && is == 3) // Back from step 3
2885  {
2886  is = 1;
2887  lastIsNight = false;
2888  } // Goto to Day
2889  if (lastIsNight) is = 2; // Back to Dusk on step 3
2890  if (is == 3) lastIsNight = true; // Step 2 Night
2891  s = (ColorScheme)is;
2892  if (s == N_COLOR_SCHEMES) s = GLOBAL_COLOR_SCHEME_RGB;
2893 
2894  SetAndApplyColorScheme(s);
2895 }
2896 
2897 void MyFrame::ToggleFullScreen() {
2898  bool to = !IsFullScreen();
2899 
2900 #ifdef __WXOSX__
2901  ShowFullScreen(to);
2902 #else
2903  long style = wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION;
2904  ; // | wxFULLSCREEN_NOMENUBAR;
2905  ShowFullScreen(to, style);
2906 #endif
2907 
2908  UpdateAllToolbars(global_color_scheme);
2909  //SurfaceAllCanvasToolbars();
2910  UpdateControlBar(GetPrimaryCanvas());
2911  Layout();
2912  TriggerRecaptureTimer();
2913 }
2914 
2915 void MyFrame::ActivateMOB(void) {
2916  // The MOB point
2917  wxDateTime mob_time = wxDateTime::Now();
2918  wxString mob_label(_("MAN OVERBOARD"));
2919  mob_label += _(" at ");
2920  mob_label += mob_time.FormatTime();
2921  mob_label += _(" on ");
2922  mob_label += mob_time.FormatISODate();
2923 
2924  RoutePoint *pWP_MOB =
2925  new RoutePoint(gLat, gLon, _T ( "mob" ), mob_label, wxEmptyString);
2926  pWP_MOB->SetShared(true);
2927  pWP_MOB->m_bIsolatedMark = true;
2928  pWP_MOB->SetWaypointArrivalRadius(
2929  -1.0); // Negative distance is code to signal "Never Arrive"
2930  pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
2931  pSelect->AddSelectableRoutePoint(gLat, gLon, pWP_MOB);
2932  pConfig->AddNewWayPoint(pWP_MOB, -1); // use auto next num
2933 
2934  if (bGPSValid && !std::isnan(gCog) && !std::isnan(gSog)) {
2935  // Create a point that is one mile along the present course
2936  double zlat, zlon;
2937  ll_gc_ll(gLat, gLon, gCog, 1.0, &zlat, &zlon);
2938 
2939  RoutePoint *pWP_src =
2940  new RoutePoint(zlat, zlon, g_default_wp_icon,
2941  wxString(_("1.0 NM along COG")), wxEmptyString);
2942  pSelect->AddSelectableRoutePoint(zlat, zlon, pWP_src);
2943 
2944  Route *temp_route = new Route();
2945  pRouteList->Append(temp_route);
2946 
2947  temp_route->AddPoint(pWP_src);
2948  temp_route->AddPoint(pWP_MOB);
2949 
2950  pSelect->AddSelectableRouteSegment(gLat, gLon, zlat, zlon, pWP_src, pWP_MOB,
2951  temp_route);
2952 
2953  temp_route->m_RouteNameString = _("Temporary MOB Route");
2954  temp_route->m_RouteStartString = _("Assumed 1 Mile Point");
2955  ;
2956  temp_route->m_RouteEndString = mob_label;
2957 
2958  temp_route->m_bDeleteOnArrival = false;
2959 
2960  temp_route->SetRouteArrivalRadius(-1.0); // never arrives
2961 
2962  if (g_pRouteMan->GetpActiveRoute()) g_pRouteMan->DeactivateRoute();
2963  g_pRouteMan->ActivateRoute(temp_route, pWP_MOB);
2964 
2965  wxJSONValue v;
2966  v[_T("GUID")] = temp_route->m_GUID;
2967  wxString msg_id(_T("OCPN_MAN_OVERBOARD"));
2968  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
2969  }
2970 
2971  if (RouteManagerDialog::getInstanceFlag()) {
2972  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
2973  pRouteManagerDialog->UpdateRouteListCtrl();
2974  pRouteManagerDialog->UpdateWptListCtrl();
2975  }
2976  }
2977 
2978  InvalidateAllGL();
2979  RefreshAllCanvas(false);
2980 
2981  wxString mob_message(_("MAN OVERBOARD"));
2982  mob_message += _(" Time: ");
2983  mob_message += mob_time.Format();
2984  mob_message += _(" Position: ");
2985  mob_message += toSDMM(1, gLat);
2986  mob_message += _T(" ");
2987  mob_message += toSDMM(2, gLon);
2988  wxLogMessage(mob_message);
2989 }
2990 void MyFrame::TrackOn(void) {
2991  g_bTrackActive = true;
2992  g_pActiveTrack = new ActiveTrack();
2993 
2994  g_TrackList.push_back(g_pActiveTrack);
2995  if (pConfig) pConfig->AddNewTrack(g_pActiveTrack);
2996 
2997  g_pActiveTrack->Start();
2998 
2999  // The main toolbar may still be NULL here, and we will do nothing...
3000  SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
3001  if (g_MainToolbar)
3002  g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Disable Tracking"));
3003 
3004  SetMenubarItemState(ID_MENU_NAV_TRACK, g_bTrackActive);
3005 
3006 #ifdef __ANDROID__
3007  androidSetTrackTool(true);
3008 #endif
3009 
3010  if (RouteManagerDialog::getInstanceFlag()) {
3011  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3012  pRouteManagerDialog->UpdateTrkListCtrl();
3013  pRouteManagerDialog->UpdateRouteListCtrl();
3014  }
3015  }
3016 
3017  wxJSONValue v;
3018  wxDateTime now;
3019  now = now.Now().ToUTC();
3020  wxString name = g_pActiveTrack->GetName();
3021  if (name.IsEmpty()) {
3022  TrackPoint *tp = g_pActiveTrack->GetPoint(0);
3023  if (tp->GetCreateTime().IsValid())
3024  name = tp->GetCreateTime().FormatISODate() + _T(" ") +
3025  tp->GetCreateTime().FormatISOTime();
3026  else
3027  name = _("(Unnamed Track)");
3028  }
3029  v[_T("Name")] = name;
3030  v[_T("GUID")] = g_pActiveTrack->m_GUID;
3031  wxString msg_id(_T("OCPN_TRK_ACTIVATED"));
3032  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
3033  g_FlushNavobjChangesTimeout =
3034  30; // Every thirty seconds, consider flushing navob changes
3035 }
3036 
3037 Track *MyFrame::TrackOff(bool do_add_point) {
3038  Track *return_val = g_pActiveTrack;
3039 
3040  if (g_pActiveTrack) {
3041  wxJSONValue v;
3042  wxString msg_id(_T("OCPN_TRK_DEACTIVATED"));
3043  v[_T("GUID")] = g_pActiveTrack->m_GUID;
3044  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
3045 
3046  g_pActiveTrack->Stop(do_add_point);
3047 
3048  if (g_pActiveTrack->GetnPoints() < 2) {
3049  RoutemanGui(*g_pRouteMan).DeleteTrack(g_pActiveTrack);
3050  return_val = NULL;
3051  } else {
3052  if (g_bTrackDaily) {
3053  Track *pExtendTrack = g_pActiveTrack->DoExtendDaily();
3054  if (pExtendTrack) {
3055  RoutemanGui(*g_pRouteMan).DeleteTrack(g_pActiveTrack);
3056  return_val = pExtendTrack;
3057  }
3058  }
3059  }
3060  g_pActiveTrack = NULL;
3061  }
3062 
3063  g_bTrackActive = false;
3064 
3065  if (RouteManagerDialog::getInstanceFlag()) {
3066  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3067  pRouteManagerDialog->UpdateTrkListCtrl();
3068  pRouteManagerDialog->UpdateRouteListCtrl();
3069  }
3070  }
3071 
3072  SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
3073  if (g_MainToolbar)
3074  g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Enable Tracking"));
3075  SetMenubarItemState(ID_MENU_NAV_TRACK, g_bTrackActive);
3076 
3077 #ifdef __ANDROID__
3078  androidSetTrackTool(false);
3079 #endif
3080 
3081  g_FlushNavobjChangesTimeout =
3082  600; // Revert to checking/flushing navob changes every 5 minutes
3083 
3084  return return_val;
3085 }
3086 
3087 bool MyFrame::ShouldRestartTrack(void) {
3088  if (!g_pActiveTrack || !g_bTrackDaily) return false;
3089  time_t now = wxDateTime::Now().GetTicks();
3090  time_t today = wxDateTime::Today().GetTicks();
3091  int rotate_at = 0;
3092  switch (g_track_rotate_time_type) {
3093  case TIME_TYPE_LMT:
3094  rotate_at = g_track_rotate_time + wxRound(gLon * 3600. / 15.);
3095  break;
3096  case TIME_TYPE_COMPUTER:
3097  rotate_at = g_track_rotate_time;
3098  break;
3099  case TIME_TYPE_UTC:
3100  int utc_offset =
3101  wxDateTime::Now().GetTicks() - wxDateTime::Now().ToUTC().GetTicks();
3102  rotate_at = g_track_rotate_time + utc_offset;
3103  break;
3104  }
3105  if (rotate_at > 86400)
3106  rotate_at -= 86400;
3107  else if (rotate_at < 0)
3108  rotate_at += 86400;
3109  if (now >= m_last_track_rotation_ts + 86400 - 3600 &&
3110  now - today >= rotate_at) {
3111  if (m_last_track_rotation_ts == 0) {
3112  if (now - today > rotate_at)
3113  m_last_track_rotation_ts = today + rotate_at;
3114  else
3115  m_last_track_rotation_ts = today + rotate_at - 86400;
3116  return false;
3117  }
3118  m_last_track_rotation_ts = now;
3119  return true;
3120  }
3121  return false;
3122 }
3123 
3124 void MyFrame::TrackDailyRestart(void) {
3125  if (!g_pActiveTrack) return;
3126 
3127  Track *pPreviousTrack = TrackOff(true);
3128  if (pConfig && pConfig->IsChangesFileDirty()) {
3129  pConfig->UpdateNavObj(true);
3130  }
3131 
3132  TrackOn();
3133 
3134  // Set the restarted track's current state such that the current track
3135  // point's attributes match the attributes of the last point of the track
3136  // that was just stopped at midnight.
3137 
3138  if (pPreviousTrack) {
3139  TrackPoint *pMidnightPoint = pPreviousTrack->GetLastPoint();
3140  g_pActiveTrack->AdjustCurrentTrackPoint(pMidnightPoint);
3141  }
3142 
3143  if (RouteManagerDialog::getInstanceFlag()) {
3144  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3145  pRouteManagerDialog->UpdateTrkListCtrl();
3146  pRouteManagerDialog->UpdateRouteListCtrl();
3147  }
3148  }
3149 }
3150 
3151 void MyFrame::SetUpMode(ChartCanvas *cc, int mode) {
3152  if (cc) {
3153  cc->SetUpMode(mode);
3154 
3155  SetMenubarItemState(ID_MENU_CHART_COGUP, mode == COURSE_UP_MODE);
3156  SetMenubarItemState(ID_MENU_CHART_NORTHUP, mode == NORTH_UP_MODE);
3157  SetMenubarItemState(ID_MENU_CHART_HEADUP, mode == HEAD_UP_MODE);
3158 
3159  if (m_pMenuBar)
3160  m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
3161  }
3162 }
3163 
3164 void MyFrame::ToggleENCText(ChartCanvas *cc) {
3165  cc->SetShowENCText(!cc->GetShowENCText());
3166 
3167  SetMenubarItemState(ID_MENU_ENC_TEXT, cc->GetShowENCText());
3168 
3169  // if(g_pi_manager)
3170  // g_pi_manager->SendConfigToAllPlugIns();
3171 
3172  ReloadAllVP();
3173 }
3174 
3175 void MyFrame::SetENCDisplayCategory(ChartCanvas *cc, enum _DisCat nset) {
3176  if (ps52plib) {
3177  if (cc) {
3178  cc->SetENCDisplayCategory(nset);
3179 
3180  UpdateGlobalMenuItems();
3181 
3182  /* if(g_pi_manager)
3183  g_pi_manager->SendConfigToAllPlugIns();
3184  */
3185  ReloadAllVP();
3186  }
3187  }
3188 }
3189 
3190 void MyFrame::ToggleSoundings(ChartCanvas *cc) {
3191  cc->SetShowENCDepth(!cc->GetShowENCDepth());
3192 
3193  SetMenubarItemState(ID_MENU_ENC_SOUNDINGS, cc->GetShowENCDepth());
3194 
3195  // if(g_pi_manager)
3196  // g_pi_manager->SendConfigToAllPlugIns();
3197 
3198  ReloadAllVP();
3199 }
3200 
3201 bool MyFrame::ToggleLights(ChartCanvas *cc) {
3202  cc->SetShowENCLights(!cc->GetShowENCLights());
3203 
3204  SetMenubarItemState(ID_MENU_ENC_LIGHTS, cc->GetShowENCLights());
3205 
3206  if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns(true);
3207 
3208  ReloadAllVP();
3209 
3210  return true;
3211 }
3212 
3213 #if 0
3214 void MyFrame::ToggleRocks( void )
3215 {
3216  if( ps52plib ) {
3217  int vis = 0;
3218  // Need to loop once for UWTROC, which is our "master", then for
3219  // other categories, since order is unknown?
3220  for( unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount(); iPtr++ ) {
3221  OBJLElement *pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3222  if( !strncmp( pOLE->OBJLName, "UWTROC", 6 ) ) {
3223  pOLE->nViz = !pOLE->nViz;
3224  vis = pOLE->nViz;
3225  }
3226  }
3227  for( unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount(); iPtr++ ) {
3228  OBJLElement *pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3229  if( !strncmp( pOLE->OBJLName, "OBSTRN", 6 ) ) {
3230  pOLE->nViz = vis;
3231  }
3232  if( !strncmp( pOLE->OBJLName, "WRECKS", 6 ) ) {
3233  pOLE->nViz = vis;
3234  }
3235  }
3236  ps52plib->GenerateStateHash();
3237  ReloadAllVP();
3238  }
3239 }
3240 #endif
3241 
3242 void MyFrame::ToggleAnchor(ChartCanvas *cc) {
3243  cc->SetShowENCAnchor(!cc->GetShowENCAnchor());
3244 
3245  SetMenubarItemState(ID_MENU_ENC_ANCHOR, cc->GetShowENCAnchor());
3246 
3247  if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns();
3248 
3249  ReloadAllVP();
3250 }
3251 
3252 void MyFrame::ToggleDataQuality(ChartCanvas *cc) {
3253  cc->SetShowENCDataQual(!cc->GetShowENCDataQual());
3254 
3255  SetMenubarItemState(ID_MENU_ENC_DATA_QUALITY, cc->GetShowENCDataQual());
3256 
3257  if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns();
3258 
3259  ReloadAllVP();
3260 }
3261 
3262 void MyFrame::TogglebFollow(ChartCanvas *cc) {
3263  if (!cc->m_bFollow)
3264  SetbFollow(cc);
3265  else
3266  ClearbFollow(cc);
3267 }
3268 
3269 void MyFrame::ToggleNavobjects(ChartCanvas *cc) {
3270  cc->m_bShowNavobjects = !cc->m_bShowNavobjects;
3271  SetMenubarItemState(ID_MENU_SHOW_NAVOBJECTS, cc->m_bShowNavobjects);
3272  cc->Refresh();
3273 }
3274 
3275 void MyFrame::ToggleAISDisplay(ChartCanvas *cc) {
3276  cc->SetShowAIS(!cc->GetShowAIS());
3277  SetMenubarItemState(ID_MENU_AIS_TARGETS, cc->GetShowAIS());
3278  cc->Refresh();
3279 }
3280 
3281 void MyFrame::ToggleAISMinimizeTargets(ChartCanvas *cc) {
3282  cc->SetAttenAIS(!cc->GetAttenAIS());
3283  SetMenubarItemState(ID_MENU_AIS_SCALED_TARGETS, cc->GetAttenAIS());
3284  cc->Refresh();
3285 }
3286 
3287 void MyFrame::SetbFollow(ChartCanvas *cc) {
3288  JumpToPosition(cc, gLat, gLon, cc->GetVPScale());
3289  cc->m_bFollow = true;
3290 
3291  //cc->SetCanvasToolbarItemState(ID_FOLLOW, true);
3292  SetMenubarItemState(ID_MENU_NAV_FOLLOW, true);
3293 
3294  DoChartUpdate();
3295  cc->ReloadVP();
3296  SetChartUpdatePeriod();
3297 }
3298 
3299 void MyFrame::ClearbFollow(ChartCanvas *cc) {
3300  // Center the screen on the GPS position, for lack of a better place
3301  vLat = gLat;
3302  vLon = gLon;
3303 
3304  cc->m_bFollow = false;
3305  //cc->SetCanvasToolbarItemState(ID_FOLLOW, false);
3306  SetMenubarItemState(ID_MENU_NAV_FOLLOW, false);
3307 
3308  DoChartUpdate();
3309  cc->ReloadVP();
3310  SetChartUpdatePeriod();
3311 }
3312 
3313 void MyFrame::ToggleChartOutlines(ChartCanvas *cc) {
3314  cc->SetShowOutlines(!cc->GetShowOutlines());
3315 
3316  RefreshAllCanvas(false);
3317 
3318 #ifdef ocpnUSE_GL // opengl renders chart outlines as part of the chart this
3319  // needs a full refresh
3320  if (g_bopengl) InvalidateAllGL();
3321 #endif
3322 
3323  SetMenubarItemState(ID_MENU_CHART_OUTLINES, cc->GetShowOutlines());
3324 }
3325 
3326 void MyFrame::ToggleTestPause(void) { g_bPauseTest = !g_bPauseTest; }
3327 
3328 void MyFrame::SetMenubarItemState(int item_id, bool state) {
3329  if (m_pMenuBar) {
3330  bool enabled = m_pMenuBar->IsEnabled(item_id);
3331  m_pMenuBar->Enable(item_id, false);
3332  m_pMenuBar->Check(item_id, state);
3333  m_pMenuBar->Enable(item_id, enabled);
3334  }
3335 }
3336 
3337 void MyFrame::SetMasterToolbarItemState(int tool_id, bool state) {
3338  if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3339  g_MainToolbar->GetToolbar()->ToggleTool(tool_id, state);
3340  g_MainToolbar->Realize();
3341  }
3342 }
3343 
3344 void MyFrame::SetToolbarItemBitmaps(int tool_id, wxBitmap *bmp,
3345  wxBitmap *bmpRollover) {
3346  if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3347  g_MainToolbar->GetToolbar()->SetToolBitmaps(tool_id, bmp, bmpRollover);
3348  g_MainToolbar->Realize();
3349  }
3350 }
3351 
3352 void MyFrame::SetToolbarItemSVG(int tool_id, wxString normalSVGfile,
3353  wxString rolloverSVGfile,
3354  wxString toggledSVGfile) {
3355  if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3356  g_MainToolbar->GetToolbar()->SetToolBitmapsSVG(
3357  tool_id, normalSVGfile, rolloverSVGfile, toggledSVGfile);
3358  }
3359 }
3360 
3361 void MyFrame::ApplyGlobalSettings(bool bnewtoolbar) {
3362  // ShowDebugWindow as a wxStatusBar
3363  m_StatusBarFieldCount = g_Platform->GetStatusBarFieldCount();
3364 
3365 #ifdef __WXMSW__
3366  UseNativeStatusBar(false); // better for MSW, undocumented in frame.cpp
3367 #endif
3368 
3369  if (g_bShowStatusBar) {
3370  if (!m_pStatusBar) {
3371  m_pStatusBar =
3372  CreateStatusBar(m_StatusBarFieldCount, 0); // No wxST_SIZEGRIP needed
3373  ApplyGlobalColorSchemetoStatusBar();
3374  }
3375 
3376  } else {
3377  if (m_pStatusBar) {
3378  m_pStatusBar->Destroy();
3379  m_pStatusBar = NULL;
3380  SetStatusBar(NULL);
3381  }
3382  }
3383 
3384  wxSize lastOptSize = options_lastWindowSize;
3385  SendSizeEvent();
3386 
3387  BuildMenuBar();
3388 
3389  SendSizeEvent();
3390  options_lastWindowSize = lastOptSize;
3391 
3392  if (bnewtoolbar) UpdateAllToolbars(global_color_scheme);
3393 }
3394 
3395 wxString _menuText(wxString name, wxString shortcut) {
3396  wxString menutext;
3397  menutext << name;
3398 #ifndef __ANDROID__
3399  menutext << _T("\t") << shortcut;
3400 #endif
3401  return menutext;
3402 }
3403 
3404 void MyFrame::BuildMenuBar(void) {
3405  /*
3406  * Menu Bar - add or remove it if necessary, and update the state of the menu
3407  * items
3408  */
3409 #ifdef __WXOSX__
3410  bool showMenuBar = true; // the menu bar is always visible in OS X
3411 #else
3412  bool showMenuBar = g_bShowMenuBar; // get visibility from options
3413 
3414  if (!showMenuBar &&
3415  g_bTempShowMenuBar) // allows pressing alt to temporarily show
3416  showMenuBar = true;
3417 #endif
3418 
3419  if (showMenuBar) {
3420  // Menu bar has some dependencies on S52 PLIB, so be sure it is loaded.
3421  LoadS57();
3422 
3423  if (!m_pMenuBar) { // add the menu bar if it is enabled
3424  m_pMenuBar = new wxMenuBar();
3425  RegisterGlobalMenuItems();
3426  SetMenuBar(m_pMenuBar); // must be after RegisterGlobalMenuItems for wx
3427  // to populate the OS X App Menu correctly
3428  }
3429 
3430  UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks
3431  // etc.)
3432  } else {
3433  if (m_pMenuBar) { // remove the menu bar if it is disabled
3434  SetMenuBar(NULL);
3435  m_pMenuBar->Destroy();
3436  m_pMenuBar = NULL;
3437  }
3438  }
3439 }
3440 
3441 void MyFrame::RegisterGlobalMenuItems() {
3442  if (!m_pMenuBar) return; // if there isn't a menu bar
3443 
3444  wxMenu *nav_menu = new wxMenu();
3445  nav_menu->AppendCheckItem(ID_MENU_NAV_FOLLOW,
3446  _menuText(_("Auto Follow"), _T("Ctrl-A")));
3447  nav_menu->AppendCheckItem(ID_MENU_NAV_TRACK, _("Enable Tracking"));
3448  nav_menu->AppendSeparator();
3449  nav_menu->AppendRadioItem(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
3450  nav_menu->AppendRadioItem(ID_MENU_CHART_COGUP, _("Course Up Mode"));
3451  nav_menu->AppendRadioItem(ID_MENU_CHART_HEADUP, _("Head Up Mode"));
3452  nav_menu->AppendSeparator();
3453 #ifndef __WXOSX__
3454  nav_menu->Append(ID_MENU_ZOOM_IN, _menuText(_("Zoom In"), _T("+")));
3455  nav_menu->Append(ID_MENU_ZOOM_OUT, _menuText(_("Zoom Out"), _T("-")));
3456 #else
3457  nav_menu->Append(ID_MENU_ZOOM_IN, _menuText(_("Zoom In"), _T("Alt-+")));
3458  nav_menu->Append(ID_MENU_ZOOM_OUT, _menuText(_("Zoom Out"), _T("Alt--")));
3459 #endif
3460  nav_menu->AppendSeparator();
3461  nav_menu->Append(ID_MENU_SCALE_IN,
3462  _menuText(_("Larger Scale Chart"), _T("Ctrl-Left")));
3463  nav_menu->Append(ID_MENU_SCALE_OUT,
3464  _menuText(_("Smaller Scale Chart"), _T("Ctrl-Right")));
3465 #ifndef __WXOSX__
3466  nav_menu->AppendSeparator();
3467  nav_menu->Append(ID_MENU_OQUIT, _menuText(_("Exit OpenCPN"), _T("Ctrl-Q")));
3468 #endif
3469  m_pMenuBar->Append(nav_menu, _("&Navigate"));
3470 
3471  wxMenu *view_menu = new wxMenu();
3472 #ifndef __WXOSX__
3473  view_menu->AppendCheckItem(ID_MENU_CHART_QUILTING,
3474  _menuText(_("Enable Chart Quilting"), _T("Q")));
3475  view_menu->AppendCheckItem(ID_MENU_CHART_OUTLINES,
3476  _menuText(_("Show Chart Outlines"), _T("O")));
3477 #else
3478  view_menu->AppendCheckItem(
3479  ID_MENU_CHART_QUILTING,
3480  _menuText(_("Enable Chart Quilting"), _T("Alt-Q")));
3481  view_menu->AppendCheckItem(ID_MENU_CHART_OUTLINES,
3482  _menuText(_("Show Chart Outlines"), _T("Alt-O")));
3483 #endif
3484  view_menu->AppendCheckItem(ID_MENU_UI_CHARTBAR,
3485  _menuText(_("Show Chart Bar"), _T("Ctrl-B")));
3486 
3487  view_menu->AppendSeparator();
3488 #ifndef __WXOSX__
3489  view_menu->AppendCheckItem(ID_MENU_ENC_TEXT,
3490  _menuText(_("Show ENC text"), _T("T")));
3491  view_menu->AppendCheckItem(ID_MENU_ENC_LIGHTS,
3492  _menuText(_("Show ENC Lights"), _T("L")));
3493  view_menu->AppendCheckItem(ID_MENU_ENC_SOUNDINGS,
3494  _menuText(_("Show ENC Soundings"), _T("S")));
3495  view_menu->AppendCheckItem(ID_MENU_ENC_ANCHOR,
3496  _menuText(_("Show ENC Anchoring Info"), _T("A")));
3497  view_menu->AppendCheckItem(ID_MENU_ENC_DATA_QUALITY,
3498  _menuText(_("Show ENC Data Quality"), _T("U")));
3499  view_menu->AppendCheckItem(ID_MENU_SHOW_NAVOBJECTS,
3500  _menuText(_("Show Navobjects"), _T("V")));
3501 #else
3502  view_menu->AppendCheckItem(ID_MENU_ENC_TEXT,
3503  _menuText(_("Show ENC text"), _T("Alt-T")));
3504  view_menu->AppendCheckItem(ID_MENU_ENC_LIGHTS,
3505  _menuText(_("Show ENC Lights"), _T("Alt-L")));
3506  view_menu->AppendCheckItem(ID_MENU_ENC_SOUNDINGS,
3507  _menuText(_("Show ENC Soundings"), _T("Alt-S")));
3508  view_menu->AppendCheckItem(
3509  ID_MENU_ENC_ANCHOR, _menuText(_("Show ENC Anchoring Info"), _T("Alt-A")));
3510  view_menu->AppendCheckItem(
3511  ID_MENU_ENC_DATA_QUALITY,
3512  _menuText(_("Show ENC Data Quality"), _T("Alt-U")));
3513  view_menu->AppendCheckItem(ID_MENU_SHOW_NAVOBJECTS,
3514  _menuText(_("Show Navobjects"), _T("Alt-V")));
3515 #endif
3516  view_menu->AppendSeparator();
3517  view_menu->AppendCheckItem(ID_MENU_SHOW_TIDES, _("Show Tides"));
3518  view_menu->AppendCheckItem(ID_MENU_SHOW_CURRENTS, _("Show Currents"));
3519  view_menu->AppendSeparator();
3520 #ifndef __WXOSX__
3521  view_menu->Append(ID_MENU_UI_COLSCHEME,
3522  _menuText(_("Change Color Scheme"), _T("C")));
3523 #else
3524  view_menu->Append(ID_MENU_UI_COLSCHEME,
3525  _menuText(_("Change Color Scheme"), _T("Alt-C")));
3526 #endif
3527 
3528  view_menu->AppendSeparator();
3529 #ifndef __WXOSX__
3530  view_menu->Append(ID_MENU_UI_FULLSCREEN,
3531  _menuText(_("Toggle Full Screen"), _T("F11")));
3532 #endif
3533  m_pMenuBar->Append(view_menu, _("&View"));
3534 
3535  wxMenu *ais_menu = new wxMenu();
3536  ais_menu->AppendCheckItem(ID_MENU_AIS_TARGETS, _("Show AIS Targets"));
3537  ais_menu->AppendCheckItem(ID_MENU_AIS_SCALED_TARGETS,
3538  _("Attenuate less critical AIS targets"));
3539  ais_menu->AppendSeparator();
3540  ais_menu->AppendCheckItem(ID_MENU_AIS_MOORED_TARGETS,
3541  _("Hide Moored AIS Targets"));
3542  ais_menu->AppendCheckItem(ID_MENU_AIS_TRACKS, _("Show AIS Target Tracks"));
3543  ais_menu->AppendCheckItem(ID_MENU_AIS_CPADIALOG, _("Show CPA Alert Dialogs"));
3544  ais_menu->AppendCheckItem(ID_MENU_AIS_CPASOUND, _("Sound CPA Alarms"));
3545 
3546 #ifndef __WXOSX__
3547  ais_menu->AppendCheckItem(ID_MENU_AIS_CPAWARNING,
3548  _menuText(_("Show CPA Warnings"), _T("W")));
3549 #else
3550  ais_menu->AppendCheckItem(ID_MENU_AIS_CPAWARNING,
3551  _menuText(_("Show CPA Warnings"), _T("Alt-W")));
3552 #endif
3553 
3554  ais_menu->AppendSeparator();
3555  ais_menu->Append(ID_MENU_AIS_TARGETLIST, _("AIS target list") + _T("..."));
3556  m_pMenuBar->Append(ais_menu, _("&AIS"));
3557 
3558  wxMenu *tools_menu = new wxMenu();
3559 #ifndef __WXOSX__
3560  tools_menu->Append(ID_MENU_TOOL_MEASURE,
3561  _menuText(_("Measure Distance"), _T("M")));
3562 #else
3563  tools_menu->Append(ID_MENU_TOOL_MEASURE,
3564  _menuText(_("Measure Distance"), _T("Alt-M")));
3565 #endif
3566 
3567  tools_menu->AppendSeparator();
3568  tools_menu->Append(ID_MENU_ROUTE_MANAGER, _("Route && Mark Manager..."));
3569  tools_menu->Append(ID_MENU_ROUTE_NEW,
3570  _menuText(_("Create Route"), _T("Ctrl-R")));
3571  tools_menu->AppendSeparator();
3572  tools_menu->Append(ID_MENU_MARK_BOAT,
3573  _menuText(_("Drop Mark at Boat"), _T("Ctrl-O")));
3574  tools_menu->Append(ID_MENU_MARK_CURSOR,
3575  _menuText(_("Drop Mark at Cursor"), _T("Ctrl-M")));
3576  tools_menu->AppendSeparator();
3577 #ifdef __WXOSX__
3578  tools_menu->Append(
3579  ID_MENU_MARK_MOB,
3580  _menuText(
3581  _("Drop MOB Marker"),
3582  _T("RawCtrl-Space"))); // NOTE Cmd+Space is reserved for Spotlight
3583  tools_menu->AppendSeparator();
3584  tools_menu->Append(wxID_PREFERENCES,
3585  _menuText(_("Preferences") + _T("..."), _T("Ctrl-,")));
3586 #else
3587  tools_menu->Append(ID_MENU_MARK_MOB,
3588  _menuText(_("Drop MOB Marker"), _T("Ctrl-Space")));
3589  tools_menu->AppendSeparator();
3590  tools_menu->Append(wxID_PREFERENCES,
3591  _menuText(_("Options") + _T("..."), _T("Ctrl-,")));
3592 #endif
3593  m_pMenuBar->Append(tools_menu, _("&Tools"));
3594 
3595 #ifdef __WXOSX__
3596  wxMenu *window_menu = new wxMenu();
3597  m_pMenuBar->Append(window_menu, _("&Window"));
3598 #endif
3599 
3600  wxMenu *help_menu = new wxMenu();
3601  help_menu->Append(wxID_ABOUT, _("About OpenCPN"));
3602  help_menu->Append(wxID_HELP, _("OpenCPN Help"));
3603  m_pMenuBar->Append(help_menu, _("&Help"));
3604 
3605  // Set initial values for menu check items and radio items
3606  UpdateGlobalMenuItems();
3607 }
3608 
3609 void MyFrame::UpdateGlobalMenuItems() {
3610  if (!m_pMenuBar) return; // if there isn't a menu bar
3611 
3612  m_pMenuBar->FindItem(ID_MENU_NAV_FOLLOW)
3613  ->Check(GetPrimaryCanvas()->m_bFollow);
3614  m_pMenuBar->FindItem(ID_MENU_CHART_NORTHUP)
3615  ->Check(GetPrimaryCanvas()->GetUpMode() == NORTH_UP_MODE);
3616  m_pMenuBar->FindItem(ID_MENU_CHART_COGUP)
3617  ->Check(GetPrimaryCanvas()->GetUpMode() == COURSE_UP_MODE);
3618  m_pMenuBar->FindItem(ID_MENU_CHART_HEADUP)
3619  ->Check(GetPrimaryCanvas()->GetUpMode() == HEAD_UP_MODE);
3620  m_pMenuBar->FindItem(ID_MENU_NAV_TRACK)->Check(g_bTrackActive);
3621  m_pMenuBar->FindItem(ID_MENU_CHART_OUTLINES)->Check(g_bShowOutlines);
3622  m_pMenuBar->FindItem(ID_MENU_CHART_QUILTING)->Check(g_bQuiltEnable);
3623  m_pMenuBar->FindItem(ID_MENU_UI_CHARTBAR)->Check(g_bShowChartBar);
3624  m_pMenuBar->FindItem(ID_MENU_AIS_TARGETS)->Check(g_bShowAIS);
3625  m_pMenuBar->FindItem(ID_MENU_AIS_MOORED_TARGETS)->Check(g_bHideMoored);
3626  m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Check(g_bShowScaled);
3627  m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Enable(g_bAllowShowScaled);
3628  m_pMenuBar->FindItem(ID_MENU_AIS_TRACKS)->Check(g_bAISShowTracks);
3629  m_pMenuBar->FindItem(ID_MENU_AIS_CPADIALOG)->Check(g_bAIS_CPA_Alert);
3630  if (g_bAIS_CPA_Alert) {
3631  m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(g_bAIS_CPA_Alert_Audio);
3632  m_pMenuBar->Enable(ID_MENU_AIS_CPASOUND, true);
3633  }
3634  else {
3635  m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(false);
3636  m_pMenuBar->Enable(ID_MENU_AIS_CPASOUND, false);
3637  }
3638 
3639  m_pMenuBar->FindItem(ID_MENU_AIS_CPAWARNING)->Check(g_bCPAWarn);
3640  m_pMenuBar->FindItem(ID_MENU_SHOW_NAVOBJECTS)
3641  ->Check(GetPrimaryCanvas()->m_bShowNavobjects);
3642 
3643  if (ps52plib) {
3644  m_pMenuBar->FindItem(ID_MENU_ENC_TEXT)->Check(ps52plib->GetShowS57Text());
3645  m_pMenuBar->FindItem(ID_MENU_ENC_SOUNDINGS)
3646  ->Check(ps52plib->GetShowSoundings());
3647 
3648  bool light_state = false;
3649  if (ps52plib) {
3650  for (unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount();
3651  iPtr++) {
3652  OBJLElement *pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
3653  if (!strncmp(pOLE->OBJLName, "LIGHTS", 6)) {
3654  light_state = (pOLE->nViz == 1);
3655  break;
3656  }
3657  }
3658  }
3659  m_pMenuBar->FindItem(ID_MENU_ENC_LIGHTS)
3660  ->Check((!ps52plib->IsObjNoshow("LIGHTS")) && light_state);
3661 
3662  // Menu "Anchor Info" entry is only accessible in "All" or "User Standard"
3663  // categories
3664  DisCat nset = ps52plib->GetDisplayCategory();
3665  if ((nset == MARINERS_STANDARD) || (nset == OTHER)) {
3666  m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)
3667  ->Check(!ps52plib->IsObjNoshow("SBDARE"));
3668  m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, true);
3669  m_pMenuBar->FindItem(ID_MENU_ENC_DATA_QUALITY)
3670  ->Check(!ps52plib->IsObjNoshow("M_QUAL"));
3671  m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, true);
3672  } else {
3673  m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(false);
3674  m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, false);
3675  m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, false);
3676  }
3677  }
3678 }
3679 
3680 void MyFrame::UpdateGlobalMenuItems(ChartCanvas *cc) {
3681  if (!m_pMenuBar) return; // if there isn't a menu bar
3682 
3683  m_pMenuBar->FindItem(ID_MENU_NAV_FOLLOW)->Check(cc->m_bFollow);
3684 
3685  if (cc->GetUpMode() == NORTH_UP_MODE)
3686  m_pMenuBar->FindItem(ID_MENU_CHART_NORTHUP)->Check(true);
3687  else if (cc->GetUpMode() == COURSE_UP_MODE)
3688  m_pMenuBar->FindItem(ID_MENU_CHART_COGUP)->Check(true);
3689  else
3690  m_pMenuBar->FindItem(ID_MENU_CHART_HEADUP)->Check(true);
3691 
3692  m_pMenuBar->FindItem(ID_MENU_NAV_TRACK)->Check(g_bTrackActive);
3693  m_pMenuBar->FindItem(ID_MENU_CHART_OUTLINES)->Check(cc->GetShowOutlines());
3694  m_pMenuBar->FindItem(ID_MENU_CHART_QUILTING)->Check(cc->GetQuiltMode());
3695  m_pMenuBar->FindItem(ID_MENU_UI_CHARTBAR)->Check(cc->GetShowChartbar());
3696  m_pMenuBar->FindItem(ID_MENU_AIS_TARGETS)->Check(cc->GetShowAIS());
3697  m_pMenuBar->FindItem(ID_MENU_AIS_MOORED_TARGETS)->Check(g_bHideMoored);
3698  m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Check(cc->GetAttenAIS());
3699  m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Enable(g_bAllowShowScaled);
3700  m_pMenuBar->FindItem(ID_MENU_AIS_TRACKS)->Check(g_bAISShowTracks);
3701  m_pMenuBar->FindItem(ID_MENU_AIS_CPADIALOG)->Check(g_bAIS_CPA_Alert);
3702  m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(g_bAIS_CPA_Alert_Audio);
3703  m_pMenuBar->FindItem(ID_MENU_AIS_CPAWARNING)->Check(g_bCPAWarn);
3704  m_pMenuBar->FindItem(ID_MENU_SHOW_NAVOBJECTS)->Check(cc->m_bShowNavobjects);
3705  m_pMenuBar->FindItem(ID_MENU_SHOW_TIDES)->Check(cc->GetbShowTide());
3706  m_pMenuBar->FindItem(ID_MENU_SHOW_CURRENTS)->Check(cc->GetbShowCurrent());
3707 
3708  if (ps52plib) {
3709  m_pMenuBar->FindItem(ID_MENU_ENC_TEXT)->Check(cc->GetShowENCText());
3710  m_pMenuBar->FindItem(ID_MENU_ENC_SOUNDINGS)->Check(cc->GetShowENCDepth());
3711 
3712  if (ps52plib) {
3713  for (unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount();
3714  iPtr++) {
3715  OBJLElement *pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
3716  if (!strncmp(pOLE->OBJLName, "LIGHTS", 6)) {
3717  break;
3718  }
3719  }
3720  }
3721  m_pMenuBar->FindItem(ID_MENU_ENC_LIGHTS)->Check(cc->GetShowENCLights());
3722 
3723  // Menu "Anchor Info" entry is only accessible in "All" or "UserStandard"
3724  // categories
3725  DisCat nset = (DisCat)cc->GetENCDisplayCategory();
3726  if ((nset == MARINERS_STANDARD) || (nset == OTHER)) {
3727  m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(cc->GetShowENCAnchor());
3728  m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, true);
3729  m_pMenuBar->FindItem(ID_MENU_ENC_DATA_QUALITY)
3730  ->Check(cc->GetShowENCDataQual());
3731  m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, true);
3732  } else {
3733  m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(false);
3734  m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, false);
3735  m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, false);
3736  }
3737  }
3738 }
3739 
3740 void MyFrame::InvalidateAllCanvasUndo() {
3741  // .. for each canvas...
3742  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3743  ChartCanvas *cc = g_canvasArray.Item(i);
3744  if (cc) cc->undo->InvalidateUndo();
3745  }
3746 }
3747 #if 0
3748 void MyFrame::SubmergeAllCanvasToolbars(void) {
3749  // .. for each canvas...
3750  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3751  ChartCanvas *cc = g_canvasArray.Item(i);
3752  if (cc) cc->SubmergeToolbar();
3753  }
3754 }
3755 
3756 void MyFrame::SurfaceAllCanvasToolbars(void) {
3757  if (g_bshowToolbar) {
3758  // .. for each canvas...
3759  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3760  ChartCanvas *cc = g_canvasArray.Item(i);
3761  if (cc && cc->GetToolbarEnable()) cc->SurfaceToolbar();
3762  }
3763  }
3764 
3765 }
3766 #endif
3767 
3768 void MyFrame::JumpToPosition(ChartCanvas *cc, double lat, double lon,
3769  double scale) {
3770  if (lon > 180.0) lon -= 360.0;
3771  // XXX is vLat/vLon always equal to cc m_vLat, m_vLon after SetViewPoint? Does
3772  // it matter?
3773  vLat = lat;
3774  vLon = lon;
3775  cc->JumpToPosition(lat, lon, scale);
3776 
3777  if (g_pi_manager) {
3778  g_pi_manager->SendViewPortToRequestingPlugIns(cc->GetVP());
3779  }
3780 }
3781 
3782 void MyFrame::UpdateCanvasConfigDescriptors() {
3783  // ..For each canvas...
3784  for (unsigned int i = 0; i < ConfigMgr::Get().GetCanvasConfigArray().GetCount(); i++) {
3785  canvasConfig *cc = ConfigMgr::Get().GetCanvasConfigArray().Item(i);
3786  if (cc) {
3787  ChartCanvas *chart = cc->canvas;
3788  if (chart) {
3789  cc->iLat = chart->GetVP().clat;
3790  cc->iLon = chart->GetVP().clon;
3791  cc->iRotation = chart->GetVP().rotation;
3792  cc->iScale = chart->GetVP().view_scale_ppm;
3793  cc->DBindex = chart->GetQuiltReferenceChartIndex();
3794  cc->GroupID = chart->m_groupIndex;
3795  cc->canvasSize = chart->GetSize();
3796 
3797  cc->bQuilt = chart->GetQuiltMode();
3798  cc->bShowTides = chart->GetbShowTide();
3799  cc->bShowCurrents = chart->GetbShowCurrent();
3800  cc->bShowGrid = chart->GetShowGrid();
3801  cc->bShowOutlines = chart->GetShowOutlines();
3802  cc->bShowDepthUnits = chart->GetShowDepthUnits();
3803 
3804  cc->bFollow = chart->m_bFollow;
3805  cc->bLookahead = chart->m_bLookAhead;
3806  cc->bCourseUp = false;
3807  cc->bHeadUp = false;;
3808  int upmode = chart->GetUpMode();
3809  if (upmode == COURSE_UP_MODE)
3810  cc->bCourseUp = true;
3811  else if (upmode == HEAD_UP_MODE)
3812  cc->bHeadUp = true;
3813  }
3814  }
3815  }
3816 }
3817 
3818 void MyFrame::CenterView(ChartCanvas *cc, const LLBBox &RBBox) {
3819  if (!RBBox.GetValid()) return;
3820  // Calculate bbox center
3821  double clat = (RBBox.GetMinLat() + RBBox.GetMaxLat()) / 2;
3822  double clon = (RBBox.GetMinLon() + RBBox.GetMaxLon()) / 2;
3823  double ppm; // final ppm scale to use
3824 
3825  if (RBBox.GetMinLat() == RBBox.GetMaxLat() &&
3826  RBBox.GetMinLon() == RBBox.GetMaxLon()) {
3827  // only one point, (should be a box?)
3828  ppm = cc->GetVPScale();
3829  } else {
3830  // Calculate ppm
3831  double rw, rh; // route width, height
3832  int ww, wh; // chart window width, height
3833  // route bbox width in nm
3834  DistanceBearingMercator(RBBox.GetMinLat(), RBBox.GetMinLon(),
3835  RBBox.GetMinLat(), RBBox.GetMaxLon(), NULL, &rw);
3836  // route bbox height in nm
3837  DistanceBearingMercator(RBBox.GetMinLat(), RBBox.GetMinLon(),
3838  RBBox.GetMaxLat(), RBBox.GetMinLon(), NULL, &rh);
3839 
3840  cc->GetSize(&ww, &wh);
3841 
3842  ppm = wxMin(ww / (rw * 1852), wh / (rh * 1852)) * (100 - fabs(clat)) / 90;
3843 
3844  ppm = wxMin(ppm, 1.0);
3845  }
3846 
3847  JumpToPosition(cc, clat, clon, ppm);
3848 }
3849 
3850 int MyFrame::DoOptionsDialog() {
3851  if (g_boptionsactive) return 0;
3852 
3853  g_boptionsactive = true;
3854  g_last_ChartScaleFactor = g_ChartScaleFactor;
3855 
3856  if (NULL == g_options) {
3857  AbstractPlatform::ShowBusySpinner();
3858 
3859  int sx, sy;
3860  pConfig->SetPath("/Settings");
3861  pConfig->Read("OptionsSizeX", &sx, -1);
3862  pConfig->Read("OptionsSizeY", &sy, -1);
3863 
3864  wxWindow *optionsParent = this;
3865 #ifdef __WXOSX__
3866  optionsParent = GetPrimaryCanvas();
3867 #endif
3868  g_options =
3869  new options(optionsParent, -1, _("Options"), wxPoint(-1, -1), wxSize(sx, sy));
3870 
3871  AbstractPlatform::HideBusySpinner();
3872  }
3873 
3874  // Set initial Chart Dir
3875  g_options->SetInitChartDir(*pInit_Chart_Dir);
3876 
3877  // Pass two working pointers for Chart Dir Dialog
3878  g_options->SetCurrentDirList(ChartData->GetChartDirArray());
3879  ArrayOfCDI *pWorkDirArray = new ArrayOfCDI;
3880  g_options->SetWorkDirListPtr(pWorkDirArray);
3881 
3882  // Pass a ptr to MyConfig, for updates
3883  g_options->SetConfigPtr(pConfig);
3884 
3885  g_options->SetInitialSettings();
3886 
3887  prev_locale = g_locale;
3888 
3889  bool b_sub = false;
3890 #if 0
3891  if (g_MainToolbar && g_MainToolbar->IsShown()) {
3892  wxRect bx_rect = g_options->GetScreenRect();
3893  wxRect tb_rect = g_MainToolbar->GetScreenRect();
3894  if (tb_rect.Intersects(bx_rect)) b_sub = true;
3895 
3896  if (b_sub) g_MainToolbar->Submerge();
3897  }
3898 #endif
3899 
3900 #if defined(__WXOSX__) || defined(__WXQT__)
3901  bool b_restoreAIS = false;
3902  if (g_pAISTargetList && g_pAISTargetList->IsShown()) {
3903  b_restoreAIS = true;
3904  g_pAISTargetList->Shutdown();
3905  g_pAISTargetList = NULL;
3906  }
3907 #endif
3908 
3909 #ifdef __WXOSX__
3910  //SubmergeAllCanvasToolbars();
3911  g_MainToolbar->Submerge();
3912 #endif
3913 
3914  g_options->SetInitialPage(options_lastPage, options_subpage);
3915 
3916 #ifndef __ANDROID__ // if(!g_bresponsive){
3917  g_options->lastWindowPos = options_lastWindowPos;
3918  if (options_lastWindowPos != wxPoint(0, 0)) {
3919  g_options->Move(options_lastWindowPos);
3920  g_options->SetSize(options_lastWindowSize);
3921  } else {
3922  g_options->CenterOnScreen();
3923  }
3924  if (options_lastWindowSize != wxSize(0, 0)) {
3925  g_options->SetSize(options_lastWindowSize);
3926  }
3927 
3928  // Correct some fault in Options dialog layout logic on GTK3 by forcing a
3929  // re-layout to new slightly reduced size.
3930 #ifdef __WXGTK3__
3931  if (options_lastWindowSize != wxSize(0, 0))
3932  g_options->SetSize(options_lastWindowSize.x - 1, options_lastWindowSize.y);
3933 #endif
3934 
3935 #endif
3936 
3937  if (g_MainToolbar) g_MainToolbar->DisableTooltips();
3938 
3939 #ifdef __ANDROID__
3940  androidEnableBackButton(false);
3941  androidEnableOptionsMenu(false);
3942  androidDisableFullScreen();
3943 #endif
3944 
3945  // Record current canvas config
3946  unsigned int last_canvasConfig = g_canvasConfig;
3947  wxSize cc1SizeBefore;
3948  if (g_canvasConfig > 0) {
3949  canvasConfig *cc = ConfigMgr::Get().GetCanvasConfigArray().Item(0);
3950  if (cc) cc1SizeBefore = g_canvasArray.Item(0)->GetSize();
3951  }
3952 
3953  // Capture the full path names and VPScale of charts currently shown in all
3954  // canvases
3955  wxArrayString pathArray;
3956  double restoreScale[4];
3957 
3958  // ..For each canvas...
3959  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3960  ChartCanvas *cc = g_canvasArray.Item(i);
3961  if (cc) {
3962  wxString chart_file_name;
3963  if (cc->GetQuiltMode()) {
3964  int dbi = cc->GetQuiltRefChartdbIndex();
3965  chart_file_name = ChartData->GetDBChartFileName(dbi);
3966  } else {
3967  if (cc->m_singleChart)
3968  chart_file_name = cc->m_singleChart->GetFullPath();
3969  }
3970 
3971  pathArray.Add(chart_file_name);
3972  restoreScale[i] = cc->GetVPScale();
3973  }
3974  }
3975 
3976  int rr = g_options->ShowModal();
3977 
3978 #ifdef __ANDROID__
3979  androidEnableBackButton(true);
3980  androidEnableOptionsMenu(true);
3981  androidRestoreFullScreen();
3982  androidEnableRotation();
3983 #endif
3984 
3985  if (g_MainToolbar) g_MainToolbar->EnableTooltips();
3986 
3987  options_lastPage = g_options->lastPage;
3988 #ifdef __ANDROID__
3989  // This is necessary to force a manual change to charts page,
3990  // in order to properly refresh the chart directory list.
3991  // Root cause: In Android, trouble with clearing the wxScrolledWindow
3992  if (options_lastPage == 1) options_lastPage = 0;
3993 #endif
3994 
3995  options_subpage = g_options->lastSubPage;
3996 
3997  options_lastWindowPos = g_options->lastWindowPos;
3998  options_lastWindowSize = g_options->lastWindowSize;
3999 
4000  if (1 /*b_sub*/) { // always surface toolbar, and restart the timer if needed
4001 #ifdef __ANDROID__
4002  g_MainToolbar->SetDockX(-1);
4003  g_MainToolbar->SetDockY(-1);
4004 #endif
4005  //g_MainToolbar->Surface();
4006  //SurfaceAllCanvasToolbars();
4007  GetPrimaryCanvas()->SetFocus();
4008  }
4009 
4010 #ifdef __WXGTK__
4011  Raise(); // I dunno why...
4012 #endif
4013 
4014  bool ret_val = false;
4015  rr = g_options->GetReturnCode();
4016 
4017  if (g_last_ChartScaleFactor != g_ChartScaleFactor) rr |= S52_CHANGED;
4018 
4019  bool b_refresh = true;
4020 
4021 #if 0
4022  bool ccRightSizeChanged = false;
4023  if( g_canvasConfig > 0 ){
4024  canvasConfig *cc = g_canvasConfigArray.Item(0);
4025  if(cc ){
4026  wxSize cc1Size = cc->canvasSize;
4027  if(cc1Size.x != cc1SizeBefore.x)
4028  ccRightSizeChanged = true;
4029  }
4030  }
4031 #endif
4032 
4033  if ((g_canvasConfig != last_canvasConfig) || (rr & GL_CHANGED)) {
4034  DestroyPersistentDialogs();
4035 
4036  UpdateCanvasConfigDescriptors();
4037 
4038  if ((g_canvasConfig > 0) && (last_canvasConfig == 0))
4039  CreateCanvasLayout(true);
4040  else
4041  CreateCanvasLayout();
4042 
4043  SendSizeEvent();
4044 
4045  g_pauimgr->Update();
4046 
4047  // We need a yield() here to pick up the size event
4048  // so that the toolbars will be sized correctly
4049  wxYield();
4050 
4051  // ..For each canvas...
4052  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4053  ChartCanvas *cc = g_canvasArray.Item(i);
4054  if (cc) cc->CreateMUIBar();
4055  }
4056 
4057  rr |= GENERIC_CHANGED;
4058 
4059  if (g_bopengl) // Force mark/waypoint icon reload
4060  rr |= S52_CHANGED;
4061 
4062  b_refresh = true;
4063  }
4064 
4065  // Here check for the case wherein the relative sizes of a multicanvas layout
4066  // have been changed. We do not need to reqbuild the canvases, we just need to
4067  // resize whichever one is docked.
4068 
4069  // if( (g_canvasConfig > 0) && ccRightSizeChanged ){
4070  // canvasConfig *cc = g_canvasConfigArray.Item(1);
4071  // if(cc ){
4072  // wxAuiPaneInfo& p = g_pauimgr->GetPane(g_canvasArray.Item(1));
4073  // wxAuiDockInfo *dockRight = g_pauimgr->FindDock(p);
4074  // if(dockRight)
4075  // g_pauimgr->SetDockSize(dockRight, cc->canvasSize.x);
4076  // }
4077  // }
4078 
4079  if (rr & CONFIG_CHANGED) {
4080  // Apply the changed canvas configs to each canvas
4081  // ..For each canvas...
4082  for (unsigned int i = 0; i < ConfigMgr::Get().GetCanvasConfigArray().GetCount(); i++) {
4083  canvasConfig *cc = ConfigMgr::Get().GetCanvasConfigArray().Item(i);
4084  if (cc) {
4085  ChartCanvas *chartCanvas = cc->canvas;
4086  if (chartCanvas) {
4087  chartCanvas->ApplyCanvasConfig(cc);
4088  }
4089  }
4090  }
4091  }
4092 
4093  if (rr) {
4094  bDBUpdateInProgress = true;
4095  b_refresh |= ProcessOptionsDialog(rr, g_options->GetWorkDirListPtr());
4096  ChartData->GetChartDirArray() =
4097  *(g_options->GetWorkDirListPtr()); // Perform a deep copy back to main
4098  // database.
4099  bDBUpdateInProgress = false;
4100  ret_val = true;
4101  }
4102 
4103  delete pWorkDirArray;
4104 
4105  DoChartUpdate();
4106 
4107  // We set the compass size first, since that establishes the available space
4108  // for the toolbar.
4109  SetGPSCompassScale();
4110  // ..For each canvas...
4111  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4112  ChartCanvas *cc = g_canvasArray.Item(i);
4113  if (cc) {
4114  cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
4115  cc->UpdateCanvasControlBar();
4116  }
4117  }
4118  UpdateGPSCompassStatusBoxes();
4119 
4120  SetAllToolbarScale();
4121  RequestNewToolbars();
4122 
4123  // Rebuild cursors
4124  // ..For each canvas...
4125  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4126  ChartCanvas *cc = g_canvasArray.Item(i);
4127  if (cc) {
4128  cc->RebuildCursors();
4129  }
4130  }
4131 
4132  // Change of master toolbar scale?
4133  bool b_masterScaleChange = false;
4134  if (fabs(g_MainToolbar->GetScaleFactor() - g_toolbar_scalefactor) > 0.01f)
4135  b_masterScaleChange = true;
4136 
4137  if ((rr & TOOLBAR_CHANGED) || b_masterScaleChange)
4138  RequestNewMasterToolbar(true);
4139 
4140  bool bMuiChange = false;
4141 #ifdef __ANDROID__
4142  bMuiChange = true; // to pick up possible "zoom" button visibility change
4143 #endif
4144 
4145  // Inform the canvases
4146  if (b_masterScaleChange || bMuiChange) {
4147  // ..For each canvas...
4148  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4149  ChartCanvas *cc = g_canvasArray.Item(i);
4150  if (cc) {
4151  cc->ProcessNewGUIScale();
4152  }
4153  }
4154  }
4155 
4156  if (g_MainToolbar) {
4157  if (IsFullScreen() && !g_bFullscreenToolbar) g_MainToolbar->Submerge();
4158  }
4159 
4160 #if defined(__WXOSX__) || defined(__WXQT__)
4161  if (b_restoreAIS) {
4162  g_pAISTargetList = new AISTargetListDialog(this, g_pauimgr, g_pAIS);
4163  g_pAISTargetList->UpdateAISTargetList();
4164  }
4165 #endif
4166 
4167  if (console && console->IsShown()) console->Raise();
4168 
4169  auto alert_dlg_active =
4170  dynamic_cast<AISTargetAlertDialog*>(g_pais_alert_dialog_active);
4171  if (alert_dlg_active) alert_dlg_active->Raise();
4172 
4173  if (NMEALogWindow::GetInstance().Active())
4174  NMEALogWindow::GetInstance().GetTTYWindow()->Raise();
4175 
4176 #ifdef __ANDROID__
4177  if (g_pi_manager) g_pi_manager->NotifyAuiPlugIns();
4178 #endif
4179 
4180  // Force reload of options dialog to pick up font changes or other major
4181  // layout changes
4182  if ((rr & FONT_CHANGED) || (rr & NEED_NEW_OPTIONS)) {
4183  delete g_options;
4184  g_options = NULL;
4185  g_pOptions = NULL;
4186  }
4187 
4188  // Pick up chart object icon size changes (g_ChartScaleFactorExp)
4189  if (g_pMarkInfoDialog) {
4190  g_pMarkInfoDialog->Hide();
4191  g_pMarkInfoDialog->Destroy();
4192  g_pMarkInfoDialog = NULL;
4193  }
4194 
4195 #if wxUSE_XLOCALE
4196  if (rr & LOCALE_CHANGED) {
4197  g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
4198  ApplyLocale();
4199  }
4200 #endif
4201 
4202  // If needed, refresh each canvas,
4203  // trying to reload the previously displayed chart by name as saved in
4204  // pathArray Also, restoring the previous chart VPScale, if possible
4205  if (b_refresh) {
4206  // ..For each canvas...
4207  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4208  ChartCanvas *cc = g_canvasArray.Item(i);
4209  if (cc) {
4210  int index_hint = -1;
4211  if (i < pathArray.GetCount())
4212  index_hint = ChartData->FinddbIndex(pathArray.Item(i));
4213  cc->canvasChartsRefresh(index_hint);
4214  if (index_hint != -1) cc->SetVPScale(restoreScale[i]);
4215  }
4216  }
4217  }
4218 
4219  g_boptionsactive = false;
4220 
4221  // If we had a config chamge, then schedule a re-entry to the settings dialog
4222  if (rr & CONFIG_CHANGED) {
4223  options_subpage = 3; // Back to the "templates" page
4224  ScheduleSettingsDialog();
4225  } else
4226  options_subpage = 0;
4227 
4228  return ret_val;
4229 }
4230 
4231 bool MyFrame::ProcessOptionsDialog(int rr, ArrayOfCDI *pNewDirArray) {
4232  bool b_need_refresh = false; // Do we need a full reload?
4233 
4234  if ((rr & VISIT_CHARTS) &&
4235  ((rr & CHANGE_CHARTS) || (rr & FORCE_UPDATE) || (rr & SCAN_UPDATE))) {
4236  if (pNewDirArray) {
4237  UpdateChartDatabaseInplace(*pNewDirArray,
4238  ((rr & FORCE_UPDATE) == FORCE_UPDATE), true,
4239  ChartListFileName);
4240 
4241  b_need_refresh = true;
4242  }
4243  }
4244 
4245  if (rr & STYLE_CHANGED) {
4246  OCPNMessageBox(
4247  NULL,
4248  _("Please restart OpenCPN to activate language or style changes."),
4249  _("OpenCPN Info"), wxOK | wxICON_INFORMATION);
4250  }
4251 
4252  bool b_groupchange = false;
4253  if (((rr & VISIT_CHARTS) &&
4254  ((rr & CHANGE_CHARTS) || (rr & FORCE_UPDATE) || (rr & SCAN_UPDATE))) ||
4255  (rr & GROUPS_CHANGED)) {
4256  b_groupchange = ScrubGroupArray();
4257  ChartData->ApplyGroupArray(g_pGroupArray);
4258  RefreshGroupIndices();
4259  }
4260 
4261  if (rr & GROUPS_CHANGED || b_groupchange) {
4262  pConfig->DestroyConfigGroups();
4263  pConfig->CreateConfigGroups(g_pGroupArray);
4264  }
4265 
4266  if (rr & TIDES_CHANGED) {
4267  LoadHarmonics();
4268  }
4269 
4270  // S52_CHANGED is a byproduct of a change in the chart object render scale
4271  // So, applies to RoutePoint icons also
4272  if (rr & S52_CHANGED) {
4273  WayPointmanGui(*pWayPointMan).ReloadAllIcons(g_Platform->GetDisplayDPmm());
4274  }
4275 
4276  pConfig->UpdateSettings();
4277 
4278  if (g_pActiveTrack) {
4279  g_pActiveTrack->SetPrecision(g_nTrackPrecision);
4280  }
4281 
4282  // if( ( bPrevQuilt != g_bQuiltEnable ) || ( bPrevFullScreenQuilt !=
4283  // g_bFullScreenQuilt ) ) {
4284  // GetPrimaryCanvas()->SetQuiltMode( g_bQuiltEnable );
4285  // GetPrimaryCanvas()->SetupCanvasQuiltMode();
4286  // }
4287 
4288 #if 0
4289 //TODO Not need with per-canvas CourseUp
4290  if( g_bCourseUp ) {
4291  // Stuff the COGAvg table in case COGUp is selected
4292  double stuff = NAN;
4293  if( !std::isnan(gCog) ) stuff = gCog;
4294  if( g_COGAvgSec > 0 ) {
4295  for( int i = 0; i < g_COGAvgSec; i++ )
4296  COGTable[i] = stuff;
4297  }
4298 
4299  g_COGAvg = stuff;
4300 
4301  DoCOGSet();
4302  }
4303 #endif
4304 
4305  // reload pens and brushes
4306  g_pRouteMan->SetColorScheme(global_color_scheme,
4307  g_Platform->GetDisplayDPmm());
4308 
4309  // Stuff the Filter tables
4310  double stuffcog = NAN;
4311  double stuffsog = NAN;
4312  if (!std::isnan(gCog)) stuffcog = gCog;
4313  if (!std::isnan(gSog)) stuffsog = gSog;
4314 
4315  for (int i = 0; i < MAX_COGSOG_FILTER_SECONDS; i++) {
4316  COGFilterTable[i] = stuffcog;
4317  SOGFilterTable[i] = stuffsog;
4318  }
4319 
4320  SetChartUpdatePeriod(); // Pick up changes to skew compensator
4321 
4322  if (rr & GL_CHANGED) {
4323  // Refresh the chart display, after flushing cache.
4324  // This will allow all charts to recognise new OpenGL configuration, if
4325  // any
4326  b_need_refresh = true;
4327  }
4328 
4329  if (rr & S52_CHANGED) {
4330  b_need_refresh = true;
4331  }
4332 
4333 #ifdef ocpnUSE_GL
4334  if (rr & REBUILD_RASTER_CACHE) {
4335  if (g_glTextureManager) {
4336  GetPrimaryCanvas()->Disable();
4337  g_glTextureManager->BuildCompressedCache();
4338  GetPrimaryCanvas()->Enable();
4339  }
4340  }
4341 #endif
4342 
4343  if (g_config_display_size_manual && g_config_display_size_mm.size() > g_current_monitor && g_config_display_size_mm[g_current_monitor] > 0) {
4344  g_display_size_mm = g_config_display_size_mm[g_current_monitor];
4345  } else {
4346  g_display_size_mm = wxMax(50, g_Platform->GetDisplaySizeMM());
4347  }
4348 
4349  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4350  ChartCanvas *cc = g_canvasArray.Item(i);
4351  if (cc) cc->SetDisplaySizeMM(g_display_size_mm);
4352  }
4353 
4354  if (g_pi_manager) {
4355  g_pi_manager->SendBaseConfigToAllPlugIns();
4356  int rrt = rr & S52_CHANGED;
4357  g_pi_manager->SendS52ConfigToAllPlugIns(
4358  (rrt == S52_CHANGED) ||
4359  (g_last_ChartScaleFactor != g_ChartScaleFactor));
4360  }
4361 
4362  if (g_MainToolbar) {
4363  g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
4364  g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
4365  }
4366 
4367  // update S52 PLIB scale factors
4368  if (ps52plib){
4369  ps52plib->SetScaleFactorExp(g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor));
4370  ps52plib-> SetScaleFactorZoomMod(g_chart_zoom_modifier_vector);
4371  }
4372 
4373  // Apply any needed updates to each canvas
4374  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4375  ChartCanvas *cc = g_canvasArray.Item(i);
4376  if (cc) cc->ApplyGlobalSettings();
4377  }
4378 
4379  // Do a full Refresh, trying to open the last open chart
4380 // TODO This got move up a level. FIX ANDROID codepath
4381 #if 0
4382  if(b_need_refresh){
4383  int index_hint = ChartData->FinddbIndex( chart_file_name );
4384  if( -1 == index_hint )
4385  b_autofind = true;
4386  ChartsRefresh( );
4387  }
4388 #endif
4389 
4390  // The zoom-scale factor may have changed
4391  // so, trigger a recalculation of the reference chart
4392 
4393  bool ztc = g_bEnableZoomToCursor; // record the present state
4394  g_bEnableZoomToCursor =
4395  false; // since we don't want to pan to an unknown cursor position
4396 
4397  // This is needed to recognise changes in zoom-scale factors
4398  GetPrimaryCanvas()->DoZoomCanvas(1.0001);
4399 
4400  g_bEnableZoomToCursor = ztc;
4401 
4402  g_last_ChartScaleFactor = g_ChartScaleFactor;
4403 
4404  return b_need_refresh;
4405 }
4406 
4407 bool MyFrame::CheckGroup(int igroup) {
4408  if (igroup == 0) return true; // "all charts" is always OK
4409 
4410  ChartGroup *pGroup = g_pGroupArray->Item(igroup - 1);
4411 
4412  if (!pGroup->m_element_array.size()) // truly empty group is OK
4413  return true;
4414 
4415  for (const auto &elem : pGroup->m_element_array) {
4416  for (unsigned int ic = 0;
4417  ic < (unsigned int)ChartData->GetChartTableEntries(); ic++) {
4418  ChartTableEntry *pcte = ChartData->GetpChartTableEntry(ic);
4419  wxString chart_full_path(pcte->GetpFullPath(), wxConvUTF8);
4420 
4421  if (chart_full_path.StartsWith(elem.m_element_name)) return true;
4422  }
4423  }
4424 
4425  return false; // this group is empty
4426 }
4427 
4428 bool MyFrame::ScrubGroupArray() {
4429  // For each group,
4430  // make sure that each group element (dir or chart) references at least
4431  // oneitem in the database. If not, remove the element.
4432 
4433  bool b_change = false;
4434  unsigned int igroup = 0;
4435  while (igroup < g_pGroupArray->GetCount()) {
4436  bool b_chart_in_element = false;
4437  ChartGroup *pGroup = g_pGroupArray->Item(igroup);
4438 
4439  for (unsigned int j = 0; j < pGroup->m_element_array.size(); j++) {
4440  const wxString &element_root = pGroup->m_element_array[j].m_element_name;
4441 
4442  for (unsigned int ic = 0;
4443  ic < (unsigned int)ChartData->GetChartTableEntries(); ic++) {
4444  ChartTableEntry *pcte = ChartData->GetpChartTableEntry(ic);
4445  wxString chart_full_path = pcte->GetFullSystemPath();
4446 
4447  if (chart_full_path.StartsWith(element_root)) {
4448  b_chart_in_element = true;
4449  break;
4450  }
4451  }
4452 
4453  // Explicit check to avoid removing a group containing only GSHHS
4454  if (!b_chart_in_element) {
4455  wxString test_string = _T("GSHH");
4456  if (element_root.Upper().Contains(test_string))
4457  b_chart_in_element = true;
4458  }
4459 
4460  if (!b_chart_in_element) // delete the element
4461  {
4462  pGroup->m_element_array.erase(pGroup->m_element_array.begin() + j);
4463  j--;
4464  b_change = true;
4465  }
4466  }
4467 
4468  igroup++; // next group
4469  }
4470 
4471  return b_change;
4472 }
4473 
4474 void MyFrame::RefreshCanvasOther(ChartCanvas *ccThis) {
4475  // ..For each canvas...
4476  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4477  ChartCanvas *cc = g_canvasArray.Item(i);
4478  if (cc && (cc != ccThis)) cc->Refresh();
4479  }
4480 }
4481 
4482 // Flav: This method reloads all charts for convenience
4483 void MyFrame::ChartsRefresh() {
4484  if (!ChartData) return;
4485 
4486  AbstractPlatform::ShowBusySpinner();
4487 
4488  bool b_run = FrameTimer1.IsRunning();
4489 
4490  FrameTimer1.Stop(); // stop other asynchronous activity
4491 
4492  // ..For each canvas...
4493  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4494  ChartCanvas *cc = g_canvasArray.Item(i);
4495  if (cc) {
4496  int currentIndex = cc->GetpCurrentStack()->GetCurrentEntrydbIndex();
4497  if (cc->GetQuiltMode()) {
4498  currentIndex = cc->GetQuiltReferenceChartIndex();
4499  }
4500  cc->canvasChartsRefresh(currentIndex);
4501  }
4502  }
4503 
4504  if (b_run) FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
4505 
4506  AbstractPlatform::HideBusySpinner();
4507 }
4508 
4509 void MyFrame::InvalidateAllQuilts() {
4510  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4511  ChartCanvas *cc = g_canvasArray.Item(i);
4512  if (cc) {
4513  cc->InvalidateQuilt();
4514  cc->SetQuiltRefChart(-1);
4515  cc->m_singleChart = NULL;
4516  }
4517  }
4518 }
4519 
4520 bool MyFrame::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray, bool b_force,
4521  bool b_prog,
4522  const wxString &ChartListFileName) {
4523  bool b_run = FrameTimer1.IsRunning();
4524  FrameTimer1.Stop(); // stop other asynchronous activity
4525  bool b_runCOGTimer = FrameCOGTimer.IsRunning();
4526  FrameCOGTimer.Stop();
4527 
4528  // ..For each canvas...
4529  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4530  ChartCanvas *cc = g_canvasArray.Item(i);
4531  if (cc) {
4532  cc->InvalidateQuilt();
4533  cc->SetQuiltRefChart(-1);
4534  cc->m_singleChart = NULL;
4535  }
4536  }
4537 
4538  ChartData->PurgeCache();
4539 
4540  // TODO
4541  // delete pCurrentStack;
4542  // pCurrentStack = NULL;
4543 
4544  AbstractPlatform::ShowBusySpinner();
4545 
4546  wxGenericProgressDialog *pprog = nullptr;
4547  if (b_prog) {
4548  wxString longmsg = _("OpenCPN Chart Update");
4549  longmsg +=
4550  _T("..................................................................")
4551  _T("........");
4552 
4553  pprog = new wxGenericProgressDialog();
4554 
4555  wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
4556  pprog->SetFont(*qFont);
4557 
4558  pprog->Create(_("OpenCPN Chart Update"), longmsg, 100, gFrame,
4559  wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
4560  wxPD_REMAINING_TIME);
4561 
4562  DimeControl(pprog);
4563  pprog->Show();
4564  }
4565 
4566  wxLogMessage(_T(" "));
4567  wxLogMessage(_T("Starting chart database Update..."));
4568  wxString gshhg_chart_loc = gWorldMapLocation;
4569  gWorldMapLocation = wxEmptyString;
4570  ChartData->Update(DirArray, b_force, pprog);
4571  ChartData->SaveBinary(ChartListFileName);
4572  wxLogMessage(_T("Finished chart database Update"));
4573  wxLogMessage(_T(" "));
4574  if (gWorldMapLocation.empty()) { // Last resort. User might have deleted all
4575  // GSHHG data, but we still might have the
4576  // default dataset distributed with OpenCPN
4577  // or from the package repository...
4578  gWorldMapLocation = gDefaultWorldMapLocation;
4579  gshhg_chart_loc = wxEmptyString;
4580  }
4581 
4582  if (gWorldMapLocation != gshhg_chart_loc) {
4583  // ..For each canvas...
4584  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4585  ChartCanvas *cc = g_canvasArray.Item(i);
4586  if (cc) cc->ResetWorldBackgroundChart();
4587  }
4588  }
4589 
4590  delete pprog;
4591 
4592  AbstractPlatform::HideBusySpinner();
4593 
4594  pConfig->UpdateChartDirs(DirArray);
4595 
4596  // Restart timers, if necessary
4597  if (b_run) FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
4598  if (b_runCOGTimer) {
4599  // Restart the COG rotation timer, max frequency is 10 hz.
4600  int period_ms = 100;
4601  if (g_COGAvgSec > 0) period_ms = g_COGAvgSec * 1000;
4602  FrameCOGTimer.Start(period_ms, wxTIMER_CONTINUOUS);
4603  }
4604  return true;
4605 }
4606 
4607 void MyFrame::ToggleQuiltMode(ChartCanvas *cc) {
4608  if (cc) {
4609  cc->ToggleCanvasQuiltMode();
4610 #if 0
4611  bool cur_mode = cc->GetQuiltMode();
4612 
4613  if( !cc->GetQuiltMode() )
4614  cc->SetQuiltMode( true );
4615  else
4616  if( cc->GetQuiltMode() ) {
4617  cc->SetQuiltMode( false );
4618  g_sticky_chart = cc->GetQuiltReferenceChartIndex();
4619  }
4620 
4621 
4622  if( cur_mode != cc->GetQuiltMode() ){
4623  //TODO >>SetupQuiltMode();
4624  DoChartUpdate();
4625  cc->InvalidateGL();
4626  Refresh();
4627  }
4628  g_bQuiltEnable = cc->GetQuiltMode();
4629 
4630  // Recycle the S52 PLIB so that vector charts will flush caches and re-render
4631  if(ps52plib)
4632  ps52plib->GenerateStateHash();
4633 #endif
4634  }
4635 }
4636 
4637 void MyFrame::DoStackDown(ChartCanvas *cc) { DoStackDelta(cc, -1); }
4638 
4639 void MyFrame::DoStackUp(ChartCanvas *cc) { DoStackDelta(cc, 1); }
4640 
4641 void MyFrame::DoStackDelta(ChartCanvas *cc, int direction) {
4642  if (cc) {
4643  cc->DoCanvasStackDelta(direction);
4644  }
4645 }
4646 
4647 void MyFrame::PositionIENCToolbar() {
4648 #if 0
4649  if (g_iENCToolbar) {
4650  wxPoint posn;
4651  posn.x = (GetPrimaryCanvas()->GetSize().x - g_iENCToolbar->GetSize().x) / 2;
4652  posn.y = 4;
4653  g_iENCToolbar->Move(GetPrimaryCanvas()->ClientToScreen(posn));
4654  }
4655 #endif
4656 }
4657 
4658 // Defered initialization for anything that is not required to render the
4659 // initial frame and takes a while to initialize. This gets opencpn up and
4660 // running much faster.
4661 void MyFrame::OnInitTimer(wxTimerEvent &event) {
4662  InitTimer.Stop();
4663  wxString msg;
4664  msg.Printf(_T("OnInitTimer...%d"), m_iInitCount);
4665  wxLogMessage(msg);
4666 
4667  wxLog::FlushActive();
4668 
4669  switch (m_iInitCount++) {
4670  case 0: {
4671  if (g_MainToolbar) g_MainToolbar->EnableTool(ID_SETTINGS, false);
4672 
4673  if (g_bInlandEcdis) {
4674  double range = GetPrimaryCanvas()->GetCanvasRangeMeters();
4675  double range_set = 500.;
4676 
4677  range = wxRound(range * 10) / 10.;
4678 
4679  if (range > 4000.)
4680  range_set = 4000.;
4681  else if (range > 2000.)
4682  range_set = 2000.;
4683  else if (range > 1600.)
4684  range_set = 1600.;
4685  else if (range > 1200.)
4686  range_set = 1200.;
4687  else if (range > 800.)
4688  range_set = 800.;
4689  else
4690  range_set = 500.;
4691 
4692  GetPrimaryCanvas()->SetCanvasRangeMeters(range_set);
4693  }
4694 
4695  // Set persistent Fullscreen mode
4696  g_Platform->SetFullscreen(g_bFullscreen);
4697 
4698  // Rebuild chart database, if necessary
4699  if (g_NeedDBUpdate > 0) {
4700  RebuildChartDatabase();
4701  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4702  ChartCanvas *cc = g_canvasArray.Item(i);
4703  if (cc) {
4704  cc->SetGroupIndex(0, false); // all charts
4705  }
4706  }
4707 
4708  // As a favor to new users, poll the database and
4709  // move the initial viewport so that a chart will come up.
4710 
4711  double clat, clon;
4712  if (ChartData->GetCentroidOfLargestScaleChart(&clat, &clon,
4713  CHART_FAMILY_RASTER)) {
4714  gLat = clat;
4715  gLon = clon;
4716  gFrame->ClearbFollow(gFrame->GetPrimaryCanvas());
4717  } else {
4718  if (ChartData->GetCentroidOfLargestScaleChart(&clat, &clon,
4719  CHART_FAMILY_VECTOR)) {
4720  gLat = clat;
4721  gLon = clon;
4722  gFrame->ClearbFollow(gFrame->GetPrimaryCanvas());
4723  }
4724  }
4725 
4726  g_NeedDBUpdate = 0;
4727  }
4728 
4729  // Load the waypoints. Both of these routines are very slow to execute
4730  // which is why they have been to defered until here
4731  auto colour_func = [](wxString c) { return GetGlobalColor(c); };
4732  pWayPointMan = new WayPointman(colour_func);
4733  WayPointmanGui(*pWayPointMan).SetColorScheme(global_color_scheme,
4734  g_Platform->GetDisplayDPmm());
4735  // Reload the ownship icon from UserIcons, if present
4736  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4737  ChartCanvas *cc = g_canvasArray.Item(i);
4738  if (cc) {
4739  if (cc->SetUserOwnship()) cc->SetColorScheme(global_color_scheme);
4740  }
4741  }
4742 
4743  pConfig->LoadNavObjects();
4744  // Re-enable anchor watches if set in config file
4745  if (!g_AW1GUID.IsEmpty()) {
4746  pAnchorWatchPoint1 = pWayPointMan->FindRoutePointByGUID(g_AW1GUID);
4747  }
4748  if (!g_AW2GUID.IsEmpty()) {
4749  pAnchorWatchPoint2 = pWayPointMan->FindRoutePointByGUID(g_AW2GUID);
4750  }
4751 
4752  // Import Layer-wise any .gpx files from /layers directory
4753  wxString layerdir = g_Platform->GetPrivateDataDir();
4754  appendOSDirSlash(&layerdir);
4755  layerdir.Append(_T("layers"));
4756 
4757  if (wxDir::Exists(layerdir)) {
4758  wxString laymsg;
4759  laymsg.Printf(wxT("Getting .gpx layer files from: %s"),
4760  layerdir.c_str());
4761  wxLogMessage(laymsg);
4762  pConfig->LoadLayers(layerdir);
4763  }
4764 
4765  break;
4766  }
4767  case 1:
4768  // Connect Datastreams
4769 
4770  for (size_t i = 0; i < TheConnectionParams()->Count(); i++) {
4771  ConnectionParams *cp = TheConnectionParams()->Item(i);
4772  if (cp->bEnabled) {
4773  auto driver = MakeCommDriver(cp);
4774  cp->b_IsSetup = TRUE;
4775  }
4776  }
4777 
4778  console = new ConsoleCanvas(gFrame); // the console
4779  console->SetColorScheme(global_color_scheme);
4780 
4781  // Draw console if persisted route is active
4782  if (g_pRouteMan){
4783  if (g_pRouteMan->IsAnyRouteActive()){
4784  g_pRouteMan->GetDlgContext().show_with_fresh_fonts();
4785  }
4786  }
4787 
4788  break;
4789 
4790  case 2: {
4791  if (m_initializing) break;
4792  m_initializing = true;
4793  AbstractPlatform::ShowBusySpinner();
4794  PluginLoader::getInstance()->LoadAllPlugIns(true);
4795  AbstractPlatform::HideBusySpinner();
4796  // RequestNewToolbars();
4797  RequestNewMasterToolbar();
4798  // A Plugin (e.g. Squiddio) may have redefined some routepoint icons...
4799  // Reload all icons, to be sure.
4800  if (pWayPointMan) WayPointmanGui(*pWayPointMan).ReloadRoutepointIcons();
4801 
4802  if (g_MainToolbar) g_MainToolbar->EnableTool(ID_SETTINGS, false);
4803 
4804  wxString perspective;
4805  pConfig->SetPath(_T ( "/AUI" ));
4806  pConfig->Read(_T ( "AUIPerspective" ), &perspective);
4807 
4808  // Make sure the perspective saved in the config file is "reasonable"
4809  // In particular, the perspective should have an entry for every
4810  // windows added to the AUI manager so far.
4811  // If any are not found, then use the default layout
4812 
4813  bool bno_load = false;
4814 
4815  wxArrayString name_array;
4816  wxStringTokenizer st(perspective, _T("|;"));
4817  while (st.HasMoreTokens()) {
4818  wxString s1 = st.GetNextToken();
4819  if (s1.StartsWith(_T("name="))) {
4820  wxString sc = s1.AfterFirst('=');
4821  name_array.Add(sc);
4822  }
4823  }
4824 
4825  wxAuiPaneInfoArray pane_array_val = g_pauimgr->GetAllPanes();
4826  for (unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
4827  wxAuiPaneInfo pane = pane_array_val.Item(i);
4828 
4829  // If we find a pane that is not in the perspective,
4830  // then we should not load the perspective at all
4831  if (name_array.Index(pane.name) == wxNOT_FOUND) {
4832  bno_load = true;
4833  break;
4834  }
4835  }
4836 
4837  if (!bno_load) g_pauimgr->LoadPerspective(perspective, false);
4838 
4839 #if 0
4840  // Undefine the canvas sizes as expressed by the loaded perspective
4841  for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){
4842  ChartCanvas *cc = g_canvasArray.Item(i);
4843  if(cc)
4844  g_pauimgr->GetPane(cc).MinSize(10,10);
4845  }
4846 
4847 #endif
4848 
4849  // Touch up the AUI manager
4850  // Make sure that any pane width is reasonable default value
4851  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4852  ChartCanvas *cc = g_canvasArray.Item(i);
4853  if (cc) {
4854  wxSize frameSize = GetClientSize();
4855  wxSize minSize = g_pauimgr->GetPane(cc).min_size;
4856  int width = wxMax(minSize.x, frameSize.x / 10);
4857  g_pauimgr->GetPane(cc).MinSize(frameSize.x * 1 / 5, frameSize.y);
4858  }
4859  }
4860  g_pauimgr->Update();
4861 
4862  // Notify all the AUI PlugIns so that they may syncronize with the
4863  // Perspective
4864  g_pi_manager->NotifyAuiPlugIns();
4865 
4866  // Give the user dialog on any blacklisted PlugIns
4867  g_pi_manager ->ShowDeferredBlacklistMessages();
4868 
4869  g_pi_manager->CallLateInit();
4870 
4871  // If any PlugIn implements PlugIn Charts, we need to re-run the initial
4872  // chart load logic to select the correct chart as saved from the last
4873  // run of the app. This will be triggered at the next DoChartUpdate()
4874  if (g_pi_manager->IsAnyPlugInChartEnabled()) {
4875  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4876  ChartCanvas *cc = g_canvasArray.Item(i);
4877  if (cc) cc->SetFirstAuto(true);
4878  }
4879 
4880  b_reloadForPlugins = true;
4881  }
4882 
4883  break;
4884  }
4885 
4886  case 3: {
4887  if (g_MainToolbar) {
4888  g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
4889  g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
4890  }
4891 
4892 #if 0 // per-canvas toolbars deprecated in MUI
4893 
4894  // .. for each canvas...
4895  for(unsigned int i=0 ; i < g_canvasArray.GetCount() ; i++){
4896  ChartCanvas *cc = g_canvasArray.Item(i);
4897  cc->RequestNewCanvasToolbar( true );
4898 
4899  if(cc && cc->GetToolbarEnable()){
4900  cc->GetToolbar()->SetAutoHide(g_bAutoHideToolbar);
4901  cc->GetToolbar()->SetAutoHideTimer(g_nAutoHideToolbar);
4902  }
4903  }
4904 #endif
4905 
4906  break;
4907  }
4908 
4909  case 4: {
4910  int sx, sy;
4911  pConfig->SetPath("/Settings");
4912  pConfig->Read("OptionsSizeX", &sx, -1);
4913  pConfig->Read("OptionsSizeY", &sy, -1);
4914 
4915  wxWindow *optionsParent = this;
4916 #ifdef __WXOSX__
4917  optionsParent = GetPrimaryCanvas();
4918 #endif
4919  g_options =
4920  new options(optionsParent, -1, _("Options"), wxPoint(-1, -1), wxSize(sx, sy));
4921 
4922  BuildiENCToolbar(true);
4923 
4924  break;
4925  }
4926 
4927  case 5: {
4928  // FIXME (leamas) Remove, delegate to CmdlineClient ctor
4929  if (!g_params.empty()) {
4930  for (size_t n = 0; n < g_params.size(); n++) {
4931  wxString path = g_params[n];
4932  if (::wxFileExists(path)) {
4934  pSet->load_file(path.fn_str());
4935  int wpt_dups;
4936 
4937  pSet->LoadAllGPXObjects(
4938  !pSet->IsOpenCPN(), wpt_dups,
4939  true); // Import with full vizibility of names and objects
4940  LLBBox box = pSet->GetBBox();
4941  if (box.GetValid()) {
4942  CenterView(GetPrimaryCanvas(), box);
4943  }
4944  delete pSet;
4945  }
4946  }
4947  }
4948  break;
4949  }
4950  case 6: {
4951  InitAppMsgBusListener();
4952  InitApiListeners();
4953 
4954  break;
4955  }
4956 
4957  default: {
4958  // Last call....
4959  wxLogMessage(_T("OnInitTimer...Last Call"));
4960 
4961  PositionIENCToolbar();
4962 
4963  g_bDeferredInitDone = true;
4964 
4965  GetPrimaryCanvas()->SetFocus();
4966  g_focusCanvas = GetPrimaryCanvas();
4967 
4968 #ifndef __ANDROID__
4969  gFrame->Raise();
4970 #endif
4971 
4972  if (b_reloadForPlugins) {
4973  DoChartUpdate();
4974  ChartsRefresh();
4975  }
4976 
4977  wxLogMessage(_T("OnInitTimer...Finalize Canvases"));
4978 
4979  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4980  ChartCanvas *cc = g_canvasArray.Item(i);
4981  if (cc) {
4982  cc->CreateMUIBar();
4983  cc->CheckGroupValid();
4984  }
4985  }
4986 
4987 #ifdef __ANDROID__
4988  androidEnableBackButton(true);
4989  androidEnableRotation();
4990  androidEnableOptionItems(true);
4991  androidLastCall();
4992 #endif
4993 
4994  if (g_MainToolbar) g_MainToolbar->EnableTool(ID_SETTINGS, true);
4995 
4996  UpdateStatusBar();
4997 
4998  SendSizeEvent();
4999 
5000  break;
5001  }
5002  } // switch
5003 
5004  if (!g_bDeferredInitDone) InitTimer.Start(100, wxTIMER_ONE_SHOT);
5005 
5006  wxLog::FlushActive();
5007 
5008  RefreshAllCanvas(true);
5009  wxGetApp().m_usb_watcher.Start();
5010 }
5011 
5012 wxDEFINE_EVENT(EVT_BASIC_NAV_DATA, ObservedEvt);
5013 wxDEFINE_EVENT(EVT_GPS_WATCHDOG, ObservedEvt);
5014 
5015 void MyFrame::InitAppMsgBusListener() {
5016  auto &msgbus = AppMsgBus::GetInstance();
5017 
5018  // BasicNavData
5019  AppMsg msg_basic(AppMsg::Type::BasicNavData);
5020  listener_basic_navdata.Listen(msg_basic, this, EVT_BASIC_NAV_DATA);
5021 
5022  Bind(EVT_BASIC_NAV_DATA, [&](ObservedEvt ev) {
5023  auto ptr = ev.GetSharedPtr();
5024  auto basicnav_msg = std::static_pointer_cast<const BasicNavDataMsg>(ptr);
5025  HandleBasicNavMsg( basicnav_msg );
5026  });
5027 
5028  // GPS Watchdog expiry status
5029  AppMsg msg_watchdog(AppMsg::Type::GPSWatchdog);
5030  listener_gps_watchdog.Listen(msg_watchdog, this, EVT_GPS_WATCHDOG);
5031 
5032  Bind(EVT_GPS_WATCHDOG, [&](ObservedEvt ev) {
5033  auto ptr = ev.GetSharedPtr();
5034  auto msg = std::static_pointer_cast<const GPSWatchdogMsg>(ptr);
5035  HandleGPSWatchdogMsg(msg);
5036  });
5037 
5038 }
5039 
5041 #ifdef __ANDROID__
5042 void MyFrame::InitApiListeners() {}
5043 void MyFrame::ReleaseApiListeners() {}
5044 
5045 #else
5047  auto& server = LocalServerApi::GetInstance();
5048  m_on_raise_listener.Init(server.on_raise, [&](ObservedEvt){ Raise(); });
5049  m_on_quit_listener.Init(server.on_quit, [&](ObservedEvt){ FastClose(); });
5050  server.SetGetRestApiEndpointCb(
5051  [&]{ return wxGetApp().m_rest_server.GetEndpoint(); });
5052  server.open_file_cb =
5053  [](const std::string& path) { return wxGetApp().OpenFile(path); };
5054 
5055 }
5056 
5057 void MyFrame::ReleaseApiListeners() { LocalServerApi::ReleaseInstance(); }
5058 #endif
5059 
5060 void MyFrame::HandleGPSWatchdogMsg(std::shared_ptr<const GPSWatchdogMsg> msg) {
5061 
5062  if (msg->gps_watchdog <= 0){
5063  if (msg->wd_source == GPSWatchdogMsg::WDSource::position){
5064  bool last_bGPSValid = bGPSValid;
5065  bGPSValid = false;
5066  m_fixtime = 0; // Invalidate fix time
5067  if (last_bGPSValid != bGPSValid) UpdateGPSCompassStatusBoxes(true);
5068  }
5069  else if (msg->wd_source == GPSWatchdogMsg::WDSource::velocity){
5070  bool last_bVelocityValid = bVelocityValid;
5071  bVelocityValid = false;
5072  }
5073 
5074  UpdateStatusBar();
5075  }
5076 }
5077 
5078 void MyFrame::HandleBasicNavMsg(std::shared_ptr<const BasicNavDataMsg> msg) {
5079  m_fixtime = msg->time;
5080 
5081  // Maintain average COG for Course Up Mode
5082  if (!std::isnan(gCog)) {
5083  if (g_COGAvgSec > 0) {
5084  // Make a hole
5085  for (int i = g_COGAvgSec - 1; i > 0; i--) COGTable[i] = COGTable[i - 1];
5086  COGTable[0] = gCog;
5087 
5088  double sum = 0., count = 0;
5089  for (int i = 0; i < g_COGAvgSec; i++) {
5090  double adder = COGTable[i];
5091  if (std::isnan(adder)) continue;
5092 
5093  if (fabs(adder - g_COGAvg) > 180.) {
5094  if ((adder - g_COGAvg) > 0.)
5095  adder -= 360.;
5096  else
5097  adder += 360.;
5098  }
5099 
5100  sum += adder;
5101  count++;
5102  }
5103  sum /= count;
5104 
5105  if (sum < 0.)
5106  sum += 360.;
5107  else if (sum >= 360.)
5108  sum -= 360.;
5109 
5110  g_COGAvg = sum;
5111  } else
5112  g_COGAvg = gCog;
5113  }
5114 
5115  FilterCogSog();
5116 
5117  // If gSog is greater than some threshold, we determine that we are
5118  // "cruising"
5119  if (gSog > 3.0) g_bCruising = true;
5120 
5121 
5122 // Maintain the GPS position validity flag
5123 // Determined by source validity of RMC, GGA, GLL (N0183)
5124 // or PGNs 129029, 129025 (N2K)
5125 // Positions by sK and AIVDO are assumed valid
5126  bool last_bGPSValid = bGPSValid;
5127  if ((msg->vflag & POS_UPDATE) == POS_UPDATE) {
5128  if ((msg->vflag & POS_VALID) == POS_VALID)
5129  bGPSValid = true;
5130  else
5131  bGPSValid = false;
5132  }
5133  if (last_bGPSValid != bGPSValid)
5134  UpdateGPSCompassStatusBoxes(true);
5135 
5136  bVelocityValid = true;
5137  UpdateStatusBar();
5138 
5139 #if 0
5140 #ifdef ocpnUPDATE_SYSTEM_TIME
5141 
5142  // Use the fix time to update the local system clock, only once per
5143  // session
5144  if (!m_bTimeIsSet) {
5145  if (!s_bSetSystemTime) {
5146  m_bTimeIsSet = true;
5147  return;
5148  }
5149  wxDateTime Fix_Time(wxDateTime::Now());
5150 
5151  if (6 == sfixtime.Len() ||
5152  6 == sfixtime.find('.')) { // perfectly recognised format?
5153  wxString a;
5154  long b;
5155  a = sfixtime.Mid(0, 2);
5156  if (a.ToLong(&b)) Fix_Time.SetHour((wxDateTime::wxDateTime_t)b);
5157  a = sfixtime.Mid(2, 2);
5158  if (a.ToLong(&b)) Fix_Time.SetMinute((wxDateTime::wxDateTime_t)b);
5159  a = sfixtime.Mid(4, 2);
5160  if (a.ToLong(&b)) Fix_Time.SetSecond((wxDateTime::wxDateTime_t)b);
5161  } else
5162  return; // not a good sfixtime format
5163 
5164  time_t TimeOff = Fix_Time.GetTicks() - wxDateTime::Now().GetTicks();
5165 
5166  if (g_bHasHwClock) { // if a realtime hardwareclock isavailable we only
5167  // check for time and a max of 2 hours of to prevent
5168  // bogus info from some gps devices
5169  if ((abs(TimeOff) > 20) && (abs(TimeOff) < 7200)) {
5170  wxString msg;
5171  msg.Printf(_T("Setting system time, delta t is %d seconds"), TimeOff);
5172  wxLogMessage(msg);
5173 #ifdef __WXMSW__
5174  // Fix up the fix_time to convert to GMT
5175  Fix_Time = Fix_Time.ToGMT();
5176 
5177  // Code snippet following borrowed from wxDateCtrl, MSW
5178  const wxDateTime::Tm tm(Fix_Time.GetTm());
5179  SYSTEMTIME stm;
5180  stm.wYear = (WXWORD)tm.year;
5181  stm.wMonth = (WXWORD)(tm.mon - wxDateTime::Jan + 1);
5182  stm.wDay = tm.mday;
5183  stm.wDayOfWeek = 0;
5184  stm.wHour = Fix_Time.GetHour();
5185  stm.wMinute = tm.min;
5186  stm.wSecond = tm.sec;
5187  stm.wMilliseconds = 0;
5188 
5189  ::SetSystemTime(&stm); // in GMT
5190 #else
5191  // This contortion sets the system date/time on POSIX host
5192  // Requires the following line in /etc/sudoers
5193  // "nav ALL=NOPASSWD:/bin/date *" (where nav is your username)
5194  // or "%sudo ALL=NOPASSWD:/bin/date *"
5195  wxString CommandStr("sudo /bin/date +%T --utc --set=\"");
5196  CommandStr.Append(Fix_Time.Format("%T"));
5197  CommandStr.Append("\"");
5198  msg.Printf(_T("Linux command is:"));
5199  msg += CommandStr;
5200  wxLogMessage(msg);
5201  wxExecute(CommandStr, wxEXEC_ASYNC);
5202 #endif //__WXMSW__
5203  }
5204  m_bTimeIsSet = true;
5205  } else { // no hw-clock set both date and time
5206  if (gRmcDate.Len() == 6) {
5207 #if !defined(__WXMSW__) // not for windows
5208  wxString a;
5209  long b;
5210  Fix_Time.SetMonth((wxDateTime::Month)2);
5211  a = gRmcDate.Mid(0, 2);
5212  if (a.ToLong(&b)) Fix_Time.SetDay(b);
5213  a = gRmcDate.Mid(2, 2);
5214  if (a.ToLong(&b)) Fix_Time.SetMonth((wxDateTime::Month)(b - 1));
5215  a = gRmcDate.Mid(4, 2);
5216  if (a.ToLong(&b))
5217  Fix_Time.SetYear(b + 2000); // TODO fix this before the year 2100
5218  wxString msg;
5219  wxString CommandStr("sudo /bin/date --utc --set=\"");
5220  CommandStr.Append(Fix_Time.Format("%D %T\""));
5221  msg.Printf(_T("Set Date/Time, Linux command is: %s"), CommandStr);
5222  wxLogMessage(msg);
5223  wxExecute(CommandStr, wxEXEC_ASYNC);
5224 #endif // !__WXMSW__
5225  m_bTimeIsSet = true;
5226  }
5227  }
5228  }
5229 #endif // ocpnUPDATE_SYSTEM_TIME
5230 #endif
5231 
5232 }
5233 
5234 void MyFrame::UpdateStatusBar() {
5235  // Show a little heartbeat tick in StatusWindow0 on NMEA events
5236  // But no faster than 10 hz.
5237  unsigned long uiCurrentTickCount;
5238  m_MMEAeventTime.SetToCurrent();
5239  uiCurrentTickCount =
5240  m_MMEAeventTime.GetMillisecond() / 100; // tenths of a second
5241  uiCurrentTickCount += m_MMEAeventTime.GetTicks() * 10;
5242  if (uiCurrentTickCount > m_ulLastNMEATicktime + 1) {
5243  m_ulLastNMEATicktime = uiCurrentTickCount;
5244 
5245  if (m_tick_idx++ > 6) m_tick_idx = 0;
5246  }
5247 
5248  // Show gLat/gLon in StatusWindow0
5249 
5250  if (NULL != GetStatusBar()) {
5251  if (1/*pos_valid*/) {
5252  char tick_buf[2];
5253  tick_buf[0] = nmea_tick_chars[m_tick_idx];
5254  tick_buf[1] = 0;
5255 
5256  wxString s1(tick_buf, wxConvUTF8);
5257  s1 += _(" Ship ");
5258  s1 += toSDMM(1, gLat);
5259  s1 += _T(" ");
5260  s1 += toSDMM(2, gLon);
5261 
5262  if (STAT_FIELD_TICK >= 0) SetStatusText(s1, STAT_FIELD_TICK);
5263  }
5264 
5265  wxString sogcog;
5266  if (!std::isnan(gSog))
5267  sogcog.Printf(_T("SOG %2.2f ") + getUsrSpeedUnit() + _T(" "),
5268  toUsrSpeed(gSog));
5269  else
5270  sogcog.Printf(_T("SOG --- "));
5271 
5272  wxString cogs;
5273  // We show COG only if SOG is > 0.05
5274  if (!std::isnan(gCog) && !std::isnan(gSog) && (gSog > 0.05)) {
5275  if (g_bShowTrue)
5276  cogs << wxString::Format(wxString("COG %03d%c "), (int)gCog, 0x00B0);
5277  if (g_bShowMag)
5278  cogs << wxString::Format(wxString("COG %03d%c(M) "), (int)toMagnetic(gCog),
5279  0x00B0);
5280  } else
5281  cogs.Printf(("COG ---%c"), 0x00B0);
5282 
5283  sogcog.Append(cogs);
5284  SetStatusText(sogcog, STAT_FIELD_SOGCOG);
5285  }
5286 
5287 }
5288 
5289 
5290 // Manage the application memory footprint on a periodic schedule
5291 void MyFrame::OnMemFootTimer(wxTimerEvent &event) {
5292  MemFootTimer.Stop();
5293 
5294  int memsize = GetApplicationMemoryUse();
5295 
5296  g_MemFootMB = 100;
5297  printf("Memsize: %d \n", memsize);
5298  // The application memory usage has exceeded the target, so try to manage it
5299  // down....
5300  if (memsize > (g_MemFootMB * 1000)) {
5301  ChartCanvas *cc = GetPrimaryCanvas();
5302  if (ChartData && cc) {
5303  // Get a local copy of the cache info
5304  wxArrayPtrVoid *pCache = ChartData->GetChartCache();
5305  unsigned int nCache = pCache->GetCount();
5306  CacheEntry *pcea = new CacheEntry[nCache];
5307 
5308  for (unsigned int i = 0; i < nCache; i++) {
5309  CacheEntry *pce = (CacheEntry *)(pCache->Item(i));
5310  pcea[i] = *pce; // ChartBase *Ch = (ChartBase *)pce->pChart;
5311  }
5312 
5313  if (nCache > 1) {
5314  // Bubble Sort the local cache entry array
5315  bool b_cont = true;
5316  while (b_cont) {
5317  b_cont = false;
5318  for (unsigned int i = 0; i < nCache - 1; i++) {
5319  if (pcea[i].RecentTime > pcea[i + 1].RecentTime) {
5320  CacheEntry tmp = pcea[i];
5321  pcea[i] = pcea[i + 1];
5322  pcea[i + 1] = tmp;
5323  b_cont = true;
5324  break;
5325  }
5326  }
5327  }
5328 
5329  // Free up some chart cache entries until the memory footprint target
5330  // is realized
5331 
5332  unsigned int idelete = 0; // starting at top. which is oldest
5333  unsigned int idelete_max = pCache->GetCount();
5334 
5335  // How many can be deleted?
5336  unsigned int minimum_cache = 1;
5337  if (cc->GetQuiltMode()) minimum_cache = cc->GetQuiltChartCount();
5338 
5339  while ((memsize > (g_MemFootMB * 1000)) &&
5340  (pCache->GetCount() > minimum_cache) &&
5341  (idelete < idelete_max)) {
5342  int memsizeb = memsize;
5343 
5344  ChartData->DeleteCacheChart((ChartBase *)pcea[idelete].pChart);
5345  idelete++;
5346  memsize = GetApplicationMemoryUse();
5347  printf("delete, before: %d after: %d\n", memsizeb, memsize);
5348  }
5349  }
5350 
5351  delete[] pcea;
5352  }
5353  }
5354 
5355  MemFootTimer.Start(9000, wxTIMER_CONTINUOUS);
5356 }
5357 
5358 int ut_index;
5359 
5360 void MyFrame::CheckToolbarPosition() {
5361 #ifdef __WXMAC__
5362  // Manage Full Screen mode on Mac Mojave 10.14
5363  static bool bMaximized;
5364 
5365  if (IsMaximized() && !bMaximized) {
5366  bMaximized = true;
5367  if (g_MainToolbar) {
5368  g_MainToolbar->SetYAuxOffset(g_MainToolbar->GetToolSize().y * 15 / 10);
5369  g_MainToolbar->SetDefaultPosition();
5370  g_MainToolbar->Realize();
5371  }
5372  PositionIENCToolbar();
5373  } else if (!IsMaximized() && bMaximized) {
5374  bMaximized = false;
5375  if (g_MainToolbar) {
5376  g_MainToolbar->SetYAuxOffset(0);
5377  g_MainToolbar->SetDockY(-1);
5378  g_MainToolbar->SetDefaultPosition();
5379  g_MainToolbar->Realize();
5380  }
5381  PositionIENCToolbar();
5382  }
5383 #endif
5384 }
5385 
5386 void MyFrame::OnFrameTimer1(wxTimerEvent &event) {
5387  CheckToolbarPosition();
5388 
5389  if (!g_bPauseTest && (g_unit_test_1 || g_unit_test_2)) {
5390  // if((0 == ut_index) && GetQuiltMode())
5391  // ToggleQuiltMode();
5392 
5393  // We use only one canvas for the unit tests, so far...
5394  ChartCanvas *cc = GetPrimaryCanvas();
5395 
5396  cc->m_bFollow = false;
5397  if (g_MainToolbar && g_MainToolbar->GetToolbar())
5398  g_MainToolbar->GetToolbar()->ToggleTool(ID_FOLLOW, cc->m_bFollow);
5399  int ut_index_max = ((g_unit_test_1 > 0) ? (g_unit_test_1 - 1) : INT_MAX);
5400 
5401  if (ChartData) {
5402  if (cc->m_groupIndex > 0) {
5403  while (ut_index < ChartData->GetChartTableEntries() &&
5404  !ChartData->IsChartInGroup(ut_index, cc->m_groupIndex)) {
5405  ut_index++;
5406  }
5407  }
5408  if (ut_index < ChartData->GetChartTableEntries()) {
5409  // printf("%d / %d\n", ut_index, ChartData->GetChartTableEntries());
5410  const ChartTableEntry *cte = &ChartData->GetChartTableEntry(ut_index);
5411 
5412  double clat = (cte->GetLatMax() + cte->GetLatMin()) / 2;
5413  double clon = (cte->GetLonMax() + cte->GetLonMin()) / 2;
5414 
5415  vLat = clat;
5416  vLon = clon;
5417 
5418  cc->SetViewPoint(clat, clon);
5419 
5420  if (cc->GetQuiltMode()) {
5421  if (cc->IsChartQuiltableRef(ut_index))
5422  cc->SelectQuiltRefdbChart(ut_index);
5423  } else
5424  cc->SelectdbChart(ut_index);
5425 
5426  double ppm; // final ppm scale to use
5427  if (g_unit_test_1) {
5428  ppm = cc->GetCanvasScaleFactor() / cte->GetScale();
5429  ppm /= 2;
5430  } else {
5431  double rw, rh; // width, height
5432  int ww, wh; // chart window width, height
5433 
5434  // width in nm
5435  DistanceBearingMercator(cte->GetLatMin(), cte->GetLonMin(),
5436  cte->GetLatMin(), cte->GetLonMax(), NULL,
5437  &rw);
5438 
5439  // height in nm
5440  DistanceBearingMercator(cte->GetLatMin(), cte->GetLonMin(),
5441  cte->GetLatMax(), cte->GetLonMin(), NULL,
5442  &rh);
5443 
5444  cc->GetSize(&ww, &wh);
5445  ppm = wxMin(ww / (rw * 1852), wh / (rh * 1852)) * (100 - fabs(clat)) /
5446  90;
5447  ppm = wxMin(ppm, 1.0);
5448  }
5449  cc->SetVPScale(ppm);
5450 
5451  cc->ReloadVP();
5452 
5453  ut_index++;
5454  if (ut_index > ut_index_max) exit(0);
5455  } else {
5456  _exit(0);
5457  }
5458  }
5459  }
5460  g_tick++;
5461 
5462  // Listen for quitflag to be set, requesting application close
5463  if (quitflag) {
5464  wxLogMessage(_T("Got quitflag from SIGNAL"));
5465  FrameTimer1.Stop();
5466  Close();
5467  return;
5468  }
5469 
5470  if (bDBUpdateInProgress) return;
5471 
5472  FrameTimer1.Stop();
5473 
5474  // If tracking carryover was found in config file, enable tracking as soon as
5475  // GPS become valid
5476  if (g_bDeferredStartTrack) {
5477  if (!g_bTrackActive) {
5478  if (bGPSValid) {
5479  gFrame->TrackOn();
5480  g_bDeferredStartTrack = false;
5481  }
5482  } else { // tracking has been manually activated
5483  g_bDeferredStartTrack = false;
5484  }
5485  }
5486 
5487 
5488  // Build and send a Position Fix event to PlugIns
5489  if (g_pi_manager) {
5490  GenericPosDatEx GPSData;
5491  GPSData.kLat = gLat;
5492  GPSData.kLon = gLon;
5493  GPSData.kCog = gCog;
5494  GPSData.kSog = gSog;
5495  GPSData.kVar = gVar;
5496  GPSData.kHdm = gHdm;
5497  GPSData.kHdt = gHdt;
5498  GPSData.nSats = g_SatsInView;
5499 
5500  wxDateTime tCheck((time_t)m_fixtime);
5501 
5502  if (tCheck.IsValid())
5503  GPSData.FixTime = m_fixtime;
5504  else
5505  GPSData.FixTime = wxDateTime::Now().GetTicks();
5506 
5507  g_pi_manager->SendPositionFixToAllPlugIns(&GPSData);
5508  }
5509 
5510  // Check for anchorwatch alarms // pjotrc
5511  // 2010.02.15
5512  if (pAnchorWatchPoint1) {
5513  double dist;
5514  double brg;
5515  DistanceBearingMercator(pAnchorWatchPoint1->m_lat,
5516  pAnchorWatchPoint1->m_lon, gLat, gLon, &brg, &dist);
5517  double d = g_nAWMax;
5518  (pAnchorWatchPoint1->GetName()).ToDouble(&d);
5519  d = AnchorDistFix(d, AnchorPointMinDist, g_nAWMax);
5520  bool toofar = false;
5521  bool tooclose = false;
5522  if (d >= 0.0) toofar = (dist * 1852. > d);
5523  if (d < 0.0) tooclose = (dist * 1852 < -d);
5524 
5525  if (tooclose || toofar)
5526  AnchorAlertOn1 = true;
5527  else
5528  AnchorAlertOn1 = false;
5529  } else
5530  AnchorAlertOn1 = false;
5531 
5532  if (pAnchorWatchPoint2) {
5533  double dist;
5534  double brg;
5535  DistanceBearingMercator(pAnchorWatchPoint2->m_lat,
5536  pAnchorWatchPoint2->m_lon, gLat, gLon, &brg, &dist);
5537 
5538  double d = g_nAWMax;
5539  (pAnchorWatchPoint2->GetName()).ToDouble(&d);
5540  d = AnchorDistFix(d, AnchorPointMinDist, g_nAWMax);
5541  bool toofar = false;
5542  bool tooclose = false;
5543  if (d >= 0) toofar = (dist * 1852. > d);
5544  if (d < 0) tooclose = (dist * 1852 < -d);
5545 
5546  if (tooclose || toofar)
5547  AnchorAlertOn2 = true;
5548  else
5549  AnchorAlertOn2 = false;
5550  } else
5551  AnchorAlertOn2 = false;
5552 
5553  if ((pAnchorWatchPoint1 || pAnchorWatchPoint2) && !bGPSValid)
5554  AnchorAlertOn1 = true;
5555 
5556  // Send current nav status data to log file on every half hour // pjotrc
5557  // 2010.02.09
5558 
5559  wxDateTime lognow = wxDateTime::Now(); // pjotrc 2010.02.09
5560  int hourLOC = lognow.GetHour();
5561  int minuteLOC = lognow.GetMinute();
5562  lognow.MakeGMT();
5563  int minuteUTC = lognow.GetMinute();
5564  int second = lognow.GetSecond();
5565 
5566  wxTimeSpan logspan = lognow.Subtract(g_loglast_time);
5567  if ((logspan.IsLongerThan(wxTimeSpan(0, 30, 0, 0))) || (minuteUTC == 0) ||
5568  (minuteUTC == 30)) {
5569  if (logspan.IsLongerThan(wxTimeSpan(0, 1, 0, 0))) {
5570  wxString day = lognow.FormatISODate();
5571  wxString utc = lognow.FormatISOTime();
5572  wxString navmsg = _T("LOGBOOK: ");
5573  navmsg += day;
5574  navmsg += _T(" ");
5575  navmsg += utc;
5576  navmsg += _T(" UTC ");
5577 
5578  if (bGPSValid) {
5579  wxString data;
5580  data.Printf(_T(" GPS Lat %10.5f Lon %10.5f "), gLat, gLon);
5581  navmsg += data;
5582 
5583  wxString cog;
5584  if (std::isnan(gCog))
5585  cog.Printf(_T("COG ----- "));
5586  else
5587  cog.Printf(_T("COG %10.5f "), gCog);
5588 
5589  wxString sog;
5590  if (std::isnan(gSog))
5591  sog.Printf(_T("SOG ----- "));
5592  else
5593  sog.Printf(_T("SOG %6.2f ") + getUsrSpeedUnit(), toUsrSpeed(gSog));
5594 
5595  navmsg += cog;
5596  navmsg += sog;
5597  } else {
5598  wxString data;
5599  data.Printf(_T(" DR Lat %10.5f Lon %10.5f"), gLat, gLon);
5600  navmsg += data;
5601  }
5602  wxLogMessage(navmsg);
5603  g_loglast_time = lognow;
5604 
5605  int bells = (hourLOC % 4) * 2; // 2 bells each hour
5606  if (minuteLOC != 0) bells++; // + 1 bell on 30 minutes
5607  if (!bells) bells = 8; // 0 is 8 bells
5608 
5609  if (g_bPlayShipsBells && ((minuteLOC == 0) || (minuteLOC == 30))) {
5610  m_BellsToPlay = bells;
5611  wxCommandEvent ev(BELLS_PLAYED_EVTYPE);
5612  wxPostEvent(this, ev);
5613  }
5614  }
5615  }
5616 
5617  if (ShouldRestartTrack()) TrackDailyRestart();
5618 
5619  // If no alerts are on, then safe to resume sleeping
5620  if (g_bSleep && !AnchorAlertOn1 && !AnchorAlertOn2) {
5621  FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
5622  return;
5623  }
5624 
5625  // Update the Toolbar Status windows and lower status bar
5626  // just after start of ticks.
5627 
5628  if (g_tick == 2) {
5629  wxString sogcog(_T("SOG --- ") + getUsrSpeedUnit() + +_T(" ") +
5630  _T(" COG ---\u00B0"));
5631  if (GetStatusBar()) SetStatusText(sogcog, STAT_FIELD_SOGCOG);
5632 
5633  gCog = 0.0; // say speed is zero to kill ownship predictor
5634  }
5635 
5636 // TODO
5637 // Not needed?
5638 #if 0
5639 #if !defined(__WXGTK__) && !defined(__WXQT__)
5640  {
5641  double cursor_lat, cursor_lon;
5642  GetPrimaryCanvas()->GetCursorLatLon( &cursor_lat, &cursor_lon );
5643  GetPrimaryCanvas()->SetCursorStatus(cursor_lat, cursor_lon);
5644  }
5645 #endif
5646 #endif
5647 
5648  // Update the chart database and displayed chart
5649  bool bnew_view = false;
5650 
5651  // Do the chart update based on the global update period currently set
5652  // If in COG UP mode, the chart update is handled by COG Update timer
5653  if ( (0 != g_ChartUpdatePeriod)) {
5654  if (0 == m_ChartUpdatePeriod--) {
5655  bnew_view = DoChartUpdate();
5656  m_ChartUpdatePeriod = g_ChartUpdatePeriod;
5657  }
5658  }
5659 
5660  nBlinkerTick++;
5661 
5662  // This call sends autopilot output strings to output ports.
5663 
5664  bool bactiveRouteUpdate = RoutemanGui(*g_pRouteMan).UpdateProgress();
5665 
5666  // For each canvas....
5667  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5668  ChartCanvas *cc = g_canvasArray.Item(i);
5669  if (cc) {
5670  cc->DrawBlinkObjects();
5671 
5672  // Update the active route, if any, as determined above
5673  if (bactiveRouteUpdate) {
5674  // This RefreshRect will cause any active routepoint to blink
5675  if (g_pRouteMan->GetpActiveRoute())
5676  cc->RefreshRect(g_blink_rect, false);
5677  }
5678 
5679  // Force own-ship drawing parameters
5680  cc->SetOwnShipState(SHIP_NORMAL);
5681 
5682  if (cc->GetQuiltMode()) {
5683  double erf = cc->GetQuiltMaxErrorFactor();
5684  if (erf > 0.02) cc->SetOwnShipState(SHIP_LOWACCURACY);
5685  } else {
5686  if (cc->m_singleChart) {
5687  if (cc->m_singleChart->GetChart_Error_Factor() > 0.02)
5688  cc->SetOwnShipState(SHIP_LOWACCURACY);
5689  }
5690  }
5691 
5692  if (!bGPSValid) cc->SetOwnShipState(SHIP_INVALID);
5693 
5694  if ((bGPSValid != m_last_bGPSValid) ||
5695  (bVelocityValid != m_last_bVelocityValid)) {
5696  if (!g_bopengl) cc->UpdateShips();
5697 
5698  bnew_view = true; // force a full Refresh()
5699  }
5700  }
5701  }
5702 
5703  m_last_bGPSValid = bGPSValid;
5704  m_last_bVelocityValid = bVelocityValid;
5705 
5706  // If any PlugIn requested dynamic overlay callbacks, force a full canvas
5707  // refresh thus, ensuring at least 1 Hz. callback.
5708  bool brq_dynamic = false;
5709  if (g_pi_manager) {
5710  auto *pplugin_array = PluginLoader::getInstance()->GetPlugInArray();
5711  for (unsigned int i = 0; i < pplugin_array->GetCount(); i++) {
5712  PlugInContainer *pic = pplugin_array->Item(i);
5713  if (pic->m_enabled && pic->m_init_state) {
5714  if (pic->m_cap_flag & WANTS_DYNAMIC_OPENGL_OVERLAY_CALLBACK) {
5715  brq_dynamic = true;
5716  break;
5717  }
5718  }
5719  }
5720 
5721  if (brq_dynamic) bnew_view = true;
5722  }
5723 
5724  // Make sure we get a redraw and alert sound on AnchorWatch excursions.
5725  if (AnchorAlertOn1 || AnchorAlertOn2) bnew_view = true;
5726 
5727  // For each canvas....
5728  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5729  ChartCanvas *cc = g_canvasArray.Item(i);
5730  if (cc) {
5731  if (g_bopengl) {
5732 #ifdef ocpnUSE_GL
5733  if (cc->GetglCanvas()) {
5734  if (m_fixtime - cc->GetglCanvas()->m_last_render_time > 0)
5735  bnew_view = true;
5736  }
5737 
5738  if (AnyAISTargetsOnscreen(cc, cc->GetVP())) bnew_view = true;
5739 
5740  if (bnew_view) /* full frame in opengl mode */
5741  cc->Refresh(false);
5742 #endif
5743  } else {
5744  // Invalidate the ChartCanvas window appropriately
5745  // In non-follow mode, invalidate the rectangles containing the AIS
5746  // targets and the ownship, etc... In follow mode, if there has
5747  // already been a full screen refresh, there is no need to check
5748  // ownship or AIS,
5749  // since they will be always drawn on the full screen paint.
5750 
5751  if ((!cc->m_bFollow) || (cc->GetUpMode() != NORTH_UP_MODE)) {
5752  cc->UpdateShips();
5753  cc->UpdateAIS();
5754  cc->UpdateAlerts();
5755  } else {
5756  if (!bnew_view) { // There has not been a Refresh() yet.....
5757  cc->UpdateAIS();
5758  cc->UpdateAlerts();
5759  }
5760  }
5761  }
5762  }
5763  }
5764 
5765  if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown())
5766  g_pais_query_dialog_active->UpdateText();
5767 
5768  // Refresh AIS target list every 5 seconds to avoid blinking
5769  if (g_pAISTargetList && (0 == (g_tick % (5))))
5770  g_pAISTargetList->UpdateAISTargetList();
5771 
5772  // Pick up any change Toolbar status displays
5773  UpdateGPSCompassStatusBoxes();
5774  UpdateAISTool();
5775 
5776  if (console && console->IsShown()) {
5777  // console->Raise();
5778  console->RefreshConsoleData();
5779  }
5780 
5781  // This little hack fixes a problem seen with some UniChrome OpenGL drivers
5782  // We need a deferred resize to get glDrawPixels() to work right.
5783  // So we set a trigger to generate a resize after 5 seconds....
5784  // See the "UniChrome" hack elsewhere
5785  if (m_bdefer_resize) {
5786  if (0 == (g_tick % (5))) {
5787  printf("___RESIZE\n");
5788  SetSize(m_defer_size);
5789  g_pauimgr->Update();
5790  m_bdefer_resize = false;
5791  }
5792  }
5793 
5794 #ifdef __ANDROID__
5795 
5796  // Update the navobj file on a fixed schedule (5 minutes)
5797  // This will do nothing if the navobj.changes file is empty and clean
5798  if (((g_tick % g_FlushNavobjChangesTimeout) == 0) || g_FlushNavobjChanges) {
5799  if (pConfig && pConfig->IsChangesFileDirty()) {
5800  androidShowBusyIcon();
5801  wxStopWatch update_sw;
5802  pConfig->UpdateNavObj(true);
5803  wxString msg = wxString::Format(
5804  _T("OpenCPN periodic navobj update took %ld ms."), update_sw.Time());
5805  wxLogMessage(msg);
5806  qDebug() << msg.mb_str();
5807  g_FlushNavobjChanges = false;
5808  androidHideBusyIcon();
5809  }
5810  }
5811 
5812 #endif
5813 
5814  // Reset pending next AppMsgBus notification
5815 
5816  if (g_unit_test_2)
5817  FrameTimer1.Start(TIMER_GFRAME_1 * 3, wxTIMER_CONTINUOUS);
5818  else
5819  FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
5820 }
5821 
5822 double MyFrame::GetMag(double a, double lat, double lon) {
5823  double Variance = std::isnan(gVar) ? g_UserVar : gVar;
5824  auto loader = PluginLoader::getInstance();
5825  if (loader && loader->IsPlugInAvailable(_T("WMM"))) {
5826  // Request variation at a specific lat/lon
5827 
5828  // Note that the requested value is returned sometime later in the event
5829  // stream, so there may be invalid data returned on the first call to this
5830  // method. In the case of rollover windows, the value is requested
5831  // continuously, so will be correct very soon.
5832  wxDateTime now = wxDateTime::Now();
5833  SendJSON_WMM_Var_Request(lat, lon, now);
5834  if (fabs(gQueryVar) < 360.0) // Don't use WMM variance if not updated yet
5835  Variance = gQueryVar;
5836  }
5837  return toMagnetic(a, Variance);
5838 }
5839 
5840 bool MyFrame::SendJSON_WMM_Var_Request(double lat, double lon,
5841  wxDateTime date) {
5842  if (g_pi_manager) {
5843  wxJSONValue v;
5844  v[_T("Lat")] = lat;
5845  v[_T("Lon")] = lon;
5846  v[_T("Year")] = date.GetYear();
5847  v[_T("Month")] = date.GetMonth();
5848  v[_T("Day")] = date.GetDay();
5849 
5850  g_pi_manager->SendJSONMessageToAllPlugins(_T("WMM_VARIATION_REQUEST"), v);
5851  return true;
5852  } else
5853  return false;
5854 }
5855 
5856 void MyFrame::TouchAISActive(void) {
5857  // .. for each canvas...
5858  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5859  ChartCanvas *cc = g_canvasArray.Item(i);
5860  if (cc) cc->TouchAISToolActive();
5861  }
5862 }
5863 
5864 void MyFrame::UpdateAISTool(void) {
5865  if (!g_pAIS) return;
5866 
5867  // .. for each canvas...
5868  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5869  ChartCanvas *cc = g_canvasArray.Item(i);
5870  if (cc) cc->UpdateAISTBTool();
5871  }
5872 }
5873 
5874 // Cause refresh of active Tide/Current data, if displayed
5875 void MyFrame::OnFrameTCTimer(wxTimerEvent &event) {
5876  // ..For each canvas...
5877  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5878  ChartCanvas *cc = g_canvasArray.Item(i);
5879  if (cc) cc->SetbTCUpdate(true);
5880  }
5881 
5882  RefreshAllCanvas(false);
5883 }
5884 
5885 // Keep and update the Viewport rotation angle according to average COG for
5886 // COGUP mode
5887 void MyFrame::OnFrameCOGTimer(wxTimerEvent &event) {
5888  // ..For each canvas...
5889  bool b_rotate = false;
5890  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5891  ChartCanvas *cc = g_canvasArray.Item(i);
5892  if (cc) b_rotate |= (cc->GetUpMode() != NORTH_UP_MODE);
5893  }
5894 
5895  if (!b_rotate) {
5896  FrameCOGTimer.Stop();
5897  return;
5898  }
5899 
5900  DoCOGSet();
5901 
5902  // Restart the timer, max frequency is 10 hz.
5903  int period_ms = 100;
5904  //if (g_COGAvgSec > 0) period_ms = g_COGAvgSec * 1000;
5905  FrameCOGTimer.Start(period_ms, wxTIMER_CONTINUOUS);
5906 }
5907 
5908 void MyFrame::DoCOGSet(void) {
5909  // ..For each canvas...
5910  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5911  ChartCanvas *cc = g_canvasArray.Item(i);
5912  if (cc) cc->DoCanvasCOGSet();
5913  }
5914 }
5915 
5916 void RenderShadowText(wxDC *pdc, wxFont *pFont, wxString &str, int x, int y) {
5917 #ifdef DrawText
5918 #undef DrawText
5919 #define FIXIT
5920 #endif
5921 
5922  wxFont oldfont = pdc->GetFont(); // save current font
5923 
5924  pdc->SetFont(*pFont);
5925  pdc->SetTextForeground(GetGlobalColor(_T("CHGRF")));
5926  pdc->SetBackgroundMode(wxTRANSPARENT);
5927 
5928  pdc->DrawText(str, x, y + 1);
5929  pdc->DrawText(str, x, y - 1);
5930  pdc->DrawText(str, x + 1, y);
5931  pdc->DrawText(str, x - 1, y);
5932 
5933  pdc->SetTextForeground(GetGlobalColor(_T("CHBLK")));
5934 
5935  pdc->DrawText(str, x, y);
5936 
5937  pdc->SetFont(oldfont); // restore last font
5938 }
5939 
5940 // TODO How does this relate to per-canvas rotation?
5941 void MyFrame::UpdateRotationState(double rotation) {
5942  // If rotated manually, we switch to NORTHUP
5943  g_bCourseUp = false;
5944 
5945  if (fabs(rotation) > .001) {
5946  SetMenubarItemState(ID_MENU_CHART_COGUP, false);
5947  SetMenubarItemState(ID_MENU_CHART_NORTHUP, true);
5948  if (m_pMenuBar) {
5949  m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("Rotated Mode"));
5950  }
5951  } else {
5952  SetMenubarItemState(ID_MENU_CHART_COGUP, g_bCourseUp);
5953  SetMenubarItemState(ID_MENU_CHART_NORTHUP, !g_bCourseUp);
5954  if (m_pMenuBar) {
5955  m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
5956  }
5957  }
5958 
5959  UpdateGPSCompassStatusBoxes(true);
5960  DoChartUpdate();
5961 }
5962 
5963 void MyFrame::UpdateGPSCompassStatusBoxes(bool b_force_new) {
5964  // ..For each canvas...
5965  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5966  ChartCanvas *cc = g_canvasArray.Item(i);
5967  if (cc) cc->UpdateGPSCompassStatusBox(b_force_new);
5968  }
5969 }
5970 
5971 // Application memory footprint management
5972 
5973 int MyFrame::GetApplicationMemoryUse(void) {
5974  int memsize = -1;
5975 #ifdef __linux__
5976 
5977  // Use a contrived ps command to get the virtual memory size associated
5978  // with this process
5979  wxWindow *fWin = wxWindow::FindFocus();
5980 
5981  wxArrayString outputArray;
5982  wxString cmd(_T("ps --no-headers -o vsize "));
5983  unsigned long pid = wxGetProcessId();
5984  wxString cmd1;
5985  cmd1.Printf(_T("%ld"), pid);
5986  cmd += cmd1;
5987  wxExecute(cmd, outputArray);
5988 
5989  if (outputArray.GetCount()) {
5990  wxString s = outputArray.Item(0);
5991  long vtmp;
5992  if (s.ToLong(&vtmp)) memsize = vtmp;
5993  }
5994 
5995  if (fWin) fWin->SetFocus();
5996 
5997 #endif
5998 
5999 #ifdef __WXMSW__
6000  HANDLE hProcess;
6001  PROCESS_MEMORY_COUNTERS pmc;
6002 
6003  unsigned long processID = wxGetProcessId();
6004 
6005  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
6006  processID);
6007  if (NULL == hProcess) return 0;
6008 
6009  if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
6010  /*
6011  printf( "\tPageFaultCount: 0x%08X\n", pmc.PageFaultCount );
6012  printf( "\tPeakWorkingSetSize: 0x%08X\n",
6013  pmc.PeakWorkingSetSize );
6014  printf( "\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize );
6015  printf( "\tQuotaPeakPagedPoolUsage: 0x%08X\n",
6016  pmc.QuotaPeakPagedPoolUsage );
6017  printf( "\tQuotaPagedPoolUsage: 0x%08X\n",
6018  pmc.QuotaPagedPoolUsage );
6019  printf( "\tQuotaPeakNonPagedPoolUsage: 0x%08X\n",
6020  pmc.QuotaPeakNonPagedPoolUsage );
6021  printf( "\tQuotaNonPagedPoolUsage: 0x%08X\n",
6022  pmc.QuotaNonPagedPoolUsage );
6023  printf( "\tPagefileUsage: 0x%08X\n", pmc.PagefileUsage );
6024  printf( "\tPeakPagefileUsage: 0x%08X\n",
6025  pmc.PeakPagefileUsage );
6026  */
6027  memsize = pmc.WorkingSetSize / 1024;
6028  }
6029 
6030  CloseHandle(hProcess);
6031 
6032 #endif
6033 
6034  return memsize;
6035 }
6036 
6037 double MyFrame::GetBestVPScale(ChartBase *pchart) {
6038  return GetPrimaryCanvas()->GetBestVPScale(pchart);
6039 }
6040 
6041 void MyFrame::SetChartUpdatePeriod() {
6042  // Set the chart update period based upon chart skew and skew compensator
6043 
6044  g_ChartUpdatePeriod = 0; // General default
6045 
6046  // In non-GL, singlele-chart mode, rotation of skewed charts is very slow
6047  // So we need to use a slower update time constant to preserve adequate UI
6048  // performance
6049  bool bskewdc = false;
6050  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6051  ChartCanvas *cc = g_canvasArray.Item(i);
6052  if (cc) {
6053  if (!g_bopengl && !cc->GetVP().b_quilt) {
6054  if (fabs(cc->GetVP().skew) > 0.0001) bskewdc = true;
6055  }
6056  if (cc->m_bFollow) g_ChartUpdatePeriod = 1;
6057  }
6058  }
6059 
6060  if (bskewdc) g_ChartUpdatePeriod = g_SkewCompUpdatePeriod;
6061 
6062  m_ChartUpdatePeriod = g_ChartUpdatePeriod;
6063 }
6064 
6065 void MyFrame::UpdateControlBar(ChartCanvas *cc) {
6066  if (!cc) return;
6067  cc->UpdateCanvasControlBar();
6068 }
6069 
6070 void MyFrame::selectChartDisplay(int type, int family) {
6071  // ..For each canvas...
6072  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6073  ChartCanvas *cc = g_canvasArray.Item(i);
6074  if (cc) cc->selectCanvasChartDisplay(type, family);
6075  }
6076 
6077  UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks
6078  // etc.)
6079 }
6080 
6081 //----------------------------------------------------------------------------------
6082 // DoChartUpdate
6083 // Create a chartstack based on current lat/lon.
6084 // Return true if a Refresh(false) was called within.
6085 //----------------------------------------------------------------------------------
6086 bool MyFrame::DoChartUpdate(void) {
6087  bool return_val = false;
6088 
6089  // ..For each canvas...
6090  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6091  ChartCanvas *cc = g_canvasArray.Item(i);
6092  if (cc) return_val |= cc->DoCanvasUpdate();
6093  }
6094 
6095  return return_val;
6096 }
6097 
6098 void MyFrame::MouseEvent(wxMouseEvent &event) {
6099  int x, y;
6100  event.GetPosition(&x, &y);
6101 }
6102 
6103 // Memory monitor support
6104 #ifdef __WXMAC__
6105 #include <mach/mach.h>
6106 #include <mach/message.h> // for mach_msg_type_number_t
6107 #include <mach/kern_return.h> // for kern_return_t
6108 #include <mach/task_info.h>
6109 #include <stdio.h>
6110 #include <malloc/malloc.h>
6111 #endif
6112 
6113 #ifdef __WXGTK__
6114 #include <malloc.h>
6115 #endif
6116 
6117 #if defined(__linux__)
6118 #include "sys/types.h"
6119 #include "sys/sysinfo.h"
6120 #endif /* __linux__ */
6121 
6122 int g_lastMemTick = -1;
6123 
6124 /* Return total system RAM and size of program */
6125 /* Values returned are in kilobytes */
6126 bool GetMemoryStatus(int *mem_total, int *mem_used) {
6127 #ifdef __ANDROID__
6128  return androidGetMemoryStatus(mem_total, mem_used);
6129 #endif
6130 
6131 #if defined(__linux__)
6132  // Use sysinfo to obtain total RAM
6133  if (mem_total) {
6134  *mem_total = 0;
6135  struct sysinfo sys_info;
6136  if (sysinfo(&sys_info) != -1)
6137  *mem_total = ((uint64_t)sys_info.totalram * sys_info.mem_unit) / 1024;
6138  }
6139  // Use filesystem /proc/self/statm to determine memory status
6140  // Provides information about memory usage, measured in pages. The columns
6141  // are: size total program size (same as VmSize in /proc/[pid]/status)
6142  // resident resident set size (same as VmRSS in /proc/[pid]/status)
6143  // share shared pages (from shared mappings)
6144  // text text (code)
6145  // lib library (unused in Linux 2.6)
6146  // data data + stack
6147  // dt dirty pages (unused in Linux 2.6)
6148 
6149  if (mem_used) {
6150  *mem_used = 0;
6151  FILE *file = fopen("/proc/self/statm", "r");
6152  if (file) {
6153  if (fscanf(file, "%d", mem_used) != 1) {
6154  wxLogWarning("Cannot parse /proc/self/statm (!)");
6155  }
6156  *mem_used *= 4; // XXX assume 4K page
6157  fclose(file);
6158  }
6159  }
6160 
6161  return true;
6162 
6163 #endif /* __linux__ */
6164 
6165 #ifdef __WXMSW__
6166  HANDLE hProcess;
6167  PROCESS_MEMORY_COUNTERS pmc;
6168 
6169  unsigned long processID = wxGetProcessId();
6170 
6171  if (mem_used) {
6172  hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
6173  processID);
6174 
6175  if (hProcess && GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
6176  /*
6177  printf( "\tPageFaultCount: 0x%08X\n", pmc.PageFaultCount );
6178  printf( "\tPeakWorkingSetSize: 0x%08X\n",
6179  pmc.PeakWorkingSetSize );
6180  printf( "\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize );
6181  printf( "\tQuotaPeakPagedPoolUsage: 0x%08X\n",
6182  pmc.QuotaPeakPagedPoolUsage );
6183  printf( "\tQuotaPagedPoolUsage: 0x%08X\n",
6184  pmc.QuotaPagedPoolUsage );
6185  printf( "\tQuotaPeakNonPagedPoolUsage: 0x%08X\n",
6186  pmc.QuotaPeakNonPagedPoolUsage );
6187  printf( "\tQuotaNonPagedPoolUsage: 0x%08X\n",
6188  pmc.QuotaNonPagedPoolUsage );
6189  printf( "\tPagefileUsage: 0x%08X\n", pmc.PagefileUsage );
6190  printf( "\tPeakPagefileUsage: 0x%08X\n",
6191  pmc.PeakPagefileUsage );
6192  */
6193  *mem_used = pmc.WorkingSetSize / 1024;
6194  }
6195 
6196  CloseHandle(hProcess);
6197  }
6198 
6199  if (mem_total) {
6200  MEMORYSTATUSEX statex;
6201 
6202  statex.dwLength = sizeof(statex);
6203 
6204  GlobalMemoryStatusEx(&statex);
6205  /*
6206  _tprintf (TEXT("There is %*ld percent of memory in use.\n"),
6207  WIDTH, statex.dwMemoryLoad);
6208  _tprintf (TEXT("There are %*I64d total Kbytes of physical memory.\n"),
6209  WIDTH, statex.ullTotalPhys/DIV);
6210  _tprintf (TEXT("There are %*I64d free Kbytes of physical memory.\n"),
6211  WIDTH, statex.ullAvailPhys/DIV);
6212  _tprintf (TEXT("There are %*I64d total Kbytes of paging file.\n"),
6213  WIDTH, statex.ullTotalPageFile/DIV);
6214  _tprintf (TEXT("There are %*I64d free Kbytes of paging file.\n"),
6215  WIDTH, statex.ullAvailPageFile/DIV);
6216  _tprintf (TEXT("There are %*I64d total Kbytes of virtual memory.\n"),
6217  WIDTH, statex.ullTotalVirtual/DIV);
6218  _tprintf (TEXT("There are %*I64d free Kbytes of virtual memory.\n"),
6219  WIDTH, statex.ullAvailVirtual/DIV);
6220  */
6221 
6222  *mem_total = statex.ullTotalPhys / 1024;
6223  }
6224  return true;
6225 #endif
6226 
6227 #ifdef __WXMAC__
6228 
6229  if (g_tick != g_lastMemTick) {
6230  malloc_zone_pressure_relief(NULL, 0);
6231 
6232  int bytesInUse = 0;
6233  int blocksInUse = 0;
6234  int sizeAllocated = 0;
6235 
6236  malloc_statistics_t stats;
6237  stats.blocks_in_use = 0;
6238  stats.size_in_use = 0;
6239  stats.max_size_in_use = 0;
6240  stats.size_allocated = 0;
6241  malloc_zone_statistics(NULL, &stats);
6242  bytesInUse += stats.size_in_use;
6243  blocksInUse += stats.blocks_in_use;
6244  sizeAllocated += stats.size_allocated;
6245 
6246  g_memUsed = sizeAllocated >> 10;
6247 
6248  // printf("mem_used (Mb): %d %d \n", g_tick, g_memUsed / 1024);
6249  g_lastMemTick = g_tick;
6250  }
6251 
6252  if (mem_used) *mem_used = g_memUsed;
6253  if (mem_total) {
6254  *mem_total = 4000;
6255  FILE *fpIn = popen("sysctl -n hw.memsize", "r");
6256  if (fpIn) {
6257  double pagesUsed = 0.0, totalPages = 0.0;
6258  char buf[64];
6259  if (fgets(buf, sizeof(buf), fpIn) != NULL) {
6260  *mem_total = atol(buf) >> 10;
6261  }
6262  }
6263  }
6264 
6265  return true;
6266 #endif
6267 
6268  if (mem_used) *mem_used = 0;
6269  if (mem_total) *mem_total = 0;
6270  return false;
6271 }
6272 
6273 void MyFrame::DoPrint(void) {
6274  //avoid toolbars being printed
6275  g_PrintingInProgress = true;
6276 #ifdef ocpnUSE_GL
6277  if(g_bopengl){
6278  GetPrimaryCanvas()->GetglCanvas()->Render();
6279  GetPrimaryCanvas()->GetglCanvas()->SwapBuffers();
6280  }else
6281 #endif
6282  Refresh();
6283 
6284  if (NULL == g_printData) {
6285  g_printData = new wxPrintData;
6286  g_printData->SetOrientation(wxLANDSCAPE);
6287  g_pageSetupData = new wxPageSetupDialogData;
6288  }
6289 
6290  wxPrintDialogData printDialogData(*g_printData);
6291  printDialogData.EnablePageNumbers(false);
6292 
6293  wxPrinter printer(&printDialogData);
6294 
6295  MyPrintout printout(wxT("Chart Print"));
6296 
6297  // In OperGL mode, make the bitmap capture of the screen before the print
6298  // method starts as to be sure the "Abort..." dialog does not appear on
6299  // the image
6300  if (g_bopengl) printout.GenerateGLbmp();
6301 
6302  if (!printer.Print(this, &printout, true)) {
6303  if (wxPrinter::GetLastError() == wxPRINTER_ERROR)
6304  OCPNMessageBox(NULL,
6305  _("There was a problem printing.\nPerhaps your current "
6306  "printer is not set correctly?"),
6307  _T("OpenCPN"), wxOK);
6308  // else
6309  // OCPNMessageBox(_T("Print Cancelled"), _T("OpenCPN"), wxOK);
6310  } else {
6311  (*g_printData) = printer.GetPrintDialogData().GetPrintData();
6312  }
6313 
6314  // Pass two printout objects: for preview, and possible printing.
6315  /*
6316  wxPrintDialogData printDialogData(* g_printData);
6317  wxPrintPreview *preview = new wxPrintPreview(new MyPrintout, new MyPrintout,
6318  & printDialogData); if (!preview->Ok())
6319  {
6320  delete preview;
6321  OCPNMessageBox(_T("There was a problem previewing.\nPerhaps your current
6322  printer is not set correctly?"), _T("Previewing"), wxOK); return;
6323  }
6324 
6325  wxPreviewFrame *frame = new wxPreviewFrame(preview, this, _T("Demo Print
6326  Preview"), wxPoint(100, 100), wxSize(600, 650)); frame->Centre(wxBOTH);
6327  frame->Initialize();
6328  frame->Show();
6329  */
6330  g_PrintingInProgress = false;
6331  Refresh();
6332 #ifdef __WXGTK__
6333  GetPrimaryCanvas()->SetFocus();
6334  Raise(); // I dunno why...
6335 #endif
6336 }
6337 
6338 wxDateTime gTimeSource;
6339 
6340 void MyFrame::OnEvtPlugInMessage(OCPN_MsgEvent &event) {
6341  wxString message_ID = event.GetID();
6342  wxString message_JSONText = event.GetJSONText();
6343 
6344  // We are free to use or ignore any or all of the PlugIn messages flying
6345  // through this pipe tee.
6346 
6347  // We can possibly use the estimated magnetic variation if WMM_pi is
6348  // present, active, and we have no other source of Variation
6349  if (!g_bVAR_Rx) {
6350  if (message_ID == _T("WMM_VARIATION_BOAT")) {
6351  // construct the JSON root object
6352  wxJSONValue root;
6353  // construct a JSON parser
6354  wxJSONReader reader;
6355 
6356  // now read the JSON text and store it in the 'root' structure
6357  // check for errors before retreiving values...
6358  int numErrors = reader.Parse(message_JSONText, &root);
6359  if (numErrors > 0) {
6360  // const wxArrayString& errors = reader.GetErrors();
6361  return;
6362  }
6363 
6364  // get the DECL value from the JSON message
6365  wxString decl = root[_T("Decl")].AsString();
6366  double decl_val;
6367  decl.ToDouble(&decl_val);
6368 
6369  gVar = decl_val;
6370  }
6371  }
6372 
6373  if (message_ID == _T("WMM_VARIATION")) {
6374  // construct the JSON root object
6375  wxJSONValue root;
6376  // construct a JSON parser
6377  wxJSONReader reader;
6378 
6379  // now read the JSON text and store it in the 'root' structure
6380  // check for errors before retreiving values...
6381  int numErrors = reader.Parse(message_JSONText, &root);
6382  if (numErrors > 0) {
6383  // const wxArrayString& errors = reader.GetErrors();
6384  return;
6385  }
6386 
6387  // get the DECL value from the JSON message
6388  wxString decl = root[_T("Decl")].AsString();
6389  double decl_val;
6390  decl.ToDouble(&decl_val);
6391 
6392  gQueryVar = decl_val;
6393  }
6394 
6395  if (message_ID == _T("GRIB_TIMELINE")) {
6396  wxJSONReader r;
6397  wxJSONValue v;
6398  r.Parse(message_JSONText, &v);
6399  if (v[_T("Day")].AsInt() == -1)
6400  gTimeSource = wxInvalidDateTime;
6401  else
6402  gTimeSource.Set(v[_T("Day")].AsInt(),
6403  (wxDateTime::Month)v[_T("Month")].AsInt(),
6404  v[_T("Year")].AsInt(), v[_T("Hour")].AsInt(),
6405  v[_T("Minute")].AsInt(), v[_T("Second")].AsInt());
6406  }
6407  if (message_ID == _T("OCPN_TRACK_REQUEST")) {
6408  wxJSONValue root;
6409  wxJSONReader reader;
6410  wxString trk_id = wxEmptyString;
6411 
6412  int numErrors = reader.Parse(message_JSONText, &root);
6413  if (numErrors > 0) return;
6414 
6415  if (root.HasMember(_T("Track_ID")))
6416  trk_id = root[_T("Track_ID")].AsString();
6417 
6418  wxJSONValue v;
6419  v[_T("Track_ID")] = trk_id;
6420  for (Track *ptrack : g_TrackList) {
6421  wxString name = wxEmptyString;
6422  if (ptrack->m_GUID == trk_id) {
6423  name = ptrack->GetName();
6424  if (name.IsEmpty()) {
6425  TrackPoint *rp = ptrack->GetPoint(0);
6426  if (rp && rp->GetCreateTime().IsValid())
6427  name = rp->GetCreateTime().FormatISODate() + _T(" ") +
6428  rp->GetCreateTime().FormatISOTime();
6429  else
6430  name = _("(Unnamed Track)");
6431  }
6432 
6433  /* To avoid memory problems send a single trackpoint.
6434  * It's up to the plugin to collect the data. */
6435  int i = 1;
6436  v[_T("error")] = false;
6437  v[_T("TotalNodes")] = ptrack->GetnPoints();
6438  for (int j = 0; j < ptrack->GetnPoints(); j++) {
6439  TrackPoint *tp = ptrack->GetPoint(j);
6440  v[_T("lat")] = tp->m_lat;
6441  v[_T("lon")] = tp->m_lon;
6442  v[_T("NodeNr")] = i;
6443  i++;
6444  wxString msg_id(_T("OCPN_TRACKPOINTS_COORDS"));
6445  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6446  }
6447  return;
6448  }
6449  v[_T("error")] = true;
6450 
6451  wxString msg_id(_T("OCPN_TRACKPOINTS_COORDS"));
6452  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6453  }
6454  } else if (message_ID == _T("OCPN_ROUTE_REQUEST")) {
6455  wxJSONValue root;
6456  wxJSONReader reader;
6457  wxString guid = wxEmptyString;
6458 
6459  int numErrors = reader.Parse(message_JSONText, &root);
6460  if (numErrors > 0) {
6461  return;
6462  }
6463 
6464  if (root.HasMember(_T("GUID"))) guid = root[_T("GUID")].AsString();
6465 
6466  wxJSONValue v;
6467  v[_T("GUID")] = guid;
6468  for (RouteList::iterator it = pRouteList->begin(); it != pRouteList->end();
6469  it++) {
6470  wxString name = wxEmptyString;
6471 
6472  if ((*it)->m_GUID == guid) {
6473  name = (*it)->m_RouteNameString;
6474  if (name.IsEmpty()) name = _("(Unnamed Route)");
6475 
6476  v[_T("Name")] = name;
6477  v[_T("error")] = false;
6478  wxJSONValue w;
6479  int i = 0;
6480  for (RoutePointList::iterator itp = (*it)->pRoutePointList->begin();
6481  itp != (*it)->pRoutePointList->end(); itp++) {
6482  w[i][_T("lat")] = (*itp)->m_lat;
6483  w[i][_T("lon")] = (*itp)->m_lon;
6484  w[i][_T("Name")] = (*itp)->GetName();
6485  w[i][_T("Description")] = (*itp)->GetDescription();
6486  w[i][_T("GUID")] = (*itp)->m_GUID;
6487  w[i][_T("ArrivalRadius")] = (*itp)->GetWaypointArrivalRadius();
6488  wxHyperlinkListNode *node = (*itp)->m_HyperlinkList->GetFirst();
6489  if (node) {
6490  int n = 1;
6491  while (node) {
6492  Hyperlink *httpLink = node->GetData();
6493  v[i][_T("WPLink") + wxString::Format(_T("%d"), n)] =
6494  httpLink->Link;
6495  v[i][_T("WPLinkDesciption") + wxString::Format(_T("%d"), n++)] =
6496  httpLink->DescrText;
6497  node = node->GetNext();
6498  }
6499  }
6500  i++;
6501  }
6502  v[_T("waypoints")] = w;
6503  wxString msg_id(_T("OCPN_ROUTE_RESPONSE"));
6504  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6505  return;
6506  }
6507  }
6508 
6509  v[_T("error")] = true;
6510 
6511  wxString msg_id(_T("OCPN_ROUTE_RESPONSE"));
6512  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6513  } else if (message_ID == _T("OCPN_ROUTELIST_REQUEST")) {
6514  wxJSONValue root;
6515  wxJSONReader reader;
6516  bool route = true;
6517 
6518  int numErrors = reader.Parse(message_JSONText, &root);
6519  if (numErrors > 0) return;
6520 
6521  if (root.HasMember(_T("mode"))) {
6522  wxString str = root[_T("mode")].AsString();
6523  if (str == _T("Track")) route = false;
6524 
6525  wxJSONValue v;
6526  int i = 1;
6527  if (route) {
6528  for (RouteList::iterator it = pRouteList->begin();
6529  it != pRouteList->end(); it++) {
6530  wxString name = (*it)->m_RouteNameString;
6531  if (name.IsEmpty()) name = _("(Unnamed Route)");
6532 
6533  v[i][_T("error")] = false;
6534  v[i][_T("name")] = name;
6535  v[i][_T("GUID")] = (*it)->m_GUID;
6536  v[i][_T("active")] = (*it)->IsActive();
6537  i++;
6538  }
6539  } else { // track
6540  for (Track *ptrack : g_TrackList) {
6541  wxString name = ptrack->GetName();
6542  if (name.IsEmpty()) {
6543  TrackPoint *tp = ptrack->GetPoint(0);
6544  if (tp && tp->GetCreateTime().IsValid())
6545  name = tp->GetCreateTime().FormatISODate() + _T(" ") +
6546  tp->GetCreateTime().FormatISOTime();
6547  else
6548  name = _("(Unnamed Track)");
6549  }
6550  v[i][_T("error")] = false;
6551  v[i][_T("name")] = name;
6552  v[i][_T("GUID")] = ptrack->m_GUID;
6553  v[i][_T("active")] = g_pActiveTrack == ptrack;
6554  i++;
6555  }
6556  }
6557  wxString msg_id(_T("OCPN_ROUTELIST_RESPONSE"));
6558  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6559  } else {
6560  wxJSONValue v;
6561  v[0][_T("error")] = true;
6562  wxString msg_id(_T("OCPN_ROUTELIST_RESPONSE"));
6563  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6564  }
6565  } else if (message_ID == _T("OCPN_ACTIVE_ROUTELEG_REQUEST")) {
6566  wxJSONValue v;
6567  v[0][_T("error")] = true;
6568  if (g_pRouteMan->GetpActiveRoute()) {
6569  if (g_pRouteMan->m_bDataValid) {
6570  v[0][_T("error")] = false;
6571  v[0][_T("range")] = g_pRouteMan->GetCurrentRngToActivePoint();
6572  v[0][_T("bearing")] = g_pRouteMan->GetCurrentBrgToActivePoint();
6573  v[0][_T("XTE")] = g_pRouteMan->GetCurrentXTEToActivePoint();
6574  v[0][_T("active_route_GUID")] =
6575  g_pRouteMan->GetpActiveRoute()->GetGUID();
6576  v[0][_T("active_waypoint_lat")] =
6577  g_pRouteMan->GetpActiveRoute()->m_pRouteActivePoint->GetLatitude();
6578  v[0][_T("active_waypoint_lon")] =
6579  g_pRouteMan->GetpActiveRoute()->m_pRouteActivePoint->GetLongitude();
6580  }
6581  }
6582  wxString msg_id(_T("OCPN_ACTIVE_ROUTELEG_RESPONSE"));
6583  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6584  }
6585 }
6586 
6587 void MyFrame::FilterCogSog(void) {
6588  if (g_bfilter_cogsog && !g_own_ship_sog_cog_calc) {
6589  // Simple averaging filter for COG
6590  double cog_last = gCog; // most recent reported value
6591 
6592  // Make a hole in array
6593  for (int i = g_COGFilterSec - 1; i > 0; i--)
6594  COGFilterTable[i] = COGFilterTable[i - 1];
6595  COGFilterTable[0] = cog_last;
6596 
6597  // If the lastest data is undefined, leave it
6598  if (!std::isnan(cog_last)) {
6599  //
6600  double sum = 0., count = 0;
6601  for (int i = 0; i < g_COGFilterSec; i++) {
6602  double adder = COGFilterTable[i];
6603  if (std::isnan(adder)) continue;
6604 
6605  if (fabs(adder - cog_last) > 180.) {
6606  if ((adder - cog_last) > 0.)
6607  adder -= 360.;
6608  else
6609  adder += 360.;
6610  }
6611 
6612  sum += adder;
6613  count++;
6614  }
6615  sum /= count;
6616 
6617  if (sum < 0.)
6618  sum += 360.;
6619  else if (sum >= 360.)
6620  sum -= 360.;
6621 
6622  gCog = sum;
6623  }
6624 
6625  // Simple averaging filter for SOG
6626  double sog_last = gSog; // most recent reported value
6627 
6628  // Make a hole in array
6629  for (int i = g_SOGFilterSec - 1; i > 0; i--)
6630  SOGFilterTable[i] = SOGFilterTable[i - 1];
6631  SOGFilterTable[0] = sog_last;
6632 
6633  // If the data are undefined, leave the array intact
6634  if (!std::isnan(gSog)) {
6635  double sum = 0., count = 0;
6636  for (int i = 0; i < g_SOGFilterSec; i++) {
6637  if (std::isnan(SOGFilterTable[i])) continue;
6638 
6639  sum += SOGFilterTable[i];
6640  count++;
6641  }
6642  sum /= count;
6643 
6644  gSog = sum;
6645  }
6646  }
6647 }
6648 
6649 void MyFrame::LoadHarmonics() {
6650  if (!ptcmgr) {
6651  ptcmgr = new TCMgr;
6652  ptcmgr->LoadDataSources(TideCurrentDataSet);
6653  } else {
6654  bool b_newdataset = false;
6655 
6656  // Test both ways
6657  for (auto a : ptcmgr->GetDataSet()) {
6658  bool b_foundi = false;
6659  for (auto b : TideCurrentDataSet) {
6660  if (a == b) {
6661  b_foundi = true;
6662  break; // j loop
6663  }
6664  }
6665  if (!b_foundi) {
6666  b_newdataset = true;
6667  break; // i loop
6668  }
6669  }
6670 
6671  for (auto a : TideCurrentDataSet) {
6672  bool b_foundi = false;
6673  for (auto b : ptcmgr->GetDataSet()) {
6674  if (a == b) {
6675  b_foundi = true;
6676  break; // j loop
6677  }
6678  }
6679  if (!b_foundi) {
6680  b_newdataset = true;
6681  break; // i loop
6682  }
6683  }
6684 
6685  if (b_newdataset) ptcmgr->LoadDataSources(TideCurrentDataSet);
6686  }
6687 }
6688 
6689 void MyFrame::ActivateAISMOBRoute(const AisTargetData *ptarget) {
6690  if (!ptarget) return;
6691 
6692  // The MOB point
6693  wxDateTime mob_time = wxDateTime::Now();
6694  wxString mob_label(_("AIS MAN OVERBOARD"));
6695  mob_label += _(" at ");
6696  mob_label += mob_time.FormatTime();
6697  mob_label += _(" on ");
6698  mob_label += mob_time.FormatISODate();
6699 
6700  RoutePoint *pWP_MOB = new RoutePoint(ptarget->Lat, ptarget->Lon, _T ( "mob" ),
6701  mob_label, wxEmptyString);
6702  pWP_MOB->SetShared(true);
6703  pWP_MOB->m_bIsolatedMark = true;
6704  pSelect->AddSelectableRoutePoint(ptarget->Lat, ptarget->Lon, pWP_MOB);
6705  pConfig->AddNewWayPoint(pWP_MOB, -1); // use auto next num
6706  pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
6707 
6708  /* We want to start tracking any MOB in range (Which will trigger false alarms
6709  with messages received over the network etc., but will a) not discard nearby
6710  event even in case our GPS is momentarily unavailable and b) work even when
6711  the boat is stationary, in which case some GPS units do not provide COG) if(
6712  bGPSValid && !std::isnan(gCog) && !std::isnan(gSog) ) { */
6713  RoutePoint *pWP_src = new RoutePoint(gLat, gLon, g_default_wp_icon,
6714  wxString(_("Own ship")), wxEmptyString);
6715  pSelect->AddSelectableRoutePoint(gLat, gLon, pWP_src);
6716  pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
6717  pAISMOBRoute = new Route();
6718  pRouteList->Append(pAISMOBRoute);
6719 
6720  pAISMOBRoute->AddPoint(pWP_src);
6721  pAISMOBRoute->AddPoint(pWP_MOB);
6722 
6723  pSelect->AddSelectableRouteSegment(ptarget->Lat, ptarget->Lon, gLat, gLon,
6724  pWP_src, pWP_MOB, pAISMOBRoute);
6725 
6726  pAISMOBRoute->m_RouteNameString = _("Temporary AISMOB Route");
6727  pAISMOBRoute->m_RouteStartString = _("Present own ship");
6728  pAISMOBRoute->m_RouteEndString = mob_label;
6729 
6730  pAISMOBRoute->m_bDeleteOnArrival = false;
6731 
6732  pAISMOBRoute->SetRouteArrivalRadius(-1.0); // never arrives
6733 
6734  if (g_pRouteMan->GetpActiveRoute()) g_pRouteMan->DeactivateRoute();
6735  // g_pRouteMan->ActivateRoute( pAISMOBRoute, pWP_MOB );
6736 
6737  wxJSONValue v;
6738  v[_T("GUID")] = pAISMOBRoute->m_GUID;
6739  wxString msg_id(_T("OCPN_MAN_OVERBOARD"));
6740  g_pi_manager->SendJSONMessageToAllPlugins(msg_id, v);
6741  //}
6742 
6743  if (RouteManagerDialog::getInstanceFlag()) {
6744  if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
6745  pRouteManagerDialog->UpdateRouteListCtrl();
6746  pRouteManagerDialog->UpdateWptListCtrl();
6747  }
6748  }
6749 
6750  RefreshAllCanvas(false);
6751 
6752  wxString mob_message(_("AIS MAN OVERBOARD"));
6753  mob_message += _(" Time: ");
6754  mob_message += mob_time.Format();
6755  mob_message += _(" Ownship Position: ");
6756  mob_message += toSDMM(1, gLat);
6757  mob_message += _T(" ");
6758  mob_message += toSDMM(2, gLon);
6759  mob_message += _(" MOB Position: ");
6760  mob_message += toSDMM(1, ptarget->Lat);
6761  mob_message += _T(" ");
6762  mob_message += toSDMM(2, ptarget->Lon);
6763  wxLogMessage(mob_message);
6764 }
6765 
6766 void MyFrame::UpdateAISMOBRoute(const AisTargetData *ptarget) {
6767  if (pAISMOBRoute && ptarget) {
6768  // Update Current Ownship point
6769  RoutePoint *OwnPoint = pAISMOBRoute->GetPoint(1);
6770  OwnPoint->m_lat = gLat;
6771  OwnPoint->m_lon = gLon;
6772 
6773  pSelect->DeleteSelectableRoutePoint(OwnPoint);
6774  pSelect->AddSelectableRoutePoint(gLat, gLon, OwnPoint);
6775 
6776  // Update Current MOB point
6777  RoutePoint *MOB_Point = pAISMOBRoute->GetPoint(2);
6778  MOB_Point->m_lat = ptarget->Lat;
6779  MOB_Point->m_lon = ptarget->Lon;
6780 
6781  pSelect->DeleteSelectableRoutePoint(MOB_Point);
6782  pSelect->AddSelectableRoutePoint(ptarget->Lat, ptarget->Lon, MOB_Point);
6783 
6784  pSelect->UpdateSelectableRouteSegments(OwnPoint);
6785  pSelect->UpdateSelectableRouteSegments(MOB_Point);
6786  }
6787 
6788  RefreshAllCanvas(false);
6789 
6790  if (ptarget) {
6791  wxDateTime mob_time = wxDateTime::Now();
6792 
6793  wxString mob_message(_("AIS MAN OVERBOARD UPDATE"));
6794  mob_message += _(" Time: ");
6795  mob_message += mob_time.Format();
6796  mob_message += _(" Ownship Position: ");
6797  mob_message += toSDMM(1, gLat);
6798  mob_message += _T(" ");
6799  mob_message += toSDMM(2, gLon);
6800  mob_message += _(" MOB Position: ");
6801  mob_message += toSDMM(1, ptarget->Lat);
6802  mob_message += _T(" ");
6803  mob_message += toSDMM(2, ptarget->Lon);
6804 
6805  wxLogMessage(mob_message);
6806  }
6807 }
6808 
6809 void MyFrame::applySettingsString(wxString settings) {
6810  // Save some present values
6811  int last_UIScaleFactor = g_GUIScaleFactor;
6812  bool previous_expert = g_bUIexpert;
6813  g_last_ChartScaleFactor = g_ChartScaleFactor;
6814  ArrayOfCDI *pNewDirArray = new ArrayOfCDI;
6815 
6816  int rr =
6817  g_Platform->platformApplyPrivateSettingsString(settings, pNewDirArray);
6818 
6819  // And apply the changes
6820  pConfig->UpdateSettings();
6821 
6822  // Might need to rebuild symbols
6823  if (g_last_ChartScaleFactor != g_ChartScaleFactor) rr |= S52_CHANGED;
6824 
6825  if (rr & S52_CHANGED) {
6826  if (ps52plib) {
6827  ps52plib->FlushSymbolCaches(ChartCtxFactory());
6828  ps52plib
6829  ->ClearCNSYLUPArray(); // some CNSY depends on renderer (e.g. CARC)
6830  ps52plib->GenerateStateHash();
6831  }
6832  }
6833 
6834  ProcessOptionsDialog(rr, pNewDirArray);
6835 
6836  // Try to detect if the toolbar is changing, to avoid a rebuild if not
6837  // necessary.
6838 
6839  bool b_newToolbar = false;
6840 
6841  if (g_GUIScaleFactor != last_UIScaleFactor) b_newToolbar = true;
6842 
6843  if (previous_expert != g_bUIexpert) b_newToolbar = true;
6844 
6845  if (rr & TOOLBAR_CHANGED) b_newToolbar = true;
6846 
6847 
6848  // We do this is one case only to remove an orphan recovery window
6849 #ifdef __ANDROID__
6850  if (previous_expert && !g_bUIexpert) {
6851  androidForceFullRepaint();
6852  }
6853 #endif
6854 
6855  if (previous_expert != g_bUIexpert) g_Platform->applyExpertMode(g_bUIexpert);
6856 
6857  // We set the compass size first, since that establishes the available space
6858  // for the toolbar.
6859  SetGPSCompassScale();
6860  // ..For each canvas...
6861  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6862  ChartCanvas *cc = g_canvasArray.Item(i);
6863  if (cc) cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
6864  }
6865  UpdateGPSCompassStatusBoxes(true);
6866 
6867  if (b_newToolbar) {
6868  AbstractPlatform::ShowBusySpinner();
6869 
6870  SetAllToolbarScale();
6871  RequestNewToolbars(
6872  true); // Force rebuild, to pick up bGUIexpert and scale settings.
6873 
6874  AbstractPlatform::HideBusySpinner();
6875 
6876  RequestNewMasterToolbar(true);
6877  }
6878 
6879  gFrame->Raise();
6880 
6881  InvalidateAllGL();
6882  DoChartUpdate();
6883  UpdateControlBar(GetPrimaryCanvas());
6884  Refresh();
6885 
6886  if (console) console->Raise();
6887 
6888  Refresh(false);
6889 
6890  if (NMEALogWindow::GetInstance().Active())
6891  NMEALogWindow::GetInstance().GetTTYWindow()->Raise();
6892 }
6893 
6894 #ifdef wxHAS_POWER_EVENTS
6895 void MyFrame::OnSuspending(wxPowerEvent &event) {
6896  // wxDateTime now = wxDateTime::Now();
6897  // printf("OnSuspending...%d\n", now.GetTicks());
6898 
6899  wxLogMessage(_T("System suspend starting..."));
6900 }
6901 
6902 void MyFrame::OnSuspended(wxPowerEvent &WXUNUSED(event)) {
6903  // wxDateTime now = wxDateTime::Now();
6904  // printf("OnSuspended...%d\n", now.GetTicks());
6905  wxLogMessage(_T("System is going to suspend."));
6906 }
6907 
6908 void MyFrame::OnSuspendCancel(wxPowerEvent &WXUNUSED(event)) {
6909  // wxDateTime now = wxDateTime::Now();
6910  // printf("OnSuspendCancel...%d\n", now.GetTicks());
6911  wxLogMessage(_T("System suspend was cancelled."));
6912 }
6913 
6914 int g_last_resume_ticks;
6915 void MyFrame::OnResume(wxPowerEvent &WXUNUSED(event)) {
6916  wxDateTime now = wxDateTime::Now();
6917  wxLogMessage(_T("System resumed from suspend."));
6918 
6919 
6920  if ((now.GetTicks() - g_last_resume_ticks) > 5) {
6921  SystemEvents::GetInstance().evt_resume.Notify();
6922 
6923  wxLogMessage("Restarting streams.");
6924  g_last_resume_ticks = now.GetTicks();
6925 //FIXME (dave)
6926 #if 0
6927  if (g_pMUX) {
6928  g_pMUX->ClearStreams();
6929 
6930  g_pMUX->StartAllStreams();
6931  }
6932 #endif
6933  }
6934 
6935  // If OpenGL is enabled, Windows Resume does not properly refresh the
6936  // application GL context. We need to force a Resize event that actually does
6937  // something.
6938  if (g_bopengl) {
6939  if (IsMaximized()) { // This is not real pretty on-screen, but works
6940  Maximize(false);
6941  wxYield();
6942  Maximize(true);
6943  } else {
6944  wxSize sz = GetSize();
6945  SetSize(wxSize(sz.x - 1, sz.y));
6946  wxYield();
6947  SetSize(sz);
6948  }
6949  }
6950 }
6951 #endif // wxHAS_POWER_EVENTS
6952 
6953 //----------------------------------------------------------------------------------------------------------
6954 // Master Toolbar support
6955 //----------------------------------------------------------------------------------------------------------
6956 
6957 void MyFrame::RequestNewMasterToolbar(bool bforcenew) {
6958  bool btbRebuild = false;
6959 
6960  bool b_reshow = true;
6961  if (g_MainToolbar) {
6962  b_reshow = true; //g_MainToolbar->IsShown();
6963  float ff = fabs(g_MainToolbar->GetScaleFactor() - g_toolbar_scalefactor);
6964  if ((ff > 0.01f) || bforcenew) {
6965  g_MainToolbar->DestroyToolBar();
6966  delete g_MainToolbar;
6967  g_MainToolbar = NULL;
6968  }
6969 
6970  btbRebuild = true;
6971  }
6972 
6973  if (!g_MainToolbar) {
6974  long orient = g_Platform->GetDefaultToolbarOrientation();
6975  wxWindow *toolbarParent = this;
6976 #ifdef __WXOSX__
6977  toolbarParent = GetPrimaryCanvas();
6978 #endif
6979  g_MainToolbar = new ocpnFloatingToolbarDialog(toolbarParent, wxPoint(-1, -1), orient,
6980  g_toolbar_scalefactor);
6981  g_MainToolbar->SetBackGroundColorString(_T("GREY3"));
6982  g_MainToolbar->SetToolbarHideMethod(TOOLBAR_HIDE_TO_FIRST_TOOL);
6983  g_MainToolbar->SetToolConfigString(g_toolbarConfig);
6984  g_MainToolbar->EnableRolloverBitmaps(false);
6985 
6986  g_MainToolbar->CreateConfigMenu();
6987  g_MainToolbar->SetDefaultPosition();
6988 
6989  g_bmasterToolbarFull = true;
6990  }
6991 
6992  if (g_MainToolbar) {
6993  CreateMasterToolbar();
6994  {
6995  //g_MainToolbar->RestoreRelativePosition(g_maintoolbar_x, g_maintoolbar_y);
6996  g_MainToolbar->SetColorScheme(global_color_scheme);
6997  //g_MainToolbar->Show(b_reshow && g_bshowToolbar);
6998  }
6999  }
7000 
7001  if (btbRebuild) {
7002  g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
7003  g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
7004  }
7005 }
7006 
7007 bool MyFrame::CollapseGlobalToolbar() {
7008  if (g_MainToolbar) {
7009  m_nMasterToolCountShown = 1;
7010  g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
7011  g_MainToolbar->GetToolbar()->InvalidateBitmaps();
7012  g_MainToolbar->Realize();
7013  g_bmasterToolbarFull = false;
7014  return true;
7015  } else
7016  return false;
7017 }
7018 
7019 ocpnToolBarSimple *MyFrame::CreateMasterToolbar() {
7020  ocpnToolBarSimple *tb = NULL;
7021 
7022  if (g_MainToolbar) tb = g_MainToolbar->GetToolbar();
7023 
7024  if (!tb) return 0;
7025 
7026  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
7027 
7029  ID_MASTERTOGGLE, style->GetToolIcon(_T("MUI_menu"), TOOLICON_NORMAL),
7030  wxITEM_NORMAL, _("Hide Toolbar"), _T("MUI_menu"));
7031  tic->m_bRequired = true;
7032 
7033  g_MainToolbar->AddToolItem(tic);
7034 
7035  tic = new ToolbarItemContainer(
7036  ID_SETTINGS, style->GetToolIcon(_T("MUI_settings"), TOOLICON_NORMAL),
7037  wxITEM_NORMAL, _("Options"), _T("MUI_settings"));
7038  g_MainToolbar->AddToolItem(tic);
7039 
7040  tic = new ToolbarItemContainer(
7041  ID_MENU_ROUTE_NEW, style->GetToolIcon(_T("MUI_route"), TOOLICON_NORMAL),
7042  style->GetToolIcon(_T("MUI_route"), TOOLICON_TOGGLED), wxITEM_CHECK,
7043  wxString(_("Create Route")) << _T(" (Ctrl-R)"), _T("MUI_route"));
7044 
7045  g_MainToolbar->AddToolItem(tic);
7046 
7047  tic = new ToolbarItemContainer(
7048  ID_ROUTEMANAGER, style->GetToolIcon(_T("MUI_RMD"), TOOLICON_NORMAL),
7049  wxITEM_NORMAL, _("Route & Mark Manager"), _T("MUI_RMD"));
7050  g_MainToolbar->AddToolItem(tic);
7051 
7052  tic = new ToolbarItemContainer(
7053  ID_TRACK, style->GetToolIcon(_T("MUI_track"), TOOLICON_NORMAL),
7054  style->GetToolIcon(_T("MUI_track"), TOOLICON_TOGGLED), wxITEM_CHECK,
7055  _("Enable Tracking"), _T("MUI_track"));
7056  g_MainToolbar->AddToolItem(tic);
7057 
7058  tic = new ToolbarItemContainer(
7059  ID_COLSCHEME, style->GetToolIcon(_T("MUI_colorscheme"), TOOLICON_NORMAL),
7060  wxITEM_NORMAL, _("Change Color Scheme"), _T("MUI_colorscheme"));
7061  g_MainToolbar->AddToolItem(tic);
7062  // if( GetMasterToolItemShow(ID_COLSCHEME) ){
7063  // tb->AddTool( ID_COLSCHEME, _T("MUI_colorscheme"), style->GetToolIcon(
7064  // _T("MUI_colorscheme"), TOOLICON_NORMAL ),
7065  // tipString, wxITEM_NORMAL );
7066  // tb->SetToolTooltipHiViz( ID_COLSCHEME, true ); // cause the Tooltip to
7067  // always be visible, whatever
7068  // the colorscheme
7069  //}
7070 
7071  tic = new ToolbarItemContainer(
7072  ID_PRINT, style->GetToolIcon(_T("MUI_print"), TOOLICON_NORMAL),
7073  wxITEM_NORMAL, _("Print Chart"), _T("MUI_print"));
7074  g_MainToolbar->AddToolItem(tic);
7075 
7076  tic = new ToolbarItemContainer(
7077  ID_ABOUT, style->GetToolIcon(_T("MUI_help"), TOOLICON_NORMAL),
7078  wxITEM_NORMAL, _("About OpenCPN"), _T("MUI_help"));
7079  g_MainToolbar->AddToolItem(tic);
7080 
7081  // Add any PlugIn toolbar tools that request default positioning
7082  AddDefaultPositionPlugInTools();
7083 
7084  // And finally add the MOB tool
7085  tic = new ToolbarItemContainer(
7086  ID_MOB, style->GetToolIcon(_T("mob_btn"), TOOLICON_NORMAL), wxITEM_NORMAL,
7087  wxString(_("Drop MOB Marker")) << _(" (Ctrl-Space)"), _T("mob_btn"));
7088  g_MainToolbar->AddToolItem(tic);
7089 
7090  // Build the toolbar
7091  g_MainToolbar->RebuildToolbar();
7092 
7093  // Realize() the toolbar for current geometry
7094  style->Unload();
7095  g_MainToolbar->Realize();
7096 
7097  // Set PlugIn tool toggle states
7098  ArrayOfPlugInToolbarTools tool_array =
7099  g_pi_manager->GetPluginToolbarToolArray();
7100  for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
7101  PlugInToolbarToolContainer *pttc = tool_array.Item(i);
7102  if (!pttc->b_viz) continue;
7103 
7104  if (pttc->kind == wxITEM_CHECK) tb->ToggleTool(pttc->id, pttc->b_toggle);
7105  }
7106 
7107  SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
7108  if (g_bTrackActive) {
7109  g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Disable Tracking"));
7110  }
7111  g_MainToolbar->Realize();
7112 
7113  return tb;
7114 }
7115 
7116 bool MyFrame::CheckAndAddPlugInTool() {
7117  if (!g_pi_manager) return false;
7118 
7119  bool bret = false;
7120  ocpnToolBarSimple *tb = NULL;
7121 
7122  if (g_MainToolbar) tb = g_MainToolbar->GetToolbar();
7123 
7124  if (!tb) return false;
7125 
7126  int n_tools = tb->GetToolsCount();
7127 
7128  // Walk the PlugIn tool spec array, checking the requested position
7129  // If a tool has been requested by a plugin at this position, add it
7130  ArrayOfPlugInToolbarTools tool_array =
7131  g_pi_manager->GetPluginToolbarToolArray();
7132 
7133  for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
7134  PlugInToolbarToolContainer *pttc = tool_array.Item(i);
7135  if (pttc->position == n_tools) {
7136  wxBitmap *ptool_bmp;
7137 
7138  switch (global_color_scheme) {
7139  case GLOBAL_COLOR_SCHEME_DAY:
7140  ptool_bmp = pttc->bitmap_day;
7141  ;
7142  break;
7143  case GLOBAL_COLOR_SCHEME_DUSK:
7144  ptool_bmp = pttc->bitmap_dusk;
7145  break;
7146  case GLOBAL_COLOR_SCHEME_NIGHT:
7147  ptool_bmp = pttc->bitmap_night;
7148  break;
7149  default:
7150  ptool_bmp = pttc->bitmap_day;
7151  break;
7152  }
7153 
7155  pttc->id, *(ptool_bmp), pttc->kind, pttc->shortHelp, _T(""));
7156 
7157  tic->m_NormalIconSVG = pttc->pluginNormalIconSVG;
7158  tic->m_RolloverIconSVG = pttc->pluginRolloverIconSVG;
7159  tic->m_ToggledIconSVG = pttc->pluginToggledIconSVG;
7160  tic->m_bPlugin = true;
7161 
7162  bret = true;
7163  }
7164  }
7165 
7166  // If we added a tool, call again (recursively) to allow for adding
7167  // adjacent tools
7168  if (bret)
7169  while (CheckAndAddPlugInTool()) { /* nothing to do */
7170  }
7171 
7172  return bret;
7173 }
7174 
7175 bool MyFrame::AddDefaultPositionPlugInTools() {
7176  if (!g_pi_manager) return false;
7177 
7178  bool bret = false;
7179 
7180  // Walk the PlugIn tool spec array, checking the requested position
7181  // If a tool has been requested by a plugin at this position, add it
7182  ArrayOfPlugInToolbarTools tool_array =
7183  g_pi_manager->GetPluginToolbarToolArray();
7184 
7185  for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
7186  PlugInToolbarToolContainer *pttc = tool_array.Item(i);
7187 
7188  // Tool is currently tagged as invisible
7189  if (!pttc->b_viz) continue;
7190 
7191  if (pttc->position == -1) // PlugIn has requested default positioning
7192  {
7193  wxBitmap *ptool_bmp;
7194 
7195  switch (global_color_scheme) {
7196  case GLOBAL_COLOR_SCHEME_DAY:
7197  ptool_bmp = pttc->bitmap_day;
7198  break;
7199  case GLOBAL_COLOR_SCHEME_DUSK:
7200  ptool_bmp = pttc->bitmap_dusk;
7201  break;
7202  case GLOBAL_COLOR_SCHEME_NIGHT:
7203  ptool_bmp = pttc->bitmap_night;
7204  break;
7205  default:
7206  ptool_bmp = pttc->bitmap_day;
7207  break;
7208  }
7209 
7211  pttc->id, *(ptool_bmp), pttc->kind, pttc->shortHelp, _T(""));
7212 
7213  tic->m_NormalIconSVG = pttc->pluginNormalIconSVG;
7214  tic->m_RolloverIconSVG = pttc->pluginRolloverIconSVG;
7215  tic->m_ToggledIconSVG = pttc->pluginToggledIconSVG;
7216  tic->m_bPlugin = true;
7217 
7218  g_MainToolbar->AddToolItem(tic);
7219 
7220  bret = true;
7221  }
7222  }
7223  return bret;
7224 }
7225 
7226 /*************************************************************************
7227  * Global color management routines
7228  *
7229  *************************************************************************/
7230 
7231 wxColour GetGlobalColor(wxString colorName); // -> color_handler
7232 
7233 static const char *usercolors[] = {
7234  "Table:DAY",
7235  "GREEN1;120;255;120;",
7236  "GREEN2; 45;150; 45;",
7237  "GREEN3;200;220;200;",
7238  "GREEN4; 0;255; 0;",
7239  "BLUE1; 170;170;255;",
7240  "BLUE2; 45; 45;170;",
7241  "BLUE3; 0; 0;255;",
7242  "GREY1; 200;200;200;",
7243  "GREY2; 230;230;230;",
7244  "RED1; 220;200;200;",
7245  "UBLCK; 0; 0; 0;",
7246  "UWHIT; 255;255;255;",
7247  "URED; 255; 0; 0;",
7248  "UGREN; 0;255; 0;",
7249  "YELO1; 243;229; 47;",
7250  "YELO2; 128; 80; 0;",
7251  "TEAL1; 0;128;128;",
7252  "GREEN5;170;254; 0;",
7253  "COMPT; 245;247;244",
7254 #ifdef __WXOSX__
7255  "DILG0; 255;255;255;", // Dialog Background white
7256 #else
7257  "DILG0; 238;239;242;", // Dialog Background white
7258 #endif
7259  "DILG1; 212;208;200;", // Dialog Background
7260  "DILG2; 255;255;255;", // Control Background
7261  "DILG3; 0; 0; 0;", // Text
7262  "UITX1; 0; 0; 0;", // Menu Text, derived from UINFF
7263 
7264  "CHGRF; 163; 180; 183;",
7265  "UINFM; 197; 69; 195;",
7266  "UINFG; 104; 228; 86;",
7267  "UINFF; 125; 137; 140;",
7268  "UINFR; 241; 84; 105;",
7269  "SHIPS; 7; 7; 7;",
7270  "CHYLW; 244; 218; 72;",
7271  "CHWHT; 212; 234; 238;",
7272 
7273  "UDKRD; 124; 16; 0;",
7274  "UARTE; 200; 0; 0;", // Active Route, Grey on Dusk/Night
7275 
7276  "NODTA; 163; 180; 183;",
7277  "CHBLK; 7; 7; 7;",
7278  "SNDG1; 125; 137; 140;",
7279  "SNDG2; 7; 7; 7;",
7280  "SCLBR; 235; 125; 54;",
7281  "UIBDR; 125; 137; 140;",
7282  "UINFB; 58; 120; 240;",
7283  "UINFD; 7; 7; 7;",
7284  "UINFO; 235; 125; 54;",
7285  "PLRTE; 220; 64; 37;",
7286  "CHMGD; 197; 69; 195;",
7287  "UIBCK; 212; 234; 238;",
7288 
7289  "DASHB; 255;255;255;", // Dashboard Instr background
7290  "DASHL; 175;175;175;", // Dashboard Instr Label
7291  "DASHF; 50; 50; 50;", // Dashboard Foreground
7292  "DASHR; 200; 0; 0;", // Dashboard Red
7293  "DASHG; 0;200; 0;", // Dashboard Green
7294  "DASHN; 200;120; 0;", // Dashboard Needle
7295  "DASH1; 204;204;255;", // Dashboard Illustrations
7296  "DASH2; 122;131;172;", // Dashboard Illustrations
7297  "COMP1; 211;211;211;", // Compass Window Background
7298 
7299  "GREY3; 40; 40; 40;", // MUIBar/TB background
7300  "BLUE4; 100;100;200;", // Canvas Focus Bar
7301  "VIO01; 171; 33;141;",
7302  "VIO02; 209;115;213;",
7303 
7304  "Table:DUSK",
7305  "GREEN1; 60;128; 60;",
7306  "GREEN2; 22; 75; 22;",
7307  "GREEN3; 80;100; 80;",
7308  "GREEN4; 0;128; 0;",
7309  "BLUE1; 80; 80;160;",
7310  "BLUE2; 30; 30;120;",
7311  "BLUE3; 0; 0;128;",
7312  "GREY1; 100;100;100;",
7313  "GREY2; 128;128;128;",
7314  "RED1; 150;100;100;",
7315  "UBLCK; 0; 0; 0;",
7316  "UWHIT; 255;255;255;",
7317  "URED; 120; 54; 11;",
7318  "UGREN; 35;110; 20;",
7319  "YELO1; 120;115; 24;",
7320  "YELO2; 64; 40; 0;",
7321  "TEAL1; 0; 64; 64;",
7322  "GREEN5; 85;128; 0;",
7323  "COMPT; 124;126;121",
7324 
7325  "CHGRF; 41; 46; 46;",
7326  "UINFM; 58; 20; 57;",
7327  "UINFG; 35; 76; 29;",
7328  "UINFF; 41; 46; 46;",
7329  "UINFR; 80; 28; 35;",
7330  "SHIPS; 71; 78; 79;",
7331  "CHYLW; 81; 73; 24;",
7332  "CHWHT; 71; 78; 79;",
7333 
7334  "DILG0; 110;110;110;", // Dialog Background
7335  "DILG1; 110;110;110;", // Dialog Background
7336  "DILG2; 0; 0; 0;", // Control Background
7337  "DILG3; 130;130;130;", // Text
7338  "UITX1; 41; 46; 46;", // Menu Text, derived from UINFF
7339  "UDKRD; 80; 0; 0;",
7340  "UARTE; 64; 64; 64;", // Active Route, Grey on Dusk/Night
7341 
7342  "NODTA; 41; 46; 46;",
7343  "CHBLK; 54; 60; 61;",
7344  "SNDG1; 41; 46; 46;",
7345  "SNDG2; 71; 78; 79;",
7346  "SCLBR; 75; 38; 19;",
7347  "UIBDR; 54; 60; 61;",
7348  "UINFB; 19; 40; 80;",
7349  "UINFD; 71; 78; 79;",
7350  "UINFO; 75; 38; 19;",
7351  "PLRTE; 73; 21; 12;",
7352  "CHMGD; 74; 58; 81;",
7353  "UIBCK; 7; 7; 7;",
7354 
7355  "DASHB; 77; 77; 77;", // Dashboard Instr background
7356  "DASHL; 54; 54; 54;", // Dashboard Instr Label
7357  "DASHF; 0; 0; 0;", // Dashboard Foreground
7358  "DASHR; 58; 21; 21;", // Dashboard Red
7359  "DASHG; 21; 58; 21;", // Dashboard Green
7360  "DASHN; 100; 50; 0;", // Dashboard Needle
7361  "DASH1; 76; 76;113;", // Dashboard Illustrations
7362  "DASH2; 48; 52; 72;", // Dashboard Illustrations
7363  "COMP1; 107;107;107;", // Compass Window Background
7364 
7365  "GREY3; 20; 20; 20;", // MUIBar/TB background
7366  "BLUE4; 80; 80;160;", // Canvas Focus Bar
7367  "VIO01; 128; 25;108;",
7368  "VIO02; 171; 33;141;",
7369 
7370  "Table:NIGHT",
7371  "GREEN1; 30; 80; 30;",
7372  "GREEN2; 15; 60; 15;",
7373  "GREEN3; 12; 23; 9;",
7374  "GREEN4; 0; 64; 0;",
7375  "BLUE1; 60; 60;100;",
7376  "BLUE2; 22; 22; 85;",
7377  "BLUE3; 0; 0; 40;",
7378  "GREY1; 48; 48; 48;",
7379  "GREY2; 32; 32; 32;",
7380  "RED1; 100; 50; 50;",
7381  "UWHIT; 255;255;255;",
7382  "UBLCK; 0; 0; 0;",
7383  "URED; 60; 27; 5;",
7384  "UGREN; 17; 55; 10;",
7385  "YELO1; 60; 65; 12;",
7386  "YELO2; 32; 20; 0;",
7387  "TEAL1; 0; 32; 32;",
7388  "GREEN5; 44; 64; 0;",
7389  "COMPT; 48; 49; 51",
7390  "DILG0; 80; 80; 80;", // Dialog Background
7391  "DILG1; 80; 80; 80;", // Dialog Background
7392  "DILG2; 0; 0; 0;", // Control Background
7393  "DILG3; 65; 65; 65;", // Text
7394  "UITX1; 31; 34; 35;", // Menu Text, derived from UINFF
7395  "UDKRD; 50; 0; 0;",
7396  "UARTE; 64; 64; 64;", // Active Route, Grey on Dusk/Night
7397 
7398  "CHGRF; 16; 18; 18;",
7399  "UINFM; 52; 18; 52;",
7400  "UINFG; 22; 24; 7;",
7401  "UINFF; 31; 34; 35;",
7402  "UINFR; 59; 17; 10;",
7403  "SHIPS; 37; 41; 41;",
7404  "CHYLW; 31; 33; 10;",
7405  "CHWHT; 37; 41; 41;",
7406 
7407  "NODTA; 7; 7; 7;",
7408  "CHBLK; 31; 34; 35;",
7409  "SNDG1; 31; 34; 35;",
7410  "SNDG2; 43; 48; 48;",
7411  "SCLBR; 52; 28; 12;",
7412  "UIBDR; 31; 34; 35;",
7413  "UINFB; 21; 29; 69;",
7414  "UINFD; 43; 48; 58;",
7415  "UINFO; 52; 28; 12;",
7416  "PLRTE; 66; 19; 11;",
7417  "CHMGD; 52; 18; 52;",
7418  "UIBCK; 7; 7; 7;",
7419 
7420  "DASHB; 0; 0; 0;", // Dashboard Instr background
7421  "DASHL; 20; 20; 20;", // Dashboard Instr Label
7422  "DASHF; 64; 64; 64;", // Dashboard Foreground
7423  "DASHR; 70; 15; 15;", // Dashboard Red
7424  "DASHG; 15; 70; 15;", // Dashboard Green
7425  "DASHN; 17; 80; 56;", // Dashboard Needle
7426  "DASH1; 48; 52; 72;", // Dashboard Illustrations
7427  "DASH2; 36; 36; 53;", // Dashboard Illustrations
7428  "COMP1; 24; 24; 24;", // Compass Window Background
7429 
7430  "GREY3; 10; 10; 10;", // MUIBar/TB background
7431  "BLUE4; 70; 70;140;", // Canvas Focus Bar
7432  "VIO01; 85; 16; 72;",
7433  "VIO02; 128; 25;108;",
7434 
7435  "*****"};
7436 
7437 int get_static_line(char *d, const char **p, int index, int n) {
7438  if (!strcmp(p[index], "*****")) return 0;
7439 
7440  strncpy(d, p[index], n);
7441  return strlen(d);
7442 }
7443 
7444 void InitializeUserColors(void) {
7445  const char **p = usercolors;
7446  char buf[81];
7447  int index = 0;
7448  char TableName[20];
7449  colTable *ctp;
7450  colTable *ct;
7451  int R, G, B;
7452 
7453  UserColorTableArray = new wxArrayPtrVoid;
7454  UserColourHashTableArray = new wxArrayPtrVoid;
7455 
7456  // Create 3 color table entries
7457  ct = new colTable;
7458  ct->tableName = new wxString(_T("DAY"));
7459  ct->color = new wxArrayPtrVoid;
7460  UserColorTableArray->Add((void *)ct);
7461 
7462  ct = new colTable;
7463  ct->tableName = new wxString(_T("DUSK"));
7464  ct->color = new wxArrayPtrVoid;
7465  UserColorTableArray->Add((void *)ct);
7466 
7467  ct = new colTable;
7468  ct->tableName = new wxString(_T("NIGHT"));
7469  ct->color = new wxArrayPtrVoid;
7470  UserColorTableArray->Add((void *)ct);
7471 
7472  while ((get_static_line(buf, p, index, sizeof(buf) - 1))) {
7473  if (!strncmp(buf, "Table", 5)) {
7474  sscanf(buf, "Table:%s", TableName);
7475 
7476  for (unsigned int it = 0; it < UserColorTableArray->GetCount(); it++) {
7477  ctp = (colTable *)(UserColorTableArray->Item(it));
7478  if (!strcmp(TableName, ctp->tableName->mb_str())) {
7479  ct = ctp;
7480  break;
7481  }
7482  }
7483 
7484  } else {
7485  char name[21];
7486  int j = 0;
7487  while (buf[j] != ';' && j < 20) {
7488  name[j] = buf[j];
7489  j++;
7490  }
7491  name[j] = 0;
7492 
7493  S52color *c = new S52color;
7494  strcpy(c->colName, name);
7495 
7496  sscanf(&buf[j], ";%i;%i;%i", &R, &G, &B);
7497  c->R = (char)R;
7498  c->G = (char)G;
7499  c->B = (char)B;
7500 
7501  ct->color->Add(c);
7502  }
7503 
7504  index++;
7505  }
7506 
7507  // Now create the Hash tables
7508 
7509  for (unsigned int its = 0; its < UserColorTableArray->GetCount(); its++) {
7510  wxColorHashMap *phash = new wxColorHashMap;
7511  UserColourHashTableArray->Add((void *)phash);
7512 
7513  colTable *ctp = (colTable *)(UserColorTableArray->Item(its));
7514 
7515  for (unsigned int ic = 0; ic < ctp->color->GetCount(); ic++) {
7516  S52color *c2 = (S52color *)(ctp->color->Item(ic));
7517 
7518  wxColour c(c2->R, c2->G, c2->B);
7519  wxString key(c2->colName, wxConvUTF8);
7520  (*phash)[key] = c;
7521  }
7522  }
7523 
7524  // Establish a default hash table pointer
7525  // in case a color is needed before ColorScheme is set
7526  pcurrent_user_color_hash =
7527  (wxColorHashMap *)UserColourHashTableArray->Item(0);
7528 }
7529 
7530 void DeInitializeUserColors(void) {
7531  if (!UserColorTableArray) return;
7532  for (unsigned i = 0; i < UserColorTableArray->GetCount(); i++) {
7533  colTable *ct = (colTable *)UserColorTableArray->Item(i);
7534 
7535  for (unsigned int j = 0; j < ct->color->GetCount(); j++) {
7536  S52color *c = (S52color *)ct->color->Item(j);
7537  delete c; // color
7538  }
7539 
7540  delete ct->tableName; // wxString
7541  delete ct->color; // wxArrayPtrVoid
7542 
7543  delete ct; // colTable
7544  }
7545 
7546  delete UserColorTableArray;
7547 
7548  for (unsigned i = 0; i < UserColourHashTableArray->GetCount(); i++) {
7549  wxColorHashMap *phash = (wxColorHashMap *)UserColourHashTableArray->Item(i);
7550  delete phash;
7551  }
7552 
7553  delete UserColourHashTableArray;
7554 }
7555 
7556 #ifdef __WXMSW__
7557 
7558 #define NCOLORS 40
7559 
7560 typedef struct _MSW_COLOR_SPEC {
7561  int COLOR_NAME;
7562  wxString S52_RGB_COLOR;
7563  int SysRGB_COLOR;
7564 } MSW_COLOR_SPEC;
7565 
7566 MSW_COLOR_SPEC color_spec[] = {{COLOR_MENU, _T("UIBCK"), 0},
7567  {COLOR_MENUTEXT, _T("UITX1"), 0},
7568  {COLOR_BTNSHADOW, _T("UIBCK"), 0}, // Menu Frame
7569  {-1, _T(""), 0}};
7570 
7571 void SaveSystemColors() {
7572  /*
7573  color_3dface = pGetSysColor(COLOR_3DFACE);
7574  color_3dhilite = pGetSysColor(COLOR_3DHILIGHT);
7575  color_3dshadow = pGetSysColor(COLOR_3DSHADOW);
7576  color_3ddkshadow = pGetSysColor(COLOR_3DDKSHADOW);
7577  color_3dlight = pGetSysColor(COLOR_3DLIGHT);
7578  color_activecaption = pGetSysColor(COLOR_ACTIVECAPTION);
7579  color_gradientactivecaption = pGetSysColor(27); //COLOR_3DLIGHT);
7580  color_captiontext = pGetSysColor(COLOR_CAPTIONTEXT);
7581  color_windowframe = pGetSysColor(COLOR_WINDOWFRAME);
7582  color_inactiveborder = pGetSysColor(COLOR_INACTIVEBORDER);
7583  */
7584  // Record the default system color in my substitution structure
7585  MSW_COLOR_SPEC *pcspec = &color_spec[0];
7586  while (pcspec->COLOR_NAME != -1) {
7587  pcspec->SysRGB_COLOR = pGetSysColor(pcspec->COLOR_NAME);
7588  pcspec++;
7589  }
7590 }
7591 
7592 void RestoreSystemColors() {
7593  int element[NCOLORS];
7594  int rgbcolor[NCOLORS];
7595  int i = 0;
7596 
7597  MSW_COLOR_SPEC *pcspec = &color_spec[0];
7598  while (pcspec->COLOR_NAME != -1) {
7599  element[i] = pcspec->COLOR_NAME;
7600  rgbcolor[i] = pcspec->SysRGB_COLOR;
7601 
7602  pcspec++;
7603  i++;
7604  }
7605 
7606  pSetSysColors(i, (unsigned long *)&element[0], (unsigned long *)&rgbcolor[0]);
7607 }
7608 
7609 #endif
7610 
7611 void SetSystemColors(ColorScheme cs) { //---------------
7612 #ifdef __WXMSW__
7613  int element[NCOLORS];
7614  int rgbcolor[NCOLORS];
7615  int i = 0;
7616  if ((GLOBAL_COLOR_SCHEME_DUSK == cs) || (GLOBAL_COLOR_SCHEME_NIGHT == cs)) {
7617  MSW_COLOR_SPEC *pcspec = &color_spec[0];
7618  while (pcspec->COLOR_NAME != -1) {
7619  wxColour color = GetGlobalColor(pcspec->S52_RGB_COLOR);
7620  rgbcolor[i] = (color.Red() << 16) + (color.Green() << 8) + color.Blue();
7621  element[i] = pcspec->COLOR_NAME;
7622 
7623  i++;
7624  pcspec++;
7625  }
7626 
7627  pSetSysColors(i, (unsigned long *)&element[0],
7628  (unsigned long *)&rgbcolor[0]);
7629 
7630  } else { // for daylight colors, use default windows colors as saved....
7631 
7632  RestoreSystemColors();
7633  }
7634 #endif
7635 }
7636 
7637 wxColor GetDimColor(wxColor c) {
7638  if ((global_color_scheme == GLOBAL_COLOR_SCHEME_DAY) ||
7639  (global_color_scheme == GLOBAL_COLOR_SCHEME_RGB))
7640  return c;
7641 
7642  float factor = 1.0;
7643  if (global_color_scheme == GLOBAL_COLOR_SCHEME_DUSK) factor = 0.5;
7644  if (global_color_scheme == GLOBAL_COLOR_SCHEME_NIGHT) factor = 0.25;
7645 
7646  wxImage::RGBValue rgb(c.Red(), c.Green(), c.Blue());
7647  wxImage::HSVValue hsv = wxImage::RGBtoHSV(rgb);
7648  hsv.value = hsv.value * factor;
7649  wxImage::RGBValue nrgb = wxImage::HSVtoRGB(hsv);
7650 
7651  return wxColor(nrgb.red, nrgb.green, nrgb.blue);
7652 }
7653 
7654 
7655 // A helper function to check for proper parameters of anchor
7656 // watch
7657 //
7658 double AnchorDistFix(double const d, double const AnchorPointMinDist,
7659  double const AnchorPointMaxDist) // pjotrc 2010.02.22
7660 {
7661  if (d >= 0.0)
7662  if (d < AnchorPointMinDist)
7663  return AnchorPointMinDist;
7664  else if (d > AnchorPointMaxDist)
7665  return AnchorPointMaxDist;
7666  else
7667  return d;
7668 
7669  else
7670  // if ( d < 0.0 )
7671  if (d > -AnchorPointMinDist)
7672  return -AnchorPointMinDist;
7673  else if (d < -AnchorPointMaxDist)
7674  return -AnchorPointMaxDist;
7675  else
7676  return d;
7677 }
7678 // Console supporting printf functionality for Windows GUI app
7679 
7680 #ifdef __WXMSW__
7681 static const WORD MAX_CONSOLE_LINES =
7682  500; // maximum mumber of lines the output console should have
7683 
7684 //#ifdef _DEBUG
7685 
7686 void RedirectIOToConsole()
7687 
7688 {
7689  int hConHandle;
7690 
7691  wxIntPtr lStdHandle;
7692 
7693  CONSOLE_SCREEN_BUFFER_INFO coninfo;
7694 
7695  FILE *fp;
7696 
7697  // allocate a console for this app
7698 
7699  AllocConsole();
7700 
7701  // set the screen buffer to be big enough to let us scroll text
7702 
7703  GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
7704  coninfo.dwSize.Y = MAX_CONSOLE_LINES;
7705  SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
7706 
7707  // redirect unbuffered STDOUT to the console
7708 
7709  lStdHandle = (wxIntPtr)GetStdHandle(STD_OUTPUT_HANDLE);
7710  hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7711  fp = _fdopen(hConHandle, "w");
7712  *stdout = *fp;
7713  setvbuf(stdout, NULL, _IONBF, 0);
7714 
7715  // redirect unbuffered STDIN to the console
7716 
7717  lStdHandle = (wxIntPtr)GetStdHandle(STD_INPUT_HANDLE);
7718  hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7719  fp = _fdopen(hConHandle, "r");
7720  *stdin = *fp;
7721  setvbuf(stdin, NULL, _IONBF, 0);
7722 
7723  // redirect unbuffered STDERR to the console
7724 
7725  lStdHandle = (wxIntPtr)GetStdHandle(STD_ERROR_HANDLE);
7726  hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7727  fp = _fdopen(hConHandle, "w");
7728  *stderr = *fp;
7729  setvbuf(stderr, NULL, _IONBF, 0);
7730 
7731  // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console
7732  // as well
7733 
7734  // ios::sync_with_stdio();
7735 }
7736 
7737 //#endif
7738 #endif
7739 
7740 #ifdef __WXMSW__
7741 bool TestGLCanvas(wxString prog_dir) {
7742 #ifdef __MSVC__
7743  wxString test_app = prog_dir;
7744  test_app += _T("ocpn_gltest1.exe");
7745 
7746  if (::wxFileExists(test_app)) {
7747  long proc_return = ::wxExecute(test_app, wxEXEC_SYNC);
7748  printf("OpenGL Test Process returned %0X\n", proc_return);
7749  if (proc_return == 0)
7750  printf("GLCanvas OK\n");
7751  else
7752  printf("GLCanvas failed to start, disabling OpenGL.\n");
7753 
7754  return (proc_return == 0);
7755  } else
7756  return true;
7757 #else
7758  /* until we can get the source to ocpn_gltest1 assume true for mingw */
7759  return true;
7760 #endif
7761 }
7762 #endif
7763 
7764 OCPN_ThreadMessageEvent::OCPN_ThreadMessageEvent(wxEventType commandType,
7765  int id)
7766  : wxEvent(id, commandType) {}
7767 
7768 OCPN_ThreadMessageEvent::~OCPN_ThreadMessageEvent() {}
7769 
7770 wxEvent *OCPN_ThreadMessageEvent::Clone() const {
7771  OCPN_ThreadMessageEvent *newevent = new OCPN_ThreadMessageEvent(*this);
7772  newevent->m_string = this->m_string;
7773  return newevent;
7774 }
7775 
7776 #if 0
7777 /*************************************************************************
7778  * Serial port enumeration routines
7779  *
7780  * The EnumSerialPort function will populate an array of SSerInfo structs,
7781  * each of which contains information about one serial port present in
7782  * the system. Note that this code must be linked with setupapi.lib,
7783  * which is included with the Win32 SDK.
7784  *
7785  * by Zach Gorman <gormanjz@hotmail.com>
7786  *
7787  * Copyright (c) 2002 Archetype Auction Software, Inc. All rights reserved.
7788  *
7789  * Redistribution and use in source and binary forms, with or without
7790  * modification, are permitted provided that the following condition is
7791  * met: Redistributions of source code must retain the above copyright
7792  * notice, this condition and the following disclaimer.
7793  *
7794  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
7795  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
7796  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
7797  * DISCLAIMED. IN NO EVENT SHALL ARCHETYPE AUCTION SOFTWARE OR ITS
7798  * AFFILIATES BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
7799  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
7800  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
7801  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
7802  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
7803  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
7804  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
7805  ************************************************************************/
7806 
7807 // For MFC
7808 #include <stdafx.h>
7809 
7810 // The next 3 includes are needed for serial port enumeration
7811 #include <objbase.h>
7812 #include <initguid.h>
7813 #include <Setupapi.h>
7814 
7815 #include "EnumSerial.h"
7816 
7817 // The following define is from ntddser.h in the DDK. It is also
7818 // needed for serial port enumeration.
7819 #ifndef GUID_CLASS_COMPORT
7820 DEFINE_GUID(GUID_CLASS_COMPORT, 0x86e0d1e0L, 0x8089, 0x11d0, 0x9c, 0xe4, \
7821 0x08, 0x00, 0x3e, 0x30, 0x1f, 0x73);
7822 #endif
7823 
7824 
7825 struct SSerInfo {
7826  SSerInfo() : bUsbDevice(FALSE) {}
7827  CString strDevPath; // Device path for use with CreateFile()
7828  CString strPortName; // Simple name (i.e. COM1)
7829  CString strFriendlyName; // Full name to be displayed to a user
7830  BOOL bUsbDevice; // Provided through a USB connection?
7831  CString strPortDesc; // friendly name without the COMx
7832 };
7833 
7834 //---------------------------------------------------------------
7835 // Helpers for enumerating the available serial ports.
7836 // These throw a CString on failure, describing the nature of
7837 // the error that occurred.
7838 
7839 void EnumPortsWdm(CArray<SSerInfo,SSerInfo&> &asi);
7840 void EnumPortsWNt4(CArray<SSerInfo,SSerInfo&> &asi);
7841 void EnumPortsW9x(CArray<SSerInfo,SSerInfo&> &asi);
7842 void SearchPnpKeyW9x(HKEY hkPnp, BOOL bUsbDevice,
7843  CArray<SSerInfo,SSerInfo&> &asi);
7844 
7845 
7846 //---------------------------------------------------------------
7847 // Routine for enumerating the available serial ports.
7848 // Throws a CString on failure, describing the error that
7849 // occurred. If bIgnoreBusyPorts is TRUE, ports that can't
7850 // be opened for read/write access are not included.
7851 
7852 void EnumSerialPorts(CArray<SSerInfo,SSerInfo&> &asi, BOOL bIgnoreBusyPorts)
7853 {
7854  // Clear the output array
7855  asi.RemoveAll();
7856 
7857  // Use different techniques to enumerate the available serial
7858  // ports, depending on the OS we're using
7859  OSVERSIONINFO vi;
7860  vi.dwOSVersionInfoSize = sizeof(vi);
7861  if (!::GetVersionEx(&vi)) {
7862  CString str;
7863  str.Format("Could not get OS version. (err=%lx)",
7864  GetLastError());
7865  throw str;
7866  }
7867  // Handle windows 9x and NT4 specially
7868  if (vi.dwMajorVersion < 5) {
7869  if (vi.dwPlatformId == VER_PLATFORM_WIN32_NT)
7870  EnumPortsWNt4(asi);
7871  else
7872  EnumPortsW9x(asi);
7873  }
7874  else {
7875  // Win2k and later support a standard API for
7876  // enumerating hardware devices.
7877  EnumPortsWdm(asi);
7878  }
7879 
7880  for (int ii=0; ii<asi.GetSize(); ii++)
7881  {
7882  SSerInfo& rsi = asi[ii];
7883  if (bIgnoreBusyPorts) {
7884  // Only display ports that can be opened for read/write
7885  HANDLE hCom = CreateFile(rsi.strDevPath,
7886  GENERIC_READ | GENERIC_WRITE,
7887  0, /* comm devices must be opened w/exclusive-access */
7888  NULL, /* no security attrs */
7889  OPEN_EXISTING, /* comm devices must use OPEN_EXISTING */
7890  0, /* not overlapped I/O */
7891  NULL /* hTemplate must be NULL for comm devices */
7892  );
7893  if (hCom == INVALID_HANDLE_VALUE) {
7894  // It can't be opened; remove it.
7895  asi.RemoveAt(ii);
7896  ii--;
7897  continue;
7898  }
7899  else {
7900  // It can be opened! Close it and add it to the list
7901  ::CloseHandle(hCom);
7902  }
7903  }
7904 
7905  // Come up with a name for the device.
7906  // If there is no friendly name, use the port name.
7907  if (rsi.strFriendlyName.IsEmpty())
7908  rsi.strFriendlyName = rsi.strPortName;
7909 
7910  // If there is no description, try to make one up from
7911  // the friendly name.
7912  if (rsi.strPortDesc.IsEmpty()) {
7913  // If the port name is of the form "ACME Port (COM3)"
7914  // then strip off the " (COM3)"
7915  rsi.strPortDesc = rsi.strFriendlyName;
7916  int startdex = rsi.strPortDesc.Find(" (");
7917  int enddex = rsi.strPortDesc.Find(")");
7918  if (startdex > 0 && enddex ==
7919  (rsi.strPortDesc.GetLength()-1))
7920  rsi.strPortDesc = rsi.strPortDesc.Left(startdex);
7921  }
7922  }
7923 }
7924 
7925 // Helpers for EnumSerialPorts
7926 
7927 void EnumPortsWdm(CArray<SSerInfo,SSerInfo&> &asi)
7928 {
7929  CString strErr;
7930  // Create a device information set that will be the container for
7931  // the device interfaces.
7932  GUID *guidDev = (GUID*) &GUID_CLASS_COMPORT;
7933 
7934  HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
7935  SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
7936 
7937  try {
7938  hDevInfo = SetupDiGetClassDevs( guidDev,
7939  NULL,
7940  NULL,
7941  DIGCF_PRESENT | DIGCF_DEVICEINTERFACE
7942  );
7943 
7944  if(hDevInfo == INVALID_HANDLE_VALUE)
7945  {
7946  strErr.Format("SetupDiGetClassDevs failed. (err=%lx)",
7947  GetLastError());
7948  throw strErr;
7949  }
7950 
7951  // Enumerate the serial ports
7952  BOOL bOk = TRUE;
7953  SP_DEVICE_INTERFACE_DATA ifcData;
7954  DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
7955  pDetData = (SP_DEVICE_INTERFACE_DETAIL_DATA*) new char[dwDetDataSize];
7956  // This is required, according to the documentation. Yes,
7957  // it's weird.
7958  ifcData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
7959  pDetData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
7960  for (DWORD ii=0; bOk; ii++) {
7961  bOk = SetupDiEnumDeviceInterfaces(hDevInfo,
7962  NULL, guidDev, ii, &ifcData);
7963  if (bOk) {
7964  // Got a device. Get the details.
7965  SP_DEVINFO_DATA devdata = {sizeof(SP_DEVINFO_DATA)};
7966  bOk = SetupDiGetDeviceInterfaceDetail(hDevInfo,
7967  &ifcData, pDetData, dwDetDataSize, NULL, &devdata);
7968  if (bOk) {
7969  CString strDevPath(pDetData->DevicePath);
7970  // Got a path to the device. Try to get some more info.
7971  TCHAR fname[256];
7972  TCHAR desc[256];
7973  BOOL bSuccess = SetupDiGetDeviceRegistryProperty(
7974  hDevInfo, &devdata, SPDRP_FRIENDLYNAME, NULL,
7975  (PBYTE)fname, sizeof(fname), NULL);
7976  bSuccess = bSuccess && SetupDiGetDeviceRegistryProperty(
7977  hDevInfo, &devdata, SPDRP_DEVICEDESC, NULL,
7978  (PBYTE)desc, sizeof(desc), NULL);
7979  BOOL bUsbDevice = FALSE;
7980  TCHAR locinfo[256];
7981  if (SetupDiGetDeviceRegistryProperty(
7982  hDevInfo, &devdata, SPDRP_LOCATION_INFORMATION, NULL,
7983  (PBYTE)locinfo, sizeof(locinfo), NULL))
7984  {
7985  // Just check the first three characters to determine
7986  // if the port is connected to the USB bus. This isn't
7987  // an infallible method; it would be better to use the
7988  // BUS GUID. Currently, Windows doesn't let you query
7989  // that though (SPDRP_BUSTYPEGUID seems to exist in
7990  // documentation only).
7991  bUsbDevice = (strncmp(locinfo, "USB", 3)==0);
7992  }
7993  if (bSuccess) {
7994  // Add an entry to the array
7995  SSerInfo si;
7996  si.strDevPath = strDevPath;
7997  si.strFriendlyName = fname;
7998  si.strPortDesc = desc;
7999  si.bUsbDevice = bUsbDevice;
8000  asi.Add(si);
8001  }
8002 
8003  }
8004  else {
8005  strErr.Format("SetupDiGetDeviceInterfaceDetail failed. (err=%lx)",
8006  GetLastError());
8007  throw strErr;
8008  }
8009  }
8010  else {
8011  DWORD err = GetLastError();
8012  if (err != ERROR_NO_MORE_ITEMS) {
8013  strErr.Format("SetupDiEnumDeviceInterfaces failed. (err=%lx)", err);
8014  throw strErr;
8015  }
8016  }
8017  }
8018  }
8019  catch (CString strCatchErr) {
8020  strErr = strCatchErr;
8021  }
8022 
8023  if (pDetData != NULL)
8024  delete [] (char*)pDetData;
8025  if (hDevInfo != INVALID_HANDLE_VALUE)
8026  SetupDiDestroyDeviceInfoList(hDevInfo);
8027 
8028  if (!strErr.IsEmpty())
8029  throw strErr;
8030 }
8031 
8032 void EnumPortsWNt4(CArray<SSerInfo,SSerInfo&> &asi)
8033 {
8034  // NT4's driver model is totally different, and not that
8035  // many people use NT4 anymore. Just try all the COM ports
8036  // between 1 and 16
8037  SSerInfo si;
8038  for (int ii=1; ii<=16; ii++) {
8039  CString strPort;
8040  strPort.Format("COM%d",ii);
8041  si.strDevPath = CString("\\\\.\\") + strPort;
8042  si.strPortName = strPort;
8043  asi.Add(si);
8044  }
8045 }
8046 
8047 void EnumPortsW9x(CArray<SSerInfo,SSerInfo&> &asi)
8048 {
8049  // Look at all keys in HKLM\Enum, searching for subkeys named
8050  // *PNP0500 and *PNP0501. Within these subkeys, search for
8051  // sub-subkeys containing value entries with the name "PORTNAME"
8052  // Search all subkeys of HKLM\Enum\USBPORTS for PORTNAME entries.
8053 
8054  // First, open HKLM\Enum
8055  HKEY hkEnum = NULL;
8056  HKEY hkSubEnum = NULL;
8057  HKEY hkSubSubEnum = NULL;
8058 
8059  try {
8060  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Enum", 0, KEY_READ,
8061  &hkEnum) != ERROR_SUCCESS)
8062  throw CString("Could not read from HKLM\\Enum");
8063 
8064  // Enumerate the subkeys of HKLM\Enum
8065  char acSubEnum[128];
8066  DWORD dwSubEnumIndex = 0;
8067  DWORD dwSize = sizeof(acSubEnum);
8068  while (RegEnumKeyEx(hkEnum, dwSubEnumIndex++, acSubEnum, &dwSize,
8069  NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
8070  {
8071  HKEY hkSubEnum = NULL;
8072  if (RegOpenKeyEx(hkEnum, acSubEnum, 0, KEY_READ,
8073  &hkSubEnum) != ERROR_SUCCESS)
8074  throw CString("Could not read from HKLM\\Enum\\")+acSubEnum;
8075 
8076  // Enumerate the subkeys of HKLM\Enum\*\, looking for keys
8077  // named *PNP0500 and *PNP0501 (or anything in USBPORTS)
8078  BOOL bUsbDevice = (strcmp(acSubEnum,"USBPORTS")==0);
8079  char acSubSubEnum[128];
8080  dwSize = sizeof(acSubSubEnum); // set the buffer size
8081  DWORD dwSubSubEnumIndex = 0;
8082  while (RegEnumKeyEx(hkSubEnum, dwSubSubEnumIndex++, acSubSubEnum,
8083  &dwSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
8084  {
8085  BOOL bMatch = (strcmp(acSubSubEnum,"*PNP0500")==0 ||
8086  strcmp(acSubSubEnum,"*PNP0501")==0 ||
8087  bUsbDevice);
8088  if (bMatch) {
8089  HKEY hkSubSubEnum = NULL;
8090  if (RegOpenKeyEx(hkSubEnum, acSubSubEnum, 0, KEY_READ,
8091  &hkSubSubEnum) != ERROR_SUCCESS)
8092  throw CString("Could not read from HKLM\\Enum\\") +
8093  acSubEnum + "\\" + acSubSubEnum;
8094  SearchPnpKeyW9x(hkSubSubEnum, bUsbDevice, asi);
8095  RegCloseKey(hkSubSubEnum);
8096  hkSubSubEnum = NULL;
8097  }
8098 
8099  dwSize = sizeof(acSubSubEnum); // restore the buffer size
8100  }
8101 
8102  RegCloseKey(hkSubEnum);
8103  hkSubEnum = NULL;
8104  dwSize = sizeof(acSubEnum); // restore the buffer size
8105  }
8106  }
8107  catch (CString strError) {
8108  if (hkEnum != NULL)
8109  RegCloseKey(hkEnum);
8110  if (hkSubEnum != NULL)
8111  RegCloseKey(hkSubEnum);
8112  if (hkSubSubEnum != NULL)
8113  RegCloseKey(hkSubSubEnum);
8114  throw strError;
8115  }
8116 
8117  RegCloseKey(hkEnum);
8118 }
8119 
8120 void SearchPnpKeyW9x(HKEY hkPnp, BOOL bUsbDevice,
8121  CArray<SSerInfo,SSerInfo&> &asi)
8122 {
8123  // Enumerate the subkeys of the given PNP key, looking for values with
8124  // the name "PORTNAME"
8125  // First, open HKLM\Enum
8126  HKEY hkSubPnp = NULL;
8127 
8128  try {
8129  // Enumerate the subkeys of HKLM\Enum\*\PNP050[01]
8130  char acSubPnp[128];
8131  DWORD dwSubPnpIndex = 0;
8132  DWORD dwSize = sizeof(acSubPnp);
8133  while (RegEnumKeyEx(hkPnp, dwSubPnpIndex++, acSubPnp, &dwSize,
8134  NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
8135  {
8136  HKEY hkSubPnp = NULL;
8137  if (RegOpenKeyEx(hkPnp, acSubPnp, 0, KEY_READ,
8138  &hkSubPnp) != ERROR_SUCCESS)
8139  throw CString("Could not read from HKLM\\Enum\\...\\")
8140  + acSubPnp;
8141 
8142  // Look for the PORTNAME value
8143  char acValue[128];
8144  dwSize = sizeof(acValue);
8145  if (RegQueryValueEx(hkSubPnp, "PORTNAME", NULL, NULL, (BYTE*)acValue,
8146  &dwSize) == ERROR_SUCCESS)
8147  {
8148  CString strPortName(acValue);
8149 
8150  // Got the portname value. Look for a friendly name.
8151  CString strFriendlyName;
8152  dwSize = sizeof(acValue);
8153  if (RegQueryValueEx(hkSubPnp, "FRIENDLYNAME", NULL, NULL, (BYTE*)acValue,
8154  &dwSize) == ERROR_SUCCESS)
8155  strFriendlyName = acValue;
8156 
8157  // Prepare an entry for the output array.
8158  SSerInfo si;
8159  si.strDevPath = CString("\\\\.\\") + strPortName;
8160  si.strPortName = strPortName;
8161  si.strFriendlyName = strFriendlyName;
8162  si.bUsbDevice = bUsbDevice;
8163 
8164  // Overwrite duplicates.
8165  BOOL bDup = FALSE;
8166  for (int ii=0; ii<asi.GetSize() && !bDup; ii++)
8167  {
8168  if (asi[ii].strPortName == strPortName) {
8169  bDup = TRUE;
8170  asi[ii] = si;
8171  }
8172  }
8173  if (!bDup) {
8174  // Add an entry to the array
8175  asi.Add(si);
8176  }
8177  }
8178 
8179  RegCloseKey(hkSubPnp);
8180  hkSubPnp = NULL;
8181  dwSize = sizeof(acSubPnp); // restore the buffer size
8182  }
8183  }
8184  catch (CString strError) {
8185  if (hkSubPnp != NULL)
8186  RegCloseKey(hkSubPnp);
8187  throw strError;
8188  }
8189 }
8190 
8191 #endif
8192 
8193 bool ReloadLocale() {
8194  bool ret = false;
8195 
8196 #if wxUSE_XLOCALE
8197  ret =
8198  (!g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang)
8199  .IsEmpty());
8200 #endif
8201  return ret;
8202 }
8203 
8204 void ApplyLocale() {
8205  FontMgr::Get().SetLocale(g_locale);
8206  FontMgr::Get().ScrubList();
8207 
8208  // Close and re-init various objects to allow new locale to show.
8209  delete g_options;
8210  g_options = NULL;
8211  g_pOptions = NULL;
8212 
8213  if (pRoutePropDialog) {
8214  pRoutePropDialog->Hide();
8215  pRoutePropDialog->Destroy();
8216  pRoutePropDialog = NULL;
8217  }
8218 
8219  if (pRouteManagerDialog) {
8220  pRouteManagerDialog->Hide();
8221  pRouteManagerDialog->Destroy();
8222  pRouteManagerDialog = NULL;
8223  }
8224 
8225  if (console) console->SetColorScheme(global_color_scheme);
8226  if (g_pais_query_dialog_active) {
8227  g_pais_query_dialog_active->Destroy();
8228  g_pais_query_dialog_active = NULL;
8229  }
8230 
8231  auto alert_dlg_active =
8232  dynamic_cast<AISTargetAlertDialog*>(g_pais_alert_dialog_active);
8233  if (alert_dlg_active) {
8234  alert_dlg_active->Destroy();
8235  g_pais_alert_dialog_active = nullptr;
8236  }
8237 
8238  if (g_pAISTargetList) {
8239  if (g_pauimgr) g_pauimgr->DetachPane(g_pAISTargetList);
8240  g_pAISTargetList->Disconnect_decoder();
8241  g_pAISTargetList->Destroy();
8242  g_pAISTargetList = NULL;
8243  }
8244 
8245  // Process the menubar, if present.
8246  if (gFrame->m_pMenuBar) { // remove the menu bar if it is presently enabled
8247  gFrame->SetMenuBar(NULL);
8248  gFrame->m_pMenuBar->Destroy();
8249  gFrame->m_pMenuBar = NULL;
8250  }
8251  gFrame->BuildMenuBar();
8252 
8253  // Give all canvas a chance to update, if needed
8254  for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
8255  ChartCanvas *cc = g_canvasArray.Item(i);
8256  if (cc) cc->CanvasApplyLocale();
8257  }
8258 
8259  // Capture a copy of the current perspective
8260  // So that we may restore PlugIn window sizes, position, visibility, etc.
8261  wxString perspective;
8262  pConfig->SetPath(_T ( "/AUI" ));
8263  pConfig->Read(_T ( "AUIPerspective" ), &perspective);
8264 
8265  // Compliant Plugins will reload their locale message catalog during the
8266  // Init() method. So it is sufficient to simply deactivate, and then
8267  // re-activate, all "active" plugins.
8268  PluginLoader::getInstance()->DeactivateAllPlugIns();
8269  PluginLoader::getInstance()->UpdatePlugIns();
8270 
8271  // // Make sure the perspective saved in the config file is
8272  // "reasonable"
8273  // // In particular, the perspective should have an entry for every
8274  // // windows added to the AUI manager so far.
8275  // // If any are not found, then use the default layout
8276  //
8277  bool bno_load = false;
8278  wxAuiPaneInfoArray pane_array_val = g_pauimgr->GetAllPanes();
8279 
8280  for (unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
8281  wxAuiPaneInfo pane = pane_array_val[i];
8282  if (perspective.Find(pane.name) == wxNOT_FOUND) {
8283  bno_load = true;
8284  break;
8285  }
8286  }
8287 
8288  if (!bno_load) g_pauimgr->LoadPerspective(perspective, false);
8289 
8290  g_pauimgr->Update();
8291 
8292  if (gFrame) {
8293  gFrame->RequestNewToolbars(true);
8294  gFrame->RequestNewMasterToolbar(true);
8295  }
8296 }
8297 
8298 extern s57RegistrarMgr *m_pRegistrarMan;
8299 extern wxString g_UserPresLibData;
8300 extern wxString g_SENCPrefix;
8301 extern wxString g_csv_locn;
8302 extern SENCThreadManager *g_SencThreadManager;
8303 
8304 void LoadS57() {
8305  if (ps52plib) // already loaded?
8306  return;
8307 
8308  // Start a SENC Thread manager
8309  g_SencThreadManager = new SENCThreadManager();
8310 
8311  // Set up a useable CPL library error handler for S57 stuff
8312  // FIXME (dave) Verify after moving LoadS57
8313  // CPLSetErrorHandler(MyCPLErrorHandler);
8314 
8315  // Init the s57 chart object, specifying the location of the required csv
8316  // files
8317  g_csv_locn = g_Platform->GetSharedDataDir();
8318  g_csv_locn.Append(_T("s57data"));
8319 
8320  if (g_bportable) {
8321  g_csv_locn = _T(".");
8322  appendOSDirSlash(&g_csv_locn);
8323  g_csv_locn.Append(_T("s57data"));
8324  }
8325 
8326  // If the config file contains an entry for SENC file prefix, use it.
8327  // Otherwise, default to PrivateDataDir
8328  if (g_SENCPrefix.IsEmpty()) {
8329  g_SENCPrefix = g_Platform->GetPrivateDataDir();
8330  appendOSDirSlash(&g_SENCPrefix);
8331  g_SENCPrefix.Append(_T("SENC"));
8332  }
8333 
8334  if (g_bportable) {
8335  wxFileName f(g_SENCPrefix);
8336  if (f.MakeRelativeTo(g_Platform->GetPrivateDataDir()))
8337  g_SENCPrefix = f.GetFullPath();
8338  else
8339  g_SENCPrefix = _T("SENC");
8340  }
8341 
8342  // If the config file contains an entry for PresentationLibraryData, use
8343  // it. Otherwise, default to conditionally set spot under g_pcsv_locn
8344  wxString plib_data;
8345  bool b_force_legacy = false;
8346 
8347  if (g_UserPresLibData.IsEmpty()) {
8348  plib_data = g_csv_locn;
8349  appendOSDirSlash(&plib_data);
8350  plib_data.Append(_T("S52RAZDS.RLE"));
8351  } else {
8352  plib_data = g_UserPresLibData;
8353  b_force_legacy = true;
8354  }
8355 
8356  ps52plib = new s52plib(plib_data, b_force_legacy);
8357 
8358  // If the library load failed, try looking for the s57 data elsewhere
8359 
8360  // First, look in UserDataDir
8361  /* From wxWidgets documentation
8362 
8363  wxStandardPaths::GetUserDataDir
8364  wxString GetUserDataDir() const
8365  Return the directory for the user-dependent application data files:
8366  * Unix: ~/.appname
8367  * Windows: C:\Documents and Settings\username\Application Data\appname
8368  * Mac: ~/Library/Application Support/appname
8369  */
8370 
8371  if (!ps52plib->m_bOK) {
8372  delete ps52plib;
8373 
8374  wxStandardPaths &std_path = g_Platform->GetStdPaths();
8375 
8376  wxString look_data_dir;
8377  look_data_dir.Append(std_path.GetUserDataDir());
8378  appendOSDirSlash(&look_data_dir);
8379  wxString tentative_SData_Locn = look_data_dir;
8380  look_data_dir.Append(_T("s57data"));
8381 
8382  plib_data = look_data_dir;
8383  appendOSDirSlash(&plib_data);
8384  plib_data.Append(_T("S52RAZDS.RLE"));
8385 
8386  wxLogMessage(_T("Looking for s57data in ") + look_data_dir);
8387  ps52plib = new s52plib(plib_data);
8388 
8389  if (ps52plib->m_bOK) {
8390  g_csv_locn = look_data_dir;
8392  }
8393  }
8394 
8395  // And if that doesn't work, look again in the original SData Location
8396  // This will cover the case in which the .ini file entry is corrupted or
8397  // moved
8398 
8399  if (!ps52plib->m_bOK) {
8400  delete ps52plib;
8401 
8402  wxString look_data_dir;
8403  look_data_dir = g_Platform->GetSharedDataDir();
8404  look_data_dir.Append(_T("s57data"));
8405 
8406  plib_data = look_data_dir;
8407  appendOSDirSlash(&plib_data);
8408  plib_data.Append(_T("S52RAZDS.RLE"));
8409 
8410  wxLogMessage(_T("Looking for s57data in ") + look_data_dir);
8411  ps52plib = new s52plib(plib_data);
8412 
8413  if (ps52plib->m_bOK) g_csv_locn = look_data_dir;
8414  }
8415 
8416  if (ps52plib->m_bOK) {
8417  wxLogMessage(_T("Using s57data in ") + g_csv_locn);
8418  m_pRegistrarMan =
8419  new s57RegistrarMgr(g_csv_locn, g_Platform->GetLogFilePtr());
8420 
8421  // Preset some object class visibilites for "User Standard" disply
8422  // category
8423  // They may be overridden in LoadS57Config
8424  for (unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount();
8425  iPtr++) {
8426  OBJLElement *pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
8427  if (!strncmp(pOLE->OBJLName, "DEPARE", 6)) pOLE->nViz = 1;
8428  if (!strncmp(pOLE->OBJLName, "LNDARE", 6)) pOLE->nViz = 1;
8429  if (!strncmp(pOLE->OBJLName, "COALNE", 6)) pOLE->nViz = 1;
8430  }
8431 
8432  pConfig->LoadS57Config();
8433  ps52plib->SetPLIBColorScheme(global_color_scheme, ChartCtxFactory());
8434 
8435  if (gFrame){
8436  ps52plib->SetDisplayWidth(g_monitor_info[g_current_monitor].width);
8437  ps52plib->SetPPMM(g_BasePlatform->GetDisplayDPmm());
8438  double dip_factor = g_BasePlatform->GetDisplayDIPMult(gFrame);
8439  ps52plib->SetDIPFactor(dip_factor);
8440  ps52plib->SetContentScaleFactor(OCPN_GetDisplayContentScaleFactor());
8441  }
8442 
8443  // preset S52 PLIB scale factors
8444  ps52plib->SetScaleFactorExp(g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor));
8445  ps52plib-> SetScaleFactorZoomMod(g_chart_zoom_modifier_vector);
8446 
8447 #ifdef ocpnUSE_GL
8448 
8449  // Setup PLIB OpenGL options, if enabled
8450  extern bool g_b_EnableVBO;
8451  extern GLenum g_texture_rectangle_format;
8452  extern OCPN_GLCaps *GL_Caps;
8453 
8454  if (g_bopengl){
8455  if(GL_Caps){
8456  wxString renderer = wxString(GL_Caps->Renderer.c_str());
8457  ps52plib->SetGLRendererString(renderer);
8458  }
8459 
8460  ps52plib->SetGLOptions(
8461  glChartCanvas::s_b_useStencil, glChartCanvas::s_b_useStencilAP,
8462  glChartCanvas::s_b_useScissorTest, glChartCanvas::s_b_useFBO,
8463  g_b_EnableVBO, g_texture_rectangle_format, 1, 1);
8464 
8465  }
8466 #endif
8467 
8468  } else {
8469  wxLogMessage(
8470  _T(" S52PLIB Initialization failed, disabling Vector charts."));
8471  delete ps52plib;
8472  ps52plib = NULL;
8473  }
8474 }
8475 
8476 class ParseENCWorkerThread : public wxThread {
8477 public:
8478  ParseENCWorkerThread(wxString filename, Extent &ext, int scale)
8479  : wxThread(wxTHREAD_JOINABLE) {
8480  m_filename = filename;
8481  m_ext = ext;
8482  m_scale = scale;
8483  Create();
8484  }
8485 
8486  void *Entry() {
8487  // ChartBase *pchart = ChartData->OpenChartFromDB(m_filename,
8488  // FULL_INIT); ChartData->DeleteCacheChart(pchart);
8489  s57chart *newChart = new s57chart;
8490 
8491  newChart->SetNativeScale(m_scale);
8492  newChart->SetFullExtent(m_ext);
8493 
8494  newChart->FindOrCreateSenc(m_filename);
8495  delete newChart;
8496  return 0;
8497  }
8498 
8499  wxString m_filename;
8500  Extent m_ext;
8501  int m_scale;
8502 };
8503 
8504 // begin duplicated code
8505 static double chart_dist(int index) {
8506  double d;
8507  float clon;
8508  float clat;
8509  const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
8510  // if the chart contains ownship position set the distance to 0
8511  if (cte.GetBBox().Contains(gLat, gLon))
8512  d = 0.;
8513  else {
8514  // find the nearest edge
8515  double t;
8516  clon = (cte.GetLonMax() + cte.GetLonMin()) / 2;
8517  d = DistGreatCircle(cte.GetLatMax(), clon, gLat, gLon);
8518  t = DistGreatCircle(cte.GetLatMin(), clon, gLat, gLon);
8519  if (t < d) d = t;
8520 
8521  clat = (cte.GetLatMax() + cte.GetLatMin()) / 2;
8522  t = DistGreatCircle(clat, cte.GetLonMin(), gLat, gLon);
8523  if (t < d) d = t;
8524  t = DistGreatCircle(clat, cte.GetLonMax(), gLat, gLon);
8525  if (t < d) d = t;
8526  }
8527  return d;
8528 }
8529 
8530 WX_DEFINE_SORTED_ARRAY_INT(int, MySortedArrayInt);
8531 static int CompareInts(int n1, int n2) {
8532  double d1 = chart_dist(n1);
8533  double d2 = chart_dist(n2);
8534  return (int)(d1 - d2);
8535 }
8536 
8537 class compress_target {
8538 public:
8539  wxString chart_path;
8540  double distance;
8541 };
8542 
8543 WX_DECLARE_OBJARRAY(compress_target, ArrayOfCompressTargets);
8544 WX_DEFINE_OBJARRAY(ArrayOfCompressTargets);
8545 
8546 #include <wx/arrimpl.cpp>
8547 // end duplicated code
8548 
8549 void ParseAllENC(wxWindow *parent) {
8550  MySortedArrayInt idx_sorted_by_distance(CompareInts);
8551 
8552  // Building the cache may take a long time....
8553  // Be a little smarter.
8554  // Build a sorted array of chart database indices, sorted on distance from the
8555  // ownship currently. This way, a user may build a few chart SENCs for
8556  // immediate use, then "skip" or "cancel"out on the rest until later.
8557  int count = 0;
8558  for (int i = 0; i < ChartData->GetChartTableEntries(); i++) {
8559  /* skip if not ENC */
8560  const ChartTableEntry &cte = ChartData->GetChartTableEntry(i);
8561  if (CHART_TYPE_S57 != cte.GetChartType()) continue;
8562 
8563  idx_sorted_by_distance.Add(i);
8564  count++;
8565  }
8566 
8567  if (count == 0) return;
8568 
8569  wxLogMessage(wxString::Format(_T("ParseAllENC() count = %d"), count));
8570 
8571  // Build another array of sorted compression targets.
8572  // We need to do this, as the chart table will not be invariant
8573  // after the compression threads start, so our index array will be invalid.
8574 
8575  ArrayOfCompressTargets ct_array;
8576  for (unsigned int j = 0; j < idx_sorted_by_distance.GetCount(); j++) {
8577  int i = idx_sorted_by_distance[j];
8578 
8579  const ChartTableEntry &cte = ChartData->GetChartTableEntry(i);
8580  double distance = chart_dist(i);
8581 
8582  wxString filename(cte.GetpFullPath(), wxConvUTF8);
8583 
8584  compress_target *pct = new compress_target;
8585  pct->distance = distance;
8586  pct->chart_path = filename;
8587 
8588  ct_array.push_back(pct);
8589  }
8590 
8591  int thread_count = 0;
8592  ParseENCWorkerThread **workers = NULL;
8593 
8594  extern int g_nCPUCount;
8595  if (g_nCPUCount > 0)
8596  thread_count = g_nCPUCount;
8597  else
8598  thread_count = wxThread::GetCPUCount();
8599 
8600  if (thread_count < 1) {
8601  // obviously there's at least one CPU!
8602  thread_count = 1;
8603  }
8604 
8605  // thread_count = 1; // for now because there is a problem with more than 1
8606 
8607 #if 0
8608  workers = new ParseENCWorkerThread*[thread_count];
8609  for(int t = 0; t < thread_count; t++)
8610  workers[t] = NULL;
8611 #endif
8612 
8613  wxGenericProgressDialog *prog = nullptr;
8614  wxSize csz = GetOCPNCanvasWindow()->GetClientSize();
8615 
8616  if (1) {
8617  long style = wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
8618  wxPD_REMAINING_TIME | wxPD_CAN_SKIP;
8619 
8620  prog = new wxGenericProgressDialog();
8621  wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
8622  prog->SetFont(*qFont);
8623 
8624  prog->Create(_("OpenCPN ENC Prepare"),
8625  _T("Longgggggggggggggggggggggggggggg"), count + 1, parent,
8626  style);
8627 
8628  // make wider to show long filenames
8629  // wxSize sz = prog->GetSize();
8630  // sz.x = csz.x * 8 / 10;
8631  // prog->SetSize( sz );
8632 
8633  DimeControl(prog);
8634  #ifdef __WXOSX__
8635  prog->ShowWindowModal();
8636  #else
8637  prog->Show();
8638  #endif
8639  }
8640 
8641  // parse targets
8642  bool skip = false;
8643  count = 0;
8644  for (unsigned int j = 0; j < ct_array.size(); j++) {
8645  wxString filename = ct_array[j].chart_path;
8646  double distance = ct_array[j].distance;
8647  int index = ChartData->FinddbIndex(filename);
8648  if (index < 0) continue;
8649  const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
8650  Extent ext;
8651  ext.NLAT = cte.GetLatMax();
8652  ext.SLAT = cte.GetLatMin();
8653  ext.WLON = cte.GetLonMin();
8654  ext.ELON = cte.GetLonMax();
8655 
8656  int scale = cte.GetScale();
8657 
8658  wxString msg;
8659  msg.Printf(_("Distance from Ownship: %4.0f NMi"), distance);
8660 
8661  count++;
8662  if (wxThread::IsMain()) {
8663  if (prog) {
8664  wxSize sz = prog->GetSize();
8665  if (sz.x > 600) {
8666  msg += _T(" Chart:");
8667  msg += filename;
8668  }
8669  prog->Update(count, msg, &skip);
8670 #ifndef __WXMSW__
8671  prog->Raise();
8672 #endif
8673  }
8674  if (skip) break;
8675  }
8676 
8677 #if 1
8678  if (ps52plib) {
8679  s57chart *newChart = new s57chart;
8680 
8681  newChart->SetNativeScale(scale);
8682  newChart->SetFullExtent(ext);
8683  newChart->DisableBackgroundSENC();
8684 
8685  newChart->FindOrCreateSenc(filename,
8686  false); // no progress dialog required
8687  delete newChart;
8688 
8689  if (wxThread::IsMain()) {
8690  msg.Printf(_("ENC Completed."));
8691  if (prog) {
8692  prog->Update(count, msg, &skip);
8693 #ifndef __WXMSW__
8694  prog->Raise();
8695 #endif
8696  }
8697  if (skip) break;
8698  }
8699  }
8700 
8701 #else
8702  for (int t = 0;; t = (t + 1) % thread_count) {
8703  if (!workers[t]) {
8704  workers[t] = new ParseENCWorkerThread(filename);
8705  workers[t]->Run();
8706  break;
8707  }
8708 
8709  if (!workers[t]->IsAlive()) {
8710  workers[t]->Wait();
8711  delete workers[t];
8712  workers[t] = NULL;
8713  }
8714  if (t == 0) {
8715  // ::wxYield(); // allow ChartCanvas main
8716  // message loop to run
8717  wxThread::Sleep(1); /* wait for a worker to finish */
8718  }
8719  }
8720 #endif
8721 
8722 #if defined(__WXMSW__) || defined(__WXOSX__)
8723  ::wxSafeYield();
8724 #endif
8725  }
8726 
8727 #if 0
8728  /* wait for workers to finish, and clean up after then */
8729  for(int t = 0; t<thread_count; t++) {
8730  if(workers[t]) {
8731  workers[t]->Wait();
8732  delete workers[t];
8733  }
8734  }
8735  delete [] workers;
8736 #endif
8737 
8738  delete prog;
8739 }
Global state for AIS decoder.
bool Create(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &caption=_("Object Query"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=AIS_TARGET_QUERY_STYLE)
Creation.
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
const void Notify()
Notify all listeners, no data supplied.
Definition: Layer.h:32
static void ReleaseInstance()
Release Instance.
static LocalServerApi & GetInstance()
Class MarkInfoDef.
Definition: MarkInfo.h:203
Definition: ocpn_app.h:45
void OnFrameTimer1(wxTimerEvent &event)
void InitApiListeners()
Setup handling of events from the local ipc/dbus API.
bool Active() const
Return true if log is visible i.
void Add(const wxString &s)
Add an formatted string to log output.
void Init(const KeyProvider &kp, std::function< void(ObservedEvt &ev)> action)
Initiate an object yet not listening.
Definition: observable.h:227
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Definition: observable.cpp:98
Adds a std::shared<void> element to wxCommandEvent.
Definition: ocpn_plugin.h:1652
Data for a loaded plugin, including dl-loaded library.
Definition: plugin_loader.h:99
int m_cap_flag
PlugIn Capabilities descriptor.
Definition: plugin_loader.h:75
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
Definition: route.h:75
EventVar on_routes_update
Notified when list of routes is updated (no data in event)
Definition: routeman.h:196
EventVar evt_resume
Notified when resuming from hibernate.
Definition: sys_events.h:38
Definition: tcmgr.h:86
Class TrackPropDlg.
Definition: TrackPropDlg.h:93
Definition: track.h:78
Definition: about.h:48
Global variables reflecting command line options and arguments.
Hooks int gui available in model.
General purpose GUI support.
Handle dialog reporting plugin load errors.
The local API has a server side handling commands and a client part issuing commands.
wxDEFINE_EVENT(REST_IO_EVT, ObservedEvt)
Event from IO thread to main.
Definition: Quilt.cpp:867
void DestroyDeviceNotFoundDialogs()
Destroy all open "Device not found" dialog windows.
Access checks for comm devices and dongle.