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