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