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