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