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