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