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