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