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