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 = 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 wxJSONValue root;
2725 wxJSONReader reader;
2726
2727 int numErrors = reader.Parse(event.GetString(), &root);
2728 if (numErrors == 0) {
2729 if (root["MessageID"].IsString()) {
2730 wxString MsgID = root["MessageID"].AsString();
2731 SendPluginMessage(MsgID, event.GetString()); // Send to all PlugIns
2732 }
2733 }
2734
2735 break;
2736 }
2737
2738 case ID_DENSITY:
2739 case ID_RMINUS:
2740 case ID_RPLUS: {
2741 if (g_iENCToolbar) {
2742 g_iENCToolbar->OnToolLeftClick(event);
2743 }
2744 break;
2745 }
2746
2747 default: {
2748 // Look for PlugIn tools
2749 // If found, make the callback.
2750 // TODO Modify this to allow multiple tools per plugin
2751 if (g_pi_manager) {
2752 HideTbarTooltip();
2753
2754 ArrayOfPlugInToolbarTools tool_array =
2755 g_pi_manager->GetPluginToolbarToolArray();
2756 for (unsigned int i = 0; i < tool_array.size(); i++) {
2757 PlugInToolbarToolContainer *pttc = tool_array[i];
2758 if (event.GetId() == pttc->id) {
2759 if (pttc->m_pplugin)
2760 pttc->m_pplugin->OnToolbarToolCallback(pttc->id);
2761 return; // required to prevent event.Skip() being called
2762 }
2763 }
2764 }
2765
2766 // If we didn't handle the event, allow it to bubble up to other handlers.
2767 // This is required for the system menu items (Hide, etc.) on OS X to
2768 // work. This must only be called if we did NOT handle the event,
2769 // otherwise it stops the menu items from working on Windows.
2770 event.Skip();
2771
2772 break;
2773 }
2774
2775 } // switch
2776
2777 // Finally, force a refresh of the main toolbar
2778 if (g_MainToolbar) g_MainToolbar->Realize();
2779}
2780
2781bool MyFrame::SetGlobalToolbarViz(bool viz) {
2782 bool viz_now = g_bmasterToolbarFull;
2783
2784 HideTbarTooltip();
2785 wxString tip = _("Show Toolbar");
2786 if (viz) {
2787 tip = _("Hide Toolbar");
2788 if (g_MainToolbar->GetToolbar())
2789 g_MainToolbar->GetToolbar()->SetToolShortHelp(ID_MASTERTOGGLE, tip);
2790 }
2791
2792 bool toggle = false;
2793 if (viz && !g_bmasterToolbarFull)
2794 toggle = true;
2795
2796 else if (!viz && g_bmasterToolbarFull)
2797 toggle = true;
2798
2799 if (toggle) {
2800 g_bmasterToolbarFull = !g_bmasterToolbarFull;
2801
2802#ifdef __WXOSX__
2803 if (g_bmasterToolbarFull)
2804 m_nMasterToolCountShown =
2805 g_MainToolbar->GetToolCount() -
2806 1; // TODO disable animation on OSX. Maybe use fade effect?
2807 else
2808 m_nMasterToolCountShown = 2;
2809#else
2810 m_nMasterToolCountShown =
2811 g_MainToolbar->GetToolShowCount(); // Current state
2812#endif
2813 ToolbarAnimateTimer.Start(10, wxTIMER_ONE_SHOT);
2814 }
2815
2816 return viz_now;
2817}
2818
2819void MyFrame::ScheduleReloadCharts() {
2820 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2821 evt.SetId(ID_RELOAD_CHARTS);
2822 GetEventHandler()->AddPendingEvent(evt);
2823}
2824
2825void MyFrame::ScheduleDeleteSettingsDialog() {
2826 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2827 evt.SetId(ID_SETTINGS_DELETE);
2828 GetEventHandler()->AddPendingEvent(evt);
2829}
2830
2831void MyFrame::ScheduleSettingsDialog() {
2832 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2833 evt.SetId(ID_SETTINGS);
2834 GetEventHandler()->AddPendingEvent(evt);
2835}
2836
2837void MyFrame::ScheduleSettingsDialogNew() {
2838 wxCommandEvent evt(wxEVT_COMMAND_MENU_SELECTED);
2839 evt.SetId(ID_SETTINGS_NEW);
2840 GetEventHandler()->AddPendingEvent(evt);
2841}
2842
2843void MyFrame::ScheduleReconfigAndSettingsReload(bool reload, bool new_dialog) {
2844 UpdateCanvasConfigDescriptors();
2845
2846 if ((g_canvasConfig > 0) && (last_canvasConfig == 0))
2847 CreateCanvasLayout(true);
2848 else
2849 CreateCanvasLayout();
2850 SendSizeEvent();
2851 g_pauimgr->Update();
2852
2853 ConfigureStatusBar();
2854 wxSize lastOptSize = options_lastWindowSize;
2855 SendSizeEvent();
2856
2857 BuildMenuBar();
2858 SendSizeEvent();
2859 options_lastWindowSize = lastOptSize;
2860
2861 if (reload) {
2862 if (new_dialog)
2863 ScheduleSettingsDialogNew();
2864 else
2865 ScheduleSettingsDialog();
2866 }
2867 // Trying to reload the previously displayed chart by name as saved in
2868 // pathArray Also, restoring the previous chart VPScale, if possible
2869 // ..For each canvas...
2870 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2871 ChartCanvas *cc = g_canvasArray.Item(i);
2872 if (cc) {
2873 int index_hint = -1;
2874 if (i < pathArray.GetCount())
2875 index_hint = ChartData->FinddbIndex(pathArray.Item(i));
2876 cc->canvasChartsRefresh(index_hint);
2877 if (index_hint != -1) cc->SetVPScale(restoreScale[i]);
2878 }
2879 }
2880}
2881
2882ChartCanvas *MyFrame::GetFocusCanvas() {
2883 if ((g_canvasConfig != 0) && g_focusCanvas) // multi-canvas?
2884 return g_focusCanvas;
2885 else
2886 return GetPrimaryCanvas();
2887}
2888
2889void MyFrame::OnToolbarAnimateTimer(wxTimerEvent &event) {
2890 if (!g_MainToolbar) return;
2891 if (g_bmasterToolbarFull) {
2892#ifndef OCPN_TOOLBAR_ANIMATE
2893 m_nMasterToolCountShown = (int)g_MainToolbar->GetToolCount();
2894#endif
2895
2896 if (m_nMasterToolCountShown < (int)g_MainToolbar->GetToolCount()) {
2897 m_nMasterToolCountShown++;
2898 g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2899 g_MainToolbar->Realize();
2900 g_MainToolbar->RefreshToolbar();
2901
2902 ToolbarAnimateTimer.Start(20, wxTIMER_ONE_SHOT);
2903 } else {
2904 g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2905 g_MainToolbar->GetToolbar()->InvalidateBitmaps();
2906 g_MainToolbar->Realize();
2907 g_MainToolbar->RefreshToolbar();
2908 }
2909 } else {
2910#ifndef OCPN_TOOLBAR_ANIMATE
2911 m_nMasterToolCountShown = 1;
2912#endif
2913 if (m_nMasterToolCountShown > 1) {
2914 m_nMasterToolCountShown--;
2915 g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2916 g_MainToolbar->Realize();
2917 g_MainToolbar->RefreshToolbar();
2918 ToolbarAnimateTimer.Start(10, wxTIMER_ONE_SHOT);
2919 } else {
2920 g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
2921 g_MainToolbar->GetToolbar()->InvalidateBitmaps();
2922 g_MainToolbar->Realize();
2923 g_MainToolbar->RefreshToolbar();
2924 }
2925 }
2926}
2927
2928void MyFrame::InvalidateAllGL() {
2929#ifdef ocpnUSE_GL
2930 // For each canvas
2931 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2932 ChartCanvas *cc = g_canvasArray.Item(i);
2933 if (cc) {
2934 cc->InvalidateGL();
2935 cc->Refresh();
2936 }
2937 }
2938#endif
2939}
2940
2941void MyFrame::RefreshAllCanvas(bool bErase) {
2942 // For each canvas
2943 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2944 ChartCanvas *cc = g_canvasArray.Item(i);
2945 if (cc) {
2946 cc->Refresh(bErase);
2947 }
2948 }
2949}
2950
2951void MyFrame::setStringVP(wxString VPS) {
2952 ChartCanvas *cc = GetPrimaryCanvas();
2953
2954 if (!cc) return;
2955
2956 wxStringTokenizer tkz(VPS, ";");
2957
2958 wxString token = tkz.GetNextToken();
2959 double lat = gLat;
2960 token.ToDouble(&lat);
2961
2962 token = tkz.GetNextToken();
2963 double lon = gLon;
2964 token.ToDouble(&lon);
2965
2966 token = tkz.GetNextToken();
2967 double scale_ppm = cc->GetVP().view_scale_ppm;
2968 token.ToDouble(&scale_ppm);
2969
2970 cc->SetViewPoint(lat, lon, scale_ppm, 0, cc->GetVPRotation());
2971}
2972
2973void MyFrame::DoSettingsNew() {
2974 delete g_options;
2975 g_options = nullptr;
2976
2977 DoSettings();
2978}
2979
2980void MyFrame::DoSettings() {
2981 LoadS57();
2982 DoOptionsDialog();
2983
2984 // Apply various system settings
2985 ApplyGlobalSettings(true);
2986
2987 // ..For each canvas...
2988 bool b_loadHarmonics = false;
2989 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
2990 ChartCanvas *cc = g_canvasArray.Item(i);
2991 if (cc) {
2992 if (cc->GetbShowCurrent() || cc->GetbShowTide()) b_loadHarmonics = true;
2993 }
2994 }
2995 if (b_loadHarmonics) LoadHarmonics();
2996
2997 // The chart display options may have changed, especially on S57 ENC,
2998 // So, flush the cache and redraw
2999 ReloadAllVP();
3000}
3001
3002void MyFrame::ToggleChartBar(ChartCanvas *cc) {
3003 g_bShowChartBar = !g_bShowChartBar;
3004
3005 if (g_bShowChartBar) cc->m_brepaint_piano = true;
3006
3007 cc->ReloadVP(); // needed to set VP.pix_height
3008 Refresh();
3009
3010 if (g_bShowChartBar) {
3011 DoChartUpdate();
3012 UpdateControlBar(cc);
3013 }
3014
3015 SetMenubarItemState(ID_MENU_UI_CHARTBAR, g_bShowChartBar);
3016}
3017
3018void MyFrame::ToggleColorScheme() {
3019 static bool lastIsNight;
3020 ColorScheme s = user_colors::GetColorScheme();
3021 int is = (int)s;
3022 is++;
3023 if (lastIsNight && is == 3) // Back from step 3
3024 {
3025 is = 1;
3026 lastIsNight = false;
3027 } // Goto to Day
3028 if (lastIsNight) is = 2; // Back to Dusk on step 3
3029 if (is == 3) lastIsNight = true; // Step 2 Night
3030 s = (ColorScheme)is;
3031 if (s == N_COLOR_SCHEMES) s = GLOBAL_COLOR_SCHEME_RGB;
3032
3033 SetAndApplyColorScheme(s);
3034}
3035
3036void MyFrame::ToggleFullScreen() {
3037 bool to = !IsFullScreen();
3038
3039#ifdef __WXOSX__
3040 ShowFullScreen(to);
3041#else
3042 long style = wxFULLSCREEN_NOBORDER | wxFULLSCREEN_NOCAPTION;
3043 ; // | wxFULLSCREEN_NOMENUBAR;
3044 ShowFullScreen(to, style);
3045#endif
3046
3047 UpdateAllToolbars(global_color_scheme);
3048 // SurfaceAllCanvasToolbars();
3049 UpdateControlBar(GetPrimaryCanvas());
3050 Layout();
3051 TriggerRecaptureTimer();
3052}
3053
3054void MyFrame::ActivateMOB() {
3055 // The MOB point
3056 wxDateTime mob_time = wxDateTime::Now();
3057 wxString mob_label(_("MAN OVERBOARD"));
3058 mob_label += _(" on ");
3059 mob_label += ocpn::toUsrDateTimeFormat(mob_time);
3060
3061 RoutePoint *pWP_MOB =
3062 new RoutePoint(gLat, gLon, "mob", mob_label, wxEmptyString);
3063 pWP_MOB->SetShared(true);
3064 pWP_MOB->m_bIsolatedMark = true;
3065 pWP_MOB->SetWaypointArrivalRadius(
3066 -1.0); // Negative distance is code to signal "Never Arrive"
3067 pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
3068 pSelect->AddSelectableRoutePoint(gLat, gLon, pWP_MOB);
3069 NavObj_dB::GetInstance().InsertRoutePoint(pWP_MOB);
3070
3071 if (bGPSValid && !std::isnan(gCog) && !std::isnan(gSog)) {
3072 // Create a point that is one mile along the present course
3073 double zlat, zlon;
3074 ll_gc_ll(gLat, gLon, gCog, 1.0, &zlat, &zlon);
3075
3076 RoutePoint *pWP_src =
3077 new RoutePoint(zlat, zlon, g_default_wp_icon,
3078 wxString(_("1.0 NM along COG")), wxEmptyString);
3079 pSelect->AddSelectableRoutePoint(zlat, zlon, pWP_src);
3080
3081 Route *temp_route = new Route();
3082 pRouteList->push_back(temp_route);
3083
3084 temp_route->AddPoint(pWP_src);
3085 temp_route->AddPoint(pWP_MOB);
3086
3087 pSelect->AddSelectableRouteSegment(gLat, gLon, zlat, zlon, pWP_src, pWP_MOB,
3088 temp_route);
3089
3090 temp_route->m_RouteNameString = _("Temporary MOB Route");
3091 temp_route->m_RouteStartString = _("Assumed 1 Mile Point");
3092 ;
3093 temp_route->m_RouteEndString = mob_label;
3094
3095 temp_route->m_bDeleteOnArrival = false;
3096
3097 temp_route->SetRouteArrivalRadius(-1.0); // never arrives
3098
3099 if (g_pRouteMan->GetpActiveRoute()) g_pRouteMan->DeactivateRoute();
3100 g_pRouteMan->ActivateRoute(temp_route, pWP_MOB);
3101
3102 wxJSONValue v;
3103 v["GUID"] = temp_route->m_GUID;
3104 wxString msg_id("OCPN_MAN_OVERBOARD");
3105 SendJSONMessageToAllPlugins(msg_id, v);
3106 }
3107
3108 if (RouteManagerDialog::getInstanceFlag()) {
3109 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3110 pRouteManagerDialog->UpdateRouteListCtrl();
3111 pRouteManagerDialog->UpdateWptListCtrl();
3112 }
3113 }
3114
3115 InvalidateAllGL();
3116 RefreshAllCanvas(false);
3117
3118 wxString mob_message(_("MAN OVERBOARD"));
3119 mob_message += _(" Time: ");
3120 mob_message += ocpn::toUsrDateTimeFormat(mob_time);
3121 mob_message += _(" Position: ");
3122 mob_message += toSDMM(1, gLat);
3123 mob_message += " ";
3124 mob_message += toSDMM(2, gLon);
3125 wxLogMessage(mob_message);
3126}
3127void MyFrame::TrackOn() {
3128 g_bTrackActive = true;
3130
3131 g_TrackList.push_back(g_pActiveTrack);
3132 NavObj_dB::GetInstance().InsertTrack(g_pActiveTrack);
3133 g_pActiveTrack->Start();
3134
3135 // The main toolbar may still be NULL here, and we will do nothing...
3136 SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
3137 if (g_MainToolbar)
3138 g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Disable Tracking"));
3139
3140 SetMenubarItemState(ID_MENU_NAV_TRACK, g_bTrackActive);
3141
3142#ifdef __ANDROID__
3143 androidSetTrackTool(true);
3144#endif
3145
3146 if (RouteManagerDialog::getInstanceFlag()) {
3147 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3148 pRouteManagerDialog->UpdateTrkListCtrl();
3149 pRouteManagerDialog->UpdateRouteListCtrl();
3150 }
3151 }
3152
3153 wxJSONValue v;
3154 wxString name = g_pActiveTrack->GetName();
3155 if (name.IsEmpty()) {
3156 TrackPoint *tp = g_pActiveTrack->GetPoint(0);
3157 if (tp->GetCreateTime().IsValid())
3158 name = tp->GetCreateTime().FormatISODate() + " " +
3159 tp->GetCreateTime().FormatISOTime();
3160 else
3161 name = _("(Unnamed Track)");
3162 }
3163 v["Name"] = name;
3164 v["GUID"] = g_pActiveTrack->m_GUID;
3165 wxString msg_id("OCPN_TRK_ACTIVATED");
3166 SendJSONMessageToAllPlugins(msg_id, v);
3167 g_FlushNavobjChangesTimeout =
3168 30; // Every thirty seconds, consider flushing navob changes
3169}
3170
3171Track *MyFrame::TrackOff(bool do_add_point) {
3172 Track *return_val = g_pActiveTrack;
3173
3174 if (g_pActiveTrack) {
3175 wxJSONValue v;
3176 wxString msg_id("OCPN_TRK_DEACTIVATED");
3177 v["GUID"] = g_pActiveTrack->m_GUID;
3178 SendJSONMessageToAllPlugins(msg_id, v);
3179
3180 g_pActiveTrack->Stop(do_add_point);
3181
3182 if (g_pActiveTrack->GetnPoints() < 2) {
3183 NavObj_dB::GetInstance().DeleteTrack(g_pActiveTrack);
3184 RoutemanGui(*g_pRouteMan).DeleteTrack(g_pActiveTrack);
3185 return_val = NULL;
3186 } else {
3187 if (g_bTrackDaily) {
3188 Track *pExtendTrack = g_pActiveTrack->DoExtendDaily();
3189 if (pExtendTrack) {
3190 NavObj_dB::GetInstance().DeleteTrack(g_pActiveTrack);
3191 RoutemanGui(*g_pRouteMan).DeleteTrack(g_pActiveTrack);
3192 NavObj_dB::GetInstance().UpdateTrack(pExtendTrack);
3193 return_val = pExtendTrack;
3194 }
3195 }
3196 }
3197 g_pActiveTrack = NULL;
3198 }
3199
3200 g_bTrackActive = false;
3201
3202 if (RouteManagerDialog::getInstanceFlag()) {
3203 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3204 pRouteManagerDialog->UpdateTrkListCtrl();
3205 pRouteManagerDialog->UpdateRouteListCtrl();
3206 }
3207 }
3208
3209 SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
3210 if (g_MainToolbar)
3211 g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Enable Tracking"));
3212 SetMenubarItemState(ID_MENU_NAV_TRACK, g_bTrackActive);
3213
3214#ifdef __ANDROID__
3215 androidSetTrackTool(false);
3216#endif
3217
3218 // Invalidate the rotate tiem
3219 m_target_rotate_time = wxInvalidDateTime;
3220
3221 return return_val;
3222}
3223
3224void MyFrame::InitializeTrackRestart() {
3225 if (!g_bTrackDaily) return;
3226 if (m_target_rotate_time.IsValid()) return;
3227
3228 int rotate_at = 0;
3229 switch (g_track_rotate_time_type) {
3230 case TIME_TYPE_LMT:
3231 rotate_at = g_track_rotate_time + wxRound(gLon * 3600. / 15.);
3232 break;
3233 case TIME_TYPE_COMPUTER:
3234 rotate_at = g_track_rotate_time;
3235 break;
3236 case TIME_TYPE_UTC:
3237 int utc_offset =
3238 wxDateTime::Now().GetTicks() - wxDateTime::Now().ToUTC().GetTicks();
3239 rotate_at = g_track_rotate_time + utc_offset;
3240 break;
3241 }
3242 if (rotate_at > 86400)
3243 rotate_at -= 86400;
3244 else if (rotate_at < 0)
3245 rotate_at += 86400;
3246
3247 wxTimeSpan rotate_seconds = wxTimeSpan(0, 0, rotate_at);
3248 m_target_rotate_time = wxDateTime::Today() + rotate_seconds;
3249
3250 // Avoid restarting immediately
3251 if (wxDateTime::Now().IsLaterThan(m_target_rotate_time)) {
3252 m_target_rotate_time += wxTimeSpan(24); // tomorrow, same time.
3253 }
3254}
3255
3256bool MyFrame::ShouldRestartTrack() {
3257 if (!g_pActiveTrack || !g_bTrackDaily) return false;
3258 InitializeTrackRestart();
3259
3260 if (wxDateTime::Now().IsLaterThan(m_target_rotate_time)) {
3261 m_target_rotate_time += wxTimeSpan(24); // tomorrow, same time.
3262 return true;
3263 }
3264 return false;
3265}
3266
3267void MyFrame::TrackDailyRestart() {
3268 if (!g_pActiveTrack) return;
3269 Track *pPreviousTrack = TrackOff(true);
3270 TrackOn();
3271
3272 // Set the restarted track's current state such that the current track
3273 // point's attributes match the attributes of the last point of the track
3274 // that was just stopped at midnight.
3275
3276 if (pPreviousTrack) {
3277 TrackPoint *pMidnightPoint = pPreviousTrack->GetLastPoint();
3278 g_pActiveTrack->AdjustCurrentTrackPoint(pMidnightPoint);
3279 }
3280
3281 if (RouteManagerDialog::getInstanceFlag()) {
3282 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
3283 pRouteManagerDialog->UpdateTrkListCtrl();
3284 pRouteManagerDialog->UpdateRouteListCtrl();
3285 }
3286 }
3287}
3288
3289void MyFrame::SetUpMode(ChartCanvas *cc, int mode) {
3290 if (cc) {
3291 cc->SetUpMode(mode);
3292
3293 SetMenubarItemState(ID_MENU_CHART_COGUP, mode == COURSE_UP_MODE);
3294 SetMenubarItemState(ID_MENU_CHART_NORTHUP, mode == NORTH_UP_MODE);
3295 SetMenubarItemState(ID_MENU_CHART_HEADUP, mode == HEAD_UP_MODE);
3296
3297 if (m_pMenuBar)
3298 m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
3299 }
3300}
3301
3302void MyFrame::ToggleENCText(ChartCanvas *cc) {
3303 cc->SetShowENCText(!cc->GetShowENCText());
3304
3305 SetMenubarItemState(ID_MENU_ENC_TEXT, cc->GetShowENCText());
3306
3307 // if(g_pi_manager)
3308 // g_pi_manager->SendConfigToAllPlugIns();
3309
3310 ReloadAllVP();
3311}
3312
3313void MyFrame::SetENCDisplayCategory(ChartCanvas *cc, enum _DisCat nset) {
3314 if (ps52plib) {
3315 if (cc) {
3316 cc->SetENCDisplayCategory(nset);
3317
3318 UpdateGlobalMenuItems();
3319
3320 /* if(g_pi_manager)
3321 g_pi_manager->SendConfigToAllPlugIns();
3322 */
3323 ReloadAllVP();
3324 }
3325 }
3326}
3327
3328void MyFrame::ToggleSoundings(ChartCanvas *cc) {
3329 cc->SetShowENCDepth(!cc->GetShowENCDepth());
3330
3331 SetMenubarItemState(ID_MENU_ENC_SOUNDINGS, cc->GetShowENCDepth());
3332
3333 // if(g_pi_manager)
3334 // g_pi_manager->SendConfigToAllPlugIns();
3335
3336 ReloadAllVP();
3337}
3338
3339bool MyFrame::ToggleLights(ChartCanvas *cc) {
3340 cc->SetShowENCLights(!cc->GetShowENCLights());
3341
3342 SetMenubarItemState(ID_MENU_ENC_LIGHTS, cc->GetShowENCLights());
3343
3344 if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns(true);
3345
3346 ReloadAllVP();
3347
3348 return true;
3349}
3350
3351#if 0
3352void MyFrame::ToggleRocks( void )
3353{
3354 if( ps52plib ) {
3355 int vis = 0;
3356 // Need to loop once for UWTROC, which is our "master", then for
3357 // other categories, since order is unknown?
3358 for( unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount(); iPtr++ ) {
3359 OBJLElement *pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3360 if( !strncmp( pOLE->OBJLName, "UWTROC", 6 ) ) {
3361 pOLE->nViz = !pOLE->nViz;
3362 vis = pOLE->nViz;
3363 }
3364 }
3365 for( unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount(); iPtr++ ) {
3366 OBJLElement *pOLE = (OBJLElement *) ( ps52plib->pOBJLArray->Item( iPtr ) );
3367 if( !strncmp( pOLE->OBJLName, "OBSTRN", 6 ) ) {
3368 pOLE->nViz = vis;
3369 }
3370 if( !strncmp( pOLE->OBJLName, "WRECKS", 6 ) ) {
3371 pOLE->nViz = vis;
3372 }
3373 }
3374 ps52plib->GenerateStateHash();
3375 ReloadAllVP();
3376 }
3377}
3378#endif
3379
3380void MyFrame::ToggleAnchor(ChartCanvas *cc) {
3381 cc->SetShowENCAnchor(!cc->GetShowENCAnchor());
3382
3383 SetMenubarItemState(ID_MENU_ENC_ANCHOR, cc->GetShowENCAnchor());
3384
3385 if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns();
3386
3387 ReloadAllVP();
3388}
3389
3390void MyFrame::ToggleDataQuality(ChartCanvas *cc) {
3391 cc->SetShowENCDataQual(!cc->GetShowENCDataQual());
3392
3393 SetMenubarItemState(ID_MENU_ENC_DATA_QUALITY, cc->GetShowENCDataQual());
3394
3395 if (g_pi_manager) g_pi_manager->SendS52ConfigToAllPlugIns();
3396
3397 ReloadAllVP();
3398}
3399
3400void MyFrame::TogglebFollow(ChartCanvas *cc) {
3401 if (!cc->m_bFollow)
3402 SetbFollow(cc);
3403 else
3404 ClearbFollow(cc);
3405}
3406
3407void MyFrame::ToggleNavobjects(ChartCanvas *cc) {
3408 cc->m_bShowNavobjects = !cc->m_bShowNavobjects;
3409 SetMenubarItemState(ID_MENU_SHOW_NAVOBJECTS, cc->m_bShowNavobjects);
3410 cc->Refresh();
3411}
3412
3413void MyFrame::ToggleAISDisplay(ChartCanvas *cc) {
3414 cc->SetShowAIS(!cc->GetShowAIS());
3415 SetMenubarItemState(ID_MENU_AIS_TARGETS, cc->GetShowAIS());
3416 cc->Refresh();
3417}
3418
3419void MyFrame::ToggleAISMinimizeTargets(ChartCanvas *cc) {
3420 cc->SetAttenAIS(!cc->GetAttenAIS());
3421 SetMenubarItemState(ID_MENU_AIS_SCALED_TARGETS, cc->GetAttenAIS());
3422 cc->Refresh();
3423}
3424
3425void MyFrame::SetbFollow(ChartCanvas *cc) {
3426 JumpToPosition(cc, gLat, gLon, cc->GetVPScale());
3427 cc->m_bFollow = true;
3428
3429 // cc->SetCanvasToolbarItemState(ID_FOLLOW, true);
3430 SetMenubarItemState(ID_MENU_NAV_FOLLOW, true);
3431
3432 DoChartUpdate();
3433 cc->ReloadVP();
3434 SetChartUpdatePeriod();
3435}
3436
3437void MyFrame::ClearbFollow(ChartCanvas *cc) {
3438 // Center the screen on the GPS position, for lack of a better place
3439 vLat = gLat;
3440 vLon = gLon;
3441
3442 cc->m_bFollow = false;
3443 // cc->SetCanvasToolbarItemState(ID_FOLLOW, false);
3444 SetMenubarItemState(ID_MENU_NAV_FOLLOW, false);
3445
3446 DoChartUpdate();
3447 cc->ReloadVP();
3448 SetChartUpdatePeriod();
3449}
3450
3451void MyFrame::ToggleChartOutlines(ChartCanvas *cc) {
3452 cc->SetShowOutlines(!cc->GetShowOutlines());
3453
3454 RefreshAllCanvas(false);
3455
3456#ifdef ocpnUSE_GL // opengl renders chart outlines as part of the chart this
3457 // needs a full refresh
3458 if (g_bopengl) InvalidateAllGL();
3459#endif
3460
3461 SetMenubarItemState(ID_MENU_CHART_OUTLINES, cc->GetShowOutlines());
3462}
3463
3464void MyFrame::ToggleTestPause() { g_bPauseTest = !g_bPauseTest; }
3465
3466void MyFrame::SetMenubarItemState(int item_id, bool state) {
3467 if (m_pMenuBar) {
3468 bool enabled = m_pMenuBar->IsEnabled(item_id);
3469 m_pMenuBar->Enable(item_id, false);
3470 m_pMenuBar->Check(item_id, state);
3471 m_pMenuBar->Enable(item_id, enabled);
3472 }
3473}
3474
3475void MyFrame::SetMasterToolbarItemState(int tool_id, bool state) {
3476 if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3477 g_MainToolbar->GetToolbar()->ToggleTool(tool_id, state);
3478 g_MainToolbar->Realize();
3479 }
3480}
3481
3482void MyFrame::SetToolbarItemBitmaps(int tool_id, wxBitmap *bmp,
3483 wxBitmap *bmpRollover) {
3484 if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3485 g_MainToolbar->GetToolbar()->SetToolBitmaps(tool_id, bmp, bmpRollover);
3486 g_MainToolbar->Realize();
3487 }
3488}
3489
3490void MyFrame::SetToolbarItemSVG(int tool_id, wxString normalSVGfile,
3491 wxString rolloverSVGfile,
3492 wxString toggledSVGfile) {
3493 if (g_MainToolbar && g_MainToolbar->GetToolbar()) {
3494 g_MainToolbar->GetToolbar()->SetToolBitmapsSVG(
3495 tool_id, normalSVGfile, rolloverSVGfile, toggledSVGfile);
3496 }
3497}
3498
3499void MyFrame::ConfigureStatusBar() {
3500 // ShowDebugWindow as a wxStatusBar
3501 m_StatusBarFieldCount = g_Platform->GetStatusBarFieldCount();
3502
3503#ifdef __WXMSW__
3504 UseNativeStatusBar(false); // better for MSW, undocumented in frame.cpp
3505#endif
3506
3507 if (g_bShowStatusBar) {
3508 if (!m_pStatusBar) {
3509 m_pStatusBar =
3510 CreateStatusBar(m_StatusBarFieldCount, 0); // No wxST_SIZEGRIP needed
3511 ApplyGlobalColorSchemetoStatusBar();
3512 }
3513
3514 } else {
3515 if (m_pStatusBar) {
3516 m_pStatusBar->Destroy();
3517 m_pStatusBar = NULL;
3518 SetStatusBar(NULL);
3519 }
3520 }
3521}
3522
3523void MyFrame::ApplyGlobalSettings(bool bnewtoolbar) {
3524 ConfigureStatusBar();
3525
3526 wxSize lastOptSize = options_lastWindowSize;
3527 SendSizeEvent();
3528
3529 BuildMenuBar();
3530
3531 SendSizeEvent();
3532 options_lastWindowSize = lastOptSize;
3533
3534 if (bnewtoolbar) UpdateAllToolbars(global_color_scheme);
3535}
3536
3537void MyFrame::BuildMenuBar() {
3538 /*
3539 * Menu Bar - add or remove it if necessary, and update the state of the menu
3540 * items
3541 */
3542#ifdef __WXOSX__
3543 bool showMenuBar = true; // the menu bar is always visible in OS X
3544#else
3545 bool showMenuBar = g_bShowMenuBar; // get visibility from options
3546
3547 if (!showMenuBar &&
3548 g_bTempShowMenuBar) // allows pressing alt to temporarily show
3549 showMenuBar = true;
3550#endif
3551
3552 if (showMenuBar) {
3553 // Menu bar has some dependencies on S52 PLIB, so be sure it is loaded.
3554 LoadS57();
3555
3556 if (!m_pMenuBar) { // add the menu bar if it is enabled
3557 m_pMenuBar = new wxMenuBar();
3558 RegisterGlobalMenuItems();
3559 SetMenuBar(m_pMenuBar); // must be after RegisterGlobalMenuItems for wx
3560 // to populate the OS X App Menu correctly
3561 }
3562
3563 UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks
3564 // etc.)
3565 } else {
3566 if (m_pMenuBar) { // remove the menu bar if it is disabled
3567 SetMenuBar(NULL);
3568 m_pMenuBar->Destroy();
3569 m_pMenuBar = NULL;
3570 }
3571 }
3572}
3573
3574void MyFrame::RegisterGlobalMenuItems() {
3575 if (!m_pMenuBar) return; // if there isn't a menu bar
3576
3577 wxMenu *nav_menu = new wxMenu();
3578 nav_menu->AppendCheckItem(ID_MENU_NAV_FOLLOW,
3579 _menuText(_("Auto Follow"), "Ctrl-A"));
3580 nav_menu->AppendCheckItem(ID_MENU_NAV_TRACK, _("Enable Tracking"));
3581 nav_menu->AppendSeparator();
3582 nav_menu->AppendRadioItem(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
3583 nav_menu->AppendRadioItem(ID_MENU_CHART_COGUP, _("Course Up Mode"));
3584 nav_menu->AppendRadioItem(ID_MENU_CHART_HEADUP, _("Head Up Mode"));
3585 nav_menu->AppendSeparator();
3586#ifndef __WXOSX__
3587 nav_menu->Append(ID_MENU_ZOOM_IN, _menuText(_("Zoom In"), "+"));
3588 nav_menu->Append(ID_MENU_ZOOM_OUT, _menuText(_("Zoom Out"), "-"));
3589#else
3590 nav_menu->Append(ID_MENU_ZOOM_IN, _menuText(_("Zoom In"), "Alt-+"));
3591 nav_menu->Append(ID_MENU_ZOOM_OUT, _menuText(_("Zoom Out"), "Alt--"));
3592#endif
3593 nav_menu->AppendSeparator();
3594 nav_menu->Append(ID_MENU_SCALE_IN,
3595 _menuText(_("Larger Scale Chart"), "Ctrl-Left"));
3596 nav_menu->Append(ID_MENU_SCALE_OUT,
3597 _menuText(_("Smaller Scale Chart"), "Ctrl-Right"));
3598#ifndef __WXOSX__
3599 nav_menu->AppendSeparator();
3600 nav_menu->Append(ID_MENU_OQUIT, _menuText(_("Exit OpenCPN"), "Ctrl-Q"));
3601#endif
3602 m_pMenuBar->Append(nav_menu, _("&Navigate"));
3603
3604 wxMenu *view_menu = new wxMenu();
3605#ifndef __WXOSX__
3606 view_menu->AppendCheckItem(ID_MENU_CHART_QUILTING,
3607 _menuText(_("Enable Chart Quilting"), "Q"));
3608 view_menu->AppendCheckItem(ID_MENU_CHART_OUTLINES,
3609 _menuText(_("Show Chart Outlines"), "O"));
3610#else
3611 view_menu->AppendCheckItem(ID_MENU_CHART_QUILTING,
3612 _menuText(_("Enable Chart Quilting"), "Alt-Q"));
3613 view_menu->AppendCheckItem(ID_MENU_CHART_OUTLINES,
3614 _menuText(_("Show Chart Outlines"), "Alt-O"));
3615#endif
3616 view_menu->AppendCheckItem(ID_MENU_UI_CHARTBAR,
3617 _menuText(_("Show Chart Bar"), "Ctrl-B"));
3618
3619 view_menu->AppendSeparator();
3620#ifndef __WXOSX__
3621 view_menu->AppendCheckItem(ID_MENU_ENC_TEXT,
3622 _menuText(_("Show ENC text"), "T"));
3623 view_menu->AppendCheckItem(ID_MENU_ENC_LIGHTS,
3624 _menuText(_("Show ENC Lights"), "L"));
3625 view_menu->AppendCheckItem(ID_MENU_ENC_SOUNDINGS,
3626 _menuText(_("Show ENC Soundings"), "S"));
3627 view_menu->AppendCheckItem(ID_MENU_ENC_ANCHOR,
3628 _menuText(_("Show ENC Anchoring Info"), "A"));
3629 view_menu->AppendCheckItem(ID_MENU_ENC_DATA_QUALITY,
3630 _menuText(_("Show ENC Data Quality"), "U"));
3631 view_menu->AppendCheckItem(ID_MENU_SHOW_NAVOBJECTS,
3632 _menuText(_("Show Navobjects"), "V"));
3633#else
3634 view_menu->AppendCheckItem(ID_MENU_ENC_TEXT,
3635 _menuText(_("Show ENC text"), "Alt-T"));
3636 view_menu->AppendCheckItem(ID_MENU_ENC_LIGHTS,
3637 _menuText(_("Show ENC Lights"), "Alt-L"));
3638 view_menu->AppendCheckItem(ID_MENU_ENC_SOUNDINGS,
3639 _menuText(_("Show ENC Soundings"), "Alt-S"));
3640 view_menu->AppendCheckItem(ID_MENU_ENC_ANCHOR,
3641 _menuText(_("Show ENC Anchoring Info"), "Alt-A"));
3642 view_menu->AppendCheckItem(ID_MENU_ENC_DATA_QUALITY,
3643 _menuText(_("Show ENC Data Quality"), "Alt-U"));
3644 view_menu->AppendCheckItem(ID_MENU_SHOW_NAVOBJECTS,
3645 _menuText(_("Show Navobjects"), "Alt-V"));
3646#endif
3647 view_menu->AppendSeparator();
3648 view_menu->AppendCheckItem(ID_MENU_SHOW_TIDES, _("Show Tides"));
3649 view_menu->AppendCheckItem(ID_MENU_SHOW_CURRENTS, _("Show Currents"));
3650 view_menu->AppendSeparator();
3651#ifndef __WXOSX__
3652 view_menu->Append(ID_MENU_UI_COLSCHEME,
3653 _menuText(_("Change Color Scheme"), "C"));
3654#else
3655 view_menu->Append(ID_MENU_UI_COLSCHEME,
3656 _menuText(_("Change Color Scheme"), "Alt-C"));
3657#endif
3658
3659 view_menu->AppendSeparator();
3660#ifndef __WXOSX__
3661 view_menu->Append(ID_MENU_UI_FULLSCREEN,
3662 _menuText(_("Toggle Full Screen"), "F11"));
3663#endif
3664 m_pMenuBar->Append(view_menu, _("&View"));
3665
3666 wxMenu *ais_menu = new wxMenu();
3667 ais_menu->AppendCheckItem(ID_MENU_AIS_TARGETS, _("Show AIS Targets"));
3668 ais_menu->AppendCheckItem(ID_MENU_AIS_SCALED_TARGETS,
3669 _("Attenuate less critical AIS targets"));
3670 ais_menu->AppendSeparator();
3671 ais_menu->AppendCheckItem(ID_MENU_AIS_MOORED_TARGETS,
3672 _("Hide Moored AIS Targets"));
3673 ais_menu->AppendCheckItem(ID_MENU_AIS_TRACKS, _("Show AIS Target Tracks"));
3674 ais_menu->AppendCheckItem(ID_MENU_AIS_CPADIALOG, _("Show CPA Alert Dialogs"));
3675 ais_menu->AppendCheckItem(ID_MENU_AIS_CPASOUND, _("Sound CPA Alarms"));
3676
3677#ifndef __WXOSX__
3678 ais_menu->AppendCheckItem(ID_MENU_AIS_CPAWARNING,
3679 _menuText(_("Show CPA Warnings"), "W"));
3680#else
3681 ais_menu->AppendCheckItem(ID_MENU_AIS_CPAWARNING,
3682 _menuText(_("Show CPA Warnings"), "Alt-W"));
3683#endif
3684
3685 ais_menu->AppendSeparator();
3686 ais_menu->Append(ID_MENU_AIS_TARGETLIST, _("AIS target list") + "...");
3687 m_pMenuBar->Append(ais_menu, _("&AIS"));
3688
3689 wxMenu *tools_menu = new wxMenu();
3690#ifndef __WXOSX__
3691 tools_menu->Append(ID_MENU_TOOL_NMEA_DBG_LOG,
3692 _menuText(_("Data Monitor"), "E"));
3693 tools_menu->Append(ID_MENU_TOOL_MEASURE,
3694 _menuText(_("Measure Distance"), "M"));
3695#else
3696 tools_menu->Append(ID_MENU_TOOL_NMEA_DBG_LOG,
3697 _menuText(_("Data Monitor"), "Alt-E"));
3698 tools_menu->Append(ID_MENU_TOOL_MEASURE,
3699 _menuText(_("Measure Distance"), "Alt-M"));
3700#endif
3701
3702 tools_menu->AppendSeparator();
3703 tools_menu->Append(ID_MENU_ROUTE_MANAGER, _("Route && Mark Manager..."));
3704 tools_menu->Append(ID_MENU_ROUTE_NEW, _menuText(_("Create Route"), "Ctrl-R"));
3705 tools_menu->AppendSeparator();
3706 tools_menu->Append(ID_MENU_MARK_BOAT,
3707 _menuText(_("Drop Mark at Boat"), "Ctrl-O"));
3708 tools_menu->Append(ID_MENU_MARK_CURSOR,
3709 _menuText(_("Drop Mark at Cursor"), "Ctrl-M"));
3710 tools_menu->AppendSeparator();
3711#ifdef __WXOSX__
3712 tools_menu->Append(
3713 ID_MENU_MARK_MOB,
3714 _menuText(_("Drop MOB Marker"),
3715 "RawCtrl-Space")); // NOTE Cmd+Space is reserved for Spotlight
3716 tools_menu->AppendSeparator();
3717 tools_menu->Append(wxID_PREFERENCES,
3718 _menuText(_("Preferences") + "...", "Ctrl-,"));
3719#else
3720 tools_menu->Append(ID_MENU_MARK_MOB,
3721 _menuText(_("Drop MOB Marker"), "Ctrl-Space"));
3722 tools_menu->AppendSeparator();
3723 tools_menu->Append(wxID_PREFERENCES,
3724 _menuText(_("Options") + "...", "Ctrl-,"));
3725#endif
3726 m_pMenuBar->Append(tools_menu, _("&Tools"));
3727
3728#ifdef __WXOSX__
3729 wxMenu *window_menu = new wxMenu();
3730 m_pMenuBar->Append(window_menu, _("&Window"));
3731#endif
3732
3733 wxMenu *help_menu = new wxMenu();
3734 help_menu->Append(wxID_ABOUT, _("About OpenCPN"));
3735 help_menu->Append(wxID_HELP, _("OpenCPN Help"));
3736 m_pMenuBar->Append(help_menu, _("&Help"));
3737
3738 // Set initial values for menu check items and radio items
3739 UpdateGlobalMenuItems();
3740}
3741
3742void MyFrame::UpdateGlobalMenuItems() {
3743 if (!m_pMenuBar) return; // if there isn't a menu bar
3744
3745 m_pMenuBar->FindItem(ID_MENU_NAV_FOLLOW)
3746 ->Check(GetPrimaryCanvas()->m_bFollow);
3747 m_pMenuBar->FindItem(ID_MENU_CHART_NORTHUP)
3748 ->Check(GetPrimaryCanvas()->GetUpMode() == NORTH_UP_MODE);
3749 m_pMenuBar->FindItem(ID_MENU_CHART_COGUP)
3750 ->Check(GetPrimaryCanvas()->GetUpMode() == COURSE_UP_MODE);
3751 m_pMenuBar->FindItem(ID_MENU_CHART_HEADUP)
3752 ->Check(GetPrimaryCanvas()->GetUpMode() == HEAD_UP_MODE);
3753 m_pMenuBar->FindItem(ID_MENU_NAV_TRACK)->Check(g_bTrackActive);
3754 m_pMenuBar->FindItem(ID_MENU_CHART_OUTLINES)->Check(g_bShowOutlines);
3755 m_pMenuBar->FindItem(ID_MENU_CHART_QUILTING)->Check(g_bQuiltEnable);
3756 m_pMenuBar->FindItem(ID_MENU_UI_CHARTBAR)->Check(g_bShowChartBar);
3757 m_pMenuBar->FindItem(ID_MENU_AIS_TARGETS)->Check(g_bShowAIS);
3758 m_pMenuBar->FindItem(ID_MENU_AIS_MOORED_TARGETS)->Check(g_bHideMoored);
3759 m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Check(g_bShowScaled);
3760 m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Enable(g_bAllowShowScaled);
3761 m_pMenuBar->FindItem(ID_MENU_AIS_TRACKS)->Check(g_bAISShowTracks);
3762 m_pMenuBar->FindItem(ID_MENU_AIS_CPADIALOG)->Check(g_bAIS_CPA_Alert);
3763 if (g_bAIS_CPA_Alert) {
3764 m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(g_bAIS_CPA_Alert_Audio);
3765 m_pMenuBar->Enable(ID_MENU_AIS_CPASOUND, true);
3766 } else {
3767 m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(false);
3768 m_pMenuBar->Enable(ID_MENU_AIS_CPASOUND, false);
3769 }
3770
3771 m_pMenuBar->FindItem(ID_MENU_AIS_CPAWARNING)->Check(g_bCPAWarn);
3772 m_pMenuBar->FindItem(ID_MENU_SHOW_NAVOBJECTS)
3773 ->Check(GetPrimaryCanvas()->m_bShowNavobjects);
3774
3775 if (ps52plib) {
3776 m_pMenuBar->FindItem(ID_MENU_ENC_TEXT)->Check(ps52plib->GetShowS57Text());
3777 m_pMenuBar->FindItem(ID_MENU_ENC_SOUNDINGS)
3778 ->Check(ps52plib->GetShowSoundings());
3779
3780 bool light_state = false;
3781 if (ps52plib) {
3782 for (unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount();
3783 iPtr++) {
3784 OBJLElement *pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
3785 if (!strncmp(pOLE->OBJLName, "LIGHTS", 6)) {
3786 light_state = (pOLE->nViz == 1);
3787 break;
3788 }
3789 }
3790 }
3791 m_pMenuBar->FindItem(ID_MENU_ENC_LIGHTS)
3792 ->Check((!ps52plib->IsObjNoshow("LIGHTS")) && light_state);
3793
3794 // Menu "Anchor Info" entry is only accessible in "All" or "User Standard"
3795 // categories
3796 DisCat nset = ps52plib->GetDisplayCategory();
3797 if ((nset == MARINERS_STANDARD) || (nset == OTHER)) {
3798 m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)
3799 ->Check(!ps52plib->IsObjNoshow("SBDARE"));
3800 m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, true);
3801 m_pMenuBar->FindItem(ID_MENU_ENC_DATA_QUALITY)
3802 ->Check(!ps52plib->IsObjNoshow("M_QUAL"));
3803 m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, true);
3804 } else {
3805 m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(false);
3806 m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, false);
3807 m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, false);
3808 }
3809 }
3810}
3811
3812void MyFrame::UpdateGlobalMenuItems(ChartCanvas *cc) {
3813 if (!m_pMenuBar) return; // if there isn't a menu bar
3814
3815 m_pMenuBar->FindItem(ID_MENU_NAV_FOLLOW)->Check(cc->m_bFollow);
3816
3817 if (cc->GetUpMode() == NORTH_UP_MODE)
3818 m_pMenuBar->FindItem(ID_MENU_CHART_NORTHUP)->Check(true);
3819 else if (cc->GetUpMode() == COURSE_UP_MODE)
3820 m_pMenuBar->FindItem(ID_MENU_CHART_COGUP)->Check(true);
3821 else
3822 m_pMenuBar->FindItem(ID_MENU_CHART_HEADUP)->Check(true);
3823
3824 m_pMenuBar->FindItem(ID_MENU_NAV_TRACK)->Check(g_bTrackActive);
3825 m_pMenuBar->FindItem(ID_MENU_CHART_OUTLINES)->Check(cc->GetShowOutlines());
3826 m_pMenuBar->FindItem(ID_MENU_CHART_QUILTING)->Check(cc->GetQuiltMode());
3827 m_pMenuBar->FindItem(ID_MENU_UI_CHARTBAR)->Check(cc->GetShowChartbar());
3828 m_pMenuBar->FindItem(ID_MENU_AIS_TARGETS)->Check(cc->GetShowAIS());
3829 m_pMenuBar->FindItem(ID_MENU_AIS_MOORED_TARGETS)->Check(g_bHideMoored);
3830 m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Check(cc->GetAttenAIS());
3831 m_pMenuBar->FindItem(ID_MENU_AIS_SCALED_TARGETS)->Enable(g_bAllowShowScaled);
3832 m_pMenuBar->FindItem(ID_MENU_AIS_TRACKS)->Check(g_bAISShowTracks);
3833 m_pMenuBar->FindItem(ID_MENU_AIS_CPADIALOG)->Check(g_bAIS_CPA_Alert);
3834 m_pMenuBar->FindItem(ID_MENU_AIS_CPASOUND)->Check(g_bAIS_CPA_Alert_Audio);
3835 m_pMenuBar->FindItem(ID_MENU_AIS_CPAWARNING)->Check(g_bCPAWarn);
3836 m_pMenuBar->FindItem(ID_MENU_SHOW_NAVOBJECTS)->Check(cc->m_bShowNavobjects);
3837 m_pMenuBar->FindItem(ID_MENU_SHOW_TIDES)->Check(cc->GetbShowTide());
3838 m_pMenuBar->FindItem(ID_MENU_SHOW_CURRENTS)->Check(cc->GetbShowCurrent());
3839
3840 if (ps52plib) {
3841 m_pMenuBar->FindItem(ID_MENU_ENC_TEXT)->Check(cc->GetShowENCText());
3842 m_pMenuBar->FindItem(ID_MENU_ENC_SOUNDINGS)->Check(cc->GetShowENCDepth());
3843
3844 if (ps52plib) {
3845 for (unsigned int iPtr = 0; iPtr < ps52plib->pOBJLArray->GetCount();
3846 iPtr++) {
3847 OBJLElement *pOLE = (OBJLElement *)(ps52plib->pOBJLArray->Item(iPtr));
3848 if (!strncmp(pOLE->OBJLName, "LIGHTS", 6)) {
3849 break;
3850 }
3851 }
3852 }
3853 m_pMenuBar->FindItem(ID_MENU_ENC_LIGHTS)->Check(cc->GetShowENCLights());
3854
3855 // Menu "Anchor Info" entry is only accessible in "All" or "UserStandard"
3856 // categories
3857 DisCat nset = (DisCat)cc->GetENCDisplayCategory();
3858 if ((nset == MARINERS_STANDARD) || (nset == OTHER)) {
3859 m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(cc->GetShowENCAnchor());
3860 m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, true);
3861 m_pMenuBar->FindItem(ID_MENU_ENC_DATA_QUALITY)
3862 ->Check(cc->GetShowENCDataQual());
3863 m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, true);
3864 } else {
3865 m_pMenuBar->FindItem(ID_MENU_ENC_ANCHOR)->Check(false);
3866 m_pMenuBar->Enable(ID_MENU_ENC_ANCHOR, false);
3867 m_pMenuBar->Enable(ID_MENU_ENC_DATA_QUALITY, false);
3868 }
3869 }
3870}
3871
3872void MyFrame::InvalidateAllCanvasUndo() {
3873 // .. for each canvas...
3874 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3875 ChartCanvas *cc = g_canvasArray.Item(i);
3876 if (cc) cc->undo->InvalidateUndo();
3877 }
3878}
3879#if 0
3880void MyFrame::SubmergeAllCanvasToolbars() {
3881 // .. for each canvas...
3882 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3883 ChartCanvas *cc = g_canvasArray.Item(i);
3884 if (cc) cc->SubmergeToolbar();
3885 }
3886}
3887
3888void MyFrame::SurfaceAllCanvasToolbars() {
3889 if (g_bshowToolbar) {
3890 // .. for each canvas...
3891 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
3892 ChartCanvas *cc = g_canvasArray.Item(i);
3893 if (cc && cc->GetToolbarEnable()) cc->SurfaceToolbar();
3894 }
3895 }
3896
3897}
3898#endif
3899
3900void MyFrame::JumpToPosition(ChartCanvas *cc, double lat, double lon,
3901 double scale) {
3902 if (lon > 180.0) lon -= 360.0;
3903 // XXX is vLat/vLon always equal to cc m_vLat, m_vLon after SetViewPoint? Does
3904 // it matter?
3905 vLat = lat;
3906 vLon = lon;
3907 cc->JumpToPosition(lat, lon, scale);
3908
3909 if (g_pi_manager) {
3910 g_pi_manager->SendViewPortToRequestingPlugIns(cc->GetVP());
3911 }
3912}
3913
3914void MyFrame::UpdateCanvasConfigDescriptors() {
3915 // ..For each canvas...
3916 for (unsigned int i = 0;
3917 i < ConfigMgr::Get().GetCanvasConfigArray().GetCount(); i++) {
3918 canvasConfig *cc = ConfigMgr::Get().GetCanvasConfigArray().Item(i);
3919 if (cc) {
3920 ChartCanvas *chart = cc->canvas;
3921 if (chart) {
3922 cc->iLat = chart->GetVP().clat;
3923 cc->iLon = chart->GetVP().clon;
3924 cc->iRotation = chart->GetVP().rotation;
3925 cc->iScale = chart->GetVP().view_scale_ppm;
3926 cc->DBindex = chart->GetQuiltReferenceChartIndex();
3927 cc->GroupID = chart->m_groupIndex;
3928 cc->canvasSize = chart->GetSize();
3929
3930 cc->bQuilt = chart->GetQuiltMode();
3931 cc->bShowTides = chart->GetbShowTide();
3932 cc->bShowCurrents = chart->GetbShowCurrent();
3933 cc->bShowGrid = chart->GetShowGrid();
3934 cc->bShowOutlines = chart->GetShowOutlines();
3935 cc->bShowDepthUnits = chart->GetShowDepthUnits();
3936 cc->bEnableBasemapTile = chart->GetbEnableBasemapTile();
3937
3938 cc->bFollow = chart->m_bFollow;
3939 cc->bLookahead = chart->m_bLookAhead;
3940 cc->bCourseUp = false;
3941 cc->bHeadUp = false;
3942 ;
3943 int upmode = chart->GetUpMode();
3944 if (upmode == COURSE_UP_MODE)
3945 cc->bCourseUp = true;
3946 else if (upmode == HEAD_UP_MODE)
3947 cc->bHeadUp = true;
3948 }
3949 }
3950 }
3951}
3952
3953void MyFrame::CenterView(ChartCanvas *cc, const LLBBox &RBBox) {
3954 if (!RBBox.GetValid()) return;
3955 // Calculate bbox center
3956 double clat = (RBBox.GetMinLat() + RBBox.GetMaxLat()) / 2;
3957 double clon = (RBBox.GetMinLon() + RBBox.GetMaxLon()) / 2;
3958 double ppm; // final ppm scale to use
3959
3960 if (RBBox.GetMinLat() == RBBox.GetMaxLat() &&
3961 RBBox.GetMinLon() == RBBox.GetMaxLon()) {
3962 // only one point, (should be a box?)
3963 ppm = cc->GetVPScale();
3964 } else {
3965 // Calculate ppm
3966 double rw, rh; // route width, height
3967 int ww, wh; // chart window width, height
3968 // route bbox width in nm
3969 DistanceBearingMercator(RBBox.GetMinLat(), RBBox.GetMinLon(),
3970 RBBox.GetMinLat(), RBBox.GetMaxLon(), NULL, &rw);
3971 // route bbox height in nm
3972 DistanceBearingMercator(RBBox.GetMinLat(), RBBox.GetMinLon(),
3973 RBBox.GetMaxLat(), RBBox.GetMinLon(), NULL, &rh);
3974
3975 cc->GetSize(&ww, &wh);
3976
3977 ppm = wxMin(ww / (rw * 1852), wh / (rh * 1852)) * (100 - fabs(clat)) / 90;
3978
3979 ppm = wxMin(ppm, 1.0);
3980 }
3981
3982 JumpToPosition(cc, clat, clon, ppm);
3983}
3984
3985void MyFrame::PrepareOptionsClose(options *settings,
3986 int settings_return_value) {
3987 // Capture som values from options dialog before closure
3988 options_lastPage = settings->lastPage;
3989#ifdef __ANDROID__
3990 // This is necessary to force a manual change to charts page,
3991 // in order to properly refresh the chart directory list.
3992 // Root cause: In Android, trouble with clearing the wxScrolledWindow
3993 if (options_lastPage == 1) options_lastPage = 0;
3994#endif
3995 options_subpage = settings->lastSubPage;
3996 options_lastWindowPos = settings->lastWindowPos;
3997 options_lastWindowSize = settings->lastWindowSize;
3998
3999#ifdef __ANDROID__
4000 androidEnableBackButton(true);
4001 androidEnableOptionsMenu(true);
4002 androidRestoreFullScreen();
4003 androidEnableRotation();
4004#endif
4005 ThawCharts();
4006 EnableSettingsTool(true);
4007}
4008
4009void MyFrame::DoOptionsDialog() {
4010 EnableSettingsTool(false);
4011
4012 if (NULL == g_options) {
4013 AbstractPlatform::ShowBusySpinner();
4014
4015 int sx, sy;
4016 pConfig->SetPath("/Settings");
4017 pConfig->Read("OptionsSizeX", &sx, -1);
4018 pConfig->Read("OptionsSizeY", &sy, -1);
4019
4020 wxWindow *optionsParent = this;
4021#ifdef __WXOSX__
4022 optionsParent = GetPrimaryCanvas();
4023#endif
4024 OptionsCallbacks callbacks;
4025 callbacks.prepare_close = [&](options *me, int changes) {
4026 PrepareOptionsClose(me, changes);
4027 };
4028 callbacks.process_dialog = [&](int changes, ArrayOfCDI *workdir_list) {
4029 ProcessOptionsDialog(changes, workdir_list);
4030 };
4031 g_options = new options(optionsParent, callbacks, -1, _("Options"),
4032 wxPoint(-1, -1), wxSize(sx, sy));
4033
4034 AbstractPlatform::HideBusySpinner();
4035 }
4036
4037 // Set initial Chart Dir
4038 g_options->SetInitChartDir(*pInit_Chart_Dir);
4039
4040 // Pass two working pointers for Chart Dir Dialog
4041 g_options->SetCurrentDirList(ChartData->GetChartDirArray());
4042 ArrayOfCDI *pWorkDirArray = new ArrayOfCDI;
4043 g_options->SetWorkDirListPtr(pWorkDirArray);
4044
4045 // Pass a ptr to MyConfig, for updates
4046 g_options->SetConfigPtr(pConfig);
4047 g_options->SetInitialSettings();
4048
4049 prev_locale = g_locale;
4050 g_options->SetInitialPage(options_lastPage, options_subpage);
4051
4052#ifndef __ANDROID__ // if(!g_bresponsive){
4053 g_options->lastWindowPos = options_lastWindowPos;
4054 if (options_lastWindowPos != wxPoint(0, 0)) {
4055 g_options->Move(options_lastWindowPos);
4056 g_options->SetSize(options_lastWindowSize);
4057 } else {
4058 g_options->CenterOnScreen();
4059 }
4060 if (options_lastWindowSize != wxSize(0, 0)) {
4061 g_options->SetSize(options_lastWindowSize);
4062 }
4063#endif
4064
4065#ifdef __ANDROID__
4066 androidEnableBackButton(false);
4067 androidEnableOptionsMenu(false);
4068 androidDisableFullScreen();
4069#endif
4070
4071 // Capture the full path names and VPScale of charts currently shown in all
4072 // canvases
4073 pathArray.Clear();
4074 // ..For each canvas.
4075 // TODO FIX ANDROID codepath..
4076 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4077 ChartCanvas *cc = g_canvasArray.Item(i);
4078 if (cc) {
4079 wxString chart_file_name;
4080 if (cc->GetQuiltMode()) {
4081 int dbi = cc->GetQuiltRefChartdbIndex();
4082 chart_file_name = ChartData->GetDBChartFileName(dbi);
4083 } else {
4084 if (cc->m_singleChart)
4085 chart_file_name = cc->m_singleChart->GetFullPath();
4086 }
4087
4088 pathArray.Add(chart_file_name);
4089 restoreScale[i] = cc->GetVPScale();
4090 }
4091 }
4092
4093 // Record current canvas config
4094 last_canvasConfig = g_canvasConfig;
4095
4096 // Record current chart scale factor
4097 g_last_ChartScaleFactor = g_ChartScaleFactor;
4098
4099 g_options->Show();
4100 return;
4101}
4102
4103void MyFrame::ProcessOptionsDialog(int rr, ArrayOfCDI *pNewDirArray) {
4104 bool b_need_refresh = false; // Do we need a full reload?
4105
4106#if 0
4107 if ((rr & VISIT_CHARTS) &&
4108 ((rr & CHANGE_CHARTS) || (rr & FORCE_UPDATE) || (rr & SCAN_UPDATE))) {
4109 if (pNewDirArray) {
4110 UpdateChartDatabaseInplace(*pNewDirArray,
4111 ((rr & FORCE_UPDATE) == FORCE_UPDATE), true,
4112 ChartListFileName);
4113
4114 b_need_refresh = true;
4115 }
4116 }
4117#endif
4118
4119 if (rr & STYLE_CHANGED) {
4120 OCPNMessageBox(
4121 NULL,
4122 _("Please restart OpenCPN to activate language or style changes."),
4123 _("OpenCPN Info"), wxOK | wxICON_INFORMATION);
4124 }
4125
4126 bool charts_updating =
4127 (rr & VISIT_CHARTS) && ((rr & CHANGE_CHARTS) || (rr & SCAN_UPDATE));
4128 if (!charts_updating) RefreshGroupIndices();
4129
4130 if (rr & TIDES_CHANGED) {
4131 LoadHarmonics();
4132 }
4133
4134 // S52_CHANGED is a byproduct of a change in the chart object render scale
4135 // So, applies to RoutePoint icons also
4136 if (rr & S52_CHANGED) {
4137 WayPointmanGui(*pWayPointMan).ReloadAllIcons(g_Platform->GetDisplayDPmm());
4138 }
4139
4140 pConfig->UpdateSettings();
4141
4142 if (g_pActiveTrack) {
4143 g_pActiveTrack->SetPrecision(g_nTrackPrecision);
4144 }
4145
4146 // reload pens and brushes
4147 g_pRouteMan->SetColorScheme(global_color_scheme,
4148 g_Platform->GetDisplayDPmm());
4149
4150 // Stuff the Filter tables
4151 double stuffcog = NAN;
4152 double stuffsog = NAN;
4153 if (!std::isnan(gCog)) stuffcog = gCog;
4154 if (!std::isnan(gSog)) stuffsog = gSog;
4155
4156 for (int i = 0; i < kMaxCogsogFilterSeconds; i++) {
4157 COGFilterTable[i] = stuffcog;
4158 SOGFilterTable[i] = stuffsog;
4159 }
4160
4161 SetChartUpdatePeriod(); // Pick up changes to skew compensator
4162
4163 if (rr & GL_CHANGED) {
4164 // Refresh the chart display, after flushing cache.
4165 // This will allow all charts to recognise new OpenGL configuration, if
4166 // any
4167 b_need_refresh = true;
4168 }
4169
4170 if (rr & S52_CHANGED) {
4171 b_need_refresh = true;
4172 }
4173
4174#ifdef ocpnUSE_GL
4175 if (rr & REBUILD_RASTER_CACHE) {
4176 if (g_glTextureManager) {
4177 GetPrimaryCanvas()->Disable();
4178 g_glTextureManager->BuildCompressedCache();
4179 GetPrimaryCanvas()->Enable();
4180 }
4181 }
4182#endif
4183
4184 if (g_config_display_size_manual &&
4188 } else {
4189 g_display_size_mm = wxMax(50, g_Platform->GetDisplaySizeMM());
4190 }
4191
4192 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4193 ChartCanvas *cc = g_canvasArray.Item(i);
4195 }
4196
4197 if (g_pi_manager) {
4198 g_pi_manager->SendBaseConfigToAllPlugIns();
4199 int rrt = rr & S52_CHANGED;
4200 g_pi_manager->SendS52ConfigToAllPlugIns(
4201 (rrt == S52_CHANGED) ||
4202 (g_last_ChartScaleFactor != g_ChartScaleFactor));
4203 }
4204
4205 if (g_MainToolbar) {
4206 g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
4207 g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
4208 }
4209
4210 // update S52 PLIB scale factors
4211 if (ps52plib) {
4212 ps52plib->SetScaleFactorExp(
4213 g_Platform->GetChartScaleFactorExp(g_ChartScaleFactor));
4214 ps52plib->SetScaleFactorZoomMod(g_chart_zoom_modifier_vector);
4215 }
4216
4217 // Apply any needed updates to each canvas
4218 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4219 ChartCanvas *cc = g_canvasArray.Item(i);
4220 if (cc) cc->ApplyGlobalSettings();
4221 }
4222
4223#if 0
4224 // The zoom-scale factor may have changed
4225 // so, trigger a recalculation of the reference chart
4226 // This is needed to recognise changes in zoom-scale factors
4227 // Do not call if chartdbs update is underway.
4228 if (!charts_updating) {
4229 bool ztc = g_bEnableZoomToCursor; // record the present state
4230 g_bEnableZoomToCursor = false; // since we don't want to pan
4231 // to an unknown cursor position
4232 if (!GetPrimaryCanvas()->IsFrozen())
4233 GetPrimaryCanvas()->ZoomCanvasSimple(1.0001);
4234 g_bEnableZoomToCursor = ztc;
4235 }
4236#endif
4237
4238 // Pick up chart object icon size changes (g_ChartScaleFactorExp)
4239 if (g_last_ChartScaleFactor != g_ChartScaleFactor) {
4240 if (g_pMarkInfoDialog) {
4241 g_pMarkInfoDialog->Hide();
4242 g_pMarkInfoDialog->Destroy();
4243 g_pMarkInfoDialog = NULL;
4244 }
4245 }
4246
4247 // We set the compass size
4248 SetGPSCompassScale();
4249 // ..For each canvas...
4250 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4251 ChartCanvas *cc = g_canvasArray.Item(i);
4252 if (cc) {
4253 cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
4254 if (!charts_updating) cc->UpdateCanvasControlBar();
4255 }
4256 }
4257 UpdateGPSCompassStatusBoxes();
4258
4259 SetAllToolbarScale();
4260 RequestNewToolbars();
4261
4262 if ((rr & MENU_CHANGED) == MENU_CHANGED) BuildMenuBar();
4263
4264 // Rebuild cursors
4265 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4266 ChartCanvas *cc = g_canvasArray.Item(i);
4267 if (cc) {
4268 cc->RebuildCursors();
4269 }
4270 }
4271
4272 // Change of master toolbar scale?
4273 bool b_masterScaleChange = false;
4274 if (g_MainToolbar &&
4275 (fabs(g_MainToolbar->GetScaleFactor() - g_toolbar_scalefactor) > 0.01f))
4276 b_masterScaleChange = true;
4277
4278 if ((rr & TOOLBAR_CHANGED) || b_masterScaleChange)
4279 RequestNewMasterToolbar(true);
4280
4281 bool bMuiChange = false;
4282#ifdef __ANDROID__
4283 bMuiChange = true; // to pick up possible "zoom" button visibility change
4284#endif
4285
4286 // Inform the canvases
4287 if (b_masterScaleChange || bMuiChange) {
4288 // ..For each canvas...
4289 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4290 ChartCanvas *cc = g_canvasArray.Item(i);
4291 if (cc) {
4292 cc->ProcessNewGUIScale();
4293 }
4294 }
4295 }
4296
4297#if wxUSE_XLOCALE
4298 if (rr & LOCALE_CHANGED) {
4299 g_Platform->ChangeLocale(g_locale, plocale_def_lang, &plocale_def_lang);
4300 ApplyLocale();
4301 rr |= NEED_NEW_OPTIONS;
4302 }
4303#endif
4304
4305#ifdef __ANDROID__
4306 if (g_pi_manager) g_pi_manager->NotifyAuiPlugIns();
4307#endif
4308
4309 // Reset chart scale factor trigger
4310 g_last_ChartScaleFactor = g_ChartScaleFactor;
4311
4312 return;
4313}
4314
4315bool MyFrame::CheckGroup(int igroup) {
4316 if (igroup == 0) return true; // "all charts" is always OK
4317
4318 ChartGroup *pGroup = g_pGroupArray->Item(igroup - 1);
4319
4320 if (!pGroup->m_element_array.size()) // truly empty group is OK
4321 return true;
4322
4323 for (const auto &elem : pGroup->m_element_array) {
4324 for (unsigned int ic = 0;
4325 ic < (unsigned int)ChartData->GetChartTableEntries(); ic++) {
4326 auto &cte = ChartData->GetChartTableEntry(ic);
4327 wxString chart_full_path(cte.GetpFullPath(), wxConvUTF8);
4328
4329 if (chart_full_path.StartsWith(elem.m_element_name)) return true;
4330 }
4331 }
4332
4333 return false; // this group is empty
4334}
4335
4336bool MyFrame::ScrubGroupArray() {
4337 // For each group,
4338 // make sure that each group element (dir or chart) references at least
4339 // oneitem in the database. If not, remove the element.
4340
4341 bool b_change = false;
4342 unsigned int igroup = 0;
4343 while (igroup < g_pGroupArray->GetCount()) {
4344 bool b_chart_in_element = false;
4345 ChartGroup *pGroup = g_pGroupArray->Item(igroup);
4346
4347 for (unsigned int j = 0; j < pGroup->m_element_array.size(); j++) {
4348 const wxString &element_root = pGroup->m_element_array[j].m_element_name;
4349
4350 for (unsigned int ic = 0;
4351 ic < (unsigned int)ChartData->GetChartTableEntries(); ic++) {
4352 auto &cte = ChartData->GetChartTableEntry(ic);
4353 wxString chart_full_path = cte.GetFullSystemPath();
4354
4355 if (chart_full_path.StartsWith(element_root)) {
4356 b_chart_in_element = true;
4357 break;
4358 }
4359 }
4360
4361 // Explicit check to avoid removing a group containing only GSHHS
4362 if (!b_chart_in_element) {
4363 wxString test_string = "GSHH";
4364 if (element_root.Upper().Contains(test_string))
4365 b_chart_in_element = true;
4366 }
4367
4368 if (!b_chart_in_element) // delete the element
4369 {
4370 pGroup->m_element_array.erase(pGroup->m_element_array.begin() + j);
4371 j--;
4372 b_change = true;
4373 }
4374 }
4375
4376 igroup++; // next group
4377 }
4378
4379 return b_change;
4380}
4381
4382void MyFrame::RefreshCanvasOther(ChartCanvas *ccThis) {
4383 // ..For each canvas...
4384 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4385 ChartCanvas *cc = g_canvasArray.Item(i);
4386 if (cc && (cc != ccThis)) cc->Refresh();
4387 }
4388}
4389
4390// Flav: This method reloads all charts for convenience
4391void MyFrame::ChartsRefresh() {
4392 if (!ChartData) return;
4393
4394 AbstractPlatform::ShowBusySpinner();
4395
4396 bool b_run = FrameTimer1.IsRunning();
4397
4398 FrameTimer1.Stop(); // stop other asynchronous activity
4399 FrameTenHzTimer.Stop();
4400
4401 // ..For each canvas...
4402 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4403 ChartCanvas *cc = g_canvasArray.Item(i);
4404 if (cc) {
4405 int currentIndex = cc->GetpCurrentStack()->GetCurrentEntrydbIndex();
4406 if (cc->GetQuiltMode()) {
4407 currentIndex = cc->GetQuiltReferenceChartIndex();
4408 }
4409 cc->canvasChartsRefresh(currentIndex);
4410 }
4411 }
4412
4413 if (b_run) FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
4414 if (b_run) FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
4415
4416 AbstractPlatform::HideBusySpinner();
4417}
4418
4419void MyFrame::InvalidateAllQuilts() {
4420 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4421 ChartCanvas *cc = g_canvasArray.Item(i);
4422 if (cc) {
4423 cc->InvalidateQuilt();
4424 cc->SetQuiltRefChart(-1);
4425 cc->m_singleChart = NULL;
4426 }
4427 }
4428}
4429
4430bool MyFrame::UpdateChartDatabaseInplace(ArrayOfCDI &DirArray, bool b_force,
4431 bool b_prog,
4432 const wxString &ChartListFileName) {
4433 bool b_run = FrameTimer1.IsRunning();
4434 FrameTimer1.Stop(); // stop other asynchronous activity
4435 FrameTenHzTimer.Stop();
4436
4437 bool b_runCOGTimer = FrameCOGTimer.IsRunning();
4438 FrameCOGTimer.Stop();
4439
4440 // ..For each canvas...
4441 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4442 ChartCanvas *cc = g_canvasArray.Item(i);
4443 if (cc) {
4444 cc->InvalidateQuilt();
4445 cc->SetQuiltRefChart(-1);
4446 cc->m_singleChart = NULL;
4447 }
4448 }
4449
4450 ChartData->PurgeCache();
4451
4452 // TODO
4453 // delete pCurrentStack;
4454 // pCurrentStack = NULL;
4455
4456 AbstractPlatform::ShowBusySpinner();
4457
4458 if (b_prog) {
4459 wxString longmsg = _("OpenCPN Chart Update");
4460 longmsg +=
4461 ".................................................................."
4462 "........";
4463
4464 Updateprog = new wxGenericProgressDialog();
4465
4466 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
4467 Updateprog->SetFont(*qFont);
4468
4469 Updateprog->Create(_("OpenCPN Chart Update"), longmsg, 100, gFrame,
4470 wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
4471 wxPD_REMAINING_TIME);
4472
4473 DimeControl(Updateprog);
4474 Updateprog->Show();
4475 }
4476
4477 wxLogMessage(" ");
4478 wxLogMessage("Starting chart database Update...");
4479
4480 // The Update() function may set gWorldMapLocation if at least one of the
4481 // directories contains GSHHS files. Save current situation...
4482 m_gshhg_chart_loc = gWorldMapLocation;
4483 gWorldMapLocation = wxEmptyString;
4484
4485 ChartData->Update(DirArray, b_force, Updateprog);
4486
4487 AbstractPlatform::HideBusySpinner();
4488 return true;
4489}
4490
4491void MyFrame::FinalizeChartDBUpdate() {
4492 // Finalize chartdbs update after all async events finished.
4493 if (!g_bDeferredInitDone) { // Coming from auto startup rebuild.
4494 // Resume init timer
4495 InitTimer.Start(100, wxTIMER_ONE_SHOT);
4496 }
4497
4498 bool b_groupchange = ScrubGroupArray();
4499 ChartData->ApplyGroupArray(g_pGroupArray);
4500 RefreshGroupIndices();
4501
4502 if (b_groupchange) {
4503 pConfig->DestroyConfigGroups();
4504 pConfig->CreateConfigGroups(g_pGroupArray);
4505 }
4506 pConfig->UpdateSettings();
4507
4508 if (g_bDeferredInitDone) ScheduleReloadCharts();
4509}
4510
4511void MyFrame::ToggleQuiltMode(ChartCanvas *cc) {
4512 if (cc) {
4513 cc->ToggleCanvasQuiltMode();
4514#if 0
4515 bool cur_mode = cc->GetQuiltMode();
4516
4517 if( !cc->GetQuiltMode() )
4518 cc->SetQuiltMode( true );
4519 else
4520 if( cc->GetQuiltMode() ) {
4521 cc->SetQuiltMode( false );
4522 g_sticky_chart = cc->GetQuiltReferenceChartIndex();
4523 }
4524
4525
4526 if( cur_mode != cc->GetQuiltMode() ){
4527 //TODO >>SetupQuiltMode();
4528 DoChartUpdate();
4529 cc->InvalidateGL();
4530 Refresh();
4531 }
4532 g_bQuiltEnable = cc->GetQuiltMode();
4533
4534 // Recycle the S52 PLIB so that vector charts will flush caches and re-render
4535 if(ps52plib)
4536 ps52plib->GenerateStateHash();
4537#endif
4538 }
4539}
4540
4541void MyFrame::DoStackDown(AbstractChartCanvas *arg) {
4542 auto *cc = dynamic_cast<ChartCanvas *>(arg);
4543 assert(cc);
4544 DoStackDelta(cc, -1);
4545}
4546
4547void MyFrame::DoStackUp(AbstractChartCanvas *arg) {
4548 auto *cc = dynamic_cast<ChartCanvas *>(arg);
4549 assert(cc);
4550 DoStackDelta(cc, 1);
4551}
4552
4553void MyFrame::DoStackDown(ChartCanvas *cc) { DoStackDelta(cc, -1); }
4554
4555void MyFrame::DoStackUp(ChartCanvas *cc) { DoStackDelta(cc, 1); }
4556
4557void MyFrame::DoStackDelta(ChartCanvas *cc, int direction) {
4558 if (cc) {
4559 cc->DoCanvasStackDelta(direction);
4560 }
4561}
4562
4563void MyFrame::PositionIENCToolbar() {
4564#if 0
4565 if (g_iENCToolbar) {
4566 wxPoint posn;
4567 posn.x = (GetPrimaryCanvas()->GetSize().x - g_iENCToolbar->GetSize().x) / 2;
4568 posn.y = 4;
4569 g_iENCToolbar->Move(GetPrimaryCanvas()->ClientToScreen(posn));
4570 }
4571#endif
4572}
4573
4574// Defered initialization for anything that is not required to render the
4575// initial frame and takes a while to initialize. This gets opencpn up and
4576// running much faster.
4577void MyFrame::OnInitTimer(wxTimerEvent &event) {
4578 InitTimer.Stop();
4579 wxString msg;
4580 msg.Printf("OnInitTimer...%d", m_iInitCount);
4581 wxLogMessage(msg);
4582 // printf("init-%d\n", m_iInitCount);
4583
4584 wxLog::FlushActive();
4585
4586 switch (m_iInitCount++) {
4587 case 0: {
4588 EnableSettingsTool(false);
4589
4590 FontMgr::Get()
4591 .ScrubList(); // Clean the font list, removing nonsensical entries
4592
4593 if (g_bInlandEcdis) {
4594 double range = GetPrimaryCanvas()->GetCanvasRangeMeters();
4595 double range_set = 500.;
4596
4597 range = wxRound(range * 10) / 10.;
4598
4599 if (range > 4000.)
4600 range_set = 4000.;
4601 else if (range > 2000.)
4602 range_set = 2000.;
4603 else if (range > 1600.)
4604 range_set = 1600.;
4605 else if (range > 1200.)
4606 range_set = 1200.;
4607 else if (range > 800.)
4608 range_set = 800.;
4609 else
4610 range_set = 500.;
4611
4612 GetPrimaryCanvas()->SetCanvasRangeMeters(range_set);
4613 }
4614
4615 // Synchronize persistent Fullscreen mode
4616 g_Platform->SetFullscreen(g_bFullscreen);
4617
4618 // Rebuild chart database, if necessary
4619 if (g_NeedDBUpdate > 0) {
4620 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4621 ChartCanvas *cc = g_canvasArray.Item(i);
4622 if (cc) {
4623 cc->SetGroupIndex(0, false); // all charts
4624 }
4625 }
4626
4627 // Start an async chart database update
4628 // Arrange for init timer to restart at next point in chain
4629 m_iInitCount = 1;
4630 StartRebuildChartDatabase();
4631 g_NeedDBUpdate = 0;
4632 return;
4633 }
4634
4635 break;
4636 }
4637 case 1:
4638 break;
4639
4640 case 2: {
4641 if (g_pi_manager->IsAnyPlugInChartEnabled()) b_reloadForPlugins = true;
4642 break;
4643 }
4644
4645 case 3: {
4646 if (g_MainToolbar) {
4647 g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
4648 g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
4649 }
4650
4651#ifdef ANDROID
4652 if (g_MainToolbar)
4653 m_data_monitor->Move(g_MainToolbar->GetToolbarRect().x +
4654 g_MainToolbar->GetToolbarRect().width,
4655 3 * GetCharHeight());
4656#else
4657 m_data_monitor->Center();
4658#endif
4659
4660 break;
4661 }
4662
4663 case 4: {
4664 int sx, sy;
4665 pConfig->SetPath("/Settings");
4666 pConfig->Read("OptionsSizeX", &sx, -1);
4667 pConfig->Read("OptionsSizeY", &sy, -1);
4668
4669 wxWindow *optionsParent = this;
4670#ifdef __WXOSX__
4671 optionsParent = GetPrimaryCanvas();
4672#endif
4673 BuildiENCToolbar(true, m_toolbar_callbacks);
4674
4675 break;
4676 }
4677
4678 case 5: {
4679 // FIXME (leamas) Remove, delegate to CmdlineClient ctor
4680 if (!g_params.empty()) {
4681 for (size_t n = 0; n < g_params.size(); n++) {
4682 wxString path = g_params[n];
4683 if (::wxFileExists(path)) {
4685 pSet->load_file(path.fn_str());
4686 int wpt_dups;
4687
4688 pSet->LoadAllGPXObjects(
4689 !pSet->IsOpenCPN(), wpt_dups,
4690 true); // Import with full vizibility of names and objects
4691 LLBBox box = pSet->GetBBox();
4692 if (box.GetValid()) {
4693 CenterView(GetPrimaryCanvas(), box);
4694 }
4695 delete pSet;
4696 }
4697 }
4698 }
4699 break;
4700 }
4701 case 6: {
4702 InitAppMsgBusListener();
4704
4705 // if WMM is not in use..
4706 // set the Mag Variation to the user specified value
4707 auto loader = PluginLoader::GetInstance();
4708 bool b_haveWMM = loader && loader->IsPlugInAvailable("WMM");
4709 if (!b_haveWMM) gVar = g_UserVar;
4710
4711 break;
4712 }
4713
4714 case 7: {
4715 // Load the waypoints. Both of these routines are very slow to execute
4716 // which is why they have been to defered until here
4717 auto colour_func = [](wxString c) { return GetGlobalColor(c); };
4718 pWayPointMan = new WayPointman(colour_func);
4719 WayPointmanGui(*pWayPointMan)
4720 .SetColorScheme(global_color_scheme, g_Platform->GetDisplayDPmm());
4721 // Reload the ownship icon from UserIcons, if present
4722 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4723 ChartCanvas *cc = g_canvasArray.Item(i);
4724 if (cc) {
4725 if (cc->SetUserOwnship()) cc->SetColorScheme(global_color_scheme);
4726 }
4727 }
4728
4729 NavObj_dB::GetInstance().FullSchemaMigrate(this);
4730 NavObj_dB::GetInstance().ImportLegacyNavobj(this);
4731 NavObj_dB::GetInstance().LoadNavObjects();
4732
4733 // Re-enable anchor watches if set in config file
4734 if (!g_AW1GUID.IsEmpty()) {
4735 pAnchorWatchPoint1 = pWayPointMan->FindRoutePointByGUID(g_AW1GUID);
4736 }
4737 if (!g_AW2GUID.IsEmpty()) {
4738 pAnchorWatchPoint2 = pWayPointMan->FindRoutePointByGUID(g_AW2GUID);
4739 }
4740
4741 // Import Layer-wise any .gpx files from /layers directory
4742 wxString layerdir = g_Platform->GetPrivateDataDir();
4743 appendOSDirSlash(&layerdir);
4744 layerdir.Append("layers");
4745
4746 if (wxDir::Exists(layerdir)) {
4747 wxString laymsg;
4748 laymsg.Printf("Getting .gpx layer files from: %s", layerdir.c_str());
4749 wxLogMessage(laymsg);
4750 pConfig->LoadLayers(layerdir);
4751 }
4752 break;
4753 }
4754 case 8: {
4755 if (!g_kiosk_startup) {
4756 AbstractPlatform::ShowBusySpinner();
4757
4758 int sx, sy;
4759 pConfig->SetPath("/Settings");
4760 pConfig->Read("OptionsSizeX", &sx, -1);
4761 pConfig->Read("OptionsSizeY", &sy, -1);
4762
4763 wxWindow *optionsParent = this;
4764#ifdef __WXOSX__
4765 optionsParent = GetPrimaryCanvas();
4766#endif
4767 OptionsCallbacks callbacks;
4768 callbacks.prepare_close = [&](options *me, int changes) {
4769 PrepareOptionsClose(me, changes);
4770 };
4771 callbacks.process_dialog = [&](int changes, ArrayOfCDI *workdir_list) {
4772 ProcessOptionsDialog(changes, workdir_list);
4773 };
4774 g_options = new options(optionsParent, callbacks, -1, _("Options"),
4775 wxPoint(-1, -1), wxSize(sx, sy));
4776 AbstractPlatform::HideBusySpinner();
4777 }
4778 break;
4779 }
4780
4781 default: {
4782 // Last call....
4783 wxLogMessage("OnInitTimer...Last Call");
4784
4785 if (!g_kiosk_startup) g_pi_manager->CallLateInit();
4786
4787 RequestNewMasterToolbar();
4788
4789 PositionIENCToolbar();
4790
4791 g_bDeferredInitDone = true;
4792
4793 gFrame->DoChartUpdate();
4794 FontMgr::Get()
4795 .ScrubList(); // Clean the font list, removing nonsensical entries
4796
4797 gFrame->ReloadAllVP(); // once more, and good to go
4798 // gFrame->Refresh(false);
4799 // gFrame->Raise();
4800
4801 GetPrimaryCanvas()->SetFocus();
4802 GetPrimaryCanvas()->Enable();
4803 g_focusCanvas = GetPrimaryCanvas();
4804
4805 if (b_reloadForPlugins) {
4806 // If any PlugIn implements PlugIn Charts, we need to re-run the
4807 // initial chart load logic to select the correct chart as saved from
4808 // the last run of the app. This will be triggered at the next
4809 // DoChartUpdate()
4810 if (g_pi_manager->IsAnyPlugInChartEnabled()) {
4811 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4812 ChartCanvas *cc = g_canvasArray.Item(i);
4813 if (cc) cc->SetFirstAuto(true);
4814 }
4815 }
4816
4817 DoChartUpdate();
4818 ChartsRefresh();
4819 }
4820
4821 wxLogMessage("OnInitTimer...Finalize Canvases");
4822
4823 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
4824 ChartCanvas *cc = g_canvasArray.Item(i);
4825 if (cc) {
4826 cc->CreateMUIBar();
4827 cc->CheckGroupValid();
4828 cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
4829 cc->SetShowGPSCompassWindow(true);
4830 }
4831 }
4832
4833#ifdef __ANDROID__
4834 androidEnableBackButton(true);
4835 androidEnableRotation();
4836 androidEnableOptionItems(true);
4837 androidLastCall();
4838#endif
4839
4840 if (g_start_fullscreen && !IsFullScreen()) ToggleFullScreen();
4841
4842 UpdateStatusBar();
4843 SendSizeEvent();
4844
4845 // Start up the tickers....
4846 gFrame->FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
4847 // Start up the ViewPort Rotation angle Averaging Timer....
4848 gFrame->FrameCOGTimer.Start(2000, wxTIMER_CONTINUOUS);
4849 // Start up the Ten Hz timer....
4850 gFrame->FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
4851
4852 break;
4853 }
4854 } // switch
4855
4856 if (!g_bDeferredInitDone) InitTimer.Start(10, wxTIMER_ONE_SHOT);
4857
4858 wxLog::FlushActive();
4859
4860 RefreshAllCanvas(true);
4861 UsbWatchDaemon::GetInstance().Start();
4862 EnableSettingsTool(true);
4863}
4864
4865wxDEFINE_EVENT(EVT_BASIC_NAV_DATA, ObservedEvt);
4866wxDEFINE_EVENT(EVT_GPS_WATCHDOG, ObservedEvt);
4867
4868void MyFrame::InitAppMsgBusListener() {
4869 auto &msgbus = AppMsgBus::GetInstance();
4870
4871 // BasicNavData
4872 AppMsg msg_basic(AppMsg::Type::BasicNavData);
4873 listener_basic_navdata.Listen(msg_basic, this, EVT_BASIC_NAV_DATA);
4874
4875 Bind(EVT_BASIC_NAV_DATA, [&](ObservedEvt ev) {
4876 auto ptr = ev.GetSharedPtr();
4877 auto basicnav_msg = std::static_pointer_cast<const BasicNavDataMsg>(ptr);
4878 HandleBasicNavMsg(basicnav_msg);
4879 });
4880
4881 // GPS Watchdog expiry status
4882 AppMsg msg_watchdog(AppMsg::Type::GPSWatchdog);
4883 listener_gps_watchdog.Listen(msg_watchdog, this, EVT_GPS_WATCHDOG);
4884
4885 Bind(EVT_GPS_WATCHDOG, [&](ObservedEvt ev) {
4886 auto ptr = ev.GetSharedPtr();
4887 auto msg = std::static_pointer_cast<const GPSWatchdogMsg>(ptr);
4888 HandleGPSWatchdogMsg(msg);
4889 });
4890}
4891
4893#ifdef __ANDROID__
4895void MyFrame::ReleaseApiListeners() {}
4896
4897#else
4899 auto &server = LocalServerApi::GetInstance();
4900 m_on_raise_listener.Init(server.on_raise, [&](ObservedEvt) { Raise(); });
4901 m_on_quit_listener.Init(server.on_quit, [&](ObservedEvt) { FastClose(); });
4902 server.SetGetRestApiEndpointCb([&] { return m_rest_server.GetEndpoint(); });
4903 server.open_file_cb = [&](const std::string &path) {
4904 return m_open_gpx_file(path);
4905 };
4906}
4907
4908void MyFrame::ReleaseApiListeners() { LocalServerApi::ReleaseInstance(); }
4909#endif
4910
4911void MyFrame::HandleGPSWatchdogMsg(std::shared_ptr<const GPSWatchdogMsg> msg) {
4912 if (msg->gps_watchdog <= 0) {
4913 if (msg->wd_source == GPSWatchdogMsg::WDSource::position) {
4914 bool last_bGPSValid = bGPSValid;
4915 bGPSValid = false;
4916 m_fixtime = 0; // Invalidate fix time
4917 if (last_bGPSValid != bGPSValid) UpdateGPSCompassStatusBoxes(true);
4918
4919 // Possible notification on position watchdog timeout...
4920 // if fix has been valid for at least 5 minutes, and then lost,
4921 // then post a critical notification
4922 if (m_fix_start_time.IsValid()) {
4923 wxDateTime now = wxDateTime::Now();
4924 wxTimeSpan span = now - m_fix_start_time;
4925 if (span.IsLongerThan(wxTimeSpan(0, 5))) {
4926 auto &noteman = NotificationManager::GetInstance();
4927 wxString msg = _("GNSS Position fix lost");
4928 noteman.AddNotification(NotificationSeverity::kCritical,
4929 msg.ToStdString());
4930 m_fix_start_time = wxInvalidDateTime;
4931 }
4932 }
4933
4934 } else if (msg->wd_source == GPSWatchdogMsg::WDSource::velocity) {
4935 bool last_bVelocityValid = bVelocityValid;
4936 bVelocityValid = false;
4937 }
4938
4939 UpdateStatusBar();
4940 }
4941}
4942
4943void MyFrame::CalculateCOGAverage() {
4944 // Maintain average COG for Course Up Mode
4945 if (!std::isnan(gCog)) {
4946 if (g_COGAvgSec > 0) {
4947 // Make a hole
4948 for (int i = g_COGAvgSec - 1; i > 0; i--) COGTable[i] = COGTable[i - 1];
4949 COGTable[0] = gCog;
4950
4951 double sum = 0., count = 0;
4952 for (int i = 0; i < g_COGAvgSec; i++) {
4953 double adder = COGTable[i];
4954 if (std::isnan(adder)) continue;
4955
4956 if (fabs(adder - g_COGAvg) > 180.) {
4957 if ((adder - g_COGAvg) > 0.)
4958 adder -= 360.;
4959 else
4960 adder += 360.;
4961 }
4962
4963 sum += adder;
4964 count++;
4965 }
4966 sum /= count;
4967
4968 if (sum < 0.)
4969 sum += 360.;
4970 else if (sum >= 360.)
4971 sum -= 360.;
4972
4973 g_COGAvg = sum;
4974 } else
4975 g_COGAvg = gCog;
4976 }
4977}
4978
4979void MyFrame::HandleBasicNavMsg(std::shared_ptr<const BasicNavDataMsg> msg) {
4980 m_fixtime = msg->time;
4981 double hdt_data_interval = 0;
4982 double fix_time_interval = 0;
4983
4984 double msgtime = msg->set_time.tv_sec;
4985 double m1 = msg->set_time.tv_nsec / 1e9;
4986 msgtime += m1;
4987
4988 if (((msg->vflag & POS_UPDATE) == POS_UPDATE) &&
4989 ((msg->vflag & POS_VALID) == POS_VALID)) {
4990 // Maintain valid fix start time
4991 if (!m_fix_start_time.IsValid()) {
4992 m_fix_start_time = wxDateTime::Now();
4993 }
4994
4995 // Check the position change, looking for a valid new fix.
4996 double dist, brg;
4997 DistanceBearingMercator(gLat, gLon, gLat_gt, gLon_gt, &brg, &dist);
4998
4999 if (dist > .0001) { // Avoid duplicate position report
5000 fix_time_gt_last = fix_time_gt;
5001 uint64_t fix_time_gt_now =
5002 msg->set_time.tv_sec * 1e9 + msg->set_time.tv_nsec;
5003 fix_time_interval = (fix_time_gt_now - fix_time_gt_last) / (double)1e9;
5004 // printf("interval: %g\n", fix_time_interval);
5005
5006 // Calculate an implied SOG from the position change and time interval
5007 double implied_sog = dist / (fix_time_interval / 3600);
5008
5009 // printf ("Fix Interval: %g\n", fix_time_interval);
5010 // printf("SOG est: %g %g\n", gSog, implied_sog);
5011 // shuffle history data
5012 gLat_gt_m1 = gLat_gt;
5013 gLon_gt_m1 = gLon_gt;
5014 gLat_gt = gLat;
5015 gLon_gt = gLon;
5016
5017 fix_time_gt = fix_time_gt_now;
5018 }
5019 }
5020
5021 if (((msg->vflag & COG_UPDATE) == COG_UPDATE) &&
5022 ((msg->vflag & SOG_UPDATE) == SOG_UPDATE)) {
5023 gCog_gt_m1 = gCog_gt;
5024 gCog_gt = gCog;
5025 gSog_gt = gSog;
5026
5027 // In every case, if SOG is too slow, the COG is undefined.
5028 if (gSog < 0.1) {
5029 gCog_gt = NAN;
5030 gCog_gt_m1 = NAN;
5031 }
5032
5033 if (!std::isnan(gCog_gt_m1)) { // Startup
5034#if 0
5035 if ((fabs(gSog - implied_sog) / gSog) > 0.5) {
5036 // Probably a synthetic data stream, with multiple position sources.
5037 // Do not try to interpolate position at 10 Hz.
5038 gSog_gt = 0;
5039 cog_rate_gt = 0;
5040 } else
5041#endif
5042 if ((fix_time_gt - fix_time_gt_last) > .08) {
5043 // Calculate an estimated Rate-of-turn
5044 int dir = 0;
5045 double diff = 0;
5046 double difft = 0;
5047 if (gCog_gt > gCog_gt_m1) {
5048 if ((gCog_gt - gCog_gt_m1) > 180.)
5049 dir = 1; // left
5050 else
5051 dir = 2; // right
5052 } else {
5053 if ((gCog_gt_m1 - gCog_gt) > 180.)
5054 dir = 2; // right
5055 else
5056 dir = 1; // left
5057 }
5058 difft = fabs(gCog_gt - gCog_gt_m1);
5059 if (fabs(difft > 180.)) difft = fabs(difft - 360.);
5060 if (dir == 1)
5061 diff = -difft;
5062 else
5063 diff = difft;
5064
5065 // double diff = gCog_gt - gCog_gt_m1;
5066 // printf("diff %g %d\n", diff, dir);
5067 double tentative_cog_rate_gt = diff / (fix_time_gt - fix_time_gt_last);
5068 tentative_cog_rate_gt *= 1e9; // degrees / sec
5069 cog_rate_gt = tentative_cog_rate_gt;
5070 }
5071
5072 gCog = gCog_gt_m1;
5073 }
5074 // printf("cog_rate_gt %g %g\n", gCog, cog_rate_gt);
5075 }
5076
5077 if ((msg->vflag & HDT_UPDATE) == HDT_UPDATE) {
5078#if 0
5079// Lowpass filter, 10 samples
5080static double hdt_avg;
5081 double hdt_norm = gHdt;
5082 if(gHdt > 180) hdt_norm -= 360;
5083
5084 hdt_avg *= 0.9;
5085 hdt_avg += hdt_norm * 0.1;
5086 gHdt = hdt_avg;
5087 if( gHdt < 0) gHdt += 360.;
5088#endif
5089
5090 if (!std::isnan(gHdt)) {
5091 // Prepare to estimate the gHdt from prior ground truth measurements
5092 uint64_t hdt_time_gt_last = hdt_time_gt;
5093 hdt_time_gt = msg->set_time.tv_sec * 1e9 + msg->set_time.tv_nsec;
5094 hdt_data_interval = (hdt_time_gt - hdt_time_gt_last) / 1e9;
5095
5096 // Skip data reports that come too frequently
5097 if (hdt_data_interval > .09) {
5098 // shuffle points
5099 gHdt_gt_m1 = gHdt_gt;
5100 gHdt_gt = gHdt;
5101
5102 if (!std::isnan(gHdt_gt_m1)) { // startup
5103 // Calculate an estimated Rate-of-change of gHdt
5104 double tentative_hdt_rate_gt =
5105 (gHdt_gt - gHdt_gt_m1) / (hdt_time_gt - hdt_time_gt_last);
5106 tentative_hdt_rate_gt *= 1e9; // degrees / sec
5107 // Sanity check, and resolve the "phase" problem at +/- North
5108 if (fabs(tentative_hdt_rate_gt - hdt_rate_gt) < 180.)
5109 hdt_rate_gt = tentative_hdt_rate_gt;
5110
5111 gHdt = gHdt_gt_m1;
5112 }
5113 }
5114 }
5115 }
5116
5117 if (std::isnan(gHdt)) gHdt_gt = NAN; // Handle loss of signal
5118
5119 // Some housekeeping
5120 CalculateCOGAverage();
5121 FilterCogSog();
5122
5123 // Maintain the GPS position validity flag
5124 // Determined by source validity of RMC, GGA, GLL (N0183)
5125 // or PGNs 129029, 129025 (N2K)
5126 // Positions by sK and AIVDO are assumed valid
5127 bool last_bGPSValid = bGPSValid;
5128 if ((msg->vflag & POS_UPDATE) == POS_UPDATE) {
5129 if ((msg->vflag & POS_VALID) == POS_VALID)
5130 bGPSValid = true;
5131 else
5132 bGPSValid = false;
5133 }
5134 if (last_bGPSValid != bGPSValid) UpdateGPSCompassStatusBoxes(true);
5135
5136 bVelocityValid = true;
5137 UpdateStatusBar();
5138}
5139
5140void MyFrame::UpdateStatusBar() {
5141 // Show a little heartbeat tick in StatusWindow0 on NMEA events
5142 // But no faster than 10 hz.
5143 unsigned long uiCurrentTickCount;
5144 m_MMEAeventTime.SetToCurrent();
5145 uiCurrentTickCount =
5146 m_MMEAeventTime.GetMillisecond() / 100; // tenths of a second
5147 uiCurrentTickCount += m_MMEAeventTime.GetTicks() * 10;
5148 if (uiCurrentTickCount > m_ulLastNMEATicktime + 1) {
5149 m_ulLastNMEATicktime = uiCurrentTickCount;
5150
5151 if (m_tick_idx++ > 6) m_tick_idx = 0;
5152 }
5153
5154 // Show gLat/gLon in StatusWindow0
5155
5156 if (NULL != GetStatusBar()) {
5157 if (1 /*pos_valid*/) {
5158 char tick_buf[2];
5159 tick_buf[0] = nmea_tick_chars[m_tick_idx];
5160 tick_buf[1] = 0;
5161
5162 wxString s1(tick_buf, wxConvUTF8);
5163 s1 += _(" Ship ");
5164 s1 += toSDMM(1, gLat);
5165 s1 += " ";
5166 s1 += toSDMM(2, gLon);
5167
5168 if (STAT_FIELD_TICK >= 0) SetStatusText(s1, STAT_FIELD_TICK);
5169 }
5170
5171 wxString sogcog;
5172 if (!std::isnan(gSog))
5173 sogcog.Printf("SOG %2.2f " + getUsrSpeedUnit() + " ", toUsrSpeed(gSog));
5174 else
5175 sogcog.Printf("SOG --- ");
5176
5177 wxString cogs;
5178 // We show COG only if SOG is > 0.05
5179 if (!std::isnan(gCog) && !std::isnan(gSog) && (gSog > 0.05)) {
5180 if (g_bShowTrue)
5181 cogs << wxString::Format(wxString("COG %03d%c "), (int)gCog, 0x00B0);
5182 if (g_bShowMag)
5183 cogs << wxString::Format(wxString("COG %03d%c(M) "),
5184 (int)toMagnetic(gCog), 0x00B0);
5185 } else
5186 cogs.Printf(("COG ---%c"), 0x00B0);
5187
5188 sogcog.Append(cogs);
5189 SetStatusText(sogcog, STAT_FIELD_SOGCOG);
5190 }
5191}
5192
5193// Manage the application memory footprint on a periodic schedule
5194void MyFrame::OnMemFootTimer(wxTimerEvent &event) {
5195 MemFootTimer.Stop();
5196
5197 int memsize = GetApplicationMemoryUse();
5198
5199 g_MemFootMB = 100;
5200 printf("Memsize: %d \n", memsize);
5201 // The application memory usage has exceeded the target, so try to manage it
5202 // down....
5203 if (memsize > (g_MemFootMB * 1000)) {
5204 ChartCanvas *cc = GetPrimaryCanvas();
5205 if (ChartData && cc) {
5206 // Get a local copy of the cache info
5207 wxArrayPtrVoid *pCache = ChartData->GetChartCache();
5208 unsigned int nCache = pCache->GetCount();
5209 CacheEntry *pcea = new CacheEntry[nCache];
5210
5211 for (unsigned int i = 0; i < nCache; i++) {
5212 CacheEntry *pce = (CacheEntry *)(pCache->Item(i));
5213 pcea[i] = *pce; // ChartBase *Ch = (ChartBase *)pce->pChart;
5214 }
5215
5216 if (nCache > 1) {
5217 // Bubble Sort the local cache entry array
5218 bool b_cont = true;
5219 while (b_cont) {
5220 b_cont = false;
5221 for (unsigned int i = 0; i < nCache - 1; i++) {
5222 if (pcea[i].RecentTime > pcea[i + 1].RecentTime) {
5223 CacheEntry tmp = pcea[i];
5224 pcea[i] = pcea[i + 1];
5225 pcea[i + 1] = tmp;
5226 b_cont = true;
5227 break;
5228 }
5229 }
5230 }
5231
5232 // Free up some chart cache entries until the memory footprint target
5233 // is realized
5234
5235 unsigned int idelete = 0; // starting at top. which is oldest
5236 unsigned int idelete_max = pCache->GetCount();
5237
5238 // How many can be deleted?
5239 unsigned int minimum_cache = 1;
5240 if (cc->GetQuiltMode()) minimum_cache = cc->GetQuiltChartCount();
5241
5242 while ((memsize > (g_MemFootMB * 1000)) &&
5243 (pCache->GetCount() > minimum_cache) &&
5244 (idelete < idelete_max)) {
5245 int memsizeb = memsize;
5246
5247 ChartData->DeleteCacheChart((ChartBase *)pcea[idelete].pChart);
5248 idelete++;
5249 memsize = GetApplicationMemoryUse();
5250 printf("delete, before: %d after: %d\n", memsizeb, memsize);
5251 }
5252 }
5253
5254 delete[] pcea;
5255 }
5256 }
5257
5258 MemFootTimer.Start(9000, wxTIMER_CONTINUOUS);
5259}
5260
5261int ut_index;
5262
5263void MyFrame::CheckToolbarPosition() {
5264#ifdef __WXMAC__
5265 // Manage Full Screen mode on Mac Mojave 10.14
5266 static bool bMaximized;
5267
5268 if (IsMaximized() && !bMaximized) {
5269 bMaximized = true;
5270 if (g_MainToolbar) {
5271 g_MainToolbar->SetYAuxOffset(g_MainToolbar->GetToolSize().y * 15 / 10);
5272 g_MainToolbar->SetDefaultPosition();
5273 g_MainToolbar->Realize();
5274 }
5275 PositionIENCToolbar();
5276 } else if (!IsMaximized() && bMaximized) {
5277 bMaximized = false;
5278 if (g_MainToolbar) {
5279 g_MainToolbar->SetYAuxOffset(0);
5280 g_MainToolbar->SetDockY(-1);
5281 g_MainToolbar->SetDefaultPosition();
5282 g_MainToolbar->Realize();
5283 }
5284 PositionIENCToolbar();
5285 }
5286#endif
5287}
5288
5289void MyFrame::ProcessUnitTest() {
5290 if (!g_bPauseTest && (g_unit_test_1 || g_unit_test_2)) {
5291 // if((0 == ut_index) && GetQuiltMode())
5292 // ToggleQuiltMode();
5293
5294 // We use only one canvas for the unit tests, so far...
5295 ChartCanvas *cc = GetPrimaryCanvas();
5296
5297 cc->m_bFollow = false;
5298 if (g_MainToolbar && g_MainToolbar->GetToolbar())
5299 g_MainToolbar->GetToolbar()->ToggleTool(ID_FOLLOW, cc->m_bFollow);
5300 int ut_index_max = ((g_unit_test_1 > 0) ? (g_unit_test_1 - 1) : INT_MAX);
5301
5302 if (ChartData) {
5303 if (cc->m_groupIndex > 0) {
5304 while (ut_index < ChartData->GetChartTableEntries() &&
5305 !ChartData->IsChartInGroup(ut_index, cc->m_groupIndex)) {
5306 ut_index++;
5307 }
5308 }
5309 if (ut_index < ChartData->GetChartTableEntries()) {
5310 // printf("%d / %d\n", ut_index, ChartData->GetChartTableEntries());
5311 const ChartTableEntry *cte = &ChartData->GetChartTableEntry(ut_index);
5312
5313 double clat = (cte->GetLatMax() + cte->GetLatMin()) / 2;
5314 double clon = (cte->GetLonMax() + cte->GetLonMin()) / 2;
5315
5316 vLat = clat;
5317 vLon = clon;
5318
5319 cc->SetViewPoint(clat, clon);
5320
5321 if (cc->GetQuiltMode()) {
5322 if (cc->IsChartQuiltableRef(ut_index))
5323 cc->SelectQuiltRefdbChart(ut_index);
5324 } else
5325 cc->SelectdbChart(ut_index);
5326
5327 double ppm; // final ppm scale to use
5328 if (g_unit_test_1) {
5329 ppm = cc->GetCanvasScaleFactor() / cte->GetScale();
5330 ppm /= 2;
5331 } else {
5332 double rw, rh; // width, height
5333 int ww, wh; // chart window width, height
5334
5335 // width in nm
5336 DistanceBearingMercator(cte->GetLatMin(), cte->GetLonMin(),
5337 cte->GetLatMin(), cte->GetLonMax(), NULL,
5338 &rw);
5339
5340 // height in nm
5341 DistanceBearingMercator(cte->GetLatMin(), cte->GetLonMin(),
5342 cte->GetLatMax(), cte->GetLonMin(), NULL,
5343 &rh);
5344
5345 cc->GetSize(&ww, &wh);
5346 ppm = wxMin(ww / (rw * 1852), wh / (rh * 1852)) * (100 - fabs(clat)) /
5347 90;
5348 ppm = wxMin(ppm, 1.0);
5349 }
5350 cc->SetVPScale(ppm);
5351
5352 cc->ReloadVP();
5353
5354 ut_index++;
5355 if (ut_index > ut_index_max) exit(0);
5356 } else {
5357 _exit(0);
5358 }
5359 }
5360 }
5361}
5362double gCog_last;
5363
5364void MyFrame::OnFrameTenHzTimer(wxTimerEvent &event) {
5365 // Check to see if in non-North-Up mode
5366 bool b_rotate = false;
5367 for (ChartCanvas *cc : g_canvasArray) {
5368 if (cc) b_rotate |= (cc->GetUpMode() != NORTH_UP_MODE);
5369 }
5370
5371 if (!b_rotate && !g_btenhertz) return; // Nothing to do
5372
5373 bool b_update = false;
5374 if (g_btenhertz) {
5375 if (!std::isnan(gCog) && !std::isnan(gSog)) {
5376 // Estimate current state by extrapolating from last "ground truth" state
5377
5378 struct timespec now;
5379 clock_gettime(CLOCK_MONOTONIC, &now);
5380 uint64_t diff = 1e9 * (now.tv_sec) + now.tv_nsec - fix_time_gt;
5381 double diffc = diff / 1e9; // sec
5382
5383 // Set gCog as estimated from last two ground truth fixes
5384 double gCog_tentative = gCog_gt_m1 + (cog_rate_gt * diffc);
5385 if (gCog_tentative >= 360.) gCog_tentative -= 360.;
5386 if (gCog_tentative < 0.) gCog_tentative += 360.;
5387 gCog = gCog_tentative;
5388
5389 // printf(" cog: %g\n", gCog);
5390 // And the same for gHdt
5391 if (!std::isnan(gHdt_gt) && !std::isnan(gHdt_gt_m1)) {
5392 uint64_t diff = 1e9 * (now.tv_sec) + now.tv_nsec - hdt_time_gt;
5393 double diffc = diff / 1e9; // sec
5394 gHdt = gHdt_gt_m1 + (hdt_rate_gt * diffc);
5395 }
5396
5397 // Estimate lat/lon position
5398 if (gSog_gt && !std::isnan(gCog_gt)) {
5399 double delta_t = diffc / 3600; // hours
5400 double distance = gSog_gt * delta_t; // NMi
5401
5402 // spherical (close enough)
5403 double angr = gCog_gt / 180 * M_PI;
5404 double latr = gLat_gt * M_PI / 180;
5405 double D = distance / 3443; // earth radius in nm
5406 double sD = sin(D), cD = cos(D);
5407 double sy = sin(latr), cy = cos(latr);
5408 double sa = sin(angr), ca = cos(angr);
5409
5410 gLon = gLon_gt + asin(sa * sD / cy) * 180 / M_PI;
5411 gLat = asin(sy * cD + cy * sD * ca) * 180 / M_PI;
5412 }
5413 }
5414
5415 b_update = true;
5416 }
5417
5418 // In a valid rotation mode ?
5419 if (b_rotate) {
5420 for (ChartCanvas *cc : g_canvasArray) {
5421 if (cc) cc->DoCanvasCOGSet();
5422 }
5423 b_update = true;
5424 }
5425
5426 if (b_update) {
5427 for (ChartCanvas *cc : g_canvasArray) {
5428 if (cc) {
5429 if (g_bopengl) {
5430 if (cc->GetUpMode() != NORTH_UP_MODE || cc->m_bFollow) {
5431 bool bnew = cc->DoCanvasUpdate();
5432 if (!bnew) cc->UpdateShips(); // Ensure ownship HDT is rendered.
5433 } else
5434 cc->Refresh(false); // Process ownship motion at 10 Hz.
5435 }
5436 }
5437 }
5438 }
5439
5440 gCog_last = gCog;
5441 FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
5442}
5443
5444bool MyFrame::ProcessQuitFlag() {
5445 // Listen for quitflag to be set, requesting application close
5446 if (quitflag) {
5447 FrameTimer1.Stop();
5448 FrameTenHzTimer.Stop();
5449
5450 wxWindow *top = wxTheApp ? wxTheApp->GetTopWindow() : nullptr;
5451 if (top) top->Close(true);
5452
5453 return true;
5454 }
5455 return false;
5456}
5457
5458void MyFrame::ProcessDeferredTrackOn() {
5459 // If tracking carryover was found in config file, enable tracking as soon as
5460 // GPS become valid
5461 if (g_bDeferredStartTrack) {
5462 if (!g_bTrackActive) {
5463 if (bGPSValid) {
5464 gFrame->TrackOn();
5465 g_bDeferredStartTrack = false;
5466 }
5467 } else { // tracking has been manually activated
5468 g_bDeferredStartTrack = false;
5469 }
5470 }
5471}
5472
5473void MyFrame::ProcessAnchorWatch() {
5474 // Check for anchorwatch alarms // pjotrc
5475 // 2010.02.15
5476 if (pAnchorWatchPoint1) {
5477 double dist;
5478 double brg;
5479 DistanceBearingMercator(pAnchorWatchPoint1->m_lat,
5480 pAnchorWatchPoint1->m_lon, gLat, gLon, &brg, &dist);
5481 double d = g_nAWMax;
5482 (pAnchorWatchPoint1->GetName()).ToDouble(&d);
5483 d = ocpn::AnchorDistFix(d, AnchorPointMinDist, g_nAWMax);
5484 bool toofar = false;
5485 bool tooclose = false;
5486 if (d >= 0.0) toofar = (dist * 1852. > d);
5487 if (d < 0.0) tooclose = (dist * 1852 < -d);
5488
5489 if (tooclose || toofar)
5490 AnchorAlertOn1 = true;
5491 else
5492 AnchorAlertOn1 = false;
5493 } else
5494 AnchorAlertOn1 = false;
5495
5496 if (pAnchorWatchPoint2) {
5497 double dist;
5498 double brg;
5499 DistanceBearingMercator(pAnchorWatchPoint2->m_lat,
5500 pAnchorWatchPoint2->m_lon, gLat, gLon, &brg, &dist);
5501
5502 double d = g_nAWMax;
5503 (pAnchorWatchPoint2->GetName()).ToDouble(&d);
5504 d = ocpn::AnchorDistFix(d, AnchorPointMinDist, g_nAWMax);
5505 bool toofar = false;
5506 bool tooclose = false;
5507 if (d >= 0) toofar = (dist * 1852. > d);
5508 if (d < 0) tooclose = (dist * 1852 < -d);
5509
5510 if (tooclose || toofar)
5511 AnchorAlertOn2 = true;
5512 else
5513 AnchorAlertOn2 = false;
5514 } else
5515 AnchorAlertOn2 = false;
5516
5518 AnchorAlertOn1 = true;
5519}
5520
5521void MyFrame::SendFixToPlugins() {
5522 // Build and send a Position Fix event to PlugIns
5523 if (g_pi_manager) {
5524 GenericPosDatEx GPSData;
5525 GPSData.kLat = gLat;
5526 GPSData.kLon = gLon;
5527 GPSData.kCog = gCog;
5528 GPSData.kSog = gSog;
5529 GPSData.kVar = gVar;
5530 GPSData.kHdm = gHdm;
5531 GPSData.kHdt = gHdt;
5532 GPSData.nSats = g_SatsInView;
5533
5534 wxDateTime tCheck((time_t)m_fixtime);
5535 if (tCheck.IsValid()) {
5536 // As a special case, when no GNSS data is available, m_fixtime is set to
5537 // zero. Note wxDateTime(0) is valid, so the zero value is passed to the
5538 // plugins. The plugins should check for zero and not use the time in that
5539 // case.
5540 GPSData.FixTime = m_fixtime;
5541 } else {
5542 // Note: I don't think this is ever reached, as m_fixtime can never be set
5543 // to wxLongLong(wxINT64_MIN), which is the only way to get here.
5544 GPSData.FixTime = wxDateTime::Now().GetTicks();
5545 }
5546
5547 SendPositionFixToAllPlugIns(&GPSData);
5548 }
5549}
5550
5551void MyFrame::ProcessLogAndBells() {
5552 // Send current nav status data to log file on every half hour // pjotrc
5553 // 2010.02.09
5554 wxDateTime lognow = wxDateTime::Now(); // pjotrc 2010.02.09
5555 int hourLOC = lognow.GetHour();
5556 int minuteLOC = lognow.GetMinute();
5557 lognow.MakeGMT();
5558 int minuteUTC = lognow.GetMinute();
5559 int second = lognow.GetSecond();
5560
5561 wxTimeSpan logspan = lognow.Subtract(g_loglast_time);
5562 if ((logspan.IsLongerThan(wxTimeSpan(0, 30, 0, 0))) || (minuteUTC == 0) ||
5563 (minuteUTC == 30)) {
5564 if (logspan.IsLongerThan(wxTimeSpan(0, 1, 0, 0))) {
5565 wxString day = lognow.FormatISODate();
5566 wxString utc = lognow.FormatISOTime();
5567 wxString navmsg = "LOGBOOK: ";
5568 navmsg += day;
5569 navmsg += " ";
5570 navmsg += utc;
5571 navmsg += " UTC ";
5572
5573 if (bGPSValid) {
5574 wxString data;
5575 data.Printf(" GPS Lat %10.5f Lon %10.5f ", gLat, gLon);
5576 navmsg += data;
5577
5578 wxString cog;
5579 if (std::isnan(gCog))
5580 cog.Printf("COG ----- ");
5581 else
5582 cog.Printf("COG %10.5f ", gCog);
5583
5584 wxString sog;
5585 if (std::isnan(gSog))
5586 sog.Printf("SOG ----- ");
5587 else
5588 sog.Printf("SOG %6.2f " + getUsrSpeedUnit(), toUsrSpeed(gSog));
5589
5590 navmsg += cog;
5591 navmsg += sog;
5592 } else {
5593 wxString data;
5594 data.Printf(" DR Lat %10.5f Lon %10.5f", gLat, gLon);
5595 navmsg += data;
5596 }
5597 wxLogMessage(navmsg);
5598 g_loglast_time = lognow;
5599
5600 int bells = (hourLOC % 4) * 2; // 2 bells each hour
5601 if (minuteLOC != 0) bells++; // + 1 bell on 30 minutes
5602 if (!bells) bells = 8; // 0 is 8 bells
5603
5604 if (g_bPlayShipsBells && ((minuteLOC == 0) || (minuteLOC == 30))) {
5605 m_BellsToPlay = bells;
5606 wxCommandEvent ev(BELLS_PLAYED_EVTYPE);
5607 wxPostEvent(this, ev);
5608 }
5609 }
5610 }
5611}
5612
5613void MyFrame::OnFrameTimer1(wxTimerEvent &event) {
5614 CheckToolbarPosition();
5615
5616 ProcessUnitTest();
5617 g_tick++;
5618 if (ProcessQuitFlag()) return;
5619
5620 if (bDBUpdateInProgress) return;
5621
5622 FrameTimer1.Stop();
5623 FrameTenHzTimer.Stop();
5624
5625 ProcessDeferredTrackOn();
5626 SendFixToPlugins();
5627 ProcessAnchorWatch();
5628 ProcessLogAndBells();
5629
5630 if (ShouldRestartTrack()) TrackDailyRestart();
5631
5632 // If no alerts are on, then safe to resume sleeping
5633 if (g_bSleep && !AnchorAlertOn1 && !AnchorAlertOn2) {
5634 FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
5635 FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
5636 return;
5637 }
5638
5639 // If gSog is greater than some threshold,
5640 // we determine that we are"cruising"
5641 if (gSog > 3.0) g_bCruising = true;
5642
5643 // Update the Toolbar Status windows and lower status bar
5644 // just after start of ticks.
5645
5646 if (g_tick == 2) {
5647 wxString sogcog("SOG --- " + getUsrSpeedUnit() + +" " +
5648 " COG ---\u00B0");
5649 if (GetStatusBar()) SetStatusText(sogcog, STAT_FIELD_SOGCOG);
5650
5651 gCog = 0.0; // say speed is zero to kill ownship predictor
5652 }
5653
5654 // Update the chart database and displayed chart
5655 bool bnew_view = false;
5656 if (!g_btenhertz) bnew_view = DoChartUpdate();
5657
5658 g_blinker_tick++;
5659
5661
5662 // This call sends autopilot output strings to output ports.
5663 bool bactiveRouteUpdate = RoutemanGui(*g_pRouteMan).UpdateProgress();
5664
5665 // For each canvas....
5666 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5667 ChartCanvas *cc = g_canvasArray.Item(i);
5668 if (cc) {
5669 cc->DrawBlinkObjects();
5670
5671 // Update the active route, if any, as determined above
5672 if (bactiveRouteUpdate) {
5673 // This RefreshRect will cause any active routepoint to blink
5674 if (g_pRouteMan->GetpActiveRoute())
5675 cc->RefreshRect(g_blink_rect, false);
5676 }
5677
5678 // Force own-ship drawing parameters
5679 cc->SetOwnShipState(SHIP_NORMAL);
5680
5681 if (cc->GetQuiltMode()) {
5682 double erf = cc->GetQuiltMaxErrorFactor();
5683 if (erf > 0.02) cc->SetOwnShipState(SHIP_LOWACCURACY);
5684 } else {
5685 if (cc->m_singleChart) {
5686 if (cc->m_singleChart->GetChart_Error_Factor() > 0.02)
5687 cc->SetOwnShipState(SHIP_LOWACCURACY);
5688 }
5689 }
5690
5691 if (!bGPSValid) cc->SetOwnShipState(SHIP_INVALID);
5692
5693 if ((bGPSValid != m_last_bGPSValid) ||
5694 (bVelocityValid != m_last_bVelocityValid) ||
5695 (!isnan(gHdt) && (gHdt != m_last_hdt))) {
5696 if (!g_bopengl) cc->UpdateShips();
5697
5698 bnew_view = true; // force a full Refresh()
5699 }
5700 }
5701 }
5702
5703 m_last_bGPSValid = bGPSValid;
5704 m_last_bVelocityValid = bVelocityValid;
5705 m_last_hdt = gHdt;
5706
5707 // If any PlugIn requested dynamic overlay callbacks, force a full canvas
5708 // refresh thus, ensuring at least 1 Hz. callback.
5709 bool brq_dynamic = false;
5710 if (g_pi_manager) {
5711 auto *pplugin_array = PluginLoader::GetInstance()->GetPlugInArray();
5712 for (unsigned int i = 0; i < pplugin_array->GetCount(); i++) {
5713 PlugInContainer *pic = pplugin_array->Item(i);
5714 if (pic->m_enabled && pic->m_init_state) {
5715 if (pic->m_cap_flag & WANTS_DYNAMIC_OPENGL_OVERLAY_CALLBACK) {
5716 brq_dynamic = true;
5717 break;
5718 }
5719 }
5720 }
5721
5722 if (brq_dynamic) bnew_view = true;
5723 }
5724
5725 // Make sure we get a redraw and alert sound on AnchorWatch excursions.
5726 if (AnchorAlertOn1 || AnchorAlertOn2) bnew_view = true;
5727
5728 // For each canvas....
5729 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5730 ChartCanvas *cc = g_canvasArray.Item(i);
5731 if (cc) {
5732 if (g_bopengl) {
5733#ifdef ocpnUSE_GL
5734 if (cc->GetglCanvas()) {
5735 // Rotation is handled by 10Hz timer, do not duplicate here
5736 bool b_rotate = cc->GetUpMode() != NORTH_UP_MODE;
5737 if (!b_rotate) {
5738 if ((!g_btenhertz)) {
5739 if (cc->m_bFollow) {
5740 cc->DoCanvasUpdate();
5741 if (bnew_view)
5742 cc->Refresh(false); // honor ownship state update
5743 } else
5744 cc->Refresh(false);
5745 }
5746 }
5747 }
5748#endif
5749 } else {
5750 // Invalidate the ChartCanvas window appropriately
5751 // In non-follow mode, invalidate the rectangles containing the AIS
5752 // targets and the ownship, etc... In follow mode, if there has
5753 // already been a full screen refresh, there is no need to check
5754 // ownship or AIS,
5755 // since they will be always drawn on the full screen paint.
5756
5757 if ((!cc->m_bFollow) || (cc->GetUpMode() != NORTH_UP_MODE)) {
5758 cc->UpdateShips();
5759 cc->UpdateAIS();
5760 cc->UpdateAlerts();
5761 } else {
5762 if (!bnew_view) { // There has not been a Refresh() yet.....
5763 cc->UpdateAIS();
5764 cc->UpdateAlerts();
5765 }
5766 }
5767 }
5768 }
5769 }
5770
5771 if (g_pais_query_dialog_active && g_pais_query_dialog_active->IsShown())
5772 g_pais_query_dialog_active->UpdateText();
5773
5774 // Refresh AIS target list every 5 seconds to avoid blinking
5775 if (g_pAISTargetList && (0 == (g_tick % (5))))
5776 g_pAISTargetList->UpdateAISTargetList();
5777
5778 // Pick up any change Toolbar status displays
5779 UpdateGPSCompassStatusBoxes();
5780 UpdateAISTool();
5781
5782 if (console && console->IsShown()) {
5783 // console->Raise();
5784 console->RefreshConsoleData();
5785 }
5786
5787 // This little hack fixes a problem seen with some UniChrome OpenGL drivers
5788 // We need a deferred resize to get glDrawPixels() to work right.
5789 // So we set a trigger to generate a resize after 5 seconds....
5790 // See the "UniChrome" hack elsewhere
5791 if (m_bdefer_resize) {
5792 if (0 == (g_tick % (5))) {
5793 printf("___RESIZE\n");
5794 SetSize(m_defer_size);
5795 g_pauimgr->Update();
5796 m_bdefer_resize = false;
5797 }
5798 }
5799
5800 // Reset pending next AppMsgBus notification
5801
5802 if (g_unit_test_2)
5803 FrameTimer1.Start(TIMER_GFRAME_1 * 3, wxTIMER_CONTINUOUS);
5804 else {
5805 FrameTimer1.Start(TIMER_GFRAME_1, wxTIMER_CONTINUOUS);
5806 FrameTenHzTimer.Start(100, wxTIMER_CONTINUOUS);
5807 }
5808}
5809
5810double MyFrame::GetMag(double a, double lat, double lon) {
5811 double Variance = std::isnan(gVar) ? g_UserVar : gVar;
5812 auto loader = PluginLoader::GetInstance();
5813 if (loader && loader->IsPlugInAvailable("WMM")) {
5814 // Request variation at a specific lat/lon
5815
5816 // Note that the requested value is returned sometime later in the event
5817 // stream, so there may be invalid data returned on the first call to this
5818 // method. In the case of rollover windows, the value is requested
5819 // continuously, so will be correct very soon.
5820 wxDateTime now = wxDateTime::Now();
5821 SendJSON_WMM_Var_Request(lat, lon, now);
5822 if (fabs(gQueryVar) < 360.0) // Don't use WMM variance if not updated yet
5823 Variance = gQueryVar;
5824 }
5825 return toMagnetic(a, Variance);
5826}
5827
5828bool MyFrame::SendJSON_WMM_Var_Request(double lat, double lon,
5829 wxDateTime date) {
5830 if (g_pi_manager) {
5831 wxJSONValue v;
5832 v["Lat"] = lat;
5833 v["Lon"] = lon;
5834 v["Year"] = date.GetYear();
5835 v["Month"] = date.GetMonth();
5836 v["Day"] = date.GetDay();
5837
5838 SendJSONMessageToAllPlugins("WMM_VARIATION_REQUEST", v);
5839 return true;
5840 } else
5841 return false;
5842}
5843
5844void MyFrame::TouchAISActive() {
5845 // .. for each canvas...
5846 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5847 ChartCanvas *cc = g_canvasArray.Item(i);
5848 if (cc) cc->TouchAISToolActive();
5849 }
5850}
5851
5852void MyFrame::UpdateAISTool() {
5853 if (!g_pAIS) return;
5854
5855 // .. for each canvas...
5856 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5857 ChartCanvas *cc = g_canvasArray.Item(i);
5858 if (cc) cc->UpdateAISTBTool();
5859 }
5860}
5861
5862// Cause refresh of active Tide/Current data, if displayed
5863void MyFrame::OnFrameTCTimer(wxTimerEvent &event) {
5864 // ..For each canvas...
5865 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5866 ChartCanvas *cc = g_canvasArray.Item(i);
5867 if (cc) cc->SetbTCUpdate(true);
5868 }
5869
5870 RefreshAllCanvas(false);
5871}
5872
5873// Keep and update the Viewport rotation angle according to average COG for
5874// COGUP mode
5875void MyFrame::OnFrameCOGTimer(wxTimerEvent &event) {
5876 return;
5877
5878 // ..For each canvas...
5879 bool b_rotate = false;
5880 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5881 ChartCanvas *cc = g_canvasArray.Item(i);
5882 if (cc) b_rotate |= (cc->GetUpMode() != NORTH_UP_MODE);
5883 }
5884
5885 if (!b_rotate) {
5886 FrameCOGTimer.Stop();
5887 return;
5888 }
5889
5890 DoCOGSet();
5891
5892 // Restart the timer, max frequency is 10 hz.
5893 int period_ms = 100;
5894 // if (g_COGAvgSec > 0) period_ms = g_COGAvgSec * 1000;
5895 FrameCOGTimer.Start(period_ms, wxTIMER_CONTINUOUS);
5896}
5897
5898void MyFrame::DoCOGSet() {
5899 // ..For each canvas...
5900 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5901 ChartCanvas *cc = g_canvasArray.Item(i);
5902 if (cc) cc->DoCanvasCOGSet();
5903 }
5904}
5905
5906void RenderShadowText(wxDC *pdc, wxFont *pFont, wxString &str, int x, int y) {
5907#ifdef DrawText
5908#undef DrawText
5909#define FIXIT
5910#endif
5911
5912 wxFont oldfont = pdc->GetFont(); // save current font
5913
5914 pdc->SetFont(*pFont);
5915 pdc->SetTextForeground(GetGlobalColor("CHGRF"));
5916 pdc->SetBackgroundMode(wxTRANSPARENT);
5917
5918 pdc->DrawText(str, x, y + 1);
5919 pdc->DrawText(str, x, y - 1);
5920 pdc->DrawText(str, x + 1, y);
5921 pdc->DrawText(str, x - 1, y);
5922
5923 pdc->SetTextForeground(GetGlobalColor("CHBLK"));
5924
5925 pdc->DrawText(str, x, y);
5926
5927 pdc->SetFont(oldfont); // restore last font
5928}
5929
5930// TODO How does this relate to per-canvas rotation?
5931void MyFrame::UpdateRotationState(double rotation) {
5932 // If rotated manually, we switch to NORTHUP
5933 g_bCourseUp = false;
5934
5935 if (fabs(rotation) > .001) {
5936 SetMenubarItemState(ID_MENU_CHART_COGUP, false);
5937 SetMenubarItemState(ID_MENU_CHART_NORTHUP, true);
5938 if (m_pMenuBar) {
5939 m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("Rotated Mode"));
5940 }
5941 } else {
5942 SetMenubarItemState(ID_MENU_CHART_COGUP, g_bCourseUp);
5943 SetMenubarItemState(ID_MENU_CHART_NORTHUP, !g_bCourseUp);
5944 if (m_pMenuBar) {
5945 m_pMenuBar->SetLabel(ID_MENU_CHART_NORTHUP, _("North Up Mode"));
5946 }
5947 }
5948
5949 UpdateGPSCompassStatusBoxes(true);
5950 DoChartUpdate();
5951}
5952
5953void MyFrame::UpdateGPSCompassStatusBoxes(bool b_force_new) {
5954 // ..For each canvas...
5955 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
5956 ChartCanvas *cc = g_canvasArray.Item(i);
5957 if (cc) cc->UpdateGPSCompassStatusBox(b_force_new);
5958 }
5959}
5960
5961// Application memory footprint management
5962
5963int MyFrame::GetApplicationMemoryUse() {
5964 int memsize = -1;
5965#ifdef __linux__
5966
5967 // Use a contrived ps command to get the virtual memory size associated
5968 // with this process
5969 wxWindow *fWin = wxWindow::FindFocus();
5970
5971 wxArrayString outputArray;
5972 wxString cmd("ps --no-headers -o vsize ");
5973 unsigned long pid = wxGetProcessId();
5974 wxString cmd1;
5975 cmd1.Printf("%ld", pid);
5976 cmd += cmd1;
5977 wxExecute(cmd, outputArray);
5978
5979 if (outputArray.GetCount()) {
5980 wxString s = outputArray.Item(0);
5981 long vtmp;
5982 if (s.ToLong(&vtmp)) memsize = vtmp;
5983 }
5984
5985 if (fWin) fWin->SetFocus();
5986
5987#endif
5988
5989#ifdef __WXMSW__
5990 HANDLE hProcess;
5991 PROCESS_MEMORY_COUNTERS pmc;
5992
5993 unsigned long processID = wxGetProcessId();
5994
5995 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE,
5996 processID);
5997 if (NULL == hProcess) return 0;
5998
5999 if (GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) {
6000 /*
6001 printf( "\tPageFaultCount: 0x%08X\n", pmc.PageFaultCount );
6002 printf( "\tPeakWorkingSetSize: 0x%08X\n",
6003 pmc.PeakWorkingSetSize );
6004 printf( "\tWorkingSetSize: 0x%08X\n", pmc.WorkingSetSize );
6005 printf( "\tQuotaPeakPagedPoolUsage: 0x%08X\n",
6006 pmc.QuotaPeakPagedPoolUsage );
6007 printf( "\tQuotaPagedPoolUsage: 0x%08X\n",
6008 pmc.QuotaPagedPoolUsage );
6009 printf( "\tQuotaPeakNonPagedPoolUsage: 0x%08X\n",
6010 pmc.QuotaPeakNonPagedPoolUsage );
6011 printf( "\tQuotaNonPagedPoolUsage: 0x%08X\n",
6012 pmc.QuotaNonPagedPoolUsage );
6013 printf( "\tPagefileUsage: 0x%08X\n", pmc.PagefileUsage );
6014 printf( "\tPeakPagefileUsage: 0x%08X\n",
6015 pmc.PeakPagefileUsage );
6016 */
6017 memsize = pmc.WorkingSetSize / 1024;
6018 }
6019
6020 CloseHandle(hProcess);
6021
6022#endif
6023
6024 return memsize;
6025}
6026
6027double MyFrame::GetBestVPScale(ChartBase *pchart) {
6028 return GetPrimaryCanvas()->GetBestVPScale(pchart);
6029}
6030
6031void MyFrame::SetChartUpdatePeriod() {
6032 // Set the chart update period based upon chart skew and skew compensator
6033
6034 g_ChartUpdatePeriod = 0; // General default
6035
6036 // In non-GL, singlele-chart mode, rotation of skewed charts is very slow
6037 // So we need to use a slower update time constant to preserve adequate UI
6038 // performance
6039 bool bskewdc = false;
6040 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6041 ChartCanvas *cc = g_canvasArray.Item(i);
6042 if (cc) {
6043 if (!g_bopengl && !cc->GetVP().b_quilt) {
6044 if (fabs(cc->GetVP().skew) > 0.0001) bskewdc = true;
6045 }
6046 if (cc->m_bFollow) g_ChartUpdatePeriod = 1;
6047 }
6048 }
6049
6050 if (bskewdc) g_ChartUpdatePeriod = g_SkewCompUpdatePeriod;
6051
6052 m_ChartUpdatePeriod = g_ChartUpdatePeriod;
6053}
6054
6055void MyFrame::UpdateControlBar(ChartCanvas *cc) {
6056 if (!cc) return;
6057 cc->UpdateCanvasControlBar();
6058}
6059
6060void MyFrame::selectChartDisplay(int type, int family) {
6061 // ..For each canvas...
6062 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6063 ChartCanvas *cc = g_canvasArray.Item(i);
6064 if (cc) cc->selectCanvasChartDisplay(type, family);
6065 }
6066
6067 UpdateGlobalMenuItems(); // update the state of the menu items (checkmarks
6068 // etc.)
6069}
6070
6071//----------------------------------------------------------------------------------
6072// DoChartUpdate
6073// Create a chartstack based on current lat/lon.
6074// Return true if a Refresh(false) was called within.
6075//----------------------------------------------------------------------------------
6076bool MyFrame::DoChartUpdate() {
6077 bool return_val = false;
6078
6079 // ..For each canvas...
6080 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6081 ChartCanvas *cc = g_canvasArray.Item(i);
6082 if (cc) return_val |= cc->DoCanvasUpdate();
6083 }
6084
6085 return return_val;
6086}
6087
6088void MyFrame::MouseEvent(wxMouseEvent &event) {
6089 int x, y;
6090 event.GetPosition(&x, &y);
6091}
6092
6093// Memory monitor support
6094#ifdef __WXMAC__
6095#include <mach/mach.h>
6096#include <mach/message.h> // for mach_msg_type_number_t
6097#include <mach/kern_return.h> // for kern_return_t
6098#include <mach/task_info.h>
6099#include <stdio.h>
6100#include <malloc/malloc.h>
6101#endif
6102
6103#ifdef __WXGTK__
6104#include <malloc.h>
6105#endif
6106
6107#if defined(__linux__)
6108#include "sys/types.h"
6109#include "sys/sysinfo.h"
6110#endif /* __linux__ */
6111
6112void MyFrame::DoPrint(void) {
6113 // avoid toolbars being printed
6114 g_PrintingInProgress = true;
6115#ifdef ocpnUSE_GL
6116 if (g_bopengl) {
6117 GetPrimaryCanvas()->GetglCanvas()->Render();
6118 GetPrimaryCanvas()->GetglCanvas()->SwapBuffers();
6119 } else
6120#endif
6121 Refresh();
6122
6123 ChartPrintout printout;
6124 if (g_bopengl) {
6125 printout.GenerateGLbmp();
6126 }
6127 auto &printer = PrintDialog::GetInstance();
6128 printer.Initialize(wxLANDSCAPE);
6129 printer.EnablePageNumbers(false);
6130 // Disable/hide the tooltips during print because they can interfere with the
6131 // print dialog
6132 bool tooltips_were_enabled = DisableTbarTooltips();
6133 printer.Print(this, &printout);
6134 if (tooltips_were_enabled) {
6135 EnableTbarTooltips();
6136 }
6137
6138 // Pass two printout objects: for preview, and possible printing.
6139 /*
6140 wxPrintDialogData printDialogData(* g_printData);
6141 wxPrintPreview *preview = new wxPrintPreview(new MyPrintout, new
6142 MyPrintout, & printDialogData); if (!preview->Ok())
6143 {
6144 delete preview;
6145 OCPNMessageBox(_T("There was a problem previewing.\nPerhaps your current
6146 printer is not set correctly?"), "Previewing", wxOK); return;
6147 }
6148
6149 wxPreviewFrame *frame = new wxPreviewFrame(preview, this, _T("Demo Print
6150 Preview"), wxPoint(100, 100), wxSize(600, 650)); frame->Centre(wxBOTH);
6151 frame->Initialize();
6152 frame->Show();
6153 */
6154 g_PrintingInProgress = false;
6155 Refresh();
6156#ifdef __WXGTK__
6157 GetPrimaryCanvas()->SetFocus();
6158 // Raise(); // I dunno why...
6159#endif
6160}
6161
6162void MyFrame::OnEvtPlugInMessage(OCPN_MsgEvent &event) {
6163 wxString message_ID = event.GetID();
6164 wxString message_JSONText = event.GetJSONText();
6165
6166 // We are free to use or ignore any or all of the PlugIn messages flying
6167 // through this pipe tee.
6168
6169 // We can possibly use the estimated magnetic variation if WMM_pi is
6170 // present, active, and we have no other source of Variation
6171 if (!g_bVAR_Rx) {
6172 if (message_ID == "WMM_VARIATION_BOAT") {
6173 // construct the JSON root object
6174 wxJSONValue root;
6175 // construct a JSON parser
6176 wxJSONReader reader;
6177
6178 // now read the JSON text and store it in the 'root' structure
6179 // check for errors before retreiving values...
6180 int numErrors = reader.Parse(message_JSONText, &root);
6181 if (numErrors > 0) {
6182 // const wxArrayString& errors = reader.GetErrors();
6183 return;
6184 }
6185
6186 // get the DECL value from the JSON message
6187 wxString decl = root["Decl"].AsString();
6188 double decl_val;
6189 decl.ToDouble(&decl_val);
6190
6191 gVar = decl_val;
6192 }
6193 }
6194
6195 if (message_ID == "WMM_VARIATION") {
6196 // construct the JSON root object
6197 wxJSONValue root;
6198 // construct a JSON parser
6199 wxJSONReader reader;
6200
6201 // now read the JSON text and store it in the 'root' structure
6202 // check for errors before retreiving values...
6203 int numErrors = reader.Parse(message_JSONText, &root);
6204 if (numErrors > 0) {
6205 // const wxArrayString& errors = reader.GetErrors();
6206 return;
6207 }
6208
6209 // get the DECL value from the JSON message
6210 wxString decl = root["Decl"].AsString();
6211 double decl_val;
6212 decl.ToDouble(&decl_val);
6213
6214 gQueryVar = decl_val;
6215 }
6216
6217 if (message_ID == "GRIB_TIMELINE") {
6218 wxJSONReader r;
6219 wxJSONValue v;
6220 int numErrors = r.Parse(message_JSONText, &v);
6221
6222 if (numErrors > 0) {
6223 wxLogMessage("GRIB_TIMELINE: JSON parse error");
6224 return;
6225 }
6226
6227 // Store old time source for comparison
6228 wxDateTime oldTimeSource = gTimeSource;
6229
6230 if (v["Day"].AsInt() == -1) {
6231 gTimeSource = wxInvalidDateTime;
6232 wxLogMessage("GRIB_TIMELINE: Reset to system time");
6233 } else {
6234 gTimeSource.Set(v["Day"].AsInt(), (wxDateTime::Month)v["Month"].AsInt(),
6235 v["Year"].AsInt(), v["Hour"].AsInt(), v["Minute"].AsInt(),
6236 v["Second"].AsInt());
6237 }
6238
6239 // Refresh tide displays if time source changed
6240 if (oldTimeSource != gTimeSource) {
6241 // Refresh all canvases that might show tide info
6242 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6243 ChartCanvas *cc = g_canvasArray.Item(i);
6244 if (cc && (cc->GetbShowTide() || cc->GetbShowCurrent())) {
6245 cc->Refresh(false);
6246
6247 // Also refresh any open tide dialog windows
6248 if (cc->pCwin) { // pCwin is the tide window pointer
6249 cc->pCwin->Refresh(false);
6250 }
6251 }
6252 }
6253 }
6254 }
6255 if (message_ID == "OCPN_TRACK_REQUEST") {
6256 wxJSONValue root;
6257 wxJSONReader reader;
6258 wxString trk_id = wxEmptyString;
6259
6260 int numErrors = reader.Parse(message_JSONText, &root);
6261 if (numErrors > 0) return;
6262
6263 if (root.HasMember("Track_ID")) trk_id = root["Track_ID"].AsString();
6264
6265 wxJSONValue v;
6266 v["Track_ID"] = trk_id;
6267 for (Track *ptrack : g_TrackList) {
6268 wxString name = wxEmptyString;
6269 if (ptrack->m_GUID == trk_id) {
6270 name = ptrack->GetName();
6271 if (name.IsEmpty()) {
6272 TrackPoint *rp = ptrack->GetPoint(0);
6273 if (rp && rp->GetCreateTime().IsValid())
6274 name = rp->GetCreateTime().FormatISODate() + " " +
6275 rp->GetCreateTime().FormatISOTime();
6276 else
6277 name = _("(Unnamed Track)");
6278 }
6279
6280 /* To avoid memory problems send a single trackpoint.
6281 * It's up to the plugin to collect the data. */
6282 int i = 1;
6283 v["error"] = false;
6284 v["TotalNodes"] = ptrack->GetnPoints();
6285 for (int j = 0; j < ptrack->GetnPoints(); j++) {
6286 TrackPoint *tp = ptrack->GetPoint(j);
6287 v["lat"] = tp->m_lat;
6288 v["lon"] = tp->m_lon;
6289 v["NodeNr"] = i;
6290 i++;
6291 wxString msg_id("OCPN_TRACKPOINTS_COORDS");
6292 SendJSONMessageToAllPlugins(msg_id, v);
6293 }
6294 return;
6295 }
6296 v["error"] = true;
6297
6298 wxString msg_id("OCPN_TRACKPOINTS_COORDS");
6299 SendJSONMessageToAllPlugins(msg_id, v);
6300 }
6301 } else if (message_ID == "OCPN_ROUTE_REQUEST") {
6302 wxJSONValue root;
6303 wxJSONReader reader;
6304 wxString guid = wxEmptyString;
6305
6306 int numErrors = reader.Parse(message_JSONText, &root);
6307 if (numErrors > 0) {
6308 return;
6309 }
6310
6311 if (root.HasMember("GUID")) guid = root["GUID"].AsString();
6312
6313 wxJSONValue v;
6314 v["GUID"] = guid;
6315 for (auto it = pRouteList->begin(); it != pRouteList->end(); ++it) {
6316 wxString name = wxEmptyString;
6317
6318 if ((*it)->m_GUID == guid) {
6319 name = (*it)->m_RouteNameString;
6320 if (name.IsEmpty()) name = _("(Unnamed Route)");
6321
6322 v["Name"] = name;
6323 v["error"] = false;
6324 wxJSONValue w;
6325 int i = 0;
6326 for (RoutePointList::iterator itp = (*it)->pRoutePointList->begin();
6327 itp != (*it)->pRoutePointList->end(); itp++) {
6328 w[i]["lat"] = (*itp)->m_lat;
6329 w[i]["lon"] = (*itp)->m_lon;
6330 w[i]["Name"] = (*itp)->GetName();
6331 w[i]["Description"] = (*itp)->GetDescription();
6332 w[i]["GUID"] = (*itp)->m_GUID;
6333 w[i]["ArrivalRadius"] = (*itp)->GetWaypointArrivalRadius();
6334
6335 auto node = (*itp)->m_HyperlinkList->begin();
6336 if (node != (*itp)->m_HyperlinkList->end()) {
6337 int n = 1;
6338 while (node != (*itp)->m_HyperlinkList->end()) {
6339 Hyperlink *httpLink = *node;
6340 v[i]["WPLink" + wxString::Format("%d", n)] = httpLink->Link;
6341 v[i]["WPLinkDesciption" + wxString::Format("%d", n++)] =
6342 httpLink->DescrText;
6343 ++node;
6344 }
6345 }
6346 i++;
6347 }
6348 v["waypoints"] = w;
6349 wxString msg_id("OCPN_ROUTE_RESPONSE");
6350 SendJSONMessageToAllPlugins(msg_id, v);
6351 return;
6352 }
6353 }
6354
6355 v["error"] = true;
6356
6357 wxString msg_id("OCPN_ROUTE_RESPONSE");
6358 SendJSONMessageToAllPlugins(msg_id, v);
6359 } else if (message_ID == "OCPN_ROUTELIST_REQUEST") {
6360 wxJSONValue root;
6361 wxJSONReader reader;
6362 bool route = true;
6363
6364 int numErrors = reader.Parse(message_JSONText, &root);
6365 if (numErrors > 0) return;
6366
6367 if (root.HasMember("mode")) {
6368 wxString str = root["mode"].AsString();
6369 if (str == "Track") route = false;
6370
6371 wxJSONValue v;
6372 int i = 1;
6373 if (route) {
6374 for (RouteList::iterator it = pRouteList->begin();
6375 it != pRouteList->end(); it++) {
6376 wxString name = (*it)->m_RouteNameString;
6377 if (name.IsEmpty()) name = _("(Unnamed Route)");
6378
6379 v[i]["error"] = false;
6380 v[i]["name"] = name;
6381 v[i]["GUID"] = (*it)->m_GUID;
6382 v[i]["active"] = (*it)->IsActive();
6383 i++;
6384 }
6385 } else { // track
6386 for (Track *ptrack : g_TrackList) {
6387 wxString name = ptrack->GetName();
6388 if (name.IsEmpty()) {
6389 TrackPoint *tp = ptrack->GetPoint(0);
6390 if (tp && tp->GetCreateTime().IsValid())
6391 name = tp->GetCreateTime().FormatISODate() + " " +
6392 tp->GetCreateTime().FormatISOTime();
6393 else
6394 name = _("(Unnamed Track)");
6395 }
6396 v[i]["error"] = false;
6397 v[i]["name"] = name;
6398 v[i]["GUID"] = ptrack->m_GUID;
6399 v[i]["active"] = g_pActiveTrack == ptrack;
6400 i++;
6401 }
6402 }
6403 wxString msg_id("OCPN_ROUTELIST_RESPONSE");
6404 SendJSONMessageToAllPlugins(msg_id, v);
6405 } else {
6406 wxJSONValue v;
6407 v[0]["error"] = true;
6408 wxString msg_id("OCPN_ROUTELIST_RESPONSE");
6409 SendJSONMessageToAllPlugins(msg_id, v);
6410 }
6411 } else if (message_ID == "OCPN_ACTIVE_ROUTELEG_REQUEST") {
6412 wxJSONValue v;
6413 v[0]["error"] = true;
6414 if (g_pRouteMan->GetpActiveRoute()) {
6415 if (g_pRouteMan->m_bDataValid) {
6416 v[0]["error"] = false;
6417 v[0]["range"] = g_pRouteMan->GetCurrentRngToActivePoint();
6418 v[0]["bearing"] = g_pRouteMan->GetCurrentBrgToActivePoint();
6419 v[0]["XTE"] = g_pRouteMan->GetCurrentXTEToActivePoint();
6420 v[0]["active_route_GUID"] = g_pRouteMan->GetpActiveRoute()->GetGUID();
6421 v[0]["active_waypoint_lat"] =
6422 g_pRouteMan->GetpActiveRoute()->m_pRouteActivePoint->GetLatitude();
6423 v[0]["active_waypoint_lon"] =
6424 g_pRouteMan->GetpActiveRoute()->m_pRouteActivePoint->GetLongitude();
6425 }
6426 }
6427 wxString msg_id("OCPN_ACTIVE_ROUTELEG_RESPONSE");
6428 SendJSONMessageToAllPlugins(msg_id, v);
6429 }
6430}
6431
6432void MyFrame::FilterCogSog() {
6433 if (g_bfilter_cogsog) {
6434 // Simple averaging filter for COG
6435 double cog_last = gCog; // most recent reported value
6436
6437 // Make a hole in array
6438 for (int i = g_COGFilterSec - 1; i > 0; i--)
6439 COGFilterTable[i] = COGFilterTable[i - 1];
6440 COGFilterTable[0] = cog_last;
6441
6442 // If the lastest data is undefined, leave it
6443 if (!std::isnan(cog_last)) {
6444 //
6445 double sum = 0., count = 0;
6446 for (int i = 0; i < g_COGFilterSec; i++) {
6447 double adder = COGFilterTable[i];
6448 if (std::isnan(adder)) continue;
6449
6450 if (fabs(adder - cog_last) > 180.) {
6451 if ((adder - cog_last) > 0.)
6452 adder -= 360.;
6453 else
6454 adder += 360.;
6455 }
6456
6457 sum += adder;
6458 count++;
6459 }
6460 sum /= count;
6461
6462 if (sum < 0.)
6463 sum += 360.;
6464 else if (sum >= 360.)
6465 sum -= 360.;
6466
6467 gCog = sum;
6468 }
6469
6470 // Simple averaging filter for SOG
6471 double sog_last = gSog; // most recent reported value
6472
6473 // Make a hole in array
6474 for (int i = g_SOGFilterSec - 1; i > 0; i--)
6475 SOGFilterTable[i] = SOGFilterTable[i - 1];
6476 SOGFilterTable[0] = sog_last;
6477
6478 // If the data are undefined, leave the array intact
6479 if (!std::isnan(gSog)) {
6480 double sum = 0., count = 0;
6481 for (int i = 0; i < g_SOGFilterSec; i++) {
6482 if (std::isnan(SOGFilterTable[i])) continue;
6483
6484 sum += SOGFilterTable[i];
6485 count++;
6486 }
6487 sum /= count;
6488
6489 gSog = sum;
6490 }
6491 }
6492}
6493
6494void MyFrame::LoadHarmonics() {
6495 if (!ptcmgr) {
6496 ptcmgr = new TCMgr;
6497 ptcmgr->LoadDataSources(TideCurrentDataSet);
6498 } else {
6499 bool b_newdataset = false;
6500
6501 // Test both ways
6502 for (auto a : ptcmgr->GetDataSet()) {
6503 bool b_foundi = false;
6504 for (auto b : TideCurrentDataSet) {
6505 if (a == b) {
6506 b_foundi = true;
6507 break; // j loop
6508 }
6509 }
6510 if (!b_foundi) {
6511 b_newdataset = true;
6512 break; // i loop
6513 }
6514 }
6515
6516 for (auto a : TideCurrentDataSet) {
6517 bool b_foundi = false;
6518 for (auto b : ptcmgr->GetDataSet()) {
6519 if (a == b) {
6520 b_foundi = true;
6521 break; // j loop
6522 }
6523 }
6524 if (!b_foundi) {
6525 b_newdataset = true;
6526 break; // i loop
6527 }
6528 }
6529
6530 if (b_newdataset) ptcmgr->LoadDataSources(TideCurrentDataSet);
6531 }
6532}
6533
6534void MyFrame::ActivateAISMOBRoute(const AisTargetData *ptarget) {
6535 if (!ptarget) return;
6536
6537 // The MOB point
6538 wxDateTime mob_time = wxDateTime::Now();
6539 wxString mob_label(_("AIS MAN OVERBOARD"));
6540 mob_label += _(" on ");
6541 mob_label += ocpn::toUsrDateTimeFormat(mob_time);
6542
6543 RoutePoint *pWP_MOB = new RoutePoint(ptarget->Lat, ptarget->Lon, "mob",
6544 mob_label, wxEmptyString);
6545 pWP_MOB->SetShared(true);
6546 pWP_MOB->m_bIsolatedMark = true;
6547 pSelect->AddSelectableRoutePoint(ptarget->Lat, ptarget->Lon, pWP_MOB);
6548 // pConfig->AddNewWayPoint(pWP_MOB, -1); // use auto next num
6549 NavObj_dB::GetInstance().InsertRoutePoint(pWP_MOB);
6550
6551 pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
6552
6553 /* We want to start tracking any MOB in range (Which will trigger false alarms
6554 with messages received over the network etc., but will a) not discard nearby
6555 event even in case our GPS is momentarily unavailable and b) work even when
6556 the boat is stationary, in which case some GPS units do not provide COG) if(
6557 bGPSValid && !std::isnan(gCog) && !std::isnan(gSog) ) { */
6558 RoutePoint *pWP_src = new RoutePoint(gLat, gLon, g_default_wp_icon,
6559 wxString(_("Own ship")), wxEmptyString);
6560 pSelect->AddSelectableRoutePoint(gLat, gLon, pWP_src);
6561 pWP_MOB->SetUseSca(false); // Do not use scaled hiding for MOB
6562 pAISMOBRoute = new Route();
6563 pRouteList->push_back(pAISMOBRoute);
6564
6565 pAISMOBRoute->AddPoint(pWP_src);
6566 pAISMOBRoute->AddPoint(pWP_MOB);
6567
6568 pSelect->AddSelectableRouteSegment(ptarget->Lat, ptarget->Lon, gLat, gLon,
6569 pWP_src, pWP_MOB, pAISMOBRoute);
6570
6571 pAISMOBRoute->m_RouteNameString = _("Temporary AISMOB Route");
6572 pAISMOBRoute->m_RouteStartString = _("Present own ship");
6573 pAISMOBRoute->m_RouteEndString = mob_label;
6574
6576
6577 pAISMOBRoute->SetRouteArrivalRadius(-1.0); // never arrives
6578
6579 if (g_pRouteMan->GetpActiveRoute()) g_pRouteMan->DeactivateRoute();
6580 // g_pRouteMan->ActivateRoute( pAISMOBRoute, pWP_MOB );
6581
6582 wxJSONValue v;
6583 v["GUID"] = pAISMOBRoute->m_GUID;
6584 wxString msg_id("OCPN_MAN_OVERBOARD");
6585 SendJSONMessageToAllPlugins(msg_id, v);
6586 //}
6587
6588 if (RouteManagerDialog::getInstanceFlag()) {
6589 if (pRouteManagerDialog && pRouteManagerDialog->IsShown()) {
6590 pRouteManagerDialog->UpdateRouteListCtrl();
6591 pRouteManagerDialog->UpdateWptListCtrl();
6592 }
6593 }
6594
6595 RefreshAllCanvas(false);
6596
6597 wxString mob_message(_("AIS MAN OVERBOARD"));
6598 mob_message += _(" Time: ");
6599 mob_message += ocpn::toUsrDateTimeFormat(mob_time);
6600 mob_message += _(" Ownship Position: ");
6601 mob_message += toSDMM(1, gLat);
6602 mob_message += " ";
6603 mob_message += toSDMM(2, gLon);
6604 mob_message += _(" MOB Position: ");
6605 mob_message += toSDMM(1, ptarget->Lat);
6606 mob_message += " ";
6607 mob_message += toSDMM(2, ptarget->Lon);
6608 wxLogMessage(mob_message);
6609}
6610
6611void MyFrame::UpdateAISMOBRoute(const AisTargetData *ptarget) {
6612 if (pAISMOBRoute && ptarget) {
6613 // Update Current Ownship point
6614 RoutePoint *OwnPoint = pAISMOBRoute->GetPoint(1);
6615 OwnPoint->m_lat = gLat;
6616 OwnPoint->m_lon = gLon;
6617
6618 pSelect->DeleteSelectableRoutePoint(OwnPoint);
6619 pSelect->AddSelectableRoutePoint(gLat, gLon, OwnPoint);
6620
6621 // Update Current MOB point
6622 RoutePoint *MOB_Point = pAISMOBRoute->GetPoint(2);
6623 MOB_Point->m_lat = ptarget->Lat;
6624 MOB_Point->m_lon = ptarget->Lon;
6625
6626 pSelect->DeleteSelectableRoutePoint(MOB_Point);
6627 pSelect->AddSelectableRoutePoint(ptarget->Lat, ptarget->Lon, MOB_Point);
6628
6629 pSelect->UpdateSelectableRouteSegments(OwnPoint);
6630 pSelect->UpdateSelectableRouteSegments(MOB_Point);
6631 }
6632
6633 RefreshAllCanvas(false);
6634
6635 if (ptarget) {
6636 wxDateTime mob_time = wxDateTime::Now();
6637
6638 wxString mob_message(_("AIS MAN OVERBOARD UPDATE"));
6639 mob_message += _(" Time: ");
6640 mob_message += ocpn::toUsrDateTimeFormat(mob_time);
6641 mob_message += _(" Ownship Position: ");
6642 mob_message += toSDMM(1, gLat);
6643 mob_message += " ";
6644 mob_message += toSDMM(2, gLon);
6645 mob_message += _(" MOB Position: ");
6646 mob_message += toSDMM(1, ptarget->Lat);
6647 mob_message += " ";
6648 mob_message += toSDMM(2, ptarget->Lon);
6649
6650 wxLogMessage(mob_message);
6651 }
6652}
6653
6654void MyFrame::applySettingsString(wxString settings) {
6655 // Save some present values
6656 int last_UIScaleFactor = g_GUIScaleFactor;
6657 bool previous_expert = g_bUIexpert;
6658 g_last_ChartScaleFactor = g_ChartScaleFactor;
6659 ArrayOfCDI *pNewDirArray = new ArrayOfCDI;
6660
6661 int rr =
6662 g_Platform->platformApplyPrivateSettingsString(settings, pNewDirArray);
6663
6664 // And apply the changes
6665 pConfig->UpdateSettings();
6666
6667 // Might need to rebuild symbols
6668 if (g_last_ChartScaleFactor != g_ChartScaleFactor) rr |= S52_CHANGED;
6669
6670 if (rr & S52_CHANGED) {
6671 if (ps52plib) {
6672 ps52plib->FlushSymbolCaches(ChartCtxFactory());
6673 ps52plib
6674 ->ClearCNSYLUPArray(); // some CNSY depends on renderer (e.g. CARC)
6675 ps52plib->GenerateStateHash();
6676 }
6677 }
6678
6679 ProcessOptionsDialog(rr, pNewDirArray);
6680
6681 // Try to detect if the toolbar is changing, to avoid a rebuild if not
6682 // necessary.
6683
6684 bool b_newToolbar = false;
6685
6686 if (g_GUIScaleFactor != last_UIScaleFactor) b_newToolbar = true;
6687
6688 if (previous_expert != g_bUIexpert) b_newToolbar = true;
6689
6690 if (rr & TOOLBAR_CHANGED) {
6691 b_newToolbar = true;
6692 }
6693
6694 // We do this is one case only to remove an orphan recovery window
6695#ifdef __ANDROID__
6696 if (previous_expert && !g_bUIexpert) {
6697 androidForceFullRepaint();
6698 }
6699#endif
6700
6701 if (previous_expert != g_bUIexpert) g_Platform->applyExpertMode(g_bUIexpert);
6702
6703 // We set the compass size first, since that establishes the available space
6704 // for the toolbar.
6705 SetGPSCompassScale();
6706 // ..For each canvas...
6707 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
6708 ChartCanvas *cc = g_canvasArray.Item(i);
6709 if (cc) cc->GetCompass()->SetScaleFactor(g_compass_scalefactor);
6710 }
6711 UpdateGPSCompassStatusBoxes(true);
6712
6713 if (b_newToolbar) {
6714 AbstractPlatform::ShowBusySpinner();
6715
6716 SetAllToolbarScale();
6717 RequestNewToolbars(
6718 true); // Force rebuild, to pick up bGUIexpert and scale settings.
6719
6720 AbstractPlatform::HideBusySpinner();
6721
6722 RequestNewMasterToolbar(true);
6723 }
6724
6725 // gFrame->Raise();
6726
6727 InvalidateAllGL();
6728 DoChartUpdate();
6729 UpdateControlBar(GetPrimaryCanvas());
6730 Refresh();
6731
6732 if (console) console->Raise();
6733
6734 Refresh(false);
6735 if (m_data_monitor->IsVisible()) m_data_monitor->Raise();
6736}
6737
6738#ifdef wxHAS_POWER_EVENTS
6739void MyFrame::OnSuspending(wxPowerEvent &event) {
6740 // wxDateTime now = wxDateTime::Now();
6741 // printf("OnSuspending...%d\n", now.GetTicks());
6742
6743 wxLogMessage("System suspend starting...");
6744}
6745
6746void MyFrame::OnSuspended(wxPowerEvent &WXUNUSED(event)) {
6747 // wxDateTime now = wxDateTime::Now();
6748 // printf("OnSuspended...%d\n", now.GetTicks());
6749 wxLogMessage("System is going to suspend.");
6750}
6751
6752void MyFrame::OnSuspendCancel(wxPowerEvent &WXUNUSED(event)) {
6753 // wxDateTime now = wxDateTime::Now();
6754 // printf("OnSuspendCancel...%d\n", now.GetTicks());
6755 wxLogMessage("System suspend was cancelled.");
6756}
6757
6758int g_last_resume_ticks;
6759void MyFrame::OnResume(wxPowerEvent &WXUNUSED(event)) {
6760 wxDateTime now = wxDateTime::Now();
6761 wxLogMessage("System resumed from suspend.");
6762
6763 if ((now.GetTicks() - g_last_resume_ticks) > 5) {
6764 SystemEvents::GetInstance().evt_resume.Notify();
6765
6766 wxLogMessage("Restarting streams.");
6767 g_last_resume_ticks = now.GetTicks();
6768// FIXME (dave)
6769#if 0
6770 if (g_pMUX) {
6771 g_pMUX->ClearStreams();
6772
6773 g_pMUX->StartAllStreams();
6774 }
6775#endif
6776 }
6777
6778 // If OpenGL is enabled, Windows Resume does not properly refresh the
6779 // application GL context. We need to force a Resize event that actually does
6780 // something.
6781 if (g_bopengl) {
6782 if (IsMaximized()) { // This is not real pretty on-screen, but works
6783 Maximize(false);
6784 wxYield();
6785 Maximize(true);
6786 } else {
6787 wxSize sz = GetSize();
6788 SetSize(wxSize(sz.x - 1, sz.y));
6789 wxYield();
6790 SetSize(sz);
6791 }
6792 }
6793}
6794#endif // wxHAS_POWER_EVENTS
6795
6796//----------------------------------------------------------------------------------------------------------
6797// Master Toolbar support
6798//----------------------------------------------------------------------------------------------------------
6799
6800void MyFrame::RequestNewMasterToolbar(bool bforcenew) {
6801 bool btbRebuild = false;
6802
6803 bool b_reshow = true;
6804 if (g_MainToolbar) {
6805 b_reshow = true; // g_MainToolbar->IsShown();
6806 float ff = fabs(g_MainToolbar->GetScaleFactor() - g_toolbar_scalefactor);
6807 if ((ff > 0.01f) || bforcenew) {
6808 g_MainToolbar->DestroyToolBar();
6809 delete g_MainToolbar;
6810 g_MainToolbar = NULL;
6811 }
6812
6813 btbRebuild = true;
6814 }
6815
6816 if (!g_MainToolbar) {
6817 long orient = g_Platform->GetDefaultToolbarOrientation();
6818 wxWindow *toolbarParent = this;
6819#ifdef __WXOSX__
6820 toolbarParent = GetPrimaryCanvas();
6821#endif
6823 toolbarParent, wxPoint(-1, -1), orient, g_toolbar_scalefactor,
6824 m_toolbar_callbacks);
6825 g_MainToolbar->SetBackGroundColorString("GREY3");
6826 g_MainToolbar->SetToolbarHideMethod(TOOLBAR_HIDE_TO_FIRST_TOOL);
6827 g_MainToolbar->SetToolConfigString(g_toolbarConfig);
6828 g_MainToolbar->EnableRolloverBitmaps(false);
6829
6830 g_MainToolbar->CreateConfigMenu();
6831 g_MainToolbar->SetDefaultPosition();
6832
6833 g_bmasterToolbarFull = true;
6834 }
6835
6836 if (g_MainToolbar) {
6837 CreateMasterToolbar();
6838 {
6839 // g_MainToolbar->RestoreRelativePosition(g_maintoolbar_x,
6840 // g_maintoolbar_y);
6841 g_MainToolbar->SetColorScheme(global_color_scheme);
6842 // g_MainToolbar->Show(b_reshow && g_bshowToolbar);
6843 }
6844 }
6845
6846 if (btbRebuild && g_MainToolbar) {
6847 g_MainToolbar->SetAutoHide(g_bAutoHideToolbar);
6848 g_MainToolbar->SetAutoHideTimer(g_nAutoHideToolbar);
6849 }
6850}
6851
6852bool MyFrame::CollapseGlobalToolbar() {
6853 if (g_MainToolbar) {
6854 m_nMasterToolCountShown = 1;
6855 g_MainToolbar->SetToolShowCount(m_nMasterToolCountShown);
6856 g_MainToolbar->GetToolbar()->InvalidateBitmaps();
6857 g_MainToolbar->Realize();
6858 g_bmasterToolbarFull = false;
6859 return true;
6860 } else
6861 return false;
6862}
6863
6864ocpnToolBarSimple *MyFrame::CreateMasterToolbar() {
6865 ocpnToolBarSimple *tb = NULL;
6866
6867 if (g_MainToolbar) tb = g_MainToolbar->GetToolbar();
6868
6869 if (!tb) return 0;
6870
6871 ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
6872
6874 ID_MASTERTOGGLE, style->GetToolIcon("MUI_menu", TOOLICON_NORMAL),
6875 wxITEM_NORMAL, _("Hide Toolbar"), "MUI_menu");
6876 tic->m_bRequired = true;
6877
6878 g_MainToolbar->AddToolItem(tic);
6879
6880 tic = new ToolbarItemContainer(
6881 ID_SETTINGS, style->GetToolIcon("MUI_settings", TOOLICON_NORMAL),
6882 wxITEM_NORMAL, _("Options"), "MUI_settings");
6883 g_MainToolbar->AddToolItem(tic);
6884
6885 tic = new ToolbarItemContainer(
6886 ID_MENU_ROUTE_NEW, style->GetToolIcon("MUI_route", TOOLICON_NORMAL),
6887 style->GetToolIcon("MUI_route", TOOLICON_TOGGLED), wxITEM_CHECK,
6888 wxString(_("Create Route")) << " (Ctrl-R)", "MUI_route");
6889
6890 g_MainToolbar->AddToolItem(tic);
6891
6892 tic = new ToolbarItemContainer(
6893 ID_ROUTEMANAGER, style->GetToolIcon("MUI_RMD", TOOLICON_NORMAL),
6894 wxITEM_NORMAL, _("Route & Mark Manager"), "MUI_RMD");
6895 g_MainToolbar->AddToolItem(tic);
6896
6897 tic = new ToolbarItemContainer(
6898 ID_TRACK, style->GetToolIcon("MUI_track", TOOLICON_NORMAL),
6899 style->GetToolIcon("MUI_track", TOOLICON_TOGGLED), wxITEM_CHECK,
6900 _("Enable Tracking"), "MUI_track");
6901 g_MainToolbar->AddToolItem(tic);
6902
6903 tic = new ToolbarItemContainer(
6904 ID_COLSCHEME, style->GetToolIcon("MUI_colorscheme", TOOLICON_NORMAL),
6905 wxITEM_NORMAL, _("Change Color Scheme"), "MUI_colorscheme");
6906 g_MainToolbar->AddToolItem(tic);
6907 // if( GetMasterToolItemShow(ID_COLSCHEME) ){
6908 // tb->AddTool( ID_COLSCHEME, "MUI_colorscheme", style->GetToolIcon(
6909 // "MUI_colorscheme", TOOLICON_NORMAL ),
6910 // tipString, wxITEM_NORMAL );
6911 // tb->SetToolTooltipHiViz( ID_COLSCHEME, true ); // cause the Tooltip to
6912 // always be visible, whatever
6913 // the colorscheme
6914 //}
6915
6916 tic = new ToolbarItemContainer(
6917 ID_PRINT, style->GetToolIcon("MUI_print", TOOLICON_NORMAL), wxITEM_NORMAL,
6918 _("Print Chart"), "MUI_print");
6919 g_MainToolbar->AddToolItem(tic);
6920
6921 tic = new ToolbarItemContainer(
6922 ID_ABOUT, style->GetToolIcon("MUI_help", TOOLICON_NORMAL), wxITEM_NORMAL,
6923 _("About OpenCPN"), "MUI_help");
6924 g_MainToolbar->AddToolItem(tic);
6925
6926 // Add any PlugIn toolbar tools that request default positioning
6927 AddDefaultPositionPlugInTools();
6928
6929 // And finally add the MOB tool
6930 tic = new ToolbarItemContainer(
6931 ID_MOB, style->GetToolIcon("mob_btn", TOOLICON_NORMAL), wxITEM_NORMAL,
6932 wxString(_("Drop MOB Marker")) << _(" (Ctrl-Space)"), "mob_btn");
6933 g_MainToolbar->AddToolItem(tic);
6934
6935 // Build the toolbar
6936 g_MainToolbar->RebuildToolbar();
6937
6938 // Realize() the toolbar for current geometry
6939 style->Unload();
6940 g_MainToolbar->Realize();
6941
6942 // Set PlugIn tool toggle states
6943 ArrayOfPlugInToolbarTools tool_array =
6944 g_pi_manager->GetPluginToolbarToolArray();
6945 for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
6946 PlugInToolbarToolContainer *pttc = tool_array.Item(i);
6947 if (!pttc->b_viz) continue;
6948
6949 if (pttc->kind == wxITEM_CHECK) tb->ToggleTool(pttc->id, pttc->b_toggle);
6950 }
6951
6952 SetMasterToolbarItemState(ID_TRACK, g_bTrackActive);
6953 if (g_bTrackActive) {
6954 g_MainToolbar->SetToolShortHelp(ID_TRACK, _("Disable Tracking"));
6955 }
6956 g_MainToolbar->Realize();
6957
6958 return tb;
6959}
6960
6961bool MyFrame::CheckAndAddPlugInTool() {
6962 if (!g_pi_manager) return false;
6963
6964 bool bret = false;
6965 ocpnToolBarSimple *tb = NULL;
6966
6967 if (g_MainToolbar) tb = g_MainToolbar->GetToolbar();
6968
6969 if (!tb) return false;
6970
6971 int n_tools = tb->GetToolsCount();
6972
6973 // Walk the PlugIn tool spec array, checking the requested position
6974 // If a tool has been requested by a plugin at this position, add it
6975 ArrayOfPlugInToolbarTools tool_array =
6976 g_pi_manager->GetPluginToolbarToolArray();
6977
6978 for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
6979 PlugInToolbarToolContainer *pttc = tool_array.Item(i);
6980 if (pttc->position == n_tools) {
6981 wxBitmap *ptool_bmp;
6982
6983 switch (global_color_scheme) {
6984 case GLOBAL_COLOR_SCHEME_DAY:
6985 ptool_bmp = pttc->bitmap_day;
6986 ;
6987 break;
6988 case GLOBAL_COLOR_SCHEME_DUSK:
6989 ptool_bmp = pttc->bitmap_dusk;
6990 break;
6991 case GLOBAL_COLOR_SCHEME_NIGHT:
6992 ptool_bmp = pttc->bitmap_night;
6993 break;
6994 default:
6995 ptool_bmp = pttc->bitmap_day;
6996 break;
6997 }
6998
7000 pttc->id, *(ptool_bmp), pttc->kind, pttc->shortHelp, "");
7001
7002 tic->m_NormalIconSVG = pttc->pluginNormalIconSVG;
7003 tic->m_RolloverIconSVG = pttc->pluginRolloverIconSVG;
7004 tic->m_ToggledIconSVG = pttc->pluginToggledIconSVG;
7005 tic->m_bPlugin = true;
7006
7007 bret = true;
7008 }
7009 }
7010
7011 // If we added a tool, call again (recursively) to allow for adding
7012 // adjacent tools
7013 if (bret)
7014 while (CheckAndAddPlugInTool()) { /* nothing to do */
7015 }
7016
7017 return bret;
7018}
7019
7020bool MyFrame::AddDefaultPositionPlugInTools() {
7021 if (!g_pi_manager) return false;
7022
7023 bool bret = false;
7024
7025 // Walk the PlugIn tool spec array, checking the requested position
7026 // If a tool has been requested by a plugin at this position, add it
7027 ArrayOfPlugInToolbarTools tool_array =
7028 g_pi_manager->GetPluginToolbarToolArray();
7029
7030 for (unsigned int i = 0; i < tool_array.GetCount(); i++) {
7031 PlugInToolbarToolContainer *pttc = tool_array.Item(i);
7032
7033 // Tool is currently tagged as invisible
7034 if (!pttc->b_viz) continue;
7035
7036 if (pttc->position == -1) // PlugIn has requested default positioning
7037 {
7038 wxBitmap *ptool_bmp;
7039
7040 switch (global_color_scheme) {
7041 case GLOBAL_COLOR_SCHEME_DAY:
7042 ptool_bmp = pttc->bitmap_day;
7043 break;
7044 case GLOBAL_COLOR_SCHEME_DUSK:
7045 ptool_bmp = pttc->bitmap_dusk;
7046 break;
7047 case GLOBAL_COLOR_SCHEME_NIGHT:
7048 ptool_bmp = pttc->bitmap_night;
7049 break;
7050 default:
7051 ptool_bmp = pttc->bitmap_day;
7052 break;
7053 }
7054
7056 pttc->id, *(ptool_bmp), pttc->kind, pttc->shortHelp, "");
7057
7058 tic->m_NormalIconSVG = pttc->pluginNormalIconSVG;
7059 tic->m_RolloverIconSVG = pttc->pluginRolloverIconSVG;
7060 tic->m_ToggledIconSVG = pttc->pluginToggledIconSVG;
7061 tic->m_bPlugin = true;
7062
7063 g_MainToolbar->AddToolItem(tic);
7064
7065 bret = true;
7066 }
7067 }
7068 return bret;
7069}
7070
7071/*************************************************************************
7072 * Global color management routines
7073 *
7074 *************************************************************************/
7075
7076#ifdef __WXMSW__
7077
7078#define NCOLORS 40
7079
7080typedef struct _MSW_COLOR_SPEC {
7081 int COLOR_NAME;
7082 wxString S52_RGB_COLOR;
7083 int SysRGB_COLOR;
7084} MSW_COLOR_SPEC;
7085
7086MSW_COLOR_SPEC color_spec[] = {{COLOR_MENU, "UIBCK", 0},
7087 {COLOR_MENUTEXT, "UITX1", 0},
7088 {COLOR_BTNSHADOW, "UIBCK", 0}, // Menu Frame
7089 {-1, "", 0}};
7090
7091void SaveSystemColors() {
7092 /*
7093 color_3dface = pGetSysColor(COLOR_3DFACE);
7094 color_3dhilite = pGetSysColor(COLOR_3DHILIGHT);
7095 color_3dshadow = pGetSysColor(COLOR_3DSHADOW);
7096 color_3ddkshadow = pGetSysColor(COLOR_3DDKSHADOW);
7097 color_3dlight = pGetSysColor(COLOR_3DLIGHT);
7098 color_activecaption = pGetSysColor(COLOR_ACTIVECAPTION);
7099 color_gradientactivecaption = pGetSysColor(27); //COLOR_3DLIGHT);
7100 color_captiontext = pGetSysColor(COLOR_CAPTIONTEXT);
7101 color_windowframe = pGetSysColor(COLOR_WINDOWFRAME);
7102 color_inactiveborder = pGetSysColor(COLOR_INACTIVEBORDER);
7103 */
7104 // Record the default system color in my substitution structure
7105 MSW_COLOR_SPEC *pcspec = &color_spec[0];
7106 while (pcspec->COLOR_NAME != -1) {
7107 pcspec->SysRGB_COLOR = pGetSysColor(pcspec->COLOR_NAME);
7108 pcspec++;
7109 }
7110}
7111
7112void RestoreSystemColors() {
7113 int element[NCOLORS];
7114 int rgbcolor[NCOLORS];
7115 int i = 0;
7116
7117 MSW_COLOR_SPEC *pcspec = &color_spec[0];
7118 while (pcspec->COLOR_NAME != -1) {
7119 element[i] = pcspec->COLOR_NAME;
7120 rgbcolor[i] = pcspec->SysRGB_COLOR;
7121
7122 pcspec++;
7123 i++;
7124 }
7125 if (pSetSysColors) {
7126 pSetSysColors(i, (unsigned long *)&element[0],
7127 (unsigned long *)&rgbcolor[0]);
7128 }
7129}
7130
7131#endif
7132
7133void SetSystemColors(ColorScheme cs) { //---------------
7134#ifdef __WXMSW__
7135 int element[NCOLORS];
7136 int rgbcolor[NCOLORS];
7137 int i = 0;
7138 if ((GLOBAL_COLOR_SCHEME_DUSK == cs) || (GLOBAL_COLOR_SCHEME_NIGHT == cs)) {
7139 MSW_COLOR_SPEC *pcspec = &color_spec[0];
7140 while (pcspec->COLOR_NAME != -1) {
7141 wxColour color = GetGlobalColor(pcspec->S52_RGB_COLOR);
7142 rgbcolor[i] = (color.Red() << 16) + (color.Green() << 8) + color.Blue();
7143 element[i] = pcspec->COLOR_NAME;
7144
7145 i++;
7146 pcspec++;
7147 }
7148
7149 pSetSysColors(i, (unsigned long *)&element[0],
7150 (unsigned long *)&rgbcolor[0]);
7151
7152 } else { // for daylight colors, use default windows colors as saved....
7153
7154 RestoreSystemColors();
7155 }
7156#endif
7157}
7158
7159// Console supporting printf functionality for Windows GUI app
7160#ifdef __WXMSW__
7161static const WORD MAX_CONSOLE_LINES =
7162 500; // maximum mumber of lines the output console should have
7163
7164// #ifdef _DEBUG
7165
7166void RedirectIOToConsole()
7167
7168{
7169 int hConHandle;
7170
7171 wxIntPtr lStdHandle;
7172
7173 CONSOLE_SCREEN_BUFFER_INFO coninfo;
7174
7175 FILE *fp;
7176
7177 // allocate a console for this app
7178
7179 AllocConsole();
7180
7181 // set the screen buffer to be big enough to let us scroll text
7182
7183 GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
7184 coninfo.dwSize.Y = MAX_CONSOLE_LINES;
7185 SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
7186
7187 // redirect unbuffered STDOUT to the console
7188
7189 lStdHandle = (wxIntPtr)GetStdHandle(STD_OUTPUT_HANDLE);
7190 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7191 fp = _fdopen(hConHandle, "w");
7192 *stdout = *fp;
7193 setvbuf(stdout, NULL, _IONBF, 0);
7194
7195 // redirect unbuffered STDIN to the console
7196
7197 lStdHandle = (wxIntPtr)GetStdHandle(STD_INPUT_HANDLE);
7198 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7199 fp = _fdopen(hConHandle, "r");
7200 *stdin = *fp;
7201 setvbuf(stdin, NULL, _IONBF, 0);
7202
7203 // redirect unbuffered STDERR to the console
7204
7205 lStdHandle = (wxIntPtr)GetStdHandle(STD_ERROR_HANDLE);
7206 hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
7207 fp = _fdopen(hConHandle, "w");
7208 *stderr = *fp;
7209 setvbuf(stderr, NULL, _IONBF, 0);
7210
7211 // make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog point to console
7212 // as well
7213
7214 // ios::sync_with_stdio();
7215}
7216
7217#endif
7218
7219void ApplyLocale() {
7220 FontMgr::Get().SetLocale(g_locale);
7221 FontMgr::Get().ScrubList();
7222
7223 // Close and re-init various objects to allow new locale to show.
7224 // delete g_options;
7225 // g_options = NULL;
7226 // g_pOptions = NULL;
7227
7228 if (pRoutePropDialog) {
7229 pRoutePropDialog->Hide();
7230 pRoutePropDialog->Destroy();
7231 pRoutePropDialog = NULL;
7232 }
7233
7234 if (pRouteManagerDialog) {
7235 pRouteManagerDialog->Hide();
7236 pRouteManagerDialog->Destroy();
7237 pRouteManagerDialog = NULL;
7238 }
7239
7240 if (console) console->SetColorScheme(global_color_scheme);
7241 if (g_pais_query_dialog_active) {
7242 g_pais_query_dialog_active->Destroy();
7243 g_pais_query_dialog_active = NULL;
7244 }
7245
7246 auto alert_dlg_active =
7247 dynamic_cast<AISTargetAlertDialog *>(g_pais_alert_dialog_active);
7248 if (alert_dlg_active) {
7249 alert_dlg_active->Destroy();
7250 g_pais_alert_dialog_active = nullptr;
7251 }
7252
7253 if (g_pAISTargetList) {
7254 if (g_pauimgr) g_pauimgr->DetachPane(g_pAISTargetList);
7255 g_pAISTargetList->Disconnect_decoder();
7256 g_pAISTargetList->Destroy();
7257 g_pAISTargetList = NULL;
7258 }
7259
7260 // Process the menubar, if present.
7261 if (gFrame->m_pMenuBar) { // remove the menu bar if it is presently enabled
7262 gFrame->SetMenuBar(NULL);
7263 gFrame->m_pMenuBar->Destroy();
7264 gFrame->m_pMenuBar = NULL;
7265 }
7266 gFrame->BuildMenuBar();
7267
7268 // Give all canvas a chance to update, if needed
7269 for (unsigned int i = 0; i < g_canvasArray.GetCount(); i++) {
7270 ChartCanvas *cc = g_canvasArray.Item(i);
7271 if (cc) cc->CanvasApplyLocale();
7272 }
7273
7274 // Capture a copy of the current perspective
7275 // So that we may restore PlugIn window sizes, position, visibility, etc.
7276 wxString perspective;
7277 pConfig->SetPath("/AUI");
7278 pConfig->Read("AUIPerspective", &perspective);
7279
7280 // Compliant Plugins will reload their locale message catalog during the
7281 // Init() method. So it is sufficient to simply deactivate, and then
7282 // re-activate, all "active" plugins.
7283 PluginLoader::GetInstance()->DeactivateAllPlugIns();
7284 PluginLoader::GetInstance()->UpdatePlugIns();
7285
7286 // // Make sure the perspective saved in the config file is
7287 // "reasonable"
7288 // // In particular, the perspective should have an entry for every
7289 // // windows added to the AUI manager so far.
7290 // // If any are not found, then use the default layout
7291 //
7292 bool bno_load = false;
7293 wxAuiPaneInfoArray pane_array_val = g_pauimgr->GetAllPanes();
7294
7295 for (unsigned int i = 0; i < pane_array_val.GetCount(); i++) {
7296 wxAuiPaneInfo pane = pane_array_val[i];
7297 if (perspective.Find(pane.name) == wxNOT_FOUND) {
7298 bno_load = true;
7299 break;
7300 }
7301 }
7302
7303 if (!bno_load) g_pauimgr->LoadPerspective(perspective, false);
7304
7305 g_pauimgr->Update();
7306
7307 if (gFrame) {
7308 gFrame->RequestNewToolbars(true);
7309 gFrame->RequestNewMasterToolbar(true);
7310 }
7311}
7312
7313class ParseENCWorkerThread : public wxThread {
7314public:
7315 ParseENCWorkerThread(wxString filename, Extent &ext, int scale)
7316 : wxThread(wxTHREAD_JOINABLE) {
7317 m_filename = filename;
7318 m_ext = ext;
7319 m_scale = scale;
7320 Create();
7321 }
7322
7323 void *Entry() {
7324 // ChartBase *pchart = ChartData->OpenChartFromDB(m_filename,
7325 // FULL_INIT); ChartData->DeleteCacheChart(pchart);
7326 s57chart *newChart = new s57chart;
7327
7328 newChart->SetNativeScale(m_scale);
7329 newChart->SetFullExtent(m_ext);
7330
7331 newChart->FindOrCreateSenc(m_filename);
7332 delete newChart;
7333 return 0;
7334 }
7335
7336 wxString m_filename;
7337 Extent m_ext;
7338 int m_scale;
7339};
7340
7341// begin duplicated code
7342static double chart_dist(int index) {
7343 double d;
7344 float clon;
7345 float clat;
7346 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
7347 // if the chart contains ownship position set the distance to 0
7348 if (cte.GetBBox().Contains(gLat, gLon))
7349 d = 0.;
7350 else {
7351 // find the nearest edge
7352 double t;
7353 clon = (cte.GetLonMax() + cte.GetLonMin()) / 2;
7354 d = DistGreatCircle(cte.GetLatMax(), clon, gLat, gLon);
7355 t = DistGreatCircle(cte.GetLatMin(), clon, gLat, gLon);
7356 if (t < d) d = t;
7357
7358 clat = (cte.GetLatMax() + cte.GetLatMin()) / 2;
7359 t = DistGreatCircle(clat, cte.GetLonMin(), gLat, gLon);
7360 if (t < d) d = t;
7361 t = DistGreatCircle(clat, cte.GetLonMax(), gLat, gLon);
7362 if (t < d) d = t;
7363 }
7364 return d;
7365}
7366
7367WX_DEFINE_SORTED_ARRAY_INT(int, MySortedArrayInt);
7368static int CompareInts(int n1, int n2) {
7369 double d1 = chart_dist(n1);
7370 double d2 = chart_dist(n2);
7371 return (int)(d1 - d2);
7372}
7373
7374class compress_target {
7375public:
7376 wxString chart_path;
7377 double distance;
7378};
7379
7380WX_DECLARE_OBJARRAY(compress_target, ArrayOfCompressTargets);
7381WX_DEFINE_OBJARRAY(ArrayOfCompressTargets);
7382
7383#include <wx/arrimpl.cpp>
7384// end duplicated code
7385
7386void ParseAllENC(wxWindow *parent) {
7387 MySortedArrayInt idx_sorted_by_distance(CompareInts);
7388
7389 // Building the cache may take a long time....
7390 // Be a little smarter.
7391 // Build a sorted array of chart database indices, sorted on distance from the
7392 // ownship currently. This way, a user may build a few chart SENCs for
7393 // immediate use, then "skip" or "cancel"out on the rest until later.
7394 int count = 0;
7395 for (int i = 0; i < ChartData->GetChartTableEntries(); i++) {
7396 /* skip if not ENC */
7397 const ChartTableEntry &cte = ChartData->GetChartTableEntry(i);
7398 if (CHART_TYPE_S57 != cte.GetChartType()) continue;
7399
7400 idx_sorted_by_distance.Add(i);
7401 count++;
7402 }
7403
7404 if (count == 0) return;
7405
7406 wxLogMessage(wxString::Format("ParseAllENC() count = %d", count));
7407
7408 // Build another array of sorted compression targets.
7409 // We need to do this, as the chart table will not be invariant
7410 // after the compression threads start, so our index array will be invalid.
7411
7412 ArrayOfCompressTargets ct_array;
7413 for (unsigned int j = 0; j < idx_sorted_by_distance.GetCount(); j++) {
7414 int i = idx_sorted_by_distance[j];
7415
7416 const ChartTableEntry &cte = ChartData->GetChartTableEntry(i);
7417 double distance = chart_dist(i);
7418
7419 wxString filename(cte.GetpFullPath(), wxConvUTF8);
7420
7422 pct->distance = distance;
7423 pct->chart_path = filename;
7424
7425 ct_array.push_back(pct);
7426 }
7427
7428 int thread_count = 0;
7429 ParseENCWorkerThread **workers = NULL;
7430
7431 if (g_nCPUCount > 0)
7432 thread_count = g_nCPUCount;
7433 else
7434 thread_count = wxThread::GetCPUCount();
7435
7436 if (thread_count < 1) {
7437 // obviously there's at least one CPU!
7438 thread_count = 1;
7439 }
7440
7441 // thread_count = 1; // for now because there is a problem with more than 1
7442
7443#if 0
7444 workers = new ParseENCWorkerThread*[thread_count];
7445 for(int t = 0; t < thread_count; t++)
7446 workers[t] = NULL;
7447#endif
7448
7449 wxGenericProgressDialog *prog = nullptr;
7450 wxSize csz = GetOCPNCanvasWindow()->GetClientSize();
7451
7452 if (1) {
7453 long style = wxPD_SMOOTH | wxPD_ELAPSED_TIME | wxPD_ESTIMATED_TIME |
7454 wxPD_REMAINING_TIME | wxPD_CAN_SKIP;
7455
7456 prog = new wxGenericProgressDialog();
7457 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
7458 prog->SetFont(*qFont);
7459
7460 prog->Create(_("OpenCPN ENC Prepare"), "Longgggggggggggggggggggggggggggg",
7461 count + 1, parent, style);
7462
7463 // make wider to show long filenames
7464 // wxSize sz = prog->GetSize();
7465 // sz.x = csz.x * 8 / 10;
7466 // prog->SetSize( sz );
7467
7468 DimeControl(prog);
7469#ifdef __WXOSX__
7470 prog->ShowWindowModal();
7471#else
7472 prog->Show();
7473#endif
7474 }
7475
7476 // parse targets
7477 bool skip = false;
7478 count = 0;
7479 for (unsigned int j = 0; j < ct_array.size(); j++) {
7480 wxString filename = ct_array[j].chart_path;
7481 double distance = ct_array[j].distance;
7482 int index = ChartData->FinddbIndex(filename);
7483 if (index < 0) continue;
7484 const ChartTableEntry &cte = ChartData->GetChartTableEntry(index);
7485 Extent ext;
7486 ext.NLAT = cte.GetLatMax();
7487 ext.SLAT = cte.GetLatMin();
7488 ext.WLON = cte.GetLonMin();
7489 ext.ELON = cte.GetLonMax();
7490
7491 int scale = cte.GetScale();
7492
7493 wxString msg;
7494 msg.Printf(_("Distance from Ownship: %4.0f NMi"), distance);
7495
7496 count++;
7497 if (wxThread::IsMain()) {
7498 if (prog) {
7499 wxSize sz = prog->GetSize();
7500 if (sz.x > 600) {
7501 msg += " Chart:";
7502 msg += filename;
7503 }
7504 prog->Update(count, msg, &skip);
7505#ifndef __WXMSW__
7506 prog->Raise();
7507#endif
7508 }
7509 if (skip) break;
7510 }
7511
7512#if 1
7513 if (ps52plib) {
7514 s57chart *newChart = new s57chart;
7515
7516 newChart->SetNativeScale(scale);
7517 newChart->SetFullExtent(ext);
7518 newChart->DisableBackgroundSENC();
7519
7520 newChart->FindOrCreateSenc(filename,
7521 false); // no progress dialog required
7522 delete newChart;
7523
7524 if (wxThread::IsMain()) {
7525 msg.Printf(_("ENC Completed."));
7526 if (prog) {
7527 prog->Update(count, msg, &skip);
7528#ifndef __WXMSW__
7529 prog->Raise();
7530#endif
7531 }
7532 if (skip) break;
7533 }
7534 }
7535
7536#else
7537 for (int t = 0;; t = (t + 1) % thread_count) {
7538 if (!workers[t]) {
7539 workers[t] = new ParseENCWorkerThread(filename);
7540 workers[t]->Run();
7541 break;
7542 }
7543
7544 if (!workers[t]->IsAlive()) {
7545 workers[t]->Wait();
7546 delete workers[t];
7547 workers[t] = NULL;
7548 }
7549 if (t == 0) {
7550 // ::wxYield(); // allow ChartCanvas main
7551 // message loop to run
7552 wxThread::Sleep(1); /* wait for a worker to finish */
7553 }
7554 }
7555#endif
7556
7557#if defined(__WXMSW__) || defined(__WXOSX__)
7558 ::wxSafeYield();
7559#endif
7560 }
7561
7562#if 0
7563 /* wait for workers to finish, and clean up after then */
7564 for(int t = 0; t<thread_count; t++) {
7565 if(workers[t]) {
7566 workers[t]->Wait();
7567 delete workers[t];
7568 }
7569 }
7570 delete [] workers;
7571#endif
7572
7573 delete prog;
7574}
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:173
double m_cursor_lat
The latitude in degrees corresponding to the most recently processed cursor position.
Definition chcanv.h:807
double GetCanvasScaleFactor()
Return the number of logical pixels per meter for the screen.
Definition chcanv.h:511
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:183
double m_cursor_lon
The longitude in degrees corresponding to the most recently processed cursor position.
Definition chcanv.h:791
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().
void Notify() override
Notify all listeners, no data supplied.
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition 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
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:108
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.
void Init(const KeyProvider &kp, const std::function< void(ObservedEvt &ev)> &action)
Initiate an object yet not listening.
Definition observable.h:295
void Listen(const std::string &key, wxEvtHandler *listener, wxEventType evt)
Set object to send wxEventType ev to listener on changes in key.
Custom event class for OpenCPN's notification system.
std::shared_ptr< const void > GetSharedPtr() const
Gets the event's payload data.
Data for a loaded plugin, including dl-loaded library.
int m_cap_flag
PlugIn Capabilities descriptor.
bool LoadAllPlugIns(bool enabled_plugins, bool keep_orphans=false)
Update catalog with imported metadata and load all plugin library files.
bool UnLoadAllPlugIns()
Unload allplugins i.
bool UpdatePlugIns()
Update the GetPlugInArray() list by reloading all plugins from disk.
const ArrayOfPlugIns * GetPlugInArray()
Return list of currently loaded plugins.
bool DeactivateAllPlugIns()
Deactivate all plugins.
static PrintDialog & GetInstance()
Get instance to handle the print process,.
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
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:138
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
The JSON parser.
Definition jsonreader.h:50
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1296
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:872
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:99
std::vector< Track * > g_TrackList
Global instance.
Definition track.cpp:96
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.