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