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