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