OpenCPN Partial API docs
Loading...
Searching...
No Matches
dashboard_pi.cpp
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 * expanded: Bernd Cirotzki 2023 (special colour design)
8 *
9 ***************************************************************************
10 * Copyright (C) 2010 by David S. Register *
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with this program; if not, write to the *
24 * Free Software Foundation, Inc., *
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
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 <assert.h>
36#include <cmath>
37// xw 2.8
38#include <wx/filename.h>
39#include <wx/fontdlg.h>
40
41#include <typeinfo>
42#include "dashboard_pi.h"
43#include "icons.h"
44#include "wx/jsonreader.h"
45#include "wx/jsonwriter.h"
46#include "N2KParser.h"
47#include "../../../gui/include/gui/ocpn_fontdlg.h"
48#include "manual.h"
49
50wxFontData *g_pFontTitle;
51wxFontData *g_pFontData;
52wxFontData *g_pFontLabel;
53wxFontData *g_pFontSmall;
54
55wxFontData g_FontTitle;
56wxFontData g_FontData;
57wxFontData g_FontLabel;
58wxFontData g_FontSmall;
59
60wxFontData *g_pUSFontTitle;
61wxFontData *g_pUSFontData;
62wxFontData *g_pUSFontLabel;
63wxFontData *g_pUSFontSmall;
64
65wxFontData g_USFontTitle;
66wxFontData g_USFontData;
67wxFontData g_USFontLabel;
68wxFontData g_USFontSmall;
69
70int g_iDashSpeedMax;
71int g_iDashCOGDamp;
72int g_iDashSpeedUnit;
73int g_iDashSOGDamp;
74int g_iDashAWADamp;
75int g_iDashAWSDamp;
76int g_iDashDepthUnit;
77int g_iDashDistanceUnit;
78int g_iDashWindSpeedUnit;
79int g_iUTCOffset;
80double g_dDashDBTOffset;
81bool g_bUseInternSumLog;
82double g_dSumLogNM;
83double d_tripNM;
84int logCount, confprint;
85bool g_bDBtrueWindGround;
86double g_dHDT;
87double g_dSOG, g_dCOG;
88int g_iDashTempUnit;
89int g_dashPrefWidth, g_dashPrefHeight;
90
91wxColor g_BackgroundColor;
92bool g_ForceBackgroundColor;
93wxAlignment g_TitleAlignment;
94double g_TitleVerticalOffset;
95int g_iTitleMargin;
96bool g_bShowUnit;
97wxAlignment g_DataAlignment;
98int g_iDataMargin;
99int g_iInstrumentSpacing;
100
101PI_ColorScheme aktuellColorScheme;
102#if !defined(NAN)
103static const long long lNaN = 0xfff8000000000000;
104#define NAN (*(double *)&lNaN)
105#endif
106
107#ifdef __OCPN__ANDROID__
108#include "qdebug.h"
109#endif
110
111// the class factories, used to create and destroy instances of the PlugIn
112
113extern "C" DECL_EXP opencpn_plugin *create_pi(void *ppimgr) {
114 return (opencpn_plugin *)new dashboard_pi(ppimgr);
115}
116
117extern "C" DECL_EXP void destroy_pi(opencpn_plugin *p) { delete p; }
118
119#ifdef __OCPN__ANDROID__
120
121QString qtStyleSheet =
122 "QScrollBar:horizontal {\
123border: 0px solid grey;\
124background-color: rgb(240, 240, 240);\
125height: 35px;\
126margin: 0px 1px 0 1px;\
127}\
128QScrollBar::handle:horizontal {\
129background-color: rgb(200, 200, 200);\
130min-width: 20px;\
131border-radius: 10px;\
132}\
133QScrollBar::add-line:horizontal {\
134border: 0px solid grey;\
135background: #32CC99;\
136width: 0px;\
137subcontrol-position: right;\
138subcontrol-origin: margin;\
139}\
140QScrollBar::sub-line:horizontal {\
141border: 0px solid grey;\
142background: #32CC99;\
143width: 0px;\
144subcontrol-position: left;\
145subcontrol-origin: margin;\
146}\
147QScrollBar:vertical {\
148border: 0px solid grey;\
149background-color: rgb(240, 240, 240);\
150width: 35px;\
151margin: 1px 0px 1px 0px;\
152}\
153QScrollBar::handle:vertical {\
154background-color: rgb(200, 200, 200);\
155min-height: 20px;\
156border-radius: 10px;\
157}\
158QScrollBar::add-line:vertical {\
159border: 0px solid grey;\
160background: #32CC99;\
161height: 0px;\
162subcontrol-position: top;\
163subcontrol-origin: margin;\
164}\
165QScrollBar::sub-line:vertical {\
166border: 0px solid grey;\
167background: #32CC99;\
168height: 0px;\
169subcontrol-position: bottom;\
170subcontrol-origin: margin;\
171}\
172QCheckBox {\
173spacing: 25px;\
174}\
175QCheckBox::indicator {\
176width: 30px;\
177height: 30px;\
178}\
179";
180
181#endif
182
183#ifdef __OCPN__ANDROID__
184#include <QtWidgets/QScroller>
185#endif
186
187//---------------------------------------------------------------------------------------------------------
188//
189// Dashboard PlugIn Implementation
190//
191//---------------------------------------------------------------------------------------------------------
192// !!! WARNING !!!
193// do not change the order, add new instruments at the end, before
194// ID_DBP_LAST_ENTRY! otherwise, for users with an existing opencpn.ini file,
195// their instruments are changing !
196enum {
197 ID_DBP_I_POS,
198 ID_DBP_I_SOG,
199 ID_DBP_D_SOG,
200 ID_DBP_I_COG,
201 ID_DBP_D_COG,
202 ID_DBP_I_STW,
203 ID_DBP_I_HDT,
204 ID_DBP_D_AW,
205 ID_DBP_D_AWA,
206 ID_DBP_I_AWS,
207 ID_DBP_D_AWS,
208 ID_DBP_D_TW,
209 ID_DBP_I_DPT,
210 ID_DBP_D_DPT,
211 ID_DBP_I_TMP,
212 ID_DBP_I_VMG,
213 ID_DBP_D_VMG,
214 ID_DBP_I_RSA,
215 ID_DBP_D_RSA,
216 ID_DBP_I_SAT,
217 ID_DBP_D_GPS,
218 ID_DBP_I_PTR,
219 ID_DBP_I_GPSUTC,
220 ID_DBP_I_SUN,
221 ID_DBP_D_MON,
222 ID_DBP_I_ATMP,
223 ID_DBP_I_AWA,
224 ID_DBP_I_TWA,
225 ID_DBP_I_TWD,
226 ID_DBP_I_TWS,
227 ID_DBP_D_TWD,
228 ID_DBP_I_HDM,
229 ID_DBP_D_HDT,
230 ID_DBP_D_WDH,
231 ID_DBP_I_VLW1,
232 ID_DBP_I_VLW2,
233 ID_DBP_D_MDA,
234 ID_DBP_I_MDA,
235 ID_DBP_D_BPH,
236 ID_DBP_I_FOS,
237 ID_DBP_M_COG,
238 ID_DBP_I_PITCH,
239 ID_DBP_I_HEEL,
240 ID_DBP_D_AWA_TWA,
241 ID_DBP_I_GPSLCL,
242 ID_DBP_I_CPULCL,
243 ID_DBP_I_SUNLCL,
244 ID_DBP_I_ALTI,
245 ID_DBP_D_ALTI,
246 ID_DBP_I_VMGW,
247 ID_DBP_I_HUM,
248 ID_DBP_D_STW,
249 ID_DBP_I_WCC,
250 ID_DBP_LAST_ENTRY // this has a reference in one of the routines; defining a
251 // "LAST_ENTRY" and setting the reference to it, is one
252 // codeline less to change (and find) when adding new
253 // instruments :-)
254};
255
256bool IsObsolete(int id) {
257 switch (id) {
258 case ID_DBP_D_AWA:
259 return true;
260 default:
261 return false;
262 }
263}
264
265wxString getInstrumentCaption(unsigned int id) {
266 switch (id) {
267 case ID_DBP_I_POS:
268 return _("Position");
269 case ID_DBP_I_SOG:
270 return _("SOG");
271 case ID_DBP_D_SOG:
272 return _("Speed SOG");
273 case ID_DBP_D_STW:
274 return _("Speed STW");
275 case ID_DBP_I_COG:
276 return _("COG");
277 case ID_DBP_M_COG:
278 return _("Mag COG");
279 case ID_DBP_D_COG:
280 return _("GNSS Compass");
281 case ID_DBP_D_HDT:
282 return _("True Compass");
283 case ID_DBP_I_STW:
284 return _("STW");
285 case ID_DBP_I_HDT:
286 return _("True HDG");
287 case ID_DBP_I_HDM:
288 return _("Mag HDG");
289 case ID_DBP_D_AW:
290 case ID_DBP_D_AWA:
291 return _("App. Wind Angle & Speed");
292 case ID_DBP_D_AWA_TWA:
293 return _("App & True Wind Angle");
294 case ID_DBP_I_AWS:
295 return _("App. Wind Speed");
296 case ID_DBP_D_AWS:
297 return _("App. Wind Speed");
298 case ID_DBP_D_TW:
299 return _("True Wind Angle & Speed");
300 case ID_DBP_I_ALTI:
301 return _("Altitude");
302 case ID_DBP_D_ALTI:
303 return _("Altitude Trace");
304 case ID_DBP_I_DPT:
305 return _("Depth");
306 case ID_DBP_D_DPT:
307 return _("Depth");
308 case ID_DBP_D_MDA:
309 return _("Barometric pressure");
310 case ID_DBP_I_MDA:
311 return _("Barometric pressure");
312 case ID_DBP_I_TMP:
313 return _("Water Temp.");
314 case ID_DBP_I_ATMP:
315 return _("Air Temp.");
316 case ID_DBP_I_AWA:
317 return _("App. Wind Angle");
318 case ID_DBP_I_TWA:
319 return _("True Wind Angle");
320 case ID_DBP_I_TWD:
321 return _("True Wind Direction");
322 case ID_DBP_I_TWS:
323 return _("True Wind Speed");
324 case ID_DBP_D_TWD:
325 return _("True Wind Dir. & Speed");
326 case ID_DBP_I_VMG:
327 return _("VMG");
328 case ID_DBP_D_VMG:
329 return _("VMG");
330 case ID_DBP_I_VMGW:
331 return _("VMG Wind");
332 case ID_DBP_I_RSA:
333 return _("Rudder Angle");
334 case ID_DBP_D_RSA:
335 return _("Rudder Angle");
336 case ID_DBP_I_SAT:
337 return _("GNSS in use");
338 case ID_DBP_D_GPS:
339 return _("GNSS Status");
340 case ID_DBP_I_PTR:
341 return _("Cursor");
342 case ID_DBP_I_GPSUTC:
343 return _("GNSS Clock");
344 case ID_DBP_I_SUN:
345 return _("Sunrise/Sunset");
346 case ID_DBP_D_MON:
347 return _("Moon phase");
348 case ID_DBP_D_WDH:
349 return _("Wind history");
350 case ID_DBP_D_BPH:
351 return _("Barometric history");
352 case ID_DBP_I_VLW1:
353 return _("Trip Log");
354 case ID_DBP_I_VLW2:
355 return _("Sum Log");
356 case ID_DBP_I_FOS:
357 return _("From Ownship");
358 case ID_DBP_I_PITCH:
359 return _("Pitch");
360 case ID_DBP_I_HEEL:
361 return _("Heel");
362 case ID_DBP_I_GPSLCL:
363 return _("Local GNSS Clock");
364 case ID_DBP_I_CPULCL:
365 return _("Local CPU Clock");
366 case ID_DBP_I_SUNLCL:
367 return _("Local Sunrise/Sunset");
368 case ID_DBP_I_HUM:
369 return _("Humidity");
370 case ID_DBP_I_WCC:
371 return _("Windlass");
372 }
373 return _T("");
374}
375
376void getListItemForInstrument(wxListItem &item, unsigned int id) {
377 item.SetData(id);
378 item.SetText(getInstrumentCaption(id));
379 switch (id) {
380 case ID_DBP_I_POS:
381 case ID_DBP_I_SOG:
382 case ID_DBP_I_COG:
383 case ID_DBP_M_COG:
384 case ID_DBP_I_STW:
385 case ID_DBP_I_HDT:
386 case ID_DBP_I_HDM:
387 case ID_DBP_I_AWS:
388 case ID_DBP_I_DPT:
389 case ID_DBP_I_MDA:
390 case ID_DBP_I_TMP:
391 case ID_DBP_I_ATMP:
392 case ID_DBP_I_TWA:
393 case ID_DBP_I_TWD:
394 case ID_DBP_I_TWS:
395 case ID_DBP_I_AWA:
396 case ID_DBP_I_VMG:
397 case ID_DBP_I_VMGW:
398 case ID_DBP_I_RSA:
399 case ID_DBP_I_SAT:
400 case ID_DBP_I_PTR:
401 case ID_DBP_I_GPSUTC:
402 case ID_DBP_I_GPSLCL:
403 case ID_DBP_I_CPULCL:
404 case ID_DBP_I_SUN:
405 case ID_DBP_I_SUNLCL:
406 case ID_DBP_I_VLW1:
407 case ID_DBP_I_VLW2:
408 case ID_DBP_I_FOS:
409 case ID_DBP_I_PITCH:
410 case ID_DBP_I_HEEL:
411 case ID_DBP_I_ALTI:
412 case ID_DBP_I_HUM:
413 case ID_DBP_I_WCC:
414 item.SetImage(0);
415 break;
416 case ID_DBP_D_SOG:
417 case ID_DBP_D_STW:
418 case ID_DBP_D_COG:
419 case ID_DBP_D_AW:
420 case ID_DBP_D_AWA:
421 case ID_DBP_D_AWS:
422 case ID_DBP_D_TW:
423 case ID_DBP_D_AWA_TWA:
424 case ID_DBP_D_TWD:
425 case ID_DBP_D_DPT:
426 case ID_DBP_D_MDA:
427 case ID_DBP_D_VMG:
428 case ID_DBP_D_RSA:
429 case ID_DBP_D_GPS:
430 case ID_DBP_D_HDT:
431 case ID_DBP_D_MON:
432 case ID_DBP_D_WDH:
433 case ID_DBP_D_BPH:
434 case ID_DBP_D_ALTI:
435 item.SetImage(1);
436 break;
437 }
438}
439
440/* These two function were taken from gpxdocument.cpp */
441int GetRandomNumber(int range_min, int range_max) {
442 long u = (long)wxRound(
443 ((double)rand() / ((double)(RAND_MAX) + 1) * (range_max - range_min)) +
444 range_min);
445 return (int)u;
446}
447
448// RFC4122 version 4 compliant random UUIDs generator.
449wxString GetUUID(void) {
450 wxString str;
451 struct {
452 int time_low;
453 int time_mid;
454 int time_hi_and_version;
455 int clock_seq_hi_and_rsv;
456 int clock_seq_low;
457 int node_hi;
458 int node_low;
459 } uuid;
460
461 uuid.time_low = GetRandomNumber(
462 0, 2147483647); // FIXME: the max should be set to something like
463 // MAXINT32, but it doesn't compile un gcc...
464 uuid.time_mid = GetRandomNumber(0, 65535);
465 uuid.time_hi_and_version = GetRandomNumber(0, 65535);
466 uuid.clock_seq_hi_and_rsv = GetRandomNumber(0, 255);
467 uuid.clock_seq_low = GetRandomNumber(0, 255);
468 uuid.node_hi = GetRandomNumber(0, 65535);
469 uuid.node_low = GetRandomNumber(0, 2147483647);
470
471 /* Set the two most significant bits (bits 6 and 7) of the
472 * clock_seq_hi_and_rsv to zero and one, respectively. */
473 uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80;
474
475 /* Set the four most significant bits (bits 12 through 15) of the
476 * time_hi_and_version field to 4 */
477 uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000;
478
479 str.Printf(_T("%08x-%04x-%04x-%02x%02x-%04x%08x"), uuid.time_low,
480 uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq_hi_and_rsv,
481 uuid.clock_seq_low, uuid.node_hi, uuid.node_low);
482
483 return str;
484}
485
486wxString MakeName() { return _T("DASH_") + GetUUID(); }
487
488//---------------------------------------------------------------------------------------------------------
489//
490// PlugIn initialization and de-init
491//
492//---------------------------------------------------------------------------------------------------------
493
494dashboard_pi::dashboard_pi(void *ppimgr)
495 : wxTimer(this), opencpn_plugin_18(ppimgr) {
496 // Create the PlugIn icons
497 initialize_images();
498 // Initialize the infinite impulse response (IIR) filters
499 mCOGFilter.setType(IIRFILTER_TYPE_DEG);
500 mAWAFilter.setType(IIRFILTER_TYPE_DEG);
501 mAWSFilter.setType(IIRFILTER_TYPE_LINEAR);
502}
503
504dashboard_pi::~dashboard_pi(void) {
505 delete _img_dashboard_pi;
506 delete _img_dashboard;
507 delete _img_dial;
508 delete _img_instrument;
509 delete _img_minus;
510 delete _img_plus;
511}
512
514 AddLocaleCatalog(_T("opencpn-dashboard_pi"));
515
516 mVar = NAN;
517 mPriPosition = 99;
518 mPriCOGSOG = 99;
519 mPriHeadingT = 99; // True heading
520 mPriHeadingM = 99; // Magnetic heading
521 mPriVar = 99;
522 mPriDateTime = 99;
523 mPriAWA = 99; // Relative wind
524 mPriTWA = 99; // True wind
525 mPriWDN = 99; // True hist. wind
526 mPriMDA = 99; // Air press
527 mPriDepth = 99;
528 mPriSTW = 99;
529 mPriWTP = 99; // Water temp
530 mPriATMP = 99; // Air temp
531 mPriSatStatus = 99;
532 mPriSatUsed = 99;
533 mSatsInView = 0;
534 mPriAlt = 99;
535 mPriRSA = 99; // Rudder angle
536 mPriPitchRoll = 99; // Pitch and roll
537 mPriHUM = 99; // Humidity
538 m_config_version = -1;
539 mHDx_Watchdog = 2;
540 mHDT_Watchdog = 2;
541 mSatsUsed_Wdog = 2;
542 mSatStatus_Wdog = 2;
543 m_PriN2kTalker = 2;
544 mVar_Watchdog = 2;
545 mMWVA_Watchdog = 2;
546 mMWVT_Watchdog = 2;
547 mDPT_DBT_Watchdog = 2; // Depth
548 mSTW_Watchdog = 2;
549 mWTP_Watchdog = 2;
550 mRSA_Watchdog = 2;
551 mVMG_Watchdog = 2;
552 mVMGW_Watchdog = 2;
553 mUTC_Watchdog = 2;
554 mATMP_Watchdog = 2;
555 mWDN_Watchdog = 2;
556 mMDA_Watchdog = 2;
557 mPITCH_Watchdog = 2;
558 mHEEL_Watchdog = 2;
559 mALT_Watchdog = 2;
560 mLOG_Watchdog = 2;
561 mTrLOG_Watchdog = 2;
562 mHUM_Watchdog = 2;
563 mWCC_Watchdog = 2;
564
565 g_pFontTitle = new wxFontData();
566 g_pFontTitle->SetChosenFont(
567 wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
568
569 g_pFontData = new wxFontData();
570 g_pFontData->SetChosenFont(
571 wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
572
573 g_pFontLabel = new wxFontData();
574 g_pFontLabel->SetChosenFont(
575 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
576
577 g_pFontSmall = new wxFontData();
578 g_pFontSmall->SetChosenFont(
579 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
580
581 g_pUSFontTitle = &g_USFontTitle;
582 g_pUSFontData = &g_USFontData;
583 g_pUSFontLabel = &g_USFontLabel;
584 g_pUSFontSmall = &g_USFontSmall;
585
586 m_pauimgr = GetFrameAuiManager();
587 m_pauimgr->Connect(wxEVT_AUI_PANE_CLOSE,
588 wxAuiManagerEventHandler(dashboard_pi::OnPaneClose), NULL,
589 this);
590
591 // Get a pointer to the opencpn configuration object
592 m_pconfig = GetOCPNConfigObject();
593
594 // And load the configuration items
595 LoadConfig();
596
597 // This PlugIn needs a toolbar icon
598 // m_toolbar_item_id = InsertPlugInTool( _T(""), _img_dashboard,
599 // _img_dashboard, wxITEM_CHECK,
600 // _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this
601 // );
602
603 wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") +
604 wxFileName::GetPathSeparator() + _T("dashboard_pi") +
605 wxFileName::GetPathSeparator() + _T("data") +
606 wxFileName::GetPathSeparator();
607
608 wxString normalIcon = shareLocn + _T("Dashboard.svg");
609 wxString toggledIcon = shareLocn + _T("Dashboard_toggled.svg");
610 wxString rolloverIcon = shareLocn + _T("Dashboard_rollover.svg");
611
612 // For journeyman styles, we prefer the built-in raster icons which match the
613 // rest of the toolbar.
614 if (GetActiveStyleName().Lower() != _T("traditional")) {
615 normalIcon = _T("");
616 toggledIcon = _T("");
617 rolloverIcon = _T("");
618 }
619
620 m_toolbar_item_id = InsertPlugInToolSVG(
621 _T(""), normalIcon, rolloverIcon, toggledIcon, wxITEM_CHECK,
622 _("Dashboard"), _T(""), NULL, DASHBOARD_TOOL_POSITION, 0, this);
623
624 ApplyConfig();
625
626 // If we loaded a version 1 config setup, convert now to version 2
627 if (m_config_version == 1) {
628 SaveConfig();
629 }
630
631 d_tripNM = 0.0;
632 logCount = confprint = 0;
633 // If use internal sumlog update its value
634 if (g_bUseInternSumLog) UpdateSumLog(false);
635
636 // initialize NavMsg listeners
637 //-----------------------------
638
639 // Rudder data PGN 127245
640 wxDEFINE_EVENT(EVT_N2K_127245, ObservedEvt);
641 NMEA2000Id id_127245 = NMEA2000Id(127245);
642 listener_127245 = GetListener(id_127245, EVT_N2K_127245, this);
643 Bind(EVT_N2K_127245, [&](ObservedEvt ev) { HandleN2K_127245(ev); });
644
645 // Roll Pitch PGN 127257
646 wxDEFINE_EVENT(EVT_N2K_127257, ObservedEvt);
647 NMEA2000Id id_127257 = NMEA2000Id(127257);
648 listener_127257 = GetListener(id_127257, EVT_N2K_127257, this);
649 Bind(EVT_N2K_127257, [&](ObservedEvt ev) { HandleN2K_127257(ev); });
650
651 // Speed through water PGN 128259
652 wxDEFINE_EVENT(EVT_N2K_128259, ObservedEvt);
653 NMEA2000Id id_128259 = NMEA2000Id(128259);
654 listener_128259 = GetListener(id_128259, EVT_N2K_128259, this);
655 Bind(EVT_N2K_128259, [&](ObservedEvt ev) { HandleN2K_128259(ev); });
656
657 // Depth Data PGN 128267
658 wxDEFINE_EVENT(EVT_N2K_128267, ObservedEvt);
659 NMEA2000Id id_128267 = NMEA2000Id(128267);
660 listener_128267 = GetListener(id_128267, EVT_N2K_128267, this);
661 Bind(EVT_N2K_128267, [&](ObservedEvt ev) { HandleN2K_128267(ev); });
662
663 // Distance log
664 wxDEFINE_EVENT(EVT_N2K_128275, ObservedEvt);
665 NMEA2000Id id_128275 = NMEA2000Id(128275);
666 listener_128275 = GetListener(id_128275, EVT_N2K_128275, this);
667 Bind(EVT_N2K_128275, [&](ObservedEvt ev) { HandleN2K_128275(ev); });
668
669 // Windlass count
670 wxDEFINE_EVENT(EVT_N2K_128777, ObservedEvt);
671 NMEA2000Id id_128777 = NMEA2000Id(128777);
672 listener_128777 = GetListener(id_128777, EVT_N2K_128777, this);
673 Bind(EVT_N2K_128777, [&](ObservedEvt ev) { HandleN2K_128777(ev); });
674
675 // GNSS Position Data PGN 129029
676 wxDEFINE_EVENT(EVT_N2K_129029, ObservedEvt);
677 NMEA2000Id id_129029 = NMEA2000Id(129029);
678 listener_129029 = GetListener(id_129029, EVT_N2K_129029, this);
679 Bind(EVT_N2K_129029, [&](ObservedEvt ev) { HandleN2K_129029(ev); });
680
681 // GNSS Satellites in View PGN 129540
682 wxDEFINE_EVENT(EVT_N2K_129540, ObservedEvt);
683 NMEA2000Id id_129540 = NMEA2000Id(129540);
684 listener_129540 = GetListener(id_129540, EVT_N2K_129540, this);
685 Bind(EVT_N2K_129540, [&](ObservedEvt ev) { HandleN2K_129540(ev); });
686
687 // Wind PGN 130306
688 wxDEFINE_EVENT(EVT_N2K_130306, ObservedEvt);
689 NMEA2000Id id_130306 = NMEA2000Id(130306);
690 listener_130306 = GetListener(id_130306, EVT_N2K_130306, this);
691 Bind(EVT_N2K_130306, [&](ObservedEvt ev) { HandleN2K_130306(ev); });
692
693 // Envorinment PGN 130310
694 wxDEFINE_EVENT(EVT_N2K_130310, ObservedEvt);
695 NMEA2000Id id_130310 = NMEA2000Id(130310);
696 listener_130310 = GetListener(id_130310, EVT_N2K_130310, this);
697 Bind(EVT_N2K_130310, [&](ObservedEvt ev) { HandleN2K_130310(ev); });
698
699 // Envorinment PGN 130313
700 wxDEFINE_EVENT(EVT_N2K_130313, ObservedEvt);
701 NMEA2000Id id_130313 = NMEA2000Id(130313);
702 listener_130313 = GetListener(id_130313, EVT_N2K_130313, this);
703 Bind(EVT_N2K_130313, [&](ObservedEvt ev) { HandleN2K_130313(ev); });
704
705 Start(1000, wxTIMER_CONTINUOUS);
706
710}
711
713 SaveConfig();
714 if (IsRunning()) // Timer started?
715 Stop(); // Stop timer
716
717 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
718 DashboardWindow *dashboard_window =
719 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
720 if (dashboard_window) {
721 m_pauimgr->DetachPane(dashboard_window);
722 dashboard_window->Close();
723 dashboard_window->Destroy();
724 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow = NULL;
725 }
726 }
727
728 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
729 DashboardWindowContainer *pdwc = m_ArrayOfDashboardWindow.Item(i);
730 delete pdwc;
731 }
732
733 // delete g_pFontTitle;
734 // delete g_pFontData;
735 // delete g_pFontLabel;
736 // delete g_pFontSmall;
737
738 return true;
739}
740
741double GetJsonDouble(wxJSONValue &value) {
742 double d_ret = 0;
743 if (value.IsDouble()) {
744 d_ret = value.AsDouble();
745 return d_ret;
746 } else if (value.IsLong()) {
747 int i_ret = value.AsLong();
748 d_ret = i_ret;
749 return d_ret;
750 }
751 return nan("");
752}
753
754void dashboard_pi::Notify() {
755 SendUtcTimeToAllInstruments(mUTCDateTime);
756 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
757 DashboardWindow *dashboard_window =
758 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
759 if (dashboard_window) {
760 dashboard_window->Refresh();
761#ifdef __OCPN__ANDROID__
762 wxWindowList list = dashboard_window->GetChildren();
763 wxWindowListNode *node = list.GetFirst();
764 for (size_t i = 0; i < list.GetCount(); i++) {
765 wxWindow *win = node->GetData();
766 // qDebug() << "Refresh Dash child:" << i;
767 win->Refresh();
768 node = node->GetNext();
769 }
770#endif
771 }
772 }
773 // Manage the watchdogs
774
775 mHDx_Watchdog--;
776 if (mHDx_Watchdog <= 0) {
777 mHdm = NAN;
778 mPriHeadingM = 99;
779 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm, _T("\u00B0"));
780 mHDx_Watchdog = gps_watchdog_timeout_ticks;
781 }
782
783 mHDT_Watchdog--;
784 if (mHDT_Watchdog <= 0) {
785 mPriHeadingT = 99;
786 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, NAN, _T("\u00B0T"));
787 mHDT_Watchdog = gps_watchdog_timeout_ticks;
788 }
789
790 mVar_Watchdog--;
791 if (mVar_Watchdog <= 0) {
792 mVar = NAN;
793 mPriVar = 99;
794 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, NAN, _T("\u00B0T"));
795 mVar_Watchdog = gps_watchdog_timeout_ticks;
796 }
797
798 mSatsUsed_Wdog--;
799 if (mSatsUsed_Wdog <= 0) {
800 mPriSatUsed = 99;
801 mSatsInUse = 0;
802 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, NAN, _T(""));
803 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
804 }
805 m_PriN2kTalker--;
806 if (m_PriN2kTalker < -1e6) m_PriN2kTalker = 0;
807
808 mSatStatus_Wdog--;
809 if (mSatStatus_Wdog <= 0) {
810 SAT_INFO sats[4];
811 for (int i = 0; i < 4; i++) {
812 sats[i].SatNumber = 0;
813 sats[i].SignalToNoiseRatio = 0;
814 }
815 SendSatInfoToAllInstruments(0, 1, wxEmptyString, sats);
816 SendSatInfoToAllInstruments(0, 2, wxEmptyString, sats);
817 SendSatInfoToAllInstruments(0, 3, wxEmptyString, sats);
818 mPriSatStatus = 99;
819 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
820 }
821 // Set Satellite Status data from the same source as OCPN use for position
822 // Get the identifiers
823 std::vector<std::string> PriorityIDs = GetActivePriorityIdentifiers();
824 // Get current satellite priority identifier = item 4
825 // Exclude "address" after ':' that may equal Protokoll
826 std::string satID = PriorityIDs[4].substr(0, PriorityIDs[4].find(':'));
827 if (satID.find("nmea0183") != std::string::npos)
828 mPriSatStatus = 3; // GSV
829 else if (satID.find("ignal") != std::string::npos)
830 mPriSatStatus = 2; // SignalK
831 else if (satID.find("nmea2000") != std::string::npos) {
832 prioN2kPGNsat = PriorityIDs[4];
833 mPriSatStatus = 1; // N2k
834 }
835
836 mMWVA_Watchdog--;
837 if (mMWVA_Watchdog <= 0) {
838 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA, NAN, _T("-"));
839 SendSentenceToAllInstruments(OCPN_DBP_STC_AWS, NAN, _T("-"));
840 mPriAWA = 99;
841 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
842 }
843
844 mMWVT_Watchdog--;
845 if (mMWVT_Watchdog <= 0) {
846 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, NAN, _T("-"));
847 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS, NAN, _T("-"));
848 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2, NAN, _T("-"));
849 mPriTWA = 99;
850 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
851 }
852
853 mDPT_DBT_Watchdog--;
854 if (mDPT_DBT_Watchdog <= 0) {
855 mPriDepth = 99;
856 SendSentenceToAllInstruments(OCPN_DBP_STC_DPT, NAN, _T("-"));
857 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
858 }
859
860 mSTW_Watchdog--;
861 if (mSTW_Watchdog <= 0) {
862 mPriSTW = 99;
863 SendSentenceToAllInstruments(OCPN_DBP_STC_STW, NAN, _T("-"));
864 mSTW_Watchdog = gps_watchdog_timeout_ticks;
865 }
866
867 mWTP_Watchdog--;
868 if (mWTP_Watchdog <= 0) {
869 mPriWTP = 99;
870 SendSentenceToAllInstruments(OCPN_DBP_STC_TMP, NAN, "-");
871 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
872 }
873 mRSA_Watchdog--;
874 if (mRSA_Watchdog <= 0) {
875 mPriRSA = 99;
876 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, NAN, "-");
877 mRSA_Watchdog = gps_watchdog_timeout_ticks;
878 }
879 mVMG_Watchdog--;
880 if (mVMG_Watchdog <= 0) {
881 SendSentenceToAllInstruments(OCPN_DBP_STC_VMG, NAN, "-");
882 mVMG_Watchdog = gps_watchdog_timeout_ticks;
883 }
884 mVMGW_Watchdog--;
885 if (mVMGW_Watchdog <= 0) {
886 SendSentenceToAllInstruments(OCPN_DBP_STC_VMGW, NAN, "-");
887 mVMGW_Watchdog = gps_watchdog_timeout_ticks;
888 }
889 mUTC_Watchdog--;
890 if (mUTC_Watchdog <= 0) {
891 mPriDateTime = 99;
892 mUTC_Watchdog = gps_watchdog_timeout_ticks;
893 }
894 mATMP_Watchdog--;
895 if (mATMP_Watchdog <= 0) {
896 SendSentenceToAllInstruments(OCPN_DBP_STC_ATMP, NAN, "-");
897 mPriATMP = 99;
898 mATMP_Watchdog = gps_watchdog_timeout_ticks;
899 }
900 mWDN_Watchdog--;
901 if (mWDN_Watchdog <= 0) {
902 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, NAN, _T("-"));
903 mPriWDN = 99;
904 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
905 }
906 mMDA_Watchdog--;
907 if (mMDA_Watchdog <= 0) {
908 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, NAN, _T("-"));
909 mPriMDA = 99;
910 mMDA_Watchdog = gps_watchdog_timeout_ticks;
911 }
912 mPITCH_Watchdog--;
913 if (mPITCH_Watchdog <= 0) {
914 mPriPitchRoll = 99;
915 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, NAN, _T("-"));
916 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
917 }
918 mHEEL_Watchdog--;
919 if (mHEEL_Watchdog <= 0) {
920 mPriPitchRoll = 99;
921 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, NAN, _T("-"));
922 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
923 }
924 mALT_Watchdog--;
925 if (mALT_Watchdog <= 0) {
926 mPriAlt = 99;
927 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, NAN, _T("-"));
928 mALT_Watchdog = gps_watchdog_timeout_ticks;
929 }
930
931 mLOG_Watchdog--;
932 if (mLOG_Watchdog <= 0) {
933 if (g_bUseInternSumLog) {
934 UpdateSumLog(false);
935 } else {
936 SendSentenceToAllInstruments(OCPN_DBP_STC_VLW2, NAN, _T("-"));
937 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
938 }
939 }
940 mTrLOG_Watchdog--;
941 if (mTrLOG_Watchdog <= 0) {
942 SendSentenceToAllInstruments(OCPN_DBP_STC_VLW1, NAN, _T("-"));
943 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
944 }
945 mHUM_Watchdog--;
946 if (mHUM_Watchdog <= 0) {
947 mPriHUM = 99;
948 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, NAN, _T("-"));
949 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
950 }
951 mWCC_Watchdog--;
952 if (mWCC_Watchdog <= 0) {
953 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, NAN, _T("-"));
954 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
955 }
956}
957
958int dashboard_pi::GetAPIVersionMajor() { return MY_API_VERSION_MAJOR; }
959
960int dashboard_pi::GetAPIVersionMinor() { return MY_API_VERSION_MINOR; }
961
962int dashboard_pi::GetPlugInVersionMajor() { return PLUGIN_VERSION_MAJOR; }
963
964int dashboard_pi::GetPlugInVersionMinor() { return PLUGIN_VERSION_MINOR; }
965
966wxBitmap *dashboard_pi::GetPlugInBitmap() { return _img_dashboard_pi; }
967
968wxString dashboard_pi::GetCommonName() { return _("Dashboard"); }
969
971 return _("Dashboard PlugIn for OpenCPN");
972}
973
975 return _(
976 "Dashboard PlugIn for OpenCPN\n\
977Provides navigation instrument display from NMEA source.");
978}
979
980void dashboard_pi::SendSentenceToAllInstruments(DASH_CAP st, double value,
981 wxString unit) {
982 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
983 DashboardWindow *dashboard_window =
984 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
985 if (dashboard_window)
986 dashboard_window->SendSentenceToAllInstruments(st, value, unit);
987 }
988 if (st == OCPN_DBP_STC_HDT) {
989 g_dHDT = value;
990 }
991 if (st == OCPN_DBP_STC_SOG) {
992 g_dSOG = value;
993 }
994 if (st == OCPN_DBP_STC_COG) {
995 g_dCOG = value;
996 }
997}
998
999void dashboard_pi::SendUtcTimeToAllInstruments(wxDateTime value) {
1000 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
1001 DashboardWindow *dashboard_window =
1002 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
1003 if (dashboard_window) dashboard_window->SendUtcTimeToAllInstruments(value);
1004 }
1005}
1006
1007void dashboard_pi::SendSatInfoToAllInstruments(int cnt, int seq, wxString talk,
1008 SAT_INFO sats[4]) {
1009 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
1010 DashboardWindow *dashboard_window =
1011 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
1012 if (dashboard_window)
1013 dashboard_window->SendSatInfoToAllInstruments(cnt, seq, talk, sats);
1014 }
1015}
1016
1017// NMEA 0183 N0183.....
1018void dashboard_pi::SetNMEASentence(wxString &sentence) {
1019 m_NMEA0183 << sentence;
1020
1021 if (m_NMEA0183.PreParse()) {
1022 if (m_NMEA0183.LastSentenceIDReceived == _T("DBT")) {
1023 if (mPriDepth >= 5) {
1024 if (m_NMEA0183.Parse()) {
1025 /*
1026 double m_NMEA0183.Dbt.DepthFeet;
1027 double m_NMEA0183.Dbt.DepthMeters;
1028 double m_NMEA0183.Dbt.DepthFathoms;
1029 */
1030 double depth = NAN;
1031 if (!std::isnan(m_NMEA0183.Dbt.DepthMeters))
1032 depth = m_NMEA0183.Dbt.DepthMeters;
1033 else if (!std::isnan(m_NMEA0183.Dbt.DepthFeet))
1034 depth = m_NMEA0183.Dbt.DepthFeet * 0.3048;
1035 else if (!std::isnan(m_NMEA0183.Dbt.DepthFathoms))
1036 depth = m_NMEA0183.Dbt.DepthFathoms * 1.82880;
1037 if (!std::isnan(depth)) depth += g_dDashDBTOffset;
1038 if (!std::isnan(depth)) {
1039 SendSentenceToAllInstruments(
1040 OCPN_DBP_STC_DPT,
1041 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1042 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1043 mPriDepth = 5;
1044 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1045 }
1046 }
1047 }
1048 }
1049
1050 else if (m_NMEA0183.LastSentenceIDReceived == _T("DPT")) {
1051 if (mPriDepth >= 4) {
1052 if (m_NMEA0183.Parse()) {
1053 /*
1054 double m_NMEA0183.Dpt.DepthMeters
1055 double m_NMEA0183.Dpt.OffsetFromTransducerMeters
1056 */
1057 double depth = m_NMEA0183.Dpt.DepthMeters;
1058 if (!std::isnan(m_NMEA0183.Dpt.OffsetFromTransducerMeters)) {
1059 depth += m_NMEA0183.Dpt.OffsetFromTransducerMeters;
1060 }
1061 depth += g_dDashDBTOffset;
1062 if (!std::isnan(depth)) {
1063 SendSentenceToAllInstruments(
1064 OCPN_DBP_STC_DPT,
1065 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1066 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1067 mPriDepth = 4;
1068 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1069 }
1070 }
1071 }
1072 }
1073 // TODO: GBS - GPS Satellite fault detection
1074 else if (m_NMEA0183.LastSentenceIDReceived == _T("GGA")) {
1075 if (0) // debug output
1076 printf("GGA mPriPosition=%d mPriSatUsed=%d \tnSat=%d alt=%3.2f\n",
1077 mPriPosition, mPriSatUsed,
1078 m_NMEA0183.Gga.NumberOfSatellitesInUse,
1079 m_NMEA0183.Gga.AntennaAltitudeMeters);
1080 if (mPriAlt >= 3 && (mPriPosition >= 1 || mPriSatUsed >= 1)) {
1081 if (m_NMEA0183.Parse()) {
1082 if (m_NMEA0183.Gga.GPSQuality > 0 &&
1083 m_NMEA0183.Gga.NumberOfSatellitesInUse >= 5) {
1084 // Altimeter, takes altitude from gps GGA message, which is
1085 // typically less accurate than lon and lat.
1086 double alt = m_NMEA0183.Gga.AntennaAltitudeMeters;
1087 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, alt, _T("m"));
1088 mPriAlt = 3;
1089 mALT_Watchdog = gps_watchdog_timeout_ticks;
1090 }
1091 }
1092 }
1093 if (mPriPosition >= 4 || mPriSatUsed >= 3) {
1094 if (m_NMEA0183.Parse()) {
1095 if (m_NMEA0183.Gga.GPSQuality > 0) {
1096 if (mPriPosition >= 4) {
1097 mPriPosition = 4;
1098 double lat, lon;
1099 float llt = m_NMEA0183.Gga.Position.Latitude.Latitude;
1100 int lat_deg_int = (int)(llt / 100);
1101 float lat_deg = lat_deg_int;
1102 float lat_min = llt - (lat_deg * 100);
1103 lat = lat_deg + (lat_min / 60.);
1104 if (m_NMEA0183.Gga.Position.Latitude.Northing == South)
1105 lat = -lat;
1106 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1107
1108 float lln = m_NMEA0183.Gga.Position.Longitude.Longitude;
1109 int lon_deg_int = (int)(lln / 100);
1110 float lon_deg = lon_deg_int;
1111 float lon_min = lln - (lon_deg * 100);
1112 lon = lon_deg + (lon_min / 60.);
1113 if (m_NMEA0183.Gga.Position.Longitude.Easting == West) lon = -lon;
1114 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1115 }
1116 if (mPriSatUsed >= 3) {
1117 mSatsInUse = m_NMEA0183.Gga.NumberOfSatellitesInUse;
1118 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, mSatsInUse,
1119 _T (""));
1120 mPriSatUsed = 3;
1121 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
1122 }
1123
1124 // if( mPriDateTime >= 4 ) {
1125 // // Not in use, we need the date too.
1126 // //mPriDateTime = 4;
1127 // //mUTCDateTime.ParseFormat( m_NMEA0183.Gga.UTCTime.c_str(),
1128 // _T("%H%M%S") );
1129 //}
1130 }
1131 }
1132 }
1133 }
1134
1135 else if (m_NMEA0183.LastSentenceIDReceived == _T("GLL")) {
1136 if (mPriPosition >= 3) {
1137 if (m_NMEA0183.Parse()) {
1138 if (m_NMEA0183.Gll.IsDataValid == NTrue) {
1139 double lat, lon;
1140 float llt = m_NMEA0183.Gll.Position.Latitude.Latitude;
1141 int lat_deg_int = (int)(llt / 100);
1142 float lat_deg = lat_deg_int;
1143 float lat_min = llt - (lat_deg * 100);
1144 lat = lat_deg + (lat_min / 60.);
1145 if (m_NMEA0183.Gll.Position.Latitude.Northing == South) lat = -lat;
1146 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1147
1148 float lln = m_NMEA0183.Gll.Position.Longitude.Longitude;
1149 int lon_deg_int = (int)(lln / 100);
1150 float lon_deg = lon_deg_int;
1151 float lon_min = lln - (lon_deg * 100);
1152 lon = lon_deg + (lon_min / 60.);
1153 if (m_NMEA0183.Gll.Position.Longitude.Easting == West) lon = -lon;
1154 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1155
1156 mPriPosition = 3;
1157 }
1158
1159 // if( mPriDateTime >= 5 ) {
1160 // // Not in use, we need the date too.
1161 // //mPriDateTime = 5;
1162 // //mUTCDateTime.ParseFormat( m_NMEA0183.Gll.UTCTime.c_str(),
1163 // _T("%H%M%S") );
1164 //}
1165 }
1166 }
1167 }
1168
1169 else if (m_NMEA0183.LastSentenceIDReceived == _T("GSV")) {
1170 if (mPriSatStatus >= 3 || mPriSatUsed >= 5) {
1171 if (m_NMEA0183.Parse()) {
1172 if (m_NMEA0183.Gsv.MessageNumber == 1) {
1173 // NMEA0183 recommend to not repeat SatsInView
1174 // in subsequent messages
1175 mSatsInView = m_NMEA0183.Gsv.SatsInView;
1176
1177 if (mPriSatUsed >= 5) {
1178 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT,
1179 m_NMEA0183.Gsv.SatsInView, _T (""));
1180 mPriSatUsed = 5;
1181 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
1182 }
1183 }
1184
1185 if (mPriSatStatus >= 3) {
1186 SendSatInfoToAllInstruments(
1187 mSatsInView, m_NMEA0183.Gsv.MessageNumber, m_NMEA0183.TalkerID,
1188 m_NMEA0183.Gsv.SatInfo);
1189 mPriSatStatus = 3;
1190 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
1191 }
1192 }
1193 }
1194 }
1195
1196 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDG")) {
1197 if (mPriVar >= 3 || mPriHeadingM >= 3 || mPriHeadingT >= 7) {
1198 if (m_NMEA0183.Parse()) {
1199 if (mPriVar >= 3) {
1200 // Any device sending VAR=0.0 can be assumed to not really know
1201 // what the actual variation is, so in this case we use WMM if
1202 // available
1203 if ((!std::isnan(m_NMEA0183.Hdg.MagneticVariationDegrees)) &&
1204 0.0 != m_NMEA0183.Hdg.MagneticVariationDegrees) {
1205 mPriVar = 3;
1206 if (m_NMEA0183.Hdg.MagneticVariationDirection == East)
1207 mVar = m_NMEA0183.Hdg.MagneticVariationDegrees;
1208 else if (m_NMEA0183.Hdg.MagneticVariationDirection == West)
1209 mVar = -m_NMEA0183.Hdg.MagneticVariationDegrees;
1210 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar,
1211 _T("\u00B0"));
1212 }
1213 }
1214 if (mPriHeadingM >= 3) {
1215 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees)) {
1216 mPriHeadingM = 3;
1217 mHdm = m_NMEA0183.Hdg.MagneticSensorHeadingDegrees;
1218 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm,
1219 _T("\u00B0"));
1220 }
1221 }
1222 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees))
1223 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1224
1225 // If Variation is available, no higher priority HDT is
1226 // available, then calculate and propagate calculated HDT
1227 if (!std::isnan(m_NMEA0183.Hdg.MagneticSensorHeadingDegrees)) {
1228 if (!std::isnan(mVar) && (mPriHeadingT >= 7)) {
1229 mPriHeadingT = 7;
1230 double heading = mHdm + mVar;
1231 if (heading < 0)
1232 heading += 360;
1233 else if (heading >= 360.0)
1234 heading -= 360;
1235 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading,
1236 _T("\u00B0"));
1237 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1238 }
1239 }
1240 }
1241 }
1242 }
1243
1244 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDM")) {
1245 if (mPriHeadingM >= 4 || mPriHeadingT >= 5) {
1246 if (m_NMEA0183.Parse()) {
1247 if (mPriHeadingM >= 4) {
1248 if (!std::isnan(m_NMEA0183.Hdm.DegreesMagnetic)) {
1249 mPriHeadingM = 4;
1250 mHdm = m_NMEA0183.Hdm.DegreesMagnetic;
1251 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, mHdm,
1252 _T("\u00B0M"));
1253 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1254 }
1255 }
1256
1257 // If Variation is available, no higher priority HDT is
1258 // available, then calculate and propagate calculated HDT
1259 if (!std::isnan(m_NMEA0183.Hdm.DegreesMagnetic)) {
1260 if (!std::isnan(mVar) && (mPriHeadingT >= 5)) {
1261 mPriHeadingT = 5;
1262 double heading = mHdm + mVar;
1263 if (heading < 0)
1264 heading += 360;
1265 else if (heading >= 360.0)
1266 heading -= 360;
1267 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading,
1268 _T("\u00B0"));
1269 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1270 }
1271 }
1272 }
1273 }
1274 }
1275
1276 else if (m_NMEA0183.LastSentenceIDReceived == _T("HDT")) {
1277 if (mPriHeadingT >= 3) {
1278 if (m_NMEA0183.Parse()) {
1279 if (!std::isnan(m_NMEA0183.Hdt.DegreesTrue)) {
1280 SendSentenceToAllInstruments(
1281 OCPN_DBP_STC_HDT, m_NMEA0183.Hdt.DegreesTrue, _T("\u00B0T"));
1282 mPriHeadingT = 3;
1283 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1284 }
1285 }
1286 }
1287 } else if (m_NMEA0183.LastSentenceIDReceived ==
1288 _T("MTA")) { // Air temperature
1289 if (mPriATMP >= 3) {
1290 if (m_NMEA0183.Parse()) {
1291 mPriATMP = 3;
1292 SendSentenceToAllInstruments(
1293 OCPN_DBP_STC_ATMP,
1294 toUsrTemp_Plugin(m_NMEA0183.Mta.Temperature, g_iDashTempUnit),
1295 getUsrTempUnit_Plugin(g_iDashTempUnit));
1296 mATMP_Watchdog = gps_watchdog_timeout_ticks;
1297 }
1298 }
1299 } else if (m_NMEA0183.LastSentenceIDReceived == _T("MDA") &&
1300 (mPriMDA >= 5 || mPriATMP >= 5 || mPriHUM >= 4)) {
1301 // Barometric pressure || HUmidity || Air temp
1302 if (m_NMEA0183.Parse()) {
1303 // TODO make posibilyti to select between Bar or InchHg
1304 /*
1305 double m_NMEA0183.Mda.Pressure;
1306 wxString m_NMEA0183.Mda.UnitOfMeasurement;
1307 */
1308 if (mPriMDA >= 5 && m_NMEA0183.Mda.Pressure > .8 &&
1309 m_NMEA0183.Mda.Pressure < 1.1) {
1310 SendSentenceToAllInstruments(
1311 OCPN_DBP_STC_MDA, m_NMEA0183.Mda.Pressure * 1000, _T("hPa"));
1312 mPriMDA = 5;
1313 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
1314 }
1315 if (mPriATMP >= 5) {
1316 double airtemp = m_NMEA0183.Mda.AirTemp;
1317 if (!std::isnan(airtemp) && airtemp < 999.0) {
1318 SendSentenceToAllInstruments(
1319 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(airtemp, g_iDashTempUnit),
1320 getUsrTempUnit_Plugin(g_iDashTempUnit));
1321 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
1322 mPriATMP = 5;
1323 }
1324 }
1325 if (mPriHUM >= 4) {
1326 double humidity = m_NMEA0183.Mda.Humidity;
1327 if (!std::isnan(humidity)) {
1328 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, humidity, "%");
1329 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
1330 mPriHUM = 4;
1331 }
1332 }
1333 }
1334
1335 } else if (m_NMEA0183.LastSentenceIDReceived == _T("MTW")) {
1336 if (mPriWTP >= 4) {
1337 if (m_NMEA0183.Parse()) {
1338 mPriWTP = 4;
1339 SendSentenceToAllInstruments(
1340 OCPN_DBP_STC_TMP,
1341 toUsrTemp_Plugin(m_NMEA0183.Mtw.Temperature, g_iDashTempUnit),
1342 getUsrTempUnit_Plugin(g_iDashTempUnit));
1343 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
1344 }
1345 }
1346
1347 } else if (m_NMEA0183.LastSentenceIDReceived == _T("VLW")) {
1348 if (m_NMEA0183.Parse()) {
1349 /*
1350 double m_NMEA0183.Vlw.TotalMileage;
1351 double m_NMEA0183.Vlw.TripMileage;
1352 */
1353 SendSentenceToAllInstruments(
1354 OCPN_DBP_STC_VLW1,
1355 toUsrDistance_Plugin(m_NMEA0183.Vlw.TripMileage,
1356 g_iDashDistanceUnit),
1357 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1358 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
1359
1360 SendSentenceToAllInstruments(
1361 OCPN_DBP_STC_VLW2,
1362 toUsrDistance_Plugin(m_NMEA0183.Vlw.TotalMileage,
1363 g_iDashDistanceUnit),
1364 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
1365 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
1366 }
1367
1368 }
1369 // NMEA 0183 standard Wind Direction and Speed, with respect to north.
1370 else if (m_NMEA0183.LastSentenceIDReceived == _T("MWD")) {
1371 if (mPriWDN >= 6) {
1372 if (m_NMEA0183.Parse()) {
1373 // Option for True vs Magnetic
1374 wxString windunit;
1375 if (!std::isnan(m_NMEA0183.Mwd.WindAngleTrue)) {
1376 // if WindAngleTrue is available, use it ...
1377 SendSentenceToAllInstruments(
1378 OCPN_DBP_STC_TWD, m_NMEA0183.Mwd.WindAngleTrue, _T("\u00B0"));
1379 mPriWDN = 6;
1380 // MWD can be seldom updated by the sensor. Set prolonged watchdog
1381 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1382 } else if (!std::isnan(m_NMEA0183.Mwd.WindAngleMagnetic)) {
1383 // Make it true and use if variation is available
1384 if (!std::isnan(mVar)) {
1385 double twd = m_NMEA0183.Mwd.WindAngleMagnetic;
1386 twd += mVar;
1387 if (twd > 360.) {
1388 twd -= 360;
1389 } else if (twd < 0.) {
1390 twd += 360;
1391 }
1392 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, twd, _T("\u00B0"));
1393 mPriWDN = 6;
1394 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1395 }
1396 }
1397 SendSentenceToAllInstruments(
1398 OCPN_DBP_STC_TWS,
1399 toUsrSpeed_Plugin(m_NMEA0183.Mwd.WindSpeedKnots,
1400 g_iDashWindSpeedUnit),
1401 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1402 SendSentenceToAllInstruments(
1403 OCPN_DBP_STC_TWS2,
1404 toUsrSpeed_Plugin(m_NMEA0183.Mwd.WindSpeedKnots,
1405 g_iDashWindSpeedUnit),
1406 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1407 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1408 // m_NMEA0183.Mwd.WindSpeedms
1409 }
1410 }
1411 }
1412 // NMEA 0183 standard Wind Speed and Angle, in relation to the vessel's
1413 // bow/centerline.
1414 else if (m_NMEA0183.LastSentenceIDReceived == _T("MWV")) {
1415 if (mPriAWA >= 4 || mPriTWA >= 5 || mPriWDN >= 5) {
1416 if (m_NMEA0183.Parse()) {
1417 if (m_NMEA0183.Mwv.IsDataValid == NTrue) {
1418 // MWV windspeed has different units. Form it to knots to fit
1419 // "toUsrSpeed_Plugin()"
1420 double m_wSpeedFactor = 1.0; // knots ("N")
1421 if (m_NMEA0183.Mwv.WindSpeedUnits == _T("K"))
1422 m_wSpeedFactor = 0.53995; // km/h > knots
1423 if (m_NMEA0183.Mwv.WindSpeedUnits == _T("M"))
1424 m_wSpeedFactor = 1.94384; // m/s > knots
1425
1426 if (m_NMEA0183.Mwv.Reference ==
1427 _T("R")) // Relative (apparent wind)
1428 {
1429 if (mPriAWA >= 4) {
1430 mPriAWA = 4;
1431 wxString m_awaunit;
1432 double m_awaangle;
1433 if (m_NMEA0183.Mwv.WindAngle > 180) {
1434 m_awaunit = _T("\u00B0L");
1435 m_awaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1436 } else {
1437 m_awaunit = _T("\u00B0R");
1438 m_awaangle = m_NMEA0183.Mwv.WindAngle;
1439 }
1440 SendSentenceToAllInstruments(
1441 OCPN_DBP_STC_AWA, mAWAFilter.filter(m_awaangle), m_awaunit);
1442 SendSentenceToAllInstruments(
1443 OCPN_DBP_STC_AWS,
1445 mAWSFilter.filter(m_NMEA0183.Mwv.WindSpeed) *
1446 m_wSpeedFactor,
1447 g_iDashWindSpeedUnit),
1448 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1449 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1450 }
1451
1452 // If we have true HDT, COG, and SOG
1453 // then using simple vector math, we can calculate true wind
1454 // direction and speed. If there is no higher priority source for
1455 // WDN, then do so here, and update the appropriate instruments.
1456 if (mPriWDN >= 8) {
1457 CalculateAndUpdateTWDS(
1458 m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1459 m_NMEA0183.Mwv.WindAngle);
1460 mPriWDN = 8;
1461 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1462 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1463 }
1464 } else if (m_NMEA0183.Mwv.Reference ==
1465 _T("T")) // Theoretical (aka True)
1466 {
1467 if (mPriTWA >= 5) {
1468 mPriTWA = 5;
1469 wxString m_twaunit;
1470 double m_twaangle;
1471 bool b_R = false;
1472 if (m_NMEA0183.Mwv.WindAngle > 180) {
1473 m_twaunit = _T("\u00B0L");
1474 m_twaangle = 180.0 - (m_NMEA0183.Mwv.WindAngle - 180.0);
1475 } else {
1476 m_twaunit = _T("\u00B0R");
1477 m_twaangle = m_NMEA0183.Mwv.WindAngle;
1478 b_R = true;
1479 }
1480 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle,
1481 m_twaunit);
1482
1483 if (mPriWDN >= 7) {
1484 // MWV has wind angle relative to the bow.
1485 // Wind history use angle relative to north.
1486 // If no TWD with higher priority is present
1487 // and true heading is available calculate it.
1488 if (g_dHDT < 361. && g_dHDT >= 0.0) {
1489 double g_dCalWdir = (m_NMEA0183.Mwv.WindAngle) + g_dHDT;
1490 if (g_dCalWdir > 360.) {
1491 g_dCalWdir -= 360;
1492 } else if (g_dCalWdir < 0.) {
1493 g_dCalWdir += 360;
1494 }
1495 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir,
1496 _T("\u00B0"));
1497 mPriWDN = 7;
1498 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1499 }
1500 }
1501
1502 SendSentenceToAllInstruments(
1503 OCPN_DBP_STC_TWS,
1504 toUsrSpeed_Plugin(m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1505 g_iDashWindSpeedUnit),
1506 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1507 SendSentenceToAllInstruments(
1508 OCPN_DBP_STC_TWS2,
1509 toUsrSpeed_Plugin(m_NMEA0183.Mwv.WindSpeed * m_wSpeedFactor,
1510 g_iDashWindSpeedUnit),
1511 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1512 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1513 }
1514 }
1515 }
1516 }
1517 }
1518 }
1519
1520 else if (m_NMEA0183.LastSentenceIDReceived == _T("RMC")) {
1521 if (mPriPosition >= 5 || mPriCOGSOG >= 3 || mPriVar >= 4 ||
1522 mPriDateTime >= 3) {
1523 if (m_NMEA0183.Parse()) {
1524 if (m_NMEA0183.Rmc.IsDataValid == NTrue) {
1525 if (mPriPosition >= 5) {
1526 mPriPosition = 5;
1527 double lat, lon;
1528 float llt = m_NMEA0183.Rmc.Position.Latitude.Latitude;
1529 int lat_deg_int = (int)(llt / 100);
1530 float lat_deg = lat_deg_int;
1531 float lat_min = llt - (lat_deg * 100);
1532 lat = lat_deg + (lat_min / 60.);
1533 if (m_NMEA0183.Rmc.Position.Latitude.Northing == South)
1534 lat = -lat;
1535 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
1536
1537 float lln = m_NMEA0183.Rmc.Position.Longitude.Longitude;
1538 int lon_deg_int = (int)(lln / 100);
1539 float lon_deg = lon_deg_int;
1540 float lon_min = lln - (lon_deg * 100);
1541 lon = lon_deg + (lon_min / 60.);
1542 if (m_NMEA0183.Rmc.Position.Longitude.Easting == West) lon = -lon;
1543 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
1544 }
1545
1546 if (mPriCOGSOG >= 3) {
1547 mPriCOGSOG = 3;
1548 if (!std::isnan(m_NMEA0183.Rmc.SpeedOverGroundKnots)) {
1549 SendSentenceToAllInstruments(
1550 OCPN_DBP_STC_SOG,
1552 mSOGFilter.filter(m_NMEA0183.Rmc.SpeedOverGroundKnots),
1553 g_iDashSpeedUnit),
1554 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1555 }
1556
1557 if (!std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue)) {
1558 SendSentenceToAllInstruments(
1559 OCPN_DBP_STC_COG,
1560 mCOGFilter.filter(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue),
1561 _T("\u00B0"));
1562 }
1563 if (!std::isnan(m_NMEA0183.Rmc.TrackMadeGoodDegreesTrue) &&
1564 !std::isnan(m_NMEA0183.Rmc.MagneticVariation)) {
1565 double dMagneticCOG;
1566 if (m_NMEA0183.Rmc.MagneticVariationDirection == East) {
1567 dMagneticCOG =
1568 mCOGFilter.get() - m_NMEA0183.Rmc.MagneticVariation;
1569 if (dMagneticCOG < 0.0) dMagneticCOG = 360.0 + dMagneticCOG;
1570 } else {
1571 dMagneticCOG =
1572 mCOGFilter.get() + m_NMEA0183.Rmc.MagneticVariation;
1573 if (dMagneticCOG > 360.0) dMagneticCOG = dMagneticCOG - 360.0;
1574 }
1575 SendSentenceToAllInstruments(OCPN_DBP_STC_MCOG, dMagneticCOG,
1576 _T("\u00B0M"));
1577 }
1578 }
1579
1580 if (mPriVar >= 4) {
1581 // Any device sending VAR=0.0 can be assumed to not really know
1582 // what the actual variation is, so in this case we use WMM if
1583 // available
1584 if ((!std::isnan(m_NMEA0183.Rmc.MagneticVariation)) &&
1585 0.0 != m_NMEA0183.Rmc.MagneticVariation) {
1586 mPriVar = 4;
1587 if (m_NMEA0183.Rmc.MagneticVariationDirection == East)
1588 mVar = m_NMEA0183.Rmc.MagneticVariation;
1589 else if (m_NMEA0183.Rmc.MagneticVariationDirection == West)
1590 mVar = -m_NMEA0183.Rmc.MagneticVariation;
1591 mVar_Watchdog = gps_watchdog_timeout_ticks;
1592
1593 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar,
1594 _T("\u00B0"));
1595 }
1596 }
1597
1598 if (mPriDateTime >= 3) {
1599 mPriDateTime = 3;
1600 wxString dt = m_NMEA0183.Rmc.Date + m_NMEA0183.Rmc.UTCTime;
1601 mUTCDateTime.ParseFormat(dt.c_str(), _T("%d%m%y%H%M%S"));
1602 mUTC_Watchdog = gps_watchdog_timeout_ticks;
1603 }
1604 }
1605 }
1606 }
1607 }
1608
1609 else if (m_NMEA0183.LastSentenceIDReceived == _T("RSA")) {
1610 if (mPriRSA >= 3) {
1611 if (m_NMEA0183.Parse()) {
1612 if (m_NMEA0183.Rsa.IsStarboardDataValid == NTrue) {
1613 SendSentenceToAllInstruments(
1614 OCPN_DBP_STC_RSA, m_NMEA0183.Rsa.Starboard, _T("\u00B0"));
1615 } else if (m_NMEA0183.Rsa.IsPortDataValid == NTrue) {
1616 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, -m_NMEA0183.Rsa.Port,
1617 _T("\u00B0"));
1618 }
1619 mRSA_Watchdog = gps_watchdog_timeout_ticks;
1620 mPriRSA = 3;
1621 }
1622 }
1623 }
1624
1625 else if (m_NMEA0183.LastSentenceIDReceived == _T("VHW")) {
1626 if (mPriHeadingT >= 4 || mPriHeadingM >= 5 || mPriSTW >= 3) {
1627 if (m_NMEA0183.Parse()) {
1628 if (mPriHeadingT >= 4) {
1629 if (!std::isnan(m_NMEA0183.Vhw.DegreesTrue)) {
1630 mPriHeadingT = 4;
1631 SendSentenceToAllInstruments(
1632 OCPN_DBP_STC_HDT, m_NMEA0183.Vhw.DegreesTrue, _T("\u00B0T"));
1633 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1634 }
1635 }
1636 if (mPriHeadingM >= 5) {
1637 if (!std::isnan(m_NMEA0183.Vhw.DegreesMagnetic)) {
1638 mPriHeadingM = 5;
1639 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM,
1640 m_NMEA0183.Vhw.DegreesMagnetic,
1641 _T("\u00B0M"));
1642 mHDx_Watchdog = gps_watchdog_timeout_ticks;
1643 }
1644 }
1645 if (mPriSTW >= 3) {
1646 double stw_kn = NAN;
1647 if (!std::isnan(m_NMEA0183.Vhw.Knots))
1648 stw_kn = m_NMEA0183.Vhw.Knots;
1649 else if (!std::isnan(m_NMEA0183.Vhw.KilometersPerHour))
1650 stw_kn = m_NMEA0183.Vhw.KilometersPerHour * 0.53995;
1651
1652 if (!std::isnan(stw_kn)) {
1653 SendSentenceToAllInstruments(
1654 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_kn, g_iDashSpeedUnit),
1655 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1656 mSTW_Watchdog = gps_watchdog_timeout_ticks;
1657 mPriSTW = 3;
1658 }
1659 }
1660 }
1661 }
1662 }
1663
1664 else if (m_NMEA0183.LastSentenceIDReceived == _T("VTG")) {
1665 if (mPriCOGSOG >= 2) {
1666 if (m_NMEA0183.Parse()) {
1667 mPriCOGSOG = 2;
1668 // Special check for unintialized values, as opposed to zero values
1669 if (!std::isnan(m_NMEA0183.Vtg.SpeedKnots)) {
1670 SendSentenceToAllInstruments(
1671 OCPN_DBP_STC_SOG,
1672 toUsrSpeed_Plugin(mSOGFilter.filter(m_NMEA0183.Vtg.SpeedKnots),
1673 g_iDashSpeedUnit),
1674 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1675 }
1676 // Vtg.SpeedKilometersPerHour;
1677 if (!std::isnan(m_NMEA0183.Vtg.TrackDegreesTrue)) {
1678 SendSentenceToAllInstruments(
1679 OCPN_DBP_STC_COG,
1680 mCOGFilter.filter(m_NMEA0183.Vtg.TrackDegreesTrue),
1681 _T("\u00B0"));
1682 }
1683 }
1684
1685 /*
1686 m_NMEA0183.Vtg.TrackDegreesMagnetic;
1687 */
1688 }
1689 }
1690 /* NMEA 0183 Relative (Apparent) Wind Speed and Angle. Wind angle in
1691 * relation
1692 * to the vessel's heading, and wind speed measured relative to the moving
1693 * vessel. */
1694 else if (m_NMEA0183.LastSentenceIDReceived == _T("VWR")) {
1695 if (mPriAWA >= 3) {
1696 if (m_NMEA0183.Parse()) {
1697 if (m_NMEA0183.Vwr.WindDirectionMagnitude < 200) {
1698 mPriAWA = 3;
1699
1700 wxString awaunit;
1701 awaunit = m_NMEA0183.Vwr.DirectionOfWind == Left ? _T("\u00B0L")
1702 : _T("\u00B0R");
1703 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
1704 m_NMEA0183.Vwr.WindDirectionMagnitude,
1705 awaunit);
1706 SendSentenceToAllInstruments(
1707 OCPN_DBP_STC_AWS,
1708 toUsrSpeed_Plugin(m_NMEA0183.Vwr.WindSpeedKnots,
1709 g_iDashWindSpeedUnit),
1710 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1711 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
1712 /*
1713 double m_NMEA0183.Vwr.WindSpeedms;
1714 double m_NMEA0183.Vwr.WindSpeedKmh;
1715 */
1716 }
1717
1718 // If we have true HDT, COG, and SOG
1719 // then using simple vector math, we can calculate true wind direction
1720 // and speed. If there is no higher priority source for WDN, then do
1721 // so here, and update the appropriate instruments.
1722 if (mPriWDN >= 9) {
1723 double awa = m_NMEA0183.Vwr.WindDirectionMagnitude;
1724 if (m_NMEA0183.Vwr.DirectionOfWind == Left)
1725 awa = 360. - m_NMEA0183.Vwr.WindDirectionMagnitude;
1726 CalculateAndUpdateTWDS(m_NMEA0183.Vwr.WindSpeedKnots, awa);
1727 mPriWDN = 9;
1728 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1729 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
1730 }
1731 }
1732 }
1733 }
1734 /* NMEA 0183 True wind angle in relation to the vessel's heading, and true
1735 * wind speed referenced to the water. True wind is the vector sum of the
1736 * Relative (apparent) wind vector and the vessel's velocity vector relative
1737 * to the water along the heading line of the vessel. It represents the wind
1738 * at the vessel if it were
1739 * stationary relative to the water and heading in the same direction. */
1740 else if (m_NMEA0183.LastSentenceIDReceived == _T("VWT")) {
1741 if (mPriTWA >= 4) {
1742 if (m_NMEA0183.Parse()) {
1743 if (m_NMEA0183.Vwt.WindDirectionMagnitude < 200) {
1744 mPriTWA = 4;
1745 wxString vwtunit;
1746 vwtunit = m_NMEA0183.Vwt.DirectionOfWind == Left ? _T("\u00B0L")
1747 : _T("\u00B0R");
1748 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA,
1749 m_NMEA0183.Vwt.WindDirectionMagnitude,
1750 vwtunit);
1751 SendSentenceToAllInstruments(
1752 OCPN_DBP_STC_TWS,
1753 toUsrSpeed_Plugin(m_NMEA0183.Vwt.WindSpeedKnots,
1754 g_iDashWindSpeedUnit),
1755 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
1756 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
1757 /*
1758 double m_NMEA0183.Vwt.WindSpeedms;
1759 double m_NMEA0183.Vwt.WindSpeedKmh;
1760 */
1761 }
1762 }
1763 }
1764 }
1765
1766 else if (m_NMEA0183.LastSentenceIDReceived ==
1767 _T("XDR")) { // Transducer measurement
1768 /* XDR Transducer types
1769 * AngularDisplacementTransducer = 'A',
1770 * TemperatureTransducer = 'C',
1771 * LinearDisplacementTransducer = 'D',
1772 * FrequencyTransducer = 'F',
1773 * HumidityTransducer = 'H',
1774 * ForceTransducer = 'N',
1775 * PressureTransducer = 'P',
1776 * FlowRateTransducer = 'R',
1777 * TachometerTransducer = 'T',
1778 * VolumeTransducer = 'V'
1779 */
1780
1781 if (m_NMEA0183.Parse()) {
1782 wxString xdrunit;
1783 double xdrdata;
1784 for (int i = 0; i < m_NMEA0183.Xdr.TransducerCnt; i++) {
1785 xdrdata = m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData;
1786 // XDR Airtemp
1787 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("C")) {
1788 if (m_NMEA0183.Xdr.TransducerInfo[i]
1789 .TransducerName.MakeUpper()
1790 .Contains(_T("AIR")) ||
1791 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == _T("Te") ||
1792 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1793 _T("ENV_OUTAIR_T") ||
1794 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1795 _T("ENV_OUTSIDE_T")) {
1796 if (mPriATMP >= 4) {
1797 mPriATMP = 4;
1798 SendSentenceToAllInstruments(
1799 OCPN_DBP_STC_ATMP,
1800 toUsrTemp_Plugin(xdrdata, g_iDashTempUnit),
1801 getUsrTempUnit_Plugin(g_iDashTempUnit));
1802 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
1803 continue;
1804 }
1805 } // Water temp
1806 if (m_NMEA0183.Xdr.TransducerInfo[i]
1807 .TransducerName.MakeUpper()
1808 .Contains("WATER") ||
1809 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == "WTHI") {
1810 if (mPriWTP >= 3) {
1811 mPriWTP = 3;
1812 SendSentenceToAllInstruments(
1813 OCPN_DBP_STC_TMP,
1815 m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData,
1816 g_iDashTempUnit),
1817 getUsrTempUnit_Plugin(g_iDashTempUnit));
1818 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
1819 continue;
1820 }
1821 }
1822 }
1823 // XDR Pressure
1824 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "P") {
1825 if (m_NMEA0183.Xdr.TransducerInfo[i]
1826 .TransducerName.MakeUpper()
1827 .Contains(_T("BARO")) &&
1828 mPriMDA >= 4) {
1829 if (m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement == "B") {
1830 xdrdata *= 1000;
1831 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, xdrdata,
1832 _T("hPa"));
1833 mPriMDA = 4;
1834 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
1835 continue;
1836 }
1837 }
1838 }
1839 // XDR Pitch (=Nose up/down) or Heel (stb/port)
1840 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == _T("A")) {
1841 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1842 _T("PTCH")) ||
1843 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1844 _T("PITCH"))) {
1845 if (mPriPitchRoll >= 3) {
1846 if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1847 xdrunit = _T("\u00B0\u2191") + _("Up");
1848 } else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData <
1849 0) {
1850 xdrunit = _T("\u00B0\u2193") + _("Down");
1851 xdrdata *= -1;
1852 } else {
1853 xdrunit = _T("\u00B0");
1854 }
1855 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, xdrdata,
1856 xdrunit);
1857 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
1858 mPriPitchRoll = 3;
1859 continue;
1860 }
1861 }
1862 // XDR Heel
1863 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName.Contains(
1864 "ROLL")) {
1865 if (mPriPitchRoll >= 3) {
1866 if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData > 0) {
1867 xdrunit = _T("\u00B0\u003E") + _("Stbd");
1868 } else if (m_NMEA0183.Xdr.TransducerInfo[i].MeasurementData <
1869 0) {
1870 xdrunit = _T("\u00B0\u003C") + _("Port");
1871 xdrdata *= -1;
1872 } else {
1873 xdrunit = _T("\u00B0");
1874 }
1875 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, xdrdata,
1876 xdrunit);
1877 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
1878 mPriPitchRoll = 3;
1879 continue;
1880 }
1881 }
1882 // XDR Rudder Angle
1883 if (m_NMEA0183.Xdr.TransducerInfo[i]
1884 .TransducerName.MakeUpper()
1885 .Contains("RUDDER")) {
1886 if (mPriRSA > 4) {
1887 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, xdrdata,
1888 _T("\u00B0"));
1889 mRSA_Watchdog = gps_watchdog_timeout_ticks;
1890 mPriRSA = 4;
1891 continue;
1892 }
1893 }
1894 }
1895 // Depth sounding and Windlass rode count
1896 if ((m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "D")) {
1897 bool good_depth = false;
1898 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName == "XDHI" &&
1899 mPriDepth >= 6) {
1900 good_depth = true;
1901 mPriDepth = 6;
1902 } else if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1903 "XDLO" &&
1904 mPriDepth >= 7) {
1905 good_depth = true;
1906 mPriDepth = 7;
1907 }
1908 // XDR Windlass chain counter
1909 else if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1910 _T("WINDLASS") ||
1911 m_NMEA0183.Xdr.TransducerInfo[i].TransducerName ==
1912 _T("WINDLASS#0")) {
1913 wxString unit =
1914 m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement;
1915 if (unit == wxEmptyString) unit = "m";
1916 unit.MakeLower();
1917 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, xdrdata, unit);
1918 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
1919 }
1920
1921 if (good_depth) {
1922 wxString unit = m_NMEA0183.Xdr.TransducerInfo[i]
1923 .UnitOfMeasurement.MakeLower();
1924 if (unit == "m") {
1925 double depth = NAN;
1926 depth = xdrdata;
1927 if (!std::isnan(depth)) {
1928 depth += g_dDashDBTOffset;
1929 SendSentenceToAllInstruments(
1930 OCPN_DBP_STC_DPT,
1931 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
1932 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
1933 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
1934 continue;
1935 }
1936 }
1937 }
1938 } // Humidity
1939 if (m_NMEA0183.Xdr.TransducerInfo[i].TransducerType == "H") {
1940 if (mPriHUM >= 3) {
1941 if (m_NMEA0183.Xdr.TransducerInfo[i].UnitOfMeasurement == "P") {
1942 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, xdrdata, "%");
1943 mPriHUM = 3;
1944 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
1945 }
1946 }
1947 }
1948 }
1949 }
1950 } else if (m_NMEA0183.LastSentenceIDReceived == _T("ZDA")) {
1951 if (mPriDateTime >= 2) {
1952 if (m_NMEA0183.Parse()) {
1953 mPriDateTime = 2;
1954 /*
1955 wxString m_NMEA0183.Zda.UTCTime;
1956 int m_NMEA0183.Zda.Day;
1957 int m_NMEA0183.Zda.Month;
1958 int m_NMEA0183.Zda.Year;
1959 int m_NMEA0183.Zda.LocalHourDeviation;
1960 int m_NMEA0183.Zda.LocalMinutesDeviation;
1961 */
1962 wxString dt;
1963 dt.Printf(_T("%4d%02d%02d"), m_NMEA0183.Zda.Year,
1964 m_NMEA0183.Zda.Month, m_NMEA0183.Zda.Day);
1965 dt.Append(m_NMEA0183.Zda.UTCTime);
1966 mUTCDateTime.ParseFormat(dt.c_str(), _T("%Y%m%d%H%M%S"));
1967 mUTC_Watchdog = gps_watchdog_timeout_ticks;
1968 }
1969 }
1970 }
1971 }
1972 // Process an AIVDO message
1973 else if (sentence.Mid(1, 5).IsSameAs(_T("AIVDO"))) {
1975 if (DecodeSingleVDOMessage(sentence, &gpd, &m_VDO_accumulator)) {
1976 if (!std::isnan(gpd.Lat))
1977 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, gpd.Lat, _T("SDMM"));
1978
1979 if (!std::isnan(gpd.Lon))
1980 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, gpd.Lon, _T("SDMM"));
1981
1982 SendSentenceToAllInstruments(
1983 OCPN_DBP_STC_SOG,
1984 toUsrSpeed_Plugin(mSOGFilter.filter(gpd.Sog), g_iDashSpeedUnit),
1985 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
1986 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(gpd.Cog),
1987 _T("\u00B0"));
1988 if (!std::isnan(gpd.Hdt)) {
1989 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, gpd.Hdt, _T("\u00B0T"));
1990 mHDT_Watchdog = gps_watchdog_timeout_ticks;
1991 }
1992 }
1993 }
1994}
1995
1996/* Calculate True Wind speed and direction from AWS and AWA
1997 * This algorithm requires HDT, SOG, and COG, which are maintained as
1998 * globals elsewhere. Also, update all instruments tagged with OCPN_DBP_STC_TWD,
1999 * OCPN_DBP_STC_TWS, and OCPN_DBP_STC_TWS2
2000 */
2001void dashboard_pi::CalculateAndUpdateTWDS(double awsKnots, double awaDegrees) {
2002 if (!std::isnan(g_dHDT)) {
2003 // Apparent wind velocity vector, relative to head-up
2004 double awsx = awsKnots * cos(awaDegrees * PI / 180.);
2005 double awsy = awsKnots * sin(awaDegrees * PI / 180.);
2006
2007 // Ownship velocity vector, relative to head-up
2008 double bsx = 0;
2009 double bsy = 0;
2010 if ((!std::isnan(g_dSOG)) && (!std::isnan(g_dCOG))) {
2011 bsx = g_dSOG * cos((g_dCOG - g_dHDT) * PI / 180.);
2012 bsy = g_dSOG * sin((g_dCOG - g_dHDT) * PI / 180.);
2013 ;
2014 }
2015
2016 // "True" wind is calculated by vector subtraction
2017 double twdx = awsx - bsx;
2018 double twdy = awsy - bsy;
2019
2020 // Calculate the speed (magnitude of the vector)
2021 double tws = pow(((twdx * twdx) + (twdy * twdy)), 0.5);
2022
2023 // calculate the True Wind Angle
2024 double twd = atan2(twdy, twdx) * 180. / PI;
2025 if (twd < 0)
2026 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, -twd, _T("\u00B0L"));
2027 else
2028 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, twd, _T("\u00B0R"));
2029
2030 // Calculate the True Wind Direction, by re-orienting to the ownship HDT
2031 double twdc = twd + g_dHDT;
2032
2033 // Normalize
2034 if (twdc < 0) twdc += 360.;
2035 if (twdc > 360.) twdc -= 360;
2036
2037 // Update the instruments
2038 // printf("CALC: %4.0f %4.0f\n", tws, twdc);
2039 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, twdc, _T("\u00B0"));
2040
2041 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS,
2042 toUsrSpeed_Plugin(tws, g_iDashWindSpeedUnit),
2043 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2044 SendSentenceToAllInstruments(OCPN_DBP_STC_TWS2,
2045 toUsrSpeed_Plugin(tws, g_iDashWindSpeedUnit),
2046 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2047 }
2048}
2049
2050// NMEA2000, N2K
2051//...............
2052
2053// Rudder data PGN 127245
2054void dashboard_pi::HandleN2K_127245(ObservedEvt ev) {
2055 NMEA2000Id id_127245(127245);
2056 std::vector<uint8_t> v = GetN2000Payload(id_127245, ev);
2057
2058 // Get a uniqe ID to prioritize source(s)
2059 unsigned char source_id = v.at(7);
2060 char ss[4];
2061 sprintf(ss, "%d", source_id);
2062 std::string ident = std::string(ss);
2063 std::string source = GetN2000Source(id_127245, ev);
2064 source += ":" + ident;
2065
2066 if (mPriRSA >= 1) {
2067 if (mPriRSA == 1) {
2068 // We favor first received after last WD
2069 if (source != prio127245) return;
2070 } else {
2071 // First time use after WD time out.
2072 prio127245 = source;
2073 }
2074
2075 double RudderPosition, AngleOrder;
2076 unsigned char Instance;
2077 tN2kRudderDirectionOrder RudderDirectionOrder;
2078
2079 // Get rudder position
2080 if (ParseN2kPGN127245(v, RudderPosition, Instance, RudderDirectionOrder,
2081 AngleOrder)) {
2082 if (!N2kIsNA(RudderPosition)) {
2083 double m_rudangle = GEODESIC_RAD2DEG(RudderPosition);
2084 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle,
2085 _T("\u00B0"));
2086 mRSA_Watchdog = gps_watchdog_timeout_ticks;
2087 mPriRSA = 1;
2088 }
2089 }
2090 }
2091}
2092
2093// Roll Pitch data PGN 127257
2094void dashboard_pi::HandleN2K_127257(ObservedEvt ev) {
2095 NMEA2000Id id_127257(127257);
2096 std::vector<uint8_t> v = GetN2000Payload(id_127257, ev);
2097
2098 // Get a uniqe ID to prioritize source(s)
2099 unsigned char source_id = v.at(7);
2100 char ss[4];
2101 sprintf(ss, "%d", source_id);
2102 std::string ident = std::string(ss);
2103 std::string source = GetN2000Source(id_127257, ev);
2104 source += ":" + ident;
2105
2106 if (mPriPitchRoll >= 1) {
2107 if (mPriPitchRoll == 1) {
2108 // We favor first received after last WD
2109 if (source != prio127257) return;
2110 } else {
2111 // First time use after WD time out.
2112 prio127257 = source;
2113 }
2114
2115 unsigned char SID;
2116 double Yaw, Pitch, Roll;
2117
2118 // Get roll and pitch
2119 if (ParseN2kPGN127257(v, SID, Yaw, Pitch, Roll)) {
2120 if (!N2kIsNA(Pitch)) {
2121 double m_pitch = GEODESIC_RAD2DEG(Pitch);
2122 wxString p_unit = _T("\u00B0\u2191") + _("Up");
2123 if (m_pitch < 0) {
2124 p_unit = _T("\u00B0\u2193") + _("Down");
2125 m_pitch *= -1;
2126 }
2127 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
2128 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
2129 mPriPitchRoll = 1;
2130 }
2131 if (!N2kIsNA(Roll)) {
2132 double m_heel = GEODESIC_RAD2DEG(Roll);
2133 wxString h_unit = _T("\u00B0\u003E") + _("Stbd");
2134 if (m_heel < 0) {
2135 h_unit = _T("\u00B0\u003C") + _("Port");
2136 m_heel *= -1;
2137 }
2138 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
2139 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
2140 mPriPitchRoll = 1;
2141 }
2142 }
2143 }
2144}
2145
2146void dashboard_pi::HandleN2K_128267(ObservedEvt ev) {
2147 NMEA2000Id id_128267(128267);
2148 std::vector<uint8_t> v = GetN2000Payload(id_128267, ev);
2149
2150 // Get a uniqe ID to prioritize source(s)
2151 unsigned char source_id = v.at(7);
2152 char ss[4];
2153 sprintf(ss, "%d", source_id);
2154 std::string ident = std::string(ss);
2155 std::string source = GetN2000Source(id_128267, ev);
2156 source += ":" + ident;
2157
2158 if (mPriDepth >= 1) {
2159 if (mPriDepth == 1) {
2160 if (source != prio128267) return;
2161 } else {
2162 prio128267 = source;
2163 }
2164
2165 unsigned char SID;
2166 double DepthBelowTransducer, Offset, Range;
2167
2168 // Get water depth
2169 if (ParseN2kPGN128267(v, SID, DepthBelowTransducer, Offset, Range)) {
2170 if (!N2kIsNA(DepthBelowTransducer)) {
2171 double depth = DepthBelowTransducer;
2172 // Set prio to sensor's offset
2173 if (!std::isnan(Offset) && !N2kIsNA(Offset))
2174 depth += Offset;
2175 else
2176 (depth += g_dDashDBTOffset);
2177
2178 SendSentenceToAllInstruments(
2179 OCPN_DBP_STC_DPT,
2180 toUsrDistance_Plugin(depth / 1852.0, g_iDashDepthUnit),
2181 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2182 mPriDepth = 1;
2183 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2184 }
2185 }
2186 }
2187}
2188
2189void dashboard_pi::HandleN2K_128275(ObservedEvt ev) {
2190 NMEA2000Id id_128275(128275);
2191 std::vector<uint8_t> v = GetN2000Payload(id_128275, ev);
2192 uint16_t DaysSince1970;
2193 double SecondsSinceMidnight;
2194 uint32_t Log, TripLog;
2195
2196 // Get log & Trip log
2197 if (ParseN2kPGN128275(v, DaysSince1970, SecondsSinceMidnight, Log, TripLog)) {
2198 if (!N2kIsNA(Log)) {
2199 double m_slog = METERS2NM((double)Log);
2200 SendSentenceToAllInstruments(
2201 OCPN_DBP_STC_VLW2, toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
2202 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
2203 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
2204 }
2205 }
2206 if (!N2kIsNA(TripLog)) {
2207 double m_tlog = METERS2NM((double)TripLog);
2208 SendSentenceToAllInstruments(
2209 OCPN_DBP_STC_VLW1, toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
2210 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
2211 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
2212 }
2213}
2214
2215void dashboard_pi::HandleN2K_128259(ObservedEvt ev) {
2216 NMEA2000Id id_128259(128259);
2217 std::vector<uint8_t> v = GetN2000Payload(id_128259, ev);
2218
2219 // Get a uniqe ID to prioritize source(s)
2220 unsigned char source_id = v.at(7);
2221 char ss[4];
2222 sprintf(ss, "%d", source_id);
2223 std::string ident = std::string(ss);
2224 std::string source = GetN2000Source(id_128259, ev);
2225 source += ":" + ident;
2226
2227 if (mPriSTW >= 1) {
2228 if (mPriSTW == 1) {
2229 if (source != prio128259) return;
2230 } else {
2231 prio128259 = source;
2232 }
2233
2234 unsigned char SID;
2235 double WaterReferenced, GroundReferenced;
2236 tN2kSpeedWaterReferenceType SWRT;
2237
2238 // Get speed through water
2239 if (ParseN2kPGN128259(v, SID, WaterReferenced, GroundReferenced, SWRT)) {
2240 if (!N2kIsNA(WaterReferenced)) {
2241 double stw_knots = MS2KNOTS(WaterReferenced);
2242 SendSentenceToAllInstruments(
2243 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
2244 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2245 mPriSTW = 1;
2246 mSTW_Watchdog = gps_watchdog_timeout_ticks;
2247 }
2248 }
2249 }
2250}
2251
2252void dashboard_pi::HandleN2K_128777(ObservedEvt ev) {
2253 NMEA2000Id id_128777(128777);
2254 std::vector<uint8_t> v = GetN2000Payload(id_128777, ev);
2255 // Get Windlass rode count
2256 unsigned char SID;
2257 unsigned char WindlassIdentifier;
2258 double RodeCounterValue;
2259 double WindlassLineSpeed;
2260 tN2kWindlassMotionStates WindlassMotionStatus;
2261 tN2kRodeTypeStates RodeTypeStatus;
2262 tN2kAnchorDockingStates AnchorDockingStatus;
2263 tN2kWindlassOperatingEvents WindlassOperatingEvents;
2264
2265 if (ParseN2kPGN128777(v, SID, WindlassIdentifier, RodeCounterValue,
2266 WindlassLineSpeed, WindlassMotionStatus, RodeTypeStatus,
2267 AnchorDockingStatus, WindlassOperatingEvents)) {
2268 if (!N2kIsNA(RodeCounterValue)) {
2269 SendSentenceToAllInstruments(OCPN_DBP_STC_WCC, RodeCounterValue, "m");
2270 mWCC_Watchdog = no_nav_watchdog_timeout_ticks;
2271 }
2272 }
2273}
2274
2275wxString talker_N2k = wxEmptyString;
2276void dashboard_pi::HandleN2K_129029(ObservedEvt ev) {
2277 NMEA2000Id id_129029(129029);
2278 std::vector<uint8_t> v = GetN2000Payload(id_129029, ev);
2279 // Get a uniqe ID to prioritize source(s)
2280 unsigned char source_id = v.at(7);
2281 char ss[4];
2282 sprintf(ss, "%d", source_id);
2283 std::string ident = std::string(ss);
2284 std::string source = GetN2000Source(id_129029, ev);
2285 source += ":" + ident;
2286 // Use the source prioritized by OCPN only
2287 if (source != prioN2kPGNsat) return;
2288
2289 unsigned char SID;
2290 uint16_t DaysSince1970;
2291 double SecondsSinceMidnight;
2292 double Latitude, Longitude, Altitude;
2293 tN2kGNSStype GNSStype;
2294 tN2kGNSSmethod GNSSmethod;
2295 unsigned char nSatellites;
2296 double HDOP, PDOP, GeoidalSeparation;
2297 unsigned char nReferenceStations;
2298 tN2kGNSStype ReferenceStationType;
2299 uint16_t ReferenceSationID;
2300 double AgeOfCorrection;
2301
2302 // Get used satellite system
2303 if (ParseN2kPGN129029(v, SID, DaysSince1970, SecondsSinceMidnight, Latitude,
2304 Longitude, Altitude, GNSStype, GNSSmethod, nSatellites,
2305 HDOP, PDOP, GeoidalSeparation, nReferenceStations,
2306 ReferenceStationType, ReferenceSationID,
2307 AgeOfCorrection)) {
2308 switch (GNSStype) {
2309 case 0:
2310 talker_N2k = "GP";
2311 break; // GPS
2312 case 1:
2313 talker_N2k = "GL";
2314 break; // GLONASS
2315 case 2:
2316 talker_N2k = "GPSGLONAS";
2317 break;
2318 case 3:
2319 talker_N2k = "GP";
2320 break;
2321 case 4:
2322 talker_N2k = "GPSGLONAS";
2323 break;
2324 case 5:
2325 talker_N2k = "Chayka";
2326 break;
2327 case 8:
2328 talker_N2k = "GA";
2329 break; // Galileo
2330 default:
2331 talker_N2k = wxEmptyString;
2332 }
2333 if (!N2kIsNA(Altitude)) {
2334 if (mPriAlt >= 1) {
2335 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, Altitude, _T("m"));
2336 mPriAlt = 1;
2337 mALT_Watchdog = gps_watchdog_timeout_ticks;
2338 }
2339 }
2340 }
2341}
2342
2343void dashboard_pi::HandleN2K_129540(ObservedEvt ev) {
2344 NMEA2000Id id_129540(129540);
2345 std::vector<uint8_t> v = GetN2000Payload(id_129540, ev);
2346
2347 // Get a uniqe ID to prioritize source(s)
2348 unsigned char source_id = v.at(7);
2349 char ss[4];
2350 sprintf(ss, "%d", source_id);
2351 std::string ident = std::string(ss);
2352 std::string source = GetN2000Source(id_129540, ev);
2353 source += ":" + ident;
2354 // Use the source prioritized by OCPN only
2355 if (source != prioN2kPGNsat) return;
2356
2357 unsigned char SID;
2358 tN2kRangeResidualMode Mode;
2359 uint8_t NumberOfSVs;
2360
2361 // Get the GNSS status data
2362 if (ParseN2kPGN129540(v, SID, Mode, NumberOfSVs)) {
2363 if (!N2kIsNA(NumberOfSVs) && mPriSatStatus == 1) {
2364 // Step through each satellite, one-by-one
2365 // Arrange to max three messages with up to 4 sats each like N0183 GSV
2366 SAT_INFO N2K_SatInfo[4];
2367 int iPRN = 0;
2368 int iSNR = 0;
2369 double dElevRad = 0;
2370 double dAzimRad = 0;
2371 int idx = 0;
2372 uint8_t index = 0;
2373 for (int iMesNum = 0; iMesNum < 3; iMesNum++) {
2374 for (idx = 0; idx < 4; idx++) {
2375 tSatelliteInfo SatelliteInfo;
2376 index = idx + 4 * iMesNum;
2377 if (index >= NumberOfSVs - 1) break;
2378 if (ParseN2kPGN129540(v, index, SatelliteInfo)) {
2379 iPRN = (int)SatelliteInfo.PRN;
2380 dElevRad = SatelliteInfo.Elevation;
2381 dAzimRad = SatelliteInfo.Azimuth;
2382 iSNR = N2kIsNA(SatelliteInfo.SNR) ? 0 : (int)SatelliteInfo.SNR;
2383
2384 N2K_SatInfo[idx].SatNumber = iPRN;
2385 N2K_SatInfo[idx].ElevationDegrees = GEODESIC_RAD2DEG(dElevRad);
2386 N2K_SatInfo[idx].AzimuthDegreesTrue = GEODESIC_RAD2DEG(dAzimRad);
2387 N2K_SatInfo[idx].SignalToNoiseRatio = iSNR;
2388 }
2389 }
2390 // Send to GPS.cpp
2391 if (idx > 0) {
2392 SendSatInfoToAllInstruments(NumberOfSVs, iMesNum + 1, talker_N2k,
2393 N2K_SatInfo);
2394 // mPriSatStatus = 1;
2395 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
2396 }
2397 }
2398 }
2399 }
2400}
2401
2402// Wind PGN 130306
2403void dashboard_pi::HandleN2K_130306(ObservedEvt ev) {
2404 NMEA2000Id id_130306(130306);
2405 std::vector<uint8_t> v = GetN2000Payload(id_130306, ev);
2406
2407 // No source prioritization for 130306 because there are
2408 // multiple variables that can come from different sources.
2409
2410 unsigned char SID;
2411 double WindSpeed, WindAngle;
2412 tN2kWindReference WindReference;
2413
2414 // Get wind data
2415 if (ParseN2kPGN130306(v, SID, WindSpeed, WindAngle, WindReference)) {
2416 if (!N2kIsNA(WindSpeed) && !N2kIsNA(WindAngle)) {
2417 double wind_angle_degr = GEODESIC_RAD2DEG(WindAngle);
2418 double wind_speed_kn = MS2KNOTS(WindSpeed);
2419 bool sendTWA = false, sendTWS = false;
2420
2421 switch (WindReference) {
2422 case 0: // N2kWind direction True North
2423 if (mPriWDN >= 1) {
2424 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, wind_angle_degr,
2425 _T("\u00B0"));
2426 mPriWDN = 1;
2427 sendTWS = true;
2428 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2429 }
2430 break;
2431 case 1: // N2kWind direction Magnetic North
2432 if (mPriWDN >= 1) {
2433 // Make it true if variation is available
2434 if (!std::isnan(mVar)) {
2435 wind_angle_degr += mVar;
2436 if (wind_angle_degr > 360.) {
2437 wind_angle_degr -= 360;
2438 } else if (wind_angle_degr < 0.) {
2439 wind_angle_degr += 360;
2440 }
2441 }
2442 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, wind_angle_degr,
2443 _T("\u00B0"));
2444 mPriWDN = 1;
2445 sendTWS = true;
2446 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2447 }
2448 break;
2449 case 2: // N2kWind_Apparent_centerline
2450 if (mPriAWA >= 1) {
2451 double calc_angle = wind_angle_degr;
2452 // Angle equals 0-360 degr
2453 wxString m_awaunit = _T("\u00B0R");
2454 // Should be unit "L" and 0-180 to port
2455 if (wind_angle_degr > 180.0) {
2456 wind_angle_degr = 360.0 - wind_angle_degr;
2457 m_awaunit = _T("\u00B0L");
2458 }
2459 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
2460 mAWAFilter.filter(wind_angle_degr),
2461 m_awaunit);
2462 // Speed
2463 SendSentenceToAllInstruments(
2464 OCPN_DBP_STC_AWS,
2465 toUsrSpeed_Plugin(mAWSFilter.filter(wind_speed_kn),
2466 g_iDashWindSpeedUnit),
2467 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2468
2469 mPriAWA = 1;
2470 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
2471
2472 // If not N2K true wind data are recently received calculate it.
2473 if (mPriTWA != 1) {
2474 // Wants -+ angle instead of "L"/"R"
2475 if (calc_angle > 180) calc_angle -= 360.0;
2476 CalculateAndUpdateTWDS(wind_speed_kn, calc_angle);
2477 mPriTWA = 2;
2478 mPriWDN = 2;
2479 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2480 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2481 }
2482 }
2483 break;
2484 case 3: // N2kWind_True_centerline_boat(ground)
2485 if (mPriTWA >= 1 && g_bDBtrueWindGround) {
2486 sendTWA = true;
2487 sendTWS = true;
2488 mPriTWA = 1;
2489 }
2490 break;
2491 case 4: // N2kWind_True_Centerline__water
2492 if (mPriTWA >= 1 && !g_bDBtrueWindGround) {
2493 sendTWA = true;
2494 sendTWS = true;
2495 mPriTWA = 1;
2496 }
2497 break;
2498 case 6: // N2kWind_Error
2499 break;
2500 case 7: // N2kWind_Unavailable
2501 break;
2502 default:
2503 break;
2504 }
2505
2506 if (sendTWA) {
2507 // Wind angle is 0-360 degr
2508 wxString m_twaunit = _T("\u00B0R");
2509 // Should be unit "L" and 0-180 to port
2510 if (wind_angle_degr > 180.0) {
2511 wind_angle_degr = 360.0 - wind_angle_degr;
2512 m_twaunit = _T("\u00B0L");
2513 }
2514 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, wind_angle_degr,
2515 m_twaunit);
2516 }
2517
2518 // Wind speed
2519 if (sendTWS) {
2520 SendSentenceToAllInstruments(
2521 OCPN_DBP_STC_TWS,
2522 toUsrSpeed_Plugin(wind_speed_kn, g_iDashWindSpeedUnit),
2523 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2524 SendSentenceToAllInstruments(
2525 OCPN_DBP_STC_TWS2,
2526 toUsrSpeed_Plugin(wind_speed_kn, g_iDashWindSpeedUnit),
2527 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2528 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2529 }
2530 }
2531 }
2532}
2533
2534void dashboard_pi::HandleN2K_130310(ObservedEvt ev) {
2535 NMEA2000Id id_130310(130310);
2536 std::vector<uint8_t> v = GetN2000Payload(id_130310, ev);
2537 unsigned char SID;
2538 double WaterTemperature, OutsideAmbientAirTemperature, AtmosphericPressure;
2539
2540 // Outside Environmental parameters
2541 if (ParseN2kPGN130310(v, SID, WaterTemperature, OutsideAmbientAirTemperature,
2542 AtmosphericPressure)) {
2543 if (mPriWTP >= 1) {
2544 if (!N2kIsNA(WaterTemperature)) {
2545 double m_wtemp KELVIN2C(WaterTemperature);
2546 SendSentenceToAllInstruments(OCPN_DBP_STC_TMP,
2547 toUsrTemp_Plugin(m_wtemp, g_iDashTempUnit),
2548 getUsrTempUnit_Plugin(g_iDashTempUnit));
2549 mPriWTP = 1;
2550 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
2551 }
2552 }
2553
2554 if (mPriATMP >= 1) {
2555 if (!N2kIsNA(OutsideAmbientAirTemperature)) {
2556 double m_airtemp = KELVIN2C(OutsideAmbientAirTemperature);
2557 if (m_airtemp > -60 && m_airtemp < 100) {
2558 SendSentenceToAllInstruments(
2559 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(m_airtemp, g_iDashTempUnit),
2560 getUsrTempUnit_Plugin(g_iDashTempUnit));
2561 mPriATMP = 1;
2562 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
2563 }
2564 }
2565 }
2566
2567 if (!N2kIsNA(AtmosphericPressure) && mPriMDA >= 1) {
2568 double m_press = PA2HPA(AtmosphericPressure);
2569 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, _T("hPa"));
2570 mPriMDA = 1;
2571 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
2572 }
2573 }
2574}
2575// Humidity (Rel %)
2576void dashboard_pi::HandleN2K_130313(ObservedEvt ev) {
2577 NMEA2000Id id_130313(130313);
2578 std::vector<uint8_t> v = GetN2000Payload(id_130313, ev);
2579 unsigned char SID, HumidityInstance;
2580 tN2kHumiditySource HumiditySource;
2581 double ActualHumidity, SetHumidity;
2582
2583 if (ParseN2kPGN130313(v, SID, HumidityInstance, HumiditySource,
2584 ActualHumidity, SetHumidity)) {
2585 if (mPriHUM >= 1) {
2586 if (!N2kIsNA(ActualHumidity)) {
2587 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, ActualHumidity, "%");
2588 mPriHUM = 1;
2589 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
2590 }
2591 }
2592 }
2593}
2594
2595/****** Signal K *******/
2596void dashboard_pi::ParseSignalK(wxString &msg) {
2597 wxJSONValue root;
2598 wxJSONReader jsonReader;
2599
2600 int errors = jsonReader.Parse(msg, &root);
2601
2602 // wxString dmsg( _T("Dashboard:SignalK Event received: ") );
2603 // dmsg.append(msg);
2604 // wxLogMessage(dmsg);
2605 // printf("%s\n", dmsg.ToUTF8().data());
2606
2607 if (root.HasMember("self")) {
2608 if (root["self"].AsString().StartsWith(_T("vessels.")))
2609 m_self = (root["self"].AsString()); // for java server, and OpenPlotter
2610 // node.js server 1.20
2611 else if (root["self"].AsString().Length())
2612 m_self =
2613 _T("vessels.") + (root["self"].AsString()); // for Node.js server
2614 }
2615
2616 if (root.HasMember("context") && root["context"].IsString()) {
2617 auto context = root["context"].AsString();
2618 if (context != m_self) {
2619 return;
2620 }
2621 }
2622
2623 if (root.HasMember("updates") && root["updates"].IsArray()) {
2624 wxJSONValue &updates = root["updates"];
2625 for (int i = 0; i < updates.Size(); ++i) {
2626 handleSKUpdate(updates[i]);
2627 }
2628 }
2629}
2630
2631void dashboard_pi::handleSKUpdate(wxJSONValue &update) {
2632 wxString sfixtime = "";
2633
2634 if (update.HasMember("timestamp")) {
2635 sfixtime = update["timestamp"].AsString();
2636 }
2637 if (update.HasMember("values") && update["values"].IsArray()) {
2638 wxString talker = wxEmptyString;
2639 if (update.HasMember("source")) {
2640 if (update["source"].HasMember("talker")) {
2641 if (update["source"]["talker"].IsString()) {
2642 talker = update["source"]["talker"].AsString();
2643 }
2644 }
2645 }
2646 for (int j = 0; j < update["values"].Size(); ++j) {
2647 wxJSONValue &item = update["values"][j];
2648 updateSKItem(item, talker, sfixtime);
2649 }
2650 }
2651}
2652
2653void dashboard_pi::updateSKItem(wxJSONValue &item, wxString &talker,
2654 wxString &sfixtime) {
2655 if (item.HasMember("path") && item.HasMember("value")) {
2656 const wxString &update_path = item["path"].AsString();
2657 wxJSONValue &value = item["value"];
2658
2659 // Container for last received sat-system info from SK-N2k
2660 static wxString talkerID = wxEmptyString;
2661 // Container for last received AWA that may be needed for TWS calculation
2662 static double skAWA;
2663
2664 if (update_path == _T("navigation.position")) {
2665 if (mPriPosition >= 2) {
2666 if (value["latitude"].IsDouble() && value["longitude"].IsDouble()) {
2667 double lat = value["latitude"].AsDouble();
2668 double lon = value["longitude"].AsDouble();
2669 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, _T("SDMM"));
2670 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, _T("SDMM"));
2671 mPriPosition = 2;
2672 }
2673 }
2674 } else if (update_path == _T("navigation.speedOverGround") &&
2675 2 == mPriPosition) {
2676 double sog_knot = GetJsonDouble(value);
2677 if (std::isnan(sog_knot)) return;
2678
2679 SendSentenceToAllInstruments(
2680 OCPN_DBP_STC_SOG,
2681 toUsrSpeed_Plugin(mSOGFilter.filter(sog_knot), g_iDashSpeedUnit),
2682 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2683 } else if (update_path == _T("navigation.courseOverGroundTrue") &&
2684 2 == mPriPosition) {
2685 double cog_rad = GetJsonDouble(value);
2686 if (std::isnan(cog_rad)) return;
2687
2688 double cog_deg = GEODESIC_RAD2DEG(cog_rad);
2689 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(cog_deg),
2690 _T("\u00B0"));
2691 } else if (update_path == _T("navigation.headingTrue")) {
2692 if (mPriHeadingT >= 2) {
2693 double hdt = GetJsonDouble(value);
2694 if (std::isnan(hdt)) return;
2695
2696 hdt = GEODESIC_RAD2DEG(hdt);
2697 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, _T("\u00B0T"));
2698 mPriHeadingT = 2;
2699 mHDT_Watchdog = gps_watchdog_timeout_ticks;
2700 }
2701 } else if (update_path == _T("navigation.headingMagnetic")) {
2702 if (mPriHeadingM >= 2) {
2703 double hdm = GetJsonDouble(value);
2704 if (std::isnan(hdm)) return;
2705
2706 hdm = GEODESIC_RAD2DEG(hdm);
2707 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, _T("\u00B0M"));
2708 mPriHeadingM = 2;
2709 mHDx_Watchdog = gps_watchdog_timeout_ticks;
2710
2711 // If no higher priority HDT, calculate it here.
2712 if (mPriHeadingT >= 6 && (!std::isnan(mVar))) {
2713 double heading = hdm + mVar;
2714 if (heading < 0)
2715 heading += 360;
2716 else if (heading >= 360.0)
2717 heading -= 360;
2718 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, _T("\u00B0"));
2719 mPriHeadingT = 6;
2720 mHDT_Watchdog = gps_watchdog_timeout_ticks;
2721 }
2722 }
2723 } else if (update_path == _T("navigation.speedThroughWater")) {
2724 if (mPriSTW >= 2) {
2725 double stw_knots = GetJsonDouble(value);
2726 if (std::isnan(stw_knots)) return;
2727
2728 stw_knots = MS2KNOTS(stw_knots);
2729 SendSentenceToAllInstruments(
2730 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
2731 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2732 mPriSTW = 2;
2733 mSTW_Watchdog = gps_watchdog_timeout_ticks;
2734 }
2735 } else if (update_path == _T("navigation.magneticVariation")) {
2736 if (mPriVar >= 2) {
2737 double dvar = GetJsonDouble(value);
2738 if (std::isnan(dvar)) return;
2739
2740 dvar = GEODESIC_RAD2DEG(dvar);
2741 if (0.0 != dvar) { // Let WMM do the job instead
2742 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, dvar, _T("\u00B0"));
2743 mPriVar = 2;
2744 mVar_Watchdog = gps_watchdog_timeout_ticks;
2745 }
2746 }
2747 } else if (update_path == _T("environment.wind.angleApparent")) {
2748 if (mPriAWA >= 2) {
2749 double m_awaangle = GetJsonDouble(value);
2750 if (std::isnan(m_awaangle)) return;
2751
2752 m_awaangle = GEODESIC_RAD2DEG(m_awaangle); // negative to port
2753 skAWA = m_awaangle;
2754 wxString m_awaunit = _T("\u00B0R");
2755 if (m_awaangle < 0) {
2756 m_awaunit = _T("\u00B0L");
2757 m_awaangle *= -1;
2758 }
2759 SendSentenceToAllInstruments(OCPN_DBP_STC_AWA,
2760 mAWAFilter.filter(m_awaangle), m_awaunit);
2761 mPriAWA = 2; // Set prio only here. No need to catch speed if no angle.
2762 mMWVA_Watchdog = gps_watchdog_timeout_ticks;
2763 }
2764 } else if (update_path == _T("environment.wind.speedApparent")) {
2765 if (mPriAWA >= 2) {
2766 double m_awaspeed_kn = GetJsonDouble(value);
2767 if (std::isnan(m_awaspeed_kn)) return;
2768
2769 m_awaspeed_kn = MS2KNOTS(m_awaspeed_kn);
2770 SendSentenceToAllInstruments(
2771 OCPN_DBP_STC_AWS,
2772 toUsrSpeed_Plugin(mAWSFilter.filter(m_awaspeed_kn),
2773 g_iDashWindSpeedUnit),
2774 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2775
2776 // If no TWA from SK try to use AWS/AWA to calculate it
2777 if (mPriTWA >= 6 && !std::isnan(skAWA)) {
2778 CalculateAndUpdateTWDS(m_awaspeed_kn, skAWA);
2779 mPriTWA = 6;
2780 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2781 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2782 }
2783 }
2784 } else if ((update_path == _T("environment.wind.angleTrueWater") &&
2785 !g_bDBtrueWindGround) ||
2786 (update_path == _T("environment.wind.angleTrueGround") &&
2787 g_bDBtrueWindGround)) {
2788 if (mPriTWA >= 3) {
2789 double m_twaangle = GetJsonDouble(value);
2790 if (std::isnan(m_twaangle)) return;
2791
2792 m_twaangle = GEODESIC_RAD2DEG(m_twaangle);
2793 double m_twaangle_raw = m_twaangle; // for wind history
2794 wxString m_twaunit = _T("\u00B0R");
2795 if (m_twaangle < 0) {
2796 m_twaunit = _T("\u00B0L");
2797 m_twaangle *= -1;
2798 }
2799 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle, m_twaunit);
2800 mPriTWA = 3; // Set prio only here. No need to catch speed if no angle.
2801 mMWVT_Watchdog = gps_watchdog_timeout_ticks;
2802
2803 if (mPriWDN >= 5) {
2804 // m_twaangle_raw has wind angle relative to the bow.
2805 // Wind history use angle relative to north.
2806 // If no TWD with higher priority is present and
2807 // true heading is available calculate it.
2808 if (g_dHDT < 361. && g_dHDT >= 0.0) {
2809 double g_dCalWdir = (m_twaangle_raw) + g_dHDT;
2810 if (g_dCalWdir > 360.) {
2811 g_dCalWdir -= 360;
2812 } else if (g_dCalWdir < 0.) {
2813 g_dCalWdir += 360;
2814 }
2815 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir,
2816 _T("\u00B0"));
2817 mPriWDN = 5;
2818 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
2819 }
2820 }
2821 }
2822 } else if ((update_path == _T("environment.wind.speedTrue") &&
2823 !g_bDBtrueWindGround) ||
2824 (update_path == _T("environment.wind.speedOverGround") &&
2825 g_bDBtrueWindGround)) {
2826 if (mPriTWA >= 3) {
2827 double m_twaspeed_kn = GetJsonDouble(value);
2828 if (std::isnan(m_twaspeed_kn)) return;
2829
2830 m_twaspeed_kn = MS2KNOTS(m_twaspeed_kn);
2831 SendSentenceToAllInstruments(
2832 OCPN_DBP_STC_TWS,
2833 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2834 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2835 SendSentenceToAllInstruments(
2836 OCPN_DBP_STC_TWS2,
2837 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2838 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2839 }
2840 } else if (update_path == _T("environment.depth.belowSurface")) {
2841 if (mPriDepth >= 3) {
2842 double depth = GetJsonDouble(value);
2843 if (std::isnan(depth)) return;
2844
2845 mPriDepth = 3;
2846 depth += g_dDashDBTOffset;
2847 depth /= 1852.0;
2848 SendSentenceToAllInstruments(
2849 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2850 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2851 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2852 }
2853 } else if (update_path == _T("environment.depth.belowTransducer")) {
2854 if (mPriDepth >= 3) {
2855 double depth = GetJsonDouble(value);
2856 if (std::isnan(depth)) return;
2857
2858 mPriDepth = 3;
2859 depth += g_dDashDBTOffset;
2860 depth /= 1852.0;
2861 SendSentenceToAllInstruments(
2862 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2863 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2864 mDPT_DBT_Watchdog = gps_watchdog_timeout_ticks;
2865 }
2866 } else if (update_path == _T("environment.water.temperature")) {
2867 if (mPriWTP >= 2) {
2868 double m_wtemp = GetJsonDouble(value);
2869 if (std::isnan(m_wtemp)) return;
2870
2871 m_wtemp = KELVIN2C(m_wtemp);
2872 if (m_wtemp > -60 && m_wtemp < 200 && !std::isnan(m_wtemp)) {
2873 SendSentenceToAllInstruments(
2874 OCPN_DBP_STC_TMP, toUsrTemp_Plugin(m_wtemp, g_iDashTempUnit),
2875 getUsrTempUnit_Plugin(g_iDashTempUnit));
2876 mPriWTP = 2;
2877 mWTP_Watchdog = no_nav_watchdog_timeout_ticks;
2878 }
2879 }
2880 } else if (update_path ==
2881 _T("navigation.courseRhumbline.nextPoint.velocityMadeGood")) {
2882 double m_vmg_kn = GetJsonDouble(value);
2883 if (std::isnan(m_vmg_kn)) return;
2884
2885 m_vmg_kn = MS2KNOTS(m_vmg_kn);
2886 SendSentenceToAllInstruments(
2887 OCPN_DBP_STC_VMG, toUsrSpeed_Plugin(m_vmg_kn, g_iDashSpeedUnit),
2888 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2889 mVMG_Watchdog = gps_watchdog_timeout_ticks;
2890 }
2891
2892 else if (update_path == _T("performance.velocityMadeGood")) {
2893 double m_vmgw_kn = GetJsonDouble(value);
2894 if (std::isnan(m_vmgw_kn)) return;
2895
2896 m_vmgw_kn = MS2KNOTS(m_vmgw_kn);
2897 SendSentenceToAllInstruments(
2898 OCPN_DBP_STC_VMGW, toUsrSpeed_Plugin(m_vmgw_kn, g_iDashSpeedUnit),
2899 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2900 mVMGW_Watchdog = gps_watchdog_timeout_ticks;
2901 }
2902
2903 else if (update_path == _T("steering.rudderAngle")) { // ->port
2904 if (mPriRSA >= 2) {
2905 double m_rudangle = GetJsonDouble(value);
2906 if (std::isnan(m_rudangle)) return;
2907
2908 m_rudangle = GEODESIC_RAD2DEG(m_rudangle);
2909 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle,
2910 _T("\u00B0"));
2911 mRSA_Watchdog = gps_watchdog_timeout_ticks;
2912 mPriRSA = 2;
2913 }
2914 } else if (update_path ==
2915 _T("navigation.gnss.satellites")) { // GNSS satellites in use
2916 if (mPriSatUsed >= 2) {
2917 int usedSats = (value).AsInt();
2918 if (usedSats < 1) return;
2919 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, usedSats, _T (""));
2920 mPriSatUsed = 2;
2921 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
2922 }
2923 } else if (update_path == _T("navigation.gnss.type")) {
2924 if (value.IsString() && value.AsString() != wxEmptyString) {
2925 talkerID = (value.AsString()); // Like "Combined GPS/GLONASS"
2926 talkerID.MakeUpper();
2927 m_PriN2kTalker = gps_watchdog_timeout_ticks;
2928 if ((talkerID.Contains(_T("GPS"))) &&
2929 (talkerID.Contains(_T("GLONASS"))))
2930 talkerID = _T("GPSGLONAS");
2931 else if (talkerID.Contains(_T("GPS")))
2932 talkerID = _T("GP");
2933 else if (talkerID.Contains(_T("GLONASS")))
2934 talkerID = _T("GL");
2935 else if (talkerID.Contains(_T("GALILEO")))
2936 talkerID = _T("GA");
2937 else if (talkerID.Contains(_T("BEIDOU")))
2938 talkerID = _T("GI");
2939 }
2940 } else if (update_path ==
2941 _T("navigation.gnss.satellitesInView")) { // GNSS satellites in
2942 // view
2943 if (mPriSatUsed >= 4) {
2944 if (value.HasMember("count") && value["count"].IsInt()) {
2945 double m_SK_SatsInView = (value["count"].AsInt());
2946 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_SK_SatsInView,
2947 _T (""));
2948 mPriSatUsed = 4;
2949 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
2950 }
2951 }
2952 if (mPriSatStatus == 2) {
2953 if (value.HasMember("satellites") && value["satellites"].IsArray()) {
2954 // Update satellites data.
2955 int iNumSats;
2956 if (value.HasMember("count") && value["count"].IsInt()) {
2957 iNumSats = (value["count"].AsInt());
2958 } else
2959 iNumSats = value[_T ("satellites")].Size();
2960
2961 SAT_INFO SK_SatInfo[4];
2962 for (int idx = 0; idx < 4; idx++) {
2963 SK_SatInfo[idx].SatNumber = 0;
2964 SK_SatInfo[idx].ElevationDegrees = 0;
2965 SK_SatInfo[idx].AzimuthDegreesTrue = 0;
2966 SK_SatInfo[idx].SignalToNoiseRatio = 0;
2967 }
2968
2969 if (iNumSats) {
2970 // Arrange SK's array[12] to max three messages like NMEA GSV
2971 int iID = 0;
2972 int iSNR = 0;
2973 double dElevRad = 0;
2974 double dAzimRad = 0;
2975 int idx = 0;
2976 int arr = 0;
2977 for (int iMesNum = 0; iMesNum < 3; iMesNum++) {
2978 for (idx = 0; idx < 4; idx++) {
2979 arr = idx + 4 * iMesNum;
2980 if (value["satellites"][arr]["id"].IsInt())
2981 iID = value["satellites"][arr]["id"].AsInt();
2982 if (value["satellites"][arr]["elevation"].IsDouble())
2983 dElevRad = value["satellites"][arr]["elevation"].AsDouble();
2984 if (value["satellites"][arr]["azimuth"].IsDouble())
2985 dAzimRad = value["satellites"][arr]["azimuth"].AsDouble();
2986 if (value["satellites"][arr]["SNR"].IsInt())
2987 iSNR = value["satellites"][arr]["SNR"].AsInt();
2988
2989 if (iID < 1) break;
2990 SK_SatInfo[idx].SatNumber = iID;
2991 SK_SatInfo[idx].ElevationDegrees = GEODESIC_RAD2DEG(dElevRad);
2992 SK_SatInfo[idx].AzimuthDegreesTrue = GEODESIC_RAD2DEG(dAzimRad);
2993 SK_SatInfo[idx].SignalToNoiseRatio = iSNR;
2994 }
2995 if (idx > 0) {
2996 if (m_PriN2kTalker <= 0 && talker != wxEmptyString &&
2997 (talker.StartsWith(_T("G")) ||
2998 talker.StartsWith(_T("BD")))) {
2999 talkerID = talker; // Origin NMEA0183
3000 }
3001 SendSatInfoToAllInstruments(iNumSats, iMesNum + 1, talkerID,
3002 SK_SatInfo);
3003 // mPriSatStatus = 2;
3004 mSatStatus_Wdog = gps_watchdog_timeout_ticks;
3005 }
3006
3007 if (iID < 1) break;
3008 }
3009 }
3010 }
3011 }
3012 } else if (update_path == _T("navigation.gnss.antennaAltitude")) {
3013 if (mPriAlt >= 2) {
3014 double m_alt = GetJsonDouble(value);
3015 if (std::isnan(m_alt)) return;
3016
3017 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, m_alt, _T("m"));
3018 mPriAlt = 2;
3019 mALT_Watchdog = gps_watchdog_timeout_ticks;
3020 }
3021
3022 } else if (update_path == _T("navigation.datetime")) {
3023 if (mPriDateTime >= 1) {
3024 mPriDateTime = 1;
3025 wxString s_dt = (value.AsString()); //"2019-12-28T09:26:58.000Z"
3026 s_dt.Replace('-', wxEmptyString);
3027 s_dt.Replace(':', wxEmptyString);
3028 wxString utc_dt = s_dt.BeforeFirst('T'); // Date
3029 utc_dt.Append(s_dt.AfterFirst('T').Left(6)); // time
3030 mUTCDateTime.ParseFormat(utc_dt.c_str(), _T("%Y%m%d%H%M%S"));
3031 mUTC_Watchdog = gps_watchdog_timeout_ticks;
3032 }
3033 } else if (update_path == _T("environment.outside.temperature")) {
3034 if (mPriATMP >= 2) {
3035 double m_airtemp = GetJsonDouble(value);
3036 if (std::isnan(m_airtemp)) return;
3037
3038 m_airtemp = KELVIN2C(m_airtemp);
3039 if (m_airtemp > -60 && m_airtemp < 100) {
3040 SendSentenceToAllInstruments(
3041 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(m_airtemp, g_iDashTempUnit),
3042 getUsrTempUnit_Plugin(g_iDashTempUnit));
3043 mPriATMP = 2;
3044 mATMP_Watchdog = no_nav_watchdog_timeout_ticks;
3045 }
3046 }
3047 } else if (update_path == _T("environment.outside.humidity") ||
3048 update_path == _T("environment.outside.relativeHumidity")) {
3049 if (mPriHUM >= 2) {
3050 double m_hum = GetJsonDouble(value) * 100; // ratio2%
3051 if (std::isnan(m_hum)) return;
3052 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, m_hum, "%");
3053 mPriHUM = 2;
3054 mHUM_Watchdog = no_nav_watchdog_timeout_ticks;
3055 }
3056 } else if (update_path ==
3057 _T("environment.wind.directionTrue")) { // relative true north
3058 if (mPriWDN >= 3) {
3059 double m_twdT = GetJsonDouble(value);
3060 if (std::isnan(m_twdT)) return;
3061
3062 m_twdT = GEODESIC_RAD2DEG(m_twdT);
3063 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdT, _T("\u00B0"));
3064 mPriWDN = 3;
3065 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
3066 }
3067 } else if (update_path == _T("environment.wind.directionMagnetic")) {
3068 // relative magn north
3069 if (mPriWDN >= 4) {
3070 double m_twdM = GetJsonDouble(value);
3071 if (std::isnan(m_twdM)) return;
3072 m_twdM = GEODESIC_RAD2DEG(m_twdM);
3073 // Make it true if variation is available
3074 if (!std::isnan(mVar)) {
3075 m_twdM = (m_twdM) + mVar;
3076 if (m_twdM > 360.) {
3077 m_twdM -= 360;
3078 } else if (m_twdM < 0.) {
3079 m_twdM += 360;
3080 }
3081 }
3082 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdM, _T("\u00B0"));
3083 mPriWDN = 4;
3084 mWDN_Watchdog = no_nav_watchdog_timeout_ticks;
3085 }
3086 } else if (update_path == _T("navigation.trip.log")) { // m
3087 double m_tlog = GetJsonDouble(value);
3088 if (std::isnan(m_tlog)) return;
3089
3090 m_tlog = METERS2NM(m_tlog);
3091 SendSentenceToAllInstruments(
3092 OCPN_DBP_STC_VLW1, toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
3093 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3094 mTrLOG_Watchdog = no_nav_watchdog_timeout_ticks;
3095 } else if (update_path == _T("navigation.log")) { // m
3096 double m_slog = GetJsonDouble(value);
3097 if (std::isnan(m_slog)) return;
3098
3099 m_slog = METERS2NM(m_slog);
3100 SendSentenceToAllInstruments(
3101 OCPN_DBP_STC_VLW2, toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
3102 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3103 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
3104 } else if (update_path == _T("environment.outside.pressure") &&
3105 mPriMDA >= 2) { // Pa
3106 double m_press = GetJsonDouble(value);
3107 if (std::isnan(m_press)) return;
3108
3109 m_press = PA2HPA(m_press);
3110 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, _T("hPa"));
3111 mPriMDA = 2;
3112 mMDA_Watchdog = no_nav_watchdog_timeout_ticks;
3113 } else if (update_path == _T("navigation.attitude")) { // rad
3114 if (mPriPitchRoll >= 2) {
3115 if (value["roll"].AsString() != "0") {
3116 double m_heel = GEODESIC_RAD2DEG(value["roll"].AsDouble());
3117 wxString h_unit = _T("\u00B0\u003E") + _("Stbd");
3118 if (m_heel < 0) {
3119 h_unit = _T("\u00B0\u003C") + _("Port");
3120 m_heel *= -1;
3121 }
3122 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
3123 mHEEL_Watchdog = gps_watchdog_timeout_ticks;
3124 mPriPitchRoll = 2;
3125 }
3126 if (value["pitch"].AsString() != "0") {
3127 double m_pitch = GEODESIC_RAD2DEG(value["pitch"].AsDouble());
3128 wxString p_unit = _T("\u00B0\u2191") + _("Up");
3129 if (m_pitch < 0) {
3130 p_unit = _T("\u00B0\u2193") + _("Down");
3131 m_pitch *= -1;
3132 }
3133 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
3134 mPITCH_Watchdog = gps_watchdog_timeout_ticks;
3135 mPriPitchRoll = 2;
3136 }
3137 }
3138 }
3139 }
3140}
3141
3142/*******Nav data from OCPN core *******/
3144 if (mPriPosition >= 1) {
3145 mPriPosition = 1;
3146 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, pfix.Lat, _T("SDMM"));
3147 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, pfix.Lon, _T("SDMM"));
3148 }
3149 if (mPriCOGSOG >= 1) {
3150 double dMagneticCOG;
3151 mPriCOGSOG = 1;
3152 SendSentenceToAllInstruments(
3153 OCPN_DBP_STC_SOG,
3154 toUsrSpeed_Plugin(mSOGFilter.filter(pfix.Sog), g_iDashSpeedUnit),
3155 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
3156 SendSentenceToAllInstruments(OCPN_DBP_STC_COG, mCOGFilter.filter(pfix.Cog),
3157 _T("\u00B0"));
3158
3159 // Collect distance to update SumLog every second.
3160 double logSOG = mSOGFilter.filter(pfix.Sog);
3161 if (logSOG > 0.4) {
3162 d_tripNM += logSOG / 3600;
3163 // Update SumLog instrument every minute when we are under way.
3164 if (++logCount > 60) {
3165 UpdateSumLog(true);
3166 d_tripNM = 0.0;
3167 logCount = 0;
3168 }
3169 }
3170
3171 dMagneticCOG = mCOGFilter.get() - pfix.Var;
3172 if (dMagneticCOG < 0.0) dMagneticCOG = 360.0 + dMagneticCOG;
3173 if (dMagneticCOG > 360.0) dMagneticCOG = dMagneticCOG - 360.0;
3174 SendSentenceToAllInstruments(OCPN_DBP_STC_MCOG, dMagneticCOG,
3175 _T("\u00B0M"));
3176 }
3177 if (mPriVar >= 1) {
3178 if (!std::isnan(pfix.Var)) {
3179 mPriVar = 1;
3180 mVar = pfix.Var;
3181 mVar_Watchdog = gps_watchdog_timeout_ticks;
3182
3183 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, pfix.Var, _T("\u00B0"));
3184 }
3185 }
3186 if (mPriDateTime >= 6) { // We prefer the GPS datetime
3187 // If GNSS data is available, the value of pfix.FixTime is the GNSS time in
3188 // UTC. If GNSS data is not available, the special value 0 is used to
3189 // indicate that the time is not valid.
3190 if (pfix.FixTime > 0)
3191 mUTCDateTime.Set(pfix.FixTime);
3192 else
3193 mUTCDateTime = wxInvalidDateTime;
3194 if (mUTCDateTime.IsValid()) {
3195 mPriDateTime = 6;
3196 mUTCDateTime = mUTCDateTime.ToUTC();
3197 mUTC_Watchdog = gps_watchdog_timeout_ticks;
3198 }
3199 }
3200 if (mPriSatUsed >= 1) {
3201 mSatsInUse = pfix.nSats;
3202 if (mSatsInUse > 0) {
3203 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, mSatsInUse, _T(""));
3204 mPriSatUsed = 1;
3205 mSatsUsed_Wdog = gps_watchdog_timeout_ticks;
3206 }
3207 }
3208 if (mPriHeadingT >= 1) {
3209 double hdt = pfix.Hdt;
3210 if (std::isnan(hdt)) return;
3211 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, _T("\u00B0T"));
3212 mPriHeadingT = 1;
3213 mHDT_Watchdog = gps_watchdog_timeout_ticks;
3214 }
3215 if (mPriHeadingM >= 1) {
3216 double hdm = pfix.Hdm;
3217 if (std::isnan(hdm) && !std::isnan(pfix.Hdt) && !std::isnan(pfix.Var)) {
3218 hdm = pfix.Hdt - pfix.Var;
3219 if (hdm < 0)
3220 hdm += 360;
3221 else if (hdm >= 360.0)
3222 hdm -= 360;
3223 }
3224 if (std::isnan(hdm)) return;
3225 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, _T("\u00B0M"));
3226 mPriHeadingM = 1;
3227 mHDx_Watchdog = gps_watchdog_timeout_ticks;
3228 }
3229}
3230
3231void dashboard_pi::SetCursorLatLon(double lat, double lon) {
3232 SendSentenceToAllInstruments(OCPN_DBP_STC_PLA, lat, _T("SDMM"));
3233 SendSentenceToAllInstruments(OCPN_DBP_STC_PLO, lon, _T("SDMM"));
3234}
3235
3236void dashboard_pi::SetPluginMessage(wxString &message_id,
3237 wxString &message_body) {
3238 if (message_id == _T("WMM_VARIATION_BOAT")) {
3239 // construct the JSON root object
3240 wxJSONValue root;
3241 // construct a JSON parser
3242 wxJSONReader reader;
3243
3244 // now read the JSON text and store it in the 'root' structure
3245 // check for errors before retreiving values...
3246 int numErrors = reader.Parse(message_body, &root);
3247 if (numErrors > 0) {
3248 // const wxArrayString& errors = reader.GetErrors();
3249 return;
3250 }
3251
3252 // get the DECL value from the JSON message
3253 wxString decl = root[_T("Decl")].AsString();
3254 double decl_val;
3255 decl.ToDouble(&decl_val);
3256
3257 if (mPriVar >= 5) {
3258 mPriVar = 5;
3259 mVar = decl_val;
3260 mVar_Watchdog = gps_watchdog_timeout_ticks;
3261 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, mVar, _T("\u00B0"));
3262 }
3263 } else if (message_id == _T("OCPN_CORE_SIGNALK")) {
3264 ParseSignalK(message_body);
3265 }
3266}
3267
3269
3272 parent, wxID_ANY, m_ArrayOfDashboardWindow);
3273
3274 dialog->RecalculateSize();
3275
3276#ifdef __OCPN__ANDROID__
3277 dialog->GetHandle()->setStyleSheet(qtStyleSheet);
3278#endif
3279
3280#ifdef __OCPN__ANDROID__
3281 wxWindow *ccwin = GetOCPNCanvasWindow();
3282
3283 if (ccwin) {
3284 int xmax = ccwin->GetSize().GetWidth();
3285 int ymax = ccwin->GetParent()
3286 ->GetSize()
3287 .GetHeight(); // This would be the Frame itself
3288 dialog->SetSize(xmax, ymax);
3289 dialog->Layout();
3290
3291 dialog->Move(0, 0);
3292 }
3293#endif
3294
3295 if (dialog->ShowModal() == wxID_OK) {
3296 double scaler = 1.0;
3297 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3298 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3299 scaler = wxMax(1.0, scaler);
3300
3301 g_USFontTitle = *(dialog->m_pFontPickerTitle->GetFontData());
3302 g_FontTitle = *g_pUSFontTitle;
3303 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3304 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
3305 g_USFontTitle = *g_pUSFontTitle;
3306
3307 g_USFontData = *(dialog->m_pFontPickerData->GetFontData());
3308 g_FontData = *g_pUSFontData;
3309 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3310 g_FontData.SetColour(g_pUSFontData->GetColour());
3311 g_USFontData = *g_pUSFontData;
3312
3313 g_USFontLabel = *(dialog->m_pFontPickerLabel->GetFontData());
3314 g_FontLabel = *g_pUSFontLabel;
3315 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3316 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
3317 g_USFontLabel = *g_pUSFontLabel;
3318
3319 g_USFontSmall = *(dialog->m_pFontPickerSmall->GetFontData());
3320 g_FontSmall = *g_pUSFontSmall;
3321 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3322 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
3323 g_USFontSmall = *g_pUSFontSmall;
3324
3325 // OnClose should handle that for us normally but it doesn't seems to do so
3326 // We must save changes first
3327 g_dashPrefWidth = dialog->GetSize().x;
3328 g_dashPrefHeight = dialog->GetSize().y;
3329
3330 dialog->SaveDashboardConfig();
3331 m_ArrayOfDashboardWindow.Clear();
3332 m_ArrayOfDashboardWindow = dialog->m_Config;
3333
3334 ApplyConfig();
3335 SaveConfig();
3336 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3337 }
3338 dialog->Destroy();
3339}
3340
3342 aktuellColorScheme = cs;
3343 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3344 DashboardWindow *dashboard_window =
3345 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3346 if (dashboard_window) dashboard_window->SetColorScheme(cs);
3347 }
3348}
3349
3350int dashboard_pi::GetDashboardWindowShownCount() {
3351 int cnt = 0;
3352
3353 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3354 DashboardWindow *dashboard_window =
3355 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3356 if (dashboard_window) {
3357 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3358 if (pane.IsOk() && pane.IsShown()) cnt++;
3359 }
3360 }
3361 return cnt;
3362}
3363
3364void dashboard_pi::OnPaneClose(wxAuiManagerEvent &event) {
3365 // if name is unique, we should use it
3366 DashboardWindow *dashboard_window = (DashboardWindow *)event.pane->window;
3367 int cnt = 0;
3368 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3369 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3370 DashboardWindow *d_w = cont->m_pDashboardWindow;
3371 if (d_w) {
3372 // we must not count this one because it is being closed
3373 if (dashboard_window != d_w) {
3374 wxAuiPaneInfo &pane = m_pauimgr->GetPane(d_w);
3375 if (pane.IsOk() && pane.IsShown()) cnt++;
3376 } else {
3377 cont->m_bIsVisible = false;
3378 }
3379 }
3380 }
3381 SetToolbarItemState(m_toolbar_item_id, cnt != 0);
3382
3383 event.Skip();
3384}
3385
3387 int cnt = GetDashboardWindowShownCount();
3388
3389 bool b_anyviz = false;
3390 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3391 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3392 if (cont->m_bIsVisible) {
3393 b_anyviz = true;
3394 break;
3395 }
3396 }
3397
3398 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3399 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3400 DashboardWindow *dashboard_window = cont->m_pDashboardWindow;
3401 if (dashboard_window) {
3402 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3403 if (pane.IsOk()) {
3404 bool b_reset_pos = false;
3405
3406#ifdef __WXMSW__
3407 // Support MultiMonitor setups which an allow negative window
3408 // positions. If the requested window title bar does not intersect any
3409 // installed monitor, then default to simple primary monitor
3410 // positioning.
3411 RECT frame_title_rect;
3412 frame_title_rect.left = pane.floating_pos.x;
3413 frame_title_rect.top = pane.floating_pos.y;
3414 frame_title_rect.right = pane.floating_pos.x + pane.floating_size.x;
3415 frame_title_rect.bottom = pane.floating_pos.y + 30;
3416
3417 if (NULL == MonitorFromRect(&frame_title_rect, MONITOR_DEFAULTTONULL))
3418 b_reset_pos = true;
3419#else
3420
3421 // Make sure drag bar (title bar) of window intersects wxClient Area
3422 // of screen, with a little slop...
3423 wxRect window_title_rect; // conservative estimate
3424 window_title_rect.x = pane.floating_pos.x;
3425 window_title_rect.y = pane.floating_pos.y;
3426 window_title_rect.width = pane.floating_size.x;
3427 window_title_rect.height = 30;
3428
3429 wxRect ClientRect = wxGetClientDisplayRect();
3430 ClientRect.Deflate(
3431 60, 60); // Prevent the new window from being too close to the edge
3432 if (!ClientRect.Intersects(window_title_rect)) b_reset_pos = true;
3433
3434#endif
3435
3436 if (b_reset_pos) pane.FloatingPosition(50, 50);
3437
3438 if (cnt == 0)
3439 if (b_anyviz)
3440 pane.Show(cont->m_bIsVisible);
3441 else {
3442 cont->m_bIsVisible = cont->m_bPersVisible;
3443 pane.Show(cont->m_bIsVisible);
3444 }
3445 else
3446 pane.Show(false);
3447 }
3448
3449 // Restore size of docked pane
3450 if (pane.IsShown() && pane.IsDocked()) {
3451 pane.BestSize(cont->m_best_size);
3452 m_pauimgr->Update();
3453 }
3454
3455 // This patch fixes a bug in wxAUIManager
3456 // FS#548
3457 // Dropping a DashBoard Window right on top on the (supposedly fixed)
3458 // chart bar window causes a resize of the chart bar, and the Dashboard
3459 // window assumes some of its properties The Dashboard window is no longer
3460 // grabbable... Workaround: detect this case, and force the pane to be on
3461 // a different Row. so that the display is corrected by toggling the
3462 // dashboard off and back on.
3463 if ((pane.dock_direction == wxAUI_DOCK_BOTTOM) && pane.IsDocked())
3464 pane.Row(2);
3465 }
3466 }
3467 // Toggle is handled by the toolbar but we must keep plugin manager b_toggle
3468 // updated to actual status to ensure right status upon toolbar rebuild
3469 SetToolbarItemState(m_toolbar_item_id,
3470 GetDashboardWindowShownCount() != 0 /*cnt==0*/);
3471 m_pauimgr->Update();
3472}
3473
3475 // This method is called after the PlugIn is initialized
3476 // and the frame has done its initial layout, possibly from a saved
3477 // wxAuiManager "Perspective" It is a chance for the PlugIn to syncronize
3478 // itself internally with the state of any Panes that were added to the
3479 // frame in the PlugIn ctor.
3480
3481 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3482 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3483 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
3484 // Initialize visible state as perspective is loaded now
3485 cont->m_bIsVisible = (pane.IsOk() && pane.IsShown());
3486
3487 // Correct for incomplete AUIManager perspective when docked dashboard is
3488 // not visible at app close.
3489 if (pane.IsDocked()) {
3490 if ((cont->m_persist_size.x > 50) && (cont->m_persist_size.y > 50))
3491 cont->m_pDashboardWindow->SetSize(cont->m_persist_size);
3492 }
3493
3494#ifdef __WXQT__
3495 if (pane.IsShown()) {
3496 pane.Show(false);
3497 m_pauimgr->Update();
3498 pane.Show(true);
3499 m_pauimgr->Update();
3500 }
3501#endif
3502 }
3503 m_pauimgr->Update();
3504
3505 // We use this callback here to keep the context menu selection in sync
3506 // with the window state
3507
3508 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3509}
3510
3511bool dashboard_pi::LoadConfig(void) {
3512 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
3513
3514 if (pConf) {
3515 pConf->SetPath(_T("/PlugIns/Dashboard"));
3516
3517 wxString version;
3518 pConf->Read(_T("Version"), &version, wxEmptyString);
3519 wxString config;
3520
3521 // Set some sensible defaults
3522 wxString TitleFont;
3523 wxString DataFont;
3524 wxString LabelFont;
3525 wxString SmallFont;
3526
3527#ifdef __OCPN__ANDROID__
3528 TitleFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3529 DataFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3530 LabelFont = _T("Roboto,16,-1,5,50,0,0,0,0,0");
3531 SmallFont = _T("Roboto,14,-1,5,50,0,0,0,0,0");
3532#else
3533 TitleFont = g_pFontTitle->GetChosenFont().GetNativeFontInfoDesc();
3534 DataFont = g_pFontData->GetChosenFont().GetNativeFontInfoDesc();
3535 LabelFont = g_pFontLabel->GetChosenFont().GetNativeFontInfoDesc();
3536 SmallFont = g_pFontSmall->GetChosenFont().GetNativeFontInfoDesc();
3537#endif
3538
3539 double scaler = 1.0;
3540 wxFont DummyFont;
3541 wxFont *pDF = &DummyFont;
3542
3543 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3544 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3545 scaler = wxMax(1.0, scaler);
3546
3547 g_pFontTitle = &g_FontTitle;
3548 pConf->Read(_T("FontTitle"), &config, TitleFont);
3549 LoadFont(&pDF, config);
3550 wxFont DummyFontTitle = *pDF;
3551 pConf->Read(_T("ColorTitle"), &config, "#000000");
3552 wxColour DummyColor(config);
3553 g_pUSFontTitle->SetChosenFont(DummyFontTitle);
3554 g_pUSFontTitle->SetColour(DummyColor);
3555
3556 g_FontTitle = *g_pUSFontTitle;
3557 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3558 g_USFontTitle = *g_pUSFontTitle;
3559
3560 g_pFontData = &g_FontData;
3561 pConf->Read(_T("FontData"), &config, DataFont);
3562 LoadFont(&pDF, config);
3563 wxFont DummyFontData = *pDF;
3564 pConf->Read(_T("ColorData"), &config, "#000000");
3565 DummyColor.Set(config);
3566 g_pUSFontData->SetChosenFont(DummyFontData);
3567 g_pUSFontData->SetColour(DummyColor);
3568 g_FontData = *g_pUSFontData;
3569 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3570 g_USFontData = *g_pUSFontData;
3571
3572 pConf->Read(_T("ForceBackgroundColor"), &g_ForceBackgroundColor, 0);
3573 pConf->Read(_T("BackgroundColor"), &config, "DASHL");
3574 g_BackgroundColor.Set(config);
3575
3576 int alignment;
3577 pConf->Read(_T("TitleAlignment"), &alignment, (int)wxALIGN_LEFT);
3578 g_TitleAlignment = (wxAlignment)alignment;
3579 if (g_TitleAlignment == wxALIGN_INVALID) g_TitleAlignment = wxALIGN_LEFT;
3580 pConf->Read(_T("TitleMargin"), &g_iTitleMargin, 5);
3581 pConf->Read(_T("DataShowUnit"), &g_bShowUnit, true);
3582 pConf->Read(_T("DataAlignment"), &alignment, (int)wxALIGN_LEFT);
3583 g_DataAlignment = (wxAlignment)alignment;
3584 if (g_DataAlignment == wxALIGN_INVALID) g_DataAlignment = wxALIGN_LEFT;
3585 pConf->Read(_T("DataMargin"), &g_iDataMargin, 10);
3586 pConf->Read(_T("InstrumentSpacing"), &g_iInstrumentSpacing, 0);
3587 pConf->Read(_T("TitleVerticalOffset"), &g_TitleVerticalOffset, 0.0);
3588
3589 g_pFontLabel = &g_FontLabel;
3590 pConf->Read(_T("FontLabel"), &config, LabelFont);
3591 LoadFont(&pDF, config);
3592 wxFont DummyFontLabel = *pDF;
3593 pConf->Read(_T("ColorLabel"), &config, "#000000");
3594 DummyColor.Set(config);
3595 g_pUSFontLabel->SetChosenFont(DummyFontLabel);
3596 g_pUSFontLabel->SetColour(DummyColor);
3597 g_FontLabel = *g_pUSFontLabel;
3598 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3599 g_USFontLabel = *g_pUSFontLabel;
3600
3601 g_pFontSmall = &g_FontSmall;
3602 pConf->Read(_T("FontSmall"), &config, SmallFont);
3603 LoadFont(&pDF, config);
3604 wxFont DummyFontSmall = *pDF;
3605 pConf->Read(_T("ColorSmall"), &config, "#000000");
3606 DummyColor.Set(config);
3607 g_pUSFontSmall->SetChosenFont(DummyFontSmall);
3608 g_pUSFontSmall->SetColour(DummyColor);
3609 g_FontSmall = *g_pUSFontSmall;
3610 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3611 g_USFontSmall = *g_pUSFontSmall;
3612
3613 pConf->Read(_T("SpeedometerMax"), &g_iDashSpeedMax, 12);
3614 pConf->Read(_T("COGDamp"), &g_iDashCOGDamp, 0);
3615 pConf->Read(_T("SpeedUnit"), &g_iDashSpeedUnit, 0);
3616 pConf->Read(_T("SOGDamp"), &g_iDashSOGDamp, 0);
3617 pConf->Read(_T("DepthUnit"), &g_iDashDepthUnit, 3);
3618 g_iDashDepthUnit = wxMax(g_iDashDepthUnit, 3);
3619 pConf->Read(_T("AWADamp"), &g_iDashAWADamp, 0);
3620 pConf->Read(_T("AWSDamp"), &g_iDashAWSDamp, 0);
3621
3622 pConf->Read(_T("DepthOffset"), &g_dDashDBTOffset, 0);
3623
3624 pConf->Read(_T("DistanceUnit"), &g_iDashDistanceUnit, 0);
3625 pConf->Read(_T("WindSpeedUnit"), &g_iDashWindSpeedUnit, 0);
3626 pConf->Read(_T("UseSignKtruewind"), &g_bDBtrueWindGround, 0);
3627 pConf->Read("UseInternSumlog", &g_bUseInternSumLog, 0);
3628 pConf->Read("SumLogNM", &g_dSumLogNM, 0.0);
3629 pConf->Read(_T("TemperatureUnit"), &g_iDashTempUnit, 0);
3630
3631 pConf->Read(_T("UTCOffset"), &g_iUTCOffset, 0);
3632
3633 pConf->Read(_T("PrefWidth"), &g_dashPrefWidth, 0);
3634 pConf->Read(_T("PrefHeight"), &g_dashPrefHeight, 0);
3635
3636 int d_cnt;
3637 pConf->Read(_T("DashboardCount"), &d_cnt, -1);
3638 // TODO: Memory leak? We should destroy everything first
3639 m_ArrayOfDashboardWindow.Clear();
3640 if (version.IsEmpty() && d_cnt == -1) {
3641 m_config_version = 1;
3642 // Let's load version 1 or default settings.
3643 int i_cnt;
3644 pConf->Read(_T("InstrumentCount"), &i_cnt, -1);
3645 wxArrayInt ar;
3646 wxArrayOfInstrumentProperties Property;
3647 if (i_cnt != -1) {
3648 for (int i = 0; i < i_cnt; i++) {
3649 int id;
3650 pConf->Read(wxString::Format(_T("Instrument%d"), i + 1), &id, -1);
3651 if (id != -1) ar.Add(id);
3652 }
3653 } else {
3654 // This is the default instrument list
3655#ifndef __OCPN__ANDROID__
3656 ar.Add(ID_DBP_I_POS);
3657 ar.Add(ID_DBP_D_COG);
3658 ar.Add(ID_DBP_D_GPS);
3659#else
3660 ar.Add(ID_DBP_I_POS);
3661 ar.Add(ID_DBP_D_COG);
3662 ar.Add(ID_DBP_I_SOG);
3663
3664#endif
3665 }
3666
3668 NULL, MakeName(), _("Dashboard"), _T("V"), ar, Property);
3669 cont->m_bPersVisible = true;
3670 m_ArrayOfDashboardWindow.Add(cont);
3671
3672 } else {
3673 // Version 2
3674 m_config_version = 2;
3675 bool b_onePersisted = false;
3676 wxSize best_size;
3677 wxSize persist_size;
3678 for (int k = 0; k < d_cnt; k++) {
3679 pConf->SetPath(
3680 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%d"), k + 1));
3681 wxString name;
3682 pConf->Read(_T("Name"), &name, MakeName());
3683 wxString caption;
3684 pConf->Read(_T("Caption"), &caption, _("Dashboard"));
3685 wxString orient;
3686 pConf->Read(_T("Orientation"), &orient, _T("V"));
3687 int i_cnt;
3688 pConf->Read(_T("InstrumentCount"), &i_cnt, -1);
3689 bool b_persist;
3690 pConf->Read(_T("Persistence"), &b_persist, 1);
3691 int val;
3692 pConf->Read(_T("BestSizeX"), &val, DefaultWidth);
3693 best_size.x = val;
3694 pConf->Read(_T("BestSizeY"), &val, DefaultWidth);
3695 best_size.y = val;
3696 pConf->Read(_T("PersistSizeX"), &val, DefaultWidth);
3697 persist_size.x = val;
3698 pConf->Read(_T("PersistSizeY"), &val, DefaultWidth);
3699 persist_size.y = val;
3700
3701 wxArrayInt ar;
3702 wxArrayOfInstrumentProperties Property;
3703 for (int i = 0; i < i_cnt; i++) {
3704 int id;
3705 pConf->Read(wxString::Format(_T("Instrument%d"), i + 1), &id, -1);
3706 if (id != -1) {
3707 ar.Add(id);
3708 InstrumentProperties *instp;
3709 if (pConf->Exists(wxString::Format(_T("InstTitleFont%d"), i + 1))) {
3710 instp = new InstrumentProperties(id, i);
3711
3712 pConf->Read(wxString::Format(_T("InstTitleFont%d"), i + 1),
3713 &config, TitleFont);
3714 LoadFont(&pDF, config);
3715 wxFont DummyFontTitleA = *pDF;
3716 pConf->Read(wxString::Format(_T("InstTitleColor%d"), i + 1),
3717 &config, "#000000");
3718 DummyColor.Set(config);
3719 instp->m_USTitleFont.SetChosenFont(DummyFontTitleA);
3720 instp->m_USTitleFont.SetColour(DummyColor);
3721 instp->m_TitleFont = instp->m_USTitleFont;
3722 instp->m_TitleFont.SetChosenFont(
3723 instp->m_USTitleFont.GetChosenFont().Scaled(scaler));
3724
3725 pConf->Read(wxString::Format(_T("InstDataShowUnit%d"), i + 1),
3726 &instp->m_ShowUnit, -1);
3727 pConf->Read(wxString::Format(_T("InstDataMargin%d"), i + 1),
3728 &instp->m_DataMargin, -1);
3729 pConf->Read(wxString::Format(_T("InstDataAlignment%d"), i + 1),
3730 &alignment, (int)wxALIGN_INVALID);
3731 instp->m_DataAlignment = (wxAlignment)alignment;
3732 pConf->Read(
3733 wxString::Format(_T("InstInstrumentSpacing%d"), i + 1),
3734 &instp->m_InstrumentSpacing, -1);
3735 pConf->Read(wxString::Format(_T("InstDataFormat%d"), i + 1),
3736 &instp->m_Format, "");
3737 pConf->Read(wxString::Format(_T("InstTitle%d"), i + 1),
3738 &instp->m_Title, "");
3739
3740 pConf->Read(wxString::Format(_T("InstDataFont%d"), i + 1),
3741 &config, DataFont);
3742 LoadFont(&pDF, config);
3743 wxFont DummyFontDataA = *pDF;
3744 pConf->Read(wxString::Format(_T("InstDataColor%d"), i + 1),
3745 &config, "#000000");
3746 DummyColor.Set(config);
3747 instp->m_USDataFont.SetChosenFont(DummyFontDataA);
3748 instp->m_USDataFont.SetColour(DummyColor);
3749 instp->m_DataFont = instp->m_USDataFont;
3750 instp->m_DataFont.SetChosenFont(
3751 instp->m_USDataFont.GetChosenFont().Scaled(scaler));
3752
3753 pConf->Read(wxString::Format(_T("InstLabelFont%d"), i + 1),
3754 &config, LabelFont);
3755 LoadFont(&pDF, config);
3756 wxFont DummyFontLabelA = *pDF;
3757 pConf->Read(wxString::Format(_T("InstLabelColor%d"), i + 1),
3758 &config, "#000000");
3759 DummyColor.Set(config);
3760 instp->m_USLabelFont.SetChosenFont(DummyFontLabelA);
3761 instp->m_USLabelFont.SetColour(DummyColor);
3762 instp->m_LabelFont = instp->m_USLabelFont;
3763 instp->m_LabelFont.SetChosenFont(
3764 instp->m_USLabelFont.GetChosenFont().Scaled(scaler));
3765
3766 pConf->Read(wxString::Format(_T("InstSmallFont%d"), i + 1),
3767 &config, SmallFont);
3768 LoadFont(&pDF, config);
3769 wxFont DummyFontSmallA = *pDF;
3770 pConf->Read(wxString::Format(_T("InstSmallColor%d"), i + 1),
3771 &config, "#000000");
3772 DummyColor.Set(config);
3773 instp->m_USSmallFont.SetChosenFont(DummyFontSmallA);
3774 instp->m_USSmallFont.SetColour(DummyColor);
3775 instp->m_SmallFont = instp->m_USSmallFont;
3776 instp->m_SmallFont.SetChosenFont(
3777 instp->m_USSmallFont.GetChosenFont().Scaled(scaler));
3778
3779 pConf->Read(wxString::Format(_T("TitleBackColor%d"), i + 1),
3780 &config, "DASHL");
3781 instp->m_TitleBackgroundColour.Set(config);
3782
3783 pConf->Read(wxString::Format(_T("DataBackColor%d"), i + 1),
3784 &config, "DASHB");
3785 instp->m_DataBackgroundColour.Set(config);
3786
3787 pConf->Read(wxString::Format(_T("ArrowFirst%d"), i + 1), &config,
3788 "DASHN");
3789 instp->m_Arrow_First_Colour.Set(config);
3790
3791 pConf->Read(wxString::Format(_T("ArrowSecond%d"), i + 1), &config,
3792 "BLUE3");
3793 instp->m_Arrow_Second_Colour.Set(config);
3794
3795 Property.Add(instp);
3796 }
3797 }
3798 }
3799 // TODO: Do not add if GetCount == 0
3800
3802 NULL, name, caption, orient, ar, Property);
3803 cont->m_bPersVisible = b_persist;
3804 cont->m_conf_best_size = best_size;
3805 cont->m_persist_size = persist_size;
3806
3807 if (b_persist) b_onePersisted = true;
3808
3809 m_ArrayOfDashboardWindow.Add(cont);
3810 }
3811
3812 // Make sure at least one dashboard is scheduled to be visible
3813 if (m_ArrayOfDashboardWindow.Count() && !b_onePersisted) {
3814 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(0);
3815 if (cont) cont->m_bPersVisible = true;
3816 }
3817 }
3818
3819 return true;
3820 } else
3821 return false;
3822}
3823
3824void dashboard_pi::LoadFont(wxFont **target, wxString native_info) {
3825 if (!native_info.IsEmpty()) {
3826#ifdef __OCPN__ANDROID__
3827 wxFont *nf = new wxFont(native_info);
3828 *target = nf;
3829#else
3830 (*target)->SetNativeFontInfo(native_info);
3831#endif
3832 }
3833}
3834
3835bool dashboard_pi::SaveConfig(void) {
3836 wxFileConfig *pConf = (wxFileConfig *)m_pconfig;
3837
3838 if (pConf) {
3839 pConf->SetPath(_T("/PlugIns/Dashboard"));
3840 pConf->Write(_T("Version"), _T("2"));
3841 pConf->Write(_T("FontTitle"),
3842 g_pUSFontTitle->GetChosenFont().GetNativeFontInfoDesc());
3843 pConf->Write(_T("ColorTitle"),
3844 g_pUSFontTitle->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3845 pConf->Write(_T("FontData"),
3846 g_pUSFontData->GetChosenFont().GetNativeFontInfoDesc());
3847 pConf->Write(_T("ColorData"),
3848 g_pUSFontData->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3849 pConf->Write(_T("FontLabel"),
3850 g_pUSFontLabel->GetChosenFont().GetNativeFontInfoDesc());
3851 pConf->Write(_T("ColorLabel"),
3852 g_pUSFontLabel->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3853 pConf->Write(_T("FontSmall"),
3854 g_pUSFontSmall->GetChosenFont().GetNativeFontInfoDesc());
3855 pConf->Write(_T("ColorSmall"),
3856 g_pUSFontSmall->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3857 pConf->Write(_T("SpeedometerMax"), g_iDashSpeedMax);
3858 pConf->Write(_T("COGDamp"), g_iDashCOGDamp);
3859 pConf->Write(_T("SpeedUnit"), g_iDashSpeedUnit);
3860 pConf->Write(_T("SOGDamp"), g_iDashSOGDamp);
3861 pConf->Write(_T("AWSDamp"), g_iDashAWSDamp);
3862 pConf->Write(_T("AWADamp"), g_iDashAWADamp);
3863 pConf->Write(_T("DepthUnit"), g_iDashDepthUnit);
3864 pConf->Write(_T("DepthOffset"), g_dDashDBTOffset);
3865 pConf->Write(_T("DistanceUnit"), g_iDashDistanceUnit);
3866 pConf->Write(_T("WindSpeedUnit"), g_iDashWindSpeedUnit);
3867 pConf->Write("UseInternSumlog", g_bUseInternSumLog);
3868 pConf->Write("SumLogNM", g_dSumLogNM);
3869 pConf->Write(_T("UTCOffset"), g_iUTCOffset);
3870 pConf->Write(_T("UseSignKtruewind"), g_bDBtrueWindGround);
3871 pConf->Write(_T("TemperatureUnit"), g_iDashTempUnit);
3872 pConf->Write(_T("PrefWidth"), g_dashPrefWidth);
3873 pConf->Write(_T("PrefHeight"), g_dashPrefHeight);
3874
3875 pConf->Write(_T("DashboardCount" ),
3876 (int)m_ArrayOfDashboardWindow.GetCount());
3877 // Delete old Dashborads
3878 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i < 20; i++) {
3879 if (pConf->Exists(
3880 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1))) {
3881 pConf->DeleteGroup(
3882 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1));
3883 }
3884 }
3885 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3886 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3887 pConf->SetPath(
3888 wxString::Format(_T("/PlugIns/Dashboard/Dashboard%zu"), i + 1));
3889 pConf->Write(_T("Name"), cont->m_sName);
3890 pConf->Write(_T("Caption"), cont->m_sCaption);
3891 pConf->Write(_T("Orientation"), cont->m_sOrientation);
3892 pConf->Write(_T("Persistence"), cont->m_bPersVisible);
3893 pConf->Write(_T("InstrumentCount"),
3894 (int)cont->m_aInstrumentList.GetCount());
3895 pConf->Write(_T("BestSizeX"), cont->m_best_size.x);
3896 pConf->Write(_T("BestSizeY"), cont->m_best_size.y);
3897 pConf->Write(_T("PersistSizeX"), cont->m_pDashboardWindow->GetSize().x);
3898 pConf->Write(_T("PersistSizeY"), cont->m_pDashboardWindow->GetSize().y);
3899
3900 // Delete old Instruments
3901 for (size_t i = cont->m_aInstrumentList.GetCount(); i < 40; i++) {
3902 if (pConf->Exists(wxString::Format(_T("Instrument%zu"), i + 1))) {
3903 pConf->DeleteEntry(wxString::Format(_T("Instrument%zu"), i + 1));
3904 if (pConf->Exists(wxString::Format(_T("InstTitleFont%zu"), i + 1))) {
3905 pConf->DeleteEntry(wxString::Format(_T("InstTitleFont%zu"), i + 1));
3906 pConf->DeleteEntry(
3907 wxString::Format(_T("InstTitleColor%zu"), i + 1));
3908 pConf->DeleteEntry(wxString::Format(_T("InstTitle%zu"), i + 1));
3909 pConf->DeleteEntry(
3910 wxString::Format(_T("InstDataShowUnit%zu"), i + 1));
3911 pConf->DeleteEntry(
3912 wxString::Format(_T("InstDataMargin%zu"), i + 1));
3913 pConf->DeleteEntry(
3914 wxString::Format(_T("InstDataAlignment%zu"), i + 1));
3915 pConf->DeleteEntry(
3916 wxString::Format(_T("InstDataFormat%zu"), i + 1));
3917 pConf->DeleteEntry(wxString::Format(_T("InstDataFont%zu"), i + 1));
3918 pConf->DeleteEntry(wxString::Format(_T("InstDataColor%zu"), i + 1));
3919 pConf->DeleteEntry(wxString::Format(_T("InstLabelFont%zu"), i + 1));
3920 pConf->DeleteEntry(
3921 wxString::Format(_T("InstLabelColor%zu"), i + 1));
3922 pConf->DeleteEntry(wxString::Format(_T("InstSmallFont%zu"), i + 1));
3923 pConf->DeleteEntry(
3924 wxString::Format(_T("InstSmallColor%zu"), i + 1));
3925 pConf->DeleteEntry(
3926 wxString::Format(_T("TitleBackColor%zu"), i + 1));
3927 pConf->DeleteEntry(wxString::Format(_T("DataBackColor%zu"), i + 1));
3928 pConf->DeleteEntry(wxString::Format(_T("ArrowFirst%zu"), i + 1));
3929 pConf->DeleteEntry(wxString::Format(_T("ArrowSecond%zu"), i + 1));
3930 }
3931 }
3932 }
3933 for (size_t j = 0; j < cont->m_aInstrumentList.GetCount(); j++) {
3934 pConf->Write(wxString::Format(_T("Instrument%zu"), j + 1),
3935 cont->m_aInstrumentList.Item(j));
3936 InstrumentProperties *Inst = NULL;
3937 // First delete
3938 if (pConf->Exists(wxString::Format(_T("InstTitleFont%zu"), j + 1))) {
3939 bool Delete = true;
3940 for (size_t i = 0; i < cont->m_aInstrumentPropertyList.GetCount();
3941 i++) {
3942 Inst = cont->m_aInstrumentPropertyList.Item(i);
3943 if (Inst->m_Listplace == (int)j) {
3944 Delete = false;
3945 break;
3946 }
3947 }
3948 if (Delete) {
3949 pConf->DeleteEntry(wxString::Format(_T("InstTitleFont%zu"), j + 1));
3950 pConf->DeleteEntry(
3951 wxString::Format(_T("InstTitleColor%zu"), j + 1));
3952 pConf->DeleteEntry(wxString::Format(_T("InstTitle%zu"), j + 1));
3953 pConf->DeleteEntry(
3954 wxString::Format(_T("InstDataShowUnit%zu"), i + 1));
3955 pConf->DeleteEntry(
3956 wxString::Format(_T("InstDataMargin%zu"), i + 1));
3957 pConf->DeleteEntry(
3958 wxString::Format(_T("InstDataAlignment%zu"), i + 1));
3959 pConf->DeleteEntry(
3960 wxString::Format(_T("InstDataFormat%zu"), i + 1));
3961 pConf->DeleteEntry(wxString::Format(_T("InstDataFont%zu"), j + 1));
3962 pConf->DeleteEntry(wxString::Format(_T("InstDataColor%zu"), j + 1));
3963 pConf->DeleteEntry(wxString::Format(_T("InstLabelFont%zu"), j + 1));
3964 pConf->DeleteEntry(
3965 wxString::Format(_T("InstLabelColor%zu"), j + 1));
3966 pConf->DeleteEntry(wxString::Format(_T("InstSmallFont%zu"), j + 1));
3967 pConf->DeleteEntry(
3968 wxString::Format(_T("InstSmallColor%zu"), j + 1));
3969 pConf->DeleteEntry(
3970 wxString::Format(_T("TitleBackColor%zu"), i + 1));
3971 pConf->DeleteEntry(wxString::Format(_T("DataBackColor%zu"), i + 1));
3972 pConf->DeleteEntry(wxString::Format(_T("ArrowFirst%zu"), i + 1));
3973 pConf->DeleteEntry(wxString::Format(_T("ArrowSecond%zu"), i + 1));
3974 }
3975 }
3976 Inst = NULL;
3977 for (size_t i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
3978 i++) {
3979 Inst = cont->m_aInstrumentPropertyList.Item(i);
3980 if (Inst->m_Listplace == (int)j) {
3981 pConf->Write(
3982 wxString::Format(_T("InstTitleFont%zu"), j + 1),
3983 Inst->m_USTitleFont.GetChosenFont().GetNativeFontInfoDesc());
3984 pConf->Write(
3985 wxString::Format(_T("InstTitleColor%zu"), j + 1),
3986 Inst->m_USTitleFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3987 pConf->Write(
3988 wxString::Format(_T("InstDataFont%zu"), j + 1),
3989 Inst->m_USDataFont.GetChosenFont().GetNativeFontInfoDesc());
3990 pConf->Write(
3991 wxString::Format(_T("InstDataColor%zu"), j + 1),
3992 Inst->m_USDataFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3993 pConf->Write(
3994 wxString::Format(_T("InstLabelFont%zu"), j + 1),
3995 Inst->m_USLabelFont.GetChosenFont().GetNativeFontInfoDesc());
3996 pConf->Write(
3997 wxString::Format(_T("InstLabelColor%zu"), j + 1),
3998 Inst->m_USLabelFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3999 pConf->Write(
4000 wxString::Format(_T("InstSmallFont%zu"), j + 1),
4001 Inst->m_USSmallFont.GetChosenFont().GetNativeFontInfoDesc());
4002 pConf->Write(
4003 wxString::Format(_T("InstSmallColor%zu"), j + 1),
4004 Inst->m_USSmallFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
4005 pConf->Write(
4006 wxString::Format(_T("TitleBackColor%zu"), j + 1),
4007 Inst->m_TitleBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
4008 pConf->Write(
4009 wxString::Format(_T("DataBackColor%zu"), j + 1),
4010 Inst->m_DataBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
4011 pConf->Write(
4012 wxString::Format(_T("ArrowFirst%zu"), j + 1),
4013 Inst->m_Arrow_First_Colour.GetAsString(wxC2S_HTML_SYNTAX));
4014 pConf->Write(
4015 wxString::Format(_T("ArrowSecond%zu"), j + 1),
4016 Inst->m_Arrow_Second_Colour.GetAsString(wxC2S_HTML_SYNTAX));
4017 break;
4018 }
4019 }
4020 }
4021 }
4022 pConf->Flush();
4023 return true;
4024 } else
4025 return false;
4026}
4027
4028void dashboard_pi::ApplyConfig(void) {
4029 // Reverse order to handle deletes
4030 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i > 0; i--) {
4031 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i - 1);
4032 int orient = (cont->m_sOrientation == _T("V") ? wxVERTICAL : wxHORIZONTAL);
4033 if (cont->m_bIsDeleted) {
4034 if (cont->m_pDashboardWindow) {
4035 m_pauimgr->DetachPane(cont->m_pDashboardWindow);
4036 cont->m_pDashboardWindow->Close();
4037 cont->m_pDashboardWindow->Destroy();
4038 cont->m_pDashboardWindow = NULL;
4039 }
4040 m_ArrayOfDashboardWindow.Remove(cont);
4041 delete cont;
4042
4043 } else if (!cont->m_pDashboardWindow) {
4044 // A new dashboard is created
4045 cont->m_pDashboardWindow = new DashboardWindow(
4046 GetOCPNCanvasWindow(), wxID_ANY, m_pauimgr, this, orient, cont);
4047 cont->m_pDashboardWindow->SetInstrumentList(
4048 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4049 bool vertical = orient == wxVERTICAL;
4050 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4051 wxSize best = cont->m_conf_best_size;
4052 if (best.x < 100) best = sz;
4053
4054// Mac has a little trouble with initial Layout() sizing...
4055#ifdef __WXOSX__
4056 if (sz.x == 0) sz.IncTo(wxSize(160, 388));
4057#endif
4058 wxAuiPaneInfo p = wxAuiPaneInfo()
4059 .Name(cont->m_sName)
4060 .Caption(cont->m_sCaption)
4061 .CaptionVisible(false)
4062 .TopDockable(!vertical)
4063 .BottomDockable(!vertical)
4064 .LeftDockable(vertical)
4065 .RightDockable(vertical)
4066 .MinSize(sz)
4067 .BestSize(best)
4068 .FloatingSize(sz)
4069 .FloatingPosition(100, 100)
4070 .Float()
4071 .Show(cont->m_bIsVisible)
4072 .Gripper(false);
4073
4074 m_pauimgr->AddPane(cont->m_pDashboardWindow, p);
4075 // wxAuiPaneInfo().Name( cont->m_sName ).Caption( cont->m_sCaption
4076 // ).CaptionVisible( false ).TopDockable(
4077 // !vertical ).BottomDockable( !vertical ).LeftDockable( vertical
4078 // ).RightDockable( vertical ).MinSize( sz ).BestSize( sz ).FloatingSize(
4079 // sz ).FloatingPosition( 100, 100 ).Float().Show( cont->m_bIsVisible ) );
4080
4081#ifdef __OCPN__ANDROID__
4082 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4083 pane.Dockable(false);
4084
4085#endif
4086
4087 } else {
4088 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4089 pane.Caption(cont->m_sCaption).Show(cont->m_bIsVisible);
4090 if (!cont->m_pDashboardWindow->isInstrumentListEqual(
4091 cont->m_aInstrumentList)) {
4092 cont->m_pDashboardWindow->SetInstrumentList(
4093 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4094 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4095 pane.MinSize(sz).BestSize(sz).FloatingSize(sz);
4096 }
4097 if (cont->m_pDashboardWindow->GetSizerOrientation() != orient) {
4098 cont->m_pDashboardWindow->ChangePaneOrientation(orient, false);
4099 }
4100 }
4101 }
4102 m_pauimgr->Update();
4103
4104 // Initialize the IIR Filter Co-efficients
4105 double sogFC = g_iDashSOGDamp ? 1.0 / (2.0 * g_iDashSOGDamp) : 0.0;
4106 double cogFC = g_iDashCOGDamp ? 1.0 / (2.0 * g_iDashCOGDamp) : 0.0;
4107 double awaFC = g_iDashAWADamp ? 1.0 / (2.0 * g_iDashAWADamp) : 0.0;
4108 double awsFC = g_iDashAWSDamp ? 1.0 / (2.0 * g_iDashAWSDamp) : 0.0;
4109
4110 if (abs(sogFC - mSOGFilter.getFc()) > 1e-6) {
4111 mSOGFilter.setFC(sogFC);
4112 }
4113 if (abs(cogFC - mCOGFilter.getFc()) > 1e-6) {
4114 mCOGFilter.setFC(cogFC);
4115 }
4116 if (abs(awaFC - mAWAFilter.getFc()) > 1e-6) {
4117 mAWAFilter.setFC(awaFC);
4118 }
4119 if (abs(awsFC - mAWSFilter.getFc()) > 1e-6) {
4120 mAWSFilter.setFC(awsFC);
4121 }
4122}
4123
4124void dashboard_pi::PopulateContextMenu(wxMenu *menu) {
4125 int nvis = 0;
4126 wxMenuItem *visItem = 0;
4127 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
4128 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
4129 wxMenuItem *item = menu->AppendCheckItem(i + 1, cont->m_sCaption);
4130 item->Check(cont->m_bIsVisible);
4131 if (cont->m_bIsVisible) {
4132 nvis++;
4133 visItem = item;
4134 }
4135 }
4136 if (nvis == 1 && visItem) visItem->Enable(false);
4137}
4138
4139void dashboard_pi::ShowDashboard(size_t id, bool visible) {
4140 if (id < m_ArrayOfDashboardWindow.GetCount()) {
4141 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(id);
4142 m_pauimgr->GetPane(cont->m_pDashboardWindow).Show(visible);
4143 cont->m_bIsVisible = visible;
4144 cont->m_bPersVisible = visible;
4145 m_pauimgr->Update();
4146 }
4147}
4148
4149/* DashboardPreferencesDialog
4150 *
4151 */
4152
4153DashboardPreferencesDialog::DashboardPreferencesDialog(
4154 wxWindow *parent, wxWindowID id, wxArrayOfDashboard config)
4155 : wxDialog(parent, id, _("Dashboard preferences"), wxDefaultPosition,
4156 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
4157#ifdef __WXQT__
4158 wxFont *pF = OCPNGetFont(_("Dialog"));
4159 SetFont(*pF);
4160#endif
4161
4162 int display_width, display_height;
4163 wxDisplaySize(&display_width, &display_height);
4164
4165 wxString shareLocn = *GetpSharedDataLocation() + _T("plugins") +
4166 wxFileName::GetPathSeparator() + _T("dashboard_pi") +
4167 wxFileName::GetPathSeparator() + _T("data") +
4168 wxFileName::GetPathSeparator();
4169
4170 Connect(wxEVT_CLOSE_WINDOW,
4171 wxCloseEventHandler(DashboardPreferencesDialog::OnCloseDialog), NULL,
4172 this);
4173
4174 // Copy original config
4175 m_Config = wxArrayOfDashboard(config);
4176 // Build Dashboard Page for Toolbox
4177 int border_size = 2;
4178
4179 wxBoxSizer *itemBoxSizerMainPanel = new wxBoxSizer(wxVERTICAL);
4180 SetSizer(itemBoxSizerMainPanel);
4181
4182 wxWindow *dparent = this;
4183 wxScrolledWindow *scrollWin = new wxScrolledWindow(
4184 this, wxID_ANY, wxDefaultPosition, wxSize(-1, -1), wxVSCROLL | wxHSCROLL);
4185
4186 scrollWin->SetScrollRate(1, 1);
4187 itemBoxSizerMainPanel->Add(scrollWin, 1, wxEXPAND | wxALL, 0);
4188
4189 dparent = scrollWin;
4190
4191 wxBoxSizer *itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
4192 scrollWin->SetSizer(itemBoxSizer2);
4193
4194 auto *DialogButtonSizer = new wxStdDialogButtonSizer();
4195 DialogButtonSizer->AddButton(new wxButton(this, wxID_OK));
4196 DialogButtonSizer->AddButton(new wxButton(this, wxID_CANCEL));
4197 wxButton *help_btn = new wxButton(this, wxID_HELP);
4198 help_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent) {
4199 wxString datadir = GetPluginDataDir("manual_pi");
4200 Manual(this, datadir.ToStdString()).Launch("Dashboard");
4201 });
4202 DialogButtonSizer->AddButton(help_btn);
4203 DialogButtonSizer->Realize();
4204
4205 itemBoxSizerMainPanel->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
4206
4207 wxNotebook *itemNotebook = new wxNotebook(
4208 dparent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxNB_TOP);
4209 itemBoxSizer2->Add(itemNotebook, 0, wxALL | wxEXPAND, border_size);
4210
4211 wxPanel *itemPanelNotebook01 =
4212 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4213 wxTAB_TRAVERSAL);
4214 wxFlexGridSizer *itemFlexGridSizer01 = new wxFlexGridSizer(2);
4215 itemFlexGridSizer01->AddGrowableCol(1);
4216 itemPanelNotebook01->SetSizer(itemFlexGridSizer01);
4217 itemNotebook->AddPage(itemPanelNotebook01, _("Dashboard"));
4218
4219 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
4220 itemFlexGridSizer01->Add(itemBoxSizer01, 1, wxEXPAND | wxTOP | wxLEFT,
4221 border_size);
4222
4223 // Scale the images in the dashboard list control
4224 int imageRefSize = GetCharWidth() * 4;
4225
4226 wxImageList *imglist1 = new wxImageList(imageRefSize, imageRefSize, true, 1);
4227
4228 wxBitmap bmDashBoard;
4229#ifdef ocpnUSE_SVG
4230 wxString filename = shareLocn + _T("Dashboard.svg");
4231 bmDashBoard = GetBitmapFromSVGFile(filename, imageRefSize, imageRefSize);
4232#else
4233 wxImage dash1 = wxBitmap(*_img_dashboard_pi).ConvertToImage();
4234 wxImage dash1s =
4235 dash1.Scale(imageRefSize, imageRefSize, wxIMAGE_QUALITY_HIGH);
4236 bmDashBoard = wxBitmap(dash1s);
4237#endif
4238
4239 imglist1->Add(bmDashBoard);
4240
4241 m_pListCtrlDashboards =
4242 new wxListCtrl(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4243 wxSize(imageRefSize * 3 / 2, 200),
4244 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4245
4246 m_pListCtrlDashboards->AssignImageList(imglist1, wxIMAGE_LIST_SMALL);
4247 m_pListCtrlDashboards->InsertColumn(0, _T(""));
4248 m_pListCtrlDashboards->Connect(
4249 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4250 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL,
4251 this);
4252 m_pListCtrlDashboards->Connect(
4253 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4254 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected), NULL,
4255 this);
4256 itemBoxSizer01->Add(m_pListCtrlDashboards, 1, wxEXPAND, 0);
4257
4258 wxBoxSizer *itemBoxSizer02 = new wxBoxSizer(wxHORIZONTAL);
4259 itemBoxSizer01->Add(itemBoxSizer02);
4260
4261 wxBitmap bmPlus, bmMinus;
4262 int bmSize = imageRefSize * 100 / 275;
4263
4264#ifdef __OCPN__ANDROID__
4265 bmSize = imageRefSize / 2;
4266#endif
4267
4268#ifdef ocpnUSE_SVG
4269 bmPlus = GetBitmapFromSVGFile(shareLocn + _T("plus.svg"), bmSize, bmSize);
4270 bmMinus = GetBitmapFromSVGFile(shareLocn + _T("minus.svg"), bmSize, bmSize);
4271#else
4272 wxImage plus1 = wxBitmap(*_img_plus).ConvertToImage();
4273 wxImage plus1s = plus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4274 bmPlus = wxBitmap(plus1s);
4275
4276 wxImage minus1 = wxBitmap(*_img_minus).ConvertToImage();
4277 wxImage minus1s = minus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4278 bmMinus = wxBitmap(minus1s);
4279#endif
4280
4281 m_pButtonAddDashboard = new wxBitmapButton(
4282 itemPanelNotebook01, wxID_ANY, bmPlus, wxDefaultPosition, wxDefaultSize);
4283 itemBoxSizer02->Add(m_pButtonAddDashboard, 0, wxALIGN_CENTER, 2);
4284 m_pButtonAddDashboard->Connect(
4285 wxEVT_COMMAND_BUTTON_CLICKED,
4286 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardAdd), NULL,
4287 this);
4288
4289 m_pButtonDeleteDashboard = new wxBitmapButton(
4290 itemPanelNotebook01, wxID_ANY, bmMinus, wxDefaultPosition, wxDefaultSize);
4291 itemBoxSizer02->Add(m_pButtonDeleteDashboard, 0, wxALIGN_CENTER, 2);
4292 m_pButtonDeleteDashboard->Connect(
4293 wxEVT_COMMAND_BUTTON_CLICKED,
4294 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardDelete),
4295 NULL, this);
4296
4297 m_pPanelDashboard =
4298 new wxPanel(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4299 wxDefaultSize, wxBORDER_SUNKEN);
4300 itemFlexGridSizer01->Add(m_pPanelDashboard, 1, wxEXPAND | wxTOP | wxRIGHT,
4301 border_size);
4302
4303 wxBoxSizer *itemBoxSizer03 = new wxBoxSizer(wxVERTICAL);
4304 m_pPanelDashboard->SetSizer(itemBoxSizer03);
4305
4306 wxStaticBox *itemStaticBox02 =
4307 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Dashboard"));
4308 wxStaticBoxSizer *itemStaticBoxSizer02 =
4309 new wxStaticBoxSizer(itemStaticBox02, wxHORIZONTAL);
4310 itemBoxSizer03->Add(itemStaticBoxSizer02, 0, wxEXPAND | wxALL, border_size);
4311 wxFlexGridSizer *itemFlexGridSizer = new wxFlexGridSizer(2);
4312 itemFlexGridSizer->AddGrowableCol(1);
4313 itemStaticBoxSizer02->Add(itemFlexGridSizer, 1, wxEXPAND | wxALL, 0);
4314
4315 m_pCheckBoxIsVisible =
4316 new wxCheckBox(m_pPanelDashboard, wxID_ANY, _("show this dashboard"),
4317 wxDefaultPosition, wxDefaultSize, 0);
4318 m_pCheckBoxIsVisible->SetMinSize(wxSize(25 * GetCharWidth(), -1));
4319
4320 itemFlexGridSizer->Add(m_pCheckBoxIsVisible, 0, wxEXPAND | wxALL,
4321 border_size);
4322 wxStaticText *itemDummy01 =
4323 new wxStaticText(m_pPanelDashboard, wxID_ANY, _T(""));
4324 itemFlexGridSizer->Add(itemDummy01, 0, wxEXPAND | wxALL, border_size);
4325
4326 wxStaticText *itemStaticText01 =
4327 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Caption:"),
4328 wxDefaultPosition, wxDefaultSize, 0);
4329 itemFlexGridSizer->Add(itemStaticText01, 0, wxEXPAND | wxALL, border_size);
4330 m_pTextCtrlCaption = new wxTextCtrl(m_pPanelDashboard, wxID_ANY, _T(""),
4331 wxDefaultPosition, wxDefaultSize);
4332 m_pCheckBoxIsVisible->SetMinSize(wxSize(30 * GetCharWidth(), -1));
4333 itemFlexGridSizer->Add(m_pTextCtrlCaption, 0, wxALIGN_RIGHT | wxALL,
4334 border_size);
4335
4336 wxStaticText *itemStaticText02 =
4337 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Orientation:"),
4338 wxDefaultPosition, wxDefaultSize, 0);
4339 itemFlexGridSizer->Add(itemStaticText02, 0, wxEXPAND | wxALL, border_size);
4340 m_pChoiceOrientation = new wxChoice(m_pPanelDashboard, wxID_ANY,
4341 wxDefaultPosition, wxDefaultSize);
4342 m_pChoiceOrientation->SetMinSize(wxSize(15 * GetCharWidth(), -1));
4343 m_pChoiceOrientation->Append(_("Vertical"));
4344 m_pChoiceOrientation->Append(_("Horizontal"));
4345 itemFlexGridSizer->Add(m_pChoiceOrientation, 0, wxALIGN_RIGHT | wxALL,
4346 border_size);
4347
4348 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
4349
4350 wxImageList *imglist =
4351 new wxImageList(instImageRefSize, instImageRefSize, true, 2);
4352
4353 wxBitmap bmDial, bmInst;
4354#ifdef ocpnUSE_SVG
4355 bmDial = GetBitmapFromSVGFile(shareLocn + _T("dial.svg"), instImageRefSize,
4356 instImageRefSize);
4357 bmInst = GetBitmapFromSVGFile(shareLocn + _T("instrument.svg"),
4358 instImageRefSize, instImageRefSize);
4359#else
4360 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
4361 wxImage dial1s =
4362 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4363 bmDial = wxBitmap(dial1);
4364
4365 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
4366 wxImage inst1s =
4367 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4368 bmInst = wxBitmap(inst1s);
4369#endif
4370
4371 imglist->Add(bmInst);
4372 imglist->Add(bmDial);
4373
4374 wxStaticBox *itemStaticBox03 =
4375 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Instruments"));
4376 wxStaticBoxSizer *itemStaticBoxSizer03 =
4377 new wxStaticBoxSizer(itemStaticBox03, wxHORIZONTAL);
4378 itemBoxSizer03->Add(itemStaticBoxSizer03, 1, wxEXPAND | wxALL, border_size);
4379
4380 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4381 wxSize list_size = wxSize(-1, dsize.y * 35 / 100);
4382
4383#ifdef __ANDROID__
4384 int xsize = GetCharWidth() * 30;
4385 list_size = wxSize(xsize, dsize.y * 50 / 100);
4386#endif
4387
4388 m_pListCtrlInstruments =
4389 new wxListCtrl(m_pPanelDashboard, wxID_ANY, wxDefaultPosition, list_size,
4390 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4391
4392 itemStaticBoxSizer03->Add(m_pListCtrlInstruments, 1, wxEXPAND | wxALL,
4393 border_size);
4394 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
4395 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
4396 m_pListCtrlInstruments->Connect(
4397 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4398 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4399 NULL, this);
4400 m_pListCtrlInstruments->Connect(
4401 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4402 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4403 NULL, this);
4404
4405 wxBoxSizer *itemBoxSizer04 = new wxBoxSizer(wxVERTICAL);
4406 itemStaticBoxSizer03->Add(itemBoxSizer04, 0, wxALIGN_TOP | wxALL,
4407 border_size);
4408 m_pButtonAdd = new wxButton(m_pPanelDashboard, wxID_ANY, _("Add"),
4409 wxDefaultPosition, wxSize(-1, -1));
4410 itemBoxSizer04->Add(m_pButtonAdd, 0, wxEXPAND | wxALL, border_size);
4411 m_pButtonAdd->Connect(
4412 wxEVT_COMMAND_BUTTON_CLICKED,
4413 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentAdd), NULL,
4414 this);
4415
4416 // TODO Instrument Properties ... done by Bernd Cirotzki
4417 m_pButtonEdit = new wxButton(m_pPanelDashboard, wxID_ANY, _("Edit"),
4418 wxDefaultPosition, wxDefaultSize);
4419 itemBoxSizer04->Add(m_pButtonEdit, 0, wxEXPAND | wxALL, border_size);
4420 m_pButtonEdit->Connect(
4421 wxEVT_COMMAND_BUTTON_CLICKED,
4422 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentEdit), NULL,
4423 this);
4424
4425 // m_pFontPickerTitle =
4426 // new wxFontPickerCtrl(m_pPanelDashboard, wxID_ANY, g_USFontTitle,
4427 // wxDefaultPosition, wxSize(-1, -1), 0,
4428 // wxDefaultValidator, "Bernd");
4429 // itemBoxSizer04->Add(m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0);
4430 // wxColor Farbe = m_pFontPickerTitle->GetSelectedColour();
4431 // m_pFontPickerTitle->SetBackgroundColour(Farbe);
4432
4433 m_pButtonDelete = new wxButton(m_pPanelDashboard, wxID_ANY, _("Delete"),
4434 wxDefaultPosition, wxSize(-1, -1));
4435 itemBoxSizer04->Add(m_pButtonDelete, 0, wxEXPAND | wxALL, border_size);
4436 m_pButtonDelete->Connect(
4437 wxEVT_COMMAND_BUTTON_CLICKED,
4438 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDelete),
4439 NULL, this);
4440 itemBoxSizer04->AddSpacer(10);
4441 m_pButtonUp = new wxButton(m_pPanelDashboard, wxID_ANY, _("Up"),
4442 wxDefaultPosition, wxDefaultSize);
4443 itemBoxSizer04->Add(m_pButtonUp, 0, wxEXPAND | wxALL, border_size);
4444 m_pButtonUp->Connect(
4445 wxEVT_COMMAND_BUTTON_CLICKED,
4446 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentUp), NULL,
4447 this);
4448 m_pButtonDown = new wxButton(m_pPanelDashboard, wxID_ANY, _("Down"),
4449 wxDefaultPosition, wxDefaultSize);
4450 itemBoxSizer04->Add(m_pButtonDown, 0, wxEXPAND | wxALL, border_size);
4451 m_pButtonDown->Connect(
4452 wxEVT_COMMAND_BUTTON_CLICKED,
4453 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDown), NULL,
4454 this);
4455
4456 wxPanel *itemPanelNotebook02 =
4457 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4458 wxTAB_TRAVERSAL);
4459 wxBoxSizer *itemBoxSizer05 = new wxBoxSizer(wxVERTICAL);
4460 itemPanelNotebook02->SetSizer(itemBoxSizer05);
4461 itemNotebook->AddPage(itemPanelNotebook02, _("Appearance"));
4462
4463 wxStaticBox *itemStaticBox01 =
4464 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Fonts"));
4465 wxStaticBoxSizer *itemStaticBoxSizer01 =
4466 new wxStaticBoxSizer(itemStaticBox01, wxHORIZONTAL);
4467 itemBoxSizer05->Add(itemStaticBoxSizer01, 0, wxEXPAND | wxALL, border_size);
4468 wxFlexGridSizer *itemFlexGridSizer03 = new wxFlexGridSizer(2);
4469 itemFlexGridSizer03->AddGrowableCol(1);
4470 itemStaticBoxSizer01->Add(itemFlexGridSizer03, 1, wxEXPAND | wxALL, 0);
4471
4472 wxStaticText *itemStaticText04 =
4473 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Title:"),
4474 wxDefaultPosition, wxDefaultSize, 0);
4475 itemFlexGridSizer03->Add(itemStaticText04, 0, wxEXPAND | wxALL, border_size);
4476
4477 m_pFontPickerTitle =
4478 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontTitle,
4479 wxDefaultPosition, wxDefaultSize);
4480 itemFlexGridSizer03->Add(m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0);
4481
4482 wxStaticText *itemStaticText05 =
4483 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Data:"),
4484 wxDefaultPosition, wxDefaultSize, 0);
4485 itemFlexGridSizer03->Add(itemStaticText05, 0, wxEXPAND | wxALL, border_size);
4486 m_pFontPickerData =
4487 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontData,
4488 wxDefaultPosition, wxDefaultSize);
4489 itemFlexGridSizer03->Add(m_pFontPickerData, 0, wxALIGN_RIGHT | wxALL, 0);
4490
4491 wxStaticText *itemStaticText06 =
4492 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Label:"),
4493 wxDefaultPosition, wxDefaultSize, 0);
4494 itemFlexGridSizer03->Add(itemStaticText06, 0, wxEXPAND | wxALL, border_size);
4495 m_pFontPickerLabel =
4496 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontLabel,
4497 wxDefaultPosition, wxDefaultSize);
4498 itemFlexGridSizer03->Add(m_pFontPickerLabel, 0, wxALIGN_RIGHT | wxALL, 0);
4499
4500 wxStaticText *itemStaticText07 =
4501 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Small:"),
4502 wxDefaultPosition, wxDefaultSize, 0);
4503 itemFlexGridSizer03->Add(itemStaticText07, 0, wxEXPAND | wxALL, border_size);
4504 m_pFontPickerSmall =
4505 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontSmall,
4506 wxDefaultPosition, wxDefaultSize);
4507 itemFlexGridSizer03->Add(m_pFontPickerSmall, 0, wxALIGN_RIGHT | wxALL, 0);
4508 // wxColourPickerCtrl
4509
4510 wxStaticText *itemStaticText80 =
4511 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Reset:"),
4512 wxDefaultPosition, wxDefaultSize, 0);
4513 itemFlexGridSizer03->Add(itemStaticText80, 0, wxEXPAND | wxALL, border_size);
4514
4515 m_pButtondefaultFont = new wxButton(itemPanelNotebook02, wxID_ANY,
4516 _("Set dashboard default fonts"),
4517 wxDefaultPosition, wxSize(-1, -1));
4518 itemFlexGridSizer03->Add(m_pButtondefaultFont, 0, wxALIGN_RIGHT | wxALL, 0);
4519 m_pButtondefaultFont->Connect(
4520 wxEVT_COMMAND_BUTTON_CLICKED,
4521 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboarddefaultFont),
4522 NULL, this);
4523
4524 wxStaticBox *itemStaticBox04 = new wxStaticBox(itemPanelNotebook02, wxID_ANY,
4525 _("Units, Ranges, Formats"));
4526 wxStaticBoxSizer *itemStaticBoxSizer04 =
4527 new wxStaticBoxSizer(itemStaticBox04, wxHORIZONTAL);
4528 itemBoxSizer05->Add(itemStaticBoxSizer04, 0, wxEXPAND | wxALL, border_size);
4529 wxFlexGridSizer *itemFlexGridSizer04 = new wxFlexGridSizer(2);
4530 itemFlexGridSizer04->AddGrowableCol(1);
4531 itemStaticBoxSizer04->Add(itemFlexGridSizer04, 1, wxEXPAND | wxALL, 0);
4532 wxStaticText *itemStaticText08 = new wxStaticText(
4533 itemPanelNotebook02, wxID_ANY, _("Speedometer max value:"),
4534 wxDefaultPosition, wxDefaultSize, 0);
4535 itemFlexGridSizer04->Add(itemStaticText08, 0, wxEXPAND | wxALL, border_size);
4536 m_pSpinSpeedMax = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4537 wxDefaultPosition, wxDefaultSize,
4538 wxSP_ARROW_KEYS, 10, 100, g_iDashSpeedMax);
4539 itemFlexGridSizer04->Add(m_pSpinSpeedMax, 0, wxALIGN_RIGHT | wxALL, 0);
4540
4541 wxStaticText *itemStaticText10 = new wxStaticText(
4542 itemPanelNotebook02, wxID_ANY, _("Speed Over Ground Damping Factor:"),
4543 wxDefaultPosition, wxDefaultSize, 0);
4544 itemFlexGridSizer04->Add(itemStaticText10, 0, wxEXPAND | wxALL, border_size);
4545 m_pSpinSOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4546 wxDefaultPosition, wxDefaultSize,
4547 wxSP_ARROW_KEYS, 0, 100, g_iDashSOGDamp);
4548 itemFlexGridSizer04->Add(m_pSpinSOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4549
4550 wxStaticText *itemStaticText11 =
4551 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("COG Damping Factor:"),
4552 wxDefaultPosition, wxDefaultSize, 0);
4553 itemFlexGridSizer04->Add(itemStaticText11, 0, wxEXPAND | wxALL, border_size);
4554 m_pSpinCOGDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4555 wxDefaultPosition, wxDefaultSize,
4556 wxSP_ARROW_KEYS, 0, 100, g_iDashCOGDamp);
4557 itemFlexGridSizer04->Add(m_pSpinCOGDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4558
4559 wxStaticText *itemStaticText13 = new wxStaticText(
4560 itemPanelNotebook02, wxID_ANY, _("Wind speed Damping Factor:"),
4561 wxDefaultPosition, wxDefaultSize, 0);
4562 itemFlexGridSizer04->Add(itemStaticText13, 0, wxEXPAND | wxALL, border_size);
4563 m_pSpinAWSDamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4564 wxDefaultPosition, wxDefaultSize,
4565 wxSP_ARROW_KEYS, 0, 100, g_iDashAWSDamp);
4566 itemFlexGridSizer04->Add(m_pSpinAWSDamp, 0, wxALIGN_RIGHT | wxALL, 0);
4567
4568 wxStaticText *itemStaticText14 = new wxStaticText(
4569 itemPanelNotebook02, wxID_ANY, _("Wind angle Damping Factor:"),
4570 wxDefaultPosition, wxDefaultSize, 0);
4571 itemFlexGridSizer04->Add(itemStaticText14, 0, wxEXPAND | wxALL, border_size);
4572 m_pSpinAWADamp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4573 wxDefaultPosition, wxDefaultSize,
4574 wxSP_ARROW_KEYS, 0, 100, g_iDashAWADamp);
4575 itemFlexGridSizer04->Add(m_pSpinAWADamp, 0, wxALIGN_RIGHT | wxALL, 0);
4576
4577 wxStaticText *itemStaticText12 = new wxStaticText(
4578 itemPanelNotebook02, wxID_ANY, _("Local Time Offset From UTC:"),
4579 wxDefaultPosition, wxDefaultSize, 0);
4580 itemFlexGridSizer04->Add(itemStaticText12, 0, wxEXPAND | wxALL, border_size);
4581 wxString m_UTCOffsetChoices[] = {
4582 _T( "-12:00" ), _T( "-11:30" ), _T( "-11:00" ), _T( "-10:30" ),
4583 _T( "-10:00" ), _T( "-09:30" ), _T( "-09:00" ), _T( "-08:30" ),
4584 _T( "-08:00" ), _T( "-07:30" ), _T( "-07:00" ), _T( "-06:30" ),
4585 _T( "-06:00" ), _T( "-05:30" ), _T( "-05:00" ), _T( "-04:30" ),
4586 _T( "-04:00" ), _T( "-03:30" ), _T( "-03:00" ), _T( "-02:30" ),
4587 _T( "-02:00" ), _T( "-01:30" ), _T( "-01:00" ), _T( "-00:30" ),
4588 _T( " 00:00" ), _T( " 00:30" ), _T( " 01:00" ), _T( " 01:30" ),
4589 _T( " 02:00" ), _T( " 02:30" ), _T( " 03:00" ), _T( " 03:30" ),
4590 _T( " 04:00" ), _T( " 04:30" ), _T( " 05:00" ), _T( " 05:30" ),
4591 _T( " 06:00" ), _T( " 06:30" ), _T( " 07:00" ), _T( " 07:30" ),
4592 _T( " 08:00" ), _T( " 08:30" ), _T( " 09:00" ), _T( " 09:30" ),
4593 _T( " 10:00" ), _T( " 10:30" ), _T( " 11:00" ), _T( " 11:30" ),
4594 _T( " 12:00" )};
4595 int m_UTCOffsetNChoices = sizeof(m_UTCOffsetChoices) / sizeof(wxString);
4596 m_pChoiceUTCOffset =
4597 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4598 wxDefaultSize, m_UTCOffsetNChoices, m_UTCOffsetChoices, 0);
4599 m_pChoiceUTCOffset->SetSelection(g_iUTCOffset + 24);
4600 itemFlexGridSizer04->Add(m_pChoiceUTCOffset, 0, wxALIGN_RIGHT | wxALL, 0);
4601
4602 wxStaticText *itemStaticText09 =
4603 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Boat speed units:"),
4604 wxDefaultPosition, wxDefaultSize, 0);
4605 itemFlexGridSizer04->Add(itemStaticText09, 0, wxEXPAND | wxALL, border_size);
4606 wxString m_SpeedUnitChoices[] = {_("Honor OpenCPN settings"), _("Kts"),
4607 _("mph"), _("km/h"), _("m/s")};
4608 int m_SpeedUnitNChoices = sizeof(m_SpeedUnitChoices) / sizeof(wxString);
4609 wxSize szSpeedUnit = wxDefaultSize;
4610 m_pChoiceSpeedUnit =
4611 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4612 szSpeedUnit, m_SpeedUnitNChoices, m_SpeedUnitChoices, 0);
4613 for (auto const &iUnit : m_SpeedUnitChoices) {
4614 szSpeedUnit.IncTo(m_pChoiceSpeedUnit->GetTextExtent(iUnit));
4615 }
4616 m_pChoiceSpeedUnit->SetSize(szSpeedUnit);
4617 m_pChoiceSpeedUnit->SetSelection(g_iDashSpeedUnit + 1);
4618 itemFlexGridSizer04->Add(m_pChoiceSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4619
4620 wxStaticText *itemStaticTextDepthU =
4621 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Depth units:"),
4622 wxDefaultPosition, wxDefaultSize, 0);
4623 itemFlexGridSizer04->Add(itemStaticTextDepthU, 0, wxEXPAND | wxALL,
4624 border_size);
4625 wxString m_DepthUnitChoices[] = {_("Meters"), _("Feet"), _("Fathoms"),
4626 _("Inches"), _("Centimeters")};
4627 int m_DepthUnitNChoices = sizeof(m_DepthUnitChoices) / sizeof(wxString);
4628 wxSize szDepthUnit = wxDefaultSize;
4629 m_pChoiceDepthUnit =
4630 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4631 szDepthUnit, m_DepthUnitNChoices, m_DepthUnitChoices, 0);
4632 for (auto const &iUnit : m_DepthUnitChoices) {
4633 szDepthUnit.IncTo(m_pChoiceDepthUnit->GetTextExtent(iUnit));
4634 }
4635 m_pChoiceDepthUnit->SetSize(szDepthUnit);
4636 m_pChoiceDepthUnit->SetSelection(g_iDashDepthUnit - 3);
4637 itemFlexGridSizer04->Add(m_pChoiceDepthUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4638 wxString dMess = wxString::Format(_("Depth Offset (%s):"),
4639 m_DepthUnitChoices[g_iDashDepthUnit - 3]);
4640 wxStaticText *itemStaticDepthO =
4641 new wxStaticText(itemPanelNotebook02, wxID_ANY, dMess, wxDefaultPosition,
4642 wxDefaultSize, 0);
4643 double DepthOffset;
4644 switch (g_iDashDepthUnit - 3) {
4645 case 1:
4646 DepthOffset = g_dDashDBTOffset * 3.2808399;
4647 break;
4648 case 2:
4649 DepthOffset = g_dDashDBTOffset * 0.54680665;
4650 break;
4651 case 3:
4652 DepthOffset = g_dDashDBTOffset * 39.3700787;
4653 break;
4654 case 4:
4655 DepthOffset = g_dDashDBTOffset * 100;
4656 break;
4657 default:
4658 DepthOffset = g_dDashDBTOffset;
4659 }
4660 itemFlexGridSizer04->Add(itemStaticDepthO, 0, wxEXPAND | wxALL, border_size);
4661 m_pSpinDBTOffset = new wxSpinCtrlDouble(
4662 itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition,
4663 wxDefaultSize, wxSP_ARROW_KEYS, -100, 100, DepthOffset, 0.1);
4664 itemFlexGridSizer04->Add(m_pSpinDBTOffset, 0, wxALIGN_RIGHT | wxALL, 0);
4665
4666 wxStaticText *itemStaticText0b =
4667 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Distance units:"),
4668 wxDefaultPosition, wxDefaultSize, 0);
4669 itemFlexGridSizer04->Add(itemStaticText0b, 0, wxEXPAND | wxALL, border_size);
4670 wxString m_DistanceUnitChoices[] = {_("Honor OpenCPN settings"),
4671 _("Nautical miles"), _("Statute miles"),
4672 _("Kilometers"), _("Meters")};
4673 wxSize szDistanceUnit = wxDefaultSize;
4674 int m_DistanceUnitNChoices = sizeof(m_DistanceUnitChoices) / sizeof(wxString);
4675 m_pChoiceDistanceUnit = new wxChoice(
4676 itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szDistanceUnit,
4677 m_DistanceUnitNChoices, m_DistanceUnitChoices, 0);
4678 for (auto const &iUnit : m_DistanceUnitChoices) {
4679 szDistanceUnit.IncTo(m_pChoiceDistanceUnit->GetTextExtent(iUnit));
4680 }
4681 m_pChoiceDistanceUnit->SetSize(szDistanceUnit);
4682 m_pChoiceDistanceUnit->SetSelection(g_iDashDistanceUnit + 1);
4683 itemFlexGridSizer04->Add(m_pChoiceDistanceUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4684 m_pChoiceDistanceUnit->Connect(
4685 wxEVT_COMMAND_CHOICE_SELECTED,
4686 wxCommandEventHandler(DashboardPreferencesDialog::OnDistanceUnitSelect),
4687 NULL, this);
4688
4689 wxStaticText *itemStaticText0a =
4690 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Wind speed units:"),
4691 wxDefaultPosition, wxDefaultSize, 0);
4692 itemFlexGridSizer04->Add(itemStaticText0a, 0, wxEXPAND | wxALL, border_size);
4693 wxString m_WSpeedUnitChoices[] = {_("Kts"), _("mph"), _("km/h"), _("m/s")};
4694 int m_WSpeedUnitNChoices = sizeof(m_WSpeedUnitChoices) / sizeof(wxString);
4695 wxSize szWSpeedUnit = wxDefaultSize;
4696 m_pChoiceWindSpeedUnit =
4697 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4698 szWSpeedUnit, m_WSpeedUnitNChoices, m_WSpeedUnitChoices, 0);
4699 for (auto const &iUnit : m_WSpeedUnitChoices) {
4700 szWSpeedUnit.IncTo(m_pChoiceWindSpeedUnit->GetTextExtent(iUnit));
4701 }
4702 m_pChoiceWindSpeedUnit->SetSize(szWSpeedUnit);
4703 m_pChoiceWindSpeedUnit->SetSelection(g_iDashWindSpeedUnit);
4704 itemFlexGridSizer04->Add(m_pChoiceWindSpeedUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4705
4706 wxStaticText *itemStaticText0c =
4707 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Temperature units:"),
4708 wxDefaultPosition, wxDefaultSize, 0);
4709 itemFlexGridSizer04->Add(itemStaticText0c, 0, wxEXPAND | wxALL, border_size);
4710 wxString m_TempUnitChoices[] = {_("Celsius"), _("Fahrenheit"), _("Kelvin")};
4711 int m_TempUnitNChoices = sizeof(m_TempUnitChoices) / sizeof(wxString);
4712 wxSize szTempUnit = wxDefaultSize;
4713 m_pChoiceTempUnit =
4714 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szTempUnit,
4715 m_TempUnitNChoices, m_TempUnitChoices, 0);
4716 for (auto const &iUnit : m_TempUnitChoices) {
4717 szTempUnit.IncTo(m_pChoiceTempUnit->GetTextExtent(iUnit));
4718 }
4719 m_pChoiceTempUnit->SetSize(szTempUnit);
4720 m_pChoiceTempUnit->SetSelection(g_iDashTempUnit);
4721 itemFlexGridSizer04->Add(m_pChoiceTempUnit, 0, wxALIGN_RIGHT | wxALL, 0);
4722
4723 // New static box to include a 3 column flexgrid sizer
4724#ifndef __ANDROID__
4725 wxStaticBox *itemStaticBox05 =
4726 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4727 wxStaticBoxSizer *itemStaticBoxSizer05 =
4728 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4729 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4730
4731 wxFlexGridSizer *itemFlexGridSizer05 = new wxFlexGridSizer(3);
4732 itemFlexGridSizer05->AddGrowableCol(1);
4733 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4734
4735 m_pUseInternSumLog = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4736 _("Use internal Sumlog.") + " " +
4737 _("Enter new value if desired"));
4738 itemFlexGridSizer05->Add(m_pUseInternSumLog, 1, wxALIGN_LEFT, border_size);
4739 m_pUseInternSumLog->SetValue(g_bUseInternSumLog);
4740
4741 m_SumLogUnit =
4742 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4743 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4744 wxDefaultPosition, wxDefaultSize, 0);
4745 itemFlexGridSizer05->Add(m_SumLogUnit, 1, wxALIGN_RIGHT, 0);
4746
4747 m_pSumLogValue = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4748 itemFlexGridSizer05->Add(m_pSumLogValue, 1, wxALIGN_LEFT, 1);
4749 m_pSumLogValue->SetValue(wxString::Format(
4750 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4751
4752 m_pUseTrueWinddata = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4753 _("Use N2K & SignalK true wind data over "
4754 "ground.\n(Instead of through water)"));
4755 itemFlexGridSizer05->Add(m_pUseTrueWinddata, 1, wxALIGN_LEFT, border_size);
4756 m_pUseTrueWinddata->SetValue(g_bDBtrueWindGround);
4757
4758#else
4759
4760 wxStaticBox *itemStaticBox05 =
4761 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4762 wxStaticBoxSizer *itemStaticBoxSizer05 =
4763 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4764 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4765
4766 wxFlexGridSizer *itemFlexGridSizer05 = new wxFlexGridSizer(2);
4767 itemFlexGridSizer05->AddGrowableCol(1);
4768 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4769
4770 m_pUseInternSumLog = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4771 _("Use internal Sumlog.") + " ");
4772 itemFlexGridSizer05->Add(m_pUseInternSumLog, 0, wxALIGN_LEFT, border_size);
4773 m_pUseInternSumLog->SetValue(g_bUseInternSumLog);
4774 itemFlexGridSizer05->AddSpacer(0);
4775
4776 auto xtext =
4777 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4778 " " + _("Enter new value if desired") + " ",
4779 wxDefaultPosition, wxDefaultSize, 0);
4780 itemFlexGridSizer05->Add(xtext, 1, wxALIGN_LEFT, 0);
4781 itemFlexGridSizer05->AddSpacer(0);
4782
4783 m_SumLogUnit = new wxStaticText(
4784 itemPanelNotebook02, wxID_ANY,
4785 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4786 wxDefaultPosition, wxDefaultSize, 0);
4787 itemFlexGridSizer05->Add(m_SumLogUnit, 1, wxALIGN_RIGHT, 0);
4788
4789 m_pSumLogValue = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4790 itemFlexGridSizer05->Add(m_pSumLogValue, 1, wxALIGN_LEFT, 1);
4791 m_pSumLogValue->SetValue(wxString::Format(
4792 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4793
4794 m_pUseTrueWinddata = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4795 _("Use N2K & SignalK true wind data over "
4796 "ground.\n(Instead of through water)"));
4797 itemFlexGridSizer05->Add(m_pUseTrueWinddata, 1, wxALIGN_LEFT, border_size);
4798 m_pUseTrueWinddata->SetValue(g_bDBtrueWindGround);
4799 itemFlexGridSizer05->AddSpacer(0);
4800
4801 auto xtext1 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4802 wxDefaultPosition, wxDefaultSize, 0);
4803 itemFlexGridSizer05->Add(xtext1, 1, wxALIGN_LEFT, 0);
4804 itemFlexGridSizer05->AddSpacer(0);
4805
4806 auto xtext2 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4807 wxDefaultPosition, wxDefaultSize, 0);
4808 itemFlexGridSizer05->Add(xtext2, 1, wxALIGN_LEFT, 0);
4809 itemFlexGridSizer05->AddSpacer(0);
4810
4811#endif
4812
4813 curSel = -1;
4814 for (size_t i = 0; i < m_Config.GetCount(); i++) {
4815 m_pListCtrlDashboards->InsertItem(i, 0);
4816 // Using data to store m_Config index for managing deletes
4817 m_pListCtrlDashboards->SetItemData(i, i);
4818 }
4819 m_pListCtrlDashboards->SetColumnWidth(0, wxLIST_AUTOSIZE);
4820
4821 m_pListCtrlDashboards->SetItemState(0, wxLIST_STATE_SELECTED,
4822 wxLIST_STATE_SELECTED);
4823 curSel = 0;
4824
4825 UpdateDashboardButtonsState();
4826 UpdateButtonsState();
4827
4828 // SetMinSize(wxSize(400, -1));
4829
4830 Fit();
4831
4832 // Constrain size on small displays
4833 SetMaxSize(wxSize(display_width, display_height));
4834
4835 wxSize canvas_size = GetOCPNCanvasWindow()->GetSize();
4836 if (display_height < 600) {
4837 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4838 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4839 else
4840 SetSize(wxSize(canvas_size.x * 8 / 10, canvas_size.y * 8 / 10));
4841 } else {
4842 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4843 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4844 else
4845 SetSize(wxSize(canvas_size.x * 3 / 4, canvas_size.y * 8 / 10));
4846 }
4847
4848 Layout();
4849 CentreOnScreen();
4850}
4851
4852void DashboardPreferencesDialog::RecalculateSize(void) {
4853#ifdef __OCPN__ANDROID__
4854 wxSize esize;
4855 esize.x = GetCharWidth() * 110;
4856 esize.y = GetCharHeight() * 40;
4857
4858 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4859 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
4860 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
4861 SetSize(esize);
4862
4863 CentreOnScreen();
4864#endif
4865}
4866
4867void DashboardPreferencesDialog::OnCloseDialog(wxCloseEvent &event) {
4868 g_dashPrefWidth = GetSize().x;
4869 g_dashPrefHeight = GetSize().y;
4870 SaveDashboardConfig();
4871 event.Skip();
4872}
4873
4874void DashboardPreferencesDialog::SaveDashboardConfig() {
4875 g_iDashSpeedMax = m_pSpinSpeedMax->GetValue();
4876 g_iDashCOGDamp = m_pSpinCOGDamp->GetValue();
4877 g_iDashSOGDamp = m_pSpinSOGDamp->GetValue();
4878 g_iDashAWADamp = m_pSpinAWADamp->GetValue();
4879 g_iDashAWSDamp = m_pSpinAWSDamp->GetValue();
4880
4881 g_bUseInternSumLog = m_pUseInternSumLog->IsChecked();
4882 double ursDist;
4883 m_pSumLogValue->GetValue().ToDouble(&ursDist);
4884 g_dSumLogNM = fromUsrDistance_Plugin(ursDist, g_iDashDistanceUnit);
4885
4886 g_iUTCOffset = m_pChoiceUTCOffset->GetSelection() - 24;
4887 g_iDashSpeedUnit = m_pChoiceSpeedUnit->GetSelection() - 1;
4888 double DashDBTOffset = m_pSpinDBTOffset->GetValue();
4889 switch (g_iDashDepthUnit - 3) {
4890 case 1:
4891 g_dDashDBTOffset = DashDBTOffset / 3.2808399;
4892 break;
4893 case 2:
4894 g_dDashDBTOffset = DashDBTOffset / 0.54680665;
4895 break;
4896 case 3:
4897 g_dDashDBTOffset = DashDBTOffset / 39.3700787;
4898 break;
4899 case 4:
4900 g_dDashDBTOffset = DashDBTOffset / 100;
4901 break;
4902 default:
4903 g_dDashDBTOffset = DashDBTOffset;
4904 }
4905 g_iDashDepthUnit = m_pChoiceDepthUnit->GetSelection() + 3;
4906 g_iDashDistanceUnit = m_pChoiceDistanceUnit->GetSelection() - 1;
4907 g_iDashWindSpeedUnit = m_pChoiceWindSpeedUnit->GetSelection();
4908 g_bDBtrueWindGround = m_pUseTrueWinddata->GetValue();
4909 g_iDashTempUnit = m_pChoiceTempUnit->GetSelection();
4910 if (curSel != -1) {
4911 DashboardWindowContainer *cont = m_Config.Item(curSel);
4912 cont->m_bIsVisible = m_pCheckBoxIsVisible->IsChecked();
4913 cont->m_sCaption = m_pTextCtrlCaption->GetValue();
4914 cont->m_sOrientation =
4915 m_pChoiceOrientation->GetSelection() == 0 ? _T("V") : _T("H");
4916 cont->m_aInstrumentList.Clear();
4917 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
4918 cont->m_aInstrumentList.Add((int)m_pListCtrlInstruments->GetItemData(i));
4919 }
4920}
4921
4922void DashboardPreferencesDialog::OnDashboardSelected(wxListEvent &event) {
4923 // save changes
4924 SaveDashboardConfig();
4925 UpdateDashboardButtonsState();
4926}
4927
4928void DashboardPreferencesDialog::UpdateDashboardButtonsState() {
4929 long item = -1;
4930 item = m_pListCtrlDashboards->GetNextItem(item, wxLIST_NEXT_ALL,
4931 wxLIST_STATE_SELECTED);
4932 bool enable = (item != -1);
4933
4934 // Disable the Dashboard Delete button if the parent(Dashboard) of this
4935 // dialog is selected.
4936 bool delete_enable = enable;
4937 if (item != -1) {
4938 int sel = m_pListCtrlDashboards->GetItemData(item);
4939 DashboardWindowContainer *cont = m_Config.Item(sel);
4940 DashboardWindow *dash_sel = cont->m_pDashboardWindow;
4941 if (dash_sel == GetParent()) delete_enable = false;
4942 }
4943 m_pButtonDeleteDashboard->Enable(delete_enable);
4944
4945 m_pPanelDashboard->Enable(enable);
4946
4947 if (item != -1) {
4948 curSel = m_pListCtrlDashboards->GetItemData(item);
4949 DashboardWindowContainer *cont = m_Config.Item(curSel);
4950 m_pCheckBoxIsVisible->SetValue(cont->m_bIsVisible);
4951 m_pTextCtrlCaption->SetValue(cont->m_sCaption);
4952 m_pChoiceOrientation->SetSelection(cont->m_sOrientation == _T("V") ? 0 : 1);
4953 m_pListCtrlInstruments->DeleteAllItems();
4954 for (size_t i = 0; i < cont->m_aInstrumentList.GetCount(); i++) {
4955 wxListItem item;
4956 getListItemForInstrument(item, cont->m_aInstrumentList.Item(i));
4957 item.SetId(m_pListCtrlInstruments->GetItemCount());
4958 m_pListCtrlInstruments->InsertItem(item);
4959 }
4960
4961 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
4962 } else {
4963 curSel = -1;
4964 m_pCheckBoxIsVisible->SetValue(false);
4965 m_pTextCtrlCaption->SetValue(_T(""));
4966 m_pChoiceOrientation->SetSelection(0);
4967 m_pListCtrlInstruments->DeleteAllItems();
4968 }
4969 // UpdateButtonsState();
4970}
4971
4972void DashboardPreferencesDialog::OnDashboarddefaultFont(wxCommandEvent &event) {
4973 m_pFontPickerTitle->SetSelectedFont(
4974 wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
4975 m_pFontPickerTitle->SetSelectedColour(wxColour(0, 0, 0));
4976 m_pFontPickerData->SetSelectedFont(
4977 wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4978 m_pFontPickerData->SetSelectedColour(wxColour(0, 0, 0));
4979 m_pFontPickerLabel->SetSelectedFont(
4980 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4981 m_pFontPickerLabel->SetSelectedColour(wxColour(0, 0, 0));
4982 m_pFontPickerSmall->SetSelectedFont(
4983 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4984 m_pFontPickerSmall->SetSelectedColour(wxColour(0, 0, 0));
4985 double scaler = 1.0;
4986 if (OCPN_GetWinDIPScaleFactor() < 1.0)
4987 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
4988 scaler = wxMax(1.0, scaler);
4989
4990 g_USFontTitle = *(m_pFontPickerTitle->GetFontData());
4991 g_FontTitle = *g_pUSFontTitle;
4992 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
4993 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
4994 g_USFontTitle = *g_pUSFontTitle;
4995
4996 g_USFontData = *(m_pFontPickerData->GetFontData());
4997 g_FontData = *g_pUSFontData;
4998 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
4999 g_FontData.SetColour(g_pUSFontData->GetColour());
5000 g_USFontData = *g_pUSFontData;
5001
5002 g_USFontLabel = *(m_pFontPickerLabel->GetFontData());
5003 g_FontLabel = *g_pUSFontLabel;
5004 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
5005 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
5006 g_USFontLabel = *g_pUSFontLabel;
5007
5008 g_USFontSmall = *(m_pFontPickerSmall->GetFontData());
5009 g_FontSmall = *g_pUSFontSmall;
5010 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
5011 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
5012 g_USFontSmall = *g_pUSFontSmall;
5013}
5014
5015void DashboardPreferencesDialog::OnDashboardAdd(wxCommandEvent &event) {
5016 int idx = m_pListCtrlDashboards->GetItemCount();
5017 m_pListCtrlDashboards->InsertItem(idx, 0);
5018 // Data is index in m_Config
5019 m_pListCtrlDashboards->SetItemData(idx, m_Config.GetCount());
5020 wxArrayInt ar;
5021 wxArrayOfInstrumentProperties Property;
5023 NULL, MakeName(), _("Dashboard"), _T("V"), ar, Property);
5024 dwc->m_bIsVisible = true;
5025 m_Config.Add(dwc);
5026}
5027
5028void DashboardPreferencesDialog::OnDashboardDelete(wxCommandEvent &event) {
5029 long itemID = -1;
5030 itemID = m_pListCtrlDashboards->GetNextItem(itemID, wxLIST_NEXT_ALL,
5031 wxLIST_STATE_SELECTED);
5032
5033 int idx = m_pListCtrlDashboards->GetItemData(itemID);
5034 m_pListCtrlDashboards->DeleteItem(itemID);
5035 m_Config.Item(idx)->m_bIsDeleted = true;
5036 UpdateDashboardButtonsState();
5037}
5038
5039void DashboardPreferencesDialog::OnInstrumentSelected(wxListEvent &event) {
5040 UpdateButtonsState();
5041}
5042
5043void DashboardPreferencesDialog::UpdateButtonsState() {
5044 long item = -1;
5045 item = m_pListCtrlInstruments->GetNextItem(item, wxLIST_NEXT_ALL,
5046 wxLIST_STATE_SELECTED);
5047 bool enable = (item != -1);
5048
5049 m_pButtonDelete->Enable(enable);
5050 m_pButtonEdit->Enable(enable); // TODO: Properties ... done Bernd Cirotzki
5051 m_pButtonUp->Enable(item > 0);
5052 m_pButtonDown->Enable(item != -1 &&
5053 item < m_pListCtrlInstruments->GetItemCount() - 1);
5054}
5055
5056void DashboardPreferencesDialog::OnInstrumentAdd(wxCommandEvent &event) {
5057 AddInstrumentDlg pdlg((wxWindow *)event.GetEventObject(), wxID_ANY);
5058
5059#ifdef __OCPN__ANDROID__
5060 wxFont *pF = OCPNGetFont(_("Dialog"));
5061 pdlg.SetFont(*pF);
5062
5063 wxSize esize;
5064 esize.x = GetCharWidth() * 110;
5065 esize.y = GetCharHeight() * 40;
5066
5067 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5068 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
5069 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
5070 pdlg.SetSize(esize);
5071
5072 pdlg.CentreOnScreen();
5073#endif
5074 pdlg.ShowModal();
5075 if (pdlg.GetReturnCode() == wxID_OK) {
5076 wxListItem item;
5077 getListItemForInstrument(item, pdlg.GetInstrumentAdded());
5078 item.SetId(m_pListCtrlInstruments->GetItemCount());
5079 m_pListCtrlInstruments->InsertItem(item);
5080 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5081 UpdateButtonsState();
5082 }
5083}
5084
5085void DashboardPreferencesDialog::OnInstrumentDelete(wxCommandEvent &event) {
5086 long itemIDWindow = -1;
5087 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5088 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5089 long itemID = -1;
5090 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5091 wxLIST_STATE_SELECTED);
5093 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5094 InstrumentProperties *InstDel = NULL;
5095 if (cont) {
5096 InstrumentProperties *Inst = NULL;
5097 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5098 i++) {
5099 Inst = cont->m_aInstrumentPropertyList.Item(i);
5100 if (Inst->m_aInstrument ==
5101 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5102 Inst->m_Listplace == itemID) {
5103 cont->m_aInstrumentPropertyList.Remove(Inst);
5104 InstDel = Inst;
5105 break;
5106 } else {
5107 if (Inst->m_Listplace > itemID) Inst->m_Listplace--;
5108 }
5109 }
5110 }
5111 m_pListCtrlInstruments->DeleteItem(itemID);
5112 if (InstDel) {
5113 cont->m_pDashboardWindow->SetInstrumentList(
5114 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5115 delete InstDel;
5116 }
5117 UpdateButtonsState();
5118}
5119
5120inline void GetFontData(OCPNFontButton *FontButton, wxFontData &UnScaledFont,
5121 wxFontData &ScaledFont, double scaler) {
5122 UnScaledFont = *(FontButton->GetFontData());
5123 ScaledFont = UnScaledFont;
5124 ScaledFont.SetChosenFont(UnScaledFont.GetChosenFont().Scaled(scaler));
5125}
5126
5127void DashboardPreferencesDialog::OnInstrumentEdit(wxCommandEvent &event) {
5128 // TODO: Instument options
5129 // m_Config = Arrayofdashboardwindows.
5130 long itemIDWindow = -1;
5131 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5132 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5133 long itemID = -1;
5134 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5135 wxLIST_STATE_SELECTED);
5136 // m_Config.
5137 // curSel = m_pListCtrlDashboards->GetItemData(itemWindow);
5138 // DashboardWindowContainer *cont = m_Config.Item(curSel);
5139 // if (cont) ....
5141 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5142 if (!cont) return;
5143 InstrumentProperties *Inst = NULL;
5144 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5145 i++) {
5146 Inst = cont->m_aInstrumentPropertyList.Item(
5147 i); // m_pListCtrlInstruments->GetItemData(itemID)
5148 if (Inst->m_aInstrument == (int)m_pListCtrlInstruments->GetItemData(
5149 itemID)) // Is for right Instrumenttype.
5150 {
5151 if (Inst->m_Listplace == itemID) break;
5152 }
5153 Inst = NULL;
5154 }
5155 if (!Inst) {
5156 Inst = new InstrumentProperties(m_pListCtrlInstruments->GetItemData(itemID),
5157 itemID);
5158 cont->m_aInstrumentPropertyList.Add(Inst);
5159 }
5160 EditDialog *Edit = new EditDialog(this, *Inst, wxID_ANY);
5161 Edit->Fit();
5162 bool DefaultFont = false;
5163 if (Edit->ShowModal() == wxID_OK) {
5164 DefaultFont = true;
5165 double scaler = 1.0;
5166 if (OCPN_GetWinDIPScaleFactor() < 1.0)
5167 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
5168 scaler = wxMax(1.0, scaler);
5169 if (Edit->m_fontPicker2->GetFont().Scaled(scaler) !=
5170 g_FontTitle.GetChosenFont() ||
5171 Edit->m_fontPicker2->GetSelectedColour() != g_FontTitle.GetColour())
5172 DefaultFont = false;
5173 if (Edit->m_fontPicker4->GetFont().Scaled(scaler) !=
5174 g_FontData.GetChosenFont() ||
5175 Edit->m_fontPicker4->GetSelectedColour() != g_FontData.GetColour())
5176 DefaultFont = false;
5177 if (Edit->m_fontPicker5->GetFont().Scaled(scaler) !=
5178 g_FontLabel.GetChosenFont() ||
5179 Edit->m_fontPicker5->GetSelectedColour() != g_FontLabel.GetColour())
5180 DefaultFont = false;
5181 if (Edit->m_fontPicker6->GetFont().Scaled(scaler) !=
5182 g_FontSmall.GetChosenFont() ||
5183 Edit->m_fontPicker6->GetSelectedColour() != g_FontSmall.GetColour())
5184 DefaultFont = false;
5185 wxColour dummy;
5186 GetGlobalColor(_T("DASHL"), &dummy);
5187 if (Edit->m_colourPicker1->GetColour() != dummy) DefaultFont = false;
5188 GetGlobalColor(_T("DASHB"), &dummy);
5189 if (Edit->m_colourPicker2->GetColour() != dummy) DefaultFont = false;
5190 GetGlobalColor(_T("DASHN"), &dummy);
5191 if (Edit->m_colourPicker3->GetColour() != dummy) DefaultFont = false;
5192 GetGlobalColor(_T("BLUE3"), &dummy);
5193 if (Edit->m_colourPicker4->GetColour() != dummy) DefaultFont = false;
5194 if (DefaultFont)
5195 cont->m_aInstrumentPropertyList.Remove(Inst);
5196 else {
5197 GetFontData(Edit->m_fontPicker2, Inst->m_USTitleFont, Inst->m_TitleFont,
5198 scaler);
5199 GetFontData(Edit->m_fontPicker4, Inst->m_USDataFont, Inst->m_DataFont,
5200 scaler);
5201 GetFontData(Edit->m_fontPicker5, Inst->m_USLabelFont, Inst->m_LabelFont,
5202 scaler);
5203 GetFontData(Edit->m_fontPicker6, Inst->m_USSmallFont, Inst->m_SmallFont,
5204 scaler);
5205 Inst->m_DataBackgroundColour = Edit->m_colourPicker2->GetColour();
5206 Inst->m_TitleBackgroundColour = Edit->m_colourPicker1->GetColour();
5207 Inst->m_Arrow_First_Colour = Edit->m_colourPicker3->GetColour();
5208 Inst->m_Arrow_Second_Colour = Edit->m_colourPicker4->GetColour();
5209 }
5210 }
5211 delete Edit;
5212 if (cont->m_pDashboardWindow) {
5213 cont->m_pDashboardWindow->SetInstrumentList(
5214 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5215 }
5216 if (DefaultFont) delete Inst;
5217}
5218
5219void DashboardPreferencesDialog::OnInstrumentUp(wxCommandEvent &event) {
5220 long itemIDWindow = -1;
5221 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5222 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5223 long itemID = -1;
5224 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5225 wxLIST_STATE_SELECTED);
5226 wxListItem item;
5227 item.SetId(itemID);
5228 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5229 m_pListCtrlInstruments->GetItem(item);
5230 item.SetId(itemID - 1);
5231 // item.SetImage(0); // image 0, by default
5232 // Now see if the Old itemId has an own Fontdata
5234 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5235 if (cont) {
5236 InstrumentProperties *Inst = NULL;
5237 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5238 i++) {
5239 Inst = cont->m_aInstrumentPropertyList.Item(i);
5240 if (Inst->m_Listplace == (itemID - 1)) Inst->m_Listplace = itemID;
5241 if (Inst->m_aInstrument ==
5242 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5243 Inst->m_Listplace == itemID) {
5244 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID - 1;
5245 }
5246 }
5247 }
5248 m_pListCtrlInstruments->DeleteItem(itemID);
5249 m_pListCtrlInstruments->InsertItem(item);
5250 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5251 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5252
5253 m_pListCtrlInstruments->SetItemState(itemID - 1, wxLIST_STATE_SELECTED,
5254 wxLIST_STATE_SELECTED);
5255
5256 UpdateButtonsState();
5257}
5258
5259void DashboardPreferencesDialog::OnInstrumentDown(wxCommandEvent &event) {
5260 long itemIDWindow = -1;
5261 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5262 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5263 long itemID = -1;
5264 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5265 wxLIST_STATE_SELECTED);
5266
5267 wxListItem item;
5268 item.SetId(itemID);
5269 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5270 m_pListCtrlInstruments->GetItem(item);
5271 item.SetId(itemID + 1);
5272 // item.SetImage(0); // image 0, by default
5273 // Now see if the Old itemId has an own Fontdata
5275 m_Config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5276 if (cont) {
5277 InstrumentProperties *Inst = NULL;
5278 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5279 i++) {
5280 Inst = cont->m_aInstrumentPropertyList.Item(i);
5281 if (Inst->m_Listplace == (itemID + 1) &&
5282 Inst->m_aInstrument !=
5283 (int)m_pListCtrlInstruments->GetItemData(itemID))
5284 Inst->m_Listplace = itemID;
5285 if (Inst->m_aInstrument ==
5286 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5287 Inst->m_Listplace == itemID) {
5288 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID + 1;
5289 break;
5290 }
5291 }
5292 }
5293 m_pListCtrlInstruments->DeleteItem(itemID);
5294 m_pListCtrlInstruments->InsertItem(item);
5295 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5296 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5297
5298 m_pListCtrlInstruments->SetItemState(itemID + 1, wxLIST_STATE_SELECTED,
5299 wxLIST_STATE_SELECTED);
5300
5301 UpdateButtonsState();
5302}
5303
5304void DashboardPreferencesDialog::OnDistanceUnitSelect(wxCommandEvent &event) {
5305 // Distance unit is changed so we need to update the SunLog value control.
5306 g_iDashDistanceUnit = m_pChoiceDistanceUnit->GetSelection() - 1;
5307 m_SumLogUnit->SetLabel(getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ");
5308 m_pSumLogValue->SetValue(wxString::Format(
5309 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
5310}
5311
5312//----------------------------------------------------------------
5313//
5314// Add Instrument Dialog Implementation
5315//
5316//----------------------------------------------------------------
5317
5318AddInstrumentDlg::AddInstrumentDlg(wxWindow *pparent, wxWindowID id)
5319 : wxDialog(pparent, id, _("Add instrument"), wxDefaultPosition,
5320 wxDefaultSize, wxDEFAULT_DIALOG_STYLE) {
5321 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
5322 SetSizer(itemBoxSizer01);
5323 wxStaticText *itemStaticText01 =
5324 new wxStaticText(this, wxID_ANY, _("Select instrument to add:"),
5325 wxDefaultPosition, wxDefaultSize, 0);
5326 itemBoxSizer01->Add(itemStaticText01, 0, wxEXPAND | wxALL, 5);
5327
5328 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
5329
5330 wxImageList *imglist =
5331 new wxImageList(instImageRefSize, instImageRefSize, true, 2);
5332
5333 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
5334 wxImage inst1s =
5335 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5336 imglist->Add(wxBitmap(inst1s));
5337
5338 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
5339 wxImage dial1s =
5340 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5341 imglist->Add(wxBitmap(dial1s));
5342
5343 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5344 int vsize = dsize.y * 50 / 100;
5345
5346#ifdef __OCPN__ANDROID__
5347 int dw, dh;
5348 wxDisplaySize(&dw, &dh);
5349 vsize = dh * 50 / 100;
5350#endif
5351
5352 m_pListCtrlInstruments = new wxListCtrl(
5353 this, wxID_ANY, wxDefaultPosition, wxSize(-1, vsize /*250, 180 */),
5354 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING);
5355 itemBoxSizer01->Add(m_pListCtrlInstruments, 0, wxEXPAND | wxALL, 5);
5356 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
5357 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
5358
5359 wxFont *pF = OCPNGetFont(_("Dialog"));
5360 m_pListCtrlInstruments->SetFont(*pF);
5361
5362#ifdef __OCPN__ANDROID__
5363 m_pListCtrlInstruments->GetHandle()->setStyleSheet(qtStyleSheet);
5365#endif
5366
5367 wxStdDialogButtonSizer *DialogButtonSizer =
5368 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
5369 itemBoxSizer01->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
5370
5371 long ident = 0;
5372 for (unsigned int i = ID_DBP_I_POS; i < ID_DBP_LAST_ENTRY;
5373 i++) { // do not reference an instrument, but the last dummy entry in
5374 // the list
5375 wxListItem item;
5376 if (IsObsolete(i)) continue;
5377 getListItemForInstrument(item, i);
5378 item.SetId(ident);
5379 m_pListCtrlInstruments->InsertItem(item);
5380 id++;
5381 }
5382
5383 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5384 m_pListCtrlInstruments->SetItemState(0, wxLIST_STATE_SELECTED,
5385 wxLIST_STATE_SELECTED);
5386 Fit();
5387}
5388
5389unsigned int AddInstrumentDlg::GetInstrumentAdded() {
5390 long itemID = -1;
5391 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5392 wxLIST_STATE_SELECTED);
5393
5394 return (int)m_pListCtrlInstruments->GetItemData(itemID);
5395}
5396
5397//----------------------------------------------------------------
5398//
5399// Dashboard Window Implementation
5400//
5401//----------------------------------------------------------------
5402
5403// wxWS_EX_VALIDATE_RECURSIVELY required to push events to parents
5404DashboardWindow::DashboardWindow(wxWindow *pparent, wxWindowID id,
5405 wxAuiManager *auimgr, dashboard_pi *plugin,
5406 int orient, DashboardWindowContainer *mycont)
5407 : wxWindow(pparent, id, wxDefaultPosition, wxDefaultSize, 0) {
5408 // wxDialog::Create(pparent, id, _("tileMine"), wxDefaultPosition,
5409 // wxDefaultSize, wxDEFAULT_DIALOG_STYLE, _T("Dashboard"));
5410
5411 m_pauimgr = auimgr;
5412 m_plugin = plugin;
5413 m_Container = mycont;
5414
5415 // wx2.9 itemBoxSizer = new wxWrapSizer( orient );
5416 itemBoxSizer = new wxBoxSizer(orient);
5417 SetSizer(itemBoxSizer);
5418 Connect(wxEVT_SIZE, wxSizeEventHandler(DashboardWindow::OnSize), NULL, this);
5419 Connect(wxEVT_CONTEXT_MENU,
5420 wxContextMenuEventHandler(DashboardWindow::OnContextMenu), NULL,
5421 this);
5422 Connect(wxEVT_COMMAND_MENU_SELECTED,
5423 wxCommandEventHandler(DashboardWindow::OnContextMenuSelect), NULL,
5424 this);
5425
5426#ifdef __OCPN__ANDROID__
5427 Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5428 Connect(wxEVT_LEFT_UP, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5429 Connect(wxEVT_MOTION, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5430
5431 GetHandle()->setAttribute(Qt::WA_AcceptTouchEvents);
5432 GetHandle()->grabGesture(Qt::PinchGesture);
5433 GetHandle()->grabGesture(Qt::PanGesture);
5434
5435 Connect(wxEVT_QT_PINCHGESTURE,
5436 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::
5437 OnEvtPinchGesture,
5438 NULL, this);
5439 Connect(
5440 wxEVT_QT_PANGESTURE,
5441 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::OnEvtPanGesture,
5442 NULL, this);
5443#endif
5444
5445 Hide();
5446
5447 m_binResize = false;
5448 m_binPinch = false;
5449}
5450
5451DashboardWindow::~DashboardWindow() {
5452 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
5453 DashboardInstrumentContainer *pdic = m_ArrayOfInstrument.Item(i);
5454 delete pdic;
5455 }
5456}
5457
5458#ifdef __OCPN__ANDROID__
5459void DashboardWindow::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5460 float zoom_gain = 0.3;
5461 float zoom_val;
5462 float total_zoom_val;
5463
5464 if (event.GetScaleFactor() > 1)
5465 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5466 else
5467 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zoom_gain);
5468
5469 if (event.GetTotalScaleFactor() > 1)
5470 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5471 else
5472 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5473
5474 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5475
5476 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5477 double aRatio = (double)currentSize.y / (double)currentSize.x;
5478
5479 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5480 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5481
5482 switch (event.GetState()) {
5483 case GestureStarted:
5484 m_binPinch = true;
5485 break;
5486
5487 case GestureUpdated:
5488 currentSize.y *= zoom_val;
5489 currentSize.x *= zoom_val;
5490
5491 if ((par_pos.y + currentSize.y) > par_size.y)
5492 currentSize.y = par_size.y - par_pos.y;
5493
5494 if ((par_pos.x + currentSize.x) > par_size.x)
5495 currentSize.x = par_size.x - par_pos.x;
5496
5498 currentSize.x = currentSize.y / aRatio;
5499
5500 currentSize.x = wxMax(currentSize.x, 150);
5501 currentSize.y = wxMax(currentSize.y, 150);
5502
5503 pane.FloatingSize(currentSize);
5504 m_pauimgr->Update();
5505
5506 break;
5507
5508 case GestureFinished: {
5509 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5510 currentSize.y *= total_zoom_val;
5511 currentSize.x = currentSize.y / aRatio;
5512 } else {
5513 currentSize.x *= total_zoom_val;
5514 currentSize.y = currentSize.x * aRatio;
5515 }
5516
5517 // Bound the resulting size
5518 if ((par_pos.y + currentSize.y) > par_size.y)
5519 currentSize.y = par_size.y - par_pos.y;
5520
5521 if ((par_pos.x + currentSize.x) > par_size.x)
5522 currentSize.x = par_size.x - par_pos.x;
5523
5524 // not too small
5525 currentSize.x = wxMax(currentSize.x, 150);
5526 currentSize.y = wxMax(currentSize.y, 150);
5527
5528 // Try a manual layout of the window, to estimate a good primary size..
5529
5530 // vertical
5531 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5532 int total_y = 0;
5533 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5534 DashboardInstrument *inst =
5535 m_ArrayOfInstrument.Item(i)->m_pInstrument;
5536 wxSize is =
5537 inst->GetSize(itemBoxSizer->GetOrientation(), currentSize);
5538 total_y += is.y;
5539 }
5540
5541 currentSize.y = total_y;
5542 }
5543
5544 pane.FloatingSize(currentSize);
5545
5546 // Reshow the window
5547 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5548 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5549 inst->Show();
5550 }
5551
5552 m_pauimgr->Update();
5553
5554 m_binPinch = false;
5555 m_binResize = false;
5556
5557 break;
5558 }
5559
5560 case GestureCanceled:
5561 m_binPinch = false;
5562 m_binResize = false;
5563 break;
5564
5565 default:
5566 break;
5567 }
5568}
5569
5570void DashboardWindow::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5571 if (m_binPinch) return;
5572
5573 if (m_binResize) return;
5574
5575 int x = event.GetOffset().x;
5576 int y = event.GetOffset().y;
5577
5578 int lx = event.GetLastOffset().x;
5579 int ly = event.GetLastOffset().y;
5580
5581 int dx = x - lx;
5582 int dy = y - ly;
5583
5584 switch (event.GetState()) {
5585 case GestureStarted:
5586 if (m_binPan) break;
5587
5588 m_binPan = true;
5589 break;
5590
5591 case GestureUpdated:
5592 if (m_binPan) {
5593 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5594 wxPoint par_pos_old = ClientToScreen(wxPoint(0, 0)); // GetPosition();
5595
5596 wxPoint par_pos = par_pos_old;
5597 par_pos.x += dx;
5598 par_pos.y += dy;
5599
5600 par_pos.x = wxMax(par_pos.x, 0);
5601 par_pos.y = wxMax(par_pos.y, 0);
5602
5603 wxSize mySize = GetSize();
5604
5605 if ((par_pos.y + mySize.y) > par_size.y)
5606 par_pos.y = par_size.y - mySize.y;
5607
5608 if ((par_pos.x + mySize.x) > par_size.x)
5609 par_pos.x = par_size.x - mySize.x;
5610
5611 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5612 pane.FloatingPosition(par_pos).Float();
5613 m_pauimgr->Update();
5614 }
5615 break;
5616
5617 case GestureFinished:
5618 if (m_binPan) {
5619 }
5620 m_binPan = false;
5621
5622 break;
5623
5624 case GestureCanceled:
5625 m_binPan = false;
5626 break;
5627
5628 default:
5629 break;
5630 }
5631}
5632
5633void DashboardWindow::OnMouseEvent(wxMouseEvent &event) {
5634 if (m_binPinch) return;
5635
5636 if (m_binResize) {
5637 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5638 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5639 double aRatio = (double)currentSize.y / (double)currentSize.x;
5640
5641 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5642 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5643
5644 if (event.LeftDown()) {
5645 m_resizeStartPoint = event.GetPosition();
5646 m_resizeStartSize = currentSize;
5647 m_binResize2 = true;
5648 }
5649
5650 if (m_binResize2) {
5651 if (event.Dragging()) {
5652 wxPoint p = event.GetPosition();
5653
5654 wxSize dragSize = m_resizeStartSize;
5655
5656 dragSize.y += p.y - m_resizeStartPoint.y;
5657 dragSize.x += p.x - m_resizeStartPoint.x;
5658 ;
5659
5660 if ((par_pos.y + dragSize.y) > par_size.y)
5661 dragSize.y = par_size.y - par_pos.y;
5662
5663 if ((par_pos.x + dragSize.x) > par_size.x)
5664 dragSize.x = par_size.x - par_pos.x;
5665
5667 // dragSize.x = dragSize.y / aRatio;
5668
5669 // not too small
5670 dragSize.x = wxMax(dragSize.x, 150);
5671 dragSize.y = wxMax(dragSize.y, 150);
5672
5673 pane.FloatingSize(dragSize);
5674 m_pauimgr->Update();
5675 }
5676
5677 if (event.LeftUp()) {
5678 wxPoint p = event.GetPosition();
5679
5680 wxSize dragSize = m_resizeStartSize;
5681
5682 dragSize.y += p.y - m_resizeStartPoint.y;
5683 dragSize.x += p.x - m_resizeStartPoint.x;
5684 ;
5685
5686 if ((par_pos.y + dragSize.y) > par_size.y)
5687 dragSize.y = par_size.y - par_pos.y;
5688
5689 if ((par_pos.x + dragSize.x) > par_size.x)
5690 dragSize.x = par_size.x - par_pos.x;
5691
5692 // not too small
5693 dragSize.x = wxMax(dragSize.x, 150);
5694 dragSize.y = wxMax(dragSize.y, 150);
5695 /*
5696 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++
5697 ) { DashboardInstrument* inst =
5698 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Show();
5699 }
5700 */
5701 pane.FloatingSize(dragSize);
5702 m_pauimgr->Update();
5703
5704 m_binResize = false;
5705 m_binResize2 = false;
5706 }
5707 }
5708 }
5709}
5710#endif
5711
5712void DashboardWindow::OnSize(wxSizeEvent &event) {
5713 event.Skip();
5714 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5715 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5716 inst->SetMinSize(
5717 inst->GetSize(itemBoxSizer->GetOrientation(), GetClientSize()));
5718 }
5719 Layout();
5720 Refresh();
5721 // Capture the user adjusted docked Dashboard size
5722 this->m_Container->m_best_size = event.GetSize();
5723}
5724
5725void DashboardWindow::OnContextMenu(wxContextMenuEvent &event) {
5726 wxMenu *contextMenu = new wxMenu();
5727
5728#ifdef __WXQT__
5729 wxFont *pf = OCPNGetFont(_("Menu"));
5730
5731 // add stuff
5732 wxMenuItem *item1 =
5733 new wxMenuItem(contextMenu, ID_DASH_PREFS, _("Preferences..."));
5734 item1->SetFont(*pf);
5735 contextMenu->Append(item1);
5736
5737 wxMenuItem *item2 =
5738 new wxMenuItem(contextMenu, ID_DASH_RESIZE, _("Resize..."));
5739 item2->SetFont(*pf);
5740 contextMenu->Append(item2);
5741
5742#else
5743
5744 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5745 if (pane.IsOk() && pane.IsDocked()) {
5746 contextMenu->Append(ID_DASH_UNDOCK, _("Undock"));
5747 }
5748 wxMenuItem *btnVertical =
5749 contextMenu->AppendRadioItem(ID_DASH_VERTICAL, _("Vertical"));
5750 btnVertical->Check(itemBoxSizer->GetOrientation() == wxVERTICAL);
5751 wxMenuItem *btnHorizontal =
5752 contextMenu->AppendRadioItem(ID_DASH_HORIZONTAL, _("Horizontal"));
5753 btnHorizontal->Check(itemBoxSizer->GetOrientation() == wxHORIZONTAL);
5754 contextMenu->AppendSeparator();
5755
5756 m_plugin->PopulateContextMenu(contextMenu);
5757
5758 contextMenu->AppendSeparator();
5759 contextMenu->Append(ID_DASH_PREFS, _("Preferences..."));
5760
5761#endif
5762
5763 PopupMenu(contextMenu);
5764 delete contextMenu;
5765}
5766
5767void DashboardWindow::OnContextMenuSelect(wxCommandEvent &event) {
5768 if (event.GetId() < ID_DASH_PREFS) { // Toggle dashboard visibility
5769 if (m_plugin->GetDashboardWindowShownCount() > 1 || event.IsChecked())
5770 m_plugin->ShowDashboard(event.GetId() - 1, event.IsChecked());
5771 else
5772 m_plugin->ShowDashboard(event.GetId() - 1, true);
5773
5774 SetToolbarItemState(m_plugin->GetToolbarItemId(),
5775 m_plugin->GetDashboardWindowShownCount() != 0);
5776 }
5777
5778 switch (event.GetId()) {
5779 case ID_DASH_PREFS: {
5780 // Capture the dashboard's floating_pos before update.
5781 wxPoint fp = m_pauimgr->GetPane(this).floating_pos;
5782 m_plugin->ShowPreferencesDialog(this);
5783 // This method sets the correct size of the edited dashboard,
5784 // but if it's not specified, also a default floating_pos.
5785 ChangePaneOrientation(GetSizerOrientation(), true, fp.x, fp.y);
5786 if (g_bUseInternSumLog) m_plugin->UpdateSumLog(false);
5787 return; // Does it's own save.
5788 }
5789 case ID_DASH_RESIZE: {
5790 /*
5791 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
5792 DashboardInstrument* inst =
5793 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Hide();
5794 }
5795 */
5796 m_binResize = true;
5797
5798 return;
5799 }
5800 case ID_DASH_VERTICAL: {
5801 ChangePaneOrientation(wxVERTICAL, true);
5802 m_Container->m_sOrientation = _T("V");
5803 break;
5804 }
5805 case ID_DASH_HORIZONTAL: {
5806 ChangePaneOrientation(wxHORIZONTAL, true);
5807 m_Container->m_sOrientation = _T("H");
5808 break;
5809 }
5810 case ID_DASH_UNDOCK: {
5811 ChangePaneOrientation(GetSizerOrientation(), true);
5812 return; // Nothing changed so nothing need be saved
5813 }
5814 }
5815 m_plugin->SaveConfig();
5816}
5817
5818void DashboardWindow::SetColorScheme(PI_ColorScheme cs) {
5819 DimeWindow(this);
5820
5821 // Improve appearance, especially in DUSK or NIGHT palette
5822 wxColour col = g_BackgroundColor;
5823
5824 if (!g_ForceBackgroundColor) GetGlobalColor(_T("DASHL"), &col);
5825 SetBackgroundColour(col);
5826
5827 Refresh(false);
5828}
5829
5830void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr,
5831 int fpx, int fpy) {
5832 m_pauimgr->DetachPane(this);
5833 SetSizerOrientation(orient);
5834 bool vertical = orient == wxVERTICAL;
5835 // wxSize sz = GetSize( orient, wxDefaultSize );
5836 wxSize sz = GetMinSize();
5837 // We must change Name to reset AUI perpective
5838 m_Container->m_sName = MakeName();
5839 m_pauimgr->AddPane(this, wxAuiPaneInfo()
5840 .Name(m_Container->m_sName)
5841 .Caption(m_Container->m_sCaption)
5842 .CaptionVisible(true)
5843 .TopDockable(!vertical)
5844 .BottomDockable(!vertical)
5845 .LeftDockable(vertical)
5846 .RightDockable(vertical)
5847 .MinSize(sz)
5848 .BestSize(sz)
5849 .FloatingSize(sz)
5850 .FloatingPosition(fpx, fpy)
5851 .Float()
5852 .Show(m_Container->m_bIsVisible));
5853
5854#ifdef __OCPN__ANDROID__
5855 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5856 pane.Dockable(false);
5857#endif
5858
5859 if (updateAUImgr) m_pauimgr->Update();
5860}
5861
5862void DashboardWindow::SetSizerOrientation(int orient) {
5863 itemBoxSizer->SetOrientation(orient);
5864 /* We must reset all MinSize to ensure we start with new default */
5865 wxWindowListNode *node = GetChildren().GetFirst();
5866 while (node) {
5867 node->GetData()->SetMinSize(wxDefaultSize);
5868 node = node->GetNext();
5869 }
5870 SetMinSize(wxDefaultSize);
5871 Fit();
5872 SetMinSize(itemBoxSizer->GetMinSize());
5873}
5874
5875int DashboardWindow::GetSizerOrientation() {
5876 return itemBoxSizer->GetOrientation();
5877}
5878
5879bool isArrayIntEqual(const wxArrayInt &l1, const wxArrayOfInstrument &l2) {
5880 if (l1.GetCount() != l2.GetCount()) return false;
5881
5882 for (size_t i = 0; i < l1.GetCount(); i++)
5883 if (l1.Item(i) != l2.Item(i)->m_ID) return false;
5884
5885 return true;
5886}
5887
5888bool DashboardWindow::isInstrumentListEqual(const wxArrayInt &list) {
5889 return isArrayIntEqual(list, m_ArrayOfInstrument);
5890}
5891
5892void DashboardWindow::SetInstrumentList(
5893 wxArrayInt list, wxArrayOfInstrumentProperties *InstrumentPropertyList) {
5894 /* options
5895 ID_DBP_D_SOG: config max value, show STW optional
5896 ID_DBP_D_COG: +SOG +HDG? +BRG?
5897 ID_DBP_D_AWS: config max value. Two arrows for AWS+TWS?
5898 ID_DBP_D_VMG: config max value
5899 ID_DBP_I_DPT: config unit (meter, feet, fathoms)
5900 ID_DBP_D_DPT: show temp optional
5901 // compass: use COG or HDG
5902 // velocity range
5903 // rudder range
5904
5905 */
5906 InstrumentProperties *Properties;
5907 m_ArrayOfInstrument.Clear();
5908 itemBoxSizer->Clear(true);
5909 for (size_t i = 0; i < list.GetCount(); i++) {
5910 int id = list.Item(i);
5911 Properties = NULL;
5912 for (size_t j = 0; j < InstrumentPropertyList->GetCount(); j++) {
5913 if (InstrumentPropertyList->Item(j)->m_aInstrument == id &&
5914 InstrumentPropertyList->Item(j)->m_Listplace == (int)i) {
5915 Properties = InstrumentPropertyList->Item(j);
5916 break;
5917 }
5918 }
5919 DashboardInstrument *instrument = NULL;
5920 switch (id) {
5921 case ID_DBP_I_POS:
5922 instrument = new DashboardInstrument_Position(
5923 this, wxID_ANY, getInstrumentCaption(id), Properties);
5924 break;
5925 case ID_DBP_I_SOG:
5926 instrument = new DashboardInstrument_Single(
5927 this, wxID_ANY, getInstrumentCaption(id), Properties,
5928 OCPN_DBP_STC_SOG, _T("%5.1f"));
5929 break;
5930 case ID_DBP_D_SOG:
5931 instrument = new DashboardInstrument_Speedometer(
5932 this, wxID_ANY, getInstrumentCaption(id), Properties,
5933 OCPN_DBP_STC_SOG, 0, g_iDashSpeedMax);
5934 ((DashboardInstrument_Dial *)instrument)
5935 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5936 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5937 // DIAL_MARKER_SIMPLE, 5);
5938 ((DashboardInstrument_Dial *)instrument)
5939 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5940 ((DashboardInstrument_Dial *)instrument)
5941 ->SetOptionExtraValue(OCPN_DBP_STC_STW, "STW %.1f",
5942 DIAL_POSITION_BOTTOMMIDDLE);
5943 break;
5944 case ID_DBP_D_STW:
5945 instrument = new DashboardInstrument_Speedometer(
5946 this, wxID_ANY, getInstrumentCaption(id), Properties,
5947 OCPN_DBP_STC_STW, 0, g_iDashSpeedMax);
5948 ((DashboardInstrument_Dial *)instrument)
5949 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5950 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5951 // DIAL_MARKER_SIMPLE, 5);
5952 ((DashboardInstrument_Dial *)instrument)
5953 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5954 ((DashboardInstrument_Dial *)instrument)
5955 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, "SOG %.1f",
5956 DIAL_POSITION_BOTTOMMIDDLE);
5957 break;
5958 case ID_DBP_I_COG:
5959 instrument = new DashboardInstrument_Single(
5960 this, wxID_ANY, getInstrumentCaption(id), Properties,
5961 OCPN_DBP_STC_COG, _T("%03.0f"));
5962 break;
5963 case ID_DBP_M_COG:
5964 instrument = new DashboardInstrument_Single(
5965 this, wxID_ANY, getInstrumentCaption(id), Properties,
5966 OCPN_DBP_STC_MCOG, _T("%03.0f"));
5967 break;
5968 case ID_DBP_D_COG:
5969 instrument = new DashboardInstrument_Compass(
5970 this, wxID_ANY, getInstrumentCaption(id), Properties,
5971 OCPN_DBP_STC_COG);
5972 ((DashboardInstrument_Dial *)instrument)
5973 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5974 ((DashboardInstrument_Dial *)instrument)
5975 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5976 ((DashboardInstrument_Dial *)instrument)
5977 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, _T("SOG\n%.2f"),
5978 DIAL_POSITION_BOTTOMLEFT);
5979 break;
5980 case ID_DBP_D_HDT:
5981 instrument = new DashboardInstrument_Compass(
5982 this, wxID_ANY, getInstrumentCaption(id), Properties,
5983 OCPN_DBP_STC_HDT);
5984 ((DashboardInstrument_Dial *)instrument)
5985 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5986 ((DashboardInstrument_Dial *)instrument)
5987 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5988 ((DashboardInstrument_Dial *)instrument)
5989 ->SetOptionExtraValue(OCPN_DBP_STC_STW, _T("STW\n%.1f"),
5990 DIAL_POSITION_BOTTOMLEFT);
5991 break;
5992 case ID_DBP_I_STW:
5993 instrument = new DashboardInstrument_Single(
5994 this, wxID_ANY, getInstrumentCaption(id), Properties,
5995 OCPN_DBP_STC_STW, _T("%.1f"));
5996 break;
5997 case ID_DBP_I_HDT: // true heading
5998 // TODO: Option True or Magnetic
5999 instrument = new DashboardInstrument_Single(
6000 this, wxID_ANY, getInstrumentCaption(id), Properties,
6001 OCPN_DBP_STC_HDT, _T("%03.0f"));
6002 break;
6003 case ID_DBP_I_HDM: // magnetic heading
6004 instrument = new DashboardInstrument_Single(
6005 this, wxID_ANY, getInstrumentCaption(id), Properties,
6006 OCPN_DBP_STC_HDM, _T("%03.0f"));
6007 break;
6008 case ID_DBP_D_AW:
6009 case ID_DBP_D_AWA:
6010 instrument = new DashboardInstrument_Wind(this, wxID_ANY,
6011 getInstrumentCaption(id),
6012 Properties, OCPN_DBP_STC_AWA);
6013 ((DashboardInstrument_Dial *)instrument)
6014 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6015 ((DashboardInstrument_Dial *)instrument)
6016 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, _T("%.1f"),
6017 DIAL_POSITION_INSIDE);
6018 break;
6019 case ID_DBP_I_AWS:
6020 instrument = new DashboardInstrument_Single(
6021 this, wxID_ANY, getInstrumentCaption(id), Properties,
6022 OCPN_DBP_STC_AWS, _T("%.1f"));
6023 break;
6024 case ID_DBP_D_AWS:
6025 instrument = new DashboardInstrument_Speedometer(
6026 this, wxID_ANY, getInstrumentCaption(id), Properties,
6027 OCPN_DBP_STC_AWS, 0, 45);
6028 ((DashboardInstrument_Dial *)instrument)
6029 ->SetOptionLabel(5, DIAL_LABEL_HORIZONTAL);
6030 ((DashboardInstrument_Dial *)instrument)
6031 ->SetOptionMarker(1, DIAL_MARKER_SIMPLE, 5);
6032 ((DashboardInstrument_Dial *)instrument)
6033 ->SetOptionMainValue(_T("A %.1f"), DIAL_POSITION_BOTTOMLEFT);
6034 ((DashboardInstrument_Dial *)instrument)
6035 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("T %.1f"),
6036 DIAL_POSITION_BOTTOMRIGHT);
6037 break;
6038 case ID_DBP_D_TW: // True Wind angle +-180 degr on boat axis
6039 instrument = new DashboardInstrument_TrueWindAngle(
6040 this, wxID_ANY, getInstrumentCaption(id), Properties,
6041 OCPN_DBP_STC_TWA);
6042 ((DashboardInstrument_Dial *)instrument)
6043 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6044 ((DashboardInstrument_Dial *)instrument)
6045 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("%.1f"),
6046 DIAL_POSITION_INSIDE);
6047 break;
6048 case ID_DBP_D_AWA_TWA: // App/True Wind angle +-180 degr on boat axis
6049 instrument = new DashboardInstrument_AppTrueWindAngle(
6050 this, wxID_ANY, getInstrumentCaption(id), Properties,
6051 OCPN_DBP_STC_AWA);
6052 ((DashboardInstrument_Dial *)instrument)->SetCapFlag(OCPN_DBP_STC_TWA);
6053 ((DashboardInstrument_Dial *)instrument)
6054 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_NONE);
6055 ((DashboardInstrument_Dial *)instrument)
6056 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, _T("%.1f"),
6057 DIAL_POSITION_NONE);
6058 ((DashboardInstrument_Dial *)instrument)
6059 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, _T("%.1f"),
6060 DIAL_POSITION_NONE);
6061 break;
6062 case ID_DBP_D_TWD: // True Wind direction
6063 instrument = new DashboardInstrument_WindCompass(
6064 this, wxID_ANY, getInstrumentCaption(id), Properties,
6065 OCPN_DBP_STC_TWD);
6066 ((DashboardInstrument_Dial *)instrument)
6067 ->SetOptionMainValue(_T("%.0f"), DIAL_POSITION_BOTTOMLEFT);
6068 ((DashboardInstrument_Dial *)instrument)
6069 ->SetOptionExtraValue(OCPN_DBP_STC_TWS2, _T("%.1f"),
6070 DIAL_POSITION_INSIDE);
6071 break;
6072 case ID_DBP_I_ALTI:
6073 instrument = new DashboardInstrument_Single(
6074 this, wxID_ANY, getInstrumentCaption(id), Properties,
6075 OCPN_DBP_STC_ALTI, _T("%6.1f"));
6076 break;
6077 case ID_DBP_D_ALTI:
6078 instrument = new DashboardInstrument_Altitude(
6079 this, wxID_ANY, getInstrumentCaption(id), Properties);
6080 break;
6081 case ID_DBP_I_DPT:
6082 instrument = new DashboardInstrument_Single(
6083 this, wxID_ANY, getInstrumentCaption(id), Properties,
6084 OCPN_DBP_STC_DPT, _T("%5.2f"));
6085 break;
6086 case ID_DBP_D_DPT:
6087 instrument = new DashboardInstrument_Depth(
6088 this, wxID_ANY, getInstrumentCaption(id), Properties);
6089 break;
6090 case ID_DBP_I_TMP: // water temperature
6091 instrument = new DashboardInstrument_Single(
6092 this, wxID_ANY, getInstrumentCaption(id), Properties,
6093 OCPN_DBP_STC_TMP, _T("%2.1f"));
6094 break;
6095 case ID_DBP_I_MDA: // barometric pressure
6096 instrument = new DashboardInstrument_Single(
6097 this, wxID_ANY, getInstrumentCaption(id), Properties,
6098 OCPN_DBP_STC_MDA, _T("%5.1f"));
6099 break;
6100 case ID_DBP_D_MDA: // barometric pressure
6101 instrument = new DashboardInstrument_Speedometer(
6102 this, wxID_ANY, getInstrumentCaption(id), Properties,
6103 OCPN_DBP_STC_MDA, 938, 1088);
6104 ((DashboardInstrument_Dial *)instrument)
6105 ->SetOptionLabel(15, DIAL_LABEL_HORIZONTAL);
6106 ((DashboardInstrument_Dial *)instrument)
6107 ->SetOptionMarker(7.5, DIAL_MARKER_SIMPLE, 1);
6108 ((DashboardInstrument_Dial *)instrument)
6109 ->SetOptionMainValue(_T("%5.1f"), DIAL_POSITION_INSIDE);
6110 break;
6111 case ID_DBP_I_ATMP: // air temperature
6112 instrument = new DashboardInstrument_Single(
6113 this, wxID_ANY, getInstrumentCaption(id), Properties,
6114 OCPN_DBP_STC_ATMP, _T("%2.1f"));
6115 break;
6116 case ID_DBP_I_VLW1: // Trip Log
6117 instrument = new DashboardInstrument_Single(
6118 this, wxID_ANY, getInstrumentCaption(id), Properties,
6119 OCPN_DBP_STC_VLW1, _T("%2.1f"));
6120 break;
6121
6122 case ID_DBP_I_VLW2: // Sum Log
6123 instrument = new DashboardInstrument_Single(
6124 this, wxID_ANY, getInstrumentCaption(id), Properties,
6125 OCPN_DBP_STC_VLW2, _T("%2.1f"));
6126 break;
6127
6128 case ID_DBP_I_TWA: // true wind angle
6129 instrument = new DashboardInstrument_Single(
6130 this, wxID_ANY, getInstrumentCaption(id), Properties,
6131 OCPN_DBP_STC_TWA, _T("%5.0f"));
6132 break;
6133 case ID_DBP_I_TWD: // true wind direction
6134 instrument = new DashboardInstrument_Single(
6135 this, wxID_ANY, getInstrumentCaption(id), Properties,
6136 OCPN_DBP_STC_TWD, _T("%3.0f"));
6137 break;
6138 case ID_DBP_I_TWS: // true wind speed
6139 instrument = new DashboardInstrument_Single(
6140 this, wxID_ANY, getInstrumentCaption(id), Properties,
6141 OCPN_DBP_STC_TWS, _T("%2.1f"));
6142 break;
6143 case ID_DBP_I_AWA: // apparent wind angle
6144 instrument = new DashboardInstrument_Single(
6145 this, wxID_ANY, getInstrumentCaption(id), Properties,
6146 OCPN_DBP_STC_AWA, _T("%3.0f"));
6147 break;
6148 case ID_DBP_I_VMGW: // VMG based on wind and STW
6149 instrument = new DashboardInstrument_Single(
6150 this, wxID_ANY, getInstrumentCaption(id), Properties,
6151 OCPN_DBP_STC_VMGW, _T("%2.1f"));
6152 break;
6153 case ID_DBP_I_VMG:
6154 instrument = new DashboardInstrument_Single(
6155 this, wxID_ANY, getInstrumentCaption(id), Properties,
6156 OCPN_DBP_STC_VMG, _T("%5.1f"));
6157 break;
6158 case ID_DBP_D_VMG:
6159 instrument = new DashboardInstrument_Speedometer(
6160 this, wxID_ANY, getInstrumentCaption(id), Properties,
6161 OCPN_DBP_STC_VMG, 0, g_iDashSpeedMax);
6162 ((DashboardInstrument_Dial *)instrument)
6163 ->SetOptionLabel(1, DIAL_LABEL_HORIZONTAL);
6164 ((DashboardInstrument_Dial *)instrument)
6165 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
6166 ((DashboardInstrument_Dial *)instrument)
6167 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, _T("SOG\n%.1f"),
6168 DIAL_POSITION_BOTTOMLEFT);
6169 break;
6170 case ID_DBP_I_RSA:
6171 instrument = new DashboardInstrument_Single(
6172 this, wxID_ANY, getInstrumentCaption(id), Properties,
6173 OCPN_DBP_STC_RSA, _T("%5.0f"));
6174 break;
6175 case ID_DBP_D_RSA:
6176 instrument = new DashboardInstrument_RudderAngle(
6177 this, wxID_ANY, getInstrumentCaption(id), Properties);
6178 break;
6179 case ID_DBP_I_SAT:
6180 instrument = new DashboardInstrument_Single(
6181 this, wxID_ANY, getInstrumentCaption(id), Properties,
6182 OCPN_DBP_STC_SAT, _T("%5.0f"));
6183 break;
6184 case ID_DBP_D_GPS:
6185 instrument = new DashboardInstrument_GPS(
6186 this, wxID_ANY, getInstrumentCaption(id), Properties);
6187 break;
6188 case ID_DBP_I_PTR:
6189 instrument = new DashboardInstrument_Position(
6190 this, wxID_ANY, getInstrumentCaption(id), Properties,
6191 OCPN_DBP_STC_PLA, OCPN_DBP_STC_PLO);
6192 break;
6193 case ID_DBP_I_GPSUTC:
6194 instrument = new DashboardInstrument_Clock(
6195 this, wxID_ANY, getInstrumentCaption(id), Properties);
6196 break;
6197 case ID_DBP_I_SUN:
6198 instrument = new DashboardInstrument_Sun(
6199 this, wxID_ANY, getInstrumentCaption(id), Properties);
6200 break;
6201 case ID_DBP_D_MON:
6202 instrument = new DashboardInstrument_Moon(
6203 this, wxID_ANY, getInstrumentCaption(id), Properties);
6204 break;
6205 case ID_DBP_D_WDH:
6206 instrument = new DashboardInstrument_WindDirHistory(
6207 this, wxID_ANY, getInstrumentCaption(id), Properties);
6208 break;
6209 case ID_DBP_D_BPH:
6210 instrument = new DashboardInstrument_BaroHistory(
6211 this, wxID_ANY, getInstrumentCaption(id), Properties);
6212 break;
6213 case ID_DBP_I_FOS:
6214 instrument = new DashboardInstrument_FromOwnship(
6215 this, wxID_ANY, getInstrumentCaption(id), Properties);
6216 break;
6217 case ID_DBP_I_PITCH:
6218 instrument = new DashboardInstrument_Single(
6219 this, wxID_ANY, getInstrumentCaption(id), Properties,
6220 OCPN_DBP_STC_PITCH, _T("%2.1f"));
6221 break;
6222 case ID_DBP_I_HEEL:
6223 instrument = new DashboardInstrument_Single(
6224 this, wxID_ANY, getInstrumentCaption(id), Properties,
6225 OCPN_DBP_STC_HEEL, _T("%2.1f"));
6226 break;
6227 // any clock display with "LCL" in the format string is converted from
6228 // UTC to local TZ
6229 case ID_DBP_I_SUNLCL:
6230 instrument = new DashboardInstrument_Sun(
6231 this, wxID_ANY, getInstrumentCaption(id), Properties,
6232 _T( "%02i:%02i:%02i LCL" ));
6233 break;
6234 case ID_DBP_I_GPSLCL:
6235 instrument = new DashboardInstrument_Clock(
6236 this, wxID_ANY, getInstrumentCaption(id), Properties,
6237 OCPN_DBP_STC_CLK, _T( "%02i:%02i:%02i LCL" ));
6238 break;
6239 case ID_DBP_I_CPULCL:
6240 instrument = new DashboardInstrument_CPUClock(
6241 this, wxID_ANY, getInstrumentCaption(id), Properties,
6242 _T( "%02i:%02i:%02i LCL" ));
6243 break;
6244 case ID_DBP_I_HUM:
6245 instrument = new DashboardInstrument_Single(
6246 this, wxID_ANY, getInstrumentCaption(id), Properties,
6247 OCPN_DBP_STC_HUM, "%3.0f");
6248 break;
6249 case ID_DBP_I_WCC:
6250 instrument = new DashboardInstrument_Single(
6251 this, wxID_ANY, getInstrumentCaption(id), Properties,
6252 OCPN_DBP_STC_WCC, _T("%5.1f"));
6253 break;
6254 }
6255 if (instrument) {
6256 instrument->instrumentTypeId = id;
6257 m_ArrayOfInstrument.Add(new DashboardInstrumentContainer(
6258 id, instrument, instrument->GetCapacity()));
6259 itemBoxSizer->Add(instrument, 0, wxEXPAND, 0);
6260 if (itemBoxSizer->GetOrientation() == wxHORIZONTAL) {
6261 itemBoxSizer->AddSpacer(5);
6262 }
6263 }
6264 }
6265
6266 // In the absense of any other hints, build the default instrument sizes by
6267 // taking the calculated with of the first (and succeeding) instruments as
6268 // hints for the next. So, best in default loads to start with an instrument
6269 // that accurately calculates its minimum width. e.g.
6270 // DashboardInstrument_Position
6271
6272 wxSize Hint = wxSize(DefaultWidth, DefaultWidth);
6273
6274 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
6275 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
6276 inst->SetMinSize(inst->GetSize(itemBoxSizer->GetOrientation(), Hint));
6277 Hint = inst->GetMinSize();
6278 }
6279
6280 Fit();
6281 Layout();
6282 SetMinSize(itemBoxSizer->GetMinSize());
6283}
6284
6285void DashboardWindow::SendSentenceToAllInstruments(DASH_CAP st, double value,
6286 wxString unit) {
6287 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6288 if (m_ArrayOfInstrument.Item(i)->m_cap_flag.test(st))
6289 m_ArrayOfInstrument.Item(i)->m_pInstrument->SetData(st, value, unit);
6290 }
6291}
6292
6293void DashboardWindow::SendSatInfoToAllInstruments(int cnt, int seq,
6294 wxString talk,
6295 SAT_INFO sats[4]) {
6296 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6297 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_GPS)) &&
6298 dynamic_cast<DashboardInstrument_GPS *>(
6299 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6300 ((DashboardInstrument_GPS *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6301 ->SetSatInfo(cnt, seq, talk, sats);
6302 }
6303}
6304
6305void DashboardWindow::SendUtcTimeToAllInstruments(wxDateTime value) {
6306 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6307 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_CLK)) &&
6308 dynamic_cast<DashboardInstrument_Clock *>(
6309 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6310 // || m_ArrayOfInstrument.Item( i
6311 // )->m_pInstrument->IsKindOf( CLASSINFO(
6312 // DashboardInstrument_Sun ) )
6313 // || m_ArrayOfInstrument.Item( i
6314 // )->m_pInstrument->IsKindOf( CLASSINFO(
6315 // DashboardInstrument_Moon ) ) ) )
6316 ((DashboardInstrument_Clock *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6317 ->SetUtcTime(value);
6318 }
6319}
6320
6321// #include "wx/fontpicker.h"
6322
6323// #include "wx/fontdlg.h"
6324
6325// ============================================================================
6326// implementation
6327// ============================================================================
6328
6329// ----------------------------------------------------------------------------
6330// OCPNFontButton
6331// ----------------------------------------------------------------------------
6332
6333bool OCPNFontButton::Create(wxWindow *parent, wxWindowID id,
6334 const wxFontData &initial, const wxPoint &pos,
6335 const wxSize &size, long style,
6336 const wxValidator &validator,
6337 const wxString &name) {
6338 wxString label = (style & wxFNTP_FONTDESC_AS_LABEL)
6339 ? wxString()
6340 : // label will be updated by UpdateFont
6341 _("Choose font");
6342 label = name;
6343 // create this button
6344 if (!wxButton::Create(parent, id, label, pos, size, style, validator, name)) {
6345 wxFAIL_MSG(wxT("OCPNFontButton creation failed"));
6346 return false;
6347 }
6348
6349 // and handle user clicks on it
6350 Connect(GetId(), wxEVT_BUTTON,
6351 wxCommandEventHandler(OCPNFontButton::OnButtonClick), NULL, this);
6352
6353 m_data = initial;
6354 m_selectedFont =
6355 initial.GetChosenFont().IsOk() ? initial.GetChosenFont() : *wxNORMAL_FONT;
6356 UpdateFont();
6357
6358 return true;
6359}
6360
6361void OCPNFontButton::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
6362 // update the wxFontData to be shown in the dialog
6363 m_data.SetInitialFont(m_selectedFont);
6364 wxFont *pF = OCPNGetFont(_("Dialog"));
6365
6366#ifdef __WXGTK__
6367 // Use a smaller font picker dialog (ocpnGenericFontDialog) for small displays
6368 int display_height = wxGetDisplaySize().y;
6369 if (display_height < 800) {
6370 // create the font dialog and display it
6371 ocpnGenericFontDialog dlg(this, m_data);
6372 dlg.SetFont(*pF);
6373 if (dlg.ShowModal() == wxID_OK) {
6374 m_data = dlg.GetFontData();
6375 m_selectedFont = m_data.GetChosenFont();
6376 // fire an event
6377 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6378 GetEventHandler()->ProcessEvent(event);
6379 UpdateFont();
6380 }
6381 } else {
6382 // create the font dialog and display it
6383 wxFontDialog dlg(this, m_data);
6384 dlg.SetFont(*pF);
6385 if (dlg.ShowModal() == wxID_OK) {
6386 m_data = dlg.GetFontData();
6387 m_selectedFont = m_data.GetChosenFont();
6388 // fire an event
6389 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6390 GetEventHandler()->ProcessEvent(event);
6391 UpdateFont();
6392 }
6393 }
6394
6395#else // Not __GTK__
6396 // create the font dialog and display it
6397 wxFontDialog dlg(this, m_data);
6398 dlg.SetFont(*pF);
6399
6400#ifdef __WXQT__
6401 // Make sure that font dialog will fit on the screen without scrolling
6402 // We do this by setting the dialog font size "small enough" to show "n" lines
6403 wxSize proposed_size = GetParent()->GetSize();
6404 float n_lines = 30;
6405 float font_size = pF->GetPointSize();
6406
6407 if ((proposed_size.y / font_size) < n_lines) {
6408 float new_font_size = proposed_size.y / n_lines;
6409 wxFont *smallFont = new wxFont(*pF);
6410 smallFont->SetPointSize(new_font_size);
6411 dlg.SetFont(*smallFont);
6412 }
6413
6414 dlg.SetSize(GetParent()->GetSize());
6415 dlg.Centre();
6416#endif
6417
6418 if (dlg.ShowModal() == wxID_OK) {
6419 m_data = dlg.GetFontData();
6420 m_selectedFont = m_data.GetChosenFont();
6421
6422 // fire an event
6423 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6424 GetEventHandler()->ProcessEvent(event);
6425
6426 UpdateFont();
6427 }
6428#endif
6429}
6430
6431void OCPNFontButton::UpdateFont() {
6432 if (!m_selectedFont.IsOk()) return;
6433
6434 // Leave black, until Instruments are modified to accept color fonts
6435 // SetForegroundColour(m_data.GetColour());
6436
6437 if (HasFlag(wxFNTP_USEFONT_FOR_LABEL)) {
6438 // use currently selected font for the label...
6439 wxButton::SetFont(m_selectedFont);
6440 wxButton::SetForegroundColour(GetSelectedColour());
6441 }
6442
6443 wxString label =
6444 wxString::Format(wxT("%s, %d"), m_selectedFont.GetFaceName().c_str(),
6445 m_selectedFont.GetPointSize());
6446
6447 if (HasFlag(wxFNTP_FONTDESC_AS_LABEL)) {
6448 SetLabel(label);
6449 }
6450
6451 auto minsize = GetTextExtent(label);
6452 SetSize(minsize);
6453
6454 GetParent()->Layout();
6455 GetParent()->Fit();
6456}
6457
6458// Edit Dialog
6459
6460EditDialog::EditDialog(wxWindow *parent, InstrumentProperties &Properties,
6461 wxWindowID id, const wxString &title, const wxPoint &pos,
6462 const wxSize &size, long style)
6463 : wxDialog(parent, id, title, pos, size, style) {
6464 this->SetSizeHints(wxDefaultSize, wxDefaultSize);
6465
6466 wxBoxSizer *bSizer5;
6467 bSizer5 = new wxBoxSizer(wxVERTICAL);
6468
6469 wxFlexGridSizer *fgSizer2;
6470 fgSizer2 = new wxFlexGridSizer(0, 2, 0, 0);
6471 fgSizer2->SetFlexibleDirection(wxBOTH);
6472 fgSizer2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
6473
6474 m_staticText1 = new wxStaticText(this, wxID_ANY, _("Title:"),
6475 wxDefaultPosition, wxDefaultSize, 0);
6476 m_staticText1->Wrap(-1);
6477 fgSizer2->Add(m_staticText1, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6478
6479 m_fontPicker2 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USTitleFont,
6480 wxDefaultPosition, wxDefaultSize);
6481 fgSizer2->Add(m_fontPicker2, 0, wxALL, 5);
6482
6483 m_staticText5 = new wxStaticText(this, wxID_ANY, _("Title background color:"),
6484 wxDefaultPosition, wxDefaultSize, 0);
6485 m_staticText5->Wrap(-1);
6486 fgSizer2->Add(m_staticText5, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6487
6488 m_colourPicker1 = new wxColourPickerCtrl(
6489 this, wxID_ANY, Properties.m_TitleBackgroundColour, wxDefaultPosition,
6490 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6491 fgSizer2->Add(m_colourPicker1, 0, wxALL, 5);
6492
6493 m_staticText2 = new wxStaticText(this, wxID_ANY, _("Data:"),
6494 wxDefaultPosition, wxDefaultSize, 0);
6495 m_staticText2->Wrap(-1);
6496 fgSizer2->Add(m_staticText2, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6497
6498 m_fontPicker4 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USDataFont,
6499 wxDefaultPosition, wxDefaultSize);
6500 fgSizer2->Add(m_fontPicker4, 0, wxALL, 5);
6501
6502 m_staticText6 = new wxStaticText(this, wxID_ANY, _("Data background color:"),
6503 wxDefaultPosition, wxDefaultSize, 0);
6504 m_staticText6->Wrap(-1);
6505 fgSizer2->Add(m_staticText6, 0, wxALL, 5);
6506
6507 m_colourPicker2 = new wxColourPickerCtrl(
6508 this, wxID_ANY, Properties.m_DataBackgroundColour, wxDefaultPosition,
6509 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6510 fgSizer2->Add(m_colourPicker2, 0, wxALL, 5);
6511
6512 m_staticText3 = new wxStaticText(this, wxID_ANY, _("Label:"),
6513 wxDefaultPosition, wxDefaultSize, 0);
6514 m_staticText3->Wrap(-1);
6515 fgSizer2->Add(m_staticText3, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6516
6517 m_fontPicker5 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USLabelFont,
6518 wxDefaultPosition, wxDefaultSize);
6519 fgSizer2->Add(m_fontPicker5, 0, wxALL, 5);
6520
6521 m_staticText4 = new wxStaticText(this, wxID_ANY, _("Small:"),
6522 wxDefaultPosition, wxDefaultSize, 0);
6523 m_staticText4->Wrap(-1);
6524 fgSizer2->Add(m_staticText4, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6525
6526 m_fontPicker6 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USSmallFont,
6527 wxDefaultPosition, wxDefaultSize);
6528 fgSizer2->Add(m_fontPicker6, 0, wxALL, 5);
6529
6530 m_staticText9 = new wxStaticText(this, wxID_ANY, _("Arrow 1 Color :"),
6531 wxDefaultPosition, wxDefaultSize, 0);
6532 m_staticText9->Wrap(-1);
6533 fgSizer2->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6534
6535 m_colourPicker3 = new wxColourPickerCtrl(
6536 this, wxID_ANY, Properties.m_Arrow_First_Colour, wxDefaultPosition,
6537 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6538 fgSizer2->Add(m_colourPicker3, 0, wxALL, 5);
6539
6540 m_staticText10 = new wxStaticText(this, wxID_ANY, _("Arrow 2 Color :"),
6541 wxDefaultPosition, wxDefaultSize, 0);
6542 m_staticText10->Wrap(-1);
6543 fgSizer2->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6544
6545 m_colourPicker4 = new wxColourPickerCtrl(
6546 this, wxID_ANY, Properties.m_Arrow_Second_Colour, wxDefaultPosition,
6547 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6548 fgSizer2->Add(m_colourPicker4, 0, wxALL, 5);
6549
6550 m_staticline1 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6551 wxDefaultSize, wxLI_HORIZONTAL);
6552 fgSizer2->Add(m_staticline1, 0, wxEXPAND | wxALL, 5);
6553
6554 m_staticline2 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6555 wxDefaultSize, wxLI_HORIZONTAL);
6556 fgSizer2->Add(m_staticline2, 0, wxEXPAND | wxALL, 5);
6557
6558 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6559
6560 fgSizer2->Add(0, 0, 1, wxEXPAND, 5);
6561
6562 m_staticText7 = new wxStaticText(this, wxID_ANY, wxEmptyString,
6563 wxDefaultPosition, wxDefaultSize, 0);
6564 m_staticText7->Wrap(-1);
6565 fgSizer2->Add(m_staticText7, 0, wxALL, 5);
6566
6567 m_button1 = new wxButton(this, wxID_ANY, _("Set default"), wxDefaultPosition,
6568 wxDefaultSize, 0);
6569 fgSizer2->Add(m_button1, 0, wxALL, 5);
6570
6571 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6572
6573 fgSizer2->Add(5, 0, 1, wxEXPAND, 5);
6574
6575 bSizer5->Add(fgSizer2, 1, wxALL | wxEXPAND, 5);
6576
6577 m_sdbSizer3 = new wxStdDialogButtonSizer();
6578 m_sdbSizer3OK = new wxButton(this, wxID_OK);
6579 m_sdbSizer3->AddButton(m_sdbSizer3OK);
6580 m_sdbSizer3Cancel = new wxButton(this, wxID_CANCEL);
6581 m_sdbSizer3->AddButton(m_sdbSizer3Cancel);
6582 m_sdbSizer3->Realize();
6583
6584 bSizer5->Add(m_sdbSizer3, 0, 0, 1);
6585
6586 bSizer5->Add(0, 10, 0, wxEXPAND, 5);
6587
6588 this->SetSizer(bSizer5);
6589 this->Layout();
6590 bSizer5->Fit(this);
6591
6592 this->Centre(wxBOTH);
6593
6594 // Connect Events
6595 m_button1->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
6596 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6597 this);
6598}
6599
6600EditDialog::~EditDialog() {
6601 // Disconnect Events
6602 m_button1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,
6603 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6604 this);
6605}
6606
6607void EditDialog::OnSetdefault(wxCommandEvent &event) {
6608 m_fontPicker2->SetSelectedFont(g_USFontTitle.GetChosenFont());
6609 m_fontPicker2->SetSelectedColour(g_USFontTitle.GetColour());
6610 m_fontPicker4->SetSelectedFont(g_USFontData.GetChosenFont());
6611 m_fontPicker4->SetSelectedColour(g_USFontData.GetColour());
6612 m_fontPicker5->SetSelectedFont(g_USFontLabel.GetChosenFont());
6613 m_fontPicker5->SetSelectedColour(g_USFontLabel.GetColour());
6614 m_fontPicker6->SetSelectedFont(g_USFontSmall.GetChosenFont());
6615 m_fontPicker6->SetSelectedColour(g_USFontSmall.GetColour());
6616 wxColour dummy;
6617 GetGlobalColor(_T("DASHL"), &dummy);
6618 m_colourPicker1->SetColour(dummy);
6619 GetGlobalColor(_T("DASHB"), &dummy);
6620 m_colourPicker2->SetColour(dummy);
6621 GetGlobalColor(_T("DASHN"), &dummy);
6622 m_colourPicker3->SetColour(dummy);
6623 GetGlobalColor(_T("BLUE3"), &dummy);
6624 m_colourPicker4->SetColour(dummy);
6625 Update();
6626}
6627
6628// Read or write SumLog data to config and update instrument if approropriate
6629// The sumlog value will be updated every minute when we are under way.
6630void dashboard_pi::UpdateSumLog(bool write) {
6631 if (write) {
6632 g_dSumLogNM += d_tripNM;
6633 // Write to config every 10 minutes to ensure that the sumlog value is
6634 // reasonably up to date even if O or the system stops wo SaveConfig().
6635 if (++confprint > 10) {
6636 wxFileConfig *logConf = (wxFileConfig *)m_pconfig;
6637 if (logConf) {
6638 logConf->SetPath("/PlugIns/Dashboard");
6639 logConf->Write("SumLogNM", g_dSumLogNM);
6640 logConf->Flush();
6641 }
6642 confprint = 0;
6643 }
6644 // If internal sumlog is used, update the instrument.
6645 if (g_bUseInternSumLog) {
6646 SendSentenceToAllInstruments(
6647 OCPN_DBP_STC_VLW2,
6648 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6649 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6650 mLOG_Watchdog = 70; // We update sumLog every minute
6651 }
6652 } else {
6653 // Update sumlog instrument from config.
6654 // This will only run if we use internal sumlog.
6655 SendSentenceToAllInstruments(
6656 OCPN_DBP_STC_VLW2,
6657 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6658 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6659 mLOG_Watchdog = no_nav_watchdog_timeout_ticks;
6660 }
6661}
A dashboard instrument that displays the current computer time.
Definition clock.h:130
A dashboard instrument that displays the GNSS clock time, if available.
Definition clock.h:54
A dashboard instrument that displays current moon phase information.
Definition clock.h:82
A dashboard instrument that displays sunrise and sunset times.
Definition clock.h:103
Custom event class for OpenCPN's notification system.
Extended position fix information.
int nSats
Number of satellites used in the fix.
double Var
Magnetic variation in degrees, typically from RMC message.
double Cog
Course over ground in degrees [0-360).
double Lat
Latitude in decimal degrees.
double Hdm
Heading magnetic in degrees [0-360).
time_t FixTime
UTC time of fix.
double Lon
Longitude in decimal degrees.
double Sog
Speed over ground in knots.
double Hdt
Heading true in degrees [0-360).
void SetCursorLatLon(double lat, double lon)
Receives cursor lat/lon position updates.
void ShowPreferencesDialog(wxWindow *parent)
Shows the plugin preferences dialog.
void OnToolbarToolCallback(int id)
Handles toolbar tool clicks.
void SetColorScheme(PI_ColorScheme cs)
Updates plugin color scheme.
void SetPositionFixEx(PlugIn_Position_Fix_Ex &pfix)
Updates plugin with extended position fix data at regular intervals.
int GetToolbarToolCount(void)
Returns the number of toolbar tools this plugin provides.
wxString GetCommonName()
Get the plugin's common (short) name.
wxString GetShortDescription()
Get a brief description of the plugin.
void UpdateAuiStatus(void)
Updates AUI manager status.
int GetAPIVersionMinor()
Returns the minor version number of the plugin API that this plugin supports.
wxBitmap * GetPlugInBitmap()
Get the plugin's icon bitmap.
void SetNMEASentence(wxString &sentence)
Receive all NMEA 0183 sentences from OpenCPN.
int GetAPIVersionMajor()
Returns the major version number of the plugin API that this plugin supports.
wxString GetLongDescription()
Get detailed plugin information.
int GetPlugInVersionMinor()
Returns the minor version number of the plugin itself.
bool DeInit(void)
Clean up plugin resources.
int Init(void)
Initialize the plugin and declare its capabilities.
int GetPlugInVersionMajor()
Returns the major version number of the plugin itself.
Base class for OpenCPN plugins.
The JSON parser.
Definition jsonreader.h:50
int Parse(const wxString &doc, wxJSONValue *val)
Parse the JSON document.
The JSON value class implementation.
Definition jsonval.h:84
bool IsArray() const
Return TRUE if the type of the value stored is an array type.
Definition jsonval.cpp:746
int Size() const
Return the size of the array or map stored in this value.
Definition jsonval.cpp:1332
bool HasMember(unsigned index) const
Return TRUE if the object contains an element at the specified index.
Definition jsonval.cpp:1298
double AsDouble() const
Return the stored value as a double.
Definition jsonval.cpp:827
bool IsString() const
Return TRUE if the type of the value stored is a wxString object.
Definition jsonval.cpp:717
wxString AsString() const
Return the stored value as a wxWidget's string.
Definition jsonval.cpp:872
#define WANTS_NMEA_EVENTS
Receive decoded NMEA events with parsed data.
PI_ColorScheme
Enumeration of color schemes.
#define USES_AUI_MANAGER
Plugin uses wxAuiManager for window management.
#define WANTS_NMEA_SENTENCES
Receive raw NMEA 0183 sentences from all active ports.
#define WANTS_PREFERENCES
Plugin will add page(s) to global preferences dialog.
#define WANTS_CONFIG
Plugin requires persistent configuration storage.
#define WANTS_PLUGIN_MESSAGING
Enable message passing between plugins.
#define INSTALLS_TOOLBAR_TOOL
Plugin will add one or more toolbar buttons.
#define WANTS_CURSOR_LATLON
Receive updates when cursor moves over chart.
Definition ocpn_plugin.h:91
#define WANTS_TOOLBAR_CALLBACK
Receive notification when user left-clicks plugin's toolbar buttons.
wxString * GetpSharedDataLocation(void)
Gets shared application data location.
double toUsrTemp_Plugin(double cel_temp, int unit)
Converts Celsius to user's preferred temperature unit.
wxWindow * GetOCPNCanvasWindow()
Gets OpenCPN's main canvas window.
wxFont * OCPNGetFont(wxString TextElement, int default_size)
Gets a font for UI elements.
wxString getUsrDistanceUnit_Plugin(int unit)
Gets display string for user's preferred distance unit.
wxBitmap GetBitmapFromSVGFile(wxString filename, unsigned int width, unsigned int height)
Creates bitmap from SVG file.
double toUsrDistance_Plugin(double nm_distance, int unit)
Converts nautical miles to user's preferred distance unit.
int InsertPlugInToolSVG(wxString label, wxString SVGfile, wxString SVGfileRollover, wxString SVGfileToggled, wxItemKind kind, wxString shortHelp, wxString longHelp, wxObject *clientData, int position, int tool_sel, opencpn_plugin *pplugin)
Adds a tool using SVG graphics.
wxString getUsrSpeedUnit_Plugin(int unit)
Gets display string for user's preferred speed unit.
void DimeWindow(wxWindow *win)
Applies system color scheme to window.
double GetOCPNGUIToolScaleFactor_PlugIn()
Gets current global GUI scaling factor.
wxString getUsrTempUnit_Plugin(int unit)
Gets display string for user's preferred temperature unit.
wxString GetActiveStyleName()
Gets name of currently active style sheet.
bool DecodeSingleVDOMessage(const wxString &str, PlugIn_Position_Fix_Ex *pos, wxString *accumulator)
Decodes a single VDO (Own Ship AIS) message.
double toUsrSpeed_Plugin(double kts_speed, int unit)
Converts knots to user's preferred speed unit.
std::vector< std::string > GetActivePriorityIdentifiers()
Gets list of active priority identifiers.
void SetToolbarItemState(int item, bool toggle)
Sets toolbar item toggle state.
double OCPN_GetWinDIPScaleFactor()
Gets Windows-specific DPI scaling factor.
wxAuiManager * GetFrameAuiManager(void)
Gets main frame AUI manager.
bool AddLocaleCatalog(wxString catalog)
Adds a locale catalog for translations.
double fromUsrDistance_Plugin(double usr_distance, int unit)
Converts from user's preferred distance unit to nautical miles.
wxFileConfig * GetOCPNConfigObject(void)
Gets OpenCPN's configuration object.
shared_ptr< ObservableListener > GetListener(NMEA2000Id id, wxEventType et, wxEvtHandler *eh)
Gets listener for NMEA 2000 messages.
std::string GetN2000Source(NMEA2000Id id, ObservedEvt ev)
Return source identifier (iface) of a received n2000 message of type id in ev.
vector< uint8_t > GetN2000Payload(NMEA2000Id id, ObservedEvt ev)
Return N2K payload for a received n2000 message of type id in ev.
Identifier for NMEA 2000 message types.