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