1 /***************************************************************************
2  * $Id: dashboard_pi.cpp, v1.0 2010/08/05 SethDart Exp $
3  *
4  * Project:  OpenCPN
5  * Purpose:  Dashboard Plugin
6  * Author:   Jean-Eudes Onfray
7  *
8  ***************************************************************************
9  *   Copyright (C) 2010 by David S. Register                               *
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  *   This program is distributed in the hope that it will be useful,       *
17  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
18  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
19  *   GNU General Public License for more details.                          *
20  *                                                                         *
21  *   You should have received a copy of the GNU General Public License     *
22  *   along with this program; if not, write to the                         *
23  *   Free Software Foundation, Inc.,                                       *
24  *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,  USA.         *
25  ***************************************************************************
26  */
27 
28 
29 #include "wx/wxprec.h"
30 
31 #ifndef  WX_PRECOMP
32 #include "wx/wx.h"
33 #endif //precompiled headers
34 
35 #include <cmath>
36 // xw 2.8
37 #include <wx/filename.h>
38 #include <wx/fontdlg.h>
39 
40 #include <typeinfo>
41 #include "dashboard_pi.h"
42 #include "icons.h"
43 #include "wx/jsonreader.h"
44 #include "wx/jsonwriter.h"
45 
46 wxFont *g_pFontTitle;
47 wxFont *g_pFontData;
48 wxFont *g_pFontLabel;
49 wxFont *g_pFontSmall;
50 int g_iDashSpeedMax;
51 int g_iDashCOGDamp;
52 int g_iDashSpeedUnit;
53 int g_iDashSOGDamp;
54 int g_iDashDepthUnit;
55 int g_iDashDistanceUnit;
56 int g_iDashWindSpeedUnit;
57 int g_iUTCOffset;
58 double g_dDashDBTOffset;
59 bool g_iDashUsetruewinddata;
60 double g_dHDT;
61 
62 #if !defined(NAN)
63 static const long long lNaN = 0xfff8000000000000;
64 #define NAN (*(double*)&lNaN)
65 #endif
66 
67 #ifdef __OCPN__ANDROID__
68 #include "qdebug.h"
69 #endif
70 
71 // the class factories, used to create and destroy instances of the PlugIn
72 
create_pi(void * ppimgr)73 extern "C" DECL_EXP opencpn_plugin* create_pi( void *ppimgr )
74 {
75     return (opencpn_plugin *) new dashboard_pi( ppimgr );
76 }
77 
destroy_pi(opencpn_plugin * p)78 extern "C" DECL_EXP void destroy_pi( opencpn_plugin* p )
79 {
80     delete p;
81 }
82 
83 #ifdef __OCPN__ANDROID__
84 
85 QString qtStyleSheet = "QScrollBar:horizontal {\
86 border: 0px solid grey;\
87 background-color: rgb(240, 240, 240);\
88 height: 35px;\
89 margin: 0px 1px 0 1px;\
90 }\
91 QScrollBar::handle:horizontal {\
92 background-color: rgb(200, 200, 200);\
93 min-width: 20px;\
94 border-radius: 10px;\
95 }\
96 QScrollBar::add-line:horizontal {\
97 border: 0px solid grey;\
98 background: #32CC99;\
99 width: 0px;\
100 subcontrol-position: right;\
101 subcontrol-origin: margin;\
102 }\
103 QScrollBar::sub-line:horizontal {\
104 border: 0px solid grey;\
105 background: #32CC99;\
106 width: 0px;\
107 subcontrol-position: left;\
108 subcontrol-origin: margin;\
109 }\
110 QScrollBar:vertical {\
111 border: 0px solid grey;\
112 background-color: rgb(240, 240, 240);\
113 width: 35px;\
114 margin: 1px 0px 1px 0px;\
115 }\
116 QScrollBar::handle:vertical {\
117 background-color: rgb(200, 200, 200);\
118 min-height: 20px;\
119 border-radius: 10px;\
120 }\
121 QScrollBar::add-line:vertical {\
122 border: 0px solid grey;\
123 background: #32CC99;\
124 height: 0px;\
125 subcontrol-position: top;\
126 subcontrol-origin: margin;\
127 }\
128 QScrollBar::sub-line:vertical {\
129 border: 0px solid grey;\
130 background: #32CC99;\
131 height: 0px;\
132 subcontrol-position: bottom;\
133 subcontrol-origin: margin;\
134 }\
135 QCheckBox {\
136 spacing: 25px;\
137 }\
138 QCheckBox::indicator {\
139 width: 30px;\
140 height: 30px;\
141 }\
142 ";
143 
144 #endif
145 
146 #ifdef __OCPN__ANDROID__
147 #include <QtWidgets/QScroller>
148 #endif
149 
150 
151 //---------------------------------------------------------------------------------------------------------
152 //
153 //    Dashboard PlugIn Implementation
154 //
155 //---------------------------------------------------------------------------------------------------------
156 // !!! WARNING !!!
157 // do not change the order, add new instruments at the end, before ID_DBP_LAST_ENTRY!
158 // otherwise, for users with an existing opencpn.ini file, their instruments are changing !
159 enum {
160     ID_DBP_I_POS, ID_DBP_I_SOG, ID_DBP_D_SOG, ID_DBP_I_COG, ID_DBP_D_COG, ID_DBP_I_STW,
161     ID_DBP_I_HDT, ID_DBP_D_AW, ID_DBP_D_AWA, ID_DBP_I_AWS, ID_DBP_D_AWS, ID_DBP_D_TW,
162     ID_DBP_I_DPT, ID_DBP_D_DPT, ID_DBP_I_TMP, ID_DBP_I_VMG, ID_DBP_D_VMG, ID_DBP_I_RSA,
163     ID_DBP_D_RSA, ID_DBP_I_SAT, ID_DBP_D_GPS, ID_DBP_I_PTR, ID_DBP_I_GPSUTC, ID_DBP_I_SUN,
164     ID_DBP_D_MON, ID_DBP_I_ATMP, ID_DBP_I_AWA, ID_DBP_I_TWA, ID_DBP_I_TWD, ID_DBP_I_TWS,
165     ID_DBP_D_TWD, ID_DBP_I_HDM, ID_DBP_D_HDT, ID_DBP_D_WDH, ID_DBP_I_VLW1, ID_DBP_I_VLW2, ID_DBP_D_MDA, ID_DBP_I_MDA,ID_DBP_D_BPH, ID_DBP_I_FOS,
166 	ID_DBP_M_COG, ID_DBP_I_PITCH, ID_DBP_I_HEEL, ID_DBP_D_AWA_TWA, ID_DBP_I_GPSLCL, ID_DBP_I_CPULCL, ID_DBP_I_SUNLCL,
167     ID_DBP_LAST_ENTRY //this has a reference in one of the routines; defining a "LAST_ENTRY" and setting the reference to it, is one codeline less to change (and find) when adding new instruments :-)
168 };
169 
IsObsolete(int id)170 bool IsObsolete( int id ) {
171     switch( id ) {
172         case ID_DBP_D_AWA: return true;
173         default: return false;
174     }
175 }
176 
getInstrumentCaption(unsigned int id)177 wxString getInstrumentCaption( unsigned int id )
178 {
179     switch( id ){
180         case ID_DBP_I_POS:
181             return _("Position");
182         case ID_DBP_I_SOG:
183             return _("SOG");
184         case ID_DBP_D_SOG:
185             return _("Speedometer");
186         case ID_DBP_I_COG:
187             return _("COG");
188         case ID_DBP_M_COG:
189             return _("Mag COG");
190         case ID_DBP_D_COG:
191             return _("GPS Compass");
192         case ID_DBP_D_HDT:
193             return _("True Compass");
194         case ID_DBP_I_STW:
195             return _("STW");
196         case ID_DBP_I_HDT:
197             return _("True HDG");
198         case ID_DBP_I_HDM:
199             return _("Mag HDG");
200         case ID_DBP_D_AW:
201         case ID_DBP_D_AWA:
202             return _("App. Wind Angle & Speed");
203 		case ID_DBP_D_AWA_TWA:
204 			return _("App & True Wind Angle");
205         case ID_DBP_I_AWS:
206             return _("App. Wind Speed");
207         case ID_DBP_D_AWS:
208             return _("App. Wind Speed");
209         case ID_DBP_D_TW:
210             return _("True Wind Angle & Speed");
211         case ID_DBP_I_DPT:
212             return _("Depth");
213         case ID_DBP_D_DPT:
214             return _("Depth");
215         case ID_DBP_D_MDA:
216             return _("Barometric pressure");
217         case ID_DBP_I_MDA:
218             return _("Barometric pressure");
219         case ID_DBP_I_TMP:
220             return _("Water Temp.");
221         case ID_DBP_I_ATMP:
222             return _("Air Temp.");
223         case ID_DBP_I_AWA:
224             return _("App. Wind Angle");
225         case ID_DBP_I_TWA:
226             return _("True Wind Angle");
227         case ID_DBP_I_TWD:
228             return _("True Wind Direction");
229         case ID_DBP_I_TWS:
230             return _("True Wind Speed");
231         case ID_DBP_D_TWD:
232             return _("True Wind Dir. & Speed");
233         case ID_DBP_I_VMG:
234             return _("VMG");
235         case ID_DBP_D_VMG:
236             return _("VMG");
237         case ID_DBP_I_RSA:
238             return _("Rudder Angle");
239         case ID_DBP_D_RSA:
240             return _("Rudder Angle");
241         case ID_DBP_I_SAT:
242             return _("GPS in View");
243         case ID_DBP_D_GPS:
244             return _("GPS Status");
245         case ID_DBP_I_PTR:
246             return _("Cursor");
247         case ID_DBP_I_GPSUTC:
248             return _("GPS Clock");
249         case ID_DBP_I_SUN:
250             return _("Sunrise/Sunset");
251         case ID_DBP_D_MON:
252             return _("Moon phase");
253         case ID_DBP_D_WDH:
254             return _("Wind history");
255         case ID_DBP_D_BPH:
256             return  _("Barometric history");
257         case ID_DBP_I_VLW1:
258             return _("Trip Log");
259         case ID_DBP_I_VLW2:
260             return _("Sum Log");
261         case ID_DBP_I_FOS:
262             return _("From Ownship");
263 		case ID_DBP_I_PITCH:
264 			return _("Pitch");
265 		case ID_DBP_I_HEEL:
266 			return _("Heel");
267         case ID_DBP_I_GPSLCL:
268             return _( "Local GPS Clock" );
269         case ID_DBP_I_CPULCL:
270             return _( "Local CPU Clock" );
271         case ID_DBP_I_SUNLCL:
272             return _( "Local Sunrise/Sunset" );
273     }
274     return _T("");
275 }
276 
getListItemForInstrument(wxListItem & item,unsigned int id)277 void getListItemForInstrument( wxListItem &item, unsigned int id )
278 {
279     item.SetData( id );
280     item.SetText( getInstrumentCaption( id ) );
281     switch( id ){
282         case ID_DBP_I_POS:
283         case ID_DBP_I_SOG:
284         case ID_DBP_I_COG:
285         case ID_DBP_M_COG:
286         case ID_DBP_I_STW:
287         case ID_DBP_I_HDT:
288         case ID_DBP_I_HDM:
289         case ID_DBP_I_AWS:
290         case ID_DBP_I_DPT:
291     	case ID_DBP_I_MDA:
292         case ID_DBP_I_TMP:
293         case ID_DBP_I_ATMP:
294         case ID_DBP_I_TWA:
295         case ID_DBP_I_TWD:
296         case ID_DBP_I_TWS:
297         case ID_DBP_I_AWA:
298         case ID_DBP_I_VMG:
299         case ID_DBP_I_RSA:
300         case ID_DBP_I_SAT:
301         case ID_DBP_I_PTR:
302         case ID_DBP_I_GPSUTC:
303         case ID_DBP_I_GPSLCL:
304         case ID_DBP_I_CPULCL:
305         case ID_DBP_I_SUN:
306         case ID_DBP_I_SUNLCL:
307         case ID_DBP_I_VLW1:
308         case ID_DBP_I_VLW2:
309         case ID_DBP_I_FOS:
310 		case ID_DBP_I_PITCH:
311 		case ID_DBP_I_HEEL:
312             item.SetImage( 0 );
313             break;
314         case ID_DBP_D_SOG:
315         case ID_DBP_D_COG:
316         case ID_DBP_D_AW:
317         case ID_DBP_D_AWA:
318         case ID_DBP_D_AWS:
319         case ID_DBP_D_TW:
320 		case ID_DBP_D_AWA_TWA:
321         case ID_DBP_D_TWD:
322         case ID_DBP_D_DPT:
323     	case ID_DBP_D_MDA:
324         case ID_DBP_D_VMG:
325         case ID_DBP_D_RSA:
326         case ID_DBP_D_GPS:
327         case ID_DBP_D_HDT:
328         case ID_DBP_D_MON:
329         case ID_DBP_D_WDH:
330         case ID_DBP_D_BPH:
331             item.SetImage( 1 );
332             break;
333     }
334 }
335 
336 /*  These two function were taken from gpxdocument.cpp */
GetRandomNumber(int range_min,int range_max)337 int GetRandomNumber(int range_min, int range_max)
338 {
339       long u = (long)wxRound(((double)rand() / ((double)(RAND_MAX) + 1) * (range_max - range_min)) + range_min);
340       return (int)u;
341 }
342 
343 // RFC4122 version 4 compliant random UUIDs generator.
GetUUID(void)344 wxString GetUUID(void)
345 {
346       wxString str;
347       struct {
348       int time_low;
349       int time_mid;
350       int time_hi_and_version;
351       int clock_seq_hi_and_rsv;
352       int clock_seq_low;
353       int node_hi;
354       int node_low;
355       } uuid;
356 
357       uuid.time_low = GetRandomNumber(0, 2147483647);//FIXME: the max should be set to something like MAXINT32, but it doesn't compile un gcc...
358       uuid.time_mid = GetRandomNumber(0, 65535);
359       uuid.time_hi_and_version = GetRandomNumber(0, 65535);
360       uuid.clock_seq_hi_and_rsv = GetRandomNumber(0, 255);
361       uuid.clock_seq_low = GetRandomNumber(0, 255);
362       uuid.node_hi = GetRandomNumber(0, 65535);
363       uuid.node_low = GetRandomNumber(0, 2147483647);
364 
365       /* Set the two most significant bits (bits 6 and 7) of the
366       * clock_seq_hi_and_rsv to zero and one, respectively. */
367       uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80;
368 
369       /* Set the four most significant bits (bits 12 through 15) of the
370       * time_hi_and_version field to 4 */
371       uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000;
372 
373       str.Printf(_T("%08x-%04x-%04x-%02x%02x-%04x%08x"),
374       uuid.time_low,
375       uuid.time_mid,
376       uuid.time_hi_and_version,
377       uuid.clock_seq_hi_and_rsv,
378       uuid.clock_seq_low,
379       uuid.node_hi,
380       uuid.node_low);
381 
382       return str;
383 }
384 
MakeName()385 wxString MakeName()
386 {
387     return _T("DASH_") + GetUUID();
388 }
389 
390 //---------------------------------------------------------------------------------------------------------
391 //
392 //          PlugIn initialization and de-init
393 //
394 //---------------------------------------------------------------------------------------------------------
395 
dashboard_pi(void * ppimgr)396 dashboard_pi::dashboard_pi( void *ppimgr ) :
397         wxTimer( this ), opencpn_plugin_16( ppimgr )
398 {
399     // Create the PlugIn icons
400     initialize_images();
401 }
402 
~dashboard_pi(void)403 dashboard_pi::~dashboard_pi( void )
404 {
405     delete _img_dashboard_pi;
406     delete _img_dashboard;
407     delete _img_dial;
408     delete _img_instrument;
409     delete _img_minus;
410     delete _img_plus;
411 }
412 
Init(void)413 int dashboard_pi::Init( void )
414 {
415     AddLocaleCatalog( _T("opencpn-dashboard_pi") );
416 
417     mVar = NAN;
418     mPriPosition = 99;
419     mPriCOGSOG = 99;
420     mPriHeadingT = 99; // True heading
421     mPriHeadingM = 99; // Magnetic heading
422     mPriVar = 99;
423     mPriDateTime = 99;
424     mPriAWA = 99; // Relative wind
425     mPriTWA = 99; // True wind
426     mPriWDN = 99; //True hist. wind
427     mPriDepth = 99;
428     mPriSTW = 99;
429     mPriWTP = 99;
430     m_config_version = -1;
431     mHDx_Watchdog = 2;
432     mHDT_Watchdog = 2;
433     mGPS_Watchdog = 2;
434     mVar_Watchdog = 2;
435     mMWVA_Watchdog = 2;
436     mMWVT_Watchdog = 2;
437     mDPT_DBT_Watchdog = 2;
438     mSTW_Watchdog = 2;
439     mWTP_Watchdog = 2;
440     mRSA_Watchdog = 2;
441     mVMG_Watchdog = 2;
442     mUTC_Watchdog = 2;
443     mATMP_Watchdog = 2;
444     mWDN_Watchdog = 2;
445     mMDA_Watchdog = 2;
446     mPITCH_Watchdog = 2;
447     mHEEL_Watchdog = 2;
448 
449     g_pFontTitle = new wxFont( 10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL );
450     g_pFontData = new wxFont( 14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
451     g_pFontLabel = new wxFont( 8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
452     g_pFontSmall = new wxFont( 8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL );
453 
454     m_pauimgr = GetFrameAuiManager();
455     m_pauimgr->Connect( wxEVT_AUI_PANE_CLOSE, wxAuiManagerEventHandler( dashboard_pi::OnPaneClose ),
456             NULL, this );
457 
458     //    Get a pointer to the opencpn configuration object
459     m_pconfig = GetOCPNConfigObject();
460 
461     //    And load the configuration items
462     LoadConfig();
463 
464     //    This PlugIn needs a toolbar icon
465 //    m_toolbar_item_id = InsertPlugInTool( _T(""), _img_dashboard, _img_dashboard, wxITEM_CHECK,
466 //            _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this );
467 
468     wxString shareLocn =*GetpSharedDataLocation() +
469                 _T("plugins") + wxFileName::GetPathSeparator() +
470                 _T("dashboard_pi") + wxFileName::GetPathSeparator()
471                 +_T("data") + wxFileName::GetPathSeparator();
472 
473      wxString normalIcon = shareLocn + _T("Dashboard.svg");
474      wxString toggledIcon = shareLocn + _T("Dashboard_toggled.svg");
475      wxString rolloverIcon = shareLocn + _T("Dashboard_rollover.svg");
476 
477      //  For journeyman styles, we prefer the built-in raster icons which match the rest of the toolbar.
478      if(GetActiveStyleName().Lower() != _T("traditional")){
479          normalIcon = _T("");
480          toggledIcon = _T("");
481          rolloverIcon = _T("");
482      }
483 
484       m_toolbar_item_id = InsertPlugInToolSVG( _T(""), normalIcon, rolloverIcon, toggledIcon, wxITEM_CHECK,
485              _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this );
486 
487 
488     ApplyConfig();
489 
490     //  If we loaded a version 1 config setup, convert now to version 2
491     if(m_config_version == 1) {
492         SaveConfig();
493     }
494 
495     Start( 1000, wxTIMER_CONTINUOUS );
496 
497     return ( WANTS_CURSOR_LATLON | WANTS_TOOLBAR_CALLBACK | INSTALLS_TOOLBAR_TOOL
498             | WANTS_PREFERENCES | WANTS_CONFIG | WANTS_NMEA_SENTENCES | WANTS_NMEA_EVENTS
499             | USES_AUI_MANAGER | WANTS_PLUGIN_MESSAGING );
500 }
501 
DeInit(void)502 bool dashboard_pi::DeInit( void )
503 {
504     SaveConfig();
505     if( IsRunning() ) // Timer started?
506     Stop(); // Stop timer
507 
508     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
509         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
510         if( dashboard_window ) {
511             m_pauimgr->DetachPane( dashboard_window );
512             dashboard_window->Close();
513             dashboard_window->Destroy();
514             m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow = NULL;
515         }
516     }
517 
518     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
519         DashboardWindowContainer *pdwc = m_ArrayOfDashboardWindow.Item( i );
520         delete pdwc;
521     }
522 
523     delete g_pFontTitle;
524     delete g_pFontData;
525     delete g_pFontLabel;
526     delete g_pFontSmall;
527 
528     return true;
529 }
530 
GetJsonDouble(wxJSONValue & value)531 double GetJsonDouble(wxJSONValue &value) {
532     double d_ret;
533     if (value.IsDouble()) {
534         return d_ret = value.AsDouble();
535     }
536     else if (value.IsInt()) {
537         int i_ret = value.AsInt();
538         return d_ret = i_ret;
539     }
540 }
541 
Notify()542 void dashboard_pi::Notify()
543 {
544     SendUtcTimeToAllInstruments( mUTCDateTime );
545     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
546         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
547         if( dashboard_window ) dashboard_window->Refresh();
548     }
549     //  Manage the watchdogs
550 
551     mHDx_Watchdog--;
552     if( mHDx_Watchdog <= 0 ) {
553         mHdm = NAN;
554         mPriHeadingM = 99;
555         SendSentenceToAllInstruments( OCPN_DBP_STC_HDM, mHdm, _T("\u00B0") );
556         mHDx_Watchdog = gps_watchdog_timeout_ticks;
557     }
558 
559     mHDT_Watchdog--;
560     if( mHDT_Watchdog <= 0 ) {
561         mPriHeadingT = 99;
562         SendSentenceToAllInstruments( OCPN_DBP_STC_HDT, NAN, _T("\u00B0T") );
563         mHDT_Watchdog = gps_watchdog_timeout_ticks;
564     }
565 
566     mVar_Watchdog--;
567     if( mVar_Watchdog <= 0 ) {
568         mVar = NAN;
569         mPriVar = 99;
570         SendSentenceToAllInstruments( OCPN_DBP_STC_HMV, NAN, _T("\u00B0T") );
571         mVar_Watchdog = gps_watchdog_timeout_ticks;
572     }
573 
574     mGPS_Watchdog--;
575     if( mGPS_Watchdog <= 0 ) {
576         SAT_INFO sats[4];
577         for(int i=0 ; i < 4 ; i++) {
578             sats[i].SatNumber = 0;
579             sats[i].SignalToNoiseRatio = 0;
580         }
581         SendSatInfoToAllInstruments( 0, 1, sats );
582         SendSatInfoToAllInstruments( 0, 2, sats );
583         SendSatInfoToAllInstruments( 0, 3, sats );
584 
585         mSatsInView = 0;
586         SendSentenceToAllInstruments( OCPN_DBP_STC_SAT, NAN, _T("") );
587         mGPS_Watchdog = gps_watchdog_timeout_ticks;
588     }
589 
590     mMWVA_Watchdog--;
591     if (mMWVA_Watchdog <= 0) {
592         SendSentenceToAllInstruments(OCPN_DBP_STC_AWA, NAN, _T("-"));
593         SendSentenceToAllInstruments(OCPN_DBP_STC_AWS, NAN, _T("-"));
594         mPriAWA = 99;
595         mMWVA_Watchdog = gps_watchdog_timeout_ticks;
596     }
597 
598     mMWVT_Watchdog--;
599     if (mMWVT_Watchdog <= 0) {
600         SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, NAN, _T("-"));
601         SendSentenceToAllInstruments(OCPN_DBP_STC_TWS, NAN, _T("-"));
602         SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2, NAN, _T("-"));
603         mPriTWA = 99;
604         mMWVT_Watchdog = gps_watchdog_timeout_ticks;
605     }
606 
607     mDPT_DBT_Watchdog--;
608     if (mDPT_DBT_Watchdog <= 0) {
609         mPriDepth = 99;
610         SendSentenceToAllInstruments(OCPN_DBP_STC_DPT, NAN, _T("-"));
611         mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
612     }
613 
614     mSTW_Watchdog--;
615     if (mSTW_Watchdog <= 0) {
616         mPriSTW = 99;
617         SendSentenceToAllInstruments(OCPN_DBP_STC_STW, NAN, _T("-"));
618         mSTW_Watchdog = gps_watchdog_timeout_ticks;
619     }
620 
621     mWTP_Watchdog--;
622     if (mWTP_Watchdog <= 0) {
623         mPriWTP = 99;
624         SendSentenceToAllInstruments(OCPN_DBP_STC_TMP, NAN, "-");
625         mWTP_Watchdog = gps_watchdog_timeout_ticks;
626     }
627     mRSA_Watchdog--;
628     if (mRSA_Watchdog <= 0) {
629         SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, NAN, "-");
630         mRSA_Watchdog = gps_watchdog_timeout_ticks;
631     }
632     mVMG_Watchdog--;
633     if (mVMG_Watchdog <= 0) {
634         SendSentenceToAllInstruments(OCPN_DBP_STC_VMG, NAN, "-");
635         mVMG_Watchdog = gps_watchdog_timeout_ticks;
636     }
637     mUTC_Watchdog--;
638     if (mUTC_Watchdog <= 0) {
639         mPriDateTime = 99;
640         mUTC_Watchdog = gps_watchdog_timeout_ticks;
641     }
642     mATMP_Watchdog--;
643     if (mATMP_Watchdog <= 0) {
644         SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, NAN, "-");
645         mPriATMP = 99;
646         mATMP_Watchdog = gps_watchdog_timeout_ticks;
647     }
648     mWDN_Watchdog--;
649     if (mWDN_Watchdog <= 0) {
650         SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, NAN, _T("-"));
651         mPriWDN = 99;
652         mWDN_Watchdog = gps_watchdog_timeout_ticks;
653     }
654     mMDA_Watchdog--;
655     if (mMDA_Watchdog <= 0) {
656         SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, NAN, _T("-"));
657         mMDA_Watchdog = gps_watchdog_timeout_ticks;
658     }
659     mPITCH_Watchdog--;
660     if (mPITCH_Watchdog <= 0) {
661         SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, NAN, _T("-"));
662         mPITCH_Watchdog = gps_watchdog_timeout_ticks;
663     }
664     mHEEL_Watchdog--;
665     if (mHEEL_Watchdog <= 0) {
666         SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, NAN, _T("-"));
667         mHEEL_Watchdog = gps_watchdog_timeout_ticks;
668     }
669 
670 }
671 
GetAPIVersionMajor()672 int dashboard_pi::GetAPIVersionMajor()
673 {
674     return MY_API_VERSION_MAJOR;
675 }
676 
GetAPIVersionMinor()677 int dashboard_pi::GetAPIVersionMinor()
678 {
679     return MY_API_VERSION_MINOR;
680 }
681 
GetPlugInVersionMajor()682 int dashboard_pi::GetPlugInVersionMajor()
683 {
684     return PLUGIN_VERSION_MAJOR;
685 }
686 
GetPlugInVersionMinor()687 int dashboard_pi::GetPlugInVersionMinor()
688 {
689     return PLUGIN_VERSION_MINOR;
690 }
691 
GetPlugInBitmap()692 wxBitmap *dashboard_pi::GetPlugInBitmap()
693 {
694     return _img_dashboard_pi;
695 }
696 
GetCommonName()697 wxString dashboard_pi::GetCommonName()
698 {
699     return _("Dashboard");
700 }
701 
GetShortDescription()702 wxString dashboard_pi::GetShortDescription()
703 {
704     return _("Dashboard PlugIn for OpenCPN");
705 }
706 
GetLongDescription()707 wxString dashboard_pi::GetLongDescription()
708 {
709     return _("Dashboard PlugIn for OpenCPN\n\
710 Provides navigation instrument display from NMEA source.");
711 
712 }
713 
SendSentenceToAllInstruments(int st,double value,wxString unit)714 void dashboard_pi::SendSentenceToAllInstruments( int st, double value, wxString unit )
715 {
716     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
717         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
718         if( dashboard_window ) dashboard_window->SendSentenceToAllInstruments( st, value, unit );
719     }
720     if (st == OCPN_DBP_STC_HDT) {
721         g_dHDT = value;
722     }
723 }
724 
SendUtcTimeToAllInstruments(wxDateTime value)725 void dashboard_pi::SendUtcTimeToAllInstruments( wxDateTime value )
726 {
727     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
728         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
729         if( dashboard_window ) dashboard_window->SendUtcTimeToAllInstruments( value );
730     }
731 }
732 
SendSatInfoToAllInstruments(int cnt,int seq,SAT_INFO sats[4])733 void dashboard_pi::SendSatInfoToAllInstruments( int cnt, int seq, SAT_INFO sats[4] )
734 {
735     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
736         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
737         if( dashboard_window ) dashboard_window->SendSatInfoToAllInstruments( cnt, seq, sats );
738     }
739 }
740 
SetNMEASentence(wxString & sentence)741 void dashboard_pi::SetNMEASentence( wxString &sentence )
742 {
743     m_NMEA0183 << sentence;
744 
745     if( m_NMEA0183.PreParse() ) {
746         if( m_NMEA0183.LastSentenceIDReceived == _T("DBT") ) {
747             if( m_NMEA0183.Parse() ) {
748                 if( mPriDepth >= 4 ) {
749                     mPriDepth = 4;
750 
751                     /*
752                      double m_NMEA0183.Dbt.DepthFeet;
753                      double m_NMEA0183.Dbt.DepthMeters;
754                      double m_NMEA0183.Dbt.DepthFathoms;
755                      */
756                     double depth = NAN;
757                     if ( !std::isnan(m_NMEA0183.Dbt.DepthMeters) )
758                         depth = m_NMEA0183.Dbt.DepthMeters;
759                     else if( !std::isnan(m_NMEA0183.Dbt.DepthFeet) )
760                         depth = m_NMEA0183.Dbt.DepthFeet * 0.3048;
761                     else if( !std::isnan(m_NMEA0183.Dbt.DepthFathoms) ) depth =
762                             m_NMEA0183.Dbt.DepthFathoms * 1.82880;
763                     if ( !std::isnan(depth) )
764                         depth += g_dDashDBTOffset;
765                     if ( !std::isnan(depth) )
766                         SendSentenceToAllInstruments( OCPN_DBP_STC_DPT, toUsrDistance_Plugin( depth / 1852.0, g_iDashDepthUnit ), getUsrDistanceUnit_Plugin( g_iDashDepthUnit ) );
767                 }
768                 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
769             }
770         }
771 
772         else if( m_NMEA0183.LastSentenceIDReceived == _T("DPT") ) {
773             if( m_NMEA0183.Parse() ) {
774                 if (mPriDepth >= 3) {
775                     mPriDepth = 3;
776 
777                     /*
778                      double m_NMEA0183.Dpt.DepthMeters
779                      double m_NMEA0183.Dpt.OffsetFromTransducerMeters
780                      */
781                     double depth = m_NMEA0183.Dpt.DepthMeters;
782                     if (!std::isnan(m_NMEA0183.Dpt.OffsetFromTransducerMeters)) depth += m_NMEA0183.Dpt.OffsetFromTransducerMeters;
783                     depth += g_dDashDBTOffset;
784                     if (!std::isnan(depth)) {
785                         SendSentenceToAllInstruments(OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit), getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
786                         mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
787                     }
788                 }
789             }
790         }
791 // TODO: GBS - GPS Satellite fault detection
792         else if( m_NMEA0183.LastSentenceIDReceived == _T("GGA") ) {
793             if( m_NMEA0183.Parse() ) {
794                 if( m_NMEA0183.Gga.GPSQuality > 0 ) {
795                     if( mPriPosition >= 4 ) {
796                         mPriPosition = 4;
797                         double lat, lon;
798                         float llt = m_NMEA0183.Gga.Position.Latitude.Latitude;
799                         int lat_deg_int = (int) ( llt / 100 );
800                         float lat_deg = lat_deg_int;
801                         float lat_min = llt - ( lat_deg * 100 );
802                         lat = lat_deg + ( lat_min / 60. );
803                         if( m_NMEA0183.Gga.Position.Latitude.Northing == South ) lat = -lat;
804                         SendSentenceToAllInstruments( OCPN_DBP_STC_LAT, lat, _T("SDMM") );
805 
806                         float lln = m_NMEA0183.Gga.Position.Longitude.Longitude;
807                         int lon_deg_int = (int) ( lln / 100 );
808                         float lon_deg = lon_deg_int;
809                         float lon_min = lln - ( lon_deg * 100 );
810                         lon = lon_deg + ( lon_min / 60. );
811                         if( m_NMEA0183.Gga.Position.Longitude.Easting == West ) lon = -lon;
812                         SendSentenceToAllInstruments( OCPN_DBP_STC_LON, lon, _T("SDMM") );
813                     }
814 
815                     //if( mPriDateTime >= 4 ) {
816                     //    // Not in use, we need the date too.
817                     //    //mPriDateTime = 4;
818                     //    //mUTCDateTime.ParseFormat( m_NMEA0183.Gga.UTCTime.c_str(), _T("%H%M%S") );
819                     //}
820 
821                     mSatsInView = m_NMEA0183.Gga.NumberOfSatellitesInUse;
822                 }
823             }
824         }
825 
826         else if( m_NMEA0183.LastSentenceIDReceived == _T("GLL") ) {
827             if( m_NMEA0183.Parse() ) {
828                 if( m_NMEA0183.Gll.IsDataValid == NTrue ) {
829                     if( mPriPosition >= 3 ) {
830                         mPriPosition = 3;
831                         double lat, lon;
832                         float llt = m_NMEA0183.Gll.Position.Latitude.Latitude;
833                         int lat_deg_int = (int) ( llt / 100 );
834                         float lat_deg = lat_deg_int;
835                         float lat_min = llt - ( lat_deg * 100 );
836                         lat = lat_deg + ( lat_min / 60. );
837                         if( m_NMEA0183.Gll.Position.Latitude.Northing == South ) lat = -lat;
838                         SendSentenceToAllInstruments( OCPN_DBP_STC_LAT, lat, _T("SDMM") );
839 
840                         float lln = m_NMEA0183.Gll.Position.Longitude.Longitude;
841                         int lon_deg_int = (int) ( lln / 100 );
842                         float lon_deg = lon_deg_int;
843                         float lon_min = lln - ( lon_deg * 100 );
844                         lon = lon_deg + ( lon_min / 60. );
845                         if( m_NMEA0183.Gll.Position.Longitude.Easting == West ) lon = -lon;
846                         SendSentenceToAllInstruments( OCPN_DBP_STC_LON, lon, _T("SDMM") );
847                     }
848 
849                     //if( mPriDateTime >= 5 ) {
850                     //    // Not in use, we need the date too.
851                     //    //mPriDateTime = 5;
852                     //    //mUTCDateTime.ParseFormat( m_NMEA0183.Gll.UTCTime.c_str(), _T("%H%M%S") );
853                     //}
854                 }
855             }
856         }
857 
858         else if( m_NMEA0183.LastSentenceIDReceived == _T("GSV") ) {
859             if( m_NMEA0183.Parse() ) {
860                 mSatsInView = m_NMEA0183.Gsv.SatsInView;
861                 // m_NMEA0183.Gsv.NumberOfMessages;
862                 SendSentenceToAllInstruments( OCPN_DBP_STC_SAT, m_NMEA0183.Gsv.SatsInView, _T("") );
863                 SendSatInfoToAllInstruments( m_NMEA0183.Gsv.SatsInView,
864                         m_NMEA0183.Gsv.MessageNumber, m_NMEA0183.Gsv.SatInfo );
865 
866                 mGPS_Watchdog = gps_watchdog_timeout_ticks;
867             }
868         }
869 
870         else if( m_NMEA0183.LastSentenceIDReceived == _T("HDG") ) {
871             if( m_NMEA0183.Parse() )
872             {
873                 if( mPriVar >= 3 )
874                 {
875                     // Any device sending VAR=0.0 can be assumed to not really know
876                     // what the actual variation is, so in this case we use WMM if available
877                     if( (!std::isnan( m_NMEA0183.Hdg.MagneticVariationDegrees )) &&
878                                0.0 != m_NMEA0183.Hdg.MagneticVariationDegrees)
879                     {
880                         mPriVar = 3;
881                         if( m_NMEA0183.Hdg.MagneticVariationDirection == East )
882                             mVar =  m_NMEA0183.Hdg.MagneticVariationDegrees;
883                         else if( m_NMEA0183.Hdg.MagneticVariationDirection == West )
884                             mVar = -m_NMEA0183.Hdg.MagneticVariationDegrees;
885                         SendSentenceToAllInstruments( OCPN_DBP_STC_HMV, mVar, _T("\u00B0") );
886                     }
887 
888                 }
889                 if( mPriHeadingM >= 2 ) {
890                     if ( !std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees) ) {
891                         mPriHeadingM = 2;
892                         mHdm = m_NMEA0183.Hdg.MagneticSensorHeadingDegrees;
893                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm, _T("\u00B0"));
894                     }
895                 }
896                 if( !std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees) )
897                        mHDx_Watchdog = gps_watchdog_timeout_ticks;
898 
899                 //      If Variation is available, no higher priority HDT is available,
900                 //      then calculate and propagate calculated HDT
901                 if( !std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees) ) {
902                     if( !std::isnan( mVar )  && (mPriHeadingT >= 6) ){
903                         mPriHeadingT = 6;
904                         double heading = mHdm + mVar;
905                         if (heading < 0)
906                             heading += 360;
907                         else if (heading >= 360.0)
908                             heading -= 360;
909                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, _T("\u00B0"));
910                         mHDT_Watchdog = gps_watchdog_timeout_ticks;
911                     }
912                 }
913             }
914         }
915 
916         else if( m_NMEA0183.LastSentenceIDReceived == _T("HDM") ) {
917             if( m_NMEA0183.Parse() ) {
918                 if( mPriHeadingM >= 3 ) {
919                     if ( !std::isnan(m_NMEA0183.Hdm.DegreesMagnetic) ) {
920                         mPriHeadingM = 3;
921                         mHdm = m_NMEA0183.Hdm.DegreesMagnetic;
922                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm, _T("\u00B0M"));
923                         mHDx_Watchdog = gps_watchdog_timeout_ticks;
924                     }
925                 }
926 
927                 //      If Variation is available, no higher priority HDT is available,
928                 //      then calculate and propagate calculated HDT
929                 if( !std::isnan(m_NMEA0183.Hdm.DegreesMagnetic) ) {
930                     if( !std::isnan( mVar )  && (mPriHeadingT >= 4) ){
931                         mPriHeadingT = 4;
932                         double heading = mHdm + mVar;
933                         if (heading < 0)
934                             heading += 360;
935                         else if (heading >= 360.0)
936                             heading -= 360;
937                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, _T("\u00B0"));
938                         mHDT_Watchdog = gps_watchdog_timeout_ticks;
939                     }
940                 }
941 
942             }
943         }
944 
945         else if( m_NMEA0183.LastSentenceIDReceived == _T("HDT") ) {
946             if( m_NMEA0183.Parse() ) {
947                 if( mPriHeadingT >= 2 ) {
948                     mPriHeadingT = 2;
949                     if( !std::isnan(m_NMEA0183.Hdt.DegreesTrue) ) {
950                         SendSentenceToAllInstruments( OCPN_DBP_STC_HDT, m_NMEA0183.Hdt.DegreesTrue,
951                                 _T("\u00B0T") );
952                     }
953                 }
954                 if( !std::isnan(m_NMEA0183.Hdt.DegreesTrue) )
955                     mHDT_Watchdog = gps_watchdog_timeout_ticks;
956 
957             }
958         } else if( m_NMEA0183.LastSentenceIDReceived == _T("MTA") ) {  //Air temperature
959             if( m_NMEA0183.Parse() ) {
960                 if (mPriATMP >= 3) {
961                     mPriATMP = 3;
962                     SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, m_NMEA0183.Mta.Temperature,
963                         m_NMEA0183.Mta.UnitOfMeasurement);
964                     mATMP_Watchdog = gps_watchdog_timeout_ticks;
965                 }
966             }
967         } else if( m_NMEA0183.LastSentenceIDReceived == _T("MDA") ) {  //Barometric pressure
968             if( m_NMEA0183.Parse() ) {
969                 // TODO make posibilyti to select between Bar or InchHg
970                 /*
971                  double   m_NMEA0183.Mda.Pressure;
972                  wxString m_NMEA0183.Mda.UnitOfMeasurement;
973                  */
974 
975                 if( m_NMEA0183.Mda.Pressure > .8 && m_NMEA0183.Mda.Pressure < 1.1 ) {
976                     SendSentenceToAllInstruments( OCPN_DBP_STC_MDA, m_NMEA0183.Mda.Pressure *1000,
977                            _T("hPa") ); //Convert to hpa befor sending to instruments.
978                     mMDA_Watchdog = gps_watchdog_timeout_ticks;
979                 }
980 
981             }
982 
983         }
984         else if( m_NMEA0183.LastSentenceIDReceived == _T("MTW") ) {
985             if( m_NMEA0183.Parse() ) {
986                 if (mPriWTP >= 3) {
987                     mPriWTP = 3;
988                     SendSentenceToAllInstruments(OCPN_DBP_STC_TMP, m_NMEA0183.Mtw.Temperature,
989                         m_NMEA0183.Mtw.UnitOfMeasurement);
990                     mWTP_Watchdog = gps_watchdog_timeout_ticks;
991                 }
992             }
993 
994         }
995         else if( m_NMEA0183.LastSentenceIDReceived == _T("VLW") ) {
996             if( m_NMEA0183.Parse() ) {
997                 /*
998                  double   m_NMEA0183.Vlw.TotalMileage;
999                  double   m_NMEA0183.Vlw.TripMileage;
1000                                   */
1001                 SendSentenceToAllInstruments( OCPN_DBP_STC_VLW1, toUsrDistance_Plugin( m_NMEA0183.Vlw.TripMileage, g_iDashDistanceUnit ),
1002                         getUsrDistanceUnit_Plugin( g_iDashDistanceUnit ) );
1003 
1004                 SendSentenceToAllInstruments( OCPN_DBP_STC_VLW2, toUsrDistance_Plugin( m_NMEA0183.Vlw.TotalMileage, g_iDashDistanceUnit ),
1005                         getUsrDistanceUnit_Plugin( g_iDashDistanceUnit ) );
1006             }
1007 
1008         }
1009         // NMEA 0183 standard Wind Direction and Speed, with respect to north.
1010         else if( m_NMEA0183.LastSentenceIDReceived == _T("MWD") ) {
1011             if( m_NMEA0183.Parse() ) {
1012                 // Option for True vs Magnetic
1013                 wxString windunit;
1014                 if (mPriWDN >= 3) {
1015                     mPriWDN = 3;
1016                     if( !std::isnan(m_NMEA0183.Mwd.WindAngleTrue) ) { //if WindAngleTrue is available, use it ...
1017                         SendSentenceToAllInstruments( OCPN_DBP_STC_TWD, m_NMEA0183.Mwd.WindAngleTrue,
1018                                 _T("\u00B0T") );
1019                         mWDN_Watchdog = gps_watchdog_timeout_ticks;
1020                     } else if( !std::isnan(m_NMEA0183.Mwd.WindAngleMagnetic) ) { //otherwise try WindAngleMagnetic ...
1021                         SendSentenceToAllInstruments( OCPN_DBP_STC_TWD, m_NMEA0183.Mwd.WindAngleMagnetic,
1022                                 _T("\u00B0M") );
1023                         mWDN_Watchdog = gps_watchdog_timeout_ticks;
1024                     }
1025                 }
1026 
1027                 SendSentenceToAllInstruments( OCPN_DBP_STC_TWS, toUsrSpeed_Plugin( m_NMEA0183.Mwd.WindSpeedKnots, g_iDashWindSpeedUnit ),
1028                                               getUsrSpeedUnit_Plugin( g_iDashWindSpeedUnit ) );
1029                 SendSentenceToAllInstruments( OCPN_DBP_STC_TWS2, toUsrSpeed_Plugin( m_NMEA0183.Mwd.WindSpeedKnots, g_iDashWindSpeedUnit ),
1030                         getUsrSpeedUnit_Plugin( g_iDashWindSpeedUnit ) );
1031                 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1032                 //m_NMEA0183.Mwd.WindSpeedms
1033             }
1034         }
1035         // NMEA 0183 standard Wind Speed and Angle, in relation to the vessel's bow/centerline.
1036         else if( m_NMEA0183.LastSentenceIDReceived == _T("MWV") ) {
1037             if( m_NMEA0183.Parse() ) {
1038                 if( m_NMEA0183.Mwv.IsDataValid == NTrue ) {
1039                     //MWV windspeed has different units. Form it to knots to fit "toUsrSpeed_Plugin()"
1040                     double m_wSpeedFactor = 1.0; //knots ("N")
1041                     if (m_NMEA0183.Mwv.WindSpeedUnits == _T("K") ) m_wSpeedFactor = 0.53995 ; //km/h > knots
1042                     if (m_NMEA0183.Mwv.WindSpeedUnits == _T("M") ) m_wSpeedFactor = 1.94384 ; //m/s > knots
1043 
1044                     if( m_NMEA0183.Mwv.Reference == _T("R") ) // Relative (apparent wind)
1045                     {
1046                         if( mPriAWA >= 3 ) {
1047                             mPriAWA = 3;
1048 							wxString m_awaunit;
1049 							double m_awaangle;
1050 							if (m_NMEA0183.Mwv.WindAngle >180) {
1051 								m_awaunit = _T("\u00B0L");
1052 								m_awaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1053 							}
1054 							else {
1055 								m_awaunit = _T("\u00B0R");
1056 								m_awaangle = m_NMEA0183.Mwv.WindAngle;
1057 							}
1058                             SendSentenceToAllInstruments( OCPN_DBP_STC_AWA,
1059 								m_awaangle, m_awaunit);
1060                             SendSentenceToAllInstruments( OCPN_DBP_STC_AWS,
1061                                     toUsrSpeed_Plugin( m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor, g_iDashWindSpeedUnit ),
1062                                     getUsrSpeedUnit_Plugin( g_iDashWindSpeedUnit ) );
1063                             mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1064                         }
1065                     } else if( m_NMEA0183.Mwv.Reference == _T("T") ) // Theoretical (aka True)
1066                     {
1067                         if( mPriTWA >= 3 ) {
1068                             mPriTWA = 3;
1069 							wxString m_twaunit;
1070 							double m_twaangle;
1071                             bool b_R = false;
1072 							if (m_NMEA0183.Mwv.WindAngle >180) {
1073 								m_twaunit = _T("\u00B0L");
1074 								m_twaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1075 							}
1076 							else {
1077 								m_twaunit = _T("\u00B0R");
1078 								m_twaangle = m_NMEA0183.Mwv.WindAngle;
1079                                 b_R = true;
1080 							}
1081                             SendSentenceToAllInstruments( OCPN_DBP_STC_TWA,
1082 								m_twaangle, m_twaunit);
1083                             if (mPriWDN >= 4) {
1084                                 //MWV has wind angle relative to the bow. Wind history use angle relative to north.
1085                                 //If no TWD with higher priority is present and true heading is available calculate it.
1086                                 if (g_dHDT < 361. && g_dHDT >= 0.0) {
1087                                     double g_dCalWdir = (m_NMEA0183.Mwv.WindAngle) + g_dHDT;
1088                                     if (g_dCalWdir > 360.) { g_dCalWdir = g_dCalWdir - 360; }
1089                                     else if (g_dCalWdir < 0.) { g_dCalWdir = 360 - g_dCalWdir; }
1090                                     SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir, _T("\u00B0T"));
1091                                     mPriWDN = 4;
1092                                     mWDN_Watchdog = gps_watchdog_timeout_ticks;
1093                                 }
1094                             }
1095 
1096                             SendSentenceToAllInstruments( OCPN_DBP_STC_TWS,
1097                                     toUsrSpeed_Plugin( m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor, g_iDashWindSpeedUnit ),
1098                                     getUsrSpeedUnit_Plugin( g_iDashWindSpeedUnit ) );
1099                             SendSentenceToAllInstruments( OCPN_DBP_STC_TWS2,
1100                                     toUsrSpeed_Plugin( m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor, g_iDashWindSpeedUnit ),
1101                                     getUsrSpeedUnit_Plugin( g_iDashWindSpeedUnit ) );
1102                             mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1103                         }
1104                     }
1105                 }
1106             }
1107         }
1108 
1109         else if( m_NMEA0183.LastSentenceIDReceived == _T("RMC") ) {
1110             if( m_NMEA0183.Parse() ) {
1111                 if( m_NMEA0183.Rmc.IsDataValid == NTrue ) {
1112                     if( mPriPosition >= 5 ) {
1113                         mPriPosition = 5;
1114                         double lat, lon;
1115                         float llt = m_NMEA0183.Rmc.Position.Latitude.Latitude;
1116                         int lat_deg_int = (int) ( llt / 100 );
1117                         float lat_deg = lat_deg_int;
1118                         float lat_min = llt - ( lat_deg * 100 );
1119                         lat = lat_deg + ( lat_min / 60. );
1120                         if( m_NMEA0183.Rmc.Position.Latitude.Northing == South ) lat = -lat;
1121                         SendSentenceToAllInstruments( OCPN_DBP_STC_LAT, lat, _T("SDMM") );
1122 
1123                         float lln = m_NMEA0183.Rmc.Position.Longitude.Longitude;
1124                         int lon_deg_int = (int) ( lln / 100 );
1125                         float lon_deg = lon_deg_int;
1126                         float lon_min = lln - ( lon_deg * 100 );
1127                         lon = lon_deg + ( lon_min / 60. );
1128                         if( m_NMEA0183.Rmc.Position.Longitude.Easting == West ) lon = -lon;
1129                         SendSentenceToAllInstruments( OCPN_DBP_STC_LON, lon, _T("SDMM") );
1130                     }
1131 
1132                     if( mPriCOGSOG >= 3 ) {
1133                         mPriCOGSOG = 3;
1134                         if( !std::isnan(m_NMEA0183.Rmc.SpeedOverGroundKnots) ) {
1135                             SendSentenceToAllInstruments( OCPN_DBP_STC_SOG,
1136                                     toUsrSpeed_Plugin( mSOGFilter.filter(m_NMEA0183.Rmc.SpeedOverGroundKnots), g_iDashSpeedUnit ), getUsrSpeedUnit_Plugin( g_iDashSpeedUnit ) );
1137                         } else {
1138                             //->SetData(_T("---"));
1139                         }
1140                         if( !std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue) ) {
1141                             SendSentenceToAllInstruments( OCPN_DBP_STC_COG,
1142                                     mCOGFilter.filter(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue), _T("\u00B0") );
1143                         } else {
1144                             //->SetData(_T("---"));
1145                         }
1146                         if( !std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue) && !std::isnan(m_NMEA0183.Rmc.MagneticVariation) ) {
1147                             double dMagneticCOG;
1148                             if (m_NMEA0183.Rmc.MagneticVariationDirection == East) {
1149                                 dMagneticCOG = mCOGFilter.get() - m_NMEA0183.Rmc.MagneticVariation;
1150                                 if ( dMagneticCOG < 0.0 ) dMagneticCOG = 360.0 + dMagneticCOG;
1151                             }
1152                             else {
1153                                 dMagneticCOG = mCOGFilter.get() + m_NMEA0183.Rmc.MagneticVariation;
1154                                 if ( dMagneticCOG > 360.0 ) dMagneticCOG = dMagneticCOG - 360.0;
1155                             }
1156                             SendSentenceToAllInstruments( OCPN_DBP_STC_MCOG,
1157                                     dMagneticCOG, _T("\u00B0M") );
1158                         } else {
1159                             //->SetData(_T("---"));
1160                         }
1161                     }
1162 
1163                     if( mPriVar >= 4 )
1164                     {
1165                         // Any device sending VAR=0.0 can be assumed to not really know
1166                         // what the actual variation is, so in this case we use WMM if available
1167                         if( (!std::isnan( m_NMEA0183.Rmc.MagneticVariation)) &&
1168                                    0.0 != m_NMEA0183.Rmc.MagneticVariation )
1169                         {
1170                             mPriVar = 4;
1171                             if (m_NMEA0183.Rmc.MagneticVariationDirection == East)
1172                                 mVar = m_NMEA0183.Rmc.MagneticVariation;
1173                             else if (m_NMEA0183.Rmc.MagneticVariationDirection == West)
1174                                 mVar = -m_NMEA0183.Rmc.MagneticVariation;
1175                             mVar_Watchdog = gps_watchdog_timeout_ticks;
1176 
1177                             SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar, _T("\u00B0"));
1178                         }
1179                     }
1180 
1181                     if( mPriDateTime >= 3 ) {
1182                         mPriDateTime = 3;
1183                         wxString dt = m_NMEA0183.Rmc.Date + m_NMEA0183.Rmc.UTCTime;
1184                         mUTCDateTime.ParseFormat( dt.c_str(), _T("%d%m%y%H%M%S") );
1185                         mUTC_Watchdog = gps_watchdog_timeout_ticks;
1186                     }
1187                 }
1188             }
1189         }
1190 
1191         else if( m_NMEA0183.LastSentenceIDReceived == _T("RSA") ) {
1192             if( m_NMEA0183.Parse() ) {
1193                 if( m_NMEA0183.Rsa.IsStarboardDataValid == NTrue ) {
1194                     SendSentenceToAllInstruments( OCPN_DBP_STC_RSA, m_NMEA0183.Rsa.Starboard,
1195                             _T("\u00B0") );
1196                 } else if( m_NMEA0183.Rsa.IsPortDataValid == NTrue ) {
1197                     SendSentenceToAllInstruments( OCPN_DBP_STC_RSA, -m_NMEA0183.Rsa.Port,
1198                             _T("\u00B0") );
1199                 }
1200                 mRSA_Watchdog = gps_watchdog_timeout_ticks;
1201             }
1202         }
1203 
1204         else if( m_NMEA0183.LastSentenceIDReceived == _T("VHW") ) {
1205             if( m_NMEA0183.Parse() ) {
1206                 if( mPriHeadingT >= 3 ) {
1207                     if( !std::isnan(m_NMEA0183.Vhw.DegreesTrue) ) {
1208                         mPriHeadingT = 3;
1209                         SendSentenceToAllInstruments( OCPN_DBP_STC_HDT, m_NMEA0183.Vhw.DegreesTrue,
1210                                 _T("\u00B0T") );
1211                     }
1212                 }
1213                 if( mPriHeadingM >= 4 ) {
1214                     if ( !std::isnan(m_NMEA0183.Vhw.DegreesMagnetic) ) {
1215                         mPriHeadingM = 4;
1216                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, m_NMEA0183.Vhw.DegreesMagnetic,
1217                             _T("\u00B0M"));
1218                     }
1219                 }
1220                 if( !std::isnan(m_NMEA0183.Vhw.Knots) ) {
1221                     if (mPriSTW >= 2) {
1222                         mPriSTW = 2;
1223                         SendSentenceToAllInstruments(OCPN_DBP_STC_STW, toUsrSpeed_Plugin(m_NMEA0183.Vhw.Knots, g_iDashSpeedUnit),
1224                             getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1225                         mSTW_Watchdog = gps_watchdog_timeout_ticks;
1226                     }
1227                 }
1228 
1229                 if( !std::isnan(m_NMEA0183.Vhw.DegreesMagnetic) )
1230                     mHDx_Watchdog = gps_watchdog_timeout_ticks;
1231                 if( !std::isnan(m_NMEA0183.Vhw.DegreesTrue) )
1232                     mHDT_Watchdog = gps_watchdog_timeout_ticks;
1233 
1234             }
1235         }
1236 
1237         else if( m_NMEA0183.LastSentenceIDReceived == _T("VTG") ) {
1238             if( m_NMEA0183.Parse() ) {
1239                 if( mPriCOGSOG >= 2 ) {
1240                     mPriCOGSOG = 2;
1241                     //    Special check for unintialized values, as opposed to zero values
1242                     if( !std::isnan(m_NMEA0183.Vtg.SpeedKnots) ) {
1243                         SendSentenceToAllInstruments( OCPN_DBP_STC_SOG, toUsrSpeed_Plugin( mSOGFilter.filter(m_NMEA0183.Vtg.SpeedKnots), g_iDashSpeedUnit ),
1244                                 getUsrSpeedUnit_Plugin( g_iDashSpeedUnit ) );
1245                     } else {
1246                         //->SetData(_T("---"));
1247                     }
1248                     // Vtg.SpeedKilometersPerHour;
1249                     if( !std::isnan(m_NMEA0183.Vtg.TrackDegreesTrue) ) {
1250                         SendSentenceToAllInstruments( OCPN_DBP_STC_COG,
1251                                 mCOGFilter.filter(m_NMEA0183.Vtg.TrackDegreesTrue), _T("\u00B0") );
1252                     } else {
1253                         //->SetData(_T("---"));
1254                     }
1255                 }
1256 
1257                 /*
1258                  m_NMEA0183.Vtg.TrackDegreesMagnetic;
1259                  */
1260             }
1261         }
1262         /* NMEA 0183 Relative (Apparent) Wind Speed and Angle. Wind angle in relation
1263          * to the vessel's heading, and wind speed measured relative to the moving vessel. */
1264         else if( m_NMEA0183.LastSentenceIDReceived == _T("VWR") ) {
1265             if( m_NMEA0183.Parse() ) {
1266                 if (mPriAWA >= 2) {
1267                     if (m_NMEA0183.Vwr.WindDirectionMagnitude < 200) {
1268                         mPriAWA = 2;
1269 
1270                         wxString awaunit;
1271                         awaunit = m_NMEA0183.Vwr.DirectionOfWind == Left ? _T("\u00B0L") : _T("\u00B0R");
1272                         SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
1273                             m_NMEA0183.Vwr.WindDirectionMagnitude, awaunit);
1274                         SendSentenceToAllInstruments(OCPN_DBP_STC_AWS, toUsrSpeed_Plugin(m_NMEA0183.Vwr.WindSpeedKnots, g_iDashWindSpeedUnit),
1275                             getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1276                         mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1277                         /*
1278                             double m_NMEA0183.Vwr.WindSpeedms;
1279                             double m_NMEA0183.Vwr.WindSpeedKmh;
1280                             */
1281                     }
1282                 }
1283             }
1284         }
1285         /* NMEA 0183 True wind angle in relation to the vessel's heading, and true wind
1286          * speed referenced to the water. True wind is the vector sum of the Relative
1287          * (apparent) wind vector and the vessel's velocity vector relative to the water along
1288          * the heading line of the vessel. It represents the wind at the vessel if it were
1289          * stationary relative to the water and heading in the same direction. */
1290         else if( m_NMEA0183.LastSentenceIDReceived == _T("VWT") ) {
1291             if( m_NMEA0183.Parse() ) {
1292                 if( mPriTWA >= 2 ) {
1293                     if (m_NMEA0183.Vwt.WindDirectionMagnitude < 200) {
1294                         mPriTWA = 2;
1295                         wxString vwtunit;
1296                         vwtunit = m_NMEA0183.Vwt.DirectionOfWind == Left ? _T("\u00B0L") : _T("\u00B0R");
1297                         SendSentenceToAllInstruments(OCPN_DBP_STC_TWA,
1298                             m_NMEA0183.Vwt.WindDirectionMagnitude, vwtunit);
1299                         SendSentenceToAllInstruments(OCPN_DBP_STC_TWS, toUsrSpeed_Plugin(m_NMEA0183.Vwt.WindSpeedKnots, g_iDashWindSpeedUnit),
1300                             getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1301                         mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1302                         /*
1303                          double           m_NMEA0183.Vwt.WindSpeedms;
1304                          double           m_NMEA0183.Vwt.WindSpeedKmh;
1305                          */
1306                     }
1307                 }
1308             }
1309         }
1310 
1311         else if (m_NMEA0183.LastSentenceIDReceived == _T("XDR")) { //Transducer measurement
1312              /* XDR Transducer types
1313               * AngularDisplacementTransducer = 'A',
1314               * TemperatureTransducer = 'C',
1315               * LinearDisplacementTransducer = 'D',
1316               * FrequencyTransducer = 'F',
1317               * HumidityTransducer = 'H',
1318               * ForceTransducer = 'N',
1319               * PressureTransducer = 'P',
1320               * FlowRateTransducer = 'R',
1321               * TachometerTransducer = 'T',
1322               * VolumeTransducer = 'V'
1323              */
1324 
1325             if (m_NMEA0183.Parse()) {
1326                 wxString xdrunit;
1327                 double xdrdata;
1328                 for (int i = 0; i<m_NMEA0183.Xdr.TransducerCnt; i++) {
1329                     xdrdata = m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData;
1330                     // XDR Airtemp
1331                     if ((m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("C") &&
1332                         m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("TempAir")) ||
1333                         m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("ENV_OUTAIR_T") ||
1334                         m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("ENV_OUTSIDE_T")) {
1335                         if (mPriATMP >= 2) {
1336                             mPriATMP = 2;
1337                             SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, xdrdata, m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement);
1338                             mATMP_Watchdog = gps_watchdog_timeout_ticks;
1339                         }
1340                     }
1341                     // XDR Pressure
1342                     if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("P")) {
1343                         if (m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement == _T("B")) {
1344                             xdrdata *= 1000;
1345                             SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, xdrdata , _T("mBar") );
1346                             mMDA_Watchdog = gps_watchdog_timeout_ticks;
1347                         }
1348                     }
1349                     // XDR Pitch (=Nose up/down) or Heel (stb/port)
1350                     if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("A")) {
1351                         if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("PTCH")
1352                             || m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("PITCH")) {
1353                             if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1354                                 xdrunit = _T("\u00B0\u2191") + _("Up");
1355                             }
1356                             else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData < 0) {
1357                                 xdrunit = _T("\u00B0\u2193") + _("Down");
1358                                 xdrdata *= -1;
1359                             }
1360                             else {
1361                                 xdrunit = _T("\u00B0");
1362                             }
1363                             SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, xdrdata, xdrunit);
1364                             mPITCH_Watchdog = gps_watchdog_timeout_ticks;
1365                         }
1366                         // XDR Heel
1367                         else if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("ROLL")) {
1368                             if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1369                                 xdrunit = _T("\u00B0\u003E") + _("Stbd");
1370                             }
1371                             else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData < 0) {
1372                                 xdrunit = _T("\u00B0\u003C") + _("Port");
1373                                 xdrdata *= -1;
1374                             }
1375                             else {
1376                                 xdrunit = _T("\u00B0");
1377                             }
1378                             SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, xdrdata, xdrunit);
1379                             mHEEL_Watchdog = gps_watchdog_timeout_ticks;
1380                         }
1381                     }
1382 		     //Nasa style water temp
1383                     if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("ENV_WATER_T")){
1384                         if (mPriWTP >= 2) {
1385                             mPriWTP = 2;
1386                             SendSentenceToAllInstruments(OCPN_DBP_STC_TMP,
1387                                 m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData,
1388                                 m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement);
1389                             mWTP_Watchdog = gps_watchdog_timeout_ticks;
1390                         }
1391                     }
1392                 }
1393             }
1394         }
1395         else if (m_NMEA0183.LastSentenceIDReceived == _T("ZDA")) {
1396            if( m_NMEA0183.Parse() ) {
1397                 if( mPriDateTime >= 2 ) {
1398                     mPriDateTime = 2;
1399                     /*
1400                      wxString m_NMEA0183.Zda.UTCTime;
1401                      int      m_NMEA0183.Zda.Day;
1402                      int      m_NMEA0183.Zda.Month;
1403                      int      m_NMEA0183.Zda.Year;
1404                      int      m_NMEA0183.Zda.LocalHourDeviation;
1405                      int      m_NMEA0183.Zda.LocalMinutesDeviation;
1406                      */
1407                     wxString dt;
1408                     dt.Printf( _T("%4d%02d%02d"), m_NMEA0183.Zda.Year, m_NMEA0183.Zda.Month,
1409                             m_NMEA0183.Zda.Day );
1410                     dt.Append( m_NMEA0183.Zda.UTCTime );
1411                     mUTCDateTime.ParseFormat( dt.c_str(), _T("%Y%m%d%H%M%S") );
1412                     mUTC_Watchdog = gps_watchdog_timeout_ticks;
1413                 }
1414             }
1415         }
1416     }
1417         //      Process an AIVDO message
1418     else if( sentence.Mid( 1, 5 ).IsSameAs( _T("AIVDO") ) ) {
1419         PlugIn_Position_Fix_Ex gpd;
1420         if( DecodeSingleVDOMessage(sentence, &gpd, &m_VDO_accumulator) ) {
1421 
1422             if( !std::isnan(gpd.Lat) )
1423                 SendSentenceToAllInstruments( OCPN_DBP_STC_LAT, gpd.Lat, _T("SDMM") );
1424 
1425             if( !std::isnan(gpd.Lon) )
1426                 SendSentenceToAllInstruments( OCPN_DBP_STC_LON, gpd.Lon, _T("SDMM") );
1427 
1428             SendSentenceToAllInstruments(OCPN_DBP_STC_SOG, toUsrSpeed_Plugin(mSOGFilter.filter(gpd.Sog), g_iDashSpeedUnit), getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1429             SendSentenceToAllInstruments( OCPN_DBP_STC_COG, mCOGFilter.filter(gpd.Cog), _T("\u00B0") );
1430             if( !std::isnan(gpd.Hdt) ) {
1431                 SendSentenceToAllInstruments( OCPN_DBP_STC_HDT, gpd.Hdt, _T("\u00B0T") );
1432                 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1433             }
1434         }
1435     }
1436 }
1437 
ParseSignalK(wxString & msg)1438 void dashboard_pi::ParseSignalK( wxString &msg)
1439 {
1440 
1441    wxJSONValue root;
1442    wxJSONReader jsonReader;
1443 
1444    int errors = jsonReader.Parse(msg, &root);
1445 
1446     //wxString dmsg( _T("Dashboard:SignalK Event received: ") );
1447     //dmsg.append(msg);
1448     //wxLogMessage(dmsg);
1449     //printf("%s\n", dmsg.ToUTF8().data());
1450 
1451     if(root.HasMember("self")) {
1452         if(root["self"].AsString().StartsWith(_T("vessels.")))
1453             m_self = (root["self"].AsString());                                 // for java server, and OpenPlotter node.js server 1.20
1454         else
1455             m_self = _T("vessels.") + (root["self"].AsString());                // for Node.js server
1456     }
1457 
1458     if(root.HasMember("context")
1459        && root["context"].IsString()) {
1460         auto context = root["context"].AsString();
1461         if (context != m_self) {
1462             return;
1463         }
1464     }
1465 
1466     if(root.HasMember("updates")
1467        && root["updates"].IsArray()) {
1468         wxJSONValue &updates = root["updates"];
1469         for (int i = 0; i < updates.Size(); ++i) {
1470             handleSKUpdate(updates[i]);
1471         }
1472     }
1473 }
1474 
handleSKUpdate(wxJSONValue & update)1475 void dashboard_pi::handleSKUpdate(wxJSONValue &update) {
1476     wxString sfixtime = "";
1477 
1478     if(update.HasMember("timestamp")) {
1479         sfixtime = update["timestamp"].AsString();
1480     }
1481     if(update.HasMember("values")
1482        && update["values"].IsArray())
1483     {
1484         for (int j = 0; j < update["values"].Size(); ++j) {
1485             wxJSONValue &item = update["values"][j];
1486             updateSKItem(item, sfixtime);
1487         }
1488     }
1489 }
1490 
updateSKItem(wxJSONValue & item,wxString & sfixtime)1491 void dashboard_pi::updateSKItem(wxJSONValue &item, wxString &sfixtime) {
1492     if(item.HasMember("path")
1493        && item.HasMember("value")) {
1494         const wxString &update_path = item["path"].AsString();
1495         wxJSONValue &value = item["value"];
1496         if(update_path == _T("navigation.position")) {
1497             if (mPriPosition >= 2) {
1498                 if (value["latitude"].IsDouble() && value["longitude"].IsDouble()) {
1499                     double lat = value["latitude"].AsDouble();
1500                     double lon = value["longitude"].AsDouble();
1501                     SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1502                     SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1503                     mPriPosition = 2;
1504                 }
1505             }
1506         }
1507         else if(update_path == _T("navigation.speedOverGround") && 2 == mPriPosition){
1508             double sog_knot = GetJsonDouble(value);
1509             if (std::isnan(sog_knot)) return;
1510 
1511                 SendSentenceToAllInstruments( OCPN_DBP_STC_SOG,
1512                         toUsrSpeed_Plugin( mSOGFilter.filter(sog_knot),
1513                         g_iDashSpeedUnit ), getUsrSpeedUnit_Plugin( g_iDashSpeedUnit ) );
1514         }
1515         else if(update_path == _T("navigation.courseOverGroundTrue") && 2 == mPriPosition){
1516             double cog_rad = GetJsonDouble(value);
1517             if (std::isnan(cog_rad)) return;
1518 
1519                 double cog_deg = GEODESIC_RAD2DEG(cog_rad);
1520                 SendSentenceToAllInstruments( OCPN_DBP_STC_COG, mCOGFilter.filter(cog_deg), _T("\u00B0") );
1521             }
1522         else if(update_path == _T("navigation.headingTrue")){
1523             if (mPriHeadingT >= 1) {
1524                 double hdt = GetJsonDouble(value);
1525                 if (std::isnan(hdt)) return;
1526 
1527                 hdt = GEODESIC_RAD2DEG(hdt);
1528                 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, _T("\u00B0T"));
1529                 mPriHeadingT = 1;
1530                 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1531             }
1532         }
1533         else if(update_path == _T("navigation.headingMagnetic")){
1534             if (mPriHeadingM >= 1){
1535                 double hdm = GetJsonDouble(value);
1536                 if (std::isnan(hdm)) return;
1537 
1538                 hdm = GEODESIC_RAD2DEG(hdm);
1539                 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, _T("\u00B0M"));
1540                 mPriHeadingM = 1;
1541                     mHDx_Watchdog = gps_watchdog_timeout_ticks;
1542 
1543                     // If no higher priority HDT, calculate it here.
1544                     if (mPriHeadingT >= 5 && (!std::isnan(mVar))) {
1545                         double heading = hdm + mVar;
1546                         if (heading < 0)
1547                             heading += 360;
1548                         else if (heading >= 360.0)
1549                             heading -= 360;
1550                         SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, _T("\u00B0"));
1551                     mPriHeadingT = 5;
1552                         mHDT_Watchdog = gps_watchdog_timeout_ticks;
1553                     }
1554                 }
1555             }
1556         else if (update_path == _T("navigation.speedThroughWater")){
1557             if (mPriSTW >= 1) {
1558                 double stw_knots = GetJsonDouble(value);
1559                 if (std::isnan(stw_knots)) return;
1560 
1561                 stw_knots = MS2KNOTS(stw_knots);
1562                 SendSentenceToAllInstruments(OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
1563                     getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1564                 mPriSTW = 1;
1565                 mSTW_Watchdog = gps_watchdog_timeout_ticks;
1566             }
1567         }
1568         else if (update_path == _T("navigation.magneticVariation")) {
1569             if (mPriVar >= 2) {
1570                 double dvar = GetJsonDouble(value);
1571                 if (std::isnan(dvar)) return;
1572 
1573                 dvar = GEODESIC_RAD2DEG(dvar);
1574                 if (0.0 != dvar) { // Let WMM do the job instead
1575                     SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, dvar, _T("\u00B0"));
1576                     mPriVar = 2;
1577                     mVar_Watchdog = gps_watchdog_timeout_ticks;
1578                 }
1579             }
1580         }
1581         else if (update_path == _T("environment.wind.angleApparent")) {
1582             if (mPriAWA >= 1) {
1583                 double m_awaangle = GetJsonDouble(value);
1584                 if (std::isnan(m_awaangle)) return;
1585 
1586                 m_awaangle = GEODESIC_RAD2DEG(m_awaangle); // negative to port
1587                 wxString m_awaunit = _T("\u00B0R");
1588                 if (m_awaangle < 0) {
1589                     m_awaunit = _T("\u00B0L");
1590                     m_awaangle *= -1;
1591                 }
1592                 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA, m_awaangle, m_awaunit);
1593                 mPriAWA = 1; // Set prio only here. No need to catch speed if no angle.
1594                 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1595             }
1596         }
1597         else if (update_path == _T("environment.wind.speedApparent")) {
1598             if (mPriAWA >= 1) {
1599                 double m_awaspeed_kn = GetJsonDouble(value);
1600                 if (std::isnan(m_awaspeed_kn)) return;
1601 
1602                 m_awaspeed_kn = MS2KNOTS(m_awaspeed_kn);
1603                 SendSentenceToAllInstruments(OCPN_DBP_STC_AWS,
1604                     toUsrSpeed_Plugin(m_awaspeed_kn, g_iDashWindSpeedUnit),
1605                     getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1606             }
1607         }
1608         else if ((update_path == _T("environment.wind.angleTrueWater")
1609                                            && !g_iDashUsetruewinddata) ||
1610                 (update_path == _T("environment.wind.angleTrueGround")
1611                                            && g_iDashUsetruewinddata)) {
1612             if (mPriTWA >= 1) {
1613                 double m_twaangle = GetJsonDouble(value);
1614                 if (std::isnan(m_twaangle)) return;
1615 
1616                 m_twaangle = GEODESIC_RAD2DEG(m_twaangle);
1617                 double m_twaangle_raw = m_twaangle; // for wind history
1618                 wxString m_twaunit = _T("\u00B0R");
1619                 if (m_twaangle < 0) {
1620                     m_twaunit = _T("\u00B0L");
1621                     m_twaangle *= -1;
1622                 }
1623                 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle, m_twaunit);
1624                 mPriTWA = 1; // Set prio only here. No need to catch speed if no angle.
1625                 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1626 
1627                 if (mPriWDN >= 3) {
1628                     //m_twaangle_raw has wind angle relative to the bow. Wind history use angle relative to north.
1629                     //If no TWD with higher priority is present and true heading is available calculate it.
1630                     if (g_dHDT < 361. && g_dHDT >= 0.0) {
1631                         double g_dCalWdir = (m_twaangle_raw) + g_dHDT;
1632                         if (g_dCalWdir > 360.) { g_dCalWdir = g_dCalWdir - 360; }
1633                         else if (g_dCalWdir < 0.) { g_dCalWdir = 360 - g_dCalWdir; }
1634                         SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir, _T("\u00B0T"));
1635                         mPriWDN = 3;
1636                         mWDN_Watchdog = gps_watchdog_timeout_ticks;
1637                     }
1638                 }
1639             }
1640         }
1641         else if ((update_path == _T("environment.wind.speedTrue")
1642                                       && !g_iDashUsetruewinddata) ||
1643            (update_path == _T("environment.wind.speedOverGround")
1644                                       && g_iDashUsetruewinddata)) {
1645             if (mPriTWA >= 1) {
1646                 double m_twaspeed_kn = GetJsonDouble(value);
1647                 if (std::isnan(m_twaspeed_kn)) return;
1648 
1649                 m_twaspeed_kn = MS2KNOTS(m_twaspeed_kn);
1650                 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS,
1651                     toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
1652                     getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1653                 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2,
1654                     toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
1655                     getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1656             }
1657         }
1658         else if (update_path == _T("environment.depth.belowSurface")) {
1659             if (mPriDepth >= 1) {
1660                 double depth = GetJsonDouble(value);
1661                 if ( std::isnan(depth) ) return;
1662 
1663                 mPriDepth = 1;
1664                 depth += g_dDashDBTOffset;
1665                 depth /= 1852.0;
1666                 SendSentenceToAllInstruments(OCPN_DBP_STC_DPT,
1667                     toUsrDistance_Plugin(depth, g_iDashDepthUnit),
1668                     getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1669                 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1670             }
1671         }
1672         else if (update_path == _T("environment.depth.belowTransducer")) {
1673             if (mPriDepth >= 2) {
1674                 double depth = GetJsonDouble(value);
1675                 if (std::isnan(depth)) return;
1676 
1677                 mPriDepth = 2;
1678                 depth += g_dDashDBTOffset;
1679                 depth /= 1852.0;
1680                 SendSentenceToAllInstruments(OCPN_DBP_STC_DPT,
1681                     toUsrDistance_Plugin(depth, g_iDashDepthUnit),
1682                     getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1683                 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1684             }
1685         }
1686         else if (update_path == _T("environment.water.temperature")) {
1687             if (mPriWTP >= 1) {
1688                 double m_wtemp = GetJsonDouble(value);
1689                 if (std::isnan(m_wtemp)) return;
1690 
1691                 m_wtemp = KELVIN2C(m_wtemp);
1692                 if (m_wtemp > -60 && m_wtemp < 200 && !std::isnan(m_wtemp)) {
1693                     SendSentenceToAllInstruments(OCPN_DBP_STC_TMP, m_wtemp, "C");
1694                     mPriWTP = 1;
1695                     mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
1696                 }
1697             }
1698         }
1699         else if (update_path == _T("navigation.courseRhumbline.nextPoint.velocityMadeGood")) {
1700             double m_vmg_kn = GetJsonDouble(value);
1701             if (std::isnan(m_vmg_kn)) return;
1702 
1703             m_vmg_kn = MS2KNOTS(m_vmg_kn);
1704             SendSentenceToAllInstruments(OCPN_DBP_STC_VMG,
1705                         toUsrSpeed_Plugin(m_vmg_kn, g_iDashSpeedUnit),
1706                         getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1707             mVMG_Watchdog = gps_watchdog_timeout_ticks;
1708         }
1709 
1710         else if (update_path == _T("steering.rudderAngle")) { // ->port
1711             double m_rudangle = GetJsonDouble(value);
1712             if (std::isnan(m_rudangle)) return;
1713 
1714             m_rudangle = GEODESIC_RAD2DEG(m_rudangle);
1715             SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle, _T("\u00B0"));
1716             mRSA_Watchdog = gps_watchdog_timeout_ticks;
1717         }
1718         else if (update_path == _T("navigation.gnss.satellites")) { //Number of satellites
1719             if (value.IsInt()) {
1720             double m_SK_SatsInView = (value.AsInt());
1721             mSatsInView = m_SK_SatsInView;
1722             SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_SK_SatsInView, _T(""));
1723             mGPS_Watchdog = gps_watchdog_timeout_ticks;
1724         }
1725         }
1726         //TODO. Add path from GSV after SignK implementation.
1727             /*SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_NMEA0183.Gsv.SatsInView, _T(""));
1728             SendSatInfoToAllInstruments(m_NMEA0183.Gsv.SatsInView,
1729                 m_NMEA0183.Gsv.MessageNumber, m_NMEA0183.Gsv.SatInfo);*/
1730 
1731         else if (update_path == _T("navigation.datetime")) {
1732             if (mPriDateTime >= 1) {
1733                 mPriDateTime = 1;
1734                 wxString s_dt = (value.AsString()); //"2019-12-28T09:26:58.000Z"
1735                 s_dt.Replace('-', wxEmptyString);
1736                 s_dt.Replace(':', wxEmptyString);
1737                 wxString utc_dt = s_dt.BeforeFirst('T'); //Date
1738                 utc_dt.Append(s_dt.AfterFirst('T').Left( 6 )); //time
1739                 mUTCDateTime.ParseFormat(utc_dt.c_str(), _T("%Y%m%d%H%M%S"));
1740                 mUTC_Watchdog = gps_watchdog_timeout_ticks;
1741             }
1742         }
1743         else if (update_path == _T("environment.outside.temperature")) {
1744             if (mPriATMP >= 1) {
1745                 double m_airtemp = GetJsonDouble(value);
1746                 if (std::isnan(m_airtemp)) return;
1747 
1748                 m_airtemp = KELVIN2C(m_airtemp);
1749                 if ( m_airtemp > -60 && m_airtemp < 100 ) {
1750                     SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, m_airtemp, "C");
1751                     mPriATMP = 1;
1752                     mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
1753                 }
1754             }
1755         }
1756         else if (update_path == _T("environment.wind.directionTrue")) { //relative true north
1757             if (mPriWDN >= 2) {
1758                 double m_twdT = GetJsonDouble(value);
1759                 if (std::isnan(m_twdT)) return;
1760 
1761                 m_twdT = GEODESIC_RAD2DEG(m_twdT);
1762                 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdT, _T("\u00B0T"));
1763                 mPriWDN = 2;
1764                 mWDN_Watchdog = gps_watchdog_timeout_ticks;
1765             }
1766         }
1767         else if (update_path == _T("environment.wind.directionMagnetic")) { //relative magn north
1768             if (mPriWDN >= 1) {
1769                 double m_twdM = GetJsonDouble(value);
1770                 if (std::isnan(m_twdM)) return;
1771 
1772                 m_twdM = GEODESIC_RAD2DEG(m_twdM);
1773                 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdM, _T("\u00B0M"));
1774                 mPriWDN = 1;
1775                 mWDN_Watchdog = gps_watchdog_timeout_ticks;
1776             }
1777         }
1778         else if (update_path == _T("navigation.trip.log")) { //m
1779             double m_tlog = GetJsonDouble(value);
1780             if (std::isnan(m_tlog)) return;
1781 
1782             m_tlog = METERS2NM(m_tlog);
1783             SendSentenceToAllInstruments(OCPN_DBP_STC_VLW1,
1784                 toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
1785                 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1786         }
1787         else if (update_path == _T("navigation.log")) { //m
1788             double m_slog = GetJsonDouble(value);
1789             if (std::isnan(m_slog)) return;
1790 
1791             m_slog = METERS2NM(m_slog);
1792             SendSentenceToAllInstruments(OCPN_DBP_STC_VLW2,
1793                 toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
1794                 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1795         }
1796         else if (update_path == _T("environment.outside.pressure")) { //Pa
1797             double m_press = GetJsonDouble(value);
1798             if (std::isnan(m_press)) return;
1799 
1800             m_press = PA2HPA(m_press);
1801             SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, _T("hPa"));
1802             mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
1803         }
1804         else if (update_path == _T("navigation.attitude")) { //rad
1805             if (value["roll"].AsString() != "0") {
1806                 double m_heel = GEODESIC_RAD2DEG(value["roll"].AsDouble());
1807                 wxString h_unit = _T("\u00B0\u003E") + _("Stbd");
1808                 if (m_heel < 0) {
1809                     h_unit = _T("\u00B0\u003C") + _("Port");
1810                     m_heel *= -1;
1811                 }
1812                 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
1813                 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
1814             }
1815             if (value["pitch"].AsString() != "0") {
1816                 double m_pitch = GEODESIC_RAD2DEG(value["pitch"].AsDouble());
1817                 wxString p_unit = _T("\u00B0\u2191") + _("Up");
1818                 if (m_pitch < 0) {
1819                     p_unit = _T("\u00B0\u2193") + _("Down");
1820                     m_pitch *= -1;
1821                 }
1822                 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
1823                 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
1824             }
1825         }
1826     }
1827 }
1828 
SetPositionFix(PlugIn_Position_Fix & pfix)1829 void dashboard_pi::SetPositionFix( PlugIn_Position_Fix &pfix )
1830 {
1831     if( mPriPosition >= 1 ) {
1832         mPriPosition = 1;
1833         SendSentenceToAllInstruments( OCPN_DBP_STC_LAT, pfix.Lat, _T("SDMM") );
1834         SendSentenceToAllInstruments( OCPN_DBP_STC_LON, pfix.Lon, _T("SDMM") );
1835     }
1836     if( mPriCOGSOG >= 1 ) {
1837         double dMagneticCOG;
1838         mPriCOGSOG = 1;
1839         SendSentenceToAllInstruments( OCPN_DBP_STC_SOG, toUsrSpeed_Plugin( mSOGFilter.filter(pfix.Sog), g_iDashSpeedUnit ), getUsrSpeedUnit_Plugin( g_iDashSpeedUnit ) );
1840         SendSentenceToAllInstruments( OCPN_DBP_STC_COG, mCOGFilter.filter(pfix.Cog), _T("\u00B0") );
1841         dMagneticCOG = mCOGFilter.get() - pfix.Var;
1842         if ( dMagneticCOG < 0.0 ) dMagneticCOG = 360.0 + dMagneticCOG;
1843         if ( dMagneticCOG > 360.0 ) dMagneticCOG = dMagneticCOG - 360.0;
1844         SendSentenceToAllInstruments( OCPN_DBP_STC_MCOG, dMagneticCOG , _T("\u00B0M") );
1845     }
1846     if( mPriVar >= 1 ) {
1847         if( !std::isnan( pfix.Var ) ){
1848             mPriVar = 1;
1849             mVar = pfix.Var;
1850             mVar_Watchdog = gps_watchdog_timeout_ticks;
1851 
1852             SendSentenceToAllInstruments( OCPN_DBP_STC_HMV, pfix.Var, _T("\u00B0") );
1853         }
1854     }
1855     if (mPriDateTime >= 6) { //We prefer the GPS datetime
1856         mPriDateTime = 6;
1857         mUTCDateTime.Set(pfix.FixTime);
1858         mUTCDateTime = mUTCDateTime.ToUTC();
1859         mUTC_Watchdog = gps_watchdog_timeout_ticks;
1860     }
1861     mSatsInView = pfix.nSats;
1862 //    SendSentenceToAllInstruments( OCPN_DBP_STC_SAT, mSatsInView, _T("") );
1863 
1864 }
1865 
SetCursorLatLon(double lat,double lon)1866 void dashboard_pi::SetCursorLatLon( double lat, double lon )
1867 {
1868     SendSentenceToAllInstruments( OCPN_DBP_STC_PLA, lat, _T("SDMM") );
1869     SendSentenceToAllInstruments( OCPN_DBP_STC_PLO, lon, _T("SDMM") );
1870 }
1871 
SetPluginMessage(wxString & message_id,wxString & message_body)1872 void dashboard_pi::SetPluginMessage(wxString &message_id, wxString &message_body)
1873 {
1874     if(message_id == _T("WMM_VARIATION_BOAT"))
1875     {
1876 
1877         // construct the JSON root object
1878         wxJSONValue  root;
1879         // construct a JSON parser
1880         wxJSONReader reader;
1881 
1882         // now read the JSON text and store it in the 'root' structure
1883         // check for errors before retreiving values...
1884         int numErrors = reader.Parse( message_body, &root );
1885         if ( numErrors > 0 )  {
1886             //              const wxArrayString& errors = reader.GetErrors();
1887             return;
1888         }
1889 
1890         // get the DECL value from the JSON message
1891         wxString decl = root[_T("Decl")].AsString();
1892         double decl_val;
1893         decl.ToDouble(&decl_val);
1894 
1895 
1896         if( mPriVar >= 5 ) {
1897             mPriVar = 5;
1898             mVar = decl_val;
1899             mVar_Watchdog = gps_watchdog_timeout_ticks;
1900             SendSentenceToAllInstruments( OCPN_DBP_STC_HMV, mVar, _T("\u00B0") );
1901         }
1902     }
1903     else if(message_id == _T("OCPN_CORE_SIGNALK"))
1904     {
1905         ParseSignalK( message_body);
1906     }
1907 
1908 }
1909 
GetToolbarToolCount(void)1910 int dashboard_pi::GetToolbarToolCount( void )
1911 {
1912     return 1;
1913 }
1914 
ShowPreferencesDialog(wxWindow * parent)1915 void dashboard_pi::ShowPreferencesDialog( wxWindow* parent )
1916 {
1917     DashboardPreferencesDialog *dialog = new DashboardPreferencesDialog( parent, wxID_ANY,
1918             m_ArrayOfDashboardWindow );
1919 
1920     dialog->RecalculateSize();
1921 
1922 #ifdef __OCPN__ANDROID__
1923     dialog->GetHandle()->setStyleSheet( qtStyleSheet);
1924 #endif
1925 
1926 #ifdef __OCPN__ANDROID__
1927     wxWindow *ccwin = GetOCPNCanvasWindow();
1928 
1929     if( ccwin ){
1930         int xmax = ccwin->GetSize().GetWidth();
1931         int ymax = ccwin->GetParent()->GetSize().GetHeight();  // This would be the Frame itself
1932         dialog->SetSize( xmax, ymax );
1933         dialog->Layout();
1934 
1935         dialog->Move(0,0);
1936     }
1937 #endif
1938 
1939     if( dialog->ShowModal() == wxID_OK ) {
1940         delete g_pFontTitle;
1941         g_pFontTitle = new wxFont( dialog->m_pFontPickerTitle->GetSelectedFont() );
1942         delete g_pFontData;
1943         g_pFontData = new wxFont( dialog->m_pFontPickerData->GetSelectedFont() );
1944         delete g_pFontLabel;
1945         g_pFontLabel = new wxFont( dialog->m_pFontPickerLabel->GetSelectedFont() );
1946         delete g_pFontSmall;
1947         g_pFontSmall = new wxFont( dialog->m_pFontPickerSmall->GetSelectedFont() );
1948 
1949         // OnClose should handle that for us normally but it doesn't seems to do so
1950         // We must save changes first
1951         dialog->SaveDashboardConfig();
1952         m_ArrayOfDashboardWindow.Clear();
1953         m_ArrayOfDashboardWindow = dialog->m_Config;
1954 
1955         ApplyConfig();
1956         SaveConfig();
1957         SetToolbarItemState( m_toolbar_item_id, GetDashboardWindowShownCount() != 0 );
1958     }
1959     dialog->Destroy();
1960 }
1961 
SetColorScheme(PI_ColorScheme cs)1962 void dashboard_pi::SetColorScheme( PI_ColorScheme cs )
1963 {
1964     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
1965         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
1966         if( dashboard_window ) dashboard_window->SetColorScheme( cs );
1967     }
1968 }
1969 
GetDashboardWindowShownCount()1970 int dashboard_pi::GetDashboardWindowShownCount()
1971 {
1972     int cnt = 0;
1973 
1974     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
1975         DashboardWindow *dashboard_window = m_ArrayOfDashboardWindow.Item( i )->m_pDashboardWindow;
1976         if( dashboard_window ) {
1977             wxAuiPaneInfo &pane = m_pauimgr->GetPane( dashboard_window );
1978             if( pane.IsOk() && pane.IsShown() ) cnt++;
1979         }
1980     }
1981     return cnt;
1982 }
1983 
OnPaneClose(wxAuiManagerEvent & event)1984 void dashboard_pi::OnPaneClose( wxAuiManagerEvent& event )
1985 {
1986     // if name is unique, we should use it
1987     DashboardWindow *dashboard_window = (DashboardWindow *) event.pane->window;
1988     int cnt = 0;
1989     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
1990         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
1991         DashboardWindow *d_w = cont->m_pDashboardWindow;
1992         if( d_w ) {
1993             // we must not count this one because it is being closed
1994             if( dashboard_window != d_w ) {
1995                 wxAuiPaneInfo &pane = m_pauimgr->GetPane( d_w );
1996                 if( pane.IsOk() && pane.IsShown() ) cnt++;
1997             } else {
1998                 cont->m_bIsVisible = false;
1999             }
2000         }
2001     }
2002     SetToolbarItemState( m_toolbar_item_id, cnt != 0 );
2003 
2004     event.Skip();
2005 }
2006 
OnToolbarToolCallback(int id)2007 void dashboard_pi::OnToolbarToolCallback( int id )
2008 {
2009     int cnt = GetDashboardWindowShownCount();
2010 
2011     bool b_anyviz = false;
2012     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
2013         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
2014         if( cont->m_bIsVisible ) {
2015             b_anyviz = true;
2016             break;
2017         }
2018     }
2019 
2020     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
2021         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
2022         DashboardWindow *dashboard_window = cont->m_pDashboardWindow;
2023         if( dashboard_window ) {
2024             wxAuiPaneInfo &pane = m_pauimgr->GetPane( dashboard_window );
2025             if( pane.IsOk() ) {
2026                 bool b_reset_pos = false;
2027 
2028 #ifdef __WXMSW__
2029                 //  Support MultiMonitor setups which an allow negative window positions.
2030                 //  If the requested window title bar does not intersect any installed monitor,
2031                 //  then default to simple primary monitor positioning.
2032                 RECT frame_title_rect;
2033                 frame_title_rect.left = pane.floating_pos.x;
2034                 frame_title_rect.top = pane.floating_pos.y;
2035                 frame_title_rect.right = pane.floating_pos.x + pane.floating_size.x;
2036                 frame_title_rect.bottom = pane.floating_pos.y + 30;
2037 
2038                 if( NULL == MonitorFromRect( &frame_title_rect, MONITOR_DEFAULTTONULL ) ) b_reset_pos =
2039                         true;
2040 #else
2041 
2042                 //    Make sure drag bar (title bar) of window intersects wxClient Area of screen, with a little slop...
2043                 wxRect window_title_rect;// conservative estimate
2044                 window_title_rect.x = pane.floating_pos.x;
2045                 window_title_rect.y = pane.floating_pos.y;
2046                 window_title_rect.width = pane.floating_size.x;
2047                 window_title_rect.height = 30;
2048 
2049                 wxRect ClientRect = wxGetClientDisplayRect();
2050                 ClientRect.Deflate(60, 60);// Prevent the new window from being too close to the edge
2051                 if(!ClientRect.Intersects(window_title_rect))
2052                 b_reset_pos = true;
2053 
2054 #endif
2055 
2056                 if( b_reset_pos ) pane.FloatingPosition( 50, 50 );
2057 
2058                 if( cnt == 0 )
2059                     if( b_anyviz )
2060                         pane.Show( cont->m_bIsVisible );
2061                     else {
2062                        cont->m_bIsVisible = cont->m_bPersVisible;
2063                        pane.Show( cont->m_bIsVisible );
2064                     }
2065                 else
2066                     pane.Show( false );
2067             }
2068 
2069             //  This patch fixes a bug in wxAUIManager
2070             //  FS#548
2071             // Dropping a DashBoard Window right on top on the (supposedly fixed) chart bar window
2072             // causes a resize of the chart bar, and the Dashboard window assumes some of its properties
2073             // The Dashboard window is no longer grabbable...
2074             // Workaround:  detect this case, and force the pane to be on a different Row.
2075             // so that the display is corrected by toggling the dashboard off and back on.
2076             if( ( pane.dock_direction == wxAUI_DOCK_BOTTOM ) && pane.IsDocked() ) pane.Row( 2 );
2077         }
2078     }
2079     // Toggle is handled by the toolbar but we must keep plugin manager b_toggle updated
2080     // to actual status to ensure right status upon toolbar rebuild
2081     SetToolbarItemState( m_toolbar_item_id, GetDashboardWindowShownCount() != 0/*cnt==0*/);
2082     m_pauimgr->Update();
2083 }
2084 
UpdateAuiStatus(void)2085 void dashboard_pi::UpdateAuiStatus( void )
2086 {
2087     //    This method is called after the PlugIn is initialized
2088     //    and the frame has done its initial layout, possibly from a saved wxAuiManager "Perspective"
2089     //    It is a chance for the PlugIn to syncronize itself internally with the state of any Panes that
2090     //    were added to the frame in the PlugIn ctor.
2091 
2092     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
2093         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
2094         wxAuiPaneInfo &pane = m_pauimgr->GetPane( cont->m_pDashboardWindow );
2095         // Initialize visible state as perspective is loaded now
2096         cont->m_bIsVisible = ( pane.IsOk() && pane.IsShown() );
2097 
2098 #ifdef __WXQT__
2099         if(pane.IsShown()){
2100             pane.Show(false);
2101             m_pauimgr->Update();
2102             pane.Show(true);
2103             m_pauimgr->Update();
2104         }
2105 #endif
2106 
2107     }
2108     m_pauimgr->Update();
2109 
2110     //    We use this callback here to keep the context menu selection in sync with the window state
2111 
2112     SetToolbarItemState( m_toolbar_item_id, GetDashboardWindowShownCount() != 0 );
2113 }
2114 
LoadConfig(void)2115 bool dashboard_pi::LoadConfig( void )
2116 {
2117     wxFileConfig *pConf = (wxFileConfig *) m_pconfig;
2118 
2119     if( pConf ) {
2120         pConf->SetPath( _T("/PlugIns/Dashboard") );
2121 
2122         wxString version;
2123         pConf->Read( _T("Version"), &version, wxEmptyString );
2124         wxString config;
2125 
2126         // Set some sensible defaults
2127         wxString TitleFont;
2128         wxString DataFont;
2129         wxString LabelFont;
2130         wxString SmallFont;
2131 
2132 #ifdef __OCPN__ANDROID__
2133         TitleFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
2134         DataFont =  _T("Roboto,16,-1,5,50,0,0,0,0,0");
2135         LabelFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
2136         SmallFont = _T("Roboto,14,-1,5,50,0,0,0,0,0");
2137 #endif
2138 
2139 
2140         pConf->Read( _T("FontTitle"), &config, TitleFont );
2141         LoadFont(&g_pFontTitle, config);
2142 
2143         pConf->Read( _T("FontData"), &config, DataFont );
2144         LoadFont(&g_pFontData, config);
2145 
2146         pConf->Read( _T("FontLabel"), &config, LabelFont );
2147         LoadFont(&g_pFontLabel, config);
2148 
2149         pConf->Read( _T("FontSmall"), &config, SmallFont );
2150         LoadFont(&g_pFontSmall, config);
2151 
2152         pConf->Read( _T("SpeedometerMax"), &g_iDashSpeedMax, 12 );
2153         pConf->Read( _T("COGDamp"), &g_iDashCOGDamp, 0);
2154         pConf->Read( _T("SpeedUnit"), &g_iDashSpeedUnit, 0 );
2155         pConf->Read( _T("SOGDamp"), &g_iDashSOGDamp, 0);
2156         pConf->Read( _T("DepthUnit"), &g_iDashDepthUnit, 3 );
2157         g_iDashDepthUnit = wxMax(g_iDashDepthUnit, 3);
2158 
2159         pConf->Read( _T("DepthOffset"), &g_dDashDBTOffset, 0 );
2160 
2161         pConf->Read( _T("DistanceUnit"), &g_iDashDistanceUnit, 0 );
2162         pConf->Read( _T("WindSpeedUnit"), &g_iDashWindSpeedUnit, 0 );
2163         pConf->Read(_T("UseSignKtruewind"), &g_iDashUsetruewinddata, 0);
2164 
2165         pConf->Read( _T("UTCOffset"), &g_iUTCOffset, 0 );
2166 
2167         int d_cnt;
2168         pConf->Read( _T("DashboardCount"), &d_cnt, -1 );
2169         // TODO: Memory leak? We should destroy everything first
2170         m_ArrayOfDashboardWindow.Clear();
2171         if( version.IsEmpty() && d_cnt == -1 ) {
2172             m_config_version = 1;
2173             // Let's load version 1 or default settings.
2174             int i_cnt;
2175             pConf->Read( _T("InstrumentCount"), &i_cnt, -1 );
2176             wxArrayInt ar;
2177             if( i_cnt != -1 ) {
2178                 for( int i = 0; i < i_cnt; i++ ) {
2179                     int id;
2180                     pConf->Read( wxString::Format( _T("Instrument%d"), i + 1 ), &id, -1 );
2181                     if( id != -1 ) ar.Add( id );
2182                 }
2183             } else {
2184                 // This is the default instrument list
2185 #ifndef __OCPN__ANDROID__
2186                 ar.Add( ID_DBP_I_POS );
2187                 ar.Add( ID_DBP_D_COG );
2188                 ar.Add( ID_DBP_D_GPS );
2189 #else
2190                 ar.Add( ID_DBP_I_POS );
2191                 ar.Add( ID_DBP_D_COG );
2192                 ar.Add( ID_DBP_I_SOG );
2193 
2194 #endif
2195             }
2196 
2197             DashboardWindowContainer *cont = new DashboardWindowContainer( NULL, MakeName(), _("Dashboard"), _T("V"), ar );
2198             cont->m_bPersVisible = true;
2199             m_ArrayOfDashboardWindow.Add(cont);
2200 
2201         } else {
2202             // Version 2
2203             m_config_version = 2;
2204             bool b_onePersisted = false;
2205             for( int i = 0; i < d_cnt; i++ ) {
2206                 pConf->SetPath( wxString::Format( _T("/PlugIns/Dashboard/Dashboard%d"), i + 1 ) );
2207                 wxString name;
2208                 pConf->Read( _T("Name"), &name, MakeName() );
2209                 wxString caption;
2210                 pConf->Read( _T("Caption"), &caption, _("Dashboard") );
2211                 wxString orient;
2212                 pConf->Read( _T("Orientation"), &orient, _T("V") );
2213                 int i_cnt;
2214                 pConf->Read( _T("InstrumentCount"), &i_cnt, -1 );
2215                 bool b_persist;
2216                 pConf->Read( _T("Persistence"), &b_persist, 1 );
2217 
2218                 wxArrayInt ar;
2219                 for( int i = 0; i < i_cnt; i++ ) {
2220                     int id;
2221                     pConf->Read( wxString::Format( _T("Instrument%d"), i + 1 ), &id, -1 );
2222                     if( id != -1 ) ar.Add( id );
2223                 }
2224 // TODO: Do not add if GetCount == 0
2225 
2226                 DashboardWindowContainer *cont = new DashboardWindowContainer( NULL, name, caption, orient, ar );
2227                 cont->m_bPersVisible = b_persist;
2228 
2229                 if(b_persist)
2230                     b_onePersisted = true;
2231 
2232                 m_ArrayOfDashboardWindow.Add(cont);
2233 
2234             }
2235 
2236             // Make sure at least one dashboard is scheduled to be visible
2237             if( m_ArrayOfDashboardWindow.Count() && !b_onePersisted){
2238                 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(0);
2239                 if(cont)
2240                     cont->m_bPersVisible = true;
2241             }
2242 
2243         }
2244 
2245         return true;
2246     } else
2247         return false;
2248 }
2249 
LoadFont(wxFont ** target,wxString native_info)2250 void dashboard_pi::LoadFont(wxFont **target, wxString native_info)
2251 {
2252     if( !native_info.IsEmpty() ){
2253 #ifdef __OCPN__ANDROID__
2254         wxFont *nf = new wxFont( native_info );
2255         *target = nf;
2256 #else
2257         (*target)->SetNativeFontInfo( native_info );
2258 #endif
2259     }
2260 }
2261 
2262 
SaveConfig(void)2263 bool dashboard_pi::SaveConfig( void )
2264 {
2265     wxFileConfig *pConf = (wxFileConfig *) m_pconfig;
2266 
2267     if( pConf ) {
2268         pConf->SetPath( _T("/PlugIns/Dashboard") );
2269         pConf->Write( _T("Version"), _T("2") );
2270         pConf->Write( _T("FontTitle"), g_pFontTitle->GetNativeFontInfoDesc() );
2271         pConf->Write( _T("FontData"), g_pFontData->GetNativeFontInfoDesc() );
2272         pConf->Write( _T("FontLabel"), g_pFontLabel->GetNativeFontInfoDesc() );
2273         pConf->Write( _T("FontSmall"), g_pFontSmall->GetNativeFontInfoDesc() );
2274 
2275         pConf->Write( _T("SpeedometerMax"), g_iDashSpeedMax );
2276         pConf->Write( _T("COGDamp"), g_iDashCOGDamp );
2277         pConf->Write( _T("SpeedUnit"), g_iDashSpeedUnit );
2278         pConf->Write( _T("SOGDamp"), g_iDashSOGDamp );
2279         pConf->Write( _T("DepthUnit"), g_iDashDepthUnit );
2280         pConf->Write( _T("DepthOffset"), g_dDashDBTOffset );
2281         pConf->Write( _T("DistanceUnit"), g_iDashDistanceUnit );
2282         pConf->Write( _T("WindSpeedUnit"), g_iDashWindSpeedUnit );
2283         pConf->Write( _T("UTCOffset"), g_iUTCOffset );
2284         pConf->Write(_T("UseSignKtruewind"), g_iDashUsetruewinddata);
2285 
2286         pConf->Write( _T("DashboardCount" ), (int) m_ArrayOfDashboardWindow.GetCount() );
2287         for( unsigned int i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
2288             DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
2289             pConf->SetPath( wxString::Format( _T("/PlugIns/Dashboard/Dashboard%d"), i + 1 ) );
2290             pConf->Write( _T("Name"), cont->m_sName );
2291             pConf->Write( _T("Caption"), cont->m_sCaption );
2292             pConf->Write( _T("Orientation"), cont->m_sOrientation );
2293             pConf->Write( _T("Persistence"), cont->m_bPersVisible );
2294 
2295             pConf->Write( _T("InstrumentCount"), (int) cont->m_aInstrumentList.GetCount() );
2296             for( unsigned int j = 0; j < cont->m_aInstrumentList.GetCount(); j++ )
2297                 pConf->Write( wxString::Format( _T("Instrument%d"), j + 1 ),
2298                         cont->m_aInstrumentList.Item( j ) );
2299         }
2300 
2301         return true;
2302     } else
2303         return false;
2304 }
2305 
ApplyConfig(void)2306 void dashboard_pi::ApplyConfig( void )
2307 {
2308     // Reverse order to handle deletes
2309     for( size_t i = m_ArrayOfDashboardWindow.GetCount(); i > 0; i-- ) {
2310         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i - 1 );
2311         int orient = ( cont->m_sOrientation == _T("V") ? wxVERTICAL : wxHORIZONTAL );
2312         if( cont->m_bIsDeleted ) {
2313             if( cont->m_pDashboardWindow ) {
2314                 m_pauimgr->DetachPane( cont->m_pDashboardWindow );
2315                 cont->m_pDashboardWindow->Close();
2316                 cont->m_pDashboardWindow->Destroy();
2317                 cont->m_pDashboardWindow = NULL;
2318             }
2319             m_ArrayOfDashboardWindow.Remove( cont );
2320             delete cont;
2321 
2322         } else if( !cont->m_pDashboardWindow ) {
2323             // A new dashboard is created
2324             cont->m_pDashboardWindow = new DashboardWindow( GetOCPNCanvasWindow(), wxID_ANY,
2325                     m_pauimgr, this, orient, cont );
2326             cont->m_pDashboardWindow->SetInstrumentList( cont->m_aInstrumentList );
2327             bool vertical = orient == wxVERTICAL;
2328             wxSize sz = cont->m_pDashboardWindow->GetMinSize();
2329 // Mac has a little trouble with initial Layout() sizing...
2330 #ifdef __WXOSX__
2331             if(sz.x == 0)
2332                 sz.IncTo( wxSize( 160, 388) );
2333 #endif
2334                 wxAuiPaneInfo p = wxAuiPaneInfo().Name( cont->m_sName ).Caption( cont->m_sCaption ).CaptionVisible( false ).TopDockable(
2335                     !vertical ).BottomDockable( !vertical ).LeftDockable( vertical ).RightDockable( vertical ).MinSize(
2336                         sz ).BestSize( sz ).FloatingSize( sz ).FloatingPosition( 100, 100 ).Float().Show( cont->m_bIsVisible ).Gripper(false) ;
2337 
2338             m_pauimgr->AddPane( cont->m_pDashboardWindow, p);
2339                 //wxAuiPaneInfo().Name( cont->m_sName ).Caption( cont->m_sCaption ).CaptionVisible( false ).TopDockable(
2340                // !vertical ).BottomDockable( !vertical ).LeftDockable( vertical ).RightDockable( vertical ).MinSize(
2341                // sz ).BestSize( sz ).FloatingSize( sz ).FloatingPosition( 100, 100 ).Float().Show( cont->m_bIsVisible ) );
2342 
2343             #ifdef __OCPN__ANDROID__
2344             wxAuiPaneInfo& pane = m_pauimgr->GetPane( cont->m_pDashboardWindow );
2345             pane.Dockable( false );
2346 
2347             #endif
2348 
2349         } else {
2350             wxAuiPaneInfo& pane = m_pauimgr->GetPane( cont->m_pDashboardWindow );
2351             pane.Caption( cont->m_sCaption ).Show( cont->m_bIsVisible );
2352             if( !cont->m_pDashboardWindow->isInstrumentListEqual( cont->m_aInstrumentList ) ) {
2353                 cont->m_pDashboardWindow->SetInstrumentList( cont->m_aInstrumentList );
2354                 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
2355                 pane.MinSize( sz ).BestSize( sz ).FloatingSize( sz );
2356             }
2357             if( cont->m_pDashboardWindow->GetSizerOrientation() != orient ) {
2358                 cont->m_pDashboardWindow->ChangePaneOrientation( orient, false );
2359             }
2360         }
2361     }
2362     m_pauimgr->Update();
2363     mSOGFilter.setFC(g_iDashSOGDamp ? 1.0 / (2.0*g_iDashSOGDamp) : 0.0);
2364     mCOGFilter.setFC(g_iDashCOGDamp ? 1.0 / (2.0*g_iDashCOGDamp) : 0.0);
2365     mCOGFilter.setType(IIRFILTER_TYPE_DEG);
2366 }
2367 
PopulateContextMenu(wxMenu * menu)2368 void dashboard_pi::PopulateContextMenu( wxMenu* menu )
2369 {
2370     for( size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++ ) {
2371         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( i );
2372         wxMenuItem* item = menu->AppendCheckItem( i+1, cont->m_sCaption );
2373         item->Check( cont->m_bIsVisible );
2374     }
2375 }
2376 
ShowDashboard(size_t id,bool visible)2377 void dashboard_pi::ShowDashboard( size_t id, bool visible )
2378 {
2379     if ( id < m_ArrayOfDashboardWindow.GetCount() ) {
2380         DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item( id );
2381         m_pauimgr->GetPane( cont->m_pDashboardWindow ).Show( visible );
2382         cont->m_bIsVisible = visible;
2383         cont->m_bPersVisible = visible;
2384         m_pauimgr->Update();
2385     }
2386 }
2387 
2388 /* DashboardPreferencesDialog
2389  *
2390  */
2391 
DashboardPreferencesDialog(wxWindow * parent,wxWindowID id,wxArrayOfDashboard config)2392 DashboardPreferencesDialog::DashboardPreferencesDialog( wxWindow *parent, wxWindowID id,
2393         wxArrayOfDashboard config ) :
2394         wxDialog( parent, id, _("Dashboard preferences"), wxDefaultPosition, wxDefaultSize,
2395                 wxDEFAULT_DIALOG_STYLE )
2396 {
2397 
2398 #ifdef __WXQT__
2399     wxFont *pF = OCPNGetFont(_T("Dialog"), 0);
2400     SetFont( *pF );
2401 #endif
2402 
2403     wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") + wxFileName::GetPathSeparator() +
2404     _T("dashboard_pi") + wxFileName::GetPathSeparator()
2405     + _T("data") + wxFileName::GetPathSeparator();
2406 
2407     Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( DashboardPreferencesDialog::OnCloseDialog ),
2408             NULL, this );
2409 
2410     // Copy original config
2411     m_Config = wxArrayOfDashboard( config );
2412     //      Build Dashboard Page for Toolbox
2413     int border_size = 2;
2414 
2415     wxBoxSizer* itemBoxSizerMainPanel = new wxBoxSizer( wxVERTICAL );
2416     SetSizer( itemBoxSizerMainPanel );
2417 
2418     wxNotebook *itemNotebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
2419             wxNB_TOP );
2420     itemBoxSizerMainPanel->Add( itemNotebook, 1, wxALL | wxEXPAND, border_size );
2421 
2422     wxPanel *itemPanelNotebook01 = new wxPanel( itemNotebook, wxID_ANY, wxDefaultPosition,
2423             wxDefaultSize, wxTAB_TRAVERSAL );
2424     wxFlexGridSizer *itemFlexGridSizer01 = new wxFlexGridSizer( 2 );
2425     itemFlexGridSizer01->AddGrowableCol( 1 );
2426     itemPanelNotebook01->SetSizer( itemFlexGridSizer01 );
2427     itemNotebook->AddPage( itemPanelNotebook01, _("Dashboard") );
2428 
2429     wxBoxSizer *itemBoxSizer01 = new wxBoxSizer( wxVERTICAL );
2430     itemFlexGridSizer01->Add( itemBoxSizer01, 1, wxEXPAND | wxTOP | wxLEFT, border_size );
2431 
2432     // Scale the images in the dashboard list control
2433     int imageRefSize = 32 * GetOCPNGUIToolScaleFactor_PlugIn();
2434 
2435     wxImageList *imglist1 = new wxImageList( imageRefSize, imageRefSize, true, 1 );
2436 
2437     wxBitmap bmDashBoard;
2438 #ifdef ocpnUSE_SVG
2439     wxString filename = shareLocn + _T("Dashboard.svg");
2440     bmDashBoard = GetBitmapFromSVGFile(filename, imageRefSize, imageRefSize);
2441 #else
2442     wxImage dash1 = wxBitmap( *_img_dashboard_pi ).ConvertToImage();
2443     wxImage dash1s = dash1.Scale(imageRefSize, imageRefSize, wxIMAGE_QUALITY_HIGH);
2444     bmDashBoard = wxBitmap(dash1s);
2445 #endif
2446 
2447     imglist1->Add( bmDashBoard );
2448 
2449     m_pListCtrlDashboards = new wxListCtrl( itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
2450                                             wxSize( imageRefSize * 3/2, 200 ), wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL );
2451 
2452 
2453     m_pListCtrlDashboards->AssignImageList( imglist1, wxIMAGE_LIST_SMALL );
2454     m_pListCtrlDashboards->InsertColumn( 0, _T("") );
2455     m_pListCtrlDashboards->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
2456             wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL, this );
2457     m_pListCtrlDashboards->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
2458             wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL, this );
2459     itemBoxSizer01->Add( m_pListCtrlDashboards, 1, wxEXPAND, 0 );
2460 
2461     wxBoxSizer *itemBoxSizer02 = new wxBoxSizer( wxHORIZONTAL );
2462     itemBoxSizer01->Add( itemBoxSizer02 );
2463 
2464     wxBitmap bmPlus, bmMinus;
2465 #ifdef ocpnUSE_SVG
2466     bmPlus = GetBitmapFromSVGFile(shareLocn + _T("plus.svg"), imageRefSize/2, imageRefSize/2);
2467     bmMinus = GetBitmapFromSVGFile(shareLocn + _T("minus.svg"), imageRefSize/2, imageRefSize/2);
2468 #else
2469     wxImage plus1 = wxBitmap( *_img_plus ).ConvertToImage();
2470     wxImage plus1s = plus1.Scale(imageRefSize/2, imageRefSize/2, wxIMAGE_QUALITY_HIGH);
2471     bmPlus = wxBitmap(plus1s);
2472 
2473     wxImage minus1 = wxBitmap( *_img_minus ).ConvertToImage();
2474     wxImage minus1s = minus1.Scale(imageRefSize/2, imageRefSize/2, wxIMAGE_QUALITY_HIGH);
2475     bmMinus = wxBitmap(minus1s);
2476 #endif
2477 
2478     m_pButtonAddDashboard = new wxBitmapButton( itemPanelNotebook01, wxID_ANY, bmPlus,
2479             wxDefaultPosition, wxDefaultSize );
2480     itemBoxSizer02->Add( m_pButtonAddDashboard, 0, wxALIGN_CENTER, 2 );
2481     m_pButtonAddDashboard->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2482             wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardAdd), NULL, this );
2483 
2484     m_pButtonDeleteDashboard = new wxBitmapButton( itemPanelNotebook01, wxID_ANY, bmMinus,
2485             wxDefaultPosition, wxDefaultSize );
2486     itemBoxSizer02->Add( m_pButtonDeleteDashboard, 0, wxALIGN_CENTER, 2 );
2487     m_pButtonDeleteDashboard->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2488             wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardDelete), NULL, this );
2489 
2490     m_pPanelDashboard = new wxPanel( itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
2491             wxDefaultSize, wxBORDER_SUNKEN );
2492     itemFlexGridSizer01->Add( m_pPanelDashboard, 1, wxEXPAND | wxTOP | wxRIGHT, border_size );
2493 
2494     wxBoxSizer* itemBoxSizer03 = new wxBoxSizer( wxVERTICAL );
2495     m_pPanelDashboard->SetSizer( itemBoxSizer03 );
2496 
2497     wxStaticBox* itemStaticBox02 = new wxStaticBox( m_pPanelDashboard, wxID_ANY, _("Dashboard") );
2498     wxStaticBoxSizer* itemStaticBoxSizer02 = new wxStaticBoxSizer( itemStaticBox02, wxHORIZONTAL );
2499     itemBoxSizer03->Add( itemStaticBoxSizer02, 0, wxEXPAND | wxALL, border_size );
2500     wxFlexGridSizer *itemFlexGridSizer = new wxFlexGridSizer( 2 );
2501     itemFlexGridSizer->AddGrowableCol( 1 );
2502     itemStaticBoxSizer02->Add( itemFlexGridSizer, 1, wxEXPAND | wxALL, 0 );
2503 
2504     m_pCheckBoxIsVisible = new wxCheckBox( m_pPanelDashboard, wxID_ANY, _("show this dashboard"),
2505             wxDefaultPosition, wxDefaultSize, 0 );
2506     itemFlexGridSizer->Add( m_pCheckBoxIsVisible, 0, wxEXPAND | wxALL, border_size );
2507     wxStaticText *itemDummy01 = new wxStaticText( m_pPanelDashboard, wxID_ANY, _T("") );
2508     itemFlexGridSizer->Add( itemDummy01, 0, wxEXPAND | wxALL, border_size );
2509 
2510     wxStaticText* itemStaticText01 = new wxStaticText( m_pPanelDashboard, wxID_ANY, _("Caption:"),
2511             wxDefaultPosition, wxDefaultSize, 0 );
2512     itemFlexGridSizer->Add( itemStaticText01, 0, wxEXPAND | wxALL, border_size );
2513     m_pTextCtrlCaption = new wxTextCtrl( m_pPanelDashboard, wxID_ANY, _T(""), wxDefaultPosition,
2514                                          wxSize( 220, -1 ) );
2515     itemFlexGridSizer->Add( m_pTextCtrlCaption, 0, wxALIGN_RIGHT | wxALL, border_size );
2516 
2517 #ifdef __OCPN__ANDROID__
2518     itemStaticText01->Hide();
2519     m_pTextCtrlCaption->Hide();
2520 #endif
2521 
2522     wxStaticText* itemStaticText02 = new wxStaticText( m_pPanelDashboard, wxID_ANY,
2523             _("Orientation:"), wxDefaultPosition, wxDefaultSize, 0 );
2524     itemFlexGridSizer->Add( itemStaticText02, 0, wxEXPAND | wxALL, border_size );
2525     m_pChoiceOrientation = new wxChoice( m_pPanelDashboard, wxID_ANY, wxDefaultPosition,
2526             wxSize( 220, -1 ) );
2527     m_pChoiceOrientation->Append( _("Vertical") );
2528     m_pChoiceOrientation->Append( _("Horizontal") );
2529     itemFlexGridSizer->Add( m_pChoiceOrientation, 0, wxALIGN_RIGHT | wxALL, border_size );
2530 
2531     int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
2532 
2533     wxImageList *imglist = new wxImageList( instImageRefSize, instImageRefSize, true, 2 );
2534 
2535     wxBitmap bmDial, bmInst;
2536 #ifdef ocpnUSE_SVG
2537     bmDial = GetBitmapFromSVGFile(shareLocn + _T("dial.svg"), instImageRefSize, instImageRefSize);
2538     bmInst = GetBitmapFromSVGFile(shareLocn + _T("instrument.svg"), instImageRefSize, instImageRefSize);
2539 #else
2540     wxImage dial1 = wxBitmap( *_img_dial ).ConvertToImage();
2541     wxImage dial1s = dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
2542     bmDial = wxBitmap(dial1);
2543 
2544     wxImage inst1 = wxBitmap( *_img_instrument ).ConvertToImage();
2545     wxImage inst1s = inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
2546     bmInst = wxBitmap(inst1s);
2547 #endif
2548 
2549     imglist->Add( bmInst );
2550     imglist->Add( bmDial );
2551 
2552     wxStaticBox* itemStaticBox03 = new wxStaticBox( m_pPanelDashboard, wxID_ANY, _("Instruments") );
2553     wxStaticBoxSizer* itemStaticBoxSizer03 = new wxStaticBoxSizer( itemStaticBox03, wxHORIZONTAL );
2554     itemBoxSizer03->Add( itemStaticBoxSizer03, 1, wxEXPAND | wxALL, border_size );
2555 
2556     int vsize = 200;
2557 
2558     #ifdef __OCPN__ANDROID__
2559     int dw, dh;
2560     wxDisplaySize(&dw, &dh);
2561     vsize = dh * 50 / 100;
2562     #endif
2563 
2564     m_pListCtrlInstruments = new wxListCtrl( m_pPanelDashboard, wxID_ANY, wxDefaultPosition,
2565             wxSize( -1, vsize ), wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL );
2566 
2567     itemStaticBoxSizer03->Add( m_pListCtrlInstruments, 1, wxEXPAND | wxALL, border_size );
2568     m_pListCtrlInstruments->AssignImageList( imglist, wxIMAGE_LIST_SMALL );
2569     m_pListCtrlInstruments->InsertColumn( 0, _("Instruments") );
2570     m_pListCtrlInstruments->Connect( wxEVT_COMMAND_LIST_ITEM_SELECTED,
2571             wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected), NULL, this );
2572     m_pListCtrlInstruments->Connect( wxEVT_COMMAND_LIST_ITEM_DESELECTED,
2573             wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected), NULL, this );
2574 
2575     wxBoxSizer* itemBoxSizer04 = new wxBoxSizer( wxVERTICAL );
2576     itemStaticBoxSizer03->Add( itemBoxSizer04, 0, wxALIGN_TOP | wxALL, border_size );
2577     m_pButtonAdd = new wxButton( m_pPanelDashboard, wxID_ANY, _("Add"), wxDefaultPosition,
2578             wxSize( 20, -1 ) );
2579     itemBoxSizer04->Add( m_pButtonAdd, 0, wxEXPAND | wxALL, border_size );
2580     m_pButtonAdd->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2581             wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentAdd), NULL, this );
2582 
2583 /* TODO  Instrument Properties
2584     m_pButtonEdit = new wxButton( m_pPanelDashboard, wxID_ANY, _("Edit"), wxDefaultPosition,
2585             wxDefaultSize );
2586     itemBoxSizer04->Add( m_pButtonEdit, 0, wxEXPAND | wxALL, border_size );
2587     m_pButtonEdit->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2588             wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentEdit), NULL, this );
2589 */
2590     m_pButtonDelete = new wxButton( m_pPanelDashboard, wxID_ANY, _("Delete"), wxDefaultPosition,
2591             wxSize( 20, -1 ) );
2592     itemBoxSizer04->Add( m_pButtonDelete, 0, wxEXPAND | wxALL, border_size );
2593     m_pButtonDelete->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2594             wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDelete), NULL, this );
2595     itemBoxSizer04->AddSpacer( 10 );
2596     m_pButtonUp = new wxButton( m_pPanelDashboard, wxID_ANY, _("Up"), wxDefaultPosition,
2597             wxDefaultSize );
2598     itemBoxSizer04->Add( m_pButtonUp, 0, wxEXPAND | wxALL, border_size );
2599     m_pButtonUp->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2600             wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentUp), NULL, this );
2601     m_pButtonDown = new wxButton( m_pPanelDashboard, wxID_ANY, _("Down"), wxDefaultPosition,
2602             wxDefaultSize );
2603     itemBoxSizer04->Add( m_pButtonDown, 0, wxEXPAND | wxALL, border_size );
2604     m_pButtonDown->Connect( wxEVT_COMMAND_BUTTON_CLICKED,
2605             wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDown), NULL, this );
2606 
2607     wxPanel *itemPanelNotebook02 = new wxPanel( itemNotebook, wxID_ANY, wxDefaultPosition,
2608             wxDefaultSize, wxTAB_TRAVERSAL );
2609     wxBoxSizer* itemBoxSizer05 = new wxBoxSizer( wxVERTICAL );
2610     itemPanelNotebook02->SetSizer( itemBoxSizer05 );
2611     itemNotebook->AddPage( itemPanelNotebook02, _("Appearance") );
2612 
2613     wxStaticBox* itemStaticBox01 = new wxStaticBox( itemPanelNotebook02, wxID_ANY, _("Fonts") );
2614     wxStaticBoxSizer* itemStaticBoxSizer01 = new wxStaticBoxSizer( itemStaticBox01, wxHORIZONTAL );
2615     itemBoxSizer05->Add( itemStaticBoxSizer01, 0, wxEXPAND | wxALL, border_size );
2616     wxFlexGridSizer *itemFlexGridSizer03 = new wxFlexGridSizer( 2 );
2617     itemFlexGridSizer03->AddGrowableCol( 1 );
2618     itemStaticBoxSizer01->Add( itemFlexGridSizer03, 1, wxEXPAND | wxALL, 0 );
2619 
2620     wxStaticText* itemStaticText04 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Title:"),
2621             wxDefaultPosition, wxDefaultSize, 0 );
2622     itemFlexGridSizer03->Add( itemStaticText04, 0, wxEXPAND | wxALL, border_size );
2623     m_pFontPickerTitle = new wxFontPickerCtrl( itemPanelNotebook02, wxID_ANY, *g_pFontTitle,
2624             wxDefaultPosition, wxDefaultSize );
2625     itemFlexGridSizer03->Add( m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0 );
2626 
2627     wxStaticText* itemStaticText05 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Data:"),
2628             wxDefaultPosition, wxDefaultSize, 0 );
2629     itemFlexGridSizer03->Add( itemStaticText05, 0, wxEXPAND | wxALL, border_size );
2630     m_pFontPickerData = new wxFontPickerCtrl( itemPanelNotebook02, wxID_ANY, *g_pFontData,
2631             wxDefaultPosition, wxDefaultSize );
2632     itemFlexGridSizer03->Add( m_pFontPickerData, 0, wxALIGN_RIGHT | wxALL, 0 );
2633 
2634     wxStaticText* itemStaticText06 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Label:"),
2635             wxDefaultPosition, wxDefaultSize, 0 );
2636     itemFlexGridSizer03->Add( itemStaticText06, 0, wxEXPAND | wxALL, border_size );
2637     m_pFontPickerLabel = new wxFontPickerCtrl( itemPanelNotebook02, wxID_ANY, *g_pFontLabel,
2638             wxDefaultPosition, wxDefaultSize );
2639     itemFlexGridSizer03->Add( m_pFontPickerLabel, 0, wxALIGN_RIGHT | wxALL, 0 );
2640 
2641     wxStaticText* itemStaticText07 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Small:"),
2642             wxDefaultPosition, wxDefaultSize, 0 );
2643     itemFlexGridSizer03->Add( itemStaticText07, 0, wxEXPAND | wxALL, border_size );
2644     m_pFontPickerSmall = new wxFontPickerCtrl( itemPanelNotebook02, wxID_ANY, *g_pFontSmall,
2645             wxDefaultPosition, wxDefaultSize );
2646     itemFlexGridSizer03->Add( m_pFontPickerSmall, 0, wxALIGN_RIGHT | wxALL, 0 );
2647 //      wxColourPickerCtrl
2648 
2649     wxStaticBox* itemStaticBox04 = new wxStaticBox( itemPanelNotebook02, wxID_ANY, _("Units, Ranges, Formats") );
2650     wxStaticBoxSizer* itemStaticBoxSizer04 = new wxStaticBoxSizer( itemStaticBox04, wxHORIZONTAL );
2651     itemBoxSizer05->Add( itemStaticBoxSizer04, 0, wxEXPAND | wxALL, border_size );
2652     wxFlexGridSizer *itemFlexGridSizer04 = new wxFlexGridSizer( 2 );
2653     itemFlexGridSizer04->AddGrowableCol( 1 );
2654     itemStaticBoxSizer04->Add( itemFlexGridSizer04, 1, wxEXPAND | wxALL, 0 );
2655     wxStaticText* itemStaticText08 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Speedometer max value:"),
2656             wxDefaultPosition, wxDefaultSize, 0 );
2657     itemFlexGridSizer04->Add( itemStaticText08, 0, wxEXPAND | wxALL, border_size );
2658     m_pSpinSpeedMax = new wxSpinCtrl( itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 10, 100, g_iDashSpeedMax );
2659     itemFlexGridSizer04->Add( m_pSpinSpeedMax, 0, wxALIGN_RIGHT | wxALL, 0 );
2660 
2661     wxStaticText* itemStaticText10 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Speed Over Ground Damping Factor:"),
2662         wxDefaultPosition, wxDefaultSize, 0);
2663     itemFlexGridSizer04->Add(itemStaticText10, 0, wxEXPAND | wxALL, border_size);
2664     m_pSpinSOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, g_iDashSOGDamp);
2665     itemFlexGridSizer04->Add(m_pSpinSOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
2666 
2667     wxStaticText* itemStaticText11 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("COG Damping Factor:"),
2668         wxDefaultPosition, wxDefaultSize, 0);
2669     itemFlexGridSizer04->Add(itemStaticText11, 0, wxEXPAND | wxALL, border_size);
2670     m_pSpinCOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, g_iDashCOGDamp);
2671     itemFlexGridSizer04->Add(m_pSpinCOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
2672 
2673     wxStaticText* itemStaticText12 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _( "Local Time Offset From UTC:" ),
2674         wxDefaultPosition, wxDefaultSize, 0 );
2675     itemFlexGridSizer04->Add( itemStaticText12, 0, wxEXPAND | wxALL, border_size );
2676     wxString m_UTCOffsetChoices[] = {
2677         _T( "-12:00" ), _T( "-11:30" ), _T( "-11:00" ), _T( "-10:30" ), _T( "-10:00" ), _T( "-09:30" ),
2678         _T( "-09:00" ), _T( "-08:30" ), _T( "-08:00" ), _T( "-07:30" ), _T( "-07:00" ), _T( "-06:30" ),
2679         _T( "-06:00" ), _T( "-05:30" ), _T( "-05:00" ), _T( "-04:30" ), _T( "-04:00" ), _T( "-03:30" ),
2680         _T( "-03:00" ), _T( "-02:30" ), _T( "-02:00" ), _T( "-01:30" ), _T( "-01:00" ), _T( "-00:30" ),
2681         _T( " 00:00" ), _T( " 00:30" ), _T( " 01:00" ), _T( " 01:30" ), _T( " 02:00" ), _T( " 02:30" ),
2682         _T( " 03:00" ), _T( " 03:30" ), _T( " 04:00" ), _T( " 04:30" ), _T( " 05:00" ), _T( " 05:30" ),
2683         _T( " 06:00" ), _T( " 06:30" ), _T( " 07:00" ), _T( " 07:30" ), _T( " 08:00" ), _T( " 08:30" ),
2684         _T( " 09:00" ), _T( " 09:30" ), _T( " 10:00" ), _T( " 10:30" ), _T( " 11:00" ), _T( " 11:30" ),
2685         _T( " 12:00" )
2686     };
2687     int m_UTCOffsetNChoices = sizeof( m_UTCOffsetChoices ) / sizeof( wxString );
2688     m_pChoiceUTCOffset = new wxChoice( itemPanelNotebook02, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_UTCOffsetNChoices, m_UTCOffsetChoices, 0 );
2689     m_pChoiceUTCOffset->SetSelection( g_iUTCOffset + 24 );
2690     itemFlexGridSizer04->Add( m_pChoiceUTCOffset, 0, wxALIGN_RIGHT | wxALL, 0 );
2691 
2692     wxStaticText* itemStaticText09 = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Boat speed units:"),
2693             wxDefaultPosition, wxDefaultSize, 0 );
2694     itemFlexGridSizer04->Add( itemStaticText09, 0, wxEXPAND | wxALL, border_size );
2695     wxString m_SpeedUnitChoices[] = { _("Honor OpenCPN settings"), _("Kts"), _("mph"), _("km/h"), _("m/s") };
2696     int m_SpeedUnitNChoices = sizeof( m_SpeedUnitChoices ) / sizeof( wxString );
2697     m_pChoiceSpeedUnit = new wxChoice( itemPanelNotebook02, wxID_ANY, wxDefaultPosition, wxSize(220, -1), m_SpeedUnitNChoices, m_SpeedUnitChoices, 0 );
2698     m_pChoiceSpeedUnit->SetSelection( g_iDashSpeedUnit + 1 );
2699     itemFlexGridSizer04->Add( m_pChoiceSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0 );
2700 
2701     wxStaticText* itemStaticTextDepthU = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Depth units:"),
2702             wxDefaultPosition, wxDefaultSize, 0 );
2703     itemFlexGridSizer04->Add( itemStaticTextDepthU, 0, wxEXPAND | wxALL, border_size );
2704     wxString m_DepthUnitChoices[] = { _("Meters"), _("Feet"), _("Fathoms"), _("Inches"), _("Centimeters") };
2705     int m_DepthUnitNChoices = sizeof( m_DepthUnitChoices ) / sizeof( wxString );
2706     m_pChoiceDepthUnit = new wxChoice( itemPanelNotebook02, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_DepthUnitNChoices, m_DepthUnitChoices, 0 );
2707     m_pChoiceDepthUnit->SetSelection( g_iDashDepthUnit - 3);
2708     itemFlexGridSizer04->Add( m_pChoiceDepthUnit, 0, wxALIGN_RIGHT | wxALL, 0 );
2709     wxString dMess = wxString::Format(_("Depth Offset (%s):"),m_DepthUnitChoices[g_iDashDepthUnit-3]);
2710     wxStaticText* itemStaticDepthO = new wxStaticText(itemPanelNotebook02, wxID_ANY, dMess,
2711         wxDefaultPosition, wxDefaultSize, 0);
2712     double DepthOffset;
2713     switch (g_iDashDepthUnit - 3) {
2714     case 1:
2715         DepthOffset = g_dDashDBTOffset * 3.2808399;
2716         break;
2717     case 2:
2718         DepthOffset = g_dDashDBTOffset * 0.54680665;
2719         break;
2720     case 3:
2721         DepthOffset = g_dDashDBTOffset * 39.3700787;
2722         break;
2723     case 4:
2724         DepthOffset = g_dDashDBTOffset * 100;
2725         break;
2726     default:
2727         DepthOffset = g_dDashDBTOffset;
2728     }
2729     itemFlexGridSizer04->Add(itemStaticDepthO, 0, wxEXPAND | wxALL, border_size);
2730     m_pSpinDBTOffset = new wxSpinCtrlDouble(itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, -100, 100, DepthOffset, 0.1);
2731     itemFlexGridSizer04->Add(m_pSpinDBTOffset, 0, wxALIGN_RIGHT | wxALL, 0);
2732 
2733     wxStaticText* itemStaticText0b = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Distance units:"),
2734             wxDefaultPosition, wxDefaultSize, 0 );
2735     itemFlexGridSizer04->Add( itemStaticText0b, 0, wxEXPAND | wxALL, border_size );
2736     wxString m_DistanceUnitChoices[] = { _("Honor OpenCPN settings"), _("Nautical miles"), _("Statute miles"), _("Kilometers"), _("Meters") };
2737     int m_DistanceUnitNChoices = sizeof( m_DistanceUnitChoices ) / sizeof( wxString );
2738     m_pChoiceDistanceUnit = new wxChoice( itemPanelNotebook02, wxID_ANY, wxDefaultPosition, wxSize(220, -1), m_DistanceUnitNChoices, m_DistanceUnitChoices, 0 );
2739     m_pChoiceDistanceUnit->SetSelection( g_iDashDistanceUnit + 1 );
2740     itemFlexGridSizer04->Add( m_pChoiceDistanceUnit, 0, wxALIGN_RIGHT | wxALL, 0 );
2741 
2742     wxStaticText* itemStaticText0a = new wxStaticText( itemPanelNotebook02, wxID_ANY, _("Wind speed units:"),
2743             wxDefaultPosition, wxDefaultSize, 0 );
2744     itemFlexGridSizer04->Add( itemStaticText0a, 0, wxEXPAND | wxALL, border_size );
2745     wxString m_WSpeedUnitChoices[] = { _("Kts"), _("mph"), _("km/h"), _("m/s") };
2746     int m_WSpeedUnitNChoices = sizeof( m_WSpeedUnitChoices ) / sizeof( wxString );
2747     m_pChoiceWindSpeedUnit = new wxChoice( itemPanelNotebook02, wxID_ANY, wxDefaultPosition, wxSize(220, -1), m_WSpeedUnitNChoices, m_WSpeedUnitChoices, 0 );
2748     m_pChoiceWindSpeedUnit->SetSelection( g_iDashWindSpeedUnit );
2749     itemFlexGridSizer04->Add( m_pChoiceWindSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0 );
2750 
2751     m_pUseTrueWinddata = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
2752         _("Use SignalK true wind data over ground. (Instead of through water)"));
2753     m_pUseTrueWinddata->SetValue(g_iDashUsetruewinddata);
2754     itemFlexGridSizer04->Add(m_pUseTrueWinddata, 1, wxALIGN_LEFT, border_size);
2755 
2756     wxStdDialogButtonSizer* DialogButtonSizer = CreateStdDialogButtonSizer( wxOK | wxCANCEL );
2757     itemBoxSizerMainPanel->Add( DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5 );
2758 
2759     curSel = -1;
2760     for( size_t i = 0; i < m_Config.GetCount(); i++ ) {
2761         m_pListCtrlDashboards->InsertItem( i, 0 );
2762         // Using data to store m_Config index for managing deletes
2763         m_pListCtrlDashboards->SetItemData( i, i );
2764     }
2765     m_pListCtrlDashboards->SetColumnWidth( 0, wxLIST_AUTOSIZE );
2766 
2767     m_pListCtrlDashboards->SetItemState(0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
2768     curSel = 0;
2769 
2770     UpdateDashboardButtonsState();
2771     UpdateButtonsState();
2772     //SetMinSize( wxSize( 450, -1 ) );
2773     SetMinSize( wxSize( 200, -1 ) );
2774     Fit();
2775 }
2776 
RecalculateSize(void)2777 void DashboardPreferencesDialog::RecalculateSize( void )
2778 {
2779 
2780 #ifdef __OCPN__ANDROID__
2781     wxSize esize;
2782     esize.x = GetCharWidth() * 110;
2783     esize.y = GetCharHeight() * 40;
2784 
2785     wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
2786     esize.y = wxMin( esize.y, dsize.y -(3 * GetCharHeight()) );
2787     esize.x = wxMin( esize.x, dsize.x -(3 * GetCharHeight()) );
2788     SetSize(esize);
2789 
2790     CentreOnScreen();
2791 #endif
2792 
2793 }
2794 
OnCloseDialog(wxCloseEvent & event)2795 void DashboardPreferencesDialog::OnCloseDialog( wxCloseEvent& event )
2796 {
2797     SaveDashboardConfig();
2798     event.Skip();
2799 }
2800 
SaveDashboardConfig()2801 void DashboardPreferencesDialog::SaveDashboardConfig()
2802 {
2803     g_iDashSpeedMax = m_pSpinSpeedMax->GetValue();
2804     g_iDashCOGDamp = m_pSpinCOGDamp->GetValue();
2805     g_iDashSOGDamp = m_pSpinSOGDamp->GetValue();
2806     g_iUTCOffset = m_pChoiceUTCOffset->GetSelection() - 24;
2807     g_iDashSpeedUnit = m_pChoiceSpeedUnit->GetSelection() - 1;
2808     double DashDBTOffset = m_pSpinDBTOffset->GetValue();
2809     switch (g_iDashDepthUnit - 3) {
2810     case 1:
2811         g_dDashDBTOffset = DashDBTOffset / 3.2808399;
2812         break;
2813     case 2:
2814         g_dDashDBTOffset = DashDBTOffset / 0.54680665;
2815         break;
2816     case 3:
2817         g_dDashDBTOffset = DashDBTOffset / 39.3700787;
2818         break;
2819     case 4:
2820         g_dDashDBTOffset = DashDBTOffset / 100;
2821         break;
2822     default:
2823         g_dDashDBTOffset = DashDBTOffset;
2824     }
2825     g_iDashDepthUnit = m_pChoiceDepthUnit->GetSelection() + 3;
2826     g_iDashDistanceUnit = m_pChoiceDistanceUnit->GetSelection() - 1;
2827     g_iDashWindSpeedUnit = m_pChoiceWindSpeedUnit->GetSelection();
2828     g_iDashUsetruewinddata = m_pUseTrueWinddata->GetValue();
2829     if( curSel != -1 ) {
2830         DashboardWindowContainer *cont = m_Config.Item( curSel );
2831         cont->m_bIsVisible = m_pCheckBoxIsVisible->IsChecked();
2832         cont->m_sCaption = m_pTextCtrlCaption->GetValue();
2833         cont->m_sOrientation = m_pChoiceOrientation->GetSelection() == 0 ? _T("V") : _T("H");
2834         cont->m_aInstrumentList.Clear();
2835         for( int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++ )
2836             cont->m_aInstrumentList.Add( (int) m_pListCtrlInstruments->GetItemData( i ) );
2837     }
2838 }
2839 
OnDashboardSelected(wxListEvent & event)2840 void DashboardPreferencesDialog::OnDashboardSelected( wxListEvent& event )
2841 {
2842     // save changes
2843     SaveDashboardConfig();
2844     UpdateDashboardButtonsState();
2845 }
2846 
UpdateDashboardButtonsState()2847 void DashboardPreferencesDialog::UpdateDashboardButtonsState()
2848 {
2849     long item = -1;
2850     item = m_pListCtrlDashboards->GetNextItem( item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
2851     bool enable = ( item != -1 );
2852 
2853     //  Disable the Dashboard Delete button if the parent(Dashboard) of this dialog is selected.
2854     bool delete_enable = enable;
2855     if( item != -1 ) {
2856         int sel = m_pListCtrlDashboards->GetItemData( item );
2857         DashboardWindowContainer *cont = m_Config.Item( sel );
2858         DashboardWindow *dash_sel = cont->m_pDashboardWindow;
2859         if(dash_sel == GetParent())
2860             delete_enable = false;
2861     }
2862     m_pButtonDeleteDashboard->Enable( delete_enable );
2863 
2864     m_pPanelDashboard->Enable( enable );
2865 
2866     if( item != -1 ) {
2867         curSel = m_pListCtrlDashboards->GetItemData( item );
2868         DashboardWindowContainer *cont = m_Config.Item( curSel );
2869         m_pCheckBoxIsVisible->SetValue( cont->m_bIsVisible );
2870         m_pTextCtrlCaption->SetValue( cont->m_sCaption );
2871         m_pChoiceOrientation->SetSelection( cont->m_sOrientation == _T("V") ? 0 : 1 );
2872         m_pListCtrlInstruments->DeleteAllItems();
2873         for( size_t i = 0; i < cont->m_aInstrumentList.GetCount(); i++ ) {
2874             wxListItem item;
2875             getListItemForInstrument( item, cont->m_aInstrumentList.Item( i ) );
2876             item.SetId( m_pListCtrlInstruments->GetItemCount() );
2877             m_pListCtrlInstruments->InsertItem( item );
2878         }
2879 
2880         m_pListCtrlInstruments->SetColumnWidth( 0, wxLIST_AUTOSIZE );
2881     } else {
2882         curSel = -1;
2883         m_pCheckBoxIsVisible->SetValue( false );
2884         m_pTextCtrlCaption->SetValue( _T("") );
2885         m_pChoiceOrientation->SetSelection( 0 );
2886         m_pListCtrlInstruments->DeleteAllItems();
2887     }
2888 //      UpdateButtonsState();
2889 }
2890 
OnDashboardAdd(wxCommandEvent & event)2891 void DashboardPreferencesDialog::OnDashboardAdd( wxCommandEvent& event )
2892 {
2893     int idx = m_pListCtrlDashboards->GetItemCount();
2894     m_pListCtrlDashboards->InsertItem( idx, 0 );
2895     // Data is index in m_Config
2896     m_pListCtrlDashboards->SetItemData( idx, m_Config.GetCount() );
2897     wxArrayInt ar;
2898     DashboardWindowContainer *dwc = new DashboardWindowContainer( NULL, MakeName(), _("Dashboard"), _T("V"), ar );
2899     dwc->m_bIsVisible = true;
2900     m_Config.Add( dwc );
2901 }
2902 
OnDashboardDelete(wxCommandEvent & event)2903 void DashboardPreferencesDialog::OnDashboardDelete( wxCommandEvent& event )
2904 {
2905     long itemID = -1;
2906     itemID = m_pListCtrlDashboards->GetNextItem( itemID, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
2907 
2908     int idx = m_pListCtrlDashboards->GetItemData( itemID );
2909     m_pListCtrlDashboards->DeleteItem( itemID );
2910     m_Config.Item( idx )->m_bIsDeleted = true;
2911     UpdateDashboardButtonsState();
2912 }
2913 
OnInstrumentSelected(wxListEvent & event)2914 void DashboardPreferencesDialog::OnInstrumentSelected( wxListEvent& event )
2915 {
2916     UpdateButtonsState();
2917 }
2918 
UpdateButtonsState()2919 void DashboardPreferencesDialog::UpdateButtonsState()
2920 {
2921     long item = -1;
2922     item = m_pListCtrlInstruments->GetNextItem( item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
2923     bool enable = ( item != -1 );
2924 
2925     m_pButtonDelete->Enable( enable );
2926 //    m_pButtonEdit->Enable( false ); // TODO: Properties
2927     m_pButtonUp->Enable( item > 0 );
2928     m_pButtonDown->Enable( item != -1 && item < m_pListCtrlInstruments->GetItemCount() - 1 );
2929 }
2930 
OnInstrumentAdd(wxCommandEvent & event)2931 void DashboardPreferencesDialog::OnInstrumentAdd( wxCommandEvent& event )
2932 {
2933     AddInstrumentDlg pdlg( (wxWindow *) event.GetEventObject(), wxID_ANY );
2934 
2935 #ifdef __OCPN__ANDROID__
2936     wxFont *pF = OCPNGetFont(_T("Dialog"), 0);
2937     pdlg.SetFont( *pF );
2938 
2939     wxSize esize;
2940     esize.x = GetCharWidth() * 110;
2941     esize.y = GetCharHeight() * 40;
2942 
2943     wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
2944     esize.y = wxMin( esize.y, dsize.y -(3 * GetCharHeight()) );
2945     esize.x = wxMin( esize.x, dsize.x -(3 * GetCharHeight()) );
2946     pdlg.SetSize(esize);
2947 
2948     pdlg.CentreOnScreen();
2949 #endif
2950     pdlg.ShowModal();
2951     if( pdlg.GetReturnCode() == wxID_OK ) {
2952         wxListItem item;
2953         getListItemForInstrument( item, pdlg.GetInstrumentAdded() );
2954         item.SetId( m_pListCtrlInstruments->GetItemCount() );
2955         m_pListCtrlInstruments->InsertItem( item );
2956         m_pListCtrlInstruments->SetColumnWidth( 0, wxLIST_AUTOSIZE );
2957         UpdateButtonsState();
2958     }
2959 }
2960 
OnInstrumentDelete(wxCommandEvent & event)2961 void DashboardPreferencesDialog::OnInstrumentDelete( wxCommandEvent& event )
2962 {
2963     long itemID = -1;
2964     itemID = m_pListCtrlInstruments->GetNextItem( itemID, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
2965 
2966     m_pListCtrlInstruments->DeleteItem( itemID );
2967     UpdateButtonsState();
2968 }
2969 
OnInstrumentEdit(wxCommandEvent & event)2970 void DashboardPreferencesDialog::OnInstrumentEdit( wxCommandEvent& event )
2971 {
2972 // TODO: Instument options
2973 }
2974 
OnInstrumentUp(wxCommandEvent & event)2975 void DashboardPreferencesDialog::OnInstrumentUp( wxCommandEvent& event )
2976 {
2977     long itemID = -1;
2978     itemID = m_pListCtrlInstruments->GetNextItem( itemID, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
2979 
2980     wxListItem item;
2981     item.SetId( itemID );
2982     item.SetMask( wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA );
2983     m_pListCtrlInstruments->GetItem( item );
2984     item.SetId( itemID - 1 );
2985     //item.SetImage(0);           // image 0, by default
2986     m_pListCtrlInstruments->DeleteItem( itemID );
2987     m_pListCtrlInstruments->InsertItem( item );
2988 
2989     for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
2990         m_pListCtrlInstruments->SetItemState(i,0,wxLIST_STATE_SELECTED);
2991 
2992     m_pListCtrlInstruments->SetItemState( itemID - 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
2993 
2994     UpdateButtonsState();
2995 }
2996 
OnInstrumentDown(wxCommandEvent & event)2997 void DashboardPreferencesDialog::OnInstrumentDown( wxCommandEvent& event )
2998 {
2999     long itemID = -1;
3000     itemID = m_pListCtrlInstruments->GetNextItem( itemID, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
3001 
3002     wxListItem item;
3003     item.SetId( itemID );
3004     item.SetMask( wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA );
3005     m_pListCtrlInstruments->GetItem( item );
3006     item.SetId( itemID + 1 );
3007     //item.SetImage(0);           // image 0, by default
3008     m_pListCtrlInstruments->DeleteItem( itemID );
3009     m_pListCtrlInstruments->InsertItem( item );
3010 
3011     for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
3012         m_pListCtrlInstruments->SetItemState(i,0,wxLIST_STATE_SELECTED);
3013 
3014     m_pListCtrlInstruments->SetItemState( itemID + 1, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
3015 
3016     UpdateButtonsState();
3017 }
3018 
3019 //----------------------------------------------------------------
3020 //
3021 //    Add Instrument Dialog Implementation
3022 //
3023 //----------------------------------------------------------------
3024 
AddInstrumentDlg(wxWindow * pparent,wxWindowID id)3025 AddInstrumentDlg::AddInstrumentDlg( wxWindow *pparent, wxWindowID id ) :
3026         wxDialog( pparent, id, _("Add instrument"), wxDefaultPosition, wxDefaultSize,
3027                 wxDEFAULT_DIALOG_STYLE )
3028 {
3029     wxBoxSizer* itemBoxSizer01 = new wxBoxSizer( wxVERTICAL );
3030     SetSizer( itemBoxSizer01 );
3031     wxStaticText* itemStaticText01 = new wxStaticText( this, wxID_ANY,
3032             _("Select instrument to add:"), wxDefaultPosition, wxDefaultSize, 0 );
3033     itemBoxSizer01->Add( itemStaticText01, 0, wxEXPAND | wxALL, 5 );
3034 
3035     int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
3036 
3037     wxImageList *imglist = new wxImageList( instImageRefSize, instImageRefSize, true, 2 );
3038 
3039     wxImage inst1 = wxBitmap( *_img_instrument ).ConvertToImage();
3040     wxImage inst1s = inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
3041     imglist->Add( wxBitmap(inst1s) );
3042 
3043     wxImage dial1 = wxBitmap( *_img_dial ).ConvertToImage();
3044     wxImage dial1s = dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
3045     imglist->Add( wxBitmap(dial1s) );
3046 
3047 
3048 
3049 
3050     int vsize = 180;
3051 
3052     #ifdef __OCPN__ANDROID__
3053     int dw, dh;
3054     wxDisplaySize(&dw, &dh);
3055     vsize = dh * 50 / 100;
3056     #endif
3057 
3058     m_pListCtrlInstruments = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxSize( -1, vsize/*250, 180 */),
3059             wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING );
3060     itemBoxSizer01->Add( m_pListCtrlInstruments, 0, wxEXPAND | wxALL, 5 );
3061     m_pListCtrlInstruments->AssignImageList( imglist, wxIMAGE_LIST_SMALL );
3062     m_pListCtrlInstruments->InsertColumn( 0, _("Instruments") );
3063 
3064     wxFont *pF = OCPNGetFont(_T("Dialog"), 0);
3065     m_pListCtrlInstruments->SetFont( *pF );
3066 
3067     #ifdef __OCPN__ANDROID__
3068     m_pListCtrlInstruments->GetHandle()->setStyleSheet( qtStyleSheet);
3069     ///QScroller::ungrabGesture(m_pListCtrlInstruments->GetHandle());
3070     #endif
3071 
3072     wxStdDialogButtonSizer* DialogButtonSizer = CreateStdDialogButtonSizer( wxOK | wxCANCEL );
3073     itemBoxSizer01->Add( DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5 );
3074 
3075     long ident = 0;
3076     for( unsigned int i = ID_DBP_I_POS; i < ID_DBP_LAST_ENTRY; i++ ) { //do not reference an instrument, but the last dummy entry in the list
3077         wxListItem item;
3078         if( IsObsolete( i ) ) continue;
3079         getListItemForInstrument( item, i );
3080         item.SetId( ident );
3081         m_pListCtrlInstruments->InsertItem( item );
3082         id++;
3083     }
3084 
3085     m_pListCtrlInstruments->SetColumnWidth( 0, wxLIST_AUTOSIZE );
3086     m_pListCtrlInstruments->SetItemState( 0, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED );
3087     Fit();
3088 }
3089 
GetInstrumentAdded()3090 unsigned int AddInstrumentDlg::GetInstrumentAdded()
3091 {
3092     long itemID = -1;
3093     itemID = m_pListCtrlInstruments->GetNextItem( itemID, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED );
3094 
3095     return (int) m_pListCtrlInstruments->GetItemData( itemID );
3096 }
3097 
3098 //----------------------------------------------------------------
3099 //
3100 //    Dashboard Window Implementation
3101 //
3102 //----------------------------------------------------------------
3103 
3104 // wxWS_EX_VALIDATE_RECURSIVELY required to push events to parents
DashboardWindow(wxWindow * pparent,wxWindowID id,wxAuiManager * auimgr,dashboard_pi * plugin,int orient,DashboardWindowContainer * mycont)3105 DashboardWindow::DashboardWindow( wxWindow *pparent, wxWindowID id, wxAuiManager *auimgr,
3106         dashboard_pi* plugin, int orient, DashboardWindowContainer* mycont ) :
3107         wxWindow(pparent, id, wxDefaultPosition, wxDefaultSize, 0)
3108 {
3109 
3110     //wxDialog::Create(pparent, id, _("tileMine"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("Dashboard"));
3111 
3112     m_pauimgr = auimgr;
3113     m_plugin = plugin;
3114     m_Container = mycont;
3115 
3116 //wx2.9      itemBoxSizer = new wxWrapSizer( orient );
3117     itemBoxSizer = new wxBoxSizer( orient );
3118     SetSizer( itemBoxSizer );
3119     Connect( wxEVT_SIZE, wxSizeEventHandler( DashboardWindow::OnSize ), NULL, this );
3120     Connect( wxEVT_CONTEXT_MENU, wxContextMenuEventHandler( DashboardWindow::OnContextMenu ), NULL,
3121             this );
3122     Connect( wxEVT_COMMAND_MENU_SELECTED,
3123             wxCommandEventHandler( DashboardWindow::OnContextMenuSelect ), NULL, this );
3124 
3125 
3126 
3127 #ifdef __OCPN__ANDROID__
3128     Connect( wxEVT_LEFT_DOWN, wxMouseEventHandler( DashboardWindow::OnMouseEvent ) );
3129     Connect( wxEVT_LEFT_UP, wxMouseEventHandler( DashboardWindow::OnMouseEvent ) );
3130     Connect( wxEVT_MOTION, wxMouseEventHandler( DashboardWindow::OnMouseEvent ) );
3131 
3132     GetHandle()->setAttribute(Qt::WA_AcceptTouchEvents);
3133     GetHandle()->grabGesture(Qt::PinchGesture);
3134     GetHandle()->grabGesture(Qt::PanGesture);
3135 
3136     Connect( wxEVT_QT_PINCHGESTURE,
3137             (wxObjectEventFunction) (wxEventFunction) &DashboardWindow::OnEvtPinchGesture, NULL, this );
3138     Connect( wxEVT_QT_PANGESTURE,
3139              (wxObjectEventFunction) (wxEventFunction) &DashboardWindow::OnEvtPanGesture, NULL, this );
3140 #endif
3141 
3142     Hide();
3143 
3144     m_binResize = false;
3145     m_binPinch = false;
3146 
3147 }
3148 
~DashboardWindow()3149 DashboardWindow::~DashboardWindow()
3150 {
3151     for( size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++ ) {
3152         DashboardInstrumentContainer *pdic = m_ArrayOfInstrument.Item( i );
3153         delete pdic;
3154     }
3155 }
3156 
3157 
3158 
3159 #ifdef __OCPN__ANDROID__
OnEvtPinchGesture(wxQT_PinchGestureEvent & event)3160 void DashboardWindow::OnEvtPinchGesture( wxQT_PinchGestureEvent &event)
3161 {
3162 
3163     float zoom_gain = 0.3;
3164     float zoom_val;
3165     float total_zoom_val;
3166 
3167     if( event.GetScaleFactor() > 1)
3168         zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
3169     else
3170         zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zoom_gain);
3171 
3172     if( event.GetTotalScaleFactor() > 1)
3173         total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
3174     else
3175         total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
3176 
3177 
3178     wxAuiPaneInfo& pane = m_pauimgr->GetPane( this );
3179 
3180     wxSize currentSize = wxSize( pane.floating_size.x, pane.floating_size.y );
3181     double aRatio = (double)currentSize.y / (double)currentSize.x;
3182 
3183     wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
3184     wxPoint par_pos = wxPoint( pane.floating_pos.x, pane.floating_pos.y );
3185 
3186     switch(event.GetState()){
3187         case GestureStarted:
3188             m_binPinch = true;
3189             break;
3190 
3191         case GestureUpdated:
3192             currentSize.y *= zoom_val;
3193             currentSize.x *= zoom_val;
3194 
3195             if((par_pos.y + currentSize.y) > par_size.y)
3196                 currentSize.y = par_size.y - par_pos.y;
3197 
3198             if((par_pos.x + currentSize.x) > par_size.x)
3199                 currentSize.x = par_size.x - par_pos.x;
3200 
3201 
3202             ///vertical
3203             currentSize.x = currentSize.y / aRatio;
3204 
3205             currentSize.x = wxMax(currentSize.x, 150);
3206             currentSize.y = wxMax(currentSize.y, 150);
3207 
3208             pane.FloatingSize(currentSize);
3209             m_pauimgr->Update();
3210 
3211 
3212             break;
3213 
3214         case GestureFinished:{
3215 
3216             if(itemBoxSizer->GetOrientation() == wxVERTICAL){
3217                 currentSize.y *= total_zoom_val;
3218                 currentSize.x = currentSize.y / aRatio;
3219             }
3220             else{
3221                 currentSize.x *= total_zoom_val;
3222                 currentSize.y = currentSize.x * aRatio;
3223             }
3224 
3225 
3226             //  Bound the resulting size
3227             if((par_pos.y + currentSize.y) > par_size.y)
3228                 currentSize.y = par_size.y - par_pos.y;
3229 
3230             if((par_pos.x + currentSize.x) > par_size.x)
3231                 currentSize.x = par_size.x - par_pos.x;
3232 
3233             // not too small
3234             currentSize.x = wxMax(currentSize.x, 150);
3235             currentSize.y = wxMax(currentSize.y, 150);
3236 
3237             //  Try a manual layout of the window, to estimate a good primary size..
3238 
3239             // vertical
3240             if(itemBoxSizer->GetOrientation() == wxVERTICAL){
3241                 int total_y = 0;
3242                 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3243                     DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3244                     wxSize is = inst->GetSize( itemBoxSizer->GetOrientation(), currentSize );
3245                     total_y += is.y;
3246                 }
3247 
3248                 currentSize.y = total_y;
3249             }
3250 
3251 
3252             pane.FloatingSize(currentSize);
3253 
3254             // Reshow the window
3255             for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3256                 DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3257                 inst->Show();
3258             }
3259 
3260             m_pauimgr->Update();
3261 
3262             m_binPinch = false;
3263             m_binResize = false;
3264 
3265             break;
3266         }
3267 
3268         case GestureCanceled:
3269             m_binPinch = false;
3270             m_binResize = false;
3271             break;
3272 
3273         default:
3274             break;
3275     }
3276 
3277 }
3278 
3279 
OnEvtPanGesture(wxQT_PanGestureEvent & event)3280 void DashboardWindow::OnEvtPanGesture( wxQT_PanGestureEvent &event)
3281 {
3282     if(m_binPinch)
3283         return;
3284 
3285     if(m_binResize)
3286         return;
3287 
3288     int x = event.GetOffset().x;
3289     int y = event.GetOffset().y;
3290 
3291     int lx = event.GetLastOffset().x;
3292     int ly = event.GetLastOffset().y;
3293 
3294     int dx = x - lx;
3295     int dy = y - ly;
3296 
3297     switch(event.GetState()){
3298         case GestureStarted:
3299             if(m_binPan)
3300                 break;
3301 
3302             m_binPan = true;
3303             break;
3304 
3305         case GestureUpdated:
3306             if(m_binPan){
3307 
3308                 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
3309                 wxPoint par_pos_old = ClientToScreen( wxPoint( 0, 0 ) ); //GetPosition();
3310 
3311                 wxPoint par_pos = par_pos_old;
3312                 par_pos.x += dx;
3313                 par_pos.y += dy;
3314 
3315                 par_pos.x = wxMax(par_pos.x, 0);
3316                 par_pos.y = wxMax(par_pos.y, 0);
3317 
3318                 wxSize mySize = GetSize();
3319 
3320                 if((par_pos.y + mySize.y) > par_size.y)
3321                     par_pos.y = par_size.y - mySize.y;
3322 
3323 
3324                 if((par_pos.x + mySize.x) > par_size.x)
3325                     par_pos.x = par_size.x - mySize.x;
3326 
3327                 wxAuiPaneInfo& pane = m_pauimgr->GetPane( this );
3328                 pane.FloatingPosition( par_pos).Float();
3329                 m_pauimgr->Update();
3330 
3331             }
3332             break;
3333 
3334         case GestureFinished:
3335             if(m_binPan){
3336             }
3337             m_binPan = false;
3338 
3339             break;
3340 
3341         case GestureCanceled:
3342             m_binPan = false;
3343             break;
3344 
3345         default:
3346             break;
3347     }
3348 
3349 
3350 }
3351 
3352 
OnMouseEvent(wxMouseEvent & event)3353 void DashboardWindow::OnMouseEvent( wxMouseEvent& event )
3354 {
3355     if(m_binPinch)
3356         return;
3357 
3358     if(m_binResize){
3359 
3360         wxAuiPaneInfo& pane = m_pauimgr->GetPane( this );
3361         wxSize currentSize = wxSize( pane.floating_size.x, pane.floating_size.y );
3362         double aRatio = (double)currentSize.y / (double)currentSize.x;
3363 
3364         wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
3365         wxPoint par_pos = wxPoint( pane.floating_pos.x, pane.floating_pos.y );
3366 
3367         if(event.LeftDown()){
3368             m_resizeStartPoint = event.GetPosition();
3369             m_resizeStartSize = currentSize;
3370             m_binResize2 = true;
3371          }
3372 
3373         if(m_binResize2){
3374             if(event.Dragging()){
3375                 wxPoint p = event.GetPosition();
3376 
3377                 wxSize dragSize = m_resizeStartSize;
3378 
3379                 dragSize.y += p.y - m_resizeStartPoint.y;
3380                 dragSize.x += p.x - m_resizeStartPoint.x;;
3381 
3382                 if((par_pos.y + dragSize.y) > par_size.y)
3383                     dragSize.y = par_size.y - par_pos.y;
3384 
3385                 if((par_pos.x + dragSize.x) > par_size.x)
3386                     dragSize.x = par_size.x - par_pos.x;
3387 
3388 
3389                 ///vertical
3390                 //dragSize.x = dragSize.y / aRatio;
3391 
3392                 // not too small
3393                 dragSize.x = wxMax(dragSize.x, 150);
3394                 dragSize.y = wxMax(dragSize.y, 150);
3395 
3396                 pane.FloatingSize(dragSize);
3397                 m_pauimgr->Update();
3398 
3399             }
3400 
3401             if(event.LeftUp()){
3402                 wxPoint p = event.GetPosition();
3403 
3404                 wxSize dragSize = m_resizeStartSize;
3405 
3406                 dragSize.y += p.y - m_resizeStartPoint.y;
3407                 dragSize.x += p.x - m_resizeStartPoint.x;;
3408 
3409                 if((par_pos.y + dragSize.y) > par_size.y)
3410                     dragSize.y = par_size.y - par_pos.y;
3411 
3412                 if((par_pos.x + dragSize.x) > par_size.x)
3413                     dragSize.x = par_size.x - par_pos.x;
3414 
3415                 // not too small
3416                 dragSize.x = wxMax(dragSize.x, 150);
3417                 dragSize.y = wxMax(dragSize.y, 150);
3418 /*
3419                 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3420                     DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3421                     inst->Show();
3422                 }
3423 */
3424                 pane.FloatingSize(dragSize);
3425                 m_pauimgr->Update();
3426 
3427 
3428                 m_binResize = false;
3429                 m_binResize2 = false;
3430             }
3431         }
3432     }
3433 }
3434 #endif
3435 
3436 
OnSize(wxSizeEvent & event)3437 void DashboardWindow::OnSize( wxSizeEvent& event )
3438 {
3439     event.Skip();
3440     for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3441         DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3442         inst->SetMinSize( inst->GetSize( itemBoxSizer->GetOrientation(), GetClientSize() ) );
3443     }
3444     Layout();
3445     Refresh();
3446 }
3447 
OnContextMenu(wxContextMenuEvent & event)3448 void DashboardWindow::OnContextMenu( wxContextMenuEvent& event )
3449 {
3450     wxMenu* contextMenu = new wxMenu();
3451 
3452 #ifdef __WXQT__
3453     wxFont *pf = OCPNGetFont(_T("Menu"), 0);
3454 
3455     // add stuff
3456     wxMenuItem *item1 = new wxMenuItem(contextMenu, ID_DASH_PREFS, _("Preferences..."));
3457     item1->SetFont(*pf);
3458     contextMenu->Append(item1);
3459 
3460     wxMenuItem *item2 = new wxMenuItem(contextMenu, ID_DASH_RESIZE, _("Resize..."));
3461     item2->SetFont(*pf);
3462     contextMenu->Append(item2);
3463 
3464 
3465 #else
3466 
3467 
3468     wxAuiPaneInfo &pane = m_pauimgr->GetPane( this );
3469     if ( pane.IsOk( ) && pane.IsDocked( ) ) {
3470         contextMenu->Append( ID_DASH_UNDOCK, _( "Undock" ) );
3471     }
3472     wxMenuItem* btnVertical = contextMenu->AppendRadioItem( ID_DASH_VERTICAL, _("Vertical") );
3473     btnVertical->Check( itemBoxSizer->GetOrientation() == wxVERTICAL );
3474     wxMenuItem* btnHorizontal = contextMenu->AppendRadioItem( ID_DASH_HORIZONTAL, _("Horizontal") );
3475     btnHorizontal->Check( itemBoxSizer->GetOrientation() == wxHORIZONTAL );
3476     contextMenu->AppendSeparator();
3477 
3478     m_plugin->PopulateContextMenu( contextMenu );
3479 
3480     contextMenu->AppendSeparator();
3481     contextMenu->Append( ID_DASH_PREFS, _("Preferences...") );
3482 
3483 #endif
3484 
3485     PopupMenu( contextMenu );
3486     delete contextMenu;
3487 }
3488 
OnContextMenuSelect(wxCommandEvent & event)3489 void DashboardWindow::OnContextMenuSelect( wxCommandEvent& event )
3490 {
3491     if( event.GetId() < ID_DASH_PREFS ) { // Toggle dashboard visibility
3492         m_plugin->ShowDashboard( event.GetId()-1, event.IsChecked() );
3493         SetToolbarItemState( m_plugin->GetToolbarItemId(), m_plugin->GetDashboardWindowShownCount() != 0 );
3494     }
3495 
3496     switch( event.GetId() ){
3497         case ID_DASH_PREFS: {
3498             m_plugin->ShowPreferencesDialog( this );
3499             return; // Does it's own save.
3500         }
3501         case ID_DASH_RESIZE: {
3502 /*
3503             for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3504                 DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3505                 inst->Hide();
3506             }
3507 */
3508             m_binResize = true;
3509 
3510             return;
3511         }
3512         case ID_DASH_VERTICAL: {
3513             ChangePaneOrientation( wxVERTICAL, true );
3514             m_Container->m_sOrientation = _T("V");
3515             break;
3516         }
3517         case ID_DASH_HORIZONTAL: {
3518             ChangePaneOrientation( wxHORIZONTAL, true );
3519             m_Container->m_sOrientation = _T("H");
3520             break;
3521         }
3522         case ID_DASH_UNDOCK: {
3523             ChangePaneOrientation( GetSizerOrientation( ), true );
3524             return;     // Nothing changed so nothing need be saved
3525         }
3526     }
3527     m_plugin->SaveConfig();
3528 }
3529 
SetColorScheme(PI_ColorScheme cs)3530 void DashboardWindow::SetColorScheme( PI_ColorScheme cs )
3531 {
3532     DimeWindow( this );
3533 
3534     //  Improve appearance, especially in DUSK or NIGHT palette
3535     wxColour col;
3536     GetGlobalColor( _T("DASHL"), &col );
3537     SetBackgroundColour( col );
3538 
3539     Refresh( false );
3540 }
3541 
ChangePaneOrientation(int orient,bool updateAUImgr)3542 void DashboardWindow::ChangePaneOrientation( int orient, bool updateAUImgr )
3543 {
3544     m_pauimgr->DetachPane( this );
3545     SetSizerOrientation( orient );
3546     bool vertical = orient == wxVERTICAL;
3547     //wxSize sz = GetSize( orient, wxDefaultSize );
3548     wxSize sz = GetMinSize();
3549     // We must change Name to reset AUI perpective
3550     m_Container->m_sName = MakeName();
3551     m_pauimgr->AddPane( this, wxAuiPaneInfo().Name( m_Container->m_sName ).Caption(
3552         m_Container->m_sCaption ).CaptionVisible( true ).TopDockable( !vertical ).BottomDockable(
3553         !vertical ).LeftDockable( vertical ).RightDockable( vertical ).MinSize( sz ).BestSize(
3554         sz ).FloatingSize( sz ).FloatingPosition( 100, 100 ).Float().Show( m_Container->m_bIsVisible ) );
3555 
3556 #ifdef __OCPN__ANDROID__
3557     wxAuiPaneInfo& pane = m_pauimgr->GetPane( this );
3558     pane.Dockable( false );
3559 #endif
3560 
3561     if ( updateAUImgr ) m_pauimgr->Update();
3562 }
3563 
SetSizerOrientation(int orient)3564 void DashboardWindow::SetSizerOrientation( int orient )
3565 {
3566     itemBoxSizer->SetOrientation( orient );
3567     /* We must reset all MinSize to ensure we start with new default */
3568     wxWindowListNode* node = GetChildren().GetFirst();
3569     while(node) {
3570         node->GetData()->SetMinSize( wxDefaultSize );
3571         node = node->GetNext();
3572     }
3573     SetMinSize( wxDefaultSize );
3574     Fit();
3575     SetMinSize( itemBoxSizer->GetMinSize() );
3576 }
3577 
GetSizerOrientation()3578 int DashboardWindow::GetSizerOrientation()
3579 {
3580     return itemBoxSizer->GetOrientation();
3581 }
3582 
isArrayIntEqual(const wxArrayInt & l1,const wxArrayOfInstrument & l2)3583 bool isArrayIntEqual( const wxArrayInt& l1, const wxArrayOfInstrument &l2 )
3584 {
3585     if( l1.GetCount() != l2.GetCount() ) return false;
3586 
3587     for( size_t i = 0; i < l1.GetCount(); i++ )
3588         if( l1.Item( i ) != l2.Item( i )->m_ID ) return false;
3589 
3590     return true;
3591 }
3592 
isInstrumentListEqual(const wxArrayInt & list)3593 bool DashboardWindow::isInstrumentListEqual( const wxArrayInt& list )
3594 {
3595     return isArrayIntEqual( list, m_ArrayOfInstrument );
3596 }
3597 
SetInstrumentList(wxArrayInt list)3598 void DashboardWindow::SetInstrumentList( wxArrayInt list )
3599 {
3600     /* options
3601      ID_DBP_D_SOG: config max value, show STW optional
3602      ID_DBP_D_COG:  +SOG +HDG? +BRG?
3603      ID_DBP_D_AWS: config max value. Two arrows for AWS+TWS?
3604      ID_DBP_D_VMG: config max value
3605      ID_DBP_I_DPT: config unit (meter, feet, fathoms)
3606      ID_DBP_D_DPT: show temp optional
3607      // compass: use COG or HDG
3608      // velocity range
3609      // rudder range
3610 
3611      */
3612     m_ArrayOfInstrument.Clear();
3613     itemBoxSizer->Clear( true );
3614     for( size_t i = 0; i < list.GetCount(); i++ ) {
3615         int id = list.Item( i );
3616         DashboardInstrument *instrument = NULL;
3617         switch( id ){
3618             case ID_DBP_I_POS:
3619                 instrument = new DashboardInstrument_Position( this, wxID_ANY,
3620                         getInstrumentCaption( id ) );
3621                 break;
3622             case ID_DBP_I_SOG:
3623                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3624                         getInstrumentCaption( id ), OCPN_DBP_STC_SOG, _T("%5.1f") );
3625                 break;
3626             case ID_DBP_D_SOG:
3627                 instrument = new DashboardInstrument_Speedometer( this, wxID_ANY,
3628                         getInstrumentCaption( id ), OCPN_DBP_STC_SOG, 0, g_iDashSpeedMax );
3629                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( g_iDashSpeedMax / 20 + 1,
3630                         DIAL_LABEL_HORIZONTAL );
3631                 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1, DIAL_MARKER_SIMPLE, 5);
3632                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 0.5,
3633                         DIAL_MARKER_SIMPLE, 2 );
3634                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3635                         OCPN_DBP_STC_STW, _T("STW\n%.2f"), DIAL_POSITION_BOTTOMLEFT );
3636                 break;
3637             case ID_DBP_I_COG:
3638                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3639                         getInstrumentCaption( id ), OCPN_DBP_STC_COG, _T("%.0f") );
3640                 break;
3641             case ID_DBP_M_COG:
3642                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3643                         getInstrumentCaption( id ), OCPN_DBP_STC_MCOG, _T("%.0f") );
3644                 break;
3645             case ID_DBP_D_COG:
3646                 instrument = new DashboardInstrument_Compass( this, wxID_ANY,
3647                         getInstrumentCaption( id ), OCPN_DBP_STC_COG );
3648                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 5,
3649                         DIAL_MARKER_SIMPLE, 2 );
3650                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( 30,
3651                         DIAL_LABEL_ROTATED );
3652                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3653                         OCPN_DBP_STC_SOG, _T("SOG\n%.2f"), DIAL_POSITION_BOTTOMLEFT );
3654                 break;
3655             case ID_DBP_D_HDT:
3656                 instrument = new DashboardInstrument_Compass( this, wxID_ANY,
3657                         getInstrumentCaption( id ), OCPN_DBP_STC_HDT );
3658                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 5,
3659                         DIAL_MARKER_SIMPLE, 2 );
3660                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( 30,
3661                         DIAL_LABEL_ROTATED );
3662                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3663                         OCPN_DBP_STC_STW, _T("STW\n%.1f"), DIAL_POSITION_BOTTOMLEFT );
3664                 break;
3665             case ID_DBP_I_STW:
3666                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3667                         getInstrumentCaption( id ), OCPN_DBP_STC_STW, _T("%.1f") );
3668                 break;
3669             case ID_DBP_I_HDT: //true heading
3670                 // TODO: Option True or Magnetic
3671                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3672                         getInstrumentCaption( id ), OCPN_DBP_STC_HDT, _T("%.0f") );
3673                 break;
3674             case ID_DBP_I_HDM:  //magnetic heading
3675                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3676                         getInstrumentCaption( id ), OCPN_DBP_STC_HDM, _T("%.0f") );
3677                 break;
3678             case ID_DBP_D_AW:
3679             case ID_DBP_D_AWA:
3680                 instrument = new DashboardInstrument_Wind( this, wxID_ANY,
3681                         getInstrumentCaption( id ), OCPN_DBP_STC_AWA );
3682                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMainValue( _T("%.0f"),
3683                         DIAL_POSITION_BOTTOMLEFT );
3684                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3685                         OCPN_DBP_STC_AWS, _T("%.1f"), DIAL_POSITION_INSIDE );
3686                 break;
3687             case ID_DBP_I_AWS:
3688                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3689                         getInstrumentCaption( id ), OCPN_DBP_STC_AWS, _T("%.1f") );
3690                 break;
3691             case ID_DBP_D_AWS:
3692                 instrument = new DashboardInstrument_Speedometer( this, wxID_ANY,
3693                         getInstrumentCaption( id ), OCPN_DBP_STC_AWS, 0, 45 );
3694                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( 5,
3695                         DIAL_LABEL_HORIZONTAL );
3696                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 1,
3697                         DIAL_MARKER_SIMPLE, 5 );
3698                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMainValue( _T("A %.1f"),
3699                         DIAL_POSITION_BOTTOMLEFT );
3700                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3701                         OCPN_DBP_STC_TWS, _T("T %.1f"), DIAL_POSITION_BOTTOMRIGHT );
3702                 break;
3703             case ID_DBP_D_TW: //True Wind angle +-180° on boat axis
3704                 instrument = new DashboardInstrument_TrueWindAngle( this, wxID_ANY,
3705                         getInstrumentCaption( id ), OCPN_DBP_STC_TWA );
3706                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMainValue( _T("%.0f"),
3707                         DIAL_POSITION_BOTTOMLEFT );
3708                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3709                         OCPN_DBP_STC_TWS, _T("%.1f"), DIAL_POSITION_INSIDE );
3710                 break;
3711 			case ID_DBP_D_AWA_TWA: //App/True Wind angle +-180° on boat axis
3712 				instrument = new DashboardInstrument_AppTrueWindAngle(this, wxID_ANY,
3713 					getInstrumentCaption(id), OCPN_DBP_STC_AWA | OCPN_DBP_STC_TWA);
3714 				((DashboardInstrument_Dial *)instrument)->SetOptionMainValue(_T("%.0f"),
3715 					DIAL_POSITION_NONE);
3716 				((DashboardInstrument_Dial *)instrument)->SetOptionExtraValue(
3717 					OCPN_DBP_STC_TWS | OCPN_DBP_STC_AWS, _T("%.1f"), DIAL_POSITION_NONE);
3718 				break;
3719             case ID_DBP_D_TWD: //True Wind direction
3720                 instrument = new DashboardInstrument_WindCompass( this, wxID_ANY,
3721                         getInstrumentCaption( id ), OCPN_DBP_STC_TWD );
3722                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMainValue( _T("%.0f"),
3723                         DIAL_POSITION_BOTTOMLEFT );
3724                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3725                         OCPN_DBP_STC_TWS2, _T("%.1f"), DIAL_POSITION_INSIDE );
3726                 break;
3727             case ID_DBP_I_DPT:
3728                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3729                         getInstrumentCaption( id ), OCPN_DBP_STC_DPT, _T("%5.2f") );
3730                 break;
3731             case ID_DBP_D_DPT:
3732                 instrument = new DashboardInstrument_Depth( this, wxID_ANY,
3733                         getInstrumentCaption( id ) );
3734                 break;
3735             case ID_DBP_I_TMP: //water temperature
3736                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3737                         getInstrumentCaption( id ), OCPN_DBP_STC_TMP, _T("%2.1f") );
3738                 break;
3739             case ID_DBP_I_MDA: //barometric pressure
3740                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3741                         getInstrumentCaption( id ), OCPN_DBP_STC_MDA, _T("%5.1f") );
3742                 break;
3743                case ID_DBP_D_MDA: //barometric pressure
3744                 instrument = new DashboardInstrument_Speedometer( this, wxID_ANY,
3745                         getInstrumentCaption( id ), OCPN_DBP_STC_MDA, 938, 1088 );
3746                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( 15,
3747                         DIAL_LABEL_HORIZONTAL );
3748                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 7.5,
3749                         DIAL_MARKER_SIMPLE, 1 );
3750                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMainValue( _T("%5.1f"),
3751                         DIAL_POSITION_INSIDE );
3752                 break;
3753             case ID_DBP_I_ATMP: //air temperature
3754                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3755                         getInstrumentCaption( id ), OCPN_DBP_STC_ATMP, _T("%2.1f") );
3756                 break;
3757             case ID_DBP_I_VLW1: // Trip Log
3758                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3759                         getInstrumentCaption( id ), OCPN_DBP_STC_VLW1, _T("%2.1f") );
3760                 break;
3761 
3762             case ID_DBP_I_VLW2: // Sum Log
3763                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3764                         getInstrumentCaption( id ), OCPN_DBP_STC_VLW2, _T("%2.1f") );
3765                 break;
3766 
3767             case ID_DBP_I_TWA: //true wind angle
3768                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3769                         getInstrumentCaption( id ), OCPN_DBP_STC_TWA, _T("%5.0f") );
3770                 break;
3771             case ID_DBP_I_TWD: //true wind direction
3772                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3773                         getInstrumentCaption( id ), OCPN_DBP_STC_TWD, _T("%5.0f") );
3774                 break;
3775             case ID_DBP_I_TWS: // true wind speed
3776                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3777                         getInstrumentCaption( id ), OCPN_DBP_STC_TWS, _T("%2.1f") );
3778                 break;
3779             case ID_DBP_I_AWA: //apparent wind angle
3780                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3781                         getInstrumentCaption( id ), OCPN_DBP_STC_AWA, _T("%5.0f") );
3782                 break;
3783             case ID_DBP_I_VMG:
3784                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3785                         getInstrumentCaption( id ), OCPN_DBP_STC_VMG, _T("%5.1f") );
3786                 break;
3787             case ID_DBP_D_VMG:
3788                 instrument = new DashboardInstrument_Speedometer( this, wxID_ANY,
3789                         getInstrumentCaption( id ), OCPN_DBP_STC_VMG, 0, g_iDashSpeedMax );
3790                 ( (DashboardInstrument_Dial *) instrument )->SetOptionLabel( 1,
3791                         DIAL_LABEL_HORIZONTAL );
3792                 ( (DashboardInstrument_Dial *) instrument )->SetOptionMarker( 0.5,
3793                         DIAL_MARKER_SIMPLE, 2 );
3794                 ( (DashboardInstrument_Dial *) instrument )->SetOptionExtraValue(
3795                         OCPN_DBP_STC_SOG, _T("SOG\n%.1f"), DIAL_POSITION_BOTTOMLEFT );
3796                 break;
3797             case ID_DBP_I_RSA:
3798                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3799                         getInstrumentCaption( id ), OCPN_DBP_STC_RSA, _T("%5.0f") );
3800                 break;
3801             case ID_DBP_D_RSA:
3802                 instrument = new DashboardInstrument_RudderAngle( this, wxID_ANY,
3803                         getInstrumentCaption( id ) );
3804                 break;
3805             case ID_DBP_I_SAT:
3806                 instrument = new DashboardInstrument_Single( this, wxID_ANY,
3807                         getInstrumentCaption( id ), OCPN_DBP_STC_SAT, _T("%5.0f") );
3808                 break;
3809             case ID_DBP_D_GPS:
3810                 instrument = new DashboardInstrument_GPS( this, wxID_ANY,
3811                         getInstrumentCaption( id ) );
3812                 break;
3813             case ID_DBP_I_PTR:
3814                 instrument = new DashboardInstrument_Position( this, wxID_ANY,
3815                         getInstrumentCaption( id ), OCPN_DBP_STC_PLA, OCPN_DBP_STC_PLO );
3816                 break;
3817             case ID_DBP_I_GPSUTC:
3818                 instrument = new DashboardInstrument_Clock( this, wxID_ANY,
3819                         getInstrumentCaption( id ) );
3820                 break;
3821             case ID_DBP_I_SUN:
3822                 instrument = new DashboardInstrument_Sun( this, wxID_ANY,
3823                         getInstrumentCaption( id ) );
3824                 break;
3825             case ID_DBP_D_MON:
3826                 instrument = new DashboardInstrument_Moon( this, wxID_ANY,
3827                         getInstrumentCaption( id ) );
3828                 break;
3829             case ID_DBP_D_WDH:
3830                 instrument = new DashboardInstrument_WindDirHistory(this, wxID_ANY,
3831                         getInstrumentCaption( id ) );
3832                 break;
3833             case ID_DBP_D_BPH:
3834                 instrument = new DashboardInstrument_BaroHistory(this, wxID_ANY,
3835                         getInstrumentCaption( id ) );
3836                 break;
3837             case ID_DBP_I_FOS:
3838                 instrument = new DashboardInstrument_FromOwnship( this, wxID_ANY,
3839                         getInstrumentCaption( id ) );
3840                 break;
3841 			case ID_DBP_I_PITCH:
3842 				instrument = new DashboardInstrument_Single(this, wxID_ANY,
3843 					getInstrumentCaption(id), OCPN_DBP_STC_PITCH, _T("%2.1f"));
3844 				break;
3845 			case ID_DBP_I_HEEL:
3846 				instrument = new DashboardInstrument_Single(this, wxID_ANY,
3847 					getInstrumentCaption(id), OCPN_DBP_STC_HEEL, _T("%2.1f"));
3848                 break;
3849              // any clock display with "LCL" in the format string is converted from UTC to local TZ
3850             case ID_DBP_I_SUNLCL:
3851                 instrument = new DashboardInstrument_Sun( this, wxID_ANY,
3852                     getInstrumentCaption( id ), _T( "%02i:%02i:%02i LCL" ) );
3853                 break;
3854             case ID_DBP_I_GPSLCL:
3855                 instrument = new DashboardInstrument_Clock( this, wxID_ANY,
3856                     getInstrumentCaption( id ), OCPN_DBP_STC_CLK, _T( "%02i:%02i:%02i LCL" ) );
3857                 break;
3858             case ID_DBP_I_CPULCL:
3859                 instrument = new DashboardInstrument_CPUClock( this, wxID_ANY,
3860                     getInstrumentCaption( id ), _T( "%02i:%02i:%02i LCL" ) );
3861         }
3862         if( instrument ) {
3863             instrument->instrumentTypeId = id;
3864             m_ArrayOfInstrument.Add(
3865                     new DashboardInstrumentContainer( id, instrument,
3866                             instrument->GetCapacity() ) );
3867             itemBoxSizer->Add( instrument, 0, wxEXPAND, 0 );
3868             if( itemBoxSizer->GetOrientation() == wxHORIZONTAL ) {
3869                 itemBoxSizer->AddSpacer( 5 );
3870             }
3871         }
3872     }
3873 
3874     //  In the absense of any other hints, build the default instrument sizes by taking the
3875     //  calculated with of the first (and succeeding) instruments as hints for the next.
3876     //  So, best in default loads to start with an instrument that accurately calculates its minimum width.
3877     //  e.g. DashboardInstrument_Position
3878 
3879     wxSize Hint = wxSize(DefaultWidth, DefaultWidth);
3880     for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
3881         DashboardInstrument* inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
3882         inst->SetMinSize( inst->GetSize( itemBoxSizer->GetOrientation(), Hint ) );
3883         Hint = inst->GetMinSize();
3884     }
3885 
3886     Fit();
3887     Layout();
3888     SetMinSize( itemBoxSizer->GetMinSize() );
3889 }
3890 
SendSentenceToAllInstruments(int st,double value,wxString unit)3891 void DashboardWindow::SendSentenceToAllInstruments( int st, double value, wxString unit )
3892 {
3893     for( size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++ ) {
3894         if( m_ArrayOfInstrument.Item( i )->m_cap_flag & st ) m_ArrayOfInstrument.Item( i )->m_pInstrument->SetData(
3895                 st, value, unit );
3896     }
3897 }
3898 
SendSatInfoToAllInstruments(int cnt,int seq,SAT_INFO sats[4])3899 void DashboardWindow::SendSatInfoToAllInstruments( int cnt, int seq, SAT_INFO sats[4] )
3900 {
3901     for( size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++ ) {
3902         if( ( m_ArrayOfInstrument.Item( i )->m_cap_flag & OCPN_DBP_STC_GPS )
3903                 && m_ArrayOfInstrument.Item( i )->m_pInstrument->IsKindOf(
3904                         CLASSINFO(DashboardInstrument_GPS)))
3905                         ((DashboardInstrument_GPS*)m_ArrayOfInstrument.Item(i)->m_pInstrument)->SetSatInfo(cnt, seq, sats);
3906                     }
3907                 }
3908 
SendUtcTimeToAllInstruments(wxDateTime value)3909 void DashboardWindow::SendUtcTimeToAllInstruments( wxDateTime value )
3910 {
3911     for( size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++ ) {
3912         if( ( m_ArrayOfInstrument.Item( i )->m_cap_flag & OCPN_DBP_STC_CLK )
3913                 && m_ArrayOfInstrument.Item( i )->m_pInstrument->IsKindOf( CLASSINFO( DashboardInstrument_Clock ) ) )
3914 //                  || m_ArrayOfInstrument.Item( i )->m_pInstrument->IsKindOf( CLASSINFO( DashboardInstrument_Sun ) )
3915 //                  || m_ArrayOfInstrument.Item( i )->m_pInstrument->IsKindOf( CLASSINFO( DashboardInstrument_Moon ) ) ) )
3916             ((DashboardInstrument_Clock*)m_ArrayOfInstrument.Item(i)->m_pInstrument)->SetUtcTime( value );
3917     }
3918 }
3919 
3920 
3921 
3922 //#include "wx/fontpicker.h"
3923 
3924 //#include "wx/fontdlg.h"
3925 
3926 
3927 // ============================================================================
3928 // implementation
3929 // ============================================================================
3930 
IMPLEMENT_DYNAMIC_CLASS(OCPNFontButton,wxButton)3931 IMPLEMENT_DYNAMIC_CLASS(OCPNFontButton, wxButton)
3932 
3933 // ----------------------------------------------------------------------------
3934 // OCPNFontButton
3935 // ----------------------------------------------------------------------------
3936 
3937 bool OCPNFontButton::Create( wxWindow *parent, wxWindowID id,
3938                                   const wxFont &initial, const wxPoint &pos,
3939                                   const wxSize &size, long style,
3940                                   const wxValidator& validator, const wxString &name)
3941 {
3942     wxString label = (style & wxFNTP_FONTDESC_AS_LABEL) ?
3943     wxString() : // label will be updated by UpdateFont
3944     _("Choose font");
3945 
3946     // create this button
3947     if (!wxButton::Create( parent, id, label, pos,
3948         size, style, validator, name ))
3949     {
3950         wxFAIL_MSG( wxT("OCPNFontButton creation failed") );
3951         return false;
3952     }
3953 
3954     // and handle user clicks on it
3955     Connect(GetId(), wxEVT_BUTTON,
3956             wxCommandEventHandler(OCPNFontButton::OnButtonClick),
3957             NULL, this);
3958 
3959     InitFontData();
3960 
3961     m_selectedFont = initial.IsOk() ? initial : *wxNORMAL_FONT;
3962     UpdateFont();
3963 
3964     return true;
3965 }
3966 
InitFontData()3967 void OCPNFontButton::InitFontData()
3968 {
3969     m_data.SetAllowSymbols(true);
3970     m_data.SetColour(*wxBLACK);
3971     m_data.EnableEffects(true);
3972 }
3973 
OnButtonClick(wxCommandEvent & WXUNUSED (ev))3974 void OCPNFontButton::OnButtonClick(wxCommandEvent& WXUNUSED(ev))
3975 {
3976     // update the wxFontData to be shown in the dialog
3977     m_data.SetInitialFont(m_selectedFont);
3978 
3979     // create the font dialog and display it
3980     wxFontDialog dlg(this, m_data);
3981 
3982     wxFont *pF = OCPNGetFont(_T("Dialog"), 0);
3983     dlg.SetFont( *pF );
3984 
3985 #ifdef __WXQT__
3986     // Make sure that font dialog will fit on the screen without scrolling
3987     // We do this by setting the dialog font size "small enough" to show "n" lines
3988     wxSize proposed_size = GetParent()->GetSize();
3989     float n_lines = 30;
3990     float font_size = pF->GetPointSize();
3991 
3992     if ( ( proposed_size.y / font_size ) < n_lines ) {
3993         float new_font_size = proposed_size.y / n_lines;
3994         wxFont *smallFont = new wxFont( *pF );
3995         smallFont->SetPointSize( new_font_size );
3996         dlg.SetFont( *smallFont );
3997     }
3998 
3999     dlg.SetSize(GetParent()->GetSize());
4000     dlg.Centre();
4001 #endif
4002 
4003 
4004 
4005 
4006     if (dlg.ShowModal() == wxID_OK)
4007     {
4008         m_data = dlg.GetFontData();
4009         m_selectedFont = m_data.GetChosenFont();
4010 
4011         // fire an event
4012         wxFontPickerEvent event(this, GetId(), m_selectedFont);
4013         GetEventHandler()->ProcessEvent(event);
4014 
4015         UpdateFont();
4016     }
4017 }
4018 
UpdateFont()4019 void OCPNFontButton::UpdateFont()
4020 {
4021     if ( !m_selectedFont.IsOk() )
4022         return;
4023 
4024     //  Leave black, until Instruments are modified to accept color fonts
4025     //SetForegroundColour(m_data.GetColour());
4026 
4027     if (HasFlag(wxFNTP_USEFONT_FOR_LABEL))
4028     {
4029         // use currently selected font for the label...
4030         wxButton::SetFont(m_selectedFont);
4031     }
4032 
4033     wxString label = wxString::Format(wxT("%s, %d"),
4034                                   m_selectedFont.GetFaceName().c_str(),
4035                                   m_selectedFont.GetPointSize());
4036 
4037     if (HasFlag(wxFNTP_FONTDESC_AS_LABEL))
4038     {
4039         SetLabel(label);
4040     }
4041 
4042     auto minsize = GetTextExtent(label);
4043     SetSize(minsize);
4044 
4045     GetParent()->Layout();
4046 
4047 }
4048 
4049