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