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