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