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