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