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