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