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