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