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