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