OpenCPN Partial API docs
OCPNPlatform.cpp
1 /***************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: OpenCPN Platform specific support utilities
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2015 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  **************************************************************************/
25 
26 #include <cstdlib>
27 #include <string>
28 #include <vector>
29 
30 #include <wx/wxprec.h>
31 
32 #ifdef __MINGW32__
33 #undef IPV6STRICT // mingw FTBS fix: missing struct ip_mreq
34 #include <windows.h>
35 #endif
36 
37 #ifndef WX_PRECOMP
38 #include <wx/wx.h>
39 #endif // precompiled headers
40 
41 #ifndef __WXMSW__
42 #include <signal.h>
43 #include <setjmp.h>
44 #endif
45 
46 #ifdef __WXMSW__
47 #include <windows.h>
48 #include <winioctl.h>
49 #include <initguid.h>
50 #include "setupapi.h" // presently stored in opencpn/src
51 #endif
52 
53 
54 #include <wx/app.h>
55 #include <wx/apptrait.h>
56 #include <wx/stdpaths.h>
57 #include <wx/filename.h>
58 #include <wx/tokenzr.h>
59 #include <wx/textfile.h>
60 #include <wx/jsonval.h>
61 #include <wx/jsonreader.h>
62 
63 #include "config.h"
64 
65 #include "model/ais_decoder.h"
66 #include "model/ais_state_vars.h"
67 #include "model/base_platform.h"
68 #include "model/cmdline.h"
69 #include "model/config_vars.h"
70 #include "model/conn_params.h"
71 #include "model/cutil.h"
72 #include "model/logger.h"
73 #include "model/ocpn_utils.h"
74 #include "model/plugin_paths.h"
75 #include "model/select.h"
76 
77 #include "AboutFrameImpl.h"
78 #include "about.h"
79 #include "displays.h"
80 #include "FontMgr.h"
81 #include "gui_lib.h"
82 #include "navutil.h"
83 #include "ocpn_frame.h"
84 #include "OCPNPlatform.h"
85 #include "options.h"
86 #include "s52s57.h"
87 #include "snd_config.h"
88 #include "styles.h"
89 
90 #ifdef __ANDROID__
91 #include "androidUTIL.h"
92 #endif
93 
94 #ifdef ocpnUSE_GL
95 #include "glChartCanvas.h"
96 #endif
97 
98 // Include CrashRpt Header
99 #ifdef OCPN_USE_CRASHREPORT
100 #include "CrashRpt.h"
101 #endif
102 #ifdef __MSVC__
103 #include <new.h>
104 #endif
105 
106 #ifdef LINUX_CRASHRPT
107 #include "crashprint.h"
108 #endif
109 
110 #ifdef __WXOSX__
111 #include "model/macutils.h"
112 #endif
113 
114 #if (defined(OCPN_GHC_FILESYSTEM) || (defined(__clang_major__) && (__clang_major__ < 15)))
115 // MacOS 1.13
116 #include <ghc/filesystem.hpp>
117 namespace fs = ghc::filesystem;
118 #else
119 #include <filesystem>
120 #include <utility>
121 namespace fs = std::filesystem;
122 #endif
123 
124 class MyApp;
125 DECLARE_APP(MyApp)
126 
127 void appendOSDirSlash(wxString *pString);
128 
129 #ifndef __WXMSW__
130 struct sigaction sa_all;
131 struct sigaction sa_all_old;
132 extern sigjmp_buf env; // the context saved by sigsetjmp();
133 #endif
134 
135 extern OCPNPlatform *g_Platform;
136 extern bool g_bFirstRun;
137 extern bool g_bUpgradeInProcess;
138 
139 extern int quitflag;
140 extern MyFrame *gFrame;
141 
142 extern MyConfig *pConfig;
143 
144 extern ocpnStyle::StyleManager *g_StyleManager;
145 
146 extern bool g_bshowToolbar;
147 extern bool g_bexpert;
148 extern bool g_bBasicMenus;
149 extern bool g_bUIexpert;
150 
151 extern bool g_bshowToolbar;
152 extern bool g_bBasicMenus;
153 
154 extern bool g_bShowOutlines;
155 extern int g_nAWDefault;
156 extern int g_nAWMax;
157 extern bool g_bPermanentMOBIcon;
158 extern float g_toolbar_scalefactor;
159 
160 extern options *g_options;
161 extern bool g_boptionsactive;
162 
163 extern wxString *pInit_Chart_Dir;
164 
165 extern std::vector<size_t> g_config_display_size_mm;
166 extern bool g_config_display_size_manual;
167 
168 extern bool g_bFullScreenQuilt;
169 extern bool g_bQuiltEnable;
170 extern bool g_bskew_comp;
171 
172 extern bool g_bopengl;
173 extern bool g_bresponsive;
174 extern bool g_bShowStatusBar;
175 extern int g_cm93_zoom_factor;
176 extern int g_GUIScaleFactor;
177 extern bool g_fog_overzoom;
178 extern bool g_oz_vector_scale;
179 extern wxString g_toolbarConfig;
180 extern bool g_bPreserveScaleOnX;
181 extern bool g_running;
182 extern bool g_bEnableZoomToCursor;
183 extern bool g_bsmoothpanzoom;
184 extern bool g_bShowMenuBar;
185 
186 extern Select *pSelectTC;
187 
188 #ifdef ocpnUSE_GL
189 extern ocpnGLOptions g_GLOptions;
190 #endif
191 extern int g_default_font_size;
192 extern wxString g_default_font_facename;
193 
194 bool g_bEmailCrashReport;
195 
196 extern double g_ChartNotRenderScaleFactor;
197 extern bool g_bRollover;
198 
199 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
200 extern wxLocale *plocale_def_lang;
201 
202 extern wxString g_locale;
203 extern wxString g_localeOverride;
204 extern wxArrayString g_locale_catalog_array;
205 
206 #endif
207 extern int options_lastPage;
208 extern AboutFrameImpl *g_pAboutDlg;
209 extern about *g_pAboutDlgLegacy;
210 extern wxColour g_colourTrackLineColour;
211 extern int g_n_ownship_min_mm;
212 
213 extern int g_AndroidVersionCode;
214 extern bool g_bShowMuiZoomButtons;
215 extern int g_FlushNavobjChangesTimeout;
216 extern wxString g_CmdSoundString;
217 extern int g_maintoolbar_x;
218 extern int g_maintoolbar_y;
219 extern std::vector<std::string> TideCurrentDataSet;
220 extern int g_Android_SDK_Version;
221 extern wxString g_androidDownloadDirectory;
222 extern wxString g_gpx_path;
223 extern BasePlatform *g_BasePlatform;
224 
225 #ifdef __ANDROID__
226 extern PlatSpec android_plat_spc;
227 #endif
228 
229 #ifdef ocpnUSE_GL
230 OCPN_GLCaps *GL_Caps = nullptr;
231 #endif
232 
233 static const char *const DEFAULT_XDG_DATA_DIRS =
234  "~/.local/share:/usr/local/share:/usr/share";
235 
236 #ifdef __WXMSW__
237 static const char PATH_SEP = ';';
238 #else
239 static const char PATH_SEP = ':';
240 #endif
241 
242 static bool checkIfFlatpacked() {
243  wxString id;
244  if (!wxGetEnv("FLATPAK_ID", &id)) {
245  return false;
246  }
247  return id == "org.opencpn.OpenCPN";
248 }
249 
250 
251 OCPNPlatform::OCPNPlatform() {
252  m_pt_per_pixel = 0; // cached value
253  m_bdisableWindowsDisplayEnum = false;
254  m_monitorWidth = m_monitorHeight = 0;
255  EnumerateMonitors();
256  for (size_t i = 0; i < g_monitor_info.size(); i++) {
257  m_displaySizeMMOverride.push_back(0);
258  }
259  m_pluginDataPath = "";
260 }
261 
262 OCPNPlatform::~OCPNPlatform() {
263 #ifdef ocpnUSE_GL
264  if (GL_Caps) {
265  delete GL_Caps;
266  }
267 #endif
268 }
269 
270 //--------------------------------------------------------------------------
271 // Per-Platform Initialization support
272 //--------------------------------------------------------------------------
273 #ifdef __WXMSW__
274 int MyNewHandler(size_t size) {
275  // Pass to wxWidgets Main Loop handler
276  throw std::bad_alloc();
277 
278  return 0;
279 }
280 #endif
281 
282 //-----------------------------------------------------------------------
283 // Signal Handlers
284 //-----------------------------------------------------------------------
285 #ifndef __WXMSW__
286 
287 // These are the signals possibly expected
288 // SIGUSR1
289 // Raised externally to cause orderly termination of application
290 // Intended to act just like pushing the "EXIT" button
291 
292 // SIGSEGV
293 // Some undefined segfault......
294 
295 int s_inhup;
296 
297 void catch_signals(int signo) {
298  switch (signo) {
299  case SIGUSR1:
300  quitflag++; // signal to the timer loop
301  break;
302 
303  case SIGSEGV:
304  siglongjmp(env, 1); // jump back to the setjmp() point
305  break;
306 
307  case SIGHUP:
308  if (!s_inhup) {
309  s_inhup++; // incase SIGHUP is closely followed by SIGTERM
310  gFrame->FastClose();
311  }
312  break;
313 
314  case SIGTERM:
315  if (!s_inhup) {
316  s_inhup++; // incase SIGHUP is closely followed by SIGTERM
317  gFrame->FastClose();
318  }
319 
320  break;
321 
322  default:
323  break;
324  }
325 }
326 #endif
327 
328 #ifdef OCPN_USE_CRASHREPORT
329 // Define the crash callback
330 int CALLBACK CrashCallback(CR_CRASH_CALLBACK_INFO *pInfo) {
331  wxLog::GetActiveTarget()->Flush(); // Flush log file
332  return CR_CB_DODEFAULT;
333 }
334 #endif
335 
336 // Called from MyApp() immediately upon entry to MyApp::OnInit()
337 void OCPNPlatform::Initialize_1(void) {
338 #ifdef OCPN_USE_CRASHREPORT
339 #ifndef _DEBUG
340  // Install Windows crash reporting
341 
342  CR_INSTALL_INFO info;
343  memset(&info, 0, sizeof(CR_INSTALL_INFO));
344  info.cb = sizeof(CR_INSTALL_INFO);
345  info.pszAppName = _T("OpenCPN");
346 
347  info.pszAppVersion = wxString(VERSION_FULL).c_str();
348 
349  int type = MiniDumpNormal;
350 
351  // This results in the inclusion of global variables
352  type |= MiniDumpWithDataSegs;
353 
354  // If this flag is specified, the contents of every readable and writeable
355  // private memory page will be included into the minidump. type |=
356  // MiniDumpWithPrivateReadWriteMemory;
357 
358  // If this flag is specified, MiniDumpWriteDump function will scan the stack
359  // memory of every thread looking for pointers that point to other readable
360  // memory pages in the process' address space. type |=
361  // MiniDumpWithIndirectlyReferencedMemory;
362 
363  info.uMiniDumpType = (MINIDUMP_TYPE)type;
364 
365  // Install all available exception handlers....
366  info.dwFlags = CR_INST_ALL_POSSIBLE_HANDLERS;
367 
368  // Except memory allocation failures
369  info.dwFlags &= ~CR_INST_NEW_OPERATOR_ERROR_HANDLER;
370 
371  // Allow user to attach files
372  info.dwFlags |= CR_INST_ALLOW_ATTACH_MORE_FILES;
373 
374  // Allow user to add more info
375  info.dwFlags |= CR_INST_SHOW_ADDITIONAL_INFO_FIELDS;
376 
377  // URL for sending error reports over HTTP.
378 
379  if (g_bEmailCrashReport) {
380  info.pszUrl = _T("https://bigdumboat.com/crashrpt/ocpn_crashrpt.php");
381  info.uPriorities[CR_HTTP] = 3; // First try send report over HTTP
382  } else {
383  info.dwFlags |= CR_INST_DONT_SEND_REPORT;
384  info.uPriorities[CR_HTTP] = CR_NEGATIVE_PRIORITY; // don't send at all
385  }
386 
387  info.uPriorities[CR_SMTP] =
388  CR_NEGATIVE_PRIORITY; // Second try send report over SMTP
389  info.uPriorities[CR_SMAPI] =
390  CR_NEGATIVE_PRIORITY; // Third try send report over Simple MAPI
391 
392  wxStandardPaths &crash_std_path = g_Platform->GetStdPaths();
393 
394  wxString crash_rpt_save_locn = crash_std_path.GetConfigDir();
395  if (g_bportable) {
396  wxFileName exec_path_crash(crash_std_path.GetExecutablePath());
397  crash_rpt_save_locn =
398  exec_path_crash.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
399  }
400 
401  wxString locn = crash_rpt_save_locn + _T("\\CrashReports");
402 
403  if (!wxDirExists(locn)) wxMkdir(locn);
404 
405  if (wxDirExists(locn)) {
406  wxCharBuffer buf = locn.ToUTF8();
407  wchar_t wlocn[256];
408  if (buf && (locn.Length() < sizeof(wlocn))) {
409  MultiByteToWideChar(0, 0, buf.data(), -1, wlocn, sizeof(wlocn) - 1);
410  info.pszErrorReportSaveDir = (LPCWSTR)wlocn;
411  }
412  }
413 
414  // Provide privacy policy URL
415  wxFileName exec_path_crash(crash_std_path.GetExecutablePath());
416  wxString policy_file =
417  exec_path_crash.GetPath(wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR);
418  policy_file += _T("PrivacyPolicy.txt");
419  policy_file.Prepend(_T("file:"));
420 
421  info.pszPrivacyPolicyURL = policy_file.c_str();
422  ;
423 
424  int nResult = crInstall(&info);
425  if (nResult != 0) {
426  TCHAR buff[256];
427  crGetLastErrorMsg(buff, 256);
428  // MessageBox(NULL, buff, _T("crInstall error, Crash Reporting disabled."),
429  // MB_OK);
430  }
431 
432  if (nResult == 0) { // Complete the installation
433  // Establish the crash callback function
434  crSetCrashCallback(CrashCallback, NULL);
435 
436  // Take screenshot of the app window at the moment of crash
437  crAddScreenshot2(CR_AS_PROCESS_WINDOWS | CR_AS_USE_JPEG_FORMAT, 95);
438 
439  // Mark some files to add to the crash report
440  wxString home_data_crash = crash_std_path.GetConfigDir();
441  if (g_bportable) {
442  wxFileName f(crash_std_path.GetExecutablePath());
443  home_data_crash = f.GetPath();
444  }
445  appendOSDirSlash(&home_data_crash);
446 
447  wxString config_crash = _T("opencpn.ini");
448  config_crash.Prepend(home_data_crash);
449  crAddFile2(config_crash.c_str(), NULL, NULL,
450  CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE);
451 
452  wxString log_crash = _T("opencpn.log");
453  log_crash.Prepend(home_data_crash);
454  crAddFile2(log_crash.c_str(), NULL, NULL,
455  CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE);
456  }
457 #endif
458 #endif
459 
460 #ifdef LINUX_CRASHRPT
461 #if wxUSE_ON_FATAL_EXCEPTION
462  // fatal exceptions handling
463  wxHandleFatalExceptions(true);
464 #endif
465 #endif
466 
467 #ifdef __MSVC__
468  // Invoke my own handler for failures of malloc/new
469  _set_new_handler(MyNewHandler);
470  // configure malloc to call the New failure handler on failure
471  _set_new_mode(1);
472 #endif
473 
474 #if 0
475 #ifdef __WXMSW__
476  // On MSW, force the entire process to run on one CPU core only
477  // This resolves some difficulty with wxThread syncronization
478  //Gets the current process handle
479  HANDLE hProc = GetCurrentProcess();
480  DWORD procMask;
481  DWORD sysMask;
482  HANDLE hDup;
483  DuplicateHandle( hProc, hProc, hProc, &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS );
484 
485  //Gets the current process affinity mask
486  GetProcessAffinityMask( hDup, &procMask, &sysMask );
487 
488  // Take a simple approach, and assume up to 4 processors
489  DWORD newMask;
490  if( ( procMask & 1 ) == 1 ) newMask = 1;
491  else
492  if( ( procMask & 2 ) == 2 ) newMask = 2;
493  else
494  if( ( procMask & 4 ) == 4 ) newMask = 4;
495  else
496  if( ( procMask & 8 ) == 8 ) newMask = 8;
497 
498  //Set te affinity mask for the process
499  BOOL res = SetProcessAffinityMask( hDup, (DWORD_PTR) newMask );
500 
501  if( res == 0 ) {
502  //Error setting affinity mask!!
503  }
504 #endif
505 #endif
506 
507 #ifdef __MSVC__
508 
509  // Handle any Floating Point Exceptions which may leak thru from other
510  // processes. The exception filter is in cutil.c
511  // Seems to only happen for W98
512 
513  wxPlatformInfo Platform;
514  if (Platform.GetOperatingSystemId() == wxOS_WINDOWS_9X)
515  SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter);
516 #endif
517 
518 #ifdef __WXMSW__
519  // _CrtSetBreakAlloc(25503);
520 #endif
521 
522 #ifndef __WXMSW__
523  // Setup Linux SIGNAL handling, for external program control
524 
525  // Build the sigaction structure
526  sa_all.sa_handler = catch_signals; // point to my handler
527  sigemptyset(&sa_all.sa_mask); // make the blocking set
528  // empty, so that all
529  // other signals will be
530  // unblocked during my handler
531  sa_all.sa_flags = 0;
532 
533  sigaction(SIGUSR1, NULL,
534  &sa_all_old); // save existing action for this signal
535 
536  // Register my request for some signals
537  sigaction(SIGUSR1, &sa_all, NULL);
538  sigaction(SIGUSR1, NULL,
539  &sa_all_old); // inspect existing action for this signal
540 
541  sigaction(SIGTERM, &sa_all, NULL);
542  sigaction(SIGTERM, NULL, &sa_all_old);
543 
544  sigaction(SIGHUP, &sa_all, NULL);
545  sigaction(SIGHUP, NULL, &sa_all_old);
546 
547 #endif
548 
549 #ifdef __ANDROID__
550  qDebug() << "Initialize_1()";
551 //#ifdef NOASSERT
552  wxDisableAsserts( ); // No asserts at all in Release mode
553 //#endif
554 #endif
555 
556 }
557 
558 // Called from MyApp() immediately before creation of MyFrame()
559 // Config is known to be loaded and stable
560 // Log is available
561 void OCPNPlatform::Initialize_2(void) {
562 #ifdef __ANDROID__
563  wxLogMessage(androidGetDeviceInfo());
564 
565  // Create some directories in App private directory
566  // Mainly required for Android 11+, but useable on all versions.
567  wxChar sep = wxFileName::GetPathSeparator();
568 
569  wxString ChartDir = GetPrivateDataDir();
570  if (ChartDir.Last() != sep) ChartDir.Append(sep);
571  ChartDir.Append( "Charts");
572  if (!::wxDirExists(ChartDir)) {
573  ::wxMkdir(ChartDir);
574  }
575 
576  wxString GRIBDir = GetPrivateDataDir();
577  if (GRIBDir.Last() != sep) GRIBDir.Append(sep);
578  GRIBDir.Append( "GRIBS");
579  if (!::wxDirExists(GRIBDir)) {
580  ::wxMkdir(GRIBDir);
581  }
582 
583  // Set the default Import/Export directory for A11+
584  if (g_Android_SDK_Version >= 30){
585  if (!g_gpx_path.StartsWith(androidGetDownloadDirectory())){
586  g_gpx_path = androidGetDownloadDirectory();
587  }
588  }
589 
590 #endif
591 
592  // Set a global toolbar scale factor
593  g_toolbar_scalefactor = GetToolbarScaleFactor(g_GUIScaleFactor);
594  auto configdir = wxFileName(GetPrivateDataDir());
595  if (!configdir.DirExists()) {
596  if (!configdir.Mkdir()) {
597  auto msg = std::string("Cannot create config directory: ");
598  wxLogWarning(msg + configdir.GetFullPath());
599  }
600  }
601 }
602 
603 // Called from MyApp()::OnInit() just after gFrame is created, so gFrame is
604 // available
605 void OCPNPlatform::Initialize_3(void) {
606  bool bcapable = IsGLCapable();
607 
608 #ifdef ocpnARM // Boot arm* platforms (meaning rPI) without OpenGL on first run
609  //bcapable = false;
610 #endif
611 
612  bool bAndroid = false;
613 #ifdef __ANDROID__
614  bAndroid = true;
615 #endif
616 
617  if(!bcapable)
618  g_bopengl = false;
619  else {
620  //g_bopengl = true;
621  g_bdisable_opengl = false;
622  pConfig->UpdateSettings();
623  }
624 
625 
626  // Try to automatically switch to guaranteed usable GL mode on an OCPN upgrade
627  // or fresh install
628 
629 #ifdef ocpnUSE_GL
630  if ((g_bFirstRun || g_bUpgradeInProcess || bAndroid) && bcapable) {
631  g_bopengl = true;
632 
633  // Set up visually nice options
634  g_GLOptions.m_bUseAcceleratedPanning = true;
635  g_GLOptions.m_bTextureCompression = true;
636  g_GLOptions.m_bTextureCompressionCaching = true;
637 
638  g_GLOptions.m_iTextureDimension = 512;
639  g_GLOptions.m_iTextureMemorySize = 64;
640 
641  g_GLOptions.m_GLPolygonSmoothing = true;
642  g_GLOptions.m_GLLineSmoothing = true;
643  }
644 #endif
645 
646  gFrame->SetGPSCompassScale();
647 
648  // Force a few items for Android, to ensure that UI is useable if config got
649  // scrambled
650  if (bAndroid) {
651  g_btouch = true;
652  }
653 
654  if (g_bFirstRun || g_bUpgradeInProcess) {
655  if (!g_bRollover) // Not explicit set before
656  g_bRollover = g_btouch ? false : true;
657  }
658 
659  g_FlushNavobjChangesTimeout = 300; // Seconds, so 5 minutes
660 }
661 
662 // Called from MyApp() just before end of MyApp::OnInit()
663 void OCPNPlatform::Initialize_4(void) {
664 #ifdef __ANDROID__
665  if (pSelect) pSelect->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
666  if (pSelectTC)
667  pSelectTC->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
668  if (pSelectAIS)
669  pSelectAIS->SetSelectPixelRadius(wxMax(25, 6.0 * getAndroidDPmm()));
670 #endif
671 
672 #ifdef __WXMAC__
673  // A bit of a hack for Mojave MacOS 10.14.
674  // Force the user to actively select "Display" tab to ensure initial rendering
675  // of canvas layout select button.
676  options_lastPage = 1;
677 #endif
678 }
679 
680 void OCPNPlatform::OnExit_1(void) {}
681 
682 void OCPNPlatform::OnExit_2(void) {
683 #ifdef OCPN_USE_CRASHREPORT
684 #ifndef _DEBUG
685  // Uninstall Windows crash reporting
686 // crUninstall();
687 #endif
688 #endif
689 }
690 
691 
692 #ifdef ocpnUSE_GL
693 
694 bool HasGLExt(wxJSONValue &glinfo, const std::string ext) {
695  if (!glinfo.HasMember("GL_EXTENSIONS")) {
696  return false;
697  }
698  for (int i = 0; i < glinfo["GL_EXTENSIONS"].Size(); i++) {
699  if (glinfo["GL_EXTENSIONS"][i].AsString() == ext) {
700  return true;
701  }
702  }
703  return false;
704 }
705 
706 bool OCPNPlatform::BuildGLCaps(void *pbuf) {
707 #ifndef __ANDROID__
708  fs::path ep(GetExePath().ToStdString());
709 #ifndef __WXMSW__
710  std::string gl_util_exe = "opencpn-glutil";
711 #else
712  std::string gl_util_exe = "opencpn-glutil.exe";
713 #endif
714  fs::path gl_util_path = ep.parent_path().append(gl_util_exe);
715 
716  if (!fs::exists(gl_util_path)) { //TODO: What to do if the utility is not found (Which it is not for developer builds that are not installed)?
717  wxLogMessage("OpenGL test utility not found at %s.", gl_util_path.c_str());
718  return false;
719  }
720 
721  std::string gl_json = fs::path(GetPrivateDataDir().ToStdString()).append("gl_caps.json").string();
722 
723  wxString cmd = wxString::Format("\"%s\" opengl-info \"%s\"", gl_util_path.c_str(), gl_json.c_str());
724 
725  wxLogMessage("Starting OpenGL test utility: %s", cmd);
726 
727  wxArrayString output;
728  if (long res = wxExecute(cmd, output); res != 0) {
729  wxLogMessage("OpenGL test utility failed with exit code %d", res);
730  for (const auto &l : output) {
731  wxLogMessage(l);
732  }
733  return false;
734  }
735 
736  wxFileInputStream fis(gl_json);
737  wxJSONReader reader;
738  wxJSONValue root;
739  reader.Parse(fis, &root);
740  if (reader.GetErrorCount() > 0){
741  wxLogMessage("Failed to parse JSON output from OpenGL test utility.");
742  for(const auto &l : reader.GetErrors()) {
743  wxLogMessage(l);
744  }
745  return false;
746  }
747 
748  OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
749 
750  if(root.HasMember("GL_RENDERER")) {
751  pcaps->Renderer = root["GL_RENDERER"].AsString();
752  } else {
753  wxLogMessage("GL_RENDERER not found.");
754  return false;
755  }
756  if (root.HasMember("GL_VERSION")) {
757  pcaps->Version = root["GL_VERSION"].AsString();
758  } else {
759  wxLogMessage("GL_VERSION not found.");
760  return false;
761  }
762  if (root.HasMember("GL_SHADING_LANGUAGE_VERSION")) {
763  pcaps->GLSL_Version = root["GL_SHADING_LANGUAGE_VERSION"].AsString();
764  } else {
765  wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found.");
766  return false;
767  }
768  if(root.HasMember("GL_USABLE")) {
769  if (!root["GL_USABLE"].AsBool()) {
770  wxLogMessage("OpenGL test utility reports that OpenGL is not usable.");
771  return false;
772  }
773  } else {
774  wxLogMessage("GL_USABLE not found.");
775  return false;
776  }
777  pcaps->dGLSL_Version = 0;
778  pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
779  if (pcaps->dGLSL_Version < 1.2) {
780  wxString msg;
781  msg.Printf(_T("GLCaps Probe: OpenGL-> GLSL Version reported: "));
782  msg += wxString(pcaps->GLSL_Version.c_str());
783  msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
784  wxLogMessage(msg);
785  pcaps->bCanDoGLSL = false;
786  return false;
787  }
788  pcaps->bCanDoGLSL = true;
789  if (HasGLExt(root, "GL_ARB_texture_non_power_of_two")) {
790  pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
791  } else if (HasGLExt(root, "GL_OES_texture_npot")) {
792  pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
793  } else if (HasGLExt(root, "GL_ARB_texture_rectangle")) {
794  pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
795  }
796 
797  pcaps->bOldIntel = false;
798 
799  pcaps->bCanDoFBO = HasGLExt(root,"GL_EXT_framebuffer_object");
800  if (!pcaps->TextureRectangleFormat) {
801  pcaps->bCanDoFBO = false;
802  }
803 
804  pcaps->bCanDoVBO = HasGLExt(root, "GL_ARB_vertex_buffer_object"); //TODO: Or the old way where we enable it without querying the extension is right?
805  gFrame->Show();
806  return true;
807 #else
808  // The original codepath doing direct probing in the main OpenCPN process, now only for Android
809  // Investigate OpenGL capabilities
810  gFrame->Show();
811  glTestCanvas *tcanvas = new glTestCanvas(gFrame);
812  tcanvas->Show();
813  wxYield();
814  wxGLContext *pctx = new wxGLContext(tcanvas);
815  tcanvas->SetCurrent(*pctx);
816 
817  OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
818 
819  char *str = (char *)glGetString(GL_RENDERER);
820  if (str == NULL) { //No GL at all...
821  wxLogMessage("GL_RENDERER not found.");
822  delete tcanvas;
823  delete pctx;
824  return false;
825  }
826  pcaps->Renderer = std::string(str);
827 
828  char *stv = (char *)glGetString(GL_VERSION);
829  if (stv == NULL) { //No GL Version...
830  wxLogMessage("GL_VERSION not found");
831  delete tcanvas;
832  delete pctx;
833  return false;
834  }
835  pcaps->Version = std::string(stv);
836 
837  char *stsv = (char *)glGetString(GL_SHADING_LANGUAGE_VERSION);
838  if (stsv == NULL) { //No GLSL...
839  wxLogMessage("GL_SHADING_LANGUAGE_VERSION not found");
840  delete tcanvas;
841  delete pctx;
842  return false;
843  }
844  pcaps->GLSL_Version = std::string(stsv);
845 
846  pcaps->dGLSL_Version = 0;
847  pcaps->dGLSL_Version = ::atof(pcaps->GLSL_Version.c_str());
848 
849  if (pcaps->dGLSL_Version < 1.2){
850  wxString msg;
851  msg.Printf(_T("GLCaps Probe: OpenGL-> GLSL Version reported: "));
852  msg += wxString(pcaps->GLSL_Version.c_str());
853  msg += "\n OpenGL disabled due to insufficient OpenGL capabilities";
854  wxLogMessage(msg);
855  pcaps->bCanDoGLSL = false;
856  delete tcanvas;
857  delete pctx;
858  return false;
859  }
860 
861  pcaps->bCanDoGLSL = true;
862 
863  if (QueryExtension("GL_ARB_texture_non_power_of_two"))
864  pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
865  else if (QueryExtension("GL_OES_texture_npot"))
866  pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
867  else if (QueryExtension("GL_ARB_texture_rectangle"))
868  pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
869 
870  pcaps->bOldIntel = false;
871 
872  // Can we use VBO?
873  pcaps->bCanDoVBO = true;
874 
875 #if defined(__WXMSW__) || defined(__WXOSX__)
876  if (pcaps->bOldIntel) pcaps->bCanDoVBO = false;
877 #endif
878 
879 #ifdef __ANDROID__
880  pcaps->bCanDoVBO = false;
881 #endif
882 
883  // Can we use FBO?
884  pcaps->bCanDoFBO = true;
885 
886 #ifndef __ANDROID__
887  // We need NPOT to support FBO rendering
888  if (!pcaps->TextureRectangleFormat) pcaps->bCanDoFBO = false;
889 
890  // We require certain extensions to support FBO rendering
891  if (!QueryExtension("GL_EXT_framebuffer_object")) pcaps->bCanDoFBO = false;
892 #endif
893 
894  delete tcanvas;
895  delete pctx;
896  return true;
897 #endif
898 }
899 #endif
900 
901 bool OCPNPlatform::IsGLCapable() {
902 #ifdef ocpnUSE_GL
903 
904 #ifdef __ANDROID__
905  return true;
906 #elif defined(CLI)
907  return false;
908 #else
909 
910  if(g_bdisable_opengl)
911  return false;
912 
913  wxLogMessage("Starting OpenGL test...");
914  wxLog::FlushActive();
915 
916  if (!GL_Caps) {
917  GL_Caps = new OCPN_GLCaps();
918  bool bcaps = BuildGLCaps(GL_Caps);
919 
920  wxLogMessage("OpenGL test complete.");
921  if (!bcaps){
922  wxLogMessage("BuildGLCaps fails.");
923  wxLog::FlushActive();
924  return false;
925  }
926  }
927 
928  // and so we decide....
929 
930  // Require a modern GLSL implementation
931  if (!GL_Caps->bCanDoGLSL) {
932  return false;
933  }
934 
935  // We insist on FBO support, since otherwise DC mode is always faster on
936  // canvas panning..
937  if (!GL_Caps->bCanDoFBO) {
938  return false;
939  }
940 
941  // OpenGL is OK for OCPN
942  wxLogMessage("OpenGL determined CAPABLE.");
943  wxLog::FlushActive();
944 
945  g_bdisable_opengl = false;
946  //g_bopengl = true;
947 
948  // Update and flush the config file
949  pConfig->UpdateSettings();
950 
951  return true;
952 #endif
953 #else
954  return false;
955 #endif
956 }
957 
958 void OCPNPlatform::SetLocaleSearchPrefixes(void) {
959 #if wxUSE_XLOCALE
960 // Add a new prefixes for search order.
961 #if defined(__WINDOWS__)
962 
963  // Legacy and system plugin location
964  wxString locale_location = GetSharedDataDir();
965  locale_location += _T("share\\locale");
966  wxLocale::AddCatalogLookupPathPrefix(locale_location);
967  wxString imsg = _T("Adding catalog lookup path: ");
968  imsg += locale_location;
969  wxLogMessage(imsg);
970 
971 
972  // Managed plugin location
973  wxFileName usrShare(GetWinPluginBaseDir() + wxFileName::GetPathSeparator());
974  usrShare.RemoveLastDir();
975  locale_location = usrShare.GetFullPath() + ("share\\locale");
976  wxLocale::AddCatalogLookupPathPrefix(locale_location);
977  imsg = _T("Adding catalog lookup path: ");
978  imsg += locale_location;
979  wxLogMessage(imsg);
980 
981 #elif defined(__ANDROID__)
982 
983  wxString locale_location = GetSharedDataDir() + _T("locale");
984  wxLocale::AddCatalogLookupPathPrefix(locale_location);
985 
986 #elif defined(__UNIX__) && !defined(__WINE__)
987 
988  // On Unix, wxWidgets defaults to installation prefix of its own, usually
989  // "/usr". On the other hand, canonical installation prefix for OpenCPN is
990  // "/usr/local".
991  wxString locale_location;
992  if (!wxGetEnv(_T("OPENCPN_PREFIX"), &locale_location)) {
993  locale_location = _T("/usr/local");
994  }
995  wxFileName location;
996  location.AssignDir(locale_location);
997  location.AppendDir(_T("share"));
998  location.SetName(_T("locale"));
999  locale_location = location.GetFullPath();
1000  wxLocale::AddCatalogLookupPathPrefix(locale_location);
1001 
1002  // And then for managed plugins
1003  std::string dir = PluginPaths::getInstance()->UserDatadir();
1004  wxString managed_locale_location(dir + "/locale");
1005  wxLocale::AddCatalogLookupPathPrefix(managed_locale_location);
1006 #endif
1007 
1008 #ifdef __WXOSX__
1009  std::string macDir =
1011  "/Library/Application Support/OpenCPN/Contents/Resources";
1012  wxString Mac_managed_locale_location(macDir);
1013  wxLocale::AddCatalogLookupPathPrefix(Mac_managed_locale_location);
1014 #endif
1015 
1016 #endif
1017 }
1018 
1019 wxString OCPNPlatform::GetDefaultSystemLocale() {
1020  wxLogMessage(_T("Getting DefaultSystemLocale..."));
1021 
1022  wxString retval = _T("en_US");
1023 
1024 #if wxUSE_XLOCALE
1025 
1026  const wxLanguageInfo *languageInfo =
1027  wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
1028  if (languageInfo) retval = languageInfo->CanonicalName;
1029 
1030 #if defined(__WXMSW__)
1031  LANGID lang_id = GetUserDefaultUILanguage();
1032 
1033  wchar_t lngcp[101];
1034  const wxLanguageInfo *languageInfoW = 0;
1035  if (0 != GetLocaleInfo(MAKELCID(lang_id, SORT_DEFAULT), LOCALE_SENGLANGUAGE,
1036  lngcp, 100)) {
1037  wxString lstring = wxString(lngcp);
1038 
1039  languageInfoW = wxLocale::FindLanguageInfo(lngcp);
1040  if (languageInfoW)
1041  wxLogMessage(_T("Found LanguageInfo for: ") + lstring);
1042  else
1043  wxLogMessage(_T("Could not find LanguageInfo for: ") + lstring);
1044  } else {
1045  wxLogMessage(_T("Could not get LocaleInfo, using wxLANGUAGE_DEFAULT"));
1046  languageInfoW = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
1047  }
1048 
1049  if (languageInfoW) retval = languageInfoW->CanonicalName;
1050 #endif
1051 
1052 #if defined(__ANDROID__)
1053  retval = androidGetAndroidSystemLocale();
1054 #endif
1055 
1056 #endif
1057 
1058  return retval;
1059 }
1060 
1061 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3, 0, 0)
1062 wxString OCPNPlatform::GetAdjustedAppLocale() {
1063  wxString adjLocale = g_locale;
1064 
1065 // For windows, installer may have left information in the registry defining
1066 // the user's selected install language. If so, override the config file value
1067 // and use this selection for opencpn...
1068 #if defined(__WXMSW__)
1069  if (g_bFirstRun || wxIsEmpty(adjLocale)) {
1070  wxRegKey RegKey(wxString(_T("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN")));
1071  if (RegKey.Exists()) {
1072  wxLogMessage(
1073  _T("Retrieving initial language selection from Windows Registry"));
1074  RegKey.QueryValue(wxString(_T("InstallerLanguage")), adjLocale);
1075  }
1076  }
1077  if (wxIsEmpty(adjLocale)) {
1078  if (g_localeOverride.Length())
1079  adjLocale = g_localeOverride;
1080  else
1081  adjLocale = GetDefaultSystemLocale();
1082  }
1083 #endif
1084 #if defined(__ANDROID__)
1085  if (g_localeOverride.Length())
1086  adjLocale = g_localeOverride;
1087  else
1088  adjLocale = GetDefaultSystemLocale();
1089 #endif
1090 
1091  return adjLocale;
1092 }
1093 
1094 wxString OCPNPlatform::ChangeLocale(wxString &newLocaleID,
1095  wxLocale *presentLocale,
1096  wxLocale **newLocale) {
1097  wxString return_val;
1098 
1099  wxString imsg = _T("ChangeLocale: Language load for: ");
1100  imsg += newLocaleID;
1101  wxLogMessage(imsg);
1102 
1103  // Old locale is done.
1104  delete (wxLocale *)presentLocale;
1105 
1106  wxLocale *locale = new wxLocale;
1107  if (isFlatpacked()) {
1108  std::string path(getenv("HOME"));
1109  path += "/.var/app/org.opencpn.OpenCPN/data/locale";
1110  locale->AddCatalogLookupPathPrefix(path);
1111  wxLogMessage("Using flatpak locales at %s", path.c_str());
1112  }
1113  wxString loc_lang_canonical;
1114 
1115  const wxLanguageInfo *pli = wxLocale::FindLanguageInfo(newLocaleID);
1116  bool b_initok = false;
1117 
1118  if (pli) {
1119  locale->Init(pli->Language, 1);
1120  // If the locale was not initialized OK, it may be that the wxstd.mo
1121  // translations of the wxWidgets strings is not present. So try again,
1122  // without attempting to load defaults wxstd.mo.
1123  if (!locale->IsOk()) {
1124  wxString imsg = _T("ChangeLocale: could not initialize: ");
1125  imsg += newLocaleID;
1126  wxLogMessage(imsg);
1127 
1128  delete locale;
1129  locale = new wxLocale;
1130  locale->Init(pli->Language, 0);
1131  }
1132  loc_lang_canonical = pli->CanonicalName;
1133 
1134  b_initok = locale->IsOk();
1135 #ifdef __ANDROID__
1136  b_initok = true;
1137 #endif
1138  }
1139 
1140  if (!b_initok) {
1141  wxString imsg = _T("ChangeLocale: Fall back to en_US");
1142  wxLogMessage(imsg);
1143 
1144  delete locale;
1145  locale = new wxLocale;
1146  locale->Init(wxLANGUAGE_ENGLISH_US, 0);
1147  loc_lang_canonical =
1148  wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH_US)->CanonicalName;
1149  }
1150 
1151  if (b_initok) {
1152  wxString imsg = _T("ChangeLocale: Locale Init OK for: ");
1153  imsg += loc_lang_canonical;
1154  wxLogMessage(imsg);
1155 
1156  // wxWidgets assigneds precedence to message catalogs in reverse order of
1157  // loading. That is, the last catalog containing a certain translatable
1158  // item takes precedence.
1159 
1160  // So, Load the catalogs saved in a global string array which is populated
1161  // as PlugIns request a catalog load. We want to load the PlugIn catalogs
1162  // first, so that core opencpn translations loaded later will become
1163  // precedent.
1164 
1165  for (unsigned int i = 0; i < g_locale_catalog_array.GetCount(); i++) {
1166  if(!locale->AddCatalog(g_locale_catalog_array[i])){
1167  wxString emsg = _T("ERROR Loading translation catalog for: ");
1168  emsg += g_locale_catalog_array[i];
1169  wxLogMessage(emsg);
1170  }
1171  else {
1172  wxString imsg = _T("Loaded translation catalog for: ");
1173  imsg += g_locale_catalog_array[i];
1174  wxLogMessage(imsg);
1175  }
1176  }
1177 
1178  // Get core opencpn catalog translation (.mo) file
1179  wxLogMessage(_T("Loading catalog for opencpn core."));
1180  locale->AddCatalog(_T("opencpn"));
1181 
1182  return_val = locale->GetCanonicalName();
1183 
1184  // We may want to override the default system locale, so set a flag.
1185  if (return_val != GetDefaultSystemLocale())
1186  g_localeOverride = return_val;
1187  else
1188  g_localeOverride = _T("");
1189  }
1190 
1191  *newLocale = locale; // return the new locale
1192 
1193  // Always use dot as decimal
1194  setlocale(LC_NUMERIC, "C");
1195 
1196  return return_val;
1197 }
1198 #endif
1199 
1200 // Setup default global options when config file is unavailable,
1201 // as on initial startup after new install
1202 // The global config object (pConfig) is available, so direct updates are
1203 // also allowed
1204 
1205 void OCPNPlatform::SetDefaultOptions(void) {
1206  // General options, applied to all platforms
1207  g_bShowOutlines = true;
1208 
1209  g_CPAMax_NM = 20.;
1210  g_CPAWarn_NM = 2.;
1211  g_TCPA_Max = 30.;
1212  g_bMarkLost = true;
1213  g_MarkLost_Mins = 8;
1214  g_bRemoveLost = true;
1215  g_RemoveLost_Mins = 10;
1216  g_bShowCOG = true;
1217  g_ShowCOG_Mins = 6;
1218  g_bSyncCogPredictors = false;
1219  g_bHideMoored = false;
1220  g_ShowMoored_Kts = 0.2;
1221  g_SOGminCOG_kts = 0.2;
1222  g_bTrackDaily = false;
1223  g_PlanSpeed = 6.;
1224  g_bFullScreenQuilt = true;
1225  g_bQuiltEnable = true;
1226  g_bskew_comp = false;
1227  g_bShowAreaNotices = false;
1228  g_bDrawAISSize = false;
1229  g_bDrawAISRealtime = false;
1230  g_AIS_RealtPred_Kts = 0.7;
1231  g_bShowAISName = false;
1232  g_nTrackPrecision = 2;
1233  g_bPreserveScaleOnX = true;
1234  g_nAWDefault = 50;
1235  g_nAWMax = 1852;
1236  gps_watchdog_timeout_ticks = GPS_TIMEOUT_SECONDS;
1237  g_n_ownship_min_mm = 8;
1238  g_bShowMuiZoomButtons = true;
1239  g_bresponsive = false;
1240 
1241  // Initial S52/S57 options
1242  if (pConfig) {
1243  pConfig->SetPath(_T ( "/Settings/GlobalState" ));
1244  pConfig->Write(_T ( "bShowS57Text" ), true);
1245  pConfig->Write(_T ( "bShowS57ImportantTextOnly" ), false);
1246  pConfig->Write(_T ( "nDisplayCategory" ), (int)(_DisCat)STANDARD);
1247  pConfig->Write(_T ( "nSymbolStyle" ), (int)(_LUPname)PAPER_CHART);
1248  pConfig->Write(_T ( "nBoundaryStyle" ), (int)(_LUPname)PLAIN_BOUNDARIES);
1249 
1250  pConfig->Write(_T ( "bShowSoundg" ), true);
1251  pConfig->Write(_T ( "bShowMeta" ), false);
1252  pConfig->Write(_T ( "bUseSCAMIN" ), true);
1253  pConfig->Write(_T ( "bShowAtonText" ), false);
1254  pConfig->Write(_T ( "bShowLightDescription" ), false);
1255  pConfig->Write(_T ( "bExtendLightSectors" ), true);
1256  pConfig->Write(_T ( "bDeClutterText" ), true);
1257  pConfig->Write(_T ( "bShowNationalText" ), true);
1258 
1259  pConfig->Write(_T ( "S52_MAR_SAFETY_CONTOUR" ), 3);
1260  pConfig->Write(_T ( "S52_MAR_SHALLOW_CONTOUR" ), 2);
1261  pConfig->Write(_T ( "S52_MAR_DEEP_CONTOUR" ), 6);
1262  pConfig->Write(_T ( "S52_MAR_TWO_SHADES" ), 0);
1263  pConfig->Write(_T ( "S52_DEPTH_UNIT_SHOW" ), 1);
1264 
1265  pConfig->Write(_T ( "ZoomDetailFactorVector" ), 3);
1266 
1267  pConfig->Write(_T ( "nColorScheme" ), 1); // higher contrast on NOAA RNCs
1268 
1269 // A few more often requested defaults, not applicable to Android
1270 #ifndef __ANDROID__
1271  g_bEnableZoomToCursor = true;
1272  g_bsmoothpanzoom = true;
1273  g_bShowMenuBar = true;
1274 #endif
1275 
1276  }
1277 
1278 #ifdef __WXMSW__
1279  // Enable some default PlugIns, and their default options
1280  if (pConfig) {
1281  pConfig->SetPath(_T ( "/PlugIns/chartdldr_pi.dll" ));
1282  pConfig->Write(_T ( "bEnabled" ), true);
1283 
1284  pConfig->SetPath(_T ( "/PlugIns/wmm_pi.dll" ));
1285  pConfig->Write(_T ( "bEnabled" ), true);
1286 
1287  pConfig->SetPath(_T ( "/Settings/WMM" ));
1288  pConfig->Write(_T ( "ShowIcon" ), true);
1289  pConfig->Write(_T ( "ShowLiveIcon" ), true);
1290  }
1291 #endif
1292 
1293 #ifdef __WXOSX__
1294  // Enable some default PlugIns, and their default options
1295  if (pConfig) {
1296  pConfig->SetPath(_T ( "/PlugIns/libchartdldr_pi.dylib" ));
1297  pConfig->Write(_T ( "bEnabled" ), true);
1298 
1299  pConfig->SetPath(_T ( "/PlugIns/libwmm_pi.dylib" ));
1300  pConfig->Write(_T ( "bEnabled" ), true);
1301 
1302  pConfig->SetPath(_T ( "/Settings/WMM" ));
1303  pConfig->Write(_T ( "ShowIcon" ), true);
1304  pConfig->Write(_T ( "ShowLiveIcon" ), true);
1305  }
1306 #endif
1307 
1308 #ifdef __linux__
1309  // Enable some default PlugIns, and their default options
1310  if (pConfig) {
1311  pConfig->SetPath(_T ( "/PlugIns/libchartdldr_pi.so" ));
1312  pConfig->Write(_T ( "bEnabled" ), true);
1313 
1314  pConfig->SetPath(_T ( "/PlugIns/libwmm_pi.so" ));
1315  pConfig->Write(_T ( "bEnabled" ), true);
1316 
1317  pConfig->SetPath(_T ( "/Settings/WMM" ));
1318  pConfig->Write(_T ( "ShowIcon" ), true);
1319  pConfig->Write(_T ( "ShowLiveIcon" ), true);
1320  }
1321 #endif
1322 
1323 #ifdef __ANDROID__
1324 
1325 #ifdef ocpnUSE_GL
1326  g_bopengl = true;
1327  g_GLOptions.m_bTextureCompression = 1;
1328  g_GLOptions.m_bTextureCompressionCaching = 1;
1329 #endif
1330 
1331  qDebug() << "SetDefaultOptions";
1332 
1333  g_btouch = true;
1334  g_bresponsive = true;
1335  g_default_font_size = 18; // This is pretty close to TextAppearance.Medium
1336  g_bUIexpert = true;
1337 
1338  g_bShowStatusBar = true;
1339  g_cm93_zoom_factor = 0;
1340  g_oz_vector_scale = false;
1341  g_fog_overzoom = false;
1342 
1343  g_bRollover = true;
1344  g_bShowMuiZoomButtons = true;
1345 
1346  g_GUIScaleFactor = 0; // nominal
1347  g_ChartNotRenderScaleFactor = 2.0;
1348 
1349  // Suppress most tools, especially those that appear in the Basic menus.
1350  // Of course, they may be re-enabled by experts...
1351  g_toolbarConfig = _T("X.....XX.......XX.XXXXXXXXXXX");
1352  g_bPermanentMOBIcon = false;
1353 
1354  wxString sGPS = _T("2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0"); // 17 parms
1355  ConnectionParams *new_params = new ConnectionParams(sGPS);
1356 
1357  new_params->bEnabled = true;
1358  TheConnectionParams()->Add(new_params);
1359 
1360  g_default_font_facename = _T("Roboto");
1361 
1362  // Enable some default PlugIns, and their default options
1363 
1364  if (pConfig) {
1365  pConfig->SetPath(_T ( "/PlugIns/libchartdldr_pi.so" ));
1366  pConfig->Write(_T ( "bEnabled" ), true);
1367 
1368  pConfig->SetPath(_T ( "/PlugIns/libwmm_pi.so" ));
1369  pConfig->Write(_T ( "bEnabled" ), true);
1370 
1371  pConfig->SetPath(_T ( "/Settings/WMM" ));
1372  pConfig->Write(_T ( "ShowIcon" ), true);
1373  pConfig->Write(_T ( "ShowLiveIcon" ), true);
1374 
1375  pConfig->SetPath(_T ( "/PlugIns/libgrib_pi.so" ));
1376  pConfig->Write(_T ( "bEnabled" ), true);
1377 
1378  pConfig->SetPath(_T ( "/PlugIns/libdashboard_pi.so" ));
1379  pConfig->Write(_T ( "bEnabled" ), true);
1380 
1381  pConfig->SetPath(_T ( "/PlugIns/GRIB" ));
1382  pConfig->Write(_T ( "GRIBCtrlBarPosX" ), 100);
1383  pConfig->Write(_T ( "GRIBCtrlBarPosY" ), 0);
1384 
1385  pConfig->SetPath(_T ( "/Settings/GRIB" ));
1386  pConfig->Write(_T ( "CursorDataShown" ), 0);
1387 
1388  // This is ugly hack
1389  // TODO
1390  pConfig->SetPath(_T ( "/PlugIns/liboesenc_pi.so" ));
1391  pConfig->Write(_T ( "bEnabled" ), true);
1392 
1393  pConfig->SetPath(_T ( "/Settings/QTFonts" ));
1394 
1395  // Status Bar
1396  wxString str = _T("en_US-b25a3899");
1397  wxString pval = _T("StatusBar:Roboto,26,-1,5,75,0,0,0,0,0:rgb(0, 0, 0)");
1398  pConfig->Write(str, pval);
1399  FontMgr::Get().LoadFontNative(&str, &pval);
1400 
1401  // Dialog
1402  str = _T("en_US-9c3b3a0d");
1403  pval = _T("DialogStatusBar:Roboto,18,-1,5,50,0,0,0,0,0:rgb(0, 0, 0)");
1404  pConfig->Write(str, pval);
1405  FontMgr::Get().LoadFontNative(&str, &pval);
1406 
1407  // Set track default color to magenta
1408  pConfig->SetPath(_T ( "/Settings/Others" ));
1409  pConfig->Write(_T("TrackLineColour"), _T("#C545C3"));
1410  g_colourTrackLineColour.Set(197, 69, 195);
1411 
1412  qDebug() << "SetDefaultOptions.Config";
1413  }
1414 
1415 #endif
1416 }
1417 
1418 // Setup global options on upgrade detected
1419 // The global config object (pConfig) has already been loaded, so updates
1420 // here override values set by config Direct updates to config for next
1421 // boot are also allowed
1422 
1423 void OCPNPlatform::SetUpgradeOptions(wxString vNew, wxString vOld) {
1424 #ifdef __ANDROID__
1425 
1426  qDebug() << "Upgrade check"
1427  << "from: " << vOld.mb_str() << " to: " << vNew.mb_str();
1428 
1429  if (androidGetVersionCode() > g_AndroidVersionCode) { // upgrade
1430  qDebug() << "Upgrade detected"
1431  << "from VC: " << g_AndroidVersionCode
1432  << " to VC: " << androidGetVersionCode();
1433 
1434  // Set some S52/S57 options
1435  if (pConfig) {
1436  pConfig->SetPath(_T ( "/Settings/GlobalState" ));
1437  pConfig->Write(_T ( "bShowS57Text" ), true);
1438  }
1439 
1440  g_ChartNotRenderScaleFactor = 2.0;
1441  g_n_ownship_min_mm = 8;
1442  g_toolbarConfig = _T("X.....XX.......XX.XXXXXXXXXXX");
1443 
1444  // Experience indicates a slightly larger default font size is better
1445  pConfig->DeleteGroup(_T ( "/Settings/QTFonts" ));
1446  g_default_font_size = 20;
1447  g_default_font_facename = _T("Roboto");
1448 
1449  FontMgr::Get().Shutdown(); // Restart the font manager
1450 
1451  // Reshow the zoom buttons
1452  g_bShowMuiZoomButtons = true;
1453 
1454  // Clear the default chart storage location
1455  // Will get set to e.g. "/storage/emulated/0" later
1456  pInit_Chart_Dir->Clear();
1457 
1458  pConfig->SetPath(_T ( "/Settings/WMM" ));
1459  pConfig->Write(_T ( "ShowIcon" ), true);
1460  pConfig->Write(_T ( "ShowLiveIcon" ), true);
1461  }
1462 
1463  // Set track default color to magenta
1464  g_colourTrackLineColour.Set(197, 69, 195);
1465 
1466  // This is ugly hack
1467  // TODO
1468  // pConfig->SetPath( _T ( "/PlugIns/liboesenc_pi.so" ) );
1469  // pConfig->Write( _T ( "bEnabled" ), true );
1470 
1471 #endif
1472 
1473  // Check for upgrade....
1474  if (!vOld.IsSameAs(vNew)) { // upgrade
1475 
1476  // Verify some default directories, create if necessary
1477 
1478  // UserIcons
1479  wxString UserIconPath = GetPrivateDataDir();
1480  wxChar sep = wxFileName::GetPathSeparator();
1481  if (UserIconPath.Last() != sep) UserIconPath.Append(sep);
1482  UserIconPath.Append(_T("UserIcons"));
1483 
1484  if (!::wxDirExists(UserIconPath)) {
1485  ::wxMkdir(UserIconPath);
1486  }
1487 
1488  // layers
1489  wxString LayersPath = GetPrivateDataDir();
1490  if (LayersPath.Last() != sep) LayersPath.Append(sep);
1491  LayersPath.Append(_T("layers"));
1492 
1493  if (!::wxDirExists(LayersPath)) {
1494  ::wxMkdir(LayersPath);
1495  }
1496 
1497  // Force a generally useable sound command, overriding any previous user's
1498  // selection
1499  // that may not be available on new build.
1500  g_CmdSoundString = wxString(OCPN_SOUND_CMD);
1501  pConfig->SetPath(_T ( "/Settings" ));
1502  pConfig->Write(_T( "CmdSoundString" ), g_CmdSoundString);
1503 
1504  // Force AIS specific sound effects ON, leaving the master control
1505  // (g_bAIS_CPA_Alert_Audio) as configured
1506  g_bAIS_GCPA_Alert_Audio = true;
1507  g_bAIS_SART_Alert_Audio = true;
1508  g_bAIS_DSC_Alert_Audio = true;
1509 
1510  // Force a recalculation of default main toolbar location
1511  g_maintoolbar_x = -1;
1512 
1513  // Check the tide/current databases for readability,
1514  // remove any not readable
1515  std::vector<std::string> TCDS_temp;
1516  for (unsigned int i=0; i < TideCurrentDataSet.size() ; i++)
1517  TCDS_temp.push_back(TideCurrentDataSet[i]);
1518 
1519  TideCurrentDataSet.clear();
1520  for (unsigned int i=0; i < TCDS_temp.size() ; i++){
1521  wxString tide = TCDS_temp[i];
1522  wxFileName ft(tide);
1523  if (ft.FileExists())
1524  TideCurrentDataSet.push_back(TCDS_temp[i]);
1525  }
1526  }
1527 }
1528 
1529 int OCPNPlatform::platformApplyPrivateSettingsString(wxString settings,
1530  ArrayOfCDI *pDirArray) {
1531  int ret_val = 0;
1532 #ifdef __ANDROID__
1533  ret_val = androidApplySettingsString(settings, pDirArray);
1534 #endif
1535 
1536  return ret_val;
1537 }
1538 
1539 void OCPNPlatform::applyExpertMode(bool mode) {
1540 #ifdef __ANDROID__
1541  g_bexpert = mode; // toolbar only shows plugin icons if expert mode is false
1542  g_bBasicMenus = !mode; // simplified context menus in basic mode
1543 #endif
1544 }
1545 
1546 wxString OCPNPlatform::GetSupplementalLicenseString() {
1547  wxString lic;
1548 #ifdef __ANDROID__
1549  lic = androidGetSupplementalLicense();
1550 #endif
1551  return lic;
1552 }
1553 
1554 //--------------------------------------------------------------------------
1555 // Per-Platform file/directory support
1556 //--------------------------------------------------------------------------
1557 
1558 
1559 static wxString ExpandPaths(wxString paths, OCPNPlatform *platform);
1560 
1561 
1562 int OCPNPlatform::DoFileSelectorDialog(wxWindow *parent, wxString *file_spec,
1563  wxString Title, wxString initDir,
1564  wxString suggestedName,
1565  wxString wildcard) {
1566  wxString file;
1567  int result = wxID_CANCEL;
1568 
1569 #ifdef __ANDROID__
1570  // Verify that initDir is traversable, fix it if not...
1571  wxString idir = initDir;
1572  if (initDir.StartsWith(
1573  _T("/data/data"))) // not good, provokes a crash usually...
1574  idir = GetWritableDocumentsDir();
1575 
1576  result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1577  if (file_spec) *file_spec = file;
1578 #else
1579  long flag = wxFD_DEFAULT_STYLE;
1580  if (suggestedName.Length()) { // new file
1581  flag = wxFD_SAVE;
1582  }
1583 
1584  wxString mask = wildcard;
1585  if (wxNOT_FOUND != mask.Find(_T("gpx")))
1586  mask.Prepend(_T("GPX files (*.gpx)|"));
1587 
1588  wxFileDialog *psaveDialog =
1589  new wxFileDialog(parent, Title, initDir, suggestedName, mask, flag);
1590 
1591  // Try to reduce the dialog size, and scale fonts down, if necessary.
1592  // if(g_bresponsive && parent)
1593  // psaveDialog = g_Platform->AdjustFileDialogFont(parent,
1594  // psaveDialog);
1595 
1596 #ifdef __WXOSX__
1597  if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1598 #endif
1599 
1600  result = psaveDialog->ShowModal();
1601 
1602 #ifdef __WXOSX__
1603  if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1604 #endif
1605 
1606  if (file_spec) *file_spec = psaveDialog->GetPath();
1607  delete psaveDialog;
1608 
1609 #endif
1610 
1611  return result;
1612 }
1613 
1614 int OCPNPlatform::DoDirSelectorDialog(wxWindow *parent, wxString *file_spec,
1615  wxString Title, wxString initDir,
1616  bool b_addFiles) {
1617  wxString dir;
1618  int result = wxID_CANCEL;
1619 
1620 #ifdef __ANDROID__
1621  // Verify that initDir is traversable, fix it if not...
1622  wxString idir = initDir;
1623  if (initDir.StartsWith(
1624  _T("/data/data"))) // not good, provokes a crash usually...
1625  idir = GetWritableDocumentsDir();
1626 
1627  result = androidFileChooser(&dir, idir, Title, _T(""), _T(""), true,
1628  b_addFiles); // Directories only, maybe add dirs
1629  if (file_spec) *file_spec = dir;
1630 #else
1631  wxDirDialog *dirSelector = new wxDirDialog(
1632  parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1633 
1634  wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1635  dirSelector->SetFont(*qFont);
1636 
1637  // Try to reduce the dialog size, and scale fonts down, if necessary.
1638  // if(g_bresponsive && parent)
1639  // dirSelector = AdjustDirDialogFont(parent, dirSelector);
1640 
1641 #ifdef __WXOSX__
1642  if (parent) parent->HideWithEffect(wxSHOW_EFFECT_BLEND);
1643 #endif
1644 
1645  result = dirSelector->ShowModal();
1646 
1647 #ifdef __WXOSX__
1648  if (parent) parent->ShowWithEffect(wxSHOW_EFFECT_BLEND);
1649 #endif
1650 
1651  if (result == wxID_CANCEL) {
1652  } else {
1653  if (file_spec) {
1654  *file_spec = dirSelector->GetPath();
1655  }
1656  }
1657 
1658  delete dirSelector;
1659 #endif
1660 
1661  return result;
1662 }
1663 
1664 MyConfig *OCPNPlatform::GetConfigObject() {
1665  MyConfig *result = NULL;
1666 
1667  result = new MyConfig(GetConfigFileName());
1668 
1669  return result;
1670 }
1671 
1672 //--------------------------------------------------------------------------
1673 // Internal GPS Support
1674 //--------------------------------------------------------------------------
1675 
1676 bool OCPNPlatform::hasInternalGPS(wxString profile) {
1677 #ifdef __ANDROID__
1678  bool t = androidDeviceHasGPS();
1679  // qDebug() << "androidDeviceHasGPS" << t;
1680  return t;
1681 #else
1682 
1683  return false;
1684 
1685 #endif
1686 }
1687 
1688 //--------------------------------------------------------------------------
1689 // Platform Display Support
1690 //--------------------------------------------------------------------------
1691 
1692 void OCPNPlatform::ShowBusySpinner(void) {
1693  AbstractPlatform::ShowBusySpinner();
1694 }
1695 
1696 void OCPNPlatform::HideBusySpinner(void) {
1697  AbstractPlatform::HideBusySpinner();
1698 }
1699 
1700 double OCPNPlatform::GetDisplayDensityFactor() {
1701 #ifdef __ANDROID__
1702  return getAndroidDisplayDensity();
1703 #else
1704  return 1.0;
1705 #endif
1706 }
1707 
1708 long OCPNPlatform::GetDefaultToolbarOrientation() {
1709 #ifndef __ANDROID__
1710  return wxTB_VERTICAL;
1711 #else
1712  return wxTB_VERTICAL;
1713 #endif
1714 }
1715 
1716 int OCPNPlatform::GetStatusBarFieldCount() {
1717 #ifdef __ANDROID__
1718  int count = 1;
1719 
1720  // Make a horizontal measurement...
1721  wxScreenDC dc;
1722  wxFont *templateFont = FontMgr::Get().GetFont(_("StatusBar"), 0);
1723  dc.SetFont(*templateFont);
1724 
1725  int width;
1726  dc.GetTextExtent(_T("WWWWWW"), &width, NULL, NULL, NULL, templateFont);
1727  double font_size_pix = (double)width / 6.0;
1728 
1729  wxSize dispSize = getDisplaySize();
1730 
1731  double nChars = dispSize.x / font_size_pix;
1732 
1733  if (nChars < 40)
1734  count = 1;
1735  else
1736  count = 2;
1737 
1738  return count;
1739 
1740 #else
1741  return STAT_FIELD_COUNT; // default
1742 #endif
1743 }
1744 
1745 double OCPNPlatform::getFontPointsperPixel(void) {
1746  double pt_per_pixel = 1.0;
1747 
1748  //#ifdef __ANDROID__
1749  // On Android, this calculation depends on the density bucket in use.
1750  // Also uses some magic numbers...
1751  // For reference, see http://pixplicity.com/dp-px-converter/
1752  // pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
1753 
1754  //#else
1755 
1756  if (m_pt_per_pixel == 0) {
1757  // Make a measurement...
1758  wxScreenDC dc;
1759 
1760  wxFont *f = FontMgr::Get().FindOrCreateFont(
1761  12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
1762  wxString(_T ( "" )), wxFONTENCODING_SYSTEM);
1763  dc.SetFont(*f);
1764 
1765  int width, height;
1766  dc.GetTextExtent(_T("H"), &width, &height, NULL, NULL, f);
1767 
1768  if (height > 0) m_pt_per_pixel = 12.0 / (double)height;
1769  }
1770  if (m_pt_per_pixel > 0) pt_per_pixel = m_pt_per_pixel;
1771  //#endif
1772 
1773  return pt_per_pixel;
1774 }
1775 
1776 wxSize OCPNPlatform::getDisplaySize() {
1777 #ifdef __ANDROID__
1778  return getAndroidDisplayDimensions();
1779 #else
1780  return wxSize(g_monitor_info[g_current_monitor].width,
1781  g_monitor_info[g_current_monitor].height);
1782 #endif
1783 }
1784 
1785 double OCPNPlatform::GetDisplaySizeMM() {
1786  if (g_current_monitor < m_displaySizeMMOverride.size()) {
1787  if (m_displaySizeMMOverride[g_current_monitor] > 0) {
1788  return m_displaySizeMMOverride[g_current_monitor];
1789  }
1790  }
1791 
1792  double ret = g_monitor_info[g_current_monitor].width_mm;
1793 
1794 #ifdef __ANDROID__
1795  ret = GetAndroidDisplaySize();
1796 #endif
1797 
1798  return ret;
1799 }
1800 
1801 double OCPNPlatform::GetDisplayAreaCM2() {
1802  double size1 = GetDisplaySizeMM();
1803  wxSize sz = getDisplaySize();
1804  double ratio = 1.;
1805  if (sz.x < sz.y)
1806  ratio = (double)sz.x / (double)sz.y; // <1
1807  else
1808  ratio = (double)sz.y / (double)sz.x; // <1
1809 
1810  double area = size1 * (size1 * ratio) / 100.;
1811  // qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
1812  return area;
1813 }
1814 
1815 void OCPNPlatform::SetDisplaySizeMM(size_t monitor, double sizeMM) {
1816  if (monitor < m_displaySizeMMOverride.size()) {
1817  m_displaySizeMMOverride[monitor] = sizeMM;
1818  }
1819 }
1820 
1821 double OCPNPlatform::GetDisplayDPmm() {
1822 #ifdef __ANDROID__
1823  return getAndroidDPmm();
1824 #else
1825  double r = getDisplaySize().x; // dots
1826  return r / GetDisplaySizeMM();
1827 #endif
1828 }
1829 
1830 unsigned int OCPNPlatform::GetSelectRadiusPix() {
1831  return GetDisplayDPmm() *
1832  (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
1833 }
1834 
1835 bool OCPNPlatform::GetFullscreen() {
1836  bool bret = false;
1837 #ifdef __ANDROID__
1838  bret = androidGetFullscreen();
1839 #else
1840 
1841 #endif
1842 
1843  return bret;
1844 }
1845 
1846 bool OCPNPlatform::SetFullscreen(bool bFull) {
1847  bool bret = false;
1848 #ifdef __ANDROID__
1849  bret = androidSetFullscreen(bFull);
1850 #else
1851 #endif
1852 
1853  return bret;
1854 }
1855 
1856 void OCPNPlatform::PositionAISAlert(wxWindow *alert_window) {
1857 #ifndef __ANDROID__
1858  if (alert_window) {
1859  alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y,
1860  g_ais_alert_dialog_sx, g_ais_alert_dialog_sy);
1861  }
1862 #else
1863  if (alert_window) {
1864  alert_window->Centre();
1865  }
1866 
1867 #endif
1868 }
1869 
1870 wxDirDialog *OCPNPlatform::AdjustDirDialogFont(wxWindow *container,
1871  wxDirDialog *dlg) {
1872  wxDirDialog *ret_dlg = dlg;
1873 #ifndef __WXGTK__
1874 
1875  dlg->Show();
1876  dlg->SetSize(container->GetSize());
1877  dlg->Centre();
1878 
1879  wxSize sds = dlg->GetSize();
1880  wxSize ss = container->GetSize();
1881 
1882  if (sds.x > ss.x) {
1883  dlg->Hide();
1884 
1885  wxString msg = dlg->GetMessage();
1886  wxString default_dir = dlg->GetPath();
1887 
1888  delete dlg;
1889 
1890  ret_dlg = new wxDirDialog(NULL, msg, default_dir,
1891  wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST);
1892 
1893  wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1894  wxFont *smallFont = new wxFont(*dialogFont);
1895  smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1896  0.5); // + 0.5 to round instead of truncate
1897  ret_dlg->SetFont(*smallFont);
1898 
1899  ret_dlg->SetSize(container->GetSize());
1900  ret_dlg->Centre();
1901  }
1902  ret_dlg->Hide();
1903 #endif
1904  return ret_dlg;
1905 }
1906 
1907 wxFileDialog *OCPNPlatform::AdjustFileDialogFont(wxWindow *container,
1908  wxFileDialog *dlg) {
1909  wxFileDialog *ret_dlg = dlg;
1910 #ifndef __WXGTK__
1911 
1912  dlg->Show();
1913  dlg->SetSize(container->GetSize());
1914  dlg->Centre();
1915 
1916  wxSize sds = dlg->GetSize();
1917  wxSize ss = container->GetSize();
1918 
1919  if (sds.x > ss.x) {
1920  dlg->Hide();
1921 
1922  wxString msg = dlg->GetMessage();
1923  wxString default_dir = dlg->GetDirectory();
1924  wxString default_file = dlg->GetFilename();
1925  wxString wildcard = dlg->GetWildcard();
1926 
1927  delete dlg;
1928 
1929  ret_dlg = new wxFileDialog(NULL, msg, default_dir, default_file, wildcard,
1930  wxFD_OPEN);
1931 
1932  wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
1933  wxFont *smallFont = new wxFont(*dialogFont);
1934  smallFont->SetPointSize((smallFont->GetPointSize() / 2) +
1935  0.5); // + 0.5 to round instead of truncate
1936  ret_dlg->SetFont(*smallFont);
1937 
1938  ret_dlg->SetSize(container->GetSize());
1939  ret_dlg->Centre();
1940  }
1941  ret_dlg->Hide();
1942 #endif
1943  return ret_dlg;
1944 }
1945 
1946 double OCPNPlatform::GetToolbarScaleFactor(int GUIScaleFactor) {
1947  double rv = 1.0;
1948 #ifdef __ANDROID__
1949 
1950  // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are
1951  // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
1952 
1953  // Get the basic size of a tool icon
1954  wxSize style_tool_size(32, 32);
1955 
1956  if (g_StyleManager) {
1957  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1958  if (style) style_tool_size = style->GetToolSize();
1959  }
1960  double tool_size = style_tool_size.x;
1961 
1962  // unless overridden by user, we declare the "best" tool size
1963  // to be roughly the same as the ActionBar height.
1964  // This may be approximated in a device orientation-independent way as:
1965  // 45pixels * DENSITY
1966  double premult = 1.0;
1967  if (g_config_display_size_manual && g_config_display_size_mm[0] > 0) {
1968  double target_size = 9.0; // mm
1969 
1970  double basic_tool_size_mm = tool_size / GetDisplayDPmm();
1971  premult = target_size / basic_tool_size_mm;
1972 
1973  } else {
1974  premult = wxMax(45 * getAndroidDisplayDensity(), 45) /
1975  tool_size; // make sure not too small
1976  }
1977 
1978  // Adjust the scale factor using the global GUI scale parameter, ranging from
1979  // 0.5 -> 2.0
1980  double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
1981 
1982  // qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
1983 
1984  rv = premult * postmult;
1985  rv = wxMin(rv, getAndroidDisplayDensity() *
1986  3); // Clamp at density * arbitrary limit factor
1987 
1988 #else
1989  double premult = 1.0;
1990 
1991  if (g_bresponsive) {
1992  // Get the basic size of a tool icon
1993  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
1994  wxSize style_tool_size = style->GetToolSize();
1995  double tool_size = style_tool_size.x;
1996 
1997  // unless overridden by user, we declare the "best" tool size
1998  // to be roughly 9 mm square.
1999  double target_size = 9.0; // mm
2000 
2001  double basic_tool_size_mm = tool_size / GetDisplayDPmm();
2002  premult = target_size / basic_tool_size_mm;
2003  }
2004 
2005  // Adjust the scale factor using the global GUI scale parameter
2006  double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
2007 
2008  rv = premult * postmult;
2009  rv = wxMin(rv, 3.0); // Clamp at 3.0
2010  rv = wxMax(rv, 0.5); // and at 0.5
2011 
2012  rv /= g_BasePlatform->GetDisplayDIPMult(gFrame);
2013 
2014 #endif
2015 
2016  return rv;
2017 }
2018 
2019 double OCPNPlatform::GetCompassScaleFactor(int GUIScaleFactor) {
2020  double rv = 1.0;
2021 #ifdef __ANDROID__
2022 
2023  // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is
2024  // approximately 9 mm in size and that the value may range from 0.5 -> 2.0
2025 
2026  if (g_bresponsive) {
2027  // Get the basic size of a tool icon
2028  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2029  wxSize style_tool_size = style->GetToolSize();
2030  double compass_size = style_tool_size.x;
2031 
2032  // We declare the "nominal best" icon size to be a bit smaller than the
2033  // ActionBar height.
2034  // This may be approximated in a device orientation-independent way as:
2035  // 28pixels * DENSITY
2036  double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
2037 
2038  // Adjust the scale factor using the global GUI scale parameter
2039  double postmult = exp(GUIScaleFactor * (log(2.0) / 5.0));
2040  // rv = wxMin(rv, 1.5); // Clamp at 1.5
2041 
2042  rv = premult * postmult;
2043  rv = wxMin(rv, getAndroidDisplayDensity() *
2044  3); // Clamp at density * arbitrary limit factor
2045  }
2046 
2047 #else
2048  double premult = 1.0;
2049 
2050  if (g_bresponsive) {
2051  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
2052  wxSize style_tool_size = style->GetToolSize();
2053  double compass_size = style_tool_size.x;
2054 
2055  // We declare the "best" tool size to be roughly 6 mm.
2056  double target_size = 6.0; // mm
2057 
2058  double basic_tool_size_mm = compass_size / GetDisplayDPmm();
2059  premult = target_size / basic_tool_size_mm;
2060  }
2061 
2062  double postmult = exp(GUIScaleFactor * (0.693 / 5.0)); // exp(2)
2063 
2064  rv = premult * postmult;
2065 
2066  rv = wxMin(rv, 3.0); // Clamp at 3.0
2067  rv = wxMax(rv, 0.5);
2068 
2069 #if defined(__WXOSX__) || defined(__WXGTK3__)
2070  // Support scaled HDPI displays.
2071  if (gFrame)
2072  rv *= gFrame->GetContentScaleFactor();
2073 #endif
2074 
2075  rv /= g_BasePlatform->GetDisplayDIPMult(gFrame);
2076 
2077 #endif
2078 
2079  return rv;
2080 }
2081 
2082 float OCPNPlatform::GetChartScaleFactorExp(float scale_linear) {
2083  double factor = 1.0;
2084 #ifndef __ANDROID__
2085  factor = exp(scale_linear * (log(3.0) / 5.0));
2086 
2087 #else
2088  // the idea here is to amplify the scale factor for higher density displays,
2089  // in a measured way....
2090  factor = exp(scale_linear * (0.693 / 5.0));
2091 // factor *= getAndroidDisplayDensity();
2092 #endif
2093 
2094  factor = wxMax(factor, .5);
2095  factor = wxMin(factor, 6.);
2096 
2097  return factor;
2098 }
2099 
2100 float OCPNPlatform::GetMarkScaleFactorExp(float scale_linear) {
2101  if(scale_linear <= 0)
2102  return GetChartScaleFactorExp(scale_linear);
2103  else
2104  return GetChartScaleFactorExp(scale_linear-1);
2105 }
2106 
2107 // float OCPNPlatform::GetDIPScaleFactor() {
2108 // float rv = 1.0;
2109 // #ifdef __WXMSW__
2110 // if (gFrame)
2111 // rv = (double)(gFrame->FromDIP(100))/100.;
2112 // #endif
2113 //
2114 // return rv;
2115 // }
2116 
2117 //--------------------------------------------------------------------------
2118 // Internal Bluetooth Support
2119 //--------------------------------------------------------------------------
2120 
2121 bool OCPNPlatform::hasInternalBT(wxString profile) {
2122 #ifdef __ANDROID__
2123  bool t = androidDeviceHasBlueTooth();
2124  // qDebug() << "androidDeviceHasBluetooth" << t;
2125  return t;
2126 #else
2127 
2128  return false;
2129 #endif
2130 }
2131 
2132 bool OCPNPlatform::startBluetoothScan() {
2133 #ifdef __ANDROID__
2134  return androidStartBluetoothScan();
2135 #else
2136 
2137  return false;
2138 #endif
2139 }
2140 
2141 bool OCPNPlatform::stopBluetoothScan() {
2142 #ifdef __ANDROID__
2143  return androidStopBluetoothScan();
2144 #else
2145 
2146  return false;
2147 #endif
2148 }
2149 
2150 wxArrayString OCPNPlatform::getBluetoothScanResults() {
2151  wxArrayString ret_val;
2152 #ifdef __ANDROID__
2153  return androidGetBluetoothScanResults();
2154 #else
2155 
2156  ret_val.Add(_T("line 1"));
2157  ret_val.Add(_T("line 2"));
2158  ret_val.Add(_T("line 3"));
2159  return ret_val;
2160 
2161 #endif
2162 }
2163 
2164 //--------------------------------------------------------------------------
2165 // Per-Platform Utility support
2166 //--------------------------------------------------------------------------
2167 
2168 bool OCPNPlatform::AllowAlertDialog(const wxString &class_name) {
2169 #ifdef __ANDROID__
2170  // allow if TopLevelWindow count is <=4, implying normal runtime screen
2171  // layout
2172  int nTLW = 0;
2173  wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2174  while (node) {
2175  wxWindow *win = node->GetData();
2176  if (win->IsShown()) nTLW++;
2177 
2178  node = node->GetNext();
2179  }
2180 
2181  // qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2182  return (g_running && !g_boptionsactive && (nTLW <= 4));
2183 
2184 #else
2185  return true;
2186 #endif
2187 }
2188 
2189 void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator) {
2190 #ifdef __ANDROID__
2191  return androidSetChartTypeMaskSel(mask, indicator);
2192 #endif
2193 }
2194 
2195 #ifdef __ANDROID__
2196 QString g_qtStyleSheet;
2197 
2198 bool LoadQtStyleSheet(wxString &sheet_file) {
2199  if (wxFileExists(sheet_file)) {
2200  // QApplication qApp = getqApp();
2201  if (qApp) {
2202  QString file(sheet_file.c_str());
2203  QFile File(file);
2204  File.open(QFile::ReadOnly);
2205  g_qtStyleSheet = QLatin1String(File.readAll());
2206 
2207  // qApp->setStyleSheet(g_qtStyleSheet);
2208  return true;
2209  } else
2210  return false;
2211  } else
2212  return false;
2213 }
2214 
2215 QString getQtStyleSheet(void) { return g_qtStyleSheet; }
2216 
2217 #endif
2218 
2219 
2220 bool OCPNPlatform::isPlatformCapable(int flag) {
2221 #ifndef __ANDROID__
2222  return true;
2223 #else
2224  if (flag == PLATFORM_CAP_PLUGINS) {
2225  long platver;
2226  wxString tsdk(android_plat_spc.msdk);
2227  if (tsdk.ToLong(&platver)) {
2228  if (platver >= 11) return true;
2229  }
2230  } else if (flag == PLATFORM_CAP_FASTPAN) {
2231  long platver;
2232  wxString tsdk(android_plat_spc.msdk);
2233  if (tsdk.ToLong(&platver)) {
2234  if (platver >= 14) return true;
2235  }
2236  }
2237 
2238  return false;
2239 #endif
2240 }
2241 
2242 void OCPNPlatform::DoHelpDialog(void) {
2243 #ifndef __ANDROID__
2244  if (!g_pAboutDlg) {
2245  g_pAboutDlg = new AboutFrameImpl(gFrame);
2246  } else {
2247  g_pAboutDlg->SetFocus();
2248  }
2249  g_pAboutDlg->Show();
2250 
2251 #else
2252  if (!g_pAboutDlgLegacy)
2253  g_pAboutDlgLegacy = new about(gFrame, GetSharedDataDir());
2254  else
2255  g_pAboutDlgLegacy->SetFocus();
2256  g_pAboutDlgLegacy->Show();
2257 
2258 #endif
2259 }
2260 
2261 void OCPNPlatform::LaunchLocalHelp(void) {
2262 #ifdef __ANDROID__
2263  androidLaunchHelpView();
2264 #else
2265  wxString def_lang_canonical = _T("en_US");
2266 
2267 #if wxUSE_XLOCALE
2268  if (plocale_def_lang)
2269  def_lang_canonical = plocale_def_lang->GetCanonicalName();
2270 #endif
2271 
2272  wxString help_locn = g_Platform->GetSharedDataDir() + _T("doc/help_");
2273 
2274  wxString help_try = help_locn + def_lang_canonical + _T(".html");
2275 
2276  if (!::wxFileExists(help_try)) {
2277  help_try = help_locn + _T("en_US") + _T(".html");
2278 
2279  if (!::wxFileExists(help_try)) {
2280  help_try = help_locn + _T("web") + _T(".html");
2281  }
2282 
2283  if (!::wxFileExists(help_try)) return;
2284  }
2285 
2286  wxLaunchDefaultBrowser(wxString(_T("file:///")) + help_try);
2287 #endif
2288 }
2289 
2290 void OCPNPlatform::platformLaunchDefaultBrowser(wxString URL) {
2291 #ifdef __ANDROID__
2292  androidLaunchBrowser(URL);
2293 #else
2294  ::wxLaunchDefaultBrowser(URL);
2295 #endif
2296 }
2297 
2298 // ============================================================================
2299 // OCPNColourPickerCtrl implementation
2300 // ============================================================================
2301 
2302 BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2303 #ifdef __WXMSW__
2304 EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2305 #endif
2306 END_EVENT_TABLE()
2307 
2308 // ----------------------------------------------------------------------------
2309 // OCPNColourPickerCtrl
2310 // ----------------------------------------------------------------------------
2311 
2312 OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent, wxWindowID id,
2313  const wxColour &initial,
2314  const wxPoint &pos,
2315  const wxSize &size, long style,
2316  const wxValidator &validator,
2317  const wxString &name) {
2318  Create(parent, id, initial, pos, size, style, validator, name);
2319 }
2320 
2321 bool OCPNColourPickerCtrl::Create(wxWindow *parent, wxWindowID id,
2322  const wxColour &col, const wxPoint &pos,
2323  const wxSize &size, long style,
2324  const wxValidator &validator,
2325  const wxString &name) {
2326  m_bitmap = wxBitmap(60, 13);
2327 
2328  // create this button
2329  if (!wxBitmapButton::Create(parent, id, m_bitmap, pos, size,
2330  style | wxBU_AUTODRAW, validator, name)) {
2331  wxFAIL_MSG(wxT("OCPNColourPickerCtrl creation failed"));
2332  return false;
2333  }
2334 
2335  // and handle user clicks on it
2336  Connect(GetId(), wxEVT_BUTTON,
2337  wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick), NULL,
2338  this);
2339 
2340  m_colour = col;
2341  UpdateColour();
2342  InitColourData();
2343 
2344  return true;
2345 }
2346 
2347 void OCPNColourPickerCtrl::InitColourData() {
2348 #if 0
2349  ms_data.SetChooseFull(true);
2350  unsigned char grey = 0;
2351  for (int i = 0; i < 16; i++, grey += 16)
2352  {
2353  // fill with grey tones the custom colors palette
2354  wxColour colour(grey, grey, grey);
2355  ms_data.SetCustomColour(i, colour);
2356  }
2357 #endif
2358 }
2359 
2360 void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
2361 #ifdef __ANDROID__
2362  unsigned int cco = 0;
2363  cco |= 0xff;
2364  cco = cco << 8;
2365  cco |= m_colour.Red();
2366  cco = cco << 8;
2367  cco |= m_colour.Green();
2368  cco = cco << 8;
2369  cco |= m_colour.Blue();
2370  unsigned int cc = androidColorPicker(cco);
2371 
2372  wxColour cnew;
2373  unsigned char blue = (unsigned char)cc % 256;
2374  unsigned char green = (unsigned char)(cc >> 8) % 256;
2375  ;
2376  unsigned char red = (unsigned char)(cc >> 16) % 256;
2377  cnew.Set(red, green, blue);
2378 
2379  SetColour(cnew);
2380 
2381 #else
2382  // update the wxColouData to be shown in the dialog
2383  ms_data.SetColour(m_colour);
2384 
2385  // create the colour dialog and display it
2386  wxColourDialog dlg(this, &ms_data);
2387  if (dlg.ShowModal() == wxID_OK) {
2388  ms_data = dlg.GetColourData();
2389  SetColour(ms_data.GetColour());
2390  }
2391 #endif
2392 }
2393 
2394 void OCPNColourPickerCtrl::UpdateColour() {
2395 #ifndef __ANDROID__
2396  SetBitmapLabel(wxBitmap());
2397 #endif
2398 
2399  wxMemoryDC dc(m_bitmap);
2400  dc.SetPen(*wxTRANSPARENT_PEN);
2401  dc.SetBrush(wxBrush(m_colour));
2402  dc.DrawRectangle(0, 0, m_bitmap.GetWidth(), m_bitmap.GetHeight());
2403 
2404  dc.SelectObject(wxNullBitmap);
2405  SetBitmapLabel(m_bitmap);
2406 }
2407 
2408 void OCPNColourPickerCtrl::SetColour(wxColour &c) {
2409  m_colour = c;
2410  m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2411  UpdateColour();
2412 }
2413 
2414 wxColour OCPNColourPickerCtrl::GetColour(void) { return m_colour; }
2415 
2416 wxSize OCPNColourPickerCtrl::DoGetBestSize() const {
2417  wxSize sz(wxBitmapButton::DoGetBestSize());
2418 #ifdef __WXMAC__
2419  sz.y += 6;
2420 #else
2421  sz.y += 2;
2422 #endif
2423  sz.x += 30;
2424  if (HasFlag(wxCLRP_SHOW_LABEL)) return sz;
2425 
2426  // if we have no label, then make this button a square
2427  // (like e.g. native GTK version of this control) ???
2428  // sz.SetWidth(sz.GetHeight());
2429  return sz;
2430 }
2431 
2432 void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event) {
2433  wxPaintDC dc(this);
2434 
2435  int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
2436  int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
2437 
2438  dc.SetPen(*wxTRANSPARENT_PEN);
2439  dc.SetBrush(wxBrush(m_colour));
2440  dc.DrawRectangle(offset_x, offset_y, m_bitmap.GetWidth(),
2441  m_bitmap.GetHeight());
2442 
2443  event.Skip();
2444 }
Global state for AIS decoder.
wxString GetWinPluginBaseDir()
Base directory for user writable windows plugins, reflects winPluginDir option, defaults to LOCALAPPD...
wxString & GetPrivateDataDir()
Return dir path for opencpn.log, etc., respecting -c cli option.
Definition: ocpn_app.h:45
std::string Homedir() const
home directory, convenience stuff.
Definition: plugin_paths.h:38
std::string UserDatadir()
The single, user-writable common parent for plugin data directories, typically ending in 'plugins'.
Definition: plugin_paths.h:23
static PluginPaths * getInstance()
Return the singleton instance.
Definition: select.h:54
Definition: about.h:48
Global variables reflecting command line options and arguments.
General purpose GUI support.