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