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