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