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