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