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 "wx/wxprec.h"
27 
28 #ifdef __MINGW32__
29 #undef IPV6STRICT    // mingw FTBS fix:  missing struct ip_mreq
30 #include <windows.h>
31 #endif
32 
33 #ifndef  WX_PRECOMP
34 #include "wx/wx.h"
35 #endif //precompiled headers
36 
37 #include <wx/app.h>
38 #include <wx/apptrait.h>
39 #include "wx/stdpaths.h"
40 #include <wx/filename.h>
41 #include <wx/tokenzr.h>
42 #include <wx/textfile.h>
43 
44 #include "config.h"
45 
46 #include "dychart.h"
47 #include "OCPNPlatform.h"
48 #include "chart1.h"
49 #include "cutil.h"
50 #include "logger.h"
51 #include "styles.h"
52 #include "navutil.h"
53 #include "ocpn_utils.h"
54 #include "ConnectionParams.h"
55 #include "FontMgr.h"
56 #include "s52s57.h"
57 #include "options.h"
58 #include "Select.h"
59 #include "AboutFrameImpl.h"
60 #include "about.h"
61 #include "PluginPaths.h"
62 #include <string>
63 #include <vector>
64 
65 #ifdef __OCPN__ANDROID__
66 #include "androidUTIL.h"
67 #endif
68 
69 #ifdef ocpnUSE_GL
70 #include "glChartCanvas.h"
71 #endif
72 
73 // Include CrashRpt Header
74 #ifdef OCPN_USE_CRASHRPT
75 #include "CrashRpt.h"
76 #endif
77 #ifdef __MSVC__
78 #include <new.h>
79 #endif
80 
81 #ifdef LINUX_CRASHRPT
82 #include "crashprint.h"
83 #endif
84 
85 #ifndef __WXMSW__
86 #include <signal.h>
87 #include <setjmp.h>
88 #endif
89 
90 #ifdef __WXMSW__
91 #include <windows.h>
92 #include <winioctl.h>
93 #include <initguid.h>
94 #include "setupapi.h"                   // presently stored in opencpn/src
95 #endif
96 
97 #ifdef __WXOSX__
98 #include "macutils.h"
99 #endif
100 
101 #ifdef __WXGTK__
102 //#include <gdk/gdk.h>
103 #endif
104 
105 #include <cstdlib>
106 
107 DECLARE_APP(MyApp)
108 
109 void appendOSDirSlash( wxString* pString );
110 
111 
112 #ifndef __WXMSW__
113 struct sigaction          sa_all;
114 struct sigaction          sa_all_old;
115 extern sigjmp_buf env;                    // the context saved by sigsetjmp();
116 #endif
117 
118 
119 extern OCPNPlatform              *g_Platform;
120 extern wxString                  g_winPluginDir;
121 extern bool                      g_bFirstRun;
122 extern bool                      g_bUpgradeInProcess;
123 
124 extern int                       quitflag;
125 extern MyFrame                   *gFrame;
126 extern bool                      g_bportable;
127 
128 extern MyConfig                  *pConfig;
129 
130 extern ocpnStyle::StyleManager* g_StyleManager;
131 
132 extern bool                      g_bshowToolbar;
133 extern bool                      g_bexpert;
134 extern bool                      g_bBasicMenus;
135 extern bool                      g_bUIexpert;
136 
137 extern bool                      g_bshowToolbar;
138 extern bool                      g_bBasicMenus;
139 
140 extern bool                      g_bShowOutlines;
141 extern int                       g_nAWDefault;
142 extern int                       g_nAWMax;
143 extern bool                      g_bPermanentMOBIcon;
144 extern float                     g_toolbar_scalefactor;
145 
146 
147 extern options                   *g_options;
148 extern bool                      g_boptionsactive;
149 
150 // AIS Global configuration
151 extern double                    g_CPAMax_NM;
152 extern double                    g_CPAWarn_NM;
153 extern double                    g_TCPA_Max;
154 extern bool                      g_bMarkLost;
155 extern double                    g_MarkLost_Mins;
156 extern bool                      g_bRemoveLost;
157 extern double                    g_RemoveLost_Mins;
158 extern bool                      g_bShowCOG;
159 extern double                    g_ShowCOG_Mins;
160 extern bool                      g_bHideMoored;
161 extern double                    g_ShowMoored_Kts;
162 extern bool                      g_bShowAreaNotices;
163 extern bool                      g_bDrawAISSize;
164 extern bool                      g_bDrawAISRealtime;
165 extern double                    g_AIS_RealtPred_Kts;
166 extern bool                      g_bShowAISName;
167 
168 extern int                       gps_watchdog_timeout_ticks;
169 extern wxString                  *pInit_Chart_Dir;
170 
171 extern double                    g_config_display_size_mm;
172 extern bool                      g_config_display_size_manual;
173 
174 extern float                     g_selection_radius_mm;
175 extern float                     g_selection_radius_touch_mm;
176 
177 extern bool                     g_bTrackDaily;
178 extern double                   g_PlanSpeed;
179 extern bool                     g_bFullScreenQuilt;
180 extern bool                     g_bQuiltEnable;
181 extern bool                     g_bskew_comp;
182 
183 extern bool                     g_bopengl;
184 extern bool                     g_btouch;
185 extern bool                     g_bresponsive;
186 extern bool                     g_bShowStatusBar;
187 extern int                      g_cm93_zoom_factor;
188 extern int                      g_GUIScaleFactor;
189 extern wxArrayOfConnPrm         *g_pConnectionParams;
190 extern bool                     g_fog_overzoom;
191 extern bool                     g_oz_vector_scale;
192 extern int                      g_nTrackPrecision;
193 extern wxString                 g_toolbarConfig;
194 extern bool                     g_bPreserveScaleOnX;
195 extern bool                     g_running;
196 
197 extern Select                    *pSelect;
198 extern Select                    *pSelectTC;
199 extern Select                    *pSelectAIS;
200 
201 extern Select                    *pSelect;
202 extern Select                    *pSelectTC;
203 extern Select                    *pSelectAIS;
204 
205 extern Select                    *pSelect;
206 extern Select                    *pSelectTC;
207 extern Select                    *pSelectAIS;
208 
209 #ifdef ocpnUSE_GL
210 extern ocpnGLOptions            g_GLOptions;
211 #endif
212 extern int                      g_default_font_size;
213 extern wxString                 g_default_font_facename;
214 
215 wxLog       *g_logger;
216 bool         g_bEmailCrashReport;
217 extern int                       g_ais_alert_dialog_x, g_ais_alert_dialog_y;
218 extern int                       g_ais_alert_dialog_sx, g_ais_alert_dialog_sy;
219 
220 extern double                    g_ChartNotRenderScaleFactor;
221 extern bool                      g_bRollover;
222 
223 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3,0,0)
224 extern wxLocale                  *plocale_def_lang;
225 
226 extern wxString                  g_locale;
227 extern wxString                  g_localeOverride;
228 extern wxArrayString             g_locale_catalog_array;
229 
230 #endif
231 extern int                       options_lastPage;
232 extern AboutFrameImpl            *g_pAboutDlg;
233 extern about                     *g_pAboutDlgLegacy;
234 extern wxColour                   g_colourTrackLineColour;
235 extern int                        g_n_ownship_min_mm;
236 
237 extern int                        g_AndroidVersionCode;
238 extern bool                       g_bShowMuiZoomButtons;
239 extern int                        g_FlushNavobjChangesTimeout;
240 
241 static const char* const DEFAULT_XDG_DATA_DIRS =
242     "~/.local/share:/usr/local/share:/usr/share";
243 
244 #ifdef __WXMSW__
245 static const char PATH_SEP = ';';
246 #else
247 static const char PATH_SEP = ':';
248 #endif
249 
checkIfFlatpacked()250 static bool checkIfFlatpacked()
251 {
252     wxString id;
253     if (!wxGetEnv("FLATPAK_ID", &id)) {
254         return false;
255     }
256     return id == "org.opencpn.OpenCPN";
257 }
258 
259 
260 
261 //  OCPN Platform implementation
262 
OCPNPlatform()263 OCPNPlatform::OCPNPlatform()
264 {
265     m_pt_per_pixel = 0;                 // cached value
266     m_bdisableWindowsDisplayEnum = false;
267     m_displaySize = wxSize(0,0);
268     m_displaySizeMM = wxSize(0,0);
269     m_monitorWidth = m_monitorHeight = 0;
270     m_displaySizeMMOverride = 0;
271     m_isFlatpacked = checkIfFlatpacked();
272     m_pluginDataPath = "";
273 
274     // Detect the OS detail parameters
275     m_osDetail = new OCPN_OSDetail;
276     DetectOSDetail( m_osDetail );
277 }
278 
~OCPNPlatform()279 OCPNPlatform::~OCPNPlatform()
280 {
281 }
282 
283 //--------------------------------------------------------------------------
284 //      Per-Platform Initialization support
285 //--------------------------------------------------------------------------
286 #ifdef __WXMSW__
MyNewHandler(size_t size)287 int MyNewHandler( size_t size )
288 {
289     //  Pass to wxWidgets Main Loop handler
290     throw std::bad_alloc();
291 
292     return 0;
293 }
294 #endif
295 
296 //-----------------------------------------------------------------------
297 //      Signal Handlers
298 //-----------------------------------------------------------------------
299 #ifndef __WXMSW__
300 
301 //These are the signals possibly expected
302 //      SIGUSR1
303 //      Raised externally to cause orderly termination of application
304 //      Intended to act just like pushing the "EXIT" button
305 
306 //      SIGSEGV
307 //      Some undefined segfault......
308 
309 int s_inhup;
310 
311 void
catch_signals(int signo)312 catch_signals(int signo)
313 {
314     switch(signo)
315     {
316         case SIGUSR1:
317             quitflag++;                             // signal to the timer loop
318             break;
319 
320         case SIGSEGV:
321             siglongjmp(env, 1);// jump back to the setjmp() point
322             break;
323 
324         case SIGHUP:
325             if(!s_inhup){
326                 s_inhup++;                  // incase SIGHUP is closely followed by SIGTERM
327                 gFrame->FastClose();
328             }
329             break;
330 
331         case SIGTERM:
332             if(!s_inhup){
333                 s_inhup++;                  // incase SIGHUP is closely followed by SIGTERM
334                 gFrame->FastClose();
335             }
336 
337             break;
338 
339         default:
340             break;
341     }
342 
343 }
344 #endif
345 
346 #ifdef OCPN_USE_CRASHRPT
347 // Define the crash callback
CrashCallback(CR_CRASH_CALLBACK_INFO * pInfo)348 int CALLBACK CrashCallback(CR_CRASH_CALLBACK_INFO* pInfo)
349 {
350     //  Flush log file
351     if( g_logger)
352         g_logger->Flush();
353 
354     return CR_CB_DODEFAULT;
355 }
356 #endif
357 
DetectOSDetail(OCPN_OSDetail * detail)358 bool OCPNPlatform::DetectOSDetail( OCPN_OSDetail *detail)
359 {
360     if(!detail)
361         return false;
362 
363     // We take some defaults from build-time definitions
364     detail->osd_name = std::string(PKG_TARGET);
365     detail->osd_version = std::string(PKG_TARGET_VERSION);
366 
367     // Now parse by basic platform
368 #ifdef __linux__
369     if(wxFileExists(_T("/etc/os-release"))){
370         wxTextFile release_file( _T("/etc/os-release") );
371         if ( release_file.Open() ) {
372             wxString val;
373             for ( wxString str = release_file.GetFirstLine(); !release_file.Eof() ; str = release_file.GetNextLine() ){
374                 if(str.StartsWith(_T("NAME"))){
375                     val = str.AfterFirst('=').Mid(1);  val = val.Mid(0, val.Length()-1);
376                     if(val.Length())  detail->osd_name = std::string(val.mb_str());
377                 }
378                 else if(str.StartsWith(_T("VERSION_ID"))){
379                     val = str.AfterFirst('=').Mid(1);  val = val.Mid(0, val.Length()-1);
380                     if(val.Length())  detail->osd_version = std::string(val.mb_str());
381                 }
382                 else if(str.StartsWith(_T("ID="))){
383                     val = str.AfterFirst('=');
384                     if(val.Length())  detail->osd_ID = ocpn::split(val.mb_str(), " ")[0];
385                 }
386                 else if(str.StartsWith(_T("ID_LIKE"))){
387                     if(val.StartsWith('"')){
388                         val = str.AfterFirst('=').Mid(1);  val = val.Mid(0, val.Length()-1);
389                     }
390                     else{
391                         val = str.AfterFirst('=');
392                     }
393 
394                     if(val.Length()){
395                         detail->osd_name_like = ocpn::split(val.mb_str(), " ");
396                     }
397                 }
398 
399             }
400 
401             release_file.Close();
402         }
403         if(detail->osd_name == _T("Linux Mint")){
404             if(wxFileExists(_T("/etc/upstream-release/lsb-release"))) {
405                 wxTextFile upstream_release_file(_T("/etc/upstream-release/lsb-release"));
406                 if(upstream_release_file.Open()) {
407                     wxString val;
408                     for(wxString str = upstream_release_file.GetFirstLine(); !upstream_release_file.Eof(); str = upstream_release_file.GetNextLine()) {
409                         if(str.StartsWith(_T("DISTRIB_RELEASE"))) {
410                             val = str.AfterFirst('=').Mid(0);
411                             val = val.Mid(0, val.Length());
412                             if(val.Length())
413                                 detail->osd_version = std::string(val.mb_str());
414                         }
415                     }
416                     upstream_release_file.Close();
417                 }
418             }
419         }
420 
421     }
422 #endif
423 
424     //  Set the default processor architecture
425     detail->osd_arch = std::string("x86_64");
426 
427     // then see what is actually running.
428     wxPlatformInfo platformInfo = wxPlatformInfo::Get();
429     wxArchitecture arch = platformInfo.GetArchitecture();
430     if(arch == wxARCH_32)
431         detail->osd_arch = std::string("i386");
432 
433 #ifdef ocpnARM
434     detail->osd_arch = std::string("arm64");
435     if(arch == wxARCH_32)
436         detail->osd_arch = std::string("armhf");
437 #endif
438 
439 #ifdef __OCPN__ANDROID__
440     detail->osd_arch = std::string("arm64");
441     if(arch == wxARCH_32)
442         detail->osd_arch = std::string("armhf");
443 #endif
444 
445     return true;
446 }
447 
GetOSDetail()448 OCPN_OSDetail *OCPNPlatform::GetOSDetail()
449 {
450     return m_osDetail;
451 }
452 
453 
454 //  Called from MyApp() immediately upon entry to MyApp::OnInit()
Initialize_1(void)455 void OCPNPlatform::Initialize_1( void )
456 {
457 
458 #ifdef OCPN_USE_CRASHRPT
459 #ifndef _DEBUG
460     // Install Windows crash reporting
461 
462     CR_INSTALL_INFO info;
463     memset(&info, 0, sizeof(CR_INSTALL_INFO));
464     info.cb = sizeof(CR_INSTALL_INFO);
465     info.pszAppName = _T("OpenCPN");
466 
467     info.pszAppVersion = wxString(VERSION_FULL).c_str();
468 
469     int type =  MiniDumpNormal;
470 
471     // This results in the inclusion of global variables
472     type |= MiniDumpWithDataSegs;
473 
474     //If this flag is specified, the contents of every readable and writeable private memory page will be included into the minidump.
475     //type |=  MiniDumpWithPrivateReadWriteMemory;
476 
477     //If this flag is specified, MiniDumpWriteDump function will scan the stack memory of every thread looking for pointers
478     //that point to other readable memory pages in the process’ address space.
479     //type |=  MiniDumpWithIndirectlyReferencedMemory;
480 
481     info.uMiniDumpType = (MINIDUMP_TYPE)type;
482 
483     // Install all available exception handlers....
484     info.dwFlags = CR_INST_ALL_POSSIBLE_HANDLERS;
485 
486     //  Except memory allocation failures
487     info.dwFlags &= ~CR_INST_NEW_OPERATOR_ERROR_HANDLER;
488 
489     //  Allow user to attach files
490     info.dwFlags |= CR_INST_ALLOW_ATTACH_MORE_FILES;
491 
492     //  Allow user to add more info
493     info.dwFlags |= CR_INST_SHOW_ADDITIONAL_INFO_FIELDS;
494 
495 
496     // URL for sending error reports over HTTP.
497 
498     if(1/*g_bEmailCrashReport*/){
499         info.pszUrl = _T("https://bigdumboat.com/crashrpt/ocpn_crashrpt.php");
500         info.uPriorities[CR_HTTP] = 3;  // First try send report over HTTP
501     }
502     else{
503         info.dwFlags |= CR_INST_DONT_SEND_REPORT;
504         info.uPriorities[CR_HTTP] = CR_NEGATIVE_PRIORITY;       // don't send at all
505     }
506 
507     info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY;  // Second try send report over SMTP
508     info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY;  // Third try send report over Simple MAPI
509 
510     wxStandardPaths& crash_std_path = g_Platform->GetStdPaths();
511 
512     wxString crash_rpt_save_locn = crash_std_path.GetConfigDir();
513     if( g_bportable ) {
514         wxFileName exec_path_crash( crash_std_path.GetExecutablePath() );
515         crash_rpt_save_locn = exec_path_crash.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
516     }
517 
518     wxString locn = crash_rpt_save_locn + _T("\\CrashReports");
519 
520     if(!wxDirExists( locn ) )
521         wxMkdir( locn );
522 
523     if(wxDirExists( locn ) ){
524         wxCharBuffer buf = locn.ToUTF8();
525         wchar_t wlocn[256];
526         if(buf && (locn.Length() < sizeof(wlocn)) ){
527             MultiByteToWideChar( 0, 0, buf.data(), -1, wlocn, sizeof(wlocn)-1);
528             info.pszErrorReportSaveDir = (LPCWSTR)wlocn;
529         }
530     }
531 
532     // Provide privacy policy URL
533     wxFileName exec_path_crash( crash_std_path.GetExecutablePath() );
534     wxString policy_file =  exec_path_crash.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
535     policy_file += _T("PrivacyPolicy.txt");
536     policy_file.Prepend(_T("file:"));
537 
538     info.pszPrivacyPolicyURL = policy_file.c_str();;
539 
540     int nResult = crInstall(&info);
541     if(nResult!=0) {
542         TCHAR buff[256];
543         crGetLastErrorMsg(buff, 256);
544         //MessageBox(NULL, buff, _T("crInstall error, Crash Reporting disabled."), MB_OK);
545     }
546 
547     if(nResult == 0){           // Complete the installation
548         // Establish the crash callback function
549         crSetCrashCallback( CrashCallback, NULL );
550 
551         // Take screenshot of the app window at the moment of crash
552         crAddScreenshot2(CR_AS_PROCESS_WINDOWS|CR_AS_USE_JPEG_FORMAT, 95);
553 
554         //  Mark some files to add to the crash report
555         wxString home_data_crash = crash_std_path.GetConfigDir();
556         if( g_bportable ) {
557             wxFileName f( crash_std_path.GetExecutablePath() );
558             home_data_crash = f.GetPath();
559         }
560         appendOSDirSlash( &home_data_crash );
561 
562         wxString config_crash = _T("opencpn.ini");
563         config_crash.Prepend( home_data_crash );
564         crAddFile2( config_crash.c_str(), NULL, NULL, CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE );
565 
566         wxString log_crash = _T("opencpn.log");
567         log_crash.Prepend( home_data_crash );
568         crAddFile2( log_crash.c_str(), NULL, NULL, CR_AF_MISSING_FILE_OK | CR_AF_ALLOW_DELETE );
569     }
570 #endif
571 #endif
572 
573 
574 #ifdef LINUX_CRASHRPT
575 #if wxUSE_ON_FATAL_EXCEPTION
576     // fatal exceptions handling
577     wxHandleFatalExceptions (true);
578 #endif
579 #endif
580 
581 #ifdef __MSVC__
582     //  Invoke my own handler for failures of malloc/new
583     _set_new_handler( MyNewHandler );
584     //  configure malloc to call the New failure handler on failure
585     _set_new_mode(1);
586 #endif
587 
588 #if 0
589 #ifdef __WXMSW__
590     //    On MSW, force the entire process to run on one CPU core only
591     //    This resolves some difficulty with wxThread syncronization
592     //Gets the current process handle
593     HANDLE hProc = GetCurrentProcess();
594     DWORD procMask;
595     DWORD sysMask;
596     HANDLE hDup;
597     DuplicateHandle( hProc, hProc, hProc, &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS );
598 
599     //Gets the current process affinity mask
600     GetProcessAffinityMask( hDup, &procMask, &sysMask );
601 
602     // Take a simple approach, and assume up to 4 processors
603     DWORD newMask;
604     if( ( procMask & 1 ) == 1 ) newMask = 1;
605     else
606         if( ( procMask & 2 ) == 2 ) newMask = 2;
607         else
608             if( ( procMask & 4 ) == 4 ) newMask = 4;
609             else
610                 if( ( procMask & 8 ) == 8 ) newMask = 8;
611 
612                 //Set te affinity mask for the process
613                 BOOL res = SetProcessAffinityMask( hDup, (DWORD_PTR) newMask );
614 
615             if( res == 0 ) {
616                 //Error setting affinity mask!!
617             }
618 #endif
619 #endif
620 
621 #ifdef __MSVC__
622 
623             //    Handle any Floating Point Exceptions which may leak thru from other
624             //    processes.  The exception filter is in cutil.c
625             //    Seems to only happen for W98
626 
627             wxPlatformInfo Platform;
628             if( Platform.GetOperatingSystemId() == wxOS_WINDOWS_9X ) SetUnhandledExceptionFilter (&MyUnhandledExceptionFilter);
629 #endif
630 
631 #ifdef __WXMSW__
632             //     _CrtSetBreakAlloc(25503);
633 #endif
634 
635 
636 #ifndef __WXMSW__
637             //      Setup Linux SIGNAL handling, for external program control
638 
639             //      Build the sigaction structure
640             sa_all.sa_handler = catch_signals;// point to my handler
641             sigemptyset(&sa_all.sa_mask);// make the blocking set
642             // empty, so that all
643             // other signals will be
644             // unblocked during my handler
645             sa_all.sa_flags = 0;
646 
647             sigaction(SIGUSR1, NULL, &sa_all_old);// save existing action for this signal
648 
649             //      Register my request for some signals
650             sigaction(SIGUSR1, &sa_all, NULL);
651             sigaction(SIGUSR1, NULL, &sa_all_old);// inspect existing action for this signal
652 
653             sigaction(SIGTERM, &sa_all, NULL);
654             sigaction(SIGTERM, NULL, &sa_all_old);
655 
656             sigaction(SIGHUP, &sa_all, NULL);
657             sigaction(SIGHUP, NULL, &sa_all_old);
658 
659 #endif
660 
661 #ifdef __OCPN__ANDROID__
662     qDebug() << "Initialize_1()";
663     androidUtilInit( );
664 #endif
665 
666 }
667 
668 
669 //  Called from MyApp() immediately before creation of MyFrame()
670 //  Config is known to be loaded and stable
671 //  Log is available
Initialize_2(void)672 void OCPNPlatform::Initialize_2( void )
673 {
674 #ifdef __OCPN__ANDROID__
675     wxLogMessage(androidGetDeviceInfo());
676 #endif
677 
678     //  Set a global toolbar scale factor
679     g_toolbar_scalefactor = GetToolbarScaleFactor( g_GUIScaleFactor );
680     auto configdir = wxFileName(GetPrivateDataDir());
681     if (!configdir.DirExists()) {
682         if (!configdir.Mkdir()) {
683 	     auto msg = std::string("Cannot create config directory: ");
684              wxLogWarning(msg + configdir.GetFullPath());
685         }
686     }
687 }
688 
689 //  Called from MyApp()::OnInit() just after gFrame is created, so gFrame is available
Initialize_3(void)690 void OCPNPlatform::Initialize_3( void )
691 {
692 
693     bool bcapable = IsGLCapable();
694 
695 #ifdef ocpnARM         // Boot arm* platforms (meaning rPI) without OpenGL on first run
696     bcapable = false;
697 #endif
698 
699     bool bAndroid =false;
700 #ifdef __OCPN__ANDROID__
701     bAndroid = true;
702 #endif
703 
704     // Try to automatically switch to guaranteed usable GL mode on an OCPN upgrade or fresh install
705 
706     if( (g_bFirstRun || g_bUpgradeInProcess || bAndroid) && bcapable){
707         g_bopengl = true;
708 
709         // Set up visually nice options
710         g_GLOptions.m_bUseAcceleratedPanning = true;
711         g_GLOptions.m_bTextureCompression = true;
712         g_GLOptions.m_bTextureCompressionCaching = true;
713 
714         g_GLOptions.m_iTextureDimension = 512;
715         g_GLOptions.m_iTextureMemorySize = 64;
716 
717         g_GLOptions.m_GLPolygonSmoothing = true;
718         g_GLOptions.m_GLLineSmoothing = true;
719 
720     }
721 
722     gFrame->SetGPSCompassScale();
723 
724     // Force a few items for Android, to ensure that UI is useable if config got scrambled
725     if(bAndroid){
726         g_btouch = true;
727     }
728 
729     if (g_bFirstRun || g_bUpgradeInProcess) {
730         if (!g_bRollover)  //Not explicit set before
731             g_bRollover = g_btouch ? false : true;
732     }
733 
734     g_FlushNavobjChangesTimeout = 300;          // Seconds, so 5 minutes
735 }
736 
737 //  Called from MyApp() just before end of MyApp::OnInit()
Initialize_4(void)738 void OCPNPlatform::Initialize_4( void )
739 {
740 #ifdef __OCPN__ANDROID__
741     if(pSelect) pSelect->SetSelectPixelRadius(wxMax( 25, 6.0 * getAndroidDPmm()) );
742     if(pSelectTC) pSelectTC->SetSelectPixelRadius( wxMax( 25, 6.0 * getAndroidDPmm()) );
743     if(pSelectAIS) pSelectAIS->SetSelectPixelRadius( wxMax( 25, 6.0 * getAndroidDPmm()) );
744 #endif
745 
746 #ifdef __WXMAC__
747     // A bit of a hack for Mojave MacOS 10.14.
748     // Force the user to actively select "Display" tab to ensure initial rendering of
749     // canvas layout select button.
750     options_lastPage = 1;
751 #endif
752 
753 }
754 
OnExit_1(void)755 void OCPNPlatform::OnExit_1( void ){
756 }
757 
OnExit_2(void)758 void OCPNPlatform::OnExit_2( void ){
759 
760 #ifdef OCPN_USE_CRASHRPT
761 #ifndef _DEBUG
762         // Uninstall Windows crash reporting
763 //    crUninstall();
764 #endif
765 #endif
766 
767 }
768 
769 
BuildGLCaps(void * pbuf)770 bool OCPNPlatform::BuildGLCaps( void *pbuf )
771 {
772 
773     // Investigate OpenGL capabilities
774     gFrame->Show();
775     glTestCanvas *tcanvas = new glTestCanvas(gFrame);
776     tcanvas->Show();
777     wxYield();
778     wxGLContext *pctx = new wxGLContext(tcanvas);
779     tcanvas->SetCurrent(*pctx);
780 
781     OCPN_GLCaps *pcaps = (OCPN_GLCaps *)pbuf;
782 
783     char *str = (char *) glGetString( GL_RENDERER );
784     if (str == NULL){
785         delete tcanvas;
786         delete pctx;
787         return false;
788     }
789 
790     char render_string[80];
791     strncpy( render_string, str, 79 );
792     pcaps->Renderer = wxString( render_string, wxConvUTF8 );
793 
794 
795     if( QueryExtension( "GL_ARB_texture_non_power_of_two" ) )
796         pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
797     else if( QueryExtension( "GL_OES_texture_npot" ) )
798         pcaps->TextureRectangleFormat = GL_TEXTURE_2D;
799     else if( QueryExtension( "GL_ARB_texture_rectangle" ) )
800         pcaps->TextureRectangleFormat = GL_TEXTURE_RECTANGLE_ARB;
801 
802 
803     GetglEntryPoints( pcaps );
804 
805     if( pcaps->Renderer.Upper().Find( _T("INTEL") ) != wxNOT_FOUND ){
806         if( pcaps->Renderer.Upper().Find( _T("965") ) != wxNOT_FOUND ){
807             pcaps->bOldIntel = true;
808         }
809     }
810 
811     // Can we use VBO?
812     pcaps->bCanDoVBO = true;
813     if( !pcaps->m_glBindBuffer || !pcaps->m_glBufferData || !pcaps->m_glGenBuffers || !pcaps->m_glDeleteBuffers )
814         pcaps->bCanDoVBO = false;
815 
816 #if defined( __WXMSW__ ) || defined(__WXOSX__)
817     if(pcaps->bOldIntel)
818         pcaps->bCanDoVBO = false;
819 #endif
820 
821 #ifdef __OCPN__ANDROID__
822     pcaps->bCanDoVBO = false;
823 #endif
824 
825     // Can we use FBO?
826     pcaps->bCanDoFBO = true;
827 
828 #ifndef __OCPN__ANDROID__
829     //  We need NPOT to support FBO rendering
830     if(!pcaps->TextureRectangleFormat)
831         pcaps->bCanDoFBO = false;
832 
833     //      We require certain extensions to support FBO rendering
834     if(!QueryExtension( "GL_EXT_framebuffer_object" ))
835         pcaps->bCanDoFBO = false;
836 #endif
837 
838     if( !pcaps->m_glGenFramebuffers  || !pcaps->m_glGenRenderbuffers        || !pcaps->m_glFramebufferTexture2D ||
839         !pcaps->m_glBindFramebuffer  || !pcaps->m_glFramebufferRenderbuffer || !pcaps->m_glRenderbufferStorage  ||
840         !pcaps->m_glBindRenderbuffer || !pcaps->m_glCheckFramebufferStatus  || !pcaps->m_glDeleteFramebuffers   ||
841         !pcaps->m_glDeleteRenderbuffers )
842         pcaps->bCanDoFBO = false;
843 
844 #ifdef __WXMSW__
845     if( pcaps->Renderer.Upper().Find( _T("INTEL") ) != wxNOT_FOUND ) {
846         if(pcaps->Renderer.Upper().Find( _T("MOBILE") ) != wxNOT_FOUND ){
847             pcaps->bCanDoFBO = false;
848         }
849     }
850 #endif
851 
852 
853     delete tcanvas;
854     delete pctx;
855 
856     return true;
857 }
858 
IsGLCapable()859 bool OCPNPlatform::IsGLCapable()
860 {
861 #ifndef __OCPN__ANDROID__
862     OCPN_GLCaps *pcaps = new OCPN_GLCaps;
863 
864     BuildGLCaps(pcaps);
865 
866     // and so we decide....
867 
868     // We insist on FBO support, since otherwise DC mode is always faster on canvas panning..
869     if(!pcaps->bCanDoFBO)
870         return false;
871 #endif
872     return true;
873 }
874 
875 
SetLocaleSearchPrefixes(void)876 void OCPNPlatform::SetLocaleSearchPrefixes( void )
877 {
878 #if wxUSE_XLOCALE
879     // Add a new prefixes for search order.
880     #if defined(__WINDOWS__)
881 
882     // Legacy and system plugin location
883     wxString locale_location = GetSharedDataDir();
884     locale_location += _T("share\\locale");
885     wxLocale::AddCatalogLookupPathPrefix( locale_location );
886 
887     // Managed plugin location
888     wxFileName usrShare(GetWinPluginBaseDir() + wxFileName::GetPathSeparator());
889     usrShare.RemoveLastDir();
890     locale_location = usrShare.GetFullPath() + ("share\\locale");
891     wxLocale::AddCatalogLookupPathPrefix( locale_location );
892 
893     #elif defined(__OCPN__ANDROID__)
894 
895     wxString locale_location = GetSharedDataDir() + _T("locale");
896     wxLocale::AddCatalogLookupPathPrefix( locale_location );
897 
898     #elif defined(__UNIX__) && !defined(__WINE__)
899 
900     // On Unix, wxWidgets defaults to installation prefix of its own, usually "/usr".
901     // On the other hand, canonical installation prefix for OpenCPN is "/usr/local".
902     wxString locale_location;
903     if( !wxGetEnv( _T("OPENCPN_PREFIX"), &locale_location ) )
904     {
905         locale_location = _T("/usr/local");
906     }
907     wxFileName location;
908     location.AssignDir( locale_location );
909     location.AppendDir( _T("share") );
910     location.SetName( _T("locale") );
911     locale_location = location.GetFullPath();
912     wxLocale::AddCatalogLookupPathPrefix( locale_location );
913 
914     // And then for managed plugins
915     std::string dir = PluginPaths::getInstance()->UserDatadir();
916     wxString managed_locale_location(dir + "/locale");
917     wxLocale::AddCatalogLookupPathPrefix( managed_locale_location );
918     #endif
919 
920     #ifdef __WXOSX__
921     std::string macDir = PluginPaths::getInstance()->Homedir() + "/Library/Application Support/OpenCPN/Contents/Resources";
922     wxString Mac_managed_locale_location(macDir);
923     wxLocale::AddCatalogLookupPathPrefix( Mac_managed_locale_location );
924     #endif
925 
926 #endif
927 }
928 
GetDefaultSystemLocale()929 wxString OCPNPlatform::GetDefaultSystemLocale()
930 {
931     wxLogMessage(_T("Getting DefaultSystemLocale..."));
932 
933     wxString retval = _T("en_US");
934 
935 #if wxUSE_XLOCALE
936 
937     const wxLanguageInfo* languageInfo = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
938     if (languageInfo)
939         retval = languageInfo->CanonicalName;
940 
941     #if defined(__WXMSW__)
942         LANGID lang_id = GetUserDefaultUILanguage();
943 
944         wchar_t lngcp[101];
945         const wxLanguageInfo* languageInfoW = 0;
946         if (0 != GetLocaleInfo(MAKELCID(lang_id, SORT_DEFAULT), LOCALE_SENGLANGUAGE, lngcp, 100)){
947             wxString lstring = wxString(lngcp);
948 
949             languageInfoW = wxLocale::FindLanguageInfo(lngcp);
950             if(languageInfoW)
951                 wxLogMessage(_T("Found LanguageInfo for: ") + lstring);
952             else
953                 wxLogMessage(_T("Could not find LanguageInfo for: ") + lstring);
954         }
955         else{
956             wxLogMessage(_T("Could not get LocaleInfo, using wxLANGUAGE_DEFAULT"));
957             languageInfoW = wxLocale::GetLanguageInfo(wxLANGUAGE_DEFAULT);
958         }
959 
960         if(languageInfoW)
961             retval = languageInfoW->CanonicalName;
962      #endif
963 
964 
965     #if defined(__OCPN__ANDROID__)
966     retval = androidGetAndroidSystemLocale();
967     #endif
968 
969 #endif
970 
971     return retval;
972 }
973 
974 
975 #if wxUSE_XLOCALE || !wxCHECK_VERSION(3,0,0)
GetAdjustedAppLocale()976 wxString OCPNPlatform::GetAdjustedAppLocale()
977 {
978     wxString adjLocale = g_locale;
979 
980     //  For windows, installer may have left information in the registry defining the
981     //  user's selected install language.
982     //  If so, override the config file value and use this selection for opencpn...
983     #if defined(__WXMSW__)
984     if ( g_bFirstRun || wxIsEmpty(adjLocale) ) {
985         wxRegKey RegKey( wxString( _T("HKEY_LOCAL_MACHINE\\SOFTWARE\\OpenCPN") ) );
986         if( RegKey.Exists() ) {
987             wxLogMessage( _T("Retrieving initial language selection from Windows Registry") );
988             RegKey.QueryValue( wxString( _T("InstallerLanguage") ), adjLocale );
989         }
990     }
991     if (wxIsEmpty(adjLocale)) {
992         if (g_localeOverride.Length())
993             adjLocale = g_localeOverride;
994         else
995            adjLocale = GetDefaultSystemLocale();
996     }
997     #endif
998     #if defined(__OCPN__ANDROID__)
999     if(g_localeOverride.Length())
1000         adjLocale = g_localeOverride;
1001     else
1002         adjLocale = GetDefaultSystemLocale();
1003     #endif
1004 
1005     return adjLocale;
1006 }
1007 
1008 
1009 
ChangeLocale(wxString & newLocaleID,wxLocale * presentLocale,wxLocale ** newLocale)1010 wxString OCPNPlatform::ChangeLocale(wxString &newLocaleID, wxLocale *presentLocale, wxLocale** newLocale)
1011 {
1012     wxString return_val;
1013 
1014     wxString imsg = _T("ChangeLocale: Language load for:  ");
1015     imsg += newLocaleID;
1016     wxLogMessage( imsg );
1017 
1018     //  Old locale is done.
1019     delete (wxLocale*)presentLocale;
1020 
1021     wxLocale *locale = new wxLocale;
1022     if (isFlatpacked()) {
1023         std::string path(getenv("HOME"));
1024         path += "/.var/app/org.opencpn.OpenCPN/data/locale";
1025         locale->AddCatalogLookupPathPrefix(path);
1026         wxLogMessage("Using flatpak locales at %s", path.c_str());
1027     }
1028     wxString loc_lang_canonical;
1029 
1030     const wxLanguageInfo *pli = wxLocale::FindLanguageInfo( newLocaleID );
1031     bool b_initok = false;
1032 
1033     if( pli ) {
1034         locale->Init( pli->Language, 1 );
1035         // If the locale was not initialized OK, it may be that the wxstd.mo translations
1036         // of the wxWidgets strings is not present.
1037         // So try again, without attempting to load defaults wxstd.mo.
1038         if( !locale->IsOk() ){
1039             wxString imsg = _T("ChangeLocale:  could not initialize:  ");
1040             imsg += newLocaleID;
1041             wxLogMessage( imsg );
1042 
1043             delete locale;
1044             locale = new wxLocale;
1045             locale->Init( pli->Language, 0 );
1046         }
1047         loc_lang_canonical = pli->CanonicalName;
1048 
1049         b_initok = locale->IsOk();
1050         #ifdef __OCPN__ANDROID__
1051         b_initok = true;
1052         #endif
1053     }
1054 
1055     if( !b_initok ) {
1056         wxString imsg = _T("ChangeLocale: Fall back to en_US");
1057         wxLogMessage( imsg );
1058 
1059         delete locale;
1060         locale = new wxLocale;
1061         locale->Init( wxLANGUAGE_ENGLISH_US, 0 );
1062         loc_lang_canonical = wxLocale::GetLanguageInfo( wxLANGUAGE_ENGLISH_US )->CanonicalName;
1063     }
1064 
1065     if(b_initok){
1066         wxString imsg = _T("ChangeLocale: Locale Init OK for:  ");
1067         imsg += loc_lang_canonical;
1068         wxLogMessage( imsg );
1069 
1070         //  wxWidgets assigneds precedence to message catalogs in reverse order of loading.
1071         //  That is, the last catalog containing a certain translatable item takes precedence.
1072 
1073         //  So, Load the catalogs saved in a global string array which is populated as PlugIns request a catalog load.
1074         //  We want to load the PlugIn catalogs first, so that core opencpn translations loaded later will become precedent.
1075 
1076 
1077 
1078         for(unsigned int i=0 ; i < g_locale_catalog_array.GetCount() ; i++){
1079             wxString imsg = _T("Loading catalog for:  ");
1080             imsg += g_locale_catalog_array[i];
1081             wxLogMessage( imsg );
1082             locale->AddCatalog( g_locale_catalog_array[i] );
1083         }
1084 
1085 
1086         // Get core opencpn catalog translation (.mo) file
1087         wxLogMessage( _T("Loading catalog for opencpn core.") );
1088         locale->AddCatalog( _T("opencpn") );
1089 
1090 
1091         return_val = locale->GetCanonicalName();
1092 
1093         // We may want to override the default system locale, so set a flag.
1094         if(return_val != GetDefaultSystemLocale())
1095             g_localeOverride = return_val;
1096         else
1097             g_localeOverride = _T("");
1098 
1099 
1100     }
1101 
1102     *newLocale = locale;                    // return the new locale
1103 
1104     //    Always use dot as decimal
1105     setlocale( LC_NUMERIC, "C" );
1106 
1107     return return_val;
1108 }
1109 #endif
1110 
1111 
1112 
1113 
1114 //      Setup default global options when config file is unavailable,
1115 //      as on initial startup after new install
1116 //      The global config object (pConfig) is available, so direct updates are also allowed
1117 
SetDefaultOptions(void)1118 void OCPNPlatform::SetDefaultOptions( void )
1119 {
1120     //  General options, applied to all platforms
1121     g_bShowOutlines = true;
1122 
1123     g_CPAMax_NM = 20.;
1124     g_CPAWarn_NM = 2.;
1125     g_TCPA_Max = 30.;
1126     g_bMarkLost = true;
1127     g_MarkLost_Mins = 8;
1128     g_bRemoveLost = true;
1129     g_RemoveLost_Mins = 10;
1130     g_bShowCOG = true;
1131     g_ShowCOG_Mins = 6;
1132     g_bHideMoored = false;
1133     g_ShowMoored_Kts = 0.2;
1134     g_bTrackDaily = false;
1135     g_PlanSpeed = 6.;
1136     g_bFullScreenQuilt = true;
1137     g_bQuiltEnable = true;
1138     g_bskew_comp = false;
1139     g_bShowAreaNotices = false;
1140     g_bDrawAISSize = false;
1141     g_bDrawAISRealtime = false;
1142     g_AIS_RealtPred_Kts = 0.7;
1143     g_bShowAISName = false;
1144     g_nTrackPrecision = 2;
1145     g_bPreserveScaleOnX = true;
1146     g_nAWDefault = 50;
1147     g_nAWMax = 1852;
1148     gps_watchdog_timeout_ticks = GPS_TIMEOUT_SECONDS;
1149     g_n_ownship_min_mm = 8;
1150     g_bShowMuiZoomButtons = true;
1151 
1152     // Initial S52/S57 options
1153     if(pConfig){
1154         pConfig->SetPath( _T ( "/Settings/GlobalState" ) );
1155         pConfig->Write( _T ( "bShowS57Text" ), true );
1156         pConfig->Write( _T ( "bShowS57ImportantTextOnly" ), false );
1157         pConfig->Write( _T ( "nDisplayCategory" ), (int)(_DisCat)STANDARD );
1158         pConfig->Write( _T ( "nSymbolStyle" ), (int)(_LUPname)PAPER_CHART );
1159         pConfig->Write( _T ( "nBoundaryStyle" ), (int)(_LUPname)PLAIN_BOUNDARIES );
1160 
1161         pConfig->Write( _T ( "bShowSoundg" ), true );
1162         pConfig->Write( _T ( "bShowMeta" ), false );
1163         pConfig->Write( _T ( "bUseSCAMIN" ), true );
1164         pConfig->Write( _T ( "bShowAtonText" ), false );
1165         pConfig->Write( _T ( "bShowLightDescription" ), false );
1166         pConfig->Write( _T ( "bExtendLightSectors" ), true );
1167         pConfig->Write( _T ( "bDeClutterText" ), true );
1168         pConfig->Write( _T ( "bShowNationalText" ), true );
1169 
1170         pConfig->Write( _T ( "S52_MAR_SAFETY_CONTOUR" ), 3 );
1171         pConfig->Write( _T ( "S52_MAR_SHALLOW_CONTOUR" ), 2 );
1172         pConfig->Write( _T ( "S52_MAR_DEEP_CONTOUR" ), 6 );
1173         pConfig->Write( _T ( "S52_MAR_TWO_SHADES" ), 0  );
1174         pConfig->Write( _T ( "S52_DEPTH_UNIT_SHOW" ), 1 );
1175 
1176         pConfig->Write( _T ( "ZoomDetailFactorVector" ), 3 );
1177 
1178         pConfig->Write( _T ( "nColorScheme" ), 1 );     // higher contrast on NOAA RNCs
1179      }
1180 
1181 
1182 #ifdef __WXMSW__
1183     //  Enable some default PlugIns, and their default options
1184     if(pConfig){
1185         pConfig->SetPath( _T ( "/PlugIns/chartdldr_pi.dll" ) );
1186         pConfig->Write( _T ( "bEnabled" ), true );
1187 
1188         pConfig->SetPath( _T ( "/PlugIns/wmm_pi.dll" ) );
1189         pConfig->Write( _T ( "bEnabled" ), true );
1190 
1191         pConfig->SetPath ( _T ( "/Settings/WMM" ) );
1192         pConfig->Write ( _T ( "ShowIcon" ), true );
1193         pConfig->Write ( _T ( "ShowLiveIcon" ), true );
1194 
1195     }
1196 #endif
1197 
1198 #ifdef __WXOSX__
1199 //  Enable some default PlugIns, and their default options
1200     if(pConfig){
1201         pConfig->SetPath( _T ( "/PlugIns/libchartdldr_pi.dylib" ) );
1202         pConfig->Write( _T ( "bEnabled" ), true );
1203 
1204         pConfig->SetPath( _T ( "/PlugIns/libwmm_pi.dylib" ) );
1205         pConfig->Write( _T ( "bEnabled" ), true );
1206 
1207         pConfig->SetPath ( _T ( "/Settings/WMM" ) );
1208         pConfig->Write ( _T ( "ShowIcon" ), true );
1209         pConfig->Write ( _T ( "ShowLiveIcon" ), true );
1210 
1211     }
1212 #endif
1213 
1214 #ifdef __linux__
1215 //  Enable some default PlugIns, and their default options
1216     if(pConfig){
1217         pConfig->SetPath( _T ( "/PlugIns/libchartdldr_pi.so" ) );
1218         pConfig->Write( _T ( "bEnabled" ), true );
1219 
1220         pConfig->SetPath( _T ( "/PlugIns/libwmm_pi.so" ) );
1221         pConfig->Write( _T ( "bEnabled" ), true );
1222 
1223         pConfig->SetPath ( _T ( "/Settings/WMM" ) );
1224         pConfig->Write ( _T ( "ShowIcon" ), true );
1225         pConfig->Write ( _T ( "ShowLiveIcon" ), true );
1226 
1227     }
1228 #endif
1229 
1230 
1231 #ifdef __OCPN__ANDROID__
1232 
1233 #ifdef ocpnUSE_GL
1234     g_bopengl = true;
1235     g_GLOptions.m_bTextureCompression = 1;
1236     g_GLOptions.m_bTextureCompressionCaching = 1;
1237 #endif
1238 
1239     qDebug() << "SetDefaultOptions";
1240 
1241     g_btouch = true;
1242     g_bresponsive = true;
1243     g_default_font_size = 18;            //  This is pretty close to TextAppearance.Medium
1244     g_bUIexpert = true;
1245 
1246     g_bShowStatusBar = true;
1247     g_cm93_zoom_factor = 0;
1248     g_oz_vector_scale = false;
1249     g_fog_overzoom = false;
1250 
1251     g_bRollover = true;
1252     g_bShowMuiZoomButtons = true;
1253 
1254     g_GUIScaleFactor = 0;               // nominal
1255     g_ChartNotRenderScaleFactor = 2.0;
1256 
1257     //  Suppress most tools, especially those that appear in the Basic menus.
1258     //  Of course, they may be re-enabled by experts...
1259     g_toolbarConfig = _T("X.....XX.......XX.XXXXXXXXXXX");
1260     g_bPermanentMOBIcon = false;
1261 
1262     wxString sGPS = _T("2;3;;0;0;;0;1;0;0;;0;;1;0;0;0;0");          // 17 parms
1263     ConnectionParams *new_params = new ConnectionParams(sGPS);
1264 
1265     new_params->bEnabled = true;
1266     g_pConnectionParams->Add(new_params);
1267 
1268     g_default_font_facename = _T("Roboto");
1269 
1270     //  Enable some default PlugIns, and their default options
1271 
1272     if(pConfig){
1273         pConfig->SetPath( _T ( "/PlugIns/libchartdldr_pi.so" ) );
1274         pConfig->Write( _T ( "bEnabled" ), true );
1275 
1276         pConfig->SetPath( _T ( "/PlugIns/libwmm_pi.so" ) );
1277         pConfig->Write( _T ( "bEnabled" ), true );
1278 
1279         pConfig->SetPath ( _T ( "/Settings/WMM" ) );
1280         pConfig->Write ( _T ( "ShowIcon" ), true );
1281         pConfig->Write ( _T ( "ShowLiveIcon" ), true );
1282 
1283         pConfig->SetPath( _T ( "/PlugIns/libgrib_pi.so" ) );
1284         pConfig->Write( _T ( "bEnabled" ), true );
1285 
1286         pConfig->SetPath( _T ( "/PlugIns/libdashboard_pi.so" ) );
1287         pConfig->Write( _T ( "bEnabled" ), true );
1288 
1289         pConfig->SetPath( _T ( "/PlugIns/GRIB" ) );
1290         pConfig->Write ( _T ( "GRIBCtrlBarPosX" ), 100 );
1291         pConfig->Write ( _T ( "GRIBCtrlBarPosY" ), 0 );
1292 
1293         pConfig->SetPath ( _T ( "/Settings/GRIB" ) );
1294         pConfig->Write ( _T ( "CursorDataShown" ), 0 );
1295 
1296         // This is ugly hack
1297         // TODO
1298         pConfig->SetPath( _T ( "/PlugIns/liboesenc_pi.so" ) );
1299         pConfig->Write( _T ( "bEnabled" ), true );
1300 
1301         pConfig->SetPath ( _T ( "/Settings/QTFonts" ) );
1302 
1303         //Status Bar
1304         wxString str = _T("en_US-b25a3899");
1305         wxString pval = _T("StatusBar:Roboto,26,-1,5,75,0,0,0,0,0:rgb(0, 0, 0)");
1306         pConfig->Write (str, pval );
1307         FontMgr::Get().LoadFontNative( &str, &pval );
1308 
1309         //Dialog
1310         str = _T("en_US-9c3b3a0d");
1311         pval = _T("DialogStatusBar:Roboto,18,-1,5,50,0,0,0,0,0:rgb(0, 0, 0)");
1312         pConfig->Write (str, pval );
1313         FontMgr::Get().LoadFontNative( &str, &pval );
1314 
1315         // Set track default color to magenta
1316         pConfig->SetPath ( _T ( "/Settings/Others" ) );
1317         pConfig->Write (_T("TrackLineColour"), _T("#C545C3"));
1318         g_colourTrackLineColour.Set(197,69,195);
1319 
1320 
1321         qDebug() << "SetDefaultOptions.Config";
1322     }
1323 
1324 
1325 
1326 #endif
1327 }
1328 
1329 //      Setup global options on upgrade detected
1330 //      The global config object (pConfig) has already been loaded, so updates here override values set by config
1331 //      Direct updates to config for next boot are also allowed
1332 
SetUpgradeOptions(wxString vNew,wxString vOld)1333 void OCPNPlatform::SetUpgradeOptions( wxString vNew, wxString vOld )
1334 {
1335 #ifdef __OCPN__ANDROID__
1336 
1337         qDebug() << "Upgrade check" << "from: " << vOld.mb_str() << " to: " << vNew.mb_str();
1338 
1339         if( androidGetVersionCode() > g_AndroidVersionCode ){            // upgrade
1340             qDebug() << "Upgrade detected" << "from VC: " << g_AndroidVersionCode << " to VC: " << androidGetVersionCode();
1341 
1342             // Set some S52/S57 options
1343             if(pConfig){
1344                 pConfig->SetPath( _T ( "/Settings/GlobalState" ) );
1345                 pConfig->Write( _T ( "bShowS57Text" ), true );
1346             }
1347 
1348             g_ChartNotRenderScaleFactor = 2.0;
1349             g_n_ownship_min_mm = 8;
1350             g_toolbarConfig = _T("X.....XX.......XX.XXXXXXXXXXX");
1351 
1352         //  Experience indicates a slightly larger default font size is better
1353             pConfig->DeleteGroup( _T ( "/Settings/QTFonts" ));
1354             g_default_font_size = 20;
1355             g_default_font_facename = _T("Roboto");
1356 
1357             FontMgr::Get().Shutdown();      // Restart the font manager
1358 
1359             // Reshow the zoom buttons
1360             g_bShowMuiZoomButtons = true;
1361 
1362             // Clear the default chart storage location
1363             // Will get set to e.g. "/storage/emulated/0" later
1364             pInit_Chart_Dir->Clear();
1365 
1366             pConfig->SetPath ( _T ( "/Settings/WMM" ) );
1367             pConfig->Write ( _T ( "ShowIcon" ), true );
1368             pConfig->Write ( _T ( "ShowLiveIcon" ), true );
1369         }
1370 
1371         // Set track default color to magenta
1372         g_colourTrackLineColour.Set(197,69,195);
1373 
1374 
1375         // This is ugly hack
1376         // TODO
1377         pConfig->SetPath( _T ( "/PlugIns/liboesenc_pi.so" ) );
1378         pConfig->Write( _T ( "bEnabled" ), true );
1379 
1380 
1381 #endif
1382 }
1383 
1384 
1385 
1386 
platformApplyPrivateSettingsString(wxString settings,ArrayOfCDI * pDirArray)1387 int OCPNPlatform::platformApplyPrivateSettingsString( wxString settings, ArrayOfCDI *pDirArray){
1388 
1389     int ret_val = 0;
1390 #ifdef __OCPN__ANDROID__
1391     ret_val = androidApplySettingsString( settings, pDirArray);
1392 #endif
1393 
1394     return ret_val;
1395 }
1396 
1397 
applyExpertMode(bool mode)1398 void OCPNPlatform::applyExpertMode(bool mode)
1399 {
1400 #ifdef __OCPN__ANDROID__
1401     g_bexpert = mode;                   // toolbar only shows plugin icons if expert mode is false
1402     g_bBasicMenus = !mode;              //  simplified context menus in basic mode
1403 #endif
1404 
1405 }
1406 
1407 
GetSupplementalLicenseString()1408 wxString OCPNPlatform::GetSupplementalLicenseString()
1409 {
1410     wxString lic;
1411 #ifdef __OCPN__ANDROID__
1412     lic = androidGetSupplementalLicense();
1413 #endif
1414     return lic;
1415 }
1416 
1417 //--------------------------------------------------------------------------
1418 //      Per-Platform file/directory support
1419 //--------------------------------------------------------------------------
1420 
GetStdPaths()1421 wxStandardPaths& OCPNPlatform::GetStdPaths()
1422 {
1423 #ifndef __OCPN__ANDROID__
1424     return *dynamic_cast<wxStandardPaths*>(&(wxGetApp().GetTraits()->GetStandardPaths()));
1425 #else
1426 //    return *dynamic_cast<wxStandardPaths*>(&wxApp::GetTraits()->GetStandardPaths());
1427     return *dynamic_cast<wxStandardPaths*>(&(wxTheApp->GetTraits())->GetStandardPaths());
1428 #endif
1429 
1430 }
1431 
1432 
GetHomeDir()1433 wxString &OCPNPlatform::GetHomeDir()
1434 {
1435     if(m_homeDir.IsEmpty()){
1436 
1437         //      Establish a "home" location
1438  //       wxStandardPaths& std_path = *dynamic_cast<wxStandardPaths*>(&wxApp::GetTraits()->GetStandardPaths());
1439         wxStandardPaths& std_path = GetStdPaths();
1440         //        wxStandardPaths &std_path = ( wxStandardPaths) wxGetApp().GetTraits()->GetStandardPaths();
1441 
1442         //TODO  Why is the following preferred?  Will not compile with gcc...
1443 //    wxStandardPaths& std_path = wxApp::GetTraits()->GetStandardPaths();
1444 
1445 #ifdef __unix__
1446         std_path.SetInstallPrefix(wxString(PREFIX, wxConvUTF8));
1447 #endif
1448 
1449 #ifdef __WXMSW__
1450         m_homeDir =  std_path.GetConfigDir();   // on w98, produces "/windows/Application Data"
1451 #else
1452         m_homeDir = std_path.GetUserConfigDir();
1453 #endif
1454 
1455 #ifdef __OCPN__ANDROID__
1456         m_homeDir =  androidGetHomeDir();
1457 #endif
1458 
1459 		if( g_bportable ) {
1460 			wxFileName path(GetExePath());
1461 			m_homeDir = path.GetPath();
1462 		}
1463 
1464 #ifdef  __WXOSX__
1465         appendOSDirSlash(&m_homeDir);
1466         m_homeDir.Append(_T("opencpn"));
1467 #endif
1468 
1469         appendOSDirSlash( &m_homeDir );
1470     }
1471 
1472     return m_homeDir;
1473 }
1474 
GetExePath()1475 wxString &OCPNPlatform::GetExePath()
1476 {
1477     if(m_exePath.IsEmpty()){
1478 
1479         wxStandardPaths& std_path = GetStdPaths();
1480         m_exePath = std_path.GetExecutablePath();
1481     }
1482 
1483     return m_exePath;
1484 }
1485 
1486 
GetWritableDocumentsDir()1487 wxString OCPNPlatform::GetWritableDocumentsDir()
1488 {
1489     wxString dir;
1490 
1491 #ifdef __OCPN__ANDROID__
1492     dir = androidGetExtStorageDir();                 // Used for Chart storage, typically
1493 #else
1494     wxStandardPaths& std_path = GetStdPaths();
1495     dir = std_path.GetDocumentsDir();
1496 #endif
1497     return dir;
1498 }
1499 
1500 
GetSharedDataDir()1501 wxString &OCPNPlatform::GetSharedDataDir()
1502 {
1503     if(m_SData_Dir.IsEmpty()){
1504         //      Establish a "shared data" location
1505         /*  From the wxWidgets documentation...
1506          *
1507          *     wxStandardPaths::GetDataDir
1508          *     wxString GetDataDir() const
1509          *     Return the location of the applications global, i.e. not user-specific, data files.
1510          * Unix: prefix/share/appname
1511          * Windows: the directory where the executable file is located
1512          * Mac: appname.app/Contents/SharedSupport bundle subdirectory
1513          */
1514         wxStandardPaths& std_path = GetStdPaths();
1515         m_SData_Dir = std_path.GetDataDir();
1516         appendOSDirSlash( &m_SData_Dir );
1517 
1518 #ifdef __OCPN__ANDROID__
1519         m_SData_Dir = androidGetSharedDir();
1520 #endif
1521 
1522         if( g_bportable )
1523             m_SData_Dir = GetHomeDir();
1524     }
1525 
1526     return m_SData_Dir;
1527 
1528 }
1529 
GetPrivateDataDir()1530 wxString &OCPNPlatform::GetPrivateDataDir()
1531 {
1532     if(m_PrivateDataDir.IsEmpty()){
1533         //      Establish the prefix of the location of user specific data files
1534         wxStandardPaths& std_path = GetStdPaths();
1535 
1536 #ifdef __WXMSW__
1537         m_PrivateDataDir = GetHomeDir();                     // should be {Documents and Settings}\......
1538 #elif defined FLATPAK
1539         std::string config_home;
1540         if (getenv("XDG_CONFIG_HOME")) {
1541             config_home = getenv("XDG_CONFIG_HOME");
1542         }
1543         else {
1544           config_home = getenv("HOME");
1545           config_home += "/.var/app/org.opencpn.OpenCPN/config";
1546         }
1547         m_PrivateDataDir = config_home + "/opencpn";
1548 
1549 #elif defined __WXOSX__
1550         m_PrivateDataDir = std_path.GetUserConfigDir();     // should be ~/Library/Preferences
1551         appendOSDirSlash(&m_PrivateDataDir);
1552         m_PrivateDataDir.Append(_T("opencpn"));
1553 #else
1554         m_PrivateDataDir = std_path.GetUserDataDir();       // should be ~/.opencpn
1555 #endif
1556 
1557         if( g_bportable ){
1558             m_PrivateDataDir = GetHomeDir();
1559             if(m_PrivateDataDir.Last() == wxFileName::GetPathSeparator())
1560                 m_PrivateDataDir.RemoveLast();
1561         }
1562 
1563 #ifdef __OCPN__ANDROID__
1564         m_PrivateDataDir = androidGetPrivateDir();
1565 #endif
1566     }
1567     return m_PrivateDataDir;
1568 }
1569 
1570 
1571 static wxString ExpandPaths(wxString paths, OCPNPlatform* platform);
1572 
1573 
GetLinuxDataPath()1574 static  wxString GetLinuxDataPath()
1575 {
1576     wxString dirs;
1577     if (wxGetEnv("XDG_DATA_DIRS", &dirs)) {
1578         dirs = wxString("~/.local/share:") + dirs;
1579     }
1580     else {
1581         dirs = DEFAULT_XDG_DATA_DIRS;
1582     }
1583     wxString s;
1584     wxStringTokenizer tokens(dirs, ':');
1585     while (tokens.HasMoreTokens()) {
1586         wxString dir = tokens.GetNextToken();
1587         if (dir.EndsWith("/")) {
1588             dir = dir.SubString(0, dir.length() - 1);
1589         }
1590         if (!dir.EndsWith("/opencpn/plugins")) {
1591             dir += "/opencpn/plugins";
1592         }
1593         s += dir + (tokens.HasMoreTokens() ? ";" : "");
1594     }
1595     return s;
1596 }
1597 
1598 
GetPluginDataPath()1599 wxString OCPNPlatform::GetPluginDataPath()
1600 {
1601     if(g_bportable){
1602         wxString sep = wxFileName::GetPathSeparator();
1603         wxString ret = GetPrivateDataDir() + sep + _T("plugins");
1604         return ret;
1605     }
1606 
1607     if (m_pluginDataPath != "" ) {
1608         return m_pluginDataPath;
1609     }
1610     wxString dirs("");
1611 #ifdef __OCPN__ANDROID__
1612     wxString pluginDir = GetPrivateDataDir() + "/plugins";
1613     dirs += pluginDir;
1614 #else
1615     auto const osSystemId = wxPlatformInfo::Get().GetOperatingSystemId();
1616     if (g_Platform->isFlatpacked()) {
1617         dirs="~/.var/app/org.opencpn.OpenCPN/data/opencpn/plugins";
1618     }
1619     else if (osSystemId & wxOS_UNIX_LINUX) {
1620         dirs = GetLinuxDataPath();
1621     }
1622     else if (osSystemId & wxOS_WINDOWS) {
1623         dirs = GetWinPluginBaseDir();
1624     }
1625     else if (osSystemId & wxOS_MAC) {
1626         dirs = "/Applications/OpenCPN.app/Contents/SharedSupport/plugins;";
1627         dirs +=
1628             "~/Library/Application Support/OpenCPN/Contents/SharedSupport/plugins";
1629     }
1630 #endif
1631 
1632     m_pluginDataPath = ExpandPaths(dirs, this);
1633     if (m_pluginDataPath != "") {
1634         m_pluginDataPath += ";";
1635     }
1636     m_pluginDataPath += GetPluginDir();
1637     if (m_pluginDataPath.EndsWith(wxFileName::GetPathSeparator())) {
1638 	m_pluginDataPath.RemoveLast();
1639     }
1640     wxLogMessage("Using plugin data path: %s",
1641                  m_pluginDataPath.mb_str().data());
1642     return m_pluginDataPath;
1643 }
1644 
1645 
GetWinPluginBaseDir()1646 wxString OCPNPlatform::GetWinPluginBaseDir()
1647 {
1648     if (g_winPluginDir != "") {
1649         wxLogMessage("winPluginDir: Using value from ini file.");
1650 	wxFileName fn(g_winPluginDir);
1651 	if (!fn.DirExists()) {
1652 	    wxLogWarning("Plugin dir %s does not exist",
1653                          fn.GetFullPath().mb_str().data());
1654 	}
1655         fn.Normalize();
1656         return fn.GetFullPath();
1657     }
1658     wxString winPluginDir;
1659     // Standard case: c:\Users\%USERPROFILE%\AppData\Local
1660     bool ok = wxGetEnv( _T("LOCALAPPDATA"), &winPluginDir);
1661     if (!ok) {
1662         wxLogMessage("winPluginDir: Cannot lookup LOCALAPPDATA");
1663         // Without %LOCALAPPDATA%: Use default location if it exists.
1664         std::string path(wxGetHomeDir().ToStdString());
1665         path += "\\AppData\\Local";
1666         if (ocpn::exists(path)) {
1667             winPluginDir = wxString(path.c_str());
1668             wxLogMessage("winPluginDir: using %s",
1669                          winPluginDir.mb_str().data());
1670             ok = true;
1671         }
1672     }
1673     if (!ok) {
1674         // Usually: c:\Users\%USERPROFILE%\AppData\Roaming
1675         ok = wxGetEnv( _T("APPDATA"), &winPluginDir);
1676     }
1677     if (!ok) {
1678         // Without %APPDATA%: Use default location if it exists.
1679         wxLogMessage("winPluginDir: Cannot lookup APPDATA");
1680         std::string path(wxGetHomeDir().ToStdString());
1681         path += "\\AppData\\Roaming";
1682         if (ocpn::exists(path)) {
1683             winPluginDir = wxString(path.c_str());
1684             ok = true;
1685             wxLogMessage("winPluginDir: using %s",
1686                          winPluginDir.mb_str().data());
1687         }
1688     }
1689     if (!ok) {
1690        // {Documents and Settings}\.. on W7, else \ProgramData
1691        winPluginDir = GetHomeDir();
1692     }
1693     wxFileName path(winPluginDir);
1694     path.Normalize();
1695     winPluginDir = path.GetFullPath()  + "\\opencpn\\plugins";
1696     wxLogMessage("Using private plugin dir: %s", winPluginDir);
1697     return winPluginDir;
1698 }
1699 
1700 
GetPluginDir()1701 wxString &OCPNPlatform::GetPluginDir()
1702 {
1703     if(m_PluginsDir.IsEmpty()){
1704 
1705         wxStandardPaths& std_path = GetStdPaths();
1706 
1707         //  Get the PlugIns directory location
1708         m_PluginsDir = std_path.GetPluginsDir();   // linux:   {prefix}/lib/opencpn
1709         // Mac:     appname.app/Contents/PlugIns
1710 #ifdef __WXMSW__
1711         m_PluginsDir += _T("\\plugins");             // Windows: {exe dir}/plugins
1712 #endif
1713         if( g_bportable ) {
1714             m_PluginsDir = GetHomeDir();
1715             m_PluginsDir += _T("plugins");
1716         }
1717 
1718 #ifdef __OCPN__ANDROID__
1719         // something like: data/data/org.opencpn.opencpn
1720         wxFileName fdir = wxFileName::DirName(std_path.GetUserConfigDir());
1721         fdir.RemoveLastDir();
1722         m_PluginsDir = fdir.GetPath();
1723 #endif
1724 
1725     }
1726     return m_PluginsDir;
1727 }
1728 
NormalizePath(const wxString & full_path)1729 wxString OCPNPlatform::NormalizePath(const wxString &full_path) {
1730   if (!g_bportable) {
1731     return full_path;
1732   } else {
1733     wxString path(full_path);
1734     wxFileName f(path);
1735     // If not on another voulme etc. make the portable relative path
1736     if (f.MakeRelativeTo(g_Platform->GetPrivateDataDir())) {
1737       path = f.GetFullPath();
1738     }
1739     return path;
1740   }
1741 }
1742 
ExpandPaths(wxString paths,OCPNPlatform * platform)1743 static wxString ExpandPaths(wxString paths, OCPNPlatform* platform)
1744 {
1745     wxStringTokenizer tokens(paths, ';');
1746     wxString s = "";
1747     while (tokens.HasMoreTokens()) {
1748         wxFileName filename(tokens.GetNextToken());
1749         filename.Normalize();
1750         s += platform->NormalizePath(filename.GetFullPath());
1751         if (tokens.HasMoreTokens()) {
1752             s += ';';
1753         }
1754     }
1755     return s;
1756 }
1757 
GetConfigFileName()1758 wxString &OCPNPlatform::GetConfigFileName()
1759 {
1760     if(m_config_file_name.IsEmpty()){
1761         //      Establish the location of the config file
1762         wxStandardPaths& std_path = GetStdPaths();
1763 
1764 #ifdef __WXMSW__
1765         m_config_file_name = _T("opencpn.ini");
1766         m_config_file_name.Prepend( GetHomeDir() );
1767 
1768 #elif defined __WXOSX__
1769         m_config_file_name = std_path.GetUserConfigDir(); // should be ~/Library/Preferences
1770         appendOSDirSlash(&m_config_file_name);
1771         m_config_file_name.Append(_T("opencpn"));
1772         appendOSDirSlash(&m_config_file_name);
1773         m_config_file_name.Append(_T("opencpn.ini"));
1774 #elif defined FLATPAK
1775         m_config_file_name = GetPrivateDataDir();
1776         m_config_file_name.Append(_T("/opencpn.conf"));
1777             // Usually ~/.var/app/org.opencpn.OpenCPN/config/opencpn.conf
1778 #else
1779         m_config_file_name = std_path.GetUserDataDir(); // should be ~/.opencpn
1780         appendOSDirSlash(&m_config_file_name);
1781         m_config_file_name.Append(_T("opencpn.conf"));
1782 #endif
1783 
1784         if( g_bportable ) {
1785             m_config_file_name = GetHomeDir();
1786 #ifdef __WXMSW__
1787             m_config_file_name += _T("opencpn.ini");
1788 #elif defined __WXOSX__
1789             m_config_file_name +=_T("opencpn.ini");
1790 #else
1791             m_config_file_name += _T("opencpn.conf");
1792 #endif
1793 
1794         }
1795 
1796 #ifdef __OCPN__ANDROID__
1797         m_config_file_name = androidGetPrivateDir();
1798         appendOSDirSlash(&m_config_file_name);
1799         m_config_file_name += _T("opencpn.conf");
1800 #endif
1801 
1802     }
1803 
1804     return m_config_file_name;
1805 }
1806 
GetPluginDirPtr()1807 wxString *OCPNPlatform::GetPluginDirPtr()
1808 {
1809     return &m_PluginsDir;
1810 }
GetSharedDataDirPtr()1811 wxString *OCPNPlatform::GetSharedDataDirPtr()
1812 {
1813     return &m_SData_Dir;
1814 }
GetPrivateDataDirPtr()1815 wxString *OCPNPlatform::GetPrivateDataDirPtr()
1816 {
1817     return &m_PrivateDataDir;
1818 }
1819 
DoFileSelectorDialog(wxWindow * parent,wxString * file_spec,wxString Title,wxString initDir,wxString suggestedName,wxString wildcard)1820 int OCPNPlatform::DoFileSelectorDialog( wxWindow *parent, wxString *file_spec, wxString Title, wxString initDir,
1821                           wxString suggestedName, wxString wildcard)
1822 {
1823     wxString file;
1824     int result = wxID_CANCEL;
1825 
1826 #ifdef __OCPN__ANDROID__
1827     //  Verify that initDir is traversable, fix it if not...
1828     wxString idir = initDir;
1829     if(initDir.StartsWith(_T("/data/data")))                 // not good, provokes a crash usually...
1830         idir = GetWritableDocumentsDir();
1831 
1832     result = androidFileChooser(&file, idir, Title, suggestedName, wildcard);
1833     if(file_spec)
1834         *file_spec = file;
1835 #else
1836     long flag = wxFD_DEFAULT_STYLE;
1837     if(suggestedName.Length()){                 // new file
1838         flag = wxFD_SAVE;
1839     }
1840 
1841     wxString mask = wildcard;
1842     if( wxNOT_FOUND != mask.Find(_T("gpx")) )
1843         mask.Prepend( _T("GPX files (*.gpx)|") );
1844 
1845     wxFileDialog *psaveDialog = new wxFileDialog( parent, Title, initDir, suggestedName, mask, flag );
1846 
1847 //    Try to reduce the dialog size, and scale fonts down, if necessary.
1848 //     if(g_bresponsive && parent)
1849 //         psaveDialog = g_Platform->AdjustFileDialogFont(parent, psaveDialog);
1850 
1851 #ifdef __WXOSX__
1852     if(parent)
1853         parent->HideWithEffect(wxSHOW_EFFECT_BLEND );
1854 #endif
1855 
1856      result = psaveDialog->ShowModal();
1857 
1858 #ifdef __WXOSX__
1859     if(parent)
1860         parent->ShowWithEffect(wxSHOW_EFFECT_BLEND );
1861 #endif
1862 
1863     if(file_spec)
1864         *file_spec = psaveDialog->GetPath();
1865     delete psaveDialog;
1866 
1867 #endif
1868 
1869     return result;
1870 }
1871 
DoDirSelectorDialog(wxWindow * parent,wxString * file_spec,wxString Title,wxString initDir,bool b_addFiles)1872 int OCPNPlatform::DoDirSelectorDialog( wxWindow *parent, wxString *file_spec, wxString Title, wxString initDir, bool b_addFiles)
1873 {
1874     wxString dir;
1875     int result = wxID_CANCEL;
1876 
1877 #ifdef __OCPN__ANDROID__
1878     //  Verify that initDir is traversable, fix it if not...
1879     wxString idir = initDir;
1880     if(initDir.StartsWith(_T("/data/data")))                 // not good, provokes a crash usually...
1881         idir = GetWritableDocumentsDir();
1882 
1883     result = androidFileChooser(&dir, idir, Title, _T(""), _T(""), true, b_addFiles);    // Directories only, maybe add dirs
1884     if(file_spec)
1885         *file_spec = dir;
1886 #else
1887     wxDirDialog *dirSelector = new wxDirDialog( parent, Title, initDir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
1888 
1889     wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
1890     dirSelector->SetFont(*qFont);
1891 
1892 //    Try to reduce the dialog size, and scale fonts down, if necessary.
1893 //     if(g_bresponsive && parent)
1894 //         dirSelector = AdjustDirDialogFont(parent, dirSelector);
1895 
1896 #ifdef __WXOSX__
1897     if(parent)
1898         parent->HideWithEffect(wxSHOW_EFFECT_BLEND );
1899 #endif
1900 
1901     result = dirSelector->ShowModal();
1902 
1903 #ifdef __WXOSX__
1904     if(parent)
1905         parent->ShowWithEffect(wxSHOW_EFFECT_BLEND );
1906 #endif
1907 
1908     if( result == wxID_CANCEL ){
1909     }
1910     else{
1911         if(file_spec){
1912             *file_spec = dirSelector->GetPath();
1913         }
1914     }
1915 
1916     delete dirSelector;
1917 #endif
1918 
1919     return result;
1920 }
1921 
1922 
InitializeLogFile(void)1923 bool OCPNPlatform::InitializeLogFile( void )
1924 {
1925     //      Establish Log File location
1926     mlog_file = GetPrivateDataDir();
1927     appendOSDirSlash( &mlog_file );
1928 
1929 #ifdef  __WXOSX__
1930 
1931     wxFileName LibPref(mlog_file);          // starts like "~/Library/Preferences/opencpn"
1932     LibPref.RemoveLastDir();// takes off "opencpn"
1933     LibPref.RemoveLastDir();// takes off "Preferences"
1934 
1935     mlog_file = LibPref.GetFullPath();
1936     appendOSDirSlash(&mlog_file);
1937 
1938     mlog_file.Append(_T("Logs/"));// so, on OS X, opencpn.log ends up in ~/Library/Logs
1939                                    // which makes it accessible to Applications/Utilities/Console....
1940 #endif
1941 
1942 
1943     // create the opencpn "home" directory if we need to
1944     wxFileName wxHomeFiledir( GetHomeDir() );
1945     if( true != wxHomeFiledir.DirExists( wxHomeFiledir.GetPath() ) )
1946         if( !wxHomeFiledir.Mkdir( wxHomeFiledir.GetPath() ) ) {
1947             wxASSERT_MSG(false,_T("Cannot create opencpn home directory"));
1948             return false;
1949     }
1950 
1951         // create the opencpn "log" directory if we need to
1952     wxFileName wxLogFiledir( mlog_file );
1953     if( true != wxLogFiledir.DirExists( wxLogFiledir.GetPath() ) ) {
1954         if( !wxLogFiledir.Mkdir( wxLogFiledir.GetPath() ) ) {
1955             wxASSERT_MSG(false,_T("Cannot create opencpn log directory"));
1956             return false;
1957         }
1958     }
1959 
1960     mlog_file.Append( _T("opencpn.log") );
1961     wxString logit = mlog_file;
1962 
1963 #ifdef __OCPN__ANDROID__
1964     wxCharBuffer abuf = mlog_file.ToUTF8();  qDebug() << "logfile " << abuf.data();
1965 #endif
1966 
1967         //  Constrain the size of the log file
1968     if( ::wxFileExists( mlog_file ) ) {
1969             if( wxFileName::GetSize( mlog_file ) > 1000000 ) {
1970                 wxString oldlog = mlog_file;
1971                 oldlog.Append( _T(".log") );
1972                 //  Defer the showing of this messagebox until the system locale is established.
1973                 large_log_message = ( _T("Old log will be moved to opencpn.log.log") );
1974                 ::wxRenameFile( mlog_file, oldlog );
1975             }
1976     }
1977 #ifdef __OCPN__ANDROID__
1978     if( ::wxFileExists( mlog_file ) ){
1979         //  Force new logfile for each instance
1980         // TODO Remove this behaviour on Release
1981         ::wxRemoveFile( mlog_file );
1982     }
1983 
1984     if(wxLog::GetLogLevel() > wxLOG_User)
1985         wxLog::SetLogLevel(wxLOG_Info);
1986 
1987 #endif
1988     g_logger = new OcpnLog(mlog_file.mb_str());
1989     m_Oldlogger = wxLog::SetActiveTarget( g_logger );
1990 
1991     return true;
1992 
1993 }
1994 
CloseLogFile(void)1995 void OCPNPlatform::CloseLogFile( void)
1996 {
1997     if( g_logger ) {
1998         wxLog::SetActiveTarget( m_Oldlogger );
1999         delete g_logger;
2000     }
2001 }
2002 
2003 
2004 
GetConfigObject()2005 MyConfig *OCPNPlatform::GetConfigObject()
2006 {
2007     MyConfig *result = NULL;
2008 
2009     result = new MyConfig( GetConfigFileName() );
2010 
2011     return result;
2012 }
2013 
2014 
2015 
2016 
2017 
2018 
2019 
2020 //--------------------------------------------------------------------------
2021 //      Internal GPS Support
2022 //--------------------------------------------------------------------------
2023 
hasInternalGPS(wxString profile)2024 bool OCPNPlatform::hasInternalGPS(wxString profile)
2025 {
2026 
2027 #ifdef __OCPN__ANDROID__
2028     bool t = androidDeviceHasGPS();
2029 //    qDebug() << "androidDeviceHasGPS" << t;
2030     return t;
2031 #else
2032 
2033     return false;
2034 
2035 #endif
2036 }
2037 
2038 
2039 //--------------------------------------------------------------------------
2040 //      Platform Display Support
2041 //--------------------------------------------------------------------------
2042 
ShowBusySpinner(void)2043 void OCPNPlatform::ShowBusySpinner( void )
2044 {
2045 #ifdef __OCPN__ANDROID__
2046     androidShowBusyIcon();
2047 #else
2048     #if wxCHECK_VERSION(2, 9, 0 )
2049 //    if( !::wxIsBusy() )
2050     {
2051         ::wxBeginBusyCursor();
2052     }
2053     #endif
2054 #endif
2055 }
2056 
HideBusySpinner(void)2057 void OCPNPlatform::HideBusySpinner( void )
2058 {
2059 #ifdef __OCPN__ANDROID__
2060     androidHideBusyIcon();
2061 #else
2062     #if wxCHECK_VERSION(2, 9, 0 )
2063 //    if( ::wxIsBusy() )
2064     {
2065         ::wxEndBusyCursor();
2066     }
2067     #endif
2068 #endif
2069 }
2070 
GetDisplayDensityFactor()2071 double OCPNPlatform::GetDisplayDensityFactor()
2072 {
2073 #ifdef __OCPN__ANDROID__
2074     return getAndroidDisplayDensity();
2075 #else
2076     return 1.0;
2077 #endif
2078 }
2079 
GetDefaultToolbarOrientation()2080 long OCPNPlatform::GetDefaultToolbarOrientation()
2081 {
2082 #ifndef __OCPN__ANDROID__
2083     return wxTB_VERTICAL;
2084 #else
2085     return wxTB_VERTICAL;
2086 #endif
2087 }
2088 
GetStatusBarFieldCount()2089 int OCPNPlatform::GetStatusBarFieldCount()
2090 {
2091 #ifdef __OCPN__ANDROID__
2092     int count = 1;
2093 
2094     //  Make a horizontal measurement...
2095     wxScreenDC dc;
2096     wxFont* templateFont = FontMgr::Get().GetFont( _("StatusBar"), 0 );
2097     dc.SetFont(*templateFont);
2098 
2099     int width;
2100     dc.GetTextExtent(_T("WWWWWW"), &width, NULL, NULL, NULL, templateFont);
2101     double font_size_pix = (double)width / 6.0;
2102 
2103     wxSize dispSize = getDisplaySize();
2104 
2105     double nChars = dispSize.x / font_size_pix;
2106 
2107     if(nChars < 40)
2108         count = 1;
2109     else
2110         count = 2;
2111 
2112     return count;
2113 
2114 #else
2115     return STAT_FIELD_COUNT;            // default
2116 #endif
2117 
2118 }
2119 
2120 
getFontPointsperPixel(void)2121 double OCPNPlatform::getFontPointsperPixel( void )
2122 {
2123     double pt_per_pixel = 1.0;
2124 
2125 //#ifdef __OCPN__ANDROID__
2126     // On Android, this calculation depends on the density bucket in use.
2127     //  Also uses some magic numbers...
2128     //  For reference, see http://pixplicity.com/dp-px-converter/
2129     //pt_per_pixel = 14.0 / (31.11 * getAndroidDisplayDensity()) ;
2130 
2131 //#else
2132 
2133     if(m_pt_per_pixel == 0){
2134     //  Make a measurement...
2135         wxScreenDC dc;
2136 
2137         wxFont *f = FontMgr::Get().FindOrCreateFont( 12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD, FALSE,
2138                                                 wxString( _T ( "" ) ), wxFONTENCODING_SYSTEM );
2139         dc.SetFont(*f);
2140 
2141         int width, height;
2142         dc.GetTextExtent(_T("H"), &width, &height, NULL, NULL, f);
2143 
2144         if(height > 0)
2145             m_pt_per_pixel = 12.0 / (double)height;
2146     }
2147     if(m_pt_per_pixel > 0)
2148         pt_per_pixel = m_pt_per_pixel;
2149 //#endif
2150 
2151     return pt_per_pixel;
2152 
2153 }
2154 
getDisplaySize()2155 wxSize OCPNPlatform::getDisplaySize()
2156 {
2157 #ifdef __OCPN__ANDROID__
2158     return getAndroidDisplayDimensions();
2159 #else
2160     if(m_displaySize.x < 10)
2161         m_displaySize = ::wxGetDisplaySize();               // default, for most platforms
2162     return m_displaySize;
2163 #endif
2164 
2165 }
2166 
GetDisplaySizeMM()2167 double  OCPNPlatform::GetDisplaySizeMM()
2168 {
2169     if(m_displaySizeMMOverride > 0)
2170         return m_displaySizeMMOverride;
2171 
2172     if(m_displaySizeMM.x < 1)
2173         m_displaySizeMM = wxGetDisplaySizeMM();
2174 
2175     double ret = m_displaySizeMM.GetWidth();
2176 
2177 #if 0
2178 #ifdef __WXGTK__
2179     GdkScreen *screen = gdk_screen_get_default();
2180     wxSize resolution = getDisplaySize();
2181     double gdk_monitor_mm;
2182     double ratio = (double)resolution.GetWidth() / (double)resolution.GetHeight();
2183     if( std::abs(ratio - 32.0/10.0) < std::abs(ratio - 16.0/10.0) ) {
2184         // We suspect that when the resolution aspect ratio is closer to 32:10 than 16:10, there are likely 2 monitors side by side. This works nicely when they are landscape, but what if both are rotated 90 degrees...
2185         gdk_monitor_mm = gdk_screen_get_width_mm(screen);
2186     } else {
2187         gdk_monitor_mm = gdk_screen_get_monitor_width_mm(screen, 0);
2188     }
2189     if(gdk_monitor_mm > 0) // if gdk detects a valid screen width (returns -1 on raspberry pi)
2190         ret = gdk_monitor_mm;
2191 #endif
2192 #endif
2193 
2194 #ifdef __WXMSW__
2195     int w,h;
2196 
2197     if( !m_bdisableWindowsDisplayEnum){
2198         if(GetWindowsMonitorSize( &w, &h) && (w > 100) ){             // sanity check
2199             m_displaySizeMM == wxSize(w, h);
2200             ret = w;
2201         }
2202         else
2203             m_bdisableWindowsDisplayEnum = true;        // disable permanently
2204     }
2205 
2206 #endif
2207 
2208 #ifdef __WXOSX__
2209     ret = GetMacMonitorSize();
2210 #endif
2211 
2212 #ifdef __OCPN__ANDROID__
2213     ret = GetAndroidDisplaySize();
2214 #endif
2215 
2216     wxString msg;
2217     msg.Printf(_T("Detected display size (horizontal): %d mm"), (int) ret);
2218 //     wxLogMessage(msg);
2219 
2220     return ret;
2221 }
2222 
GetDisplayAreaCM2()2223 double OCPNPlatform::GetDisplayAreaCM2()
2224 {
2225     double size1 = GetDisplaySizeMM();
2226     wxSize sz = getDisplaySize();
2227     double ratio = 1.;
2228     if(sz.x < sz.y)
2229         ratio = (double)sz.x / (double)sz.y;   // <1
2230     else
2231         ratio = (double)sz.y / (double)sz.x;   // <1
2232 
2233     double area = size1 * (size1*ratio) / 100.;
2234     //qDebug() << "cm2" << size1 << ratio << sz.x << sz.y;
2235     return area;
2236 }
2237 
2238 
2239 
SetDisplaySizeMM(double sizeMM)2240 void OCPNPlatform::SetDisplaySizeMM( double sizeMM)
2241 {
2242     m_displaySizeMMOverride = sizeMM;
2243 }
2244 
2245 
GetDisplayDPmm()2246 double OCPNPlatform::GetDisplayDPmm()
2247 {
2248 #ifdef __OCPN__ANDROID__
2249     return getAndroidDPmm();
2250 #else
2251     double r = getDisplaySize().x;            // dots
2252     return r / GetDisplaySizeMM();
2253 #endif
2254 }
2255 
GetSelectRadiusPix()2256 unsigned int OCPNPlatform::GetSelectRadiusPix()
2257 {
2258     return GetDisplayDPmm() * (g_btouch ? g_selection_radius_touch_mm : g_selection_radius_mm);
2259 }
2260 
GetFullscreen()2261 bool OCPNPlatform::GetFullscreen()
2262 {
2263     bool bret = false;
2264 #ifdef __OCPN__ANDROID__
2265     bret = androidGetFullscreen();
2266 #else
2267 
2268 #endif
2269 
2270     return bret;
2271 }
2272 
SetFullscreen(bool bFull)2273 bool OCPNPlatform::SetFullscreen( bool bFull )
2274 {
2275     bool bret = false;
2276 #ifdef __OCPN__ANDROID__
2277     bret = androidSetFullscreen( bFull );
2278 #else
2279 #endif
2280 
2281     return bret;
2282 }
2283 
2284 
PositionAISAlert(wxWindow * alert_window)2285 void OCPNPlatform::PositionAISAlert(wxWindow *alert_window)
2286 {
2287 #ifndef __OCPN__ANDROID__
2288     if(alert_window){
2289         alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y, g_ais_alert_dialog_sx, g_ais_alert_dialog_sy );
2290     }
2291 #else
2292     if(alert_window){
2293         alert_window->SetSize(g_ais_alert_dialog_x, g_ais_alert_dialog_y, g_ais_alert_dialog_sx, g_ais_alert_dialog_sy );
2294         alert_window->Centre();
2295     }
2296 
2297 #endif
2298 }
2299 
2300 
2301 
AdjustDirDialogFont(wxWindow * container,wxDirDialog * dlg)2302 wxDirDialog* OCPNPlatform::AdjustDirDialogFont(wxWindow *container, wxDirDialog* dlg)
2303 {
2304     wxDirDialog* ret_dlg = dlg;
2305 #ifndef __WXGTK__
2306 
2307         dlg->Show();
2308         dlg->SetSize( container->GetSize());
2309         dlg->Centre();
2310 
2311         wxSize sds = dlg->GetSize();
2312         wxSize ss = container->GetSize();
2313 
2314 
2315         if(sds.x > ss.x){
2316             dlg->Hide();
2317 
2318             wxString msg = dlg->GetMessage();
2319             wxString default_dir = dlg->GetPath();
2320 
2321             delete dlg;
2322 
2323             ret_dlg = new wxDirDialog( NULL, msg, default_dir, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST );
2324 
2325 
2326             wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
2327             wxFont *smallFont = new wxFont( * dialogFont );
2328             smallFont->SetPointSize( (smallFont->GetPointSize() / 2) + 0.5 ); // + 0.5 to round instead of truncate
2329             ret_dlg->SetFont( * smallFont );
2330 
2331             ret_dlg->SetSize( container->GetSize());
2332             ret_dlg->Centre();
2333 
2334         }
2335         ret_dlg->Hide();
2336 #endif
2337     return ret_dlg;
2338 }
2339 
AdjustFileDialogFont(wxWindow * container,wxFileDialog * dlg)2340 wxFileDialog* OCPNPlatform::AdjustFileDialogFont(wxWindow *container, wxFileDialog* dlg)
2341 {
2342             wxFileDialog* ret_dlg = dlg;
2343 #ifndef __WXGTK__
2344 
2345             dlg->Show();
2346             dlg->SetSize( container->GetSize());
2347             dlg->Centre();
2348 
2349             wxSize sds = dlg->GetSize();
2350             wxSize ss = container->GetSize();
2351 
2352 
2353             if(sds.x > ss.x){
2354                 dlg->Hide();
2355 
2356                 wxString msg = dlg->GetMessage();
2357                 wxString default_dir = dlg->GetDirectory();
2358                 wxString default_file = dlg->GetFilename();
2359                 wxString wildcard = dlg->GetWildcard();
2360 
2361                 delete dlg;
2362 
2363                 ret_dlg = new wxFileDialog( NULL, msg, default_dir, default_file,  wildcard, wxFD_OPEN );
2364 
2365 
2366                 wxFont *dialogFont = GetOCPNScaledFont(_("Dialog"));
2367                 wxFont *smallFont = new wxFont( * dialogFont );
2368                 smallFont->SetPointSize( (smallFont->GetPointSize() / 2) + 0.5 ); // + 0.5 to round instead of truncate
2369                 ret_dlg->SetFont( * smallFont );
2370 
2371                 ret_dlg->SetSize( container->GetSize());
2372                 ret_dlg->Centre();
2373 
2374             }
2375             ret_dlg->Hide();
2376 #endif
2377             return ret_dlg;
2378 }
2379 
GetToolbarScaleFactor(int GUIScaleFactor)2380 double OCPNPlatform::GetToolbarScaleFactor( int GUIScaleFactor )
2381 {
2382     double rv = 1.0;
2383 #ifdef __OCPN__ANDROID__
2384 
2385     // We try to arrange matters so that at GUIScaleFactor=0, the tool icons are approximately 9 mm in size
2386     // and that the value may range from 0.5 -> 2.0
2387 
2388     //  Get the basic size of a tool icon
2389     wxSize style_tool_size(32,32);
2390 
2391     if(g_StyleManager){
2392     ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2393         if(style)
2394             style_tool_size = style->GetToolSize();
2395     }
2396     double tool_size = style_tool_size.x;
2397 
2398     // unless overridden by user, we declare the "best" tool size
2399     // to be roughly the same as the ActionBar height.
2400     //  This may be approximated in a device orientation-independent way as:
2401     //   45pixels * DENSITY
2402     double premult = 1.0;
2403     if( g_config_display_size_manual && (g_config_display_size_mm > 0) ){
2404         double target_size = 9.0;                // mm
2405 
2406         double basic_tool_size_mm = tool_size / GetDisplayDPmm();
2407         premult = target_size / basic_tool_size_mm;
2408 
2409     }
2410     else{
2411         premult = wxMax(45 * getAndroidDisplayDensity(), 45) / tool_size;       // make sure not too small
2412     }
2413 
2414     //Adjust the scale factor using the global GUI scale parameter, ranging from 0.5 -> 2.0
2415     double postmult =  exp( GUIScaleFactor * (log(2.0) / 5.0) );
2416 
2417 //        qDebug() << "parmsF" << GUIScaleFactor << premult << postmult;
2418 
2419     rv = premult * postmult;
2420     rv = wxMin(rv, getAndroidDisplayDensity() * 3);      //  Clamp at density * arbitrary limit factor
2421 
2422 #else
2423     double premult = 1.0;
2424 
2425     if(g_bresponsive){
2426     //  Get the basic size of a tool icon
2427         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2428         wxSize style_tool_size = style->GetToolSize();
2429         double tool_size = style_tool_size.x;
2430 
2431     // unless overridden by user, we declare the "best" tool size
2432     // to be roughly 9 mm square.
2433         double target_size = 9.0;                // mm
2434 
2435         double basic_tool_size_mm = tool_size / GetDisplayDPmm();
2436         premult = target_size / basic_tool_size_mm;
2437     }
2438 
2439     //Adjust the scale factor using the global GUI scale parameter
2440     double postmult =  exp( GUIScaleFactor * (0.693 / 5.0) );       //  exp(2)
2441 
2442 
2443     rv = premult * postmult;
2444     rv = wxMin(rv, 3.0);      //  Clamp at 3.0
2445     rv = wxMax(rv, 0.5);      //  and at 0.5
2446 
2447 #endif
2448 
2449     return rv;
2450 }
2451 
GetCompassScaleFactor(int GUIScaleFactor)2452 double OCPNPlatform::GetCompassScaleFactor( int GUIScaleFactor )
2453 {
2454     double rv = 1.0;
2455 #ifdef __OCPN__ANDROID__
2456 
2457     // We try to arrange matters so that at GUIScaleFactor=0, the compass icon is approximately 9 mm in size
2458     // and that the value may range from 0.5 -> 2.0
2459 
2460     if(g_bresponsive ){
2461         //  Get the basic size of a tool icon
2462         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2463         wxSize style_tool_size = style->GetToolSize();
2464         double compass_size = style_tool_size.x;
2465 
2466         // We declare the "nominal best" icon size to be a bit smaller than the ActionBar height.
2467         //  This may be approximated in a device orientation-independent way as: 28pixels * DENSITY
2468         double premult = wxMax(28 * getAndroidDisplayDensity(), 50) / compass_size;
2469 
2470         //Adjust the scale factor using the global GUI scale parameter
2471         double postmult =  exp( GUIScaleFactor * (log(2.0) / 5.0) );
2472         //rv = wxMin(rv, 1.5);      //  Clamp at 1.5
2473 
2474         rv = premult * postmult;
2475         rv = wxMin(rv, getAndroidDisplayDensity() * 3);      //  Clamp at density * arbitrary limit factor
2476     }
2477 
2478 
2479 
2480 #else
2481     double premult = 1.0;
2482 
2483     if(g_bresponsive ){
2484 
2485         ocpnStyle::Style* style = g_StyleManager->GetCurrentStyle();
2486         wxSize style_tool_size = style->GetToolSize();
2487         double compass_size = style_tool_size.x;
2488 
2489         // We declare the "best" tool size to be roughly 6 mm.
2490         double target_size = 6.0;                // mm
2491 
2492         double basic_tool_size_mm = compass_size / GetDisplayDPmm();
2493         premult = target_size / basic_tool_size_mm;
2494     }
2495 
2496     double postmult =  exp( GUIScaleFactor * (0.693 / 5.0) );       //  exp(2)
2497 
2498     rv = premult * postmult;
2499     rv = wxMin(rv, 3.0);      //  Clamp at 3.0
2500     rv = wxMax(rv, 0.5);
2501 
2502 #endif
2503 
2504     return rv;
2505 }
2506 
getChartScaleFactorExp(float scale_linear)2507 float OCPNPlatform::getChartScaleFactorExp( float scale_linear )
2508 {
2509     double factor = 1.0;
2510 #ifndef __OCPN__ANDROID__
2511     factor =  exp( scale_linear * (log(3.0) / 5.0) );
2512 
2513 #else
2514     // the idea here is to amplify the scale factor for higher density displays, in a measured way....
2515     factor =  exp( scale_linear * (0.693 / 5.0) );
2516 //    factor *= getAndroidDisplayDensity();
2517 #endif
2518 
2519     factor = wxMax(factor, .5);
2520     factor = wxMin(factor, 6.);
2521 
2522     return factor;
2523 }
2524 
2525 
2526 #ifdef __WXMSW__
2527 
2528 #define NAME_SIZE 128
2529 
2530 const GUID GUID_CLASS_MONITOR = {0x4d36e96e, 0xe325, 0x11ce, 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18};
2531 
2532 // Assumes hDevRegKey is valid
GetMonitorSizeFromEDID(const HKEY hDevRegKey,int * WidthMm,int * HeightMm)2533 bool GetMonitorSizeFromEDID(const HKEY hDevRegKey, int *WidthMm, int *HeightMm)
2534 {
2535     DWORD dwType, AcutalValueNameLength = NAME_SIZE;
2536     TCHAR valueName[NAME_SIZE];
2537 
2538     BYTE EDIDdata[1024];
2539     DWORD edidsize=sizeof(EDIDdata);
2540 
2541     for (LONG i = 0, retValue = ERROR_SUCCESS; retValue != ERROR_NO_MORE_ITEMS; ++i)
2542     {
2543         retValue = RegEnumValue ( hDevRegKey, i, &valueName[0],
2544                                   &AcutalValueNameLength, NULL, &dwType,
2545                                   EDIDdata, // buffer
2546                                   &edidsize); // buffer size
2547 
2548         if (retValue != ERROR_SUCCESS || 0 != _tcscmp(valueName,_T("EDID")))
2549             continue;
2550 
2551         *WidthMm  = ((EDIDdata[68] & 0xF0) << 4) + EDIDdata[66];
2552         *HeightMm = ((EDIDdata[68] & 0x0F) << 8) + EDIDdata[67];
2553 
2554         return true; // valid EDID found
2555     }
2556 
2557     return false; // EDID not found
2558 }
2559 
GetSizeForDevID(wxString & TargetDevID,int * WidthMm,int * HeightMm)2560 bool GetSizeForDevID(wxString &TargetDevID, int *WidthMm, int *HeightMm)
2561 {
2562     HDEVINFO devInfo = SetupDiGetClassDevsEx(
2563         &GUID_CLASS_MONITOR, //class GUID
2564         NULL, //enumerator
2565         NULL, //HWND
2566         DIGCF_PRESENT, // Flags //DIGCF_ALLCLASSES|
2567         NULL, // device info, create a new one.
2568         NULL, // machine name, local machine
2569         NULL);// reserved
2570 
2571     if (NULL == devInfo)
2572         return false;
2573 
2574     bool bRes = false;
2575 
2576     for (ULONG i=0; ERROR_NO_MORE_ITEMS != GetLastError(); ++i)
2577     {
2578         SP_DEVINFO_DATA devInfoData;
2579         memset(&devInfoData,0,sizeof(devInfoData));
2580         devInfoData.cbSize = sizeof(devInfoData);
2581 
2582         if (SetupDiEnumDeviceInfo(devInfo,i,&devInfoData))
2583         {
2584             wchar_t    Instance[80];
2585             SetupDiGetDeviceInstanceId(devInfo, &devInfoData, Instance, MAX_PATH, NULL);
2586             wxString instance(Instance);
2587             if(instance.Upper().Find( TargetDevID.Upper() ) == wxNOT_FOUND )
2588                 continue;
2589 
2590             HKEY hDevRegKey = SetupDiOpenDevRegKey(devInfo,&devInfoData,
2591                                                    DICS_FLAG_GLOBAL, 0, DIREG_DEV, KEY_READ);
2592 
2593             if(!hDevRegKey || (hDevRegKey == INVALID_HANDLE_VALUE))
2594                 continue;
2595 
2596             bRes = GetMonitorSizeFromEDID(hDevRegKey, WidthMm, HeightMm);
2597 
2598             RegCloseKey(hDevRegKey);
2599         }
2600     }
2601     SetupDiDestroyDeviceInfoList(devInfo);
2602     return bRes;
2603 }
2604 
GetWindowsMonitorSize(int * width,int * height)2605 bool OCPNPlatform::GetWindowsMonitorSize( int *width, int *height)
2606 {
2607     bool bFoundDevice = true;
2608 
2609     if(m_monitorWidth < 10){
2610 
2611         int WidthMm = 0;
2612         int HeightMm = 0;
2613 
2614         DISPLAY_DEVICE dd;
2615         dd.cb = sizeof(dd);
2616         DWORD dev = 0; // device index
2617         int id = 1; // monitor number, as used by Display Properties > Settings
2618 
2619         wxString DeviceID;
2620         bFoundDevice = false;
2621         while (EnumDisplayDevices(0, dev, &dd, 0) && !bFoundDevice)
2622         {
2623             DISPLAY_DEVICE ddMon;
2624             ZeroMemory(&ddMon, sizeof(ddMon));
2625             ddMon.cb = sizeof(ddMon);
2626             DWORD devMon = 0;
2627 
2628             while (EnumDisplayDevices(dd.DeviceName, devMon, &ddMon, 0) && !bFoundDevice)
2629             {
2630                 if (ddMon.StateFlags & DISPLAY_DEVICE_ACTIVE &&
2631                     !(ddMon.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
2632                 {
2633                     DeviceID = wxString(ddMon.DeviceID, wxConvUTF8);
2634                     DeviceID = DeviceID.Mid (8);
2635                     DeviceID = DeviceID.Mid (0, DeviceID.Find ( '\\' ));
2636 
2637                     bFoundDevice = GetSizeForDevID(DeviceID, &WidthMm, &HeightMm);
2638                 }
2639                 devMon++;
2640 
2641                 ZeroMemory(&ddMon, sizeof(ddMon));
2642                 ddMon.cb = sizeof(ddMon);
2643             }
2644 
2645             ZeroMemory(&dd, sizeof(dd));
2646             dd.cb = sizeof(dd);
2647             dev++;
2648 
2649         }
2650         m_monitorWidth = WidthMm;
2651         m_monitorHeight = HeightMm;
2652 
2653     }
2654 
2655     if(width)
2656         *width = m_monitorWidth;
2657     if(height)
2658         *height = m_monitorHeight;
2659 
2660     return bFoundDevice;
2661 }
2662 
2663 
2664 #endif
2665 
2666 
2667 
2668 
2669 //--------------------------------------------------------------------------
2670 //      Internal Bluetooth Support
2671 //--------------------------------------------------------------------------
2672 
hasInternalBT(wxString profile)2673 bool OCPNPlatform::hasInternalBT(wxString profile)
2674 {
2675 #ifdef __OCPN__ANDROID__
2676     bool t = androidDeviceHasBlueTooth();
2677 //    qDebug() << "androidDeviceHasBluetooth" << t;
2678     return t;
2679 #else
2680 
2681     return false;
2682 #endif
2683 }
2684 
startBluetoothScan()2685 bool OCPNPlatform::startBluetoothScan()
2686 {
2687 #ifdef __OCPN__ANDROID__
2688     return androidStartBluetoothScan();
2689 #else
2690 
2691     return false;
2692 #endif
2693 }
2694 
stopBluetoothScan()2695 bool OCPNPlatform::stopBluetoothScan()
2696 {
2697 #ifdef __OCPN__ANDROID__
2698     return androidStopBluetoothScan();
2699 #else
2700 
2701     return false;
2702 #endif
2703 }
2704 
2705 
getBluetoothScanResults()2706 wxArrayString OCPNPlatform::getBluetoothScanResults()
2707 {
2708     wxArrayString ret_val;
2709 #ifdef __OCPN__ANDROID__
2710     return androidGetBluetoothScanResults();
2711 #else
2712 
2713     ret_val.Add(_T("line 1"));
2714     ret_val.Add(_T("line 2"));
2715     ret_val.Add(_T("line 3"));
2716     return ret_val;
2717 
2718 #endif
2719 
2720 }
2721 
2722 
2723 //--------------------------------------------------------------------------
2724 //      Per-Platform Utility support
2725 //--------------------------------------------------------------------------
2726 
AllowAlertDialog(const wxString & class_name)2727 bool OCPNPlatform::AllowAlertDialog(const wxString& class_name)
2728 {
2729 #ifdef __OCPN__ANDROID__
2730     //  allow if TopLevelWindow count is <=4, implying normal runtime screen layout
2731     int nTLW = 0;
2732     wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst();
2733     while (node)
2734     {
2735         wxWindow* win = node->GetData();
2736         if(win->IsShown())
2737             nTLW++;
2738 
2739         node = node->GetNext();
2740     }
2741 
2742     //qDebug() << "AllowAlertDialog" << g_boptionsactive << g_running << nTLW;
2743     return (g_running && !g_boptionsactive && (nTLW <= 4));
2744 
2745 #else
2746     return true;
2747 #endif
2748 }
2749 
2750 
setChartTypeMaskSel(int mask,wxString & indicator)2751 void OCPNPlatform::setChartTypeMaskSel(int mask, wxString &indicator)
2752 {
2753 #ifdef __OCPN__ANDROID__
2754     return androidSetChartTypeMaskSel(mask, indicator);
2755 #endif
2756 
2757 }
2758 
2759 #ifdef __OCPN__ANDROID__
2760 QString g_qtStyleSheet;
2761 
LoadQtStyleSheet(wxString & sheet_file)2762 bool LoadQtStyleSheet(wxString &sheet_file)
2763 {
2764     if(wxFileExists( sheet_file )){
2765         //        QApplication qApp = getqApp();
2766         if(qApp){
2767             QString file(sheet_file.c_str());
2768             QFile File(file);
2769             File.open(QFile::ReadOnly);
2770             g_qtStyleSheet = QLatin1String(File.readAll());
2771 
2772  //           qApp->setStyleSheet(g_qtStyleSheet);
2773             return true;
2774         }
2775         else
2776             return false;
2777     }
2778     else
2779         return false;
2780 }
2781 
getQtStyleSheet(void)2782 QString getQtStyleSheet( void )
2783 {
2784     return g_qtStyleSheet;
2785 }
2786 
2787 #endif
2788 
2789 
2790 PlatSpec android_plat_spc;
2791 
isPlatformCapable(int flag)2792 bool OCPNPlatform::isPlatformCapable( int flag){
2793 
2794 #ifndef __OCPN__ANDROID__
2795     return true;
2796 #else
2797     if(flag == PLATFORM_CAP_PLUGINS){
2798         long platver;
2799         wxString tsdk(android_plat_spc.msdk);
2800         if(tsdk.ToLong(&platver)){
2801             if(platver >= 11)
2802                 return true;
2803         }
2804     }
2805     else if(flag == PLATFORM_CAP_FASTPAN){
2806         long platver;
2807         wxString tsdk(android_plat_spc.msdk);
2808         if(tsdk.ToLong(&platver)){
2809             if(platver >= 14)
2810                 return true;
2811         }
2812     }
2813 
2814     return false;
2815 #endif
2816 }
2817 
DoHelpDialog(void)2818 void OCPNPlatform::DoHelpDialog( void ) {
2819 #ifndef __OCPN__ANDROID__
2820     if( !g_pAboutDlg ) {
2821         g_pAboutDlg = new AboutFrameImpl( gFrame );
2822         } else {
2823             g_pAboutDlg->SetFocus();
2824         }
2825     g_pAboutDlg->Show();
2826 
2827 #else
2828     if( !g_pAboutDlgLegacy )
2829         g_pAboutDlgLegacy = new about( gFrame, GetSharedDataDir() );
2830     else
2831         g_pAboutDlg->SetFocus();
2832     g_pAboutDlgLegacy->Show();
2833 
2834 
2835 #endif
2836 
2837 }
2838 
LaunchLocalHelp(void)2839 void OCPNPlatform::LaunchLocalHelp( void ) {
2840 
2841 #ifdef __OCPN__ANDROID__
2842     androidLaunchHelpView();
2843 #else
2844     wxString def_lang_canonical = _T("en_US");
2845 
2846     #if wxUSE_XLOCALE
2847     if(plocale_def_lang)
2848         def_lang_canonical = plocale_def_lang->GetCanonicalName();
2849     #endif
2850 
2851         wxString help_locn = g_Platform->GetSharedDataDir() + _T("doc/help_");
2852 
2853         wxString help_try = help_locn + def_lang_canonical + _T(".html");
2854 
2855         if( ! ::wxFileExists( help_try ) ) {
2856             help_try = help_locn + _T("en_US") + _T(".html");
2857 
2858             if( ! ::wxFileExists( help_try ) ) {
2859                 help_try = help_locn + _T("web") + _T(".html");
2860             }
2861 
2862             if( ! ::wxFileExists( help_try ) ) return;
2863         }
2864 
2865         wxLaunchDefaultBrowser(wxString( _T("file:///") ) + help_try );
2866 #endif
2867 }
2868 
platformLaunchDefaultBrowser(wxString URL)2869 void OCPNPlatform::platformLaunchDefaultBrowser( wxString URL )
2870 {
2871 #ifdef __OCPN__ANDROID__
2872     androidLaunchBrowser( URL );
2873 #else
2874     ::wxLaunchDefaultBrowser( URL );
2875 #endif
2876 }
2877 
2878 // ============================================================================
2879 // OCPNColourPickerCtrl implementation
2880 // ============================================================================
2881 
BEGIN_EVENT_TABLE(OCPNColourPickerCtrl,wxButton)2882 BEGIN_EVENT_TABLE(OCPNColourPickerCtrl, wxButton)
2883 #ifdef __WXMSW__
2884     EVT_PAINT(OCPNColourPickerCtrl::OnPaint)
2885 #endif
2886 END_EVENT_TABLE()
2887 
2888 // ----------------------------------------------------------------------------
2889 // OCPNColourPickerCtrl
2890 // ----------------------------------------------------------------------------
2891 
2892 OCPNColourPickerCtrl::OCPNColourPickerCtrl(wxWindow *parent,
2893                    wxWindowID id,
2894                    const wxColour& initial,
2895                    const wxPoint& pos,
2896                    const wxSize& size,
2897                    long style,
2898                    const wxValidator& validator,
2899                    const wxString& name)
2900 {
2901     Create(parent, id, initial, pos, size, style, validator, name);
2902 }
2903 
Create(wxWindow * parent,wxWindowID id,const wxColour & col,const wxPoint & pos,const wxSize & size,long style,const wxValidator & validator,const wxString & name)2904 bool OCPNColourPickerCtrl::Create( wxWindow *parent, wxWindowID id,
2905                         const wxColour &col, const wxPoint &pos,
2906                         const wxSize &size, long style,
2907                         const wxValidator& validator, const wxString &name)
2908 {
2909     m_bitmap = wxBitmap( 60, 13 );
2910 
2911     // create this button
2912     if (!wxBitmapButton::Create( parent, id, m_bitmap, pos,
2913                            size, style | wxBU_AUTODRAW, validator, name ))
2914     {
2915         wxFAIL_MSG( wxT("OCPNColourPickerCtrl creation failed") );
2916         return false;
2917     }
2918 
2919     // and handle user clicks on it
2920     Connect(GetId(), wxEVT_BUTTON,
2921             wxCommandEventHandler(OCPNColourPickerCtrl::OnButtonClick),
2922             NULL, this);
2923 
2924     m_colour = col;
2925     UpdateColour();
2926     InitColourData();
2927 
2928     return true;
2929 }
2930 
InitColourData()2931 void OCPNColourPickerCtrl::InitColourData()
2932 {
2933 #if 0
2934     ms_data.SetChooseFull(true);
2935     unsigned char grey = 0;
2936     for (int i = 0; i < 16; i++, grey += 16)
2937     {
2938         // fill with grey tones the custom colors palette
2939         wxColour colour(grey, grey, grey);
2940         ms_data.SetCustomColour(i, colour);
2941     }
2942 #endif
2943 }
2944 
OnButtonClick(wxCommandEvent & WXUNUSED (ev))2945 void OCPNColourPickerCtrl::OnButtonClick(wxCommandEvent& WXUNUSED(ev))
2946 {
2947 #ifdef __OCPN__ANDROID__
2948   unsigned int cco = 0;
2949   cco |= 0xff;  cco  = cco << 8;
2950   cco |= m_colour.Red(); cco = cco << 8;
2951   cco |= m_colour.Green(); cco = cco << 8;
2952   cco |= m_colour.Blue();
2953   unsigned int cc = androidColorPicker( cco);
2954 
2955   wxColour cnew;
2956   unsigned char blue = (unsigned char) cc % 256;
2957   unsigned char green = (unsigned char) (cc >> 8) % 256;;
2958   unsigned char red = (unsigned char) (cc >> 16) % 256;
2959   cnew.Set(red, green, blue);
2960 
2961   SetColour(cnew);
2962 
2963 #else
2964     // update the wxColouData to be shown in the dialog
2965     ms_data.SetColour(m_colour);
2966 
2967     // create the colour dialog and display it
2968     wxColourDialog dlg(this, &ms_data);
2969     if (dlg.ShowModal() == wxID_OK)
2970     {
2971          ms_data = dlg.GetColourData();
2972          SetColour(ms_data.GetColour());
2973     }
2974 #endif
2975 }
2976 
UpdateColour()2977 void OCPNColourPickerCtrl::UpdateColour()
2978 {
2979 #ifndef __OCPN__ANDROID__
2980     SetBitmapLabel(wxBitmap());
2981 #endif
2982 
2983     wxMemoryDC dc(m_bitmap);
2984     dc.SetPen( *wxTRANSPARENT_PEN );
2985     dc.SetBrush( wxBrush(m_colour) );
2986     dc.DrawRectangle( 0,0,m_bitmap.GetWidth(),m_bitmap.GetHeight() );
2987 
2988 
2989     dc.SelectObject( wxNullBitmap );
2990     SetBitmapLabel( m_bitmap );
2991 }
2992 
SetColour(wxColour & c)2993 void OCPNColourPickerCtrl::SetColour( wxColour& c)
2994 {
2995     m_colour = c;
2996     m_bitmap = wxBitmap(GetSize().x - 20, GetSize().y - 20);
2997     UpdateColour();
2998 }
2999 
3000 
GetColour(void)3001 wxColour OCPNColourPickerCtrl::GetColour( void )
3002 {
3003     return m_colour;
3004 }
3005 
DoGetBestSize() const3006 wxSize OCPNColourPickerCtrl::DoGetBestSize() const
3007 {
3008     wxSize sz(wxBitmapButton::DoGetBestSize());
3009 #ifdef __WXMAC__
3010     sz.y += 6;
3011 #else
3012     sz.y += 2;
3013 #endif
3014     sz.x += 30;
3015     if ( HasFlag(wxCLRP_SHOW_LABEL) )
3016         return sz;
3017 
3018     // if we have no label, then make this button a square
3019     // (like e.g. native GTK version of this control) ???
3020     // sz.SetWidth(sz.GetHeight());
3021     return sz;
3022 }
3023 
OnPaint(wxPaintEvent & event)3024 void OCPNColourPickerCtrl::OnPaint(wxPaintEvent &event)
3025 {
3026 
3027     wxPaintDC dc(this) ;
3028 
3029     int offset_x = (GetSize().x - m_bitmap.GetWidth()) / 2;
3030     int offset_y = (GetSize().y - m_bitmap.GetHeight()) / 2;
3031 
3032     dc.SetPen( *wxTRANSPARENT_PEN );
3033     dc.SetBrush( wxBrush(m_colour) );
3034     dc.DrawRectangle( offset_x, offset_y, m_bitmap.GetWidth(), m_bitmap.GetHeight() );
3035 
3036     event.Skip() ;
3037 }
3038 
3039