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