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 if (root.HasMember("self")) {
2599 if (root["self"].AsString().StartsWith("vessels."))
2600 m_self = (root["self"].AsString()); // for java server, and OpenPlotter
2601 // node.js server 1.20
2602 else if (root["self"].AsString().Length())
2603 m_self = "vessels." + (root["self"].AsString()); // for Node.js server
2604 }
2605
2606 if (root.HasMember("context") && root["context"].IsString()) {
2607 auto context = root["context"].AsString();
2608 if (context != m_self) {
2609 return;
2610 }
2611 }
2612
2613 if (root.HasMember("updates") && root["updates"].IsArray()) {
2614 wxJSONValue &updates = root["updates"];
2615 for (int i = 0; i < updates.Size(); ++i) {
2616 handleSKUpdate(updates[i]);
2617 }
2618 }
2619}
2620
2621void dashboard_pi::handleSKUpdate(wxJSONValue &update) {
2622 wxString sfixtime = "";
2623
2624 if (update.HasMember("timestamp")) {
2625 sfixtime = update["timestamp"].AsString();
2626 }
2627 if (update.HasMember("values") && update["values"].IsArray()) {
2628 wxString talker = wxEmptyString;
2629 if (update.HasMember("source")) {
2630 if (update["source"].HasMember("talker")) {
2631 if (update["source"]["talker"].IsString()) {
2632 talker = update["source"]["talker"].AsString();
2633 }
2634 }
2635 }
2636 for (int j = 0; j < update["values"].Size(); ++j) {
2637 wxJSONValue &item = update["values"][j];
2638 updateSKItem(item, talker, sfixtime);
2639 }
2640 }
2641}
2642
2643void dashboard_pi::updateSKItem(wxJSONValue &item, wxString &talker,
2644 wxString &sfixtime) {
2645 if (item.HasMember("path") && item.HasMember("value")) {
2646 const wxString &update_path = item["path"].AsString();
2647 wxJSONValue &value = item["value"];
2648
2649 // Container for last received sat-system info from SK-N2k
2650 static wxString talkerID = wxEmptyString;
2651 // Container for last received AWA that may be needed for TWS calculation
2652 static double skAWA;
2653
2654 if (update_path == "navigation.position") {
2655 if (m_pri_position >= 2) {
2656 if (value["latitude"].IsDouble() && value["longitude"].IsDouble()) {
2657 double lat = value["latitude"].AsDouble();
2658 double lon = value["longitude"].AsDouble();
2659 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, lat, "SDMM");
2660 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, lon, "SDMM");
2661 m_pri_position = 2;
2662 }
2663 }
2664 } else if (update_path == "navigation.speedOverGround" &&
2665 2 == m_pri_position) {
2666 double sog_knot = GetJsonDouble(value);
2667 if (std::isnan(sog_knot)) return;
2668
2669 SendSentenceToAllInstruments(
2671 toUsrSpeed_Plugin(m_sog_filter.Filter(sog_knot), g_iDashSpeedUnit),
2672 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2673 } else if (update_path == "navigation.courseOverGroundTrue" &&
2674 2 == m_pri_position) {
2675 double cog_rad = GetJsonDouble(value);
2676 if (std::isnan(cog_rad)) return;
2677
2678 double cog_deg = GEODESIC_RAD2DEG(cog_rad);
2679 SendSentenceToAllInstruments(OCPN_DBP_STC_COG,
2680 m_cog_filter.Filter(cog_deg), "\u00B0");
2681 } else if (update_path == "navigation.headingTrue") {
2682 if (m_pri_heading_t >= 2) {
2683 double hdt = GetJsonDouble(value);
2684 if (std::isnan(hdt)) return;
2685
2686 hdt = GEODESIC_RAD2DEG(hdt);
2687 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, "\u00B0T");
2688 m_pri_heading_t = 2;
2689 m_hdt_watchdog = gps_watchdog_timeout_ticks;
2690 }
2691 } else if (update_path == "navigation.headingMagnetic") {
2692 if (m_pri_heading_m >= 2) {
2693 double hdm = GetJsonDouble(value);
2694 if (std::isnan(hdm)) return;
2695
2696 hdm = GEODESIC_RAD2DEG(hdm);
2697 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, "\u00B0M");
2698 m_pri_heading_m = 2;
2699 m_h_dx_watchdog = gps_watchdog_timeout_ticks;
2700
2701 // If no higher priority HDT, calculate it here.
2702 if (m_pri_heading_t >= 6 && (!std::isnan(m_var))) {
2703 double heading = hdm + m_var;
2704 if (heading < 0)
2705 heading += 360;
2706 else if (heading >= 360.0)
2707 heading -= 360;
2708 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, heading, "\u00B0");
2709 m_pri_heading_t = 6;
2710 m_hdt_watchdog = gps_watchdog_timeout_ticks;
2711 }
2712 }
2713 } else if (update_path == "navigation.speedThroughWater") {
2714 if (m_pri_stw >= 2) {
2715 double stw_knots = GetJsonDouble(value);
2716 if (std::isnan(stw_knots)) return;
2717
2718 stw_knots = MS2KNOTS(stw_knots);
2719 SendSentenceToAllInstruments(
2720 OCPN_DBP_STC_STW, toUsrSpeed_Plugin(stw_knots, g_iDashSpeedUnit),
2721 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2722 m_pri_stw = 2;
2723 m_stw_watchdog = gps_watchdog_timeout_ticks;
2724 }
2725 } else if (update_path == "navigation.magneticVariation") {
2726 if (m_pri_var >= 2) {
2727 double dvar = GetJsonDouble(value);
2728 if (std::isnan(dvar)) return;
2729
2730 dvar = GEODESIC_RAD2DEG(dvar);
2731 if (0.0 != dvar) { // Let WMM do the job instead
2732 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, dvar, "\u00B0");
2733 m_pri_var = 2;
2734 m_var_watchdog = gps_watchdog_timeout_ticks;
2735 }
2736 }
2737 } else if (update_path == "environment.wind.angleApparent") {
2738 if (m_pri_awa >= 2) {
2739 double m_awaangle = GetJsonDouble(value);
2740 if (std::isnan(m_awaangle)) return;
2741
2742 m_awaangle = GEODESIC_RAD2DEG(m_awaangle); // negative to port
2743 skAWA = m_awaangle;
2744 wxString m_awaunit = "\u00B0R";
2745 if (m_awaangle < 0) {
2746 m_awaunit = "\u00B0L";
2747 m_awaangle *= -1;
2748 }
2749 SendSentenceToAllInstruments(
2750 OCPN_DBP_STC_AWA, m_awa_filter.Filter(m_awaangle), m_awaunit);
2751 m_pri_awa =
2752 2; // Set prio only here. No need to catch speed if no angle.
2753 m_mwva_watchdog = gps_watchdog_timeout_ticks;
2754 }
2755 } else if (update_path == "environment.wind.speedApparent") {
2756 if (m_pri_awa >= 2) {
2757 double m_awaspeed_kn = GetJsonDouble(value);
2758 if (std::isnan(m_awaspeed_kn)) return;
2759
2760 m_awaspeed_kn = MS2KNOTS(m_awaspeed_kn);
2761 SendSentenceToAllInstruments(
2763 toUsrSpeed_Plugin(m_aws_filter.Filter(m_awaspeed_kn),
2764 g_iDashWindSpeedUnit),
2765 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2766
2767 // If no TWA from SK try to use AWS/AWA to calculate it
2768 if (m_pri_twa >= 6 && !std::isnan(skAWA)) {
2769 CalculateAndUpdateTWDS(m_awaspeed_kn, skAWA);
2770 m_pri_twa = 6;
2771 m_mwvt_watchdog = gps_watchdog_timeout_ticks;
2772 m_wdn_watchdog = no_nav_watchdog_timeout_ticks;
2773 }
2774 }
2775 } else if ((update_path == "environment.wind.angleTrueWater" &&
2776 !g_bDBtrueWindGround) ||
2777 (update_path == "environment.wind.angleTrueGround" &&
2778 g_bDBtrueWindGround)) {
2779 if (m_pri_twa >= 3) {
2780 double m_twaangle = GetJsonDouble(value);
2781 if (std::isnan(m_twaangle)) return;
2782
2783 m_twaangle = GEODESIC_RAD2DEG(m_twaangle);
2784 double m_twaangle_raw = m_twaangle; // for wind history
2785 wxString m_twaunit = "\u00B0R";
2786 if (m_twaangle < 0) {
2787 m_twaunit = "\u00B0L";
2788 m_twaangle *= -1;
2789 }
2790 SendSentenceToAllInstruments(OCPN_DBP_STC_TWA, m_twaangle, m_twaunit);
2791 m_pri_twa =
2792 3; // Set prio only here. No need to catch speed if no angle.
2793 m_mwvt_watchdog = gps_watchdog_timeout_ticks;
2794
2795 if (m_pri_wdn >= 5) {
2796 // m_twaangle_raw has wind angle relative to the bow.
2797 // Wind history use angle relative to north.
2798 // If no TWD with higher priority is present and
2799 // true heading is available calculate it.
2800 if (g_dHDT < 361. && g_dHDT >= 0.0) {
2801 double g_dCalWdir = (m_twaangle_raw) + g_dHDT;
2802 if (g_dCalWdir > 360.) {
2803 g_dCalWdir -= 360;
2804 } else if (g_dCalWdir < 0.) {
2805 g_dCalWdir += 360;
2806 }
2807 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, g_dCalWdir,
2808 "\u00B0");
2809 m_pri_wdn = 5;
2810 m_wdn_watchdog = no_nav_watchdog_timeout_ticks;
2811 }
2812 }
2813 }
2814 } else if ((update_path == "environment.wind.speedTrue" &&
2815 !g_bDBtrueWindGround) ||
2816 (update_path == "environment.wind.speedOverGround" &&
2817 g_bDBtrueWindGround)) {
2818 if (m_pri_twa >= 3) {
2819 double m_twaspeed_kn = GetJsonDouble(value);
2820 if (std::isnan(m_twaspeed_kn)) return;
2821
2822 m_twaspeed_kn = MS2KNOTS(m_twaspeed_kn);
2823 SendSentenceToAllInstruments(
2825 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2826 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2827 SendSentenceToAllInstruments(
2829 toUsrSpeed_Plugin(m_twaspeed_kn, g_iDashWindSpeedUnit),
2830 getUsrSpeedUnit_Plugin(g_iDashWindSpeedUnit));
2831 }
2832 } else if (update_path == "environment.depth.belowSurface") {
2833 if (m_pri_depth >= 3) {
2834 double depth = GetJsonDouble(value);
2835 if (std::isnan(depth)) return;
2836
2837 m_pri_depth = 3;
2838 depth += g_dDashDBTOffset;
2839 depth /= 1852.0;
2840 SendSentenceToAllInstruments(
2841 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2842 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2843 m_dpt_dbt_watchdog = gps_watchdog_timeout_ticks;
2844 }
2845 } else if (update_path == "environment.depth.belowTransducer") {
2846 if (m_pri_depth >= 3) {
2847 double depth = GetJsonDouble(value);
2848 if (std::isnan(depth)) return;
2849
2850 m_pri_depth = 3;
2851 depth += g_dDashDBTOffset;
2852 depth /= 1852.0;
2853 SendSentenceToAllInstruments(
2854 OCPN_DBP_STC_DPT, toUsrDistance_Plugin(depth, g_iDashDepthUnit),
2855 getUsrDistanceUnit_Plugin(g_iDashDepthUnit));
2856 m_dpt_dbt_watchdog = gps_watchdog_timeout_ticks;
2857 }
2858 } else if (update_path == "environment.water.temperature") {
2859 if (m_pri_wtp >= 2) {
2860 double m_wtemp = GetJsonDouble(value);
2861 if (std::isnan(m_wtemp)) return;
2862
2863 m_wtemp = KELVIN2C(m_wtemp);
2864 if (m_wtemp > -60 && m_wtemp < 200 && !std::isnan(m_wtemp)) {
2865 SendSentenceToAllInstruments(
2866 OCPN_DBP_STC_TMP, toUsrTemp_Plugin(m_wtemp, g_iDashTempUnit),
2867 getUsrTempUnit_Plugin(g_iDashTempUnit));
2868 m_pri_wtp = 2;
2869 m_wtp_watchdog = no_nav_watchdog_timeout_ticks;
2870 }
2871 }
2872 } else if (update_path ==
2873 "navigation.courseRhumbline.nextPoint.velocityMadeGood") {
2874 double m_vmg_kn = GetJsonDouble(value);
2875 if (std::isnan(m_vmg_kn)) return;
2876
2877 m_vmg_kn = MS2KNOTS(m_vmg_kn);
2878 SendSentenceToAllInstruments(
2879 OCPN_DBP_STC_VMG, toUsrSpeed_Plugin(m_vmg_kn, g_iDashSpeedUnit),
2880 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2881 m_vmg_watchdog = gps_watchdog_timeout_ticks;
2882 }
2883
2884 else if (update_path == "performance.velocityMadeGood") {
2885 double m_vmgw_kn = GetJsonDouble(value);
2886 if (std::isnan(m_vmgw_kn)) return;
2887
2888 m_vmgw_kn = MS2KNOTS(m_vmgw_kn);
2889 SendSentenceToAllInstruments(
2890 OCPN_DBP_STC_VMGW, toUsrSpeed_Plugin(m_vmgw_kn, g_iDashSpeedUnit),
2891 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
2892 m_vmgw_watchdog = gps_watchdog_timeout_ticks;
2893 }
2894
2895 else if (update_path == "steering.rudderAngle") { // ->port
2896 if (m_pri_rsa >= 2) {
2897 double m_rudangle = GetJsonDouble(value);
2898 if (std::isnan(m_rudangle)) return;
2899
2900 m_rudangle = GEODESIC_RAD2DEG(m_rudangle);
2901 SendSentenceToAllInstruments(OCPN_DBP_STC_RSA, m_rudangle, "\u00B0");
2902 m_rsa_watchdog = gps_watchdog_timeout_ticks;
2903 m_pri_rsa = 2;
2904 }
2905 } else if (update_path ==
2906 "navigation.gnss.satellites") { // GNSS satellites in use
2907 if (m_pri_sat_used >= 2) {
2908 int usedSats = (value).AsInt();
2909 if (usedSats < 1) return;
2910 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, usedSats, _T (""));
2911 m_pri_sat_used = 2;
2912 m_sats_used_wdog = gps_watchdog_timeout_ticks;
2913 }
2914 } else if (update_path == "navigation.gnss.type") {
2915 if (value.IsString() && value.AsString() != wxEmptyString) {
2916 talkerID = (value.AsString()); // Like "Combined GPS/GLONASS"
2917 talkerID.MakeUpper();
2918 m_pri_n2_k_talker = gps_watchdog_timeout_ticks;
2919 if ((talkerID.Contains("GPS")) && (talkerID.Contains("GLONASS")))
2920 talkerID = "GPSGLONAS";
2921 else if (talkerID.Contains("GPS"))
2922 talkerID = "GP";
2923 else if (talkerID.Contains("GLONASS"))
2924 talkerID = "GL";
2925 else if (talkerID.Contains("GALILEO"))
2926 talkerID = "GA";
2927 else if (talkerID.Contains("BEIDOU"))
2928 talkerID = "GI";
2929 }
2930 } else if (update_path ==
2931 "navigation.gnss.satellitesInView") { // GNSS satellites in
2932 // view
2933 if (m_pri_sat_used >= 4) {
2934 if (value.HasMember("count") && value["count"].IsInt()) {
2935 double m_SK_SatsInView = (value["count"].AsInt());
2936 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_SK_SatsInView,
2937 _T (""));
2938 m_pri_sat_used = 4;
2939 m_sats_used_wdog = gps_watchdog_timeout_ticks;
2940 }
2941 }
2942 if (m_pri_sat_status == 2) {
2943 if (value.HasMember("satellites") && value["satellites"].IsArray()) {
2944 // Update satellites data.
2945 int iNumSats;
2946 if (value.HasMember("count") && value["count"].IsInt()) {
2947 iNumSats = (value["count"].AsInt());
2948 } else
2949 iNumSats = value[_T ("satellites")].Size();
2950
2951 SAT_INFO SK_SatInfo[4];
2952 for (int idx = 0; idx < 4; idx++) {
2953 SK_SatInfo[idx].SatNumber = 0;
2954 SK_SatInfo[idx].ElevationDegrees = 0;
2955 SK_SatInfo[idx].AzimuthDegreesTrue = 0;
2956 SK_SatInfo[idx].SignalToNoiseRatio = 0;
2957 }
2958
2959 if (iNumSats) {
2960 // Arrange SK's array[12] to max three messages like NMEA GSV
2961 int iID = 0;
2962 int iSNR = 0;
2963 double dElevRad = 0;
2964 double dAzimRad = 0;
2965 int idx = 0;
2966 int arr = 0;
2967 for (int iMesNum = 0; iMesNum < 3; iMesNum++) {
2968 for (idx = 0; idx < 4; idx++) {
2969 arr = idx + 4 * iMesNum;
2970 if (value["satellites"][arr]["id"].IsInt())
2971 iID = value["satellites"][arr]["id"].AsInt();
2972 if (value["satellites"][arr]["elevation"].IsDouble())
2973 dElevRad = value["satellites"][arr]["elevation"].AsDouble();
2974 if (value["satellites"][arr]["azimuth"].IsDouble())
2975 dAzimRad = value["satellites"][arr]["azimuth"].AsDouble();
2976 if (value["satellites"][arr]["SNR"].IsInt())
2977 iSNR = value["satellites"][arr]["SNR"].AsInt();
2978
2979 if (iID < 1) break;
2980 SK_SatInfo[idx].SatNumber = iID;
2981 SK_SatInfo[idx].ElevationDegrees = GEODESIC_RAD2DEG(dElevRad);
2982 SK_SatInfo[idx].AzimuthDegreesTrue = GEODESIC_RAD2DEG(dAzimRad);
2983 SK_SatInfo[idx].SignalToNoiseRatio = iSNR;
2984 }
2985 if (idx > 0) {
2986 if (m_pri_n2_k_talker <= 0 && talker != wxEmptyString &&
2987 (talker.StartsWith("G") || talker.StartsWith("BD"))) {
2988 talkerID = talker; // Origin NMEA0183
2989 }
2990 SendSatInfoToAllInstruments(iNumSats, iMesNum + 1, talkerID,
2991 SK_SatInfo);
2992 // mPriSatStatus = 2;
2993 m_sat_status_wdog = gps_watchdog_timeout_ticks;
2994 }
2995
2996 if (iID < 1) break;
2997 }
2998 }
2999 }
3000 }
3001 } else if (update_path == "navigation.gnss.antennaAltitude") {
3002 if (m_pri_alt >= 2) {
3003 double m_alt = GetJsonDouble(value);
3004 if (std::isnan(m_alt)) return;
3005
3006 SendSentenceToAllInstruments(OCPN_DBP_STC_ALTI, m_alt, "m");
3007 m_pri_alt = 2;
3008 m_alt_watchdog = gps_watchdog_timeout_ticks;
3009 }
3010
3011 } else if (update_path == "navigation.datetime") {
3012 if (m_pri_date_time >= 1) {
3013 m_pri_date_time = 1;
3014 wxString s_dt = (value.AsString()); //"2019-12-28T09:26:58.000Z"
3015 s_dt.Replace('-', wxEmptyString);
3016 s_dt.Replace(':', wxEmptyString);
3017 wxString utc_dt = s_dt.BeforeFirst('T'); // Date
3018 utc_dt.Append(s_dt.AfterFirst('T').Left(6)); // time
3019 m_utc_date_time.ParseFormat(utc_dt.c_str(), "%Y%m%d%H%M%S");
3020 m_utc_watchdog = gps_watchdog_timeout_ticks;
3021 }
3022 } else if (update_path == "environment.outside.temperature") {
3023 if (m_pri_atmp >= 2) {
3024 double m_airtemp = GetJsonDouble(value);
3025 if (std::isnan(m_airtemp)) return;
3026
3027 m_airtemp = KELVIN2C(m_airtemp);
3028 if (m_airtemp > -60 && m_airtemp < 100) {
3029 SendSentenceToAllInstruments(
3030 OCPN_DBP_STC_ATMP, toUsrTemp_Plugin(m_airtemp, g_iDashTempUnit),
3031 getUsrTempUnit_Plugin(g_iDashTempUnit));
3032 m_pri_atmp = 2;
3033 m_atmp_watchdog = no_nav_watchdog_timeout_ticks;
3034 }
3035 }
3036 } else if (update_path == "environment.outside.humidity" ||
3037 update_path == "environment.outside.relativeHumidity") {
3038 if (m_pri_hum >= 2) {
3039 double m_hum = GetJsonDouble(value) * 100; // ratio2%
3040 if (std::isnan(m_hum)) return;
3041 SendSentenceToAllInstruments(OCPN_DBP_STC_HUM, m_hum, "%");
3042 m_pri_hum = 2;
3043 m_hum_watchdog = no_nav_watchdog_timeout_ticks;
3044 }
3045 } else if (update_path ==
3046 "environment.wind.directionTrue") { // relative true north
3047 if (m_pri_wdn >= 3) {
3048 double m_twdT = GetJsonDouble(value);
3049 if (std::isnan(m_twdT)) return;
3050
3051 m_twdT = GEODESIC_RAD2DEG(m_twdT);
3052 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdT, "\u00B0");
3053 m_pri_wdn = 3;
3054 m_wdn_watchdog = no_nav_watchdog_timeout_ticks;
3055 }
3056 } else if (update_path == "environment.wind.directionMagnetic") {
3057 // relative magn north
3058 if (m_pri_wdn >= 4) {
3059 double m_twdM = GetJsonDouble(value);
3060 if (std::isnan(m_twdM)) return;
3061 m_twdM = GEODESIC_RAD2DEG(m_twdM);
3062 // Make it true if variation is available
3063 if (!std::isnan(m_var)) {
3064 m_twdM = (m_twdM) + m_var;
3065 if (m_twdM > 360.) {
3066 m_twdM -= 360;
3067 } else if (m_twdM < 0.) {
3068 m_twdM += 360;
3069 }
3070 }
3071 SendSentenceToAllInstruments(OCPN_DBP_STC_TWD, m_twdM, "\u00B0");
3072 m_pri_wdn = 4;
3073 m_wdn_watchdog = no_nav_watchdog_timeout_ticks;
3074 }
3075 } else if (update_path == "navigation.trip.log") { // m
3076 double m_tlog = GetJsonDouble(value);
3077 if (std::isnan(m_tlog)) return;
3078
3079 m_tlog = METERS2NM(m_tlog);
3080 SendSentenceToAllInstruments(
3081 OCPN_DBP_STC_VLW1, toUsrDistance_Plugin(m_tlog, g_iDashDistanceUnit),
3082 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3083 m_tr_log_watchdog = no_nav_watchdog_timeout_ticks;
3084 } else if (update_path == "navigation.log" && !g_bUseInternSumLog) {
3085 double m_slog = GetJsonDouble(value);
3086 if (std::isnan(m_slog)) return;
3087
3088 m_slog = METERS2NM(m_slog);
3089 SendSentenceToAllInstruments(
3090 OCPN_DBP_STC_VLW2, toUsrDistance_Plugin(m_slog, g_iDashDistanceUnit),
3091 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
3092 m_log_watchdog = no_nav_watchdog_timeout_ticks;
3093 } else if (update_path == "environment.outside.pressure" &&
3094 m_pri_mda >= 2) { // Pa
3095 double m_press = GetJsonDouble(value);
3096 if (std::isnan(m_press)) return;
3097
3098 m_press = PA2HPA(m_press);
3099 SendSentenceToAllInstruments(OCPN_DBP_STC_MDA, m_press, "hPa");
3100 m_pri_mda = 2;
3101 m_mda_watchdog = no_nav_watchdog_timeout_ticks;
3102 } else if (update_path == "navigation.attitude") { // rad
3103 if (m_pri_pitch_roll >= 2) {
3104 if (value["roll"].AsString() != "0") {
3105 double m_heel = GEODESIC_RAD2DEG(value["roll"].AsDouble());
3106 wxString h_unit = L"\u00B0\u003E" + _("Stbd");
3107 if (m_heel < 0) {
3108 h_unit = L"\u00B0\u003C" + _("Port");
3109 m_heel *= -1;
3110 }
3111 SendSentenceToAllInstruments(OCPN_DBP_STC_HEEL, m_heel, h_unit);
3112 m_heel_watchdog = gps_watchdog_timeout_ticks;
3113 m_pri_pitch_roll = 2;
3114 }
3115 if (value["pitch"].AsString() != "0") {
3116 double m_pitch = GEODESIC_RAD2DEG(value["pitch"].AsDouble());
3117 wxString p_unit = L"\u00B0\u2191" + _("Up");
3118 if (m_pitch < 0) {
3119 p_unit = L"\u00B0\u2193" + _("Down");
3120 m_pitch *= -1;
3121 }
3122 SendSentenceToAllInstruments(OCPN_DBP_STC_PITCH, m_pitch, p_unit);
3123 m_pitch_watchdog = gps_watchdog_timeout_ticks;
3124 m_pri_pitch_roll = 2;
3125 }
3126 }
3127 }
3128 }
3129}
3130
3131/*******Nav data from OCPN core *******/
3133 if (m_pri_position >= 1) {
3134 m_pri_position = 1;
3135 SendSentenceToAllInstruments(OCPN_DBP_STC_LAT, pfix.Lat, "SDMM");
3136 SendSentenceToAllInstruments(OCPN_DBP_STC_LON, pfix.Lon, "SDMM");
3137 }
3138 if (m_pri_cogsog >= 1) {
3139 double dMagneticCOG;
3140 m_pri_cogsog = 1;
3141 SendSentenceToAllInstruments(
3143 toUsrSpeed_Plugin(m_sog_filter.Filter(pfix.Sog), g_iDashSpeedUnit),
3144 getUsrSpeedUnit_Plugin(g_iDashSpeedUnit));
3145 SendSentenceToAllInstruments(OCPN_DBP_STC_COG,
3146 m_cog_filter.Filter(pfix.Cog), "\u00B0");
3147
3148 // Collect distance to update SumLog every second.
3149 double logSOG = m_sog_filter.Filter(pfix.Sog);
3150 if (logSOG > 0.4) {
3151 d_tripNM += logSOG / 3600;
3152 // Update SumLog instrument every minute when we are under way.
3153 if (++logCount > 60) {
3154 UpdateSumLog(true);
3155 d_tripNM = 0.0;
3156 logCount = 0;
3157 }
3158 }
3159
3160 dMagneticCOG = m_cog_filter.get() - pfix.Var;
3161 if (dMagneticCOG < 0.0) dMagneticCOG = 360.0 + dMagneticCOG;
3162 if (dMagneticCOG > 360.0) dMagneticCOG = dMagneticCOG - 360.0;
3163 SendSentenceToAllInstruments(OCPN_DBP_STC_MCOG, dMagneticCOG, "\u00B0M");
3164 }
3165 if (m_pri_var >= 1) {
3166 if (!std::isnan(pfix.Var)) {
3167 m_pri_var = 1;
3168 m_var = pfix.Var;
3169 m_var_watchdog = gps_watchdog_timeout_ticks;
3170
3171 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, pfix.Var, "\u00B0");
3172 }
3173 }
3174 if (m_pri_date_time >= 6) { // We prefer the GPS datetime
3175 // If GNSS data is available, the value of pfix.FixTime is the GNSS time in
3176 // UTC. If GNSS data is not available, the special value 0 is used to
3177 // indicate that the time is not valid.
3178 if (pfix.FixTime > 0)
3179 m_utc_date_time.Set(pfix.FixTime);
3180 else
3181 m_utc_date_time = wxInvalidDateTime;
3182 if (m_utc_date_time.IsValid()) {
3183 m_pri_date_time = 6;
3184 m_utc_date_time = m_utc_date_time.ToUTC();
3185 m_utc_watchdog = gps_watchdog_timeout_ticks;
3186 }
3187 }
3188 if (m_pri_sat_used >= 1) {
3189 m_sats_in_use = pfix.nSats;
3190 if (m_sats_in_use > 0) {
3191 SendSentenceToAllInstruments(OCPN_DBP_STC_SAT, m_sats_in_use, "");
3192 m_pri_sat_used = 1;
3193 m_sats_used_wdog = gps_watchdog_timeout_ticks;
3194 }
3195 }
3196 if (m_pri_heading_t >= 1) {
3197 double hdt = pfix.Hdt;
3198 if (std::isnan(hdt)) return;
3199 SendSentenceToAllInstruments(OCPN_DBP_STC_HDT, hdt, "\u00B0T");
3200 m_pri_heading_t = 1;
3201 m_hdt_watchdog = gps_watchdog_timeout_ticks;
3202 }
3203 if (m_pri_heading_m >= 1) {
3204 double hdm = pfix.Hdm;
3205 if (std::isnan(hdm) && !std::isnan(pfix.Hdt) && !std::isnan(pfix.Var)) {
3206 hdm = pfix.Hdt - pfix.Var;
3207 if (hdm < 0)
3208 hdm += 360;
3209 else if (hdm >= 360.0)
3210 hdm -= 360;
3211 }
3212 if (std::isnan(hdm)) return;
3213 SendSentenceToAllInstruments(OCPN_DBP_STC_HDM, hdm, "\u00B0M");
3214 m_pri_heading_m = 1;
3215 m_h_dx_watchdog = gps_watchdog_timeout_ticks;
3216 }
3217}
3218
3219void dashboard_pi::SetCursorLatLon(double lat, double lon) {
3220 SendSentenceToAllInstruments(OCPN_DBP_STC_PLA, lat, "SDMM");
3221 SendSentenceToAllInstruments(OCPN_DBP_STC_PLO, lon, "SDMM");
3222}
3223
3224void dashboard_pi::SetPluginMessage(wxString &message_id,
3225 wxString &message_body) {
3226 if (message_id == "WMM_VARIATION_BOAT") {
3227 // construct the JSON root object
3228 wxJSONValue root;
3229 // construct a JSON parser
3230 wxJSONReader reader;
3231
3232 // now read the JSON text and store it in the 'root' structure
3233 // check for errors before retreiving values...
3234 int numErrors = reader.Parse(message_body, &root);
3235 if (numErrors > 0) {
3236 // const wxArrayString& errors = reader.GetErrors();
3237 return;
3238 }
3239
3240 // get the DECL value from the JSON message
3241 wxString decl = root["Decl"].AsString();
3242 double decl_val;
3243 decl.ToDouble(&decl_val);
3244
3245 if (m_pri_var >= 5) {
3246 m_pri_var = 5;
3247 m_var = decl_val;
3248 m_var_watchdog = gps_watchdog_timeout_ticks;
3249 SendSentenceToAllInstruments(OCPN_DBP_STC_HMV, m_var, "\u00B0");
3250 }
3251 } else if (message_id == "OCPN_CORE_SIGNALK") {
3252 ParseSignalK(message_body);
3253 }
3254}
3255
3257
3260 parent, wxID_ANY, m_ArrayOfDashboardWindow);
3261
3262 dialog->RecalculateSize();
3263
3264#ifdef __ANDROID__
3265 dialog->GetHandle()->setStyleSheet(qtStyleSheet);
3266#endif
3267
3268#ifdef __ANDROID__
3269 wxWindow *ccwin = GetOCPNCanvasWindow();
3270
3271 if (ccwin) {
3272 int xmax = ccwin->GetSize().GetWidth();
3273 int ymax = ccwin->GetParent()
3274 ->GetSize()
3275 .GetHeight(); // This would be the Frame itself
3276 dialog->SetSize(xmax, ymax);
3277 dialog->Layout();
3278
3279 dialog->Move(0, 0);
3280 }
3281#endif
3282
3283 if (dialog->ShowModal() == wxID_OK) {
3284 double scaler = 1.0;
3285 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3286 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3287 scaler = wxMax(1.0, scaler);
3288
3289 g_USFontTitle = *(dialog->m_font_picker_title->GetFontData());
3290 g_FontTitle = *g_pUSFontTitle;
3291 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3292 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
3293 g_USFontTitle = *g_pUSFontTitle;
3294
3295 g_USFontData = *(dialog->m_font_picker_data->GetFontData());
3296 g_FontData = *g_pUSFontData;
3297 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3298 g_FontData.SetColour(g_pUSFontData->GetColour());
3299 g_USFontData = *g_pUSFontData;
3300
3301 g_USFontLabel = *(dialog->m_font_picker_label->GetFontData());
3302 g_FontLabel = *g_pUSFontLabel;
3303 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3304 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
3305 g_USFontLabel = *g_pUSFontLabel;
3306
3307 g_USFontSmall = *(dialog->m_font_picker_small->GetFontData());
3308 g_FontSmall = *g_pUSFontSmall;
3309 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3310 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
3311 g_USFontSmall = *g_pUSFontSmall;
3312
3313 // OnClose should handle that for us normally but it doesn't seems to do so
3314 // We must save changes first
3315 g_dashPrefWidth = dialog->GetSize().x;
3316 g_dashPrefHeight = dialog->GetSize().y;
3317
3318 dialog->SaveDashboardConfig();
3319 m_ArrayOfDashboardWindow.Clear();
3320 m_ArrayOfDashboardWindow = dialog->m_config;
3321
3322 ApplyConfig();
3323 SaveConfig();
3324 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3325 }
3326 dialog->Destroy();
3327}
3328
3330 aktuellColorScheme = cs;
3331 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3332 DashboardWindow *dashboard_window =
3333 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3334 if (dashboard_window) dashboard_window->SetColorScheme(cs);
3335 }
3336}
3337
3338int dashboard_pi::GetDashboardWindowShownCount() {
3339 int cnt = 0;
3340
3341 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3342 DashboardWindow *dashboard_window =
3343 m_ArrayOfDashboardWindow.Item(i)->m_pDashboardWindow;
3344 if (dashboard_window) {
3345 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3346 if (pane.IsOk() && pane.IsShown()) cnt++;
3347 }
3348 }
3349 return cnt;
3350}
3351
3352void dashboard_pi::OnPaneClose(wxAuiManagerEvent &event) {
3353 // if name is unique, we should use it
3354 auto *dashboard_window = (DashboardWindow *)event.pane->window;
3355 int cnt = 0;
3356 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3357 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3358 DashboardWindow *d_w = cont->m_pDashboardWindow;
3359 if (d_w) {
3360 // we must not count this one because it is being closed
3361 if (dashboard_window != d_w) {
3362 wxAuiPaneInfo &pane = m_pauimgr->GetPane(d_w);
3363 if (pane.IsOk() && pane.IsShown()) cnt++;
3364 } else {
3365 cont->m_bIsVisible = false;
3366 }
3367 }
3368 }
3369 SetToolbarItemState(m_toolbar_item_id, cnt != 0);
3370
3371 event.Skip();
3372}
3373
3375 int cnt = GetDashboardWindowShownCount();
3376
3377 bool b_anyviz = false;
3378 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3379 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3380 if (cont->m_bIsVisible) {
3381 b_anyviz = true;
3382 break;
3383 }
3384 }
3385
3386 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3387 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3388 DashboardWindow *dashboard_window = cont->m_pDashboardWindow;
3389 if (dashboard_window) {
3390 wxAuiPaneInfo &pane = m_pauimgr->GetPane(dashboard_window);
3391 if (pane.IsOk()) {
3392 bool b_reset_pos = false;
3393
3394#ifdef __WXMSW__
3395 // Support MultiMonitor setups which an allow negative window
3396 // positions. If the requested window title bar does not intersect any
3397 // installed monitor, then default to simple primary monitor
3398 // positioning.
3399 RECT frame_title_rect;
3400 frame_title_rect.left = pane.floating_pos.x;
3401 frame_title_rect.top = pane.floating_pos.y;
3402 frame_title_rect.right = pane.floating_pos.x + pane.floating_size.x;
3403 frame_title_rect.bottom = pane.floating_pos.y + 30;
3404
3405 if (NULL == MonitorFromRect(&frame_title_rect, MONITOR_DEFAULTTONULL))
3406 b_reset_pos = true;
3407#else
3408
3409 // Make sure drag bar (title bar) of window intersects wxClient Area
3410 // of screen, with a little slop...
3411 wxRect window_title_rect; // conservative estimate
3412 window_title_rect.x = pane.floating_pos.x;
3413 window_title_rect.y = pane.floating_pos.y;
3414 window_title_rect.width = pane.floating_size.x;
3415 window_title_rect.height = 30;
3416
3417 wxRect ClientRect = wxGetClientDisplayRect();
3418 ClientRect.Deflate(
3419 60, 60); // Prevent the new window from being too close to the edge
3420 if (!ClientRect.Intersects(window_title_rect)) b_reset_pos = true;
3421
3422#endif
3423
3424 if (b_reset_pos) pane.FloatingPosition(50, 50);
3425
3426 if (cnt == 0)
3427 if (b_anyviz)
3428 pane.Show(cont->m_bIsVisible);
3429 else {
3430 cont->m_bIsVisible = cont->m_bPersVisible;
3431 pane.Show(cont->m_bIsVisible);
3432 }
3433 else
3434 pane.Show(false);
3435 }
3436
3437 // Restore size of docked pane
3438 if (pane.IsShown() && pane.IsDocked()) {
3439 pane.BestSize(cont->m_best_size);
3440 m_pauimgr->Update();
3441 }
3442
3443 // This patch fixes a bug in wxAUIManager
3444 // FS#548
3445 // Dropping a DashBoard Window right on top on the (supposedly fixed)
3446 // chart bar window causes a resize of the chart bar, and the Dashboard
3447 // window assumes some of its properties The Dashboard window is no longer
3448 // grabbable... Workaround: detect this case, and force the pane to be on
3449 // a different Row. so that the display is corrected by toggling the
3450 // dashboard off and back on.
3451 if ((pane.dock_direction == wxAUI_DOCK_BOTTOM) && pane.IsDocked())
3452 pane.Row(2);
3453 }
3454 }
3455 // Toggle is handled by the toolbar but we must keep plugin manager b_toggle
3456 // updated to actual status to ensure right status upon toolbar rebuild
3457 SetToolbarItemState(m_toolbar_item_id,
3458 GetDashboardWindowShownCount() != 0 /*cnt==0*/);
3459 m_pauimgr->Update();
3460}
3461
3463 // This method is called after the PlugIn is initialized
3464 // and the frame has done its initial layout, possibly from a saved
3465 // wxAuiManager "Perspective" It is a chance for the PlugIn to syncronize
3466 // itself internally with the state of any Panes that were added to the
3467 // frame in the PlugIn ctor.
3468
3469 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3470 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3471 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
3472 // Initialize visible state as perspective is loaded now
3473 cont->m_bIsVisible = (pane.IsOk() && pane.IsShown());
3474
3475 // Correct for incomplete AUIManager perspective when docked dashboard is
3476 // not visible at app close.
3477 if (pane.IsDocked()) {
3478 if ((cont->m_persist_size.x > 50) && (cont->m_persist_size.y > 50))
3479 cont->m_pDashboardWindow->SetSize(cont->m_persist_size);
3480 }
3481
3482#ifdef __WXQT__
3483 if (pane.IsShown()) {
3484 pane.Show(false);
3485 m_pauimgr->Update();
3486 pane.Show(true);
3487 m_pauimgr->Update();
3488 }
3489#endif
3490 }
3491 m_pauimgr->Update();
3492
3493 // We use this callback here to keep the context menu selection in sync
3494 // with the window state
3495
3496 SetToolbarItemState(m_toolbar_item_id, GetDashboardWindowShownCount() != 0);
3497}
3498
3499bool dashboard_pi::LoadConfig() {
3500 auto *pConf = (wxFileConfig *)m_pconfig;
3501
3502 if (pConf) {
3503 pConf->SetPath("/PlugIns/Dashboard");
3504
3505 wxString version;
3506 pConf->Read("Version", &version, wxEmptyString);
3507 wxString config;
3508
3509 // Set some sensible defaults
3510 wxString TitleFont;
3511 wxString DataFont;
3512 wxString LabelFont;
3513 wxString SmallFont;
3514
3515#ifdef __ANDROID__
3516 TitleFont = "Roboto,16,-1,5,50,0,0,0,0,0";
3517 DataFont = "Roboto,16,-1,5,50,0,0,0,0,0";
3518 LabelFont = "Roboto,16,-1,5,50,0,0,0,0,0";
3519 SmallFont = "Roboto,14,-1,5,50,0,0,0,0,0";
3520#else
3521 TitleFont = g_pFontTitle->GetChosenFont().GetNativeFontInfoDesc();
3522 DataFont = g_pFontData->GetChosenFont().GetNativeFontInfoDesc();
3523 LabelFont = g_pFontLabel->GetChosenFont().GetNativeFontInfoDesc();
3524 SmallFont = g_pFontSmall->GetChosenFont().GetNativeFontInfoDesc();
3525#endif
3526
3527 double scaler = 1.0;
3528 wxFont DummyFont;
3529 wxFont *pDF = &DummyFont;
3530
3531 if (OCPN_GetWinDIPScaleFactor() < 1.0)
3532 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
3533 scaler = wxMax(1.0, scaler);
3534
3535 g_pFontTitle = &g_FontTitle;
3536 pConf->Read("FontTitle", &config, TitleFont);
3537 LoadFont(&pDF, config);
3538 wxFont DummyFontTitle = *pDF;
3539 pConf->Read("ColorTitle", &config, "#000000");
3540 wxColour DummyColor(config);
3541 g_pUSFontTitle->SetChosenFont(DummyFontTitle);
3542 g_pUSFontTitle->SetColour(DummyColor);
3543
3544 g_FontTitle = *g_pUSFontTitle;
3545 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
3546 g_USFontTitle = *g_pUSFontTitle;
3547
3548 g_pFontData = &g_FontData;
3549 pConf->Read("FontData", &config, DataFont);
3550 LoadFont(&pDF, config);
3551 wxFont DummyFontData = *pDF;
3552 pConf->Read("ColorData", &config, "#000000");
3553 DummyColor.Set(config);
3554 g_pUSFontData->SetChosenFont(DummyFontData);
3555 g_pUSFontData->SetColour(DummyColor);
3556 g_FontData = *g_pUSFontData;
3557 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
3558 g_USFontData = *g_pUSFontData;
3559
3560 pConf->Read("ForceBackgroundColor", &g_ForceBackgroundColor, false);
3561 pConf->Read("BackgroundColor", &config, "DASHL");
3562 g_BackgroundColor.Set(config);
3563
3564 int alignment;
3565 pConf->Read("TitleAlignment", &alignment, (int)wxALIGN_LEFT);
3566 g_TitleAlignment = (wxAlignment)alignment;
3567 if (g_TitleAlignment == wxALIGN_INVALID) g_TitleAlignment = wxALIGN_LEFT;
3568 pConf->Read("TitleMargin", &g_iTitleMargin, 5);
3569 pConf->Read("DataShowUnit", &g_bShowUnit, true);
3570 pConf->Read("DataAlignment", &alignment, (int)wxALIGN_LEFT);
3571 g_DataAlignment = (wxAlignment)alignment;
3572 if (g_DataAlignment == wxALIGN_INVALID) g_DataAlignment = wxALIGN_LEFT;
3573 pConf->Read("DataMargin", &g_iDataMargin, 10);
3574 pConf->Read("InstrumentSpacing", &g_iInstrumentSpacing, 0);
3575 pConf->Read("TitleVerticalOffset", &g_TitleVerticalOffset, 0.0);
3576
3577 g_pFontLabel = &g_FontLabel;
3578 pConf->Read("FontLabel", &config, LabelFont);
3579 LoadFont(&pDF, config);
3580 wxFont DummyFontLabel = *pDF;
3581 pConf->Read("ColorLabel", &config, "#000000");
3582 DummyColor.Set(config);
3583 g_pUSFontLabel->SetChosenFont(DummyFontLabel);
3584 g_pUSFontLabel->SetColour(DummyColor);
3585 g_FontLabel = *g_pUSFontLabel;
3586 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
3587 g_USFontLabel = *g_pUSFontLabel;
3588
3589 g_pFontSmall = &g_FontSmall;
3590 pConf->Read("FontSmall", &config, SmallFont);
3591 LoadFont(&pDF, config);
3592 wxFont DummyFontSmall = *pDF;
3593 pConf->Read("ColorSmall", &config, "#000000");
3594 DummyColor.Set(config);
3595 g_pUSFontSmall->SetChosenFont(DummyFontSmall);
3596 g_pUSFontSmall->SetColour(DummyColor);
3597 g_FontSmall = *g_pUSFontSmall;
3598 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
3599 g_USFontSmall = *g_pUSFontSmall;
3600
3601 pConf->Read("SpeedometerMax", &g_iDashSpeedMax, 12);
3602 pConf->Read("COGDamp", &g_iDashCOGDamp, 0);
3603 pConf->Read("SpeedUnit", &g_iDashSpeedUnit, 0);
3604 pConf->Read("SOGDamp", &g_iDashSOGDamp, 0);
3605 pConf->Read("DepthUnit", &g_iDashDepthUnit, 3);
3606 g_iDashDepthUnit = wxMax(g_iDashDepthUnit, 3);
3607 pConf->Read("AWADamp", &g_iDashAWADamp, 0);
3608 pConf->Read("AWSDamp", &g_iDashAWSDamp, 0);
3609
3610 pConf->Read("DepthOffset", &g_dDashDBTOffset, 0);
3611
3612 pConf->Read("DistanceUnit", &g_iDashDistanceUnit, 0);
3613 pConf->Read("WindSpeedUnit", &g_iDashWindSpeedUnit, 0);
3614 pConf->Read("UseSignKtruewind", &g_bDBtrueWindGround, false);
3615 pConf->Read("UseInternSumlog", &g_bUseInternSumLog, false);
3616 pConf->Read("SumLogNM", &g_dSumLogNM, 0.0);
3617 pConf->Read("TemperatureUnit", &g_iDashTempUnit, 0);
3618
3619 pConf->Read("UTCOffset", &g_iUTCOffset, 0);
3620
3621 pConf->Read("PrefWidth", &g_dashPrefWidth, 0);
3622 pConf->Read("PrefHeight", &g_dashPrefHeight, 0);
3623
3624 int d_cnt;
3625 pConf->Read("DashboardCount", &d_cnt, -1);
3626 // TODO: Memory leak? We should destroy everything first
3627 m_ArrayOfDashboardWindow.Clear();
3628 if (version.IsEmpty() && d_cnt == -1) {
3629 m_config_version = 1;
3630 // Let's load version 1 or default settings.
3631 int i_cnt;
3632 pConf->Read("InstrumentCount", &i_cnt, -1);
3633 wxArrayInt ar;
3634 wxArrayOfInstrumentProperties Property;
3635 if (i_cnt != -1) {
3636 for (int i = 0; i < i_cnt; i++) {
3637 int id;
3638 pConf->Read(wxString::Format("Instrument%d", i + 1), &id, -1);
3639 if (id != -1) ar.Add(id);
3640 }
3641 } else {
3642 // This is the default instrument list
3643#ifndef __ANDROID__
3644 ar.Add(ID_DBP_I_POS);
3645 ar.Add(ID_DBP_D_COG);
3646 ar.Add(ID_DBP_D_GPS);
3647#else
3648 ar.Add(ID_DBP_I_POS);
3649 ar.Add(ID_DBP_D_COG);
3650 ar.Add(ID_DBP_I_SOG);
3651
3652#endif
3653 }
3654
3655 auto *cont = new DashboardWindowContainer(
3656 nullptr, MakeName(), _("Dashboard"), "V", ar, Property);
3657 cont->m_bPersVisible = true;
3658 m_ArrayOfDashboardWindow.Add(cont);
3659
3660 } else {
3661 // Version 2
3662 m_config_version = 2;
3663 bool b_onePersisted = false;
3664 wxSize best_size;
3665 wxSize persist_size;
3666 for (int k = 0; k < d_cnt; k++) {
3667 pConf->SetPath(
3668 wxString::Format("/PlugIns/Dashboard/Dashboard%d", k + 1));
3669 wxString name;
3670 pConf->Read("Name", &name, MakeName());
3671 wxString caption;
3672 pConf->Read("Caption", &caption, _("Dashboard"));
3673 wxString orient;
3674 pConf->Read("Orientation", &orient, "V");
3675 int i_cnt;
3676 pConf->Read("InstrumentCount", &i_cnt, -1);
3677 bool b_persist;
3678 pConf->Read("Persistence", &b_persist, true);
3679 int val;
3680 pConf->Read("BestSizeX", &val, DefaultWidth);
3681 best_size.x = val;
3682 pConf->Read("BestSizeY", &val, DefaultWidth);
3683 best_size.y = val;
3684 pConf->Read("PersistSizeX", &val, DefaultWidth);
3685 persist_size.x = val;
3686 pConf->Read("PersistSizeY", &val, DefaultWidth);
3687 persist_size.y = val;
3688
3689 wxArrayInt ar;
3690 wxArrayOfInstrumentProperties Property;
3691 for (int i = 0; i < i_cnt; i++) {
3692 int id;
3693 pConf->Read(wxString::Format("Instrument%d", i + 1), &id, -1);
3694 if (id != -1) {
3695 ar.Add(id);
3696 InstrumentProperties *instp;
3697 if (pConf->Exists(wxString::Format("InstTitleFont%d", i + 1))) {
3698 instp = new InstrumentProperties(id, i);
3699
3700 pConf->Read(wxString::Format("InstTitleFont%d", i + 1), &config,
3701 TitleFont);
3702 LoadFont(&pDF, config);
3703 wxFont DummyFontTitleA = *pDF;
3704 pConf->Read(wxString::Format("InstTitleColor%d", i + 1), &config,
3705 "#000000");
3706 DummyColor.Set(config);
3707 instp->m_USTitleFont.SetChosenFont(DummyFontTitleA);
3708 instp->m_USTitleFont.SetColour(DummyColor);
3709 instp->m_TitleFont = instp->m_USTitleFont;
3710 instp->m_TitleFont.SetChosenFont(
3711 instp->m_USTitleFont.GetChosenFont().Scaled(scaler));
3712
3713 pConf->Read(wxString::Format("InstDataShowUnit%d", i + 1),
3714 &instp->m_ShowUnit, -1);
3715 pConf->Read(wxString::Format("InstDataMargin%d", i + 1),
3716 &instp->m_DataMargin, -1);
3717 pConf->Read(wxString::Format("InstDataAlignment%d", i + 1),
3718 &alignment, (int)wxALIGN_INVALID);
3719 instp->m_DataAlignment = (wxAlignment)alignment;
3720 pConf->Read(wxString::Format("InstInstrumentSpacing%d", i + 1),
3721 &instp->m_InstrumentSpacing, -1);
3722 pConf->Read(wxString::Format("InstDataFormat%d", i + 1),
3723 &instp->m_Format, "");
3724 pConf->Read(wxString::Format("InstTitle%d", i + 1),
3725 &instp->m_Title, "");
3726
3727 pConf->Read(wxString::Format("InstDataFont%d", i + 1), &config,
3728 DataFont);
3729 LoadFont(&pDF, config);
3730 wxFont DummyFontDataA = *pDF;
3731 pConf->Read(wxString::Format("InstDataColor%d", i + 1), &config,
3732 "#000000");
3733 DummyColor.Set(config);
3734 instp->m_USDataFont.SetChosenFont(DummyFontDataA);
3735 instp->m_USDataFont.SetColour(DummyColor);
3736 instp->m_DataFont = instp->m_USDataFont;
3737 instp->m_DataFont.SetChosenFont(
3738 instp->m_USDataFont.GetChosenFont().Scaled(scaler));
3739
3740 pConf->Read(wxString::Format("InstLabelFont%d", i + 1), &config,
3741 LabelFont);
3742 LoadFont(&pDF, config);
3743 wxFont DummyFontLabelA = *pDF;
3744 pConf->Read(wxString::Format("InstLabelColor%d", i + 1), &config,
3745 "#000000");
3746 DummyColor.Set(config);
3747 instp->m_USLabelFont.SetChosenFont(DummyFontLabelA);
3748 instp->m_USLabelFont.SetColour(DummyColor);
3749 instp->m_LabelFont = instp->m_USLabelFont;
3750 instp->m_LabelFont.SetChosenFont(
3751 instp->m_USLabelFont.GetChosenFont().Scaled(scaler));
3752
3753 pConf->Read(wxString::Format("InstSmallFont%d", i + 1), &config,
3754 SmallFont);
3755 LoadFont(&pDF, config);
3756 wxFont DummyFontSmallA = *pDF;
3757 pConf->Read(wxString::Format("InstSmallColor%d", i + 1), &config,
3758 "#000000");
3759 DummyColor.Set(config);
3760 instp->m_USSmallFont.SetChosenFont(DummyFontSmallA);
3761 instp->m_USSmallFont.SetColour(DummyColor);
3762 instp->m_SmallFont = instp->m_USSmallFont;
3763 instp->m_SmallFont.SetChosenFont(
3764 instp->m_USSmallFont.GetChosenFont().Scaled(scaler));
3765
3766 pConf->Read(wxString::Format("TitleBackColor%d", i + 1), &config,
3767 "DASHL");
3768 instp->m_TitleBackgroundColour.Set(config);
3769
3770 pConf->Read(wxString::Format("DataBackColor%d", i + 1), &config,
3771 "DASHB");
3772 instp->m_DataBackgroundColour.Set(config);
3773
3774 pConf->Read(wxString::Format("ArrowFirst%d", i + 1), &config,
3775 "DASHN");
3776 instp->m_Arrow_First_Colour.Set(config);
3777
3778 pConf->Read(wxString::Format("ArrowSecond%d", i + 1), &config,
3779 "BLUE3");
3780 instp->m_Arrow_Second_Colour.Set(config);
3781
3782 Property.Add(instp);
3783 }
3784 }
3785 }
3786 // TODO: Do not add if GetCount == 0
3787
3788 auto *cont = new DashboardWindowContainer(nullptr, name, caption,
3789 orient, ar, Property);
3790 cont->m_bPersVisible = b_persist;
3791 cont->m_conf_best_size = best_size;
3792 cont->m_persist_size = persist_size;
3793
3794 if (b_persist) b_onePersisted = true;
3795
3796 m_ArrayOfDashboardWindow.Add(cont);
3797 }
3798
3799 // Make sure at least one dashboard is scheduled to be visible
3800 if (m_ArrayOfDashboardWindow.Count() && !b_onePersisted) {
3801 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(0);
3802 if (cont) cont->m_bPersVisible = true;
3803 }
3804 }
3805
3806 return true;
3807 } else
3808 return false;
3809}
3810
3811void dashboard_pi::LoadFont(wxFont **target, const wxString &native_info) {
3812 if (!native_info.IsEmpty()) {
3813#ifdef __ANDROID__
3814 wxFont *nf = new wxFont(native_info);
3815 *target = nf;
3816#else
3817 (*target)->SetNativeFontInfo(native_info);
3818#endif
3819 }
3820}
3821
3822bool dashboard_pi::SaveConfig() {
3823 auto *pConf = (wxFileConfig *)m_pconfig;
3824
3825 if (pConf) {
3826 pConf->SetPath("/PlugIns/Dashboard");
3827 pConf->Write("Version", "2");
3828 pConf->Write("FontTitle",
3829 g_pUSFontTitle->GetChosenFont().GetNativeFontInfoDesc());
3830 pConf->Write("ColorTitle",
3831 g_pUSFontTitle->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3832 pConf->Write("FontData",
3833 g_pUSFontData->GetChosenFont().GetNativeFontInfoDesc());
3834 pConf->Write("ColorData",
3835 g_pUSFontData->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3836 pConf->Write("FontLabel",
3837 g_pUSFontLabel->GetChosenFont().GetNativeFontInfoDesc());
3838 pConf->Write("ColorLabel",
3839 g_pUSFontLabel->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3840 pConf->Write("FontSmall",
3841 g_pUSFontSmall->GetChosenFont().GetNativeFontInfoDesc());
3842 pConf->Write("ColorSmall",
3843 g_pUSFontSmall->GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3844 pConf->Write("SpeedometerMax", g_iDashSpeedMax);
3845 pConf->Write("COGDamp", g_iDashCOGDamp);
3846 pConf->Write("SpeedUnit", g_iDashSpeedUnit);
3847 pConf->Write("SOGDamp", g_iDashSOGDamp);
3848 pConf->Write("AWSDamp", g_iDashAWSDamp);
3849 pConf->Write("AWADamp", g_iDashAWADamp);
3850 pConf->Write("DepthUnit", g_iDashDepthUnit);
3851 pConf->Write("DepthOffset", g_dDashDBTOffset);
3852 pConf->Write("DistanceUnit", g_iDashDistanceUnit);
3853 pConf->Write("WindSpeedUnit", g_iDashWindSpeedUnit);
3854 pConf->Write("UseInternSumlog", g_bUseInternSumLog);
3855 pConf->Write("SumLogNM", g_dSumLogNM);
3856 pConf->Write("UTCOffset", g_iUTCOffset);
3857 pConf->Write("UseSignKtruewind", g_bDBtrueWindGround);
3858 pConf->Write("TemperatureUnit", g_iDashTempUnit);
3859 pConf->Write("PrefWidth", g_dashPrefWidth);
3860 pConf->Write("PrefHeight", g_dashPrefHeight);
3861
3862 pConf->Write("DashboardCount", (int)m_ArrayOfDashboardWindow.GetCount());
3863 // Delete old Dashborads
3864 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i < 20; i++) {
3865 if (pConf->Exists(
3866 wxString::Format("/PlugIns/Dashboard/Dashboard%zu", i + 1))) {
3867 pConf->DeleteGroup(
3868 wxString::Format("/PlugIns/Dashboard/Dashboard%zu", i + 1));
3869 }
3870 }
3871 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
3872 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
3873 pConf->SetPath(
3874 wxString::Format("/PlugIns/Dashboard/Dashboard%zu", i + 1));
3875 pConf->Write("Name", cont->m_sName);
3876 pConf->Write("Caption", cont->m_sCaption);
3877 pConf->Write("Orientation", cont->m_sOrientation);
3878 pConf->Write("Persistence", cont->m_bPersVisible);
3879 pConf->Write("InstrumentCount", (int)cont->m_aInstrumentList.GetCount());
3880 pConf->Write("BestSizeX", cont->m_best_size.x);
3881 pConf->Write("BestSizeY", cont->m_best_size.y);
3882 pConf->Write("PersistSizeX", cont->m_pDashboardWindow->GetSize().x);
3883 pConf->Write("PersistSizeY", cont->m_pDashboardWindow->GetSize().y);
3884
3885 // Delete old Instruments
3886 for (size_t ii = cont->m_aInstrumentList.GetCount(); ii < 40; ii++) {
3887 if (pConf->Exists(wxString::Format("Instrument%zu", ii + 1))) {
3888 pConf->DeleteEntry(wxString::Format("Instrument%zu", ii + 1));
3889 if (pConf->Exists(wxString::Format("InstTitleFont%zu", ii + 1))) {
3890 pConf->DeleteEntry(wxString::Format("InstTitleFont%zu", ii + 1));
3891 pConf->DeleteEntry(wxString::Format("InstTitleColor%zu", ii + 1));
3892 pConf->DeleteEntry(wxString::Format("InstTitle%zu", ii + 1));
3893 pConf->DeleteEntry(wxString::Format("InstDataShowUnit%zu", ii + 1));
3894 pConf->DeleteEntry(wxString::Format("InstDataMargin%zu", ii + 1));
3895 pConf->DeleteEntry(
3896 wxString::Format("InstDataAlignment%zu", ii + 1));
3897 pConf->DeleteEntry(wxString::Format("InstDataFormat%zu", ii + 1));
3898 pConf->DeleteEntry(wxString::Format("InstDataFont%zu", ii + 1));
3899 pConf->DeleteEntry(wxString::Format("InstDataColor%zu", ii + 1));
3900 pConf->DeleteEntry(wxString::Format("InstLabelFont%zu", ii + 1));
3901 pConf->DeleteEntry(wxString::Format("InstLabelColor%zu", ii + 1));
3902 pConf->DeleteEntry(wxString::Format("InstSmallFont%zu", ii + 1));
3903 pConf->DeleteEntry(wxString::Format("InstSmallColor%zu", ii + 1));
3904 pConf->DeleteEntry(wxString::Format("TitleBackColor%zu", ii + 1));
3905 pConf->DeleteEntry(wxString::Format("DataBackColor%zu", ii + 1));
3906 pConf->DeleteEntry(wxString::Format("ArrowFirst%zu", ii + 1));
3907 pConf->DeleteEntry(wxString::Format("ArrowSecond%zu", ii + 1));
3908 }
3909 }
3910 }
3911 for (size_t j = 0; j < cont->m_aInstrumentList.GetCount(); j++) {
3912 pConf->Write(wxString::Format("Instrument%zu", j + 1),
3913 cont->m_aInstrumentList.Item(j));
3914 InstrumentProperties *Inst = nullptr;
3915 // First delete
3916 if (pConf->Exists(wxString::Format("InstTitleFont%zu", j + 1))) {
3917 bool Delete = true;
3918 for (size_t ii = 0; ii < cont->m_aInstrumentPropertyList.GetCount();
3919 ii++) {
3920 Inst = cont->m_aInstrumentPropertyList.Item(ii);
3921 if (Inst->m_Listplace == (int)j) {
3922 Delete = false;
3923 break;
3924 }
3925 }
3926 if (Delete) {
3927 pConf->DeleteEntry(wxString::Format("InstTitleFont%zu", j + 1));
3928 pConf->DeleteEntry(wxString::Format("InstTitleColor%zu", j + 1));
3929 pConf->DeleteEntry(wxString::Format("InstTitle%zu", j + 1));
3930 pConf->DeleteEntry(wxString::Format("InstDataShowUnit%zu", i + 1));
3931 pConf->DeleteEntry(wxString::Format("InstDataMargin%zu", i + 1));
3932 pConf->DeleteEntry(wxString::Format("InstDataAlignment%zu", i + 1));
3933 pConf->DeleteEntry(wxString::Format("InstDataFormat%zu", i + 1));
3934 pConf->DeleteEntry(wxString::Format("InstDataFont%zu", j + 1));
3935 pConf->DeleteEntry(wxString::Format("InstDataColor%zu", j + 1));
3936 pConf->DeleteEntry(wxString::Format("InstLabelFont%zu", j + 1));
3937 pConf->DeleteEntry(wxString::Format("InstLabelColor%zu", j + 1));
3938 pConf->DeleteEntry(wxString::Format("InstSmallFont%zu", j + 1));
3939 pConf->DeleteEntry(wxString::Format("InstSmallColor%zu", j + 1));
3940 pConf->DeleteEntry(wxString::Format("TitleBackColor%zu", i + 1));
3941 pConf->DeleteEntry(wxString::Format("DataBackColor%zu", i + 1));
3942 pConf->DeleteEntry(wxString::Format("ArrowFirst%zu", i + 1));
3943 pConf->DeleteEntry(wxString::Format("ArrowSecond%zu", i + 1));
3944 }
3945 }
3946 Inst = nullptr;
3947 for (size_t ii = 0; ii < (cont->m_aInstrumentPropertyList.GetCount());
3948 ii++) {
3949 Inst = cont->m_aInstrumentPropertyList.Item(ii);
3950 if (Inst->m_Listplace == (int)j) {
3951 pConf->Write(
3952 wxString::Format("InstTitleFont%zu", j + 1),
3953 Inst->m_USTitleFont.GetChosenFont().GetNativeFontInfoDesc());
3954 pConf->Write(
3955 wxString::Format("InstTitleColor%zu", j + 1),
3956 Inst->m_USTitleFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3957 pConf->Write(
3958 wxString::Format("InstDataFont%zu", j + 1),
3959 Inst->m_USDataFont.GetChosenFont().GetNativeFontInfoDesc());
3960 pConf->Write(
3961 wxString::Format("InstDataColor%zu", j + 1),
3962 Inst->m_USDataFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3963 pConf->Write(
3964 wxString::Format("InstLabelFont%zu", j + 1),
3965 Inst->m_USLabelFont.GetChosenFont().GetNativeFontInfoDesc());
3966 pConf->Write(
3967 wxString::Format("InstLabelColor%zu", j + 1),
3968 Inst->m_USLabelFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3969 pConf->Write(
3970 wxString::Format("InstSmallFont%zu", j + 1),
3971 Inst->m_USSmallFont.GetChosenFont().GetNativeFontInfoDesc());
3972 pConf->Write(
3973 wxString::Format("InstSmallColor%zu", j + 1),
3974 Inst->m_USSmallFont.GetColour().GetAsString(wxC2S_HTML_SYNTAX));
3975 pConf->Write(
3976 wxString::Format("TitleBackColor%zu", j + 1),
3977 Inst->m_TitleBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
3978 pConf->Write(
3979 wxString::Format("DataBackColor%zu", j + 1),
3980 Inst->m_DataBackgroundColour.GetAsString(wxC2S_HTML_SYNTAX));
3981 pConf->Write(
3982 wxString::Format("ArrowFirst%zu", j + 1),
3983 Inst->m_Arrow_First_Colour.GetAsString(wxC2S_HTML_SYNTAX));
3984 pConf->Write(
3985 wxString::Format("ArrowSecond%zu", j + 1),
3986 Inst->m_Arrow_Second_Colour.GetAsString(wxC2S_HTML_SYNTAX));
3987 break;
3988 }
3989 }
3990 }
3991 }
3992 pConf->Flush();
3993 return true;
3994 } else
3995 return false;
3996}
3997
3998void dashboard_pi::ApplyConfig() {
3999 // Reverse order to handle deletes
4000 for (size_t i = m_ArrayOfDashboardWindow.GetCount(); i > 0; i--) {
4001 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i - 1);
4002 int orient = (cont->m_sOrientation == "V" ? wxVERTICAL : wxHORIZONTAL);
4003 if (cont->m_bIsDeleted) {
4004 if (cont->m_pDashboardWindow) {
4005 m_pauimgr->DetachPane(cont->m_pDashboardWindow);
4006 cont->m_pDashboardWindow->Close();
4007 cont->m_pDashboardWindow->Destroy();
4008 cont->m_pDashboardWindow = nullptr;
4009 }
4010 m_ArrayOfDashboardWindow.Remove(cont);
4011 delete cont;
4012
4013 } else if (!cont->m_pDashboardWindow) {
4014 // A new dashboard is created
4015 cont->m_pDashboardWindow = new DashboardWindow(
4016 GetOCPNCanvasWindow(), wxID_ANY, m_pauimgr, this, orient, cont);
4017 cont->m_pDashboardWindow->SetInstrumentList(
4018 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4019 bool vertical = orient == wxVERTICAL;
4020 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4021 wxSize best = cont->m_conf_best_size;
4022 if (best.x < 100) best = sz;
4023
4024// Mac has a little trouble with initial Layout() sizing...
4025#ifdef __WXOSX__
4026 if (sz.x == 0) sz.IncTo(wxSize(160, 388));
4027#endif
4028 wxAuiPaneInfo p = wxAuiPaneInfo()
4029 .Name(cont->m_sName)
4030 .Caption(cont->m_sCaption)
4031 .CaptionVisible(false)
4032 .TopDockable(!vertical)
4033 .BottomDockable(!vertical)
4034 .LeftDockable(vertical)
4035 .RightDockable(vertical)
4036 .MinSize(sz)
4037 .BestSize(best)
4038 .FloatingSize(sz)
4039 .FloatingPosition(100, 100)
4040 .Float()
4041 .Show(cont->m_bIsVisible)
4042 .Gripper(false);
4043
4044 m_pauimgr->AddPane(cont->m_pDashboardWindow, p);
4045 // wxAuiPaneInfo().Name( cont->m_sName ).Caption( cont->m_sCaption
4046 // ).CaptionVisible( false ).TopDockable(
4047 // !vertical ).BottomDockable( !vertical ).LeftDockable( vertical
4048 // ).RightDockable( vertical ).MinSize( sz ).BestSize( sz ).FloatingSize(
4049 // sz ).FloatingPosition( 100, 100 ).Float().Show( cont->m_bIsVisible ) );
4050
4051#ifdef __ANDROID__
4052 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4053 pane.Dockable(false);
4054
4055#endif
4056
4057 } else {
4058 wxAuiPaneInfo &pane = m_pauimgr->GetPane(cont->m_pDashboardWindow);
4059 pane.Caption(cont->m_sCaption).Show(cont->m_bIsVisible);
4060 if (!cont->m_pDashboardWindow->isInstrumentListEqual(
4061 cont->m_aInstrumentList)) {
4062 cont->m_pDashboardWindow->SetInstrumentList(
4063 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
4064 wxSize sz = cont->m_pDashboardWindow->GetMinSize();
4065 pane.MinSize(sz).BestSize(sz).FloatingSize(sz);
4066 }
4067 if (cont->m_pDashboardWindow->GetSizerOrientation() != orient) {
4068 cont->m_pDashboardWindow->ChangePaneOrientation(orient, false);
4069 }
4070 }
4071 }
4072 m_pauimgr->Update();
4073
4074 // Initialize the IIR Filter Co-efficients
4075 double sogFC = g_iDashSOGDamp ? 1.0 / (2.0 * g_iDashSOGDamp) : 0.0;
4076 double cogFC = g_iDashCOGDamp ? 1.0 / (2.0 * g_iDashCOGDamp) : 0.0;
4077 double awaFC = g_iDashAWADamp ? 1.0 / (2.0 * g_iDashAWADamp) : 0.0;
4078 double awsFC = g_iDashAWSDamp ? 1.0 / (2.0 * g_iDashAWSDamp) : 0.0;
4079
4080 if (abs(sogFC - m_sog_filter.GetFc()) > 1e-6) {
4081 m_sog_filter.SetFc(sogFC);
4082 }
4083 if (abs(cogFC - m_cog_filter.GetFc()) > 1e-6) {
4084 m_cog_filter.SetFc(cogFC);
4085 }
4086 if (abs(awaFC - m_awa_filter.GetFc()) > 1e-6) {
4087 m_awa_filter.SetFc(awaFC);
4088 }
4089 if (abs(awsFC - m_aws_filter.GetFc()) > 1e-6) {
4090 m_aws_filter.SetFc(awsFC);
4091 }
4092}
4093
4094void dashboard_pi::PopulateContextMenu(wxMenu *menu) {
4095 int nvis = 0;
4096 wxMenuItem *visItem = nullptr;
4097 for (size_t i = 0; i < m_ArrayOfDashboardWindow.GetCount(); i++) {
4098 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(i);
4099 wxMenuItem *item = menu->AppendCheckItem(i + 1, cont->m_sCaption);
4100 item->Check(cont->m_bIsVisible);
4101 if (cont->m_bIsVisible) {
4102 nvis++;
4103 visItem = item;
4104 }
4105 }
4106 if (nvis == 1 && visItem) visItem->Enable(false);
4107}
4108
4109void dashboard_pi::ShowDashboard(size_t id, bool visible) {
4110 if (id < m_ArrayOfDashboardWindow.GetCount()) {
4111 DashboardWindowContainer *cont = m_ArrayOfDashboardWindow.Item(id);
4112 m_pauimgr->GetPane(cont->m_pDashboardWindow).Show(visible);
4113 cont->m_bIsVisible = visible;
4114 cont->m_bPersVisible = visible;
4115 m_pauimgr->Update();
4116 }
4117}
4118
4119/* DashboardPreferencesDialog
4120 *
4121 */
4122
4123DashboardPreferencesDialog::DashboardPreferencesDialog(
4124 wxWindow *parent, wxWindowID id, wxArrayOfDashboard config)
4125 : wxDialog(parent, id, _("Dashboard preferences"), wxDefaultPosition,
4126 wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) {
4127#ifdef __WXQT__
4128 wxFont *pF = OCPNGetFont(_("Dialog"));
4129 SetFont(*pF);
4130#endif
4131
4132 int display_width, display_height;
4133 wxDisplaySize(&display_width, &display_height);
4134
4135 wxString shareLocn = *GetpSharedDataLocation() + "plugins" +
4136 wxFileName::GetPathSeparator() + "dashboard_pi" +
4137 wxFileName::GetPathSeparator() + "data" +
4138 wxFileName::GetPathSeparator();
4139
4140 Connect(wxEVT_CLOSE_WINDOW,
4141 wxCloseEventHandler(DashboardPreferencesDialog::OnCloseDialog),
4142 nullptr, this);
4143
4144 // Copy original config
4145 m_config = wxArrayOfDashboard(config);
4146 // Build Dashboard Page for Toolbox
4147 int border_size = 2;
4148
4149 auto *itemBoxSizerMainPanel = new wxBoxSizer(wxVERTICAL);
4150 SetSizer(itemBoxSizerMainPanel);
4151
4152 wxWindow *dparent = this;
4153 auto *scrollWin = new wxScrolledWindow(this, wxID_ANY, wxDefaultPosition,
4154 wxSize(-1, -1), wxVSCROLL | wxHSCROLL);
4155
4156 scrollWin->SetScrollRate(1, 1);
4157 itemBoxSizerMainPanel->Add(scrollWin, 1, wxEXPAND | wxALL, 0);
4158
4159 dparent = scrollWin;
4160
4161 auto *itemBoxSizer2 = new wxBoxSizer(wxVERTICAL);
4162 scrollWin->SetSizer(itemBoxSizer2);
4163
4164 auto *DialogButtonSizer = new wxStdDialogButtonSizer();
4165 DialogButtonSizer->AddButton(new wxButton(this, wxID_OK));
4166 DialogButtonSizer->AddButton(new wxButton(this, wxID_CANCEL));
4167 auto *help_btn = new wxButton(this, wxID_HELP);
4168 help_btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [&](wxCommandEvent &) {
4169 wxString datadir = GetPluginDataDir("manual_pi");
4170 Manual(this, datadir.ToStdString()).Launch("Dashboard");
4171 });
4172 DialogButtonSizer->AddButton(help_btn);
4173 DialogButtonSizer->Realize();
4174
4175 itemBoxSizerMainPanel->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
4176
4177 auto *itemNotebook = new wxNotebook(dparent, wxID_ANY, wxDefaultPosition,
4178 wxDefaultSize, wxNB_TOP);
4179 itemBoxSizer2->Add(itemNotebook, 0, wxALL | wxEXPAND, border_size);
4180
4181 auto *itemPanelNotebook01 =
4182 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4183 wxTAB_TRAVERSAL);
4184 auto *itemFlexGridSizer01 = new wxFlexGridSizer(2);
4185 itemFlexGridSizer01->AddGrowableCol(1);
4186 itemPanelNotebook01->SetSizer(itemFlexGridSizer01);
4187 itemNotebook->AddPage(itemPanelNotebook01, _("Dashboard"));
4188
4189 auto *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
4190 itemFlexGridSizer01->Add(itemBoxSizer01, 1, wxEXPAND | wxTOP | wxLEFT,
4191 border_size);
4192
4193 // Scale the images in the dashboard list control
4194 int imageRefSize = wxWindow::GetCharWidth() * 4;
4195
4196 auto *imglist1 = new wxImageList(imageRefSize, imageRefSize, true, 1);
4197
4198 wxBitmap bmDashBoard;
4199#ifdef ocpnUSE_SVG
4200 wxString filename = shareLocn + "Dashboard.svg";
4201 bmDashBoard = GetBitmapFromSVGFile(filename, imageRefSize, imageRefSize);
4202#else
4203 wxImage dash1 = wxBitmap(*_img_dashboard_pi).ConvertToImage();
4204 wxImage dash1s =
4205 dash1.Scale(imageRefSize, imageRefSize, wxIMAGE_QUALITY_HIGH);
4206 bmDashBoard = wxBitmap(dash1s);
4207#endif
4208
4209 imglist1->Add(bmDashBoard);
4210
4211 m_pListCtrlDashboards =
4212 new wxListCtrl(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4213 wxSize(imageRefSize * 3 / 2, 200),
4214 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4215
4216 m_pListCtrlDashboards->AssignImageList(imglist1, wxIMAGE_LIST_SMALL);
4217 m_pListCtrlDashboards->InsertColumn(0, "");
4218 m_pListCtrlDashboards->Connect(
4219 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4220 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected),
4221 nullptr, this);
4222 m_pListCtrlDashboards->Connect(
4223 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4224 wxListEventHandler(DashboardPreferencesDialog::OnDashboardSelected),
4225 nullptr, this);
4226 itemBoxSizer01->Add(m_pListCtrlDashboards, 1, wxEXPAND, 0);
4227
4228 auto *itemBoxSizer02 = new wxBoxSizer(wxHORIZONTAL);
4229 itemBoxSizer01->Add(itemBoxSizer02);
4230
4231 wxBitmap bmPlus, bmMinus;
4232 int bmSize = imageRefSize * 100 / 275;
4233
4234#ifdef __ANDROID__
4235 bmSize = imageRefSize / 2;
4236#endif
4237
4238#ifdef ocpnUSE_SVG
4239 bmPlus = GetBitmapFromSVGFile(shareLocn + "plus.svg", bmSize, bmSize);
4240 bmMinus = GetBitmapFromSVGFile(shareLocn + "minus.svg", bmSize, bmSize);
4241#else
4242 wxImage plus1 = wxBitmap(*_img_plus).ConvertToImage();
4243 wxImage plus1s = plus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4244 bmPlus = wxBitmap(plus1s);
4245
4246 wxImage minus1 = wxBitmap(*_img_minus).ConvertToImage();
4247 wxImage minus1s = minus1.Scale(bmSize, bmSize, wxIMAGE_QUALITY_HIGH);
4248 bmMinus = wxBitmap(minus1s);
4249#endif
4250
4251 m_pButtonAddDashboard = new wxBitmapButton(
4252 itemPanelNotebook01, wxID_ANY, bmPlus, wxDefaultPosition, wxDefaultSize);
4253 itemBoxSizer02->Add(m_pButtonAddDashboard, 0, wxALIGN_CENTER, 2);
4254 m_pButtonAddDashboard->Connect(
4255 wxEVT_COMMAND_BUTTON_CLICKED,
4256 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardAdd),
4257 nullptr, this);
4258
4259 m_pButtonDeleteDashboard = new wxBitmapButton(
4260 itemPanelNotebook01, wxID_ANY, bmMinus, wxDefaultPosition, wxDefaultSize);
4261 itemBoxSizer02->Add(m_pButtonDeleteDashboard, 0, wxALIGN_CENTER, 2);
4262 m_pButtonDeleteDashboard->Connect(
4263 wxEVT_COMMAND_BUTTON_CLICKED,
4264 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboardDelete),
4265 nullptr, this);
4266
4267 m_pPanelDashboard =
4268 new wxPanel(itemPanelNotebook01, wxID_ANY, wxDefaultPosition,
4269 wxDefaultSize, wxBORDER_SUNKEN);
4270 itemFlexGridSizer01->Add(m_pPanelDashboard, 1, wxEXPAND | wxTOP | wxRIGHT,
4271 border_size);
4272
4273 auto *itemBoxSizer03 = new wxBoxSizer(wxVERTICAL);
4274 m_pPanelDashboard->SetSizer(itemBoxSizer03);
4275
4276 auto *itemStaticBox02 =
4277 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Dashboard"));
4278 auto *itemStaticBoxSizer02 =
4279 new wxStaticBoxSizer(itemStaticBox02, wxHORIZONTAL);
4280 itemBoxSizer03->Add(itemStaticBoxSizer02, 0, wxEXPAND | wxALL, border_size);
4281 auto *itemFlexGridSizer = new wxFlexGridSizer(2);
4282 itemFlexGridSizer->AddGrowableCol(1);
4283 itemStaticBoxSizer02->Add(itemFlexGridSizer, 1, wxEXPAND | wxALL, 0);
4284
4285 m_pCheckBoxIsVisible =
4286 new wxCheckBox(m_pPanelDashboard, wxID_ANY, _("show this dashboard"),
4287 wxDefaultPosition, wxDefaultSize, 0);
4288 m_pCheckBoxIsVisible->SetMinSize(wxSize(25 * wxWindow::GetCharWidth(), -1));
4289
4290 itemFlexGridSizer->Add(m_pCheckBoxIsVisible, 0, wxEXPAND | wxALL,
4291 border_size);
4292 auto *itemDummy01 = new wxStaticText(m_pPanelDashboard, wxID_ANY, "");
4293 itemFlexGridSizer->Add(itemDummy01, 0, wxEXPAND | wxALL, border_size);
4294
4295 auto *itemStaticText01 =
4296 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Caption:"),
4297 wxDefaultPosition, wxDefaultSize, 0);
4298 itemFlexGridSizer->Add(itemStaticText01, 0, wxEXPAND | wxALL, border_size);
4299 m_pTextCtrlCaption = new wxTextCtrl(m_pPanelDashboard, wxID_ANY, "",
4300 wxDefaultPosition, wxDefaultSize);
4301 m_pCheckBoxIsVisible->SetMinSize(wxSize(30 * wxWindow::GetCharWidth(), -1));
4302 itemFlexGridSizer->Add(m_pTextCtrlCaption, 0, wxALIGN_RIGHT | wxALL,
4303 border_size);
4304
4305 auto *itemStaticText02 =
4306 new wxStaticText(m_pPanelDashboard, wxID_ANY, _("Orientation:"),
4307 wxDefaultPosition, wxDefaultSize, 0);
4308 itemFlexGridSizer->Add(itemStaticText02, 0, wxEXPAND | wxALL, border_size);
4309 m_pChoiceOrientation = new wxChoice(m_pPanelDashboard, wxID_ANY,
4310 wxDefaultPosition, wxDefaultSize);
4311 m_pChoiceOrientation->SetMinSize(wxSize(15 * wxWindow::GetCharWidth(), -1));
4312 m_pChoiceOrientation->Append(_("Vertical"));
4313 m_pChoiceOrientation->Append(_("Horizontal"));
4314 itemFlexGridSizer->Add(m_pChoiceOrientation, 0, wxALIGN_RIGHT | wxALL,
4315 border_size);
4316
4317 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
4318
4319 auto *imglist = new wxImageList(instImageRefSize, instImageRefSize, true, 2);
4320
4321 wxBitmap bmDial, bmInst;
4322#ifdef ocpnUSE_SVG
4323 bmDial = GetBitmapFromSVGFile(shareLocn + "dial.svg", instImageRefSize,
4324 instImageRefSize);
4325 bmInst = GetBitmapFromSVGFile(shareLocn + "instrument.svg", instImageRefSize,
4326 instImageRefSize);
4327#else
4328 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
4329 wxImage dial1s =
4330 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4331 bmDial = wxBitmap(dial1);
4332
4333 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
4334 wxImage inst1s =
4335 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
4336 bmInst = wxBitmap(inst1s);
4337#endif
4338
4339 imglist->Add(bmInst);
4340 imglist->Add(bmDial);
4341
4342 auto *itemStaticBox03 =
4343 new wxStaticBox(m_pPanelDashboard, wxID_ANY, _("Instruments"));
4344 auto *itemStaticBoxSizer03 =
4345 new wxStaticBoxSizer(itemStaticBox03, wxHORIZONTAL);
4346 itemBoxSizer03->Add(itemStaticBoxSizer03, 1, wxEXPAND | wxALL, border_size);
4347
4348 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4349 wxSize list_size = wxSize(-1, dsize.y * 35 / 100);
4350
4351#ifdef __ANDROID__
4352 int xsize = GetCharWidth() * 30;
4353 list_size = wxSize(xsize, dsize.y * 50 / 100);
4354#endif
4355
4356 m_pListCtrlInstruments =
4357 new wxListCtrl(m_pPanelDashboard, wxID_ANY, wxDefaultPosition, list_size,
4358 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL);
4359
4360 itemStaticBoxSizer03->Add(m_pListCtrlInstruments, 1, wxEXPAND | wxALL,
4361 border_size);
4362 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
4363 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
4364 m_pListCtrlInstruments->Connect(
4365 wxEVT_COMMAND_LIST_ITEM_SELECTED,
4366 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4367 nullptr, this);
4368 m_pListCtrlInstruments->Connect(
4369 wxEVT_COMMAND_LIST_ITEM_DESELECTED,
4370 wxListEventHandler(DashboardPreferencesDialog::OnInstrumentSelected),
4371 nullptr, this);
4372
4373 auto *itemBoxSizer04 = new wxBoxSizer(wxVERTICAL);
4374 itemStaticBoxSizer03->Add(itemBoxSizer04, 0, wxALIGN_TOP | wxALL,
4375 border_size);
4376 m_pButtonAdd = new wxButton(m_pPanelDashboard, wxID_ANY, _("Add..."),
4377 wxDefaultPosition, wxSize(-1, -1));
4378 itemBoxSizer04->Add(m_pButtonAdd, 0, wxEXPAND | wxALL, border_size);
4379 m_pButtonAdd->Connect(
4380 wxEVT_COMMAND_BUTTON_CLICKED,
4381 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentAdd),
4382 nullptr, this);
4383
4384 // TODO Instrument Properties ... done by Bernd Cirotzki
4385 m_pButtonEdit = new wxButton(m_pPanelDashboard, wxID_ANY, _("Edit..."),
4386 wxDefaultPosition, wxDefaultSize);
4387 itemBoxSizer04->Add(m_pButtonEdit, 0, wxEXPAND | wxALL, border_size);
4388 m_pButtonEdit->Connect(
4389 wxEVT_COMMAND_BUTTON_CLICKED,
4390 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentEdit),
4391 nullptr, this);
4392
4393 // m_pFontPickerTitle =
4394 // new wxFontPickerCtrl(m_pPanelDashboard, wxID_ANY, g_USFontTitle,
4395 // wxDefaultPosition, wxSize(-1, -1), 0,
4396 // wxDefaultValidator, "Bernd");
4397 // itemBoxSizer04->Add(m_pFontPickerTitle, 0, wxALIGN_RIGHT | wxALL, 0);
4398 // wxColor Farbe = m_pFontPickerTitle->GetSelectedColour();
4399 // m_pFontPickerTitle->SetBackgroundColour(Farbe);
4400
4401 m_pButtonDelete = new wxButton(m_pPanelDashboard, wxID_ANY, _("Delete"),
4402 wxDefaultPosition, wxSize(-1, -1));
4403 itemBoxSizer04->Add(m_pButtonDelete, 0, wxEXPAND | wxALL, border_size);
4404 m_pButtonDelete->Connect(
4405 wxEVT_COMMAND_BUTTON_CLICKED,
4406 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDelete),
4407 nullptr, this);
4408 itemBoxSizer04->AddSpacer(10);
4409 m_pButtonUp = new wxButton(m_pPanelDashboard, wxID_ANY, _("Up"),
4410 wxDefaultPosition, wxDefaultSize);
4411 itemBoxSizer04->Add(m_pButtonUp, 0, wxEXPAND | wxALL, border_size);
4412 m_pButtonUp->Connect(
4413 wxEVT_COMMAND_BUTTON_CLICKED,
4414 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentUp),
4415 nullptr, this);
4416 m_pButtonDown = new wxButton(m_pPanelDashboard, wxID_ANY, _("Down"),
4417 wxDefaultPosition, wxDefaultSize);
4418 itemBoxSizer04->Add(m_pButtonDown, 0, wxEXPAND | wxALL, border_size);
4419 m_pButtonDown->Connect(
4420 wxEVT_COMMAND_BUTTON_CLICKED,
4421 wxCommandEventHandler(DashboardPreferencesDialog::OnInstrumentDown),
4422 nullptr, this);
4423
4424 auto *itemPanelNotebook02 =
4425 new wxPanel(itemNotebook, wxID_ANY, wxDefaultPosition, wxDefaultSize,
4426 wxTAB_TRAVERSAL);
4427 auto *itemBoxSizer05 = new wxBoxSizer(wxVERTICAL);
4428 itemPanelNotebook02->SetSizer(itemBoxSizer05);
4429 itemNotebook->AddPage(itemPanelNotebook02, _("Appearance"));
4430
4431 auto *itemStaticBox01 =
4432 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Fonts"));
4433 auto *itemStaticBoxSizer01 =
4434 new wxStaticBoxSizer(itemStaticBox01, wxHORIZONTAL);
4435 itemBoxSizer05->Add(itemStaticBoxSizer01, 0, wxEXPAND | wxALL, border_size);
4436 auto *itemFlexGridSizer03 = new wxFlexGridSizer(2);
4437 itemFlexGridSizer03->AddGrowableCol(1);
4438 itemStaticBoxSizer01->Add(itemFlexGridSizer03, 1, wxEXPAND | wxALL, 0);
4439
4440 auto *itemStaticText04 =
4441 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Title:"),
4442 wxDefaultPosition, wxDefaultSize, 0);
4443 itemFlexGridSizer03->Add(itemStaticText04, 0, wxEXPAND | wxALL, border_size);
4444
4445 m_font_picker_title =
4446 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontTitle,
4447 wxDefaultPosition, wxDefaultSize);
4448 itemFlexGridSizer03->Add(m_font_picker_title, 0, wxALIGN_RIGHT | wxALL, 0);
4449
4450 auto *itemStaticText05 =
4451 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Data:"),
4452 wxDefaultPosition, wxDefaultSize, 0);
4453 itemFlexGridSizer03->Add(itemStaticText05, 0, wxEXPAND | wxALL, border_size);
4454 m_font_picker_data =
4455 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontData,
4456 wxDefaultPosition, wxDefaultSize);
4457 itemFlexGridSizer03->Add(m_font_picker_data, 0, wxALIGN_RIGHT | wxALL, 0);
4458
4459 auto *itemStaticText06 =
4460 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Label:"),
4461 wxDefaultPosition, wxDefaultSize, 0);
4462 itemFlexGridSizer03->Add(itemStaticText06, 0, wxEXPAND | wxALL, border_size);
4463 m_font_picker_label =
4464 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontLabel,
4465 wxDefaultPosition, wxDefaultSize);
4466 itemFlexGridSizer03->Add(m_font_picker_label, 0, wxALIGN_RIGHT | wxALL, 0);
4467
4468 auto *itemStaticText07 =
4469 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Small:"),
4470 wxDefaultPosition, wxDefaultSize, 0);
4471 itemFlexGridSizer03->Add(itemStaticText07, 0, wxEXPAND | wxALL, border_size);
4472 m_font_picker_small =
4473 new wxFontPickerCtrl(itemPanelNotebook02, wxID_ANY, g_USFontSmall,
4474 wxDefaultPosition, wxDefaultSize);
4475 itemFlexGridSizer03->Add(m_font_picker_small, 0, wxALIGN_RIGHT | wxALL, 0);
4476 // wxColourPickerCtrl
4477
4478 auto *itemStaticText80 =
4479 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Reset:"),
4480 wxDefaultPosition, wxDefaultSize, 0);
4481 itemFlexGridSizer03->Add(itemStaticText80, 0, wxEXPAND | wxALL, border_size);
4482
4483 m_pButtondefaultFont = new wxButton(itemPanelNotebook02, wxID_ANY,
4484 _("Set dashboard default fonts"),
4485 wxDefaultPosition, wxSize(-1, -1));
4486 itemFlexGridSizer03->Add(m_pButtondefaultFont, 0, wxALIGN_RIGHT | wxALL, 0);
4487 m_pButtondefaultFont->Connect(
4488 wxEVT_COMMAND_BUTTON_CLICKED,
4489 wxCommandEventHandler(DashboardPreferencesDialog::OnDashboarddefaultFont),
4490 nullptr, this);
4491
4492 auto *itemStaticBox04 = new wxStaticBox(itemPanelNotebook02, wxID_ANY,
4493 _("Units, Ranges, Formats"));
4494 auto *itemStaticBoxSizer04 =
4495 new wxStaticBoxSizer(itemStaticBox04, wxHORIZONTAL);
4496 itemBoxSizer05->Add(itemStaticBoxSizer04, 0, wxEXPAND | wxALL, border_size);
4497 auto *itemFlexGridSizer04 = new wxFlexGridSizer(2);
4498 itemFlexGridSizer04->AddGrowableCol(1);
4499 itemStaticBoxSizer04->Add(itemFlexGridSizer04, 1, wxEXPAND | wxALL, 0);
4500 auto *itemStaticText08 = new wxStaticText(
4501 itemPanelNotebook02, wxID_ANY, _("Speedometer max value:"),
4502 wxDefaultPosition, wxDefaultSize, 0);
4503 itemFlexGridSizer04->Add(itemStaticText08, 0, wxEXPAND | wxALL, border_size);
4504 m_spin_speed_max = new wxSpinCtrl(
4505 itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition,
4506 wxDefaultSize, wxSP_ARROW_KEYS, 10, 100, g_iDashSpeedMax);
4507 itemFlexGridSizer04->Add(m_spin_speed_max, 0, wxALIGN_RIGHT | wxALL, 0);
4508
4509 auto *itemStaticText10 = new wxStaticText(
4510 itemPanelNotebook02, wxID_ANY, _("Speed Over Ground Damping Factor:"),
4511 wxDefaultPosition, wxDefaultSize, 0);
4512 itemFlexGridSizer04->Add(itemStaticText10, 0, wxEXPAND | wxALL, border_size);
4513 m_spin_sog_damp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4514 wxDefaultPosition, wxDefaultSize,
4515 wxSP_ARROW_KEYS, 0, 100, g_iDashSOGDamp);
4516 itemFlexGridSizer04->Add(m_spin_sog_damp, 0, wxALIGN_RIGHT | wxALL, 0);
4517
4518 auto *itemStaticText11 =
4519 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("COG Damping Factor:"),
4520 wxDefaultPosition, wxDefaultSize, 0);
4521 itemFlexGridSizer04->Add(itemStaticText11, 0, wxEXPAND | wxALL, border_size);
4522 m_spin_cog_damp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4523 wxDefaultPosition, wxDefaultSize,
4524 wxSP_ARROW_KEYS, 0, 100, g_iDashCOGDamp);
4525 itemFlexGridSizer04->Add(m_spin_cog_damp, 0, wxALIGN_RIGHT | wxALL, 0);
4526
4527 auto *itemStaticText13 = new wxStaticText(
4528 itemPanelNotebook02, wxID_ANY, _("Wind speed Damping Factor:"),
4529 wxDefaultPosition, wxDefaultSize, 0);
4530 itemFlexGridSizer04->Add(itemStaticText13, 0, wxEXPAND | wxALL, border_size);
4531 m_spin_aws_damp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4532 wxDefaultPosition, wxDefaultSize,
4533 wxSP_ARROW_KEYS, 0, 100, g_iDashAWSDamp);
4534 itemFlexGridSizer04->Add(m_spin_aws_damp, 0, wxALIGN_RIGHT | wxALL, 0);
4535
4536 auto *itemStaticText14 = new wxStaticText(
4537 itemPanelNotebook02, wxID_ANY, _("Wind angle Damping Factor:"),
4538 wxDefaultPosition, wxDefaultSize, 0);
4539 itemFlexGridSizer04->Add(itemStaticText14, 0, wxEXPAND | wxALL, border_size);
4540 m_spin_awa_damp = new wxSpinCtrl(itemPanelNotebook02, wxID_ANY, wxEmptyString,
4541 wxDefaultPosition, wxDefaultSize,
4542 wxSP_ARROW_KEYS, 0, 100, g_iDashAWADamp);
4543 itemFlexGridSizer04->Add(m_spin_awa_damp, 0, wxALIGN_RIGHT | wxALL, 0);
4544
4545 auto *itemStaticText12 = new wxStaticText(
4546 itemPanelNotebook02, wxID_ANY, _("Local Time Offset From UTC:"),
4547 wxDefaultPosition, wxDefaultSize, 0);
4548 itemFlexGridSizer04->Add(itemStaticText12, 0, wxEXPAND | wxALL, border_size);
4549 wxString m_UTCOffsetChoices[] = {
4550 "-12:00", "-11:30", "-11:00", "-10:30", "-10:00", "-09:30", "-09:00",
4551 "-08:30", "-08:00", "-07:30", "-07:00", "-06:30", "-06:00", "-05:30",
4552 "-05:00", "-04:30", "-04:00", "-03:30", "-03:00", "-02:30", "-02:00",
4553 "-01:30", "-01:00", "-00:30", " 00:00", " 00:30", " 01:00", " 01:30",
4554 " 02:00", " 02:30", " 03:00", " 03:30", " 04:00", " 04:30", " 05:00",
4555 " 05:30", " 06:00", " 06:30", " 07:00", " 07:30", " 08:00", " 08:30",
4556 " 09:00", " 09:30", " 10:00", " 10:30", " 11:00", " 11:30", " 12:00"};
4557 int m_UTCOffsetNChoices = sizeof(m_UTCOffsetChoices) / sizeof(wxString);
4558 m_choice_utc_offset =
4559 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4560 wxDefaultSize, m_UTCOffsetNChoices, m_UTCOffsetChoices, 0);
4561 m_choice_utc_offset->SetSelection(g_iUTCOffset + 24);
4562 itemFlexGridSizer04->Add(m_choice_utc_offset, 0, wxALIGN_RIGHT | wxALL, 0);
4563
4564 auto *itemStaticText09 =
4565 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Boat speed units:"),
4566 wxDefaultPosition, wxDefaultSize, 0);
4567 itemFlexGridSizer04->Add(itemStaticText09, 0, wxEXPAND | wxALL, border_size);
4568 wxString m_SpeedUnitChoices[] = {_("Honor OpenCPN settings"), _("Kts"),
4569 _("mph"), _("km/h"), _("m/s")};
4570 int m_SpeedUnitNChoices = sizeof(m_SpeedUnitChoices) / sizeof(wxString);
4571 wxSize szSpeedUnit = wxDefaultSize;
4572 m_choice_speed_unit =
4573 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4574 szSpeedUnit, m_SpeedUnitNChoices, m_SpeedUnitChoices, 0);
4575 for (auto const &iUnit : m_SpeedUnitChoices) {
4576 szSpeedUnit.IncTo(m_choice_speed_unit->GetTextExtent(iUnit));
4577 }
4578 m_choice_speed_unit->SetSize(szSpeedUnit);
4579 m_choice_speed_unit->SetSelection(g_iDashSpeedUnit + 1);
4580 itemFlexGridSizer04->Add(m_choice_speed_unit, 0, wxALIGN_RIGHT | wxALL, 0);
4581
4582 auto *itemStaticTextDepthU =
4583 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Depth units:"),
4584 wxDefaultPosition, wxDefaultSize, 0);
4585 itemFlexGridSizer04->Add(itemStaticTextDepthU, 0, wxEXPAND | wxALL,
4586 border_size);
4587 wxString m_DepthUnitChoices[] = {_("Meters"), _("Feet"), _("Fathoms"),
4588 _("Inches"), _("Centimeters")};
4589 int m_DepthUnitNChoices = sizeof(m_DepthUnitChoices) / sizeof(wxString);
4590 wxSize szDepthUnit = wxDefaultSize;
4591 m_choice_depth_unit =
4592 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4593 szDepthUnit, m_DepthUnitNChoices, m_DepthUnitChoices, 0);
4594 for (auto const &iUnit : m_DepthUnitChoices) {
4595 szDepthUnit.IncTo(m_choice_depth_unit->GetTextExtent(iUnit));
4596 }
4597 m_choice_depth_unit->SetSize(szDepthUnit);
4598 m_choice_depth_unit->SetSelection(g_iDashDepthUnit - 3);
4599 itemFlexGridSizer04->Add(m_choice_depth_unit, 0, wxALIGN_RIGHT | wxALL, 0);
4600 wxString dMess = wxString::Format(_("Depth Offset (%s):"),
4601 m_DepthUnitChoices[g_iDashDepthUnit - 3]);
4602 auto *itemStaticDepthO =
4603 new wxStaticText(itemPanelNotebook02, wxID_ANY, dMess, wxDefaultPosition,
4604 wxDefaultSize, 0);
4605 double DepthOffset;
4606 switch (g_iDashDepthUnit - 3) {
4607 case 1:
4608 DepthOffset = g_dDashDBTOffset * 3.2808399;
4609 break;
4610 case 2:
4611 DepthOffset = g_dDashDBTOffset * 0.54680665;
4612 break;
4613 case 3:
4614 DepthOffset = g_dDashDBTOffset * 39.3700787;
4615 break;
4616 case 4:
4617 DepthOffset = g_dDashDBTOffset * 100;
4618 break;
4619 default:
4620 DepthOffset = g_dDashDBTOffset;
4621 }
4622 itemFlexGridSizer04->Add(itemStaticDepthO, 0, wxEXPAND | wxALL, border_size);
4623 m_spin_dbt_offset = new wxSpinCtrlDouble(
4624 itemPanelNotebook02, wxID_ANY, wxEmptyString, wxDefaultPosition,
4625 wxDefaultSize, wxSP_ARROW_KEYS, -100, 100, DepthOffset, 0.1);
4626 itemFlexGridSizer04->Add(m_spin_dbt_offset, 0, wxALIGN_RIGHT | wxALL, 0);
4627
4628 auto *itemStaticText0b =
4629 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Distance units:"),
4630 wxDefaultPosition, wxDefaultSize, 0);
4631 itemFlexGridSizer04->Add(itemStaticText0b, 0, wxEXPAND | wxALL, border_size);
4632 wxString m_DistanceUnitChoices[] = {_("Honor OpenCPN settings"),
4633 _("Nautical miles"), _("Statute miles"),
4634 _("Kilometers"), _("Meters")};
4635 wxSize szDistanceUnit = wxDefaultSize;
4636 int m_DistanceUnitNChoices = sizeof(m_DistanceUnitChoices) / sizeof(wxString);
4637 m_choice_distance_unit = new wxChoice(
4638 itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szDistanceUnit,
4639 m_DistanceUnitNChoices, m_DistanceUnitChoices, 0);
4640 for (auto const &iUnit : m_DistanceUnitChoices) {
4641 szDistanceUnit.IncTo(m_choice_distance_unit->GetTextExtent(iUnit));
4642 }
4643 m_choice_distance_unit->SetSize(szDistanceUnit);
4644 m_choice_distance_unit->SetSelection(g_iDashDistanceUnit + 1);
4645 itemFlexGridSizer04->Add(m_choice_distance_unit, 0, wxALIGN_RIGHT | wxALL, 0);
4646 m_choice_distance_unit->Connect(
4647 wxEVT_COMMAND_CHOICE_SELECTED,
4648 wxCommandEventHandler(DashboardPreferencesDialog::OnDistanceUnitSelect),
4649 NULL, this);
4650
4651 auto *itemStaticText0a =
4652 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Wind speed units:"),
4653 wxDefaultPosition, wxDefaultSize, 0);
4654 itemFlexGridSizer04->Add(itemStaticText0a, 0, wxEXPAND | wxALL, border_size);
4655 wxString m_WSpeedUnitChoices[] = {_("Kts"), _("mph"), _("km/h"), _("m/s")};
4656 int m_WSpeedUnitNChoices = sizeof(m_WSpeedUnitChoices) / sizeof(wxString);
4657 wxSize szWSpeedUnit = wxDefaultSize;
4658 m_choice_wind_speed_unit =
4659 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition,
4660 szWSpeedUnit, m_WSpeedUnitNChoices, m_WSpeedUnitChoices, 0);
4661 for (auto const &iUnit : m_WSpeedUnitChoices) {
4662 szWSpeedUnit.IncTo(m_choice_wind_speed_unit->GetTextExtent(iUnit));
4663 }
4664 m_choice_wind_speed_unit->SetSize(szWSpeedUnit);
4665 m_choice_wind_speed_unit->SetSelection(g_iDashWindSpeedUnit);
4666 itemFlexGridSizer04->Add(m_choice_wind_speed_unit, 0, wxALIGN_RIGHT | wxALL,
4667 0);
4668
4669 auto *itemStaticText0c =
4670 new wxStaticText(itemPanelNotebook02, wxID_ANY, _("Temperature units:"),
4671 wxDefaultPosition, wxDefaultSize, 0);
4672 itemFlexGridSizer04->Add(itemStaticText0c, 0, wxEXPAND | wxALL, border_size);
4673 wxString m_TempUnitChoices[] = {_("Celsius"), _("Fahrenheit"), _("Kelvin")};
4674 int m_TempUnitNChoices = sizeof(m_TempUnitChoices) / sizeof(wxString);
4675 wxSize szTempUnit = wxDefaultSize;
4676 m_choice_temp_unit =
4677 new wxChoice(itemPanelNotebook02, wxID_ANY, wxDefaultPosition, szTempUnit,
4678 m_TempUnitNChoices, m_TempUnitChoices, 0);
4679 for (auto const &iUnit : m_TempUnitChoices) {
4680 szTempUnit.IncTo(m_choice_temp_unit->GetTextExtent(iUnit));
4681 }
4682 m_choice_temp_unit->SetSize(szTempUnit);
4683 m_choice_temp_unit->SetSelection(g_iDashTempUnit);
4684 itemFlexGridSizer04->Add(m_choice_temp_unit, 0, wxALIGN_RIGHT | wxALL, 0);
4685
4686 // New static box to include a 3 column flexgrid sizer
4687#ifndef __ANDROID__
4688 auto *itemStaticBox05 =
4689 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4690 auto *itemStaticBoxSizer05 =
4691 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4692 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4693
4694 auto *itemFlexGridSizer05 = new wxFlexGridSizer(3);
4695 itemFlexGridSizer05->AddGrowableCol(1);
4696 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4697
4698 m_use_intern_sum_log =
4699 new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4700 _("Use internal Sumlog.") + " " +
4701 _("Enter new value if desired"));
4702 itemFlexGridSizer05->Add(m_use_intern_sum_log, 1, wxALIGN_LEFT, border_size);
4703 m_use_intern_sum_log->SetValue(g_bUseInternSumLog);
4704
4705 m_sum_log_unit =
4706 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4707 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4708 wxDefaultPosition, wxDefaultSize, 0);
4709 itemFlexGridSizer05->Add(m_sum_log_unit, 1, wxALIGN_RIGHT, 0);
4710
4711 m_sum_log_value = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4712 itemFlexGridSizer05->Add(m_sum_log_value, 1, wxALIGN_LEFT, 1);
4713 m_sum_log_value->SetValue(wxString::Format(
4714 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4715
4716 m_use_true_winddata =
4717 new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4718 _("Use N2K & SignalK true wind data over "
4719 "ground.\n(Instead of through water)"));
4720 itemFlexGridSizer05->Add(m_use_true_winddata, 1, wxALIGN_LEFT, border_size);
4721 m_use_true_winddata->SetValue(g_bDBtrueWindGround);
4722
4723#else
4724
4725 auto *itemStaticBox05 =
4726 new wxStaticBox(itemPanelNotebook02, wxID_ANY, _("Other selections"));
4727 auto *itemStaticBoxSizer05 =
4728 new wxStaticBoxSizer(itemStaticBox05, wxHORIZONTAL);
4729 itemBoxSizer05->Add(itemStaticBoxSizer05, 0, wxEXPAND | wxALL, border_size);
4730
4731 auto *itemFlexGridSizer05 = new wxFlexGridSizer(2);
4732 itemFlexGridSizer05->AddGrowableCol(1);
4733 itemStaticBoxSizer05->Add(itemFlexGridSizer05, 1, wxEXPAND | wxALL, 0);
4734
4735 m_use_intern_sum_log = new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4736 _("Use internal Sumlog.") + " ");
4737 itemFlexGridSizer05->Add(m_use_intern_sum_log, 0, wxALIGN_LEFT, border_size);
4738 m_use_intern_sum_log->SetValue(g_bUseInternSumLog);
4739 itemFlexGridSizer05->AddSpacer(0);
4740
4741 auto xtext =
4742 new wxStaticText(itemPanelNotebook02, wxID_ANY,
4743 " " + _("Enter new value if desired") + " ",
4744 wxDefaultPosition, wxDefaultSize, 0);
4745 itemFlexGridSizer05->Add(xtext, 1, wxALIGN_LEFT, 0);
4746 itemFlexGridSizer05->AddSpacer(0);
4747
4748 m_sum_log_unit = new wxStaticText(
4749 itemPanelNotebook02, wxID_ANY,
4750 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) + ": ",
4751 wxDefaultPosition, wxDefaultSize, 0);
4752 itemFlexGridSizer05->Add(m_sum_log_unit, 1, wxALIGN_RIGHT, 0);
4753
4754 m_sum_log_value = new wxTextCtrl(itemPanelNotebook02, wxID_ANY, "");
4755 itemFlexGridSizer05->Add(m_sum_log_value, 1, wxALIGN_LEFT, 1);
4756 m_sum_log_value->SetValue(wxString::Format(
4757 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
4758
4759 m_use_true_winddata =
4760 new wxCheckBox(itemPanelNotebook02, wxID_ANY,
4761 _("Use N2K & SignalK true wind data over "
4762 "ground.\n(Instead of through water)"));
4763 itemFlexGridSizer05->Add(m_use_true_winddata, 1, wxALIGN_LEFT, border_size);
4764 m_use_true_winddata->SetValue(g_bDBtrueWindGround);
4765 itemFlexGridSizer05->AddSpacer(0);
4766
4767 auto xtext1 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4768 wxDefaultPosition, wxDefaultSize, 0);
4769 itemFlexGridSizer05->Add(xtext1, 1, wxALIGN_LEFT, 0);
4770 itemFlexGridSizer05->AddSpacer(0);
4771
4772 auto xtext2 = new wxStaticText(itemPanelNotebook02, wxID_ANY, " ",
4773 wxDefaultPosition, wxDefaultSize, 0);
4774 itemFlexGridSizer05->Add(xtext2, 1, wxALIGN_LEFT, 0);
4775 itemFlexGridSizer05->AddSpacer(0);
4776
4777#endif
4778
4779 curSel = -1;
4780 for (size_t i = 0; i < m_config.GetCount(); i++) {
4781 m_pListCtrlDashboards->InsertItem(i, 0);
4782 // Using data to store m_Config index for managing deletes
4783 m_pListCtrlDashboards->SetItemData(i, i);
4784 }
4785 m_pListCtrlDashboards->SetColumnWidth(0, wxLIST_AUTOSIZE);
4786
4787 m_pListCtrlDashboards->SetItemState(0, wxLIST_STATE_SELECTED,
4788 wxLIST_STATE_SELECTED);
4789 curSel = 0;
4790
4791 UpdateDashboardButtonsState();
4792 UpdateButtonsState();
4793
4794 // SetMinSize(wxSize(400, -1));
4795
4796 Fit();
4797
4798 // Constrain size on small displays
4799 SetMaxSize(wxSize(display_width, display_height));
4800
4801 wxSize canvas_size = GetOCPNCanvasWindow()->GetSize();
4802 if (display_height < 600) {
4803 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4804 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4805 else
4806 SetSize(wxSize(canvas_size.x * 8 / 10, canvas_size.y * 8 / 10));
4807 } else {
4808 if (g_dashPrefWidth > 0 && g_dashPrefHeight > 0)
4809 SetSize(wxSize(g_dashPrefWidth, g_dashPrefHeight));
4810 else
4811 SetSize(wxSize(canvas_size.x * 3 / 4, canvas_size.y * 8 / 10));
4812 }
4813
4814 Layout();
4815 CentreOnScreen();
4816}
4817
4818void DashboardPreferencesDialog::RecalculateSize() {
4819#ifdef __ANDROID__
4820 wxSize esize;
4821 esize.x = GetCharWidth() * 110;
4822 esize.y = GetCharHeight() * 40;
4823
4824 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
4825 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
4826 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
4827 SetSize(esize);
4828
4829 CentreOnScreen();
4830#endif
4831}
4832
4833void DashboardPreferencesDialog::OnCloseDialog(wxCloseEvent &event) {
4834 g_dashPrefWidth = GetSize().x;
4835 g_dashPrefHeight = GetSize().y;
4836 SaveDashboardConfig();
4837 event.Skip();
4838}
4839
4840void DashboardPreferencesDialog::SaveDashboardConfig() {
4841 g_iDashSpeedMax = m_spin_speed_max->GetValue();
4842 g_iDashCOGDamp = m_spin_cog_damp->GetValue();
4843 g_iDashSOGDamp = m_spin_sog_damp->GetValue();
4844 g_iDashAWADamp = m_spin_awa_damp->GetValue();
4845 g_iDashAWSDamp = m_spin_aws_damp->GetValue();
4846
4847 g_bUseInternSumLog = m_use_intern_sum_log->IsChecked();
4848 double ursDist;
4849 m_sum_log_value->GetValue().ToDouble(&ursDist);
4850 g_dSumLogNM = fromUsrDistance_Plugin(ursDist, g_iDashDistanceUnit);
4851
4852 g_iUTCOffset = m_choice_utc_offset->GetSelection() - 24;
4853 g_iDashSpeedUnit = m_choice_speed_unit->GetSelection() - 1;
4854 double DashDBTOffset = m_spin_dbt_offset->GetValue();
4855 switch (g_iDashDepthUnit - 3) {
4856 case 1:
4857 g_dDashDBTOffset = DashDBTOffset / 3.2808399;
4858 break;
4859 case 2:
4860 g_dDashDBTOffset = DashDBTOffset / 0.54680665;
4861 break;
4862 case 3:
4863 g_dDashDBTOffset = DashDBTOffset / 39.3700787;
4864 break;
4865 case 4:
4866 g_dDashDBTOffset = DashDBTOffset / 100;
4867 break;
4868 default:
4869 g_dDashDBTOffset = DashDBTOffset;
4870 }
4871 g_iDashDepthUnit = m_choice_depth_unit->GetSelection() + 3;
4872 g_iDashDistanceUnit = m_choice_distance_unit->GetSelection() - 1;
4873 g_iDashWindSpeedUnit = m_choice_wind_speed_unit->GetSelection();
4874 g_bDBtrueWindGround = m_use_true_winddata->GetValue();
4875 g_iDashTempUnit = m_choice_temp_unit->GetSelection();
4876 if (curSel != -1) {
4877 DashboardWindowContainer *cont = m_config.Item(curSel);
4878 cont->m_bIsVisible = m_pCheckBoxIsVisible->IsChecked();
4879 cont->m_sCaption = m_pTextCtrlCaption->GetValue();
4880 cont->m_sOrientation =
4881 m_pChoiceOrientation->GetSelection() == 0 ? "V" : "H";
4882 cont->m_aInstrumentList.Clear();
4883 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
4884 cont->m_aInstrumentList.Add((int)m_pListCtrlInstruments->GetItemData(i));
4885 }
4886}
4887
4888void DashboardPreferencesDialog::OnDashboardSelected(wxListEvent &event) {
4889 // save changes
4890 SaveDashboardConfig();
4891 UpdateDashboardButtonsState();
4892}
4893
4894void DashboardPreferencesDialog::UpdateDashboardButtonsState() {
4895 long item = -1;
4896 item = m_pListCtrlDashboards->GetNextItem(item, wxLIST_NEXT_ALL,
4897 wxLIST_STATE_SELECTED);
4898 bool enable = (item != -1);
4899
4900 // Disable the Dashboard Delete button if the parent(Dashboard) of this
4901 // dialog is selected.
4902 bool delete_enable = enable;
4903 if (item != -1) {
4904 int sel = m_pListCtrlDashboards->GetItemData(item);
4905 DashboardWindowContainer *cont = m_config.Item(sel);
4906 DashboardWindow *dash_sel = cont->m_pDashboardWindow;
4907 if (dash_sel == GetParent()) delete_enable = false;
4908 }
4909 m_pButtonDeleteDashboard->Enable(delete_enable);
4910
4911 m_pPanelDashboard->Enable(enable);
4912
4913 if (item != -1) {
4914 curSel = m_pListCtrlDashboards->GetItemData(item);
4915 DashboardWindowContainer *cont = m_config.Item(curSel);
4916 m_pCheckBoxIsVisible->SetValue(cont->m_bIsVisible);
4917 m_pTextCtrlCaption->SetValue(cont->m_sCaption);
4918 m_pChoiceOrientation->SetSelection(cont->m_sOrientation == "V" ? 0 : 1);
4919 m_pListCtrlInstruments->DeleteAllItems();
4920 for (size_t i = 0; i < cont->m_aInstrumentList.GetCount(); i++) {
4921 wxListItem item_;
4922 getListItemForInstrument(item_, cont->m_aInstrumentList.Item(i));
4923 item_.SetId(m_pListCtrlInstruments->GetItemCount());
4924 m_pListCtrlInstruments->InsertItem(item_);
4925 }
4926
4927 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
4928 } else {
4929 curSel = -1;
4930 m_pCheckBoxIsVisible->SetValue(false);
4931 m_pTextCtrlCaption->SetValue("");
4932 m_pChoiceOrientation->SetSelection(0);
4933 m_pListCtrlInstruments->DeleteAllItems();
4934 }
4935 // UpdateButtonsState();
4936}
4937
4938void DashboardPreferencesDialog::OnDashboarddefaultFont(wxCommandEvent &event) {
4939 m_font_picker_title->SetSelectedFont(
4940 wxFont(10, wxFONTFAMILY_SWISS, wxFONTSTYLE_ITALIC, wxFONTWEIGHT_NORMAL));
4941 m_font_picker_title->SetSelectedColour(wxColour(0, 0, 0));
4942 m_font_picker_data->SetSelectedFont(
4943 wxFont(14, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4944 m_font_picker_data->SetSelectedColour(wxColour(0, 0, 0));
4945 m_font_picker_label->SetSelectedFont(
4946 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4947 m_font_picker_label->SetSelectedColour(wxColour(0, 0, 0));
4948 m_font_picker_small->SetSelectedFont(
4949 wxFont(8, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
4950 m_font_picker_small->SetSelectedColour(wxColour(0, 0, 0));
4951 double scaler = 1.0;
4952 if (OCPN_GetWinDIPScaleFactor() < 1.0)
4953 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
4954 scaler = wxMax(1.0, scaler);
4955
4956 g_USFontTitle = *(m_font_picker_title->GetFontData());
4957 g_FontTitle = *g_pUSFontTitle;
4958 g_FontTitle.SetChosenFont(g_pUSFontTitle->GetChosenFont().Scaled(scaler));
4959 g_FontTitle.SetColour(g_pUSFontTitle->GetColour());
4960 g_USFontTitle = *g_pUSFontTitle;
4961
4962 g_USFontData = *(m_font_picker_data->GetFontData());
4963 g_FontData = *g_pUSFontData;
4964 g_FontData.SetChosenFont(g_pUSFontData->GetChosenFont().Scaled(scaler));
4965 g_FontData.SetColour(g_pUSFontData->GetColour());
4966 g_USFontData = *g_pUSFontData;
4967
4968 g_USFontLabel = *(m_font_picker_label->GetFontData());
4969 g_FontLabel = *g_pUSFontLabel;
4970 g_FontLabel.SetChosenFont(g_pUSFontLabel->GetChosenFont().Scaled(scaler));
4971 g_FontLabel.SetColour(g_pUSFontLabel->GetColour());
4972 g_USFontLabel = *g_pUSFontLabel;
4973
4974 g_USFontSmall = *(m_font_picker_small->GetFontData());
4975 g_FontSmall = *g_pUSFontSmall;
4976 g_FontSmall.SetChosenFont(g_pUSFontSmall->GetChosenFont().Scaled(scaler));
4977 g_FontSmall.SetColour(g_pUSFontSmall->GetColour());
4978 g_USFontSmall = *g_pUSFontSmall;
4979}
4980
4981void DashboardPreferencesDialog::OnDashboardAdd(wxCommandEvent &event) {
4982 int idx = m_pListCtrlDashboards->GetItemCount();
4983 m_pListCtrlDashboards->InsertItem(idx, 0);
4984 // Data is index in m_Config
4985 m_pListCtrlDashboards->SetItemData(idx, m_config.GetCount());
4986 wxArrayInt ar;
4987 wxArrayOfInstrumentProperties Property;
4988 auto *dwc = new DashboardWindowContainer(nullptr, MakeName(), _("Dashboard"),
4989 "V", ar, Property);
4990 dwc->m_bIsVisible = true;
4991 m_config.Add(dwc);
4992}
4993
4994void DashboardPreferencesDialog::OnDashboardDelete(wxCommandEvent &event) {
4995 long itemID = -1;
4996 itemID = m_pListCtrlDashboards->GetNextItem(itemID, wxLIST_NEXT_ALL,
4997 wxLIST_STATE_SELECTED);
4998
4999 int idx = m_pListCtrlDashboards->GetItemData(itemID);
5000 m_pListCtrlDashboards->DeleteItem(itemID);
5001 m_config.Item(idx)->m_bIsDeleted = true;
5002 UpdateDashboardButtonsState();
5003}
5004
5005void DashboardPreferencesDialog::OnInstrumentSelected(wxListEvent &event) {
5006 UpdateButtonsState();
5007}
5008
5009void DashboardPreferencesDialog::UpdateButtonsState() {
5010 long item = -1;
5011 item = m_pListCtrlInstruments->GetNextItem(item, wxLIST_NEXT_ALL,
5012 wxLIST_STATE_SELECTED);
5013 bool enable = (item != -1);
5014
5015 m_pButtonDelete->Enable(enable);
5016 m_pButtonEdit->Enable(enable); // TODO: Properties ... done Bernd Cirotzki
5017 m_pButtonUp->Enable(item > 0);
5018 m_pButtonDown->Enable(item != -1 &&
5019 item < m_pListCtrlInstruments->GetItemCount() - 1);
5020}
5021
5022void DashboardPreferencesDialog::OnInstrumentAdd(wxCommandEvent &event) {
5023 AddInstrumentDlg pdlg((wxWindow *)event.GetEventObject(), wxID_ANY);
5024
5025#ifdef __ANDROID__
5026 wxFont *pF = OCPNGetFont(_("Dialog"));
5027 pdlg.SetFont(*pF);
5028
5029 wxSize esize;
5030 esize.x = GetCharWidth() * 110;
5031 esize.y = GetCharHeight() * 40;
5032
5033 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5034 esize.y = wxMin(esize.y, dsize.y - (3 * GetCharHeight()));
5035 esize.x = wxMin(esize.x, dsize.x - (3 * GetCharHeight()));
5036 pdlg.SetSize(esize);
5037
5038 pdlg.CentreOnScreen();
5039#endif
5040 pdlg.ShowModal();
5041 if (pdlg.GetReturnCode() == wxID_OK) {
5042 wxListItem item;
5043 getListItemForInstrument(item, pdlg.GetInstrumentAdded());
5044 item.SetId(m_pListCtrlInstruments->GetItemCount());
5045 m_pListCtrlInstruments->InsertItem(item);
5046 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5047 UpdateButtonsState();
5048 }
5049}
5050
5051void DashboardPreferencesDialog::OnInstrumentDelete(wxCommandEvent &event) {
5052 long itemIDWindow = -1;
5053 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5054 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5055 long itemID = -1;
5056 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5057 wxLIST_STATE_SELECTED);
5059 m_config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5060 InstrumentProperties *InstDel = nullptr;
5061 if (cont) {
5062 InstrumentProperties *Inst = nullptr;
5063 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5064 i++) {
5065 Inst = cont->m_aInstrumentPropertyList.Item(i);
5066 if (Inst->m_aInstrument ==
5067 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5068 Inst->m_Listplace == itemID) {
5069 cont->m_aInstrumentPropertyList.Remove(Inst);
5070 InstDel = Inst;
5071 break;
5072 } else {
5073 if (Inst->m_Listplace > itemID) Inst->m_Listplace--;
5074 }
5075 }
5076 }
5077 m_pListCtrlInstruments->DeleteItem(itemID);
5078 if (InstDel) {
5079 cont->m_pDashboardWindow->SetInstrumentList(
5080 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5081 delete InstDel;
5082 }
5083 UpdateButtonsState();
5084}
5085
5086inline void GetFontData(OCPNFontButton *FontButton, wxFontData &UnScaledFont,
5087 wxFontData &ScaledFont, double scaler) {
5088 UnScaledFont = *(FontButton->GetFontData());
5089 ScaledFont = UnScaledFont;
5090 ScaledFont.SetChosenFont(UnScaledFont.GetChosenFont().Scaled(scaler));
5091}
5092
5093void DashboardPreferencesDialog::OnInstrumentEdit(wxCommandEvent &event) {
5094 // TODO: Instument options
5095 // m_Config = Arrayofdashboardwindows.
5096 long itemIDWindow = -1;
5097 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5098 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5099 long itemID = -1;
5100 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5101 wxLIST_STATE_SELECTED);
5102 // m_Config.
5103 // curSel = m_pListCtrlDashboards->GetItemData(itemWindow);
5104 // DashboardWindowContainer *cont = m_Config.Item(curSel);
5105 // if (cont) ....
5107 m_config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5108 if (!cont) return;
5109 InstrumentProperties *Inst = NULL;
5110 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5111 i++) {
5112 Inst = cont->m_aInstrumentPropertyList.Item(
5113 i); // m_pListCtrlInstruments->GetItemData(itemID)
5114 if (Inst->m_aInstrument == (int)m_pListCtrlInstruments->GetItemData(
5115 itemID)) // Is for right Instrumenttype.
5116 {
5117 if (Inst->m_Listplace == itemID) break;
5118 }
5119 Inst = NULL;
5120 }
5121 if (!Inst) {
5122 Inst = new InstrumentProperties(m_pListCtrlInstruments->GetItemData(itemID),
5123 itemID);
5124 cont->m_aInstrumentPropertyList.Add(Inst);
5125 }
5126 auto *Edit = new EditDialog(this, *Inst, wxID_ANY);
5127 Edit->Fit();
5128 bool DefaultFont = false;
5129 if (Edit->ShowModal() == wxID_OK) {
5130 DefaultFont = true;
5131 double scaler = 1.0;
5132 if (OCPN_GetWinDIPScaleFactor() < 1.0)
5133 scaler = 1.0 + OCPN_GetWinDIPScaleFactor() / 4;
5134 scaler = wxMax(1.0, scaler);
5135 if (Edit->m_fontPicker2->GetFont().Scaled(scaler) !=
5136 g_FontTitle.GetChosenFont() ||
5137 Edit->m_fontPicker2->GetSelectedColour() != g_FontTitle.GetColour())
5138 DefaultFont = false;
5139 if (Edit->m_fontPicker4->GetFont().Scaled(scaler) !=
5140 g_FontData.GetChosenFont() ||
5141 Edit->m_fontPicker4->GetSelectedColour() != g_FontData.GetColour())
5142 DefaultFont = false;
5143 if (Edit->m_fontPicker5->GetFont().Scaled(scaler) !=
5144 g_FontLabel.GetChosenFont() ||
5145 Edit->m_fontPicker5->GetSelectedColour() != g_FontLabel.GetColour())
5146 DefaultFont = false;
5147 if (Edit->m_fontPicker6->GetFont().Scaled(scaler) !=
5148 g_FontSmall.GetChosenFont() ||
5149 Edit->m_fontPicker6->GetSelectedColour() != g_FontSmall.GetColour())
5150 DefaultFont = false;
5151 wxColour dummy;
5152 GetGlobalColor("DASHL", &dummy);
5153 if (Edit->m_colourPicker1->GetColour() != dummy) DefaultFont = false;
5154 GetGlobalColor("DASHB", &dummy);
5155 if (Edit->m_colourPicker2->GetColour() != dummy) DefaultFont = false;
5156 GetGlobalColor("DASHN", &dummy);
5157 if (Edit->m_colourPicker3->GetColour() != dummy) DefaultFont = false;
5158 GetGlobalColor("BLUE3", &dummy);
5159 if (Edit->m_colourPicker4->GetColour() != dummy) DefaultFont = false;
5160 if (DefaultFont)
5161 cont->m_aInstrumentPropertyList.Remove(Inst);
5162 else {
5163 GetFontData(Edit->m_fontPicker2, Inst->m_USTitleFont, Inst->m_TitleFont,
5164 scaler);
5165 GetFontData(Edit->m_fontPicker4, Inst->m_USDataFont, Inst->m_DataFont,
5166 scaler);
5167 GetFontData(Edit->m_fontPicker5, Inst->m_USLabelFont, Inst->m_LabelFont,
5168 scaler);
5169 GetFontData(Edit->m_fontPicker6, Inst->m_USSmallFont, Inst->m_SmallFont,
5170 scaler);
5171 Inst->m_DataBackgroundColour = Edit->m_colourPicker2->GetColour();
5172 Inst->m_TitleBackgroundColour = Edit->m_colourPicker1->GetColour();
5173 Inst->m_Arrow_First_Colour = Edit->m_colourPicker3->GetColour();
5174 Inst->m_Arrow_Second_Colour = Edit->m_colourPicker4->GetColour();
5175 }
5176 }
5177 delete Edit;
5178 if (cont->m_pDashboardWindow) {
5179 cont->m_pDashboardWindow->SetInstrumentList(
5180 cont->m_aInstrumentList, &(cont->m_aInstrumentPropertyList));
5181 }
5182 if (DefaultFont) delete Inst;
5183}
5184
5185void DashboardPreferencesDialog::OnInstrumentUp(wxCommandEvent &event) {
5186 long itemIDWindow = -1;
5187 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5188 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5189 long itemID = -1;
5190 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5191 wxLIST_STATE_SELECTED);
5192 wxListItem item;
5193 item.SetId(itemID);
5194 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5195 m_pListCtrlInstruments->GetItem(item);
5196 item.SetId(itemID - 1);
5197 // item.SetImage(0); // image 0, by default
5198 // Now see if the Old itemId has an own Fontdata
5200 m_config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5201 if (cont) {
5202 InstrumentProperties *Inst = nullptr;
5203 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5204 i++) {
5205 Inst = cont->m_aInstrumentPropertyList.Item(i);
5206 if (Inst->m_Listplace == (itemID - 1)) Inst->m_Listplace = itemID;
5207 if (Inst->m_aInstrument ==
5208 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5209 Inst->m_Listplace == itemID) {
5210 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID - 1;
5211 }
5212 }
5213 }
5214 m_pListCtrlInstruments->DeleteItem(itemID);
5215 m_pListCtrlInstruments->InsertItem(item);
5216 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5217 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5218
5219 m_pListCtrlInstruments->SetItemState(itemID - 1, wxLIST_STATE_SELECTED,
5220 wxLIST_STATE_SELECTED);
5221
5222 UpdateButtonsState();
5223}
5224
5225void DashboardPreferencesDialog::OnInstrumentDown(wxCommandEvent &event) {
5226 long itemIDWindow = -1;
5227 itemIDWindow = m_pListCtrlDashboards->GetNextItem(
5228 itemIDWindow, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
5229 long itemID = -1;
5230 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5231 wxLIST_STATE_SELECTED);
5232
5233 wxListItem item;
5234 item.SetId(itemID);
5235 item.SetMask(wxLIST_MASK_TEXT | wxLIST_MASK_IMAGE | wxLIST_MASK_DATA);
5236 m_pListCtrlInstruments->GetItem(item);
5237 item.SetId(itemID + 1);
5238 // item.SetImage(0); // image 0, by default
5239 // Now see if the Old itemId has an own Fontdata
5241 m_config.Item(m_pListCtrlDashboards->GetItemData(itemIDWindow));
5242 if (cont) {
5243 InstrumentProperties *Inst = nullptr;
5244 for (unsigned int i = 0; i < (cont->m_aInstrumentPropertyList.GetCount());
5245 i++) {
5246 Inst = cont->m_aInstrumentPropertyList.Item(i);
5247 if (Inst->m_Listplace == (itemID + 1) &&
5248 Inst->m_aInstrument !=
5249 (int)m_pListCtrlInstruments->GetItemData(itemID))
5250 Inst->m_Listplace = itemID;
5251 if (Inst->m_aInstrument ==
5252 (int)m_pListCtrlInstruments->GetItemData(itemID) &&
5253 Inst->m_Listplace == itemID) {
5254 cont->m_aInstrumentPropertyList.Item(i)->m_Listplace = itemID + 1;
5255 break;
5256 }
5257 }
5258 }
5259 m_pListCtrlInstruments->DeleteItem(itemID);
5260 m_pListCtrlInstruments->InsertItem(item);
5261 for (int i = 0; i < m_pListCtrlInstruments->GetItemCount(); i++)
5262 m_pListCtrlInstruments->SetItemState(i, 0, wxLIST_STATE_SELECTED);
5263
5264 m_pListCtrlInstruments->SetItemState(itemID + 1, wxLIST_STATE_SELECTED,
5265 wxLIST_STATE_SELECTED);
5266
5267 UpdateButtonsState();
5268}
5269
5270void DashboardPreferencesDialog::OnDistanceUnitSelect(wxCommandEvent &event) {
5271 // Distance unit is changed so we need to update the SunLog value control.
5272 g_iDashDistanceUnit = m_choice_distance_unit->GetSelection() - 1;
5273 m_sum_log_unit->SetLabel(getUsrDistanceUnit_Plugin(g_iDashDistanceUnit) +
5274 ": ");
5275 m_sum_log_value->SetValue(wxString::Format(
5276 "%.1f", toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit)));
5277}
5278
5279//----------------------------------------------------------------
5280//
5281// Add Instrument Dialog Implementation
5282//
5283//----------------------------------------------------------------
5284
5285AddInstrumentDlg::AddInstrumentDlg(wxWindow *pparent, wxWindowID id)
5286 : wxDialog(pparent, id, _("Add instrument"), wxDefaultPosition,
5287 wxDefaultSize, wxDEFAULT_DIALOG_STYLE) {
5288 wxBoxSizer *itemBoxSizer01 = new wxBoxSizer(wxVERTICAL);
5289 SetSizer(itemBoxSizer01);
5290 wxStaticText *itemStaticText01 =
5291 new wxStaticText(this, wxID_ANY, _("Select instrument to add:"),
5292 wxDefaultPosition, wxDefaultSize, 0);
5293 itemBoxSizer01->Add(itemStaticText01, 0, wxEXPAND | wxALL, 5);
5294
5295 int instImageRefSize = 20 * GetOCPNGUIToolScaleFactor_PlugIn();
5296
5297 wxImageList *imglist =
5298 new wxImageList(instImageRefSize, instImageRefSize, true, 2);
5299
5300 wxImage inst1 = wxBitmap(*_img_instrument).ConvertToImage();
5301 wxImage inst1s =
5302 inst1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5303 imglist->Add(wxBitmap(inst1s));
5304
5305 wxImage dial1 = wxBitmap(*_img_dial).ConvertToImage();
5306 wxImage dial1s =
5307 dial1.Scale(instImageRefSize, instImageRefSize, wxIMAGE_QUALITY_HIGH);
5308 imglist->Add(wxBitmap(dial1s));
5309
5310 wxSize dsize = GetOCPNCanvasWindow()->GetClientSize();
5311 int vsize = dsize.y * 50 / 100;
5312
5313#ifdef __ANDROID__
5314 int dw, dh;
5315 wxDisplaySize(&dw, &dh);
5316 vsize = dh * 50 / 100;
5317#endif
5318
5319 m_pListCtrlInstruments = new wxListCtrl(
5320 this, wxID_ANY, wxDefaultPosition, wxSize(-1, vsize /*250, 180 */),
5321 wxLC_REPORT | wxLC_NO_HEADER | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING);
5322 itemBoxSizer01->Add(m_pListCtrlInstruments, 0, wxEXPAND | wxALL, 5);
5323 m_pListCtrlInstruments->AssignImageList(imglist, wxIMAGE_LIST_SMALL);
5324 m_pListCtrlInstruments->InsertColumn(0, _("Instruments"));
5325
5326 wxFont *pF = OCPNGetFont(_("Dialog"));
5327 m_pListCtrlInstruments->SetFont(*pF);
5328
5329#ifdef __ANDROID__
5330 m_pListCtrlInstruments->GetHandle()->setStyleSheet(qtStyleSheet);
5332#endif
5333
5334 wxStdDialogButtonSizer *DialogButtonSizer =
5335 CreateStdDialogButtonSizer(wxOK | wxCANCEL);
5336 itemBoxSizer01->Add(DialogButtonSizer, 0, wxALIGN_RIGHT | wxALL, 5);
5337
5338 long ident = 0;
5339 for (unsigned int i = ID_DBP_I_POS; i < ID_DBP_LAST_ENTRY;
5340 i++) { // do not reference an instrument, but the last dummy entry in
5341 // the list
5342 wxListItem item;
5343 if (IsObsolete(i)) continue;
5344 getListItemForInstrument(item, i);
5345 item.SetId(ident);
5346 m_pListCtrlInstruments->InsertItem(item);
5347 id++;
5348 }
5349
5350 m_pListCtrlInstruments->SetColumnWidth(0, wxLIST_AUTOSIZE);
5351 m_pListCtrlInstruments->SetItemState(0, wxLIST_STATE_SELECTED,
5352 wxLIST_STATE_SELECTED);
5353 Fit();
5354}
5355
5356unsigned int AddInstrumentDlg::GetInstrumentAdded() {
5357 long itemID = -1;
5358 itemID = m_pListCtrlInstruments->GetNextItem(itemID, wxLIST_NEXT_ALL,
5359 wxLIST_STATE_SELECTED);
5360
5361 return (int)m_pListCtrlInstruments->GetItemData(itemID);
5362}
5363
5364//----------------------------------------------------------------
5365//
5366// Dashboard Window Implementation
5367//
5368//----------------------------------------------------------------
5369
5370// wxWS_EX_VALIDATE_RECURSIVELY required to push events to parents
5371DashboardWindow::DashboardWindow(wxWindow *pparent, wxWindowID id,
5372 wxAuiManager *auimgr, dashboard_pi *plugin,
5373 int orient, DashboardWindowContainer *mycont)
5374 : wxWindow(pparent, id, wxDefaultPosition, wxDefaultSize, 0) {
5375 // wxDialog::Create(pparent, id, _("tileMine"), wxDefaultPosition,
5376 // wxDefaultSize, wxDEFAULT_DIALOG_STYLE, "Dashboard");
5377
5378 m_pauimgr = auimgr;
5379 m_plugin = plugin;
5380 m_Container = mycont;
5381
5382 // wx2.9 itemBoxSizer = new wxWrapSizer( orient );
5383 itemBoxSizer = new wxBoxSizer(orient);
5384 SetSizer(itemBoxSizer);
5385 Connect(wxEVT_SIZE, wxSizeEventHandler(DashboardWindow::OnSize), NULL, this);
5386 Connect(wxEVT_CONTEXT_MENU,
5387 wxContextMenuEventHandler(DashboardWindow::OnContextMenu), NULL,
5388 this);
5389 Connect(wxEVT_COMMAND_MENU_SELECTED,
5390 wxCommandEventHandler(DashboardWindow::OnContextMenuSelect), NULL,
5391 this);
5392
5393#ifdef __ANDROID__
5394 Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5395 Connect(wxEVT_LEFT_UP, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5396 Connect(wxEVT_MOTION, wxMouseEventHandler(DashboardWindow::OnMouseEvent));
5397
5398 GetHandle()->setAttribute(Qt::WA_AcceptTouchEvents);
5399 GetHandle()->grabGesture(Qt::PinchGesture);
5400 GetHandle()->grabGesture(Qt::PanGesture);
5401
5402 Connect(wxEVT_QT_PINCHGESTURE,
5403 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::
5404 OnEvtPinchGesture,
5405 NULL, this);
5406 Connect(
5407 wxEVT_QT_PANGESTURE,
5408 (wxObjectEventFunction)(wxEventFunction)&DashboardWindow::OnEvtPanGesture,
5409 NULL, this);
5410#endif
5411
5412 Hide();
5413
5414 m_binResize = false;
5415 m_binPinch = false;
5416}
5417
5418DashboardWindow::~DashboardWindow() {
5419 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
5420 DashboardInstrumentContainer *pdic = m_ArrayOfInstrument.Item(i);
5421 delete pdic;
5422 }
5423}
5424
5425#ifdef __ANDROID__
5426void DashboardWindow::OnEvtPinchGesture(wxQT_PinchGestureEvent &event) {
5427 float zoom_gain = 0.3;
5428 float zoom_val;
5429 float total_zoom_val;
5430
5431 if (event.GetScaleFactor() > 1)
5432 zoom_val = ((event.GetScaleFactor() - 1.0) * zoom_gain) + 1.0;
5433 else
5434 zoom_val = 1.0 - ((1.0 - event.GetScaleFactor()) * zoom_gain);
5435
5436 if (event.GetTotalScaleFactor() > 1)
5437 total_zoom_val = ((event.GetTotalScaleFactor() - 1.0) * zoom_gain) + 1.0;
5438 else
5439 total_zoom_val = 1.0 - ((1.0 - event.GetTotalScaleFactor()) * zoom_gain);
5440
5441 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5442
5443 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5444 double aRatio = (double)currentSize.y / (double)currentSize.x;
5445
5446 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5447 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5448
5449 switch (event.GetState()) {
5450 case GestureStarted:
5451 m_binPinch = true;
5452 break;
5453
5454 case GestureUpdated:
5455 currentSize.y *= zoom_val;
5456 currentSize.x *= zoom_val;
5457
5458 if ((par_pos.y + currentSize.y) > par_size.y)
5459 currentSize.y = par_size.y - par_pos.y;
5460
5461 if ((par_pos.x + currentSize.x) > par_size.x)
5462 currentSize.x = par_size.x - par_pos.x;
5463
5465 currentSize.x = currentSize.y / aRatio;
5466
5467 currentSize.x = wxMax(currentSize.x, 150);
5468 currentSize.y = wxMax(currentSize.y, 150);
5469
5470 pane.FloatingSize(currentSize);
5471 m_pauimgr->Update();
5472
5473 break;
5474
5475 case GestureFinished: {
5476 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5477 currentSize.y *= total_zoom_val;
5478 currentSize.x = currentSize.y / aRatio;
5479 } else {
5480 currentSize.x *= total_zoom_val;
5481 currentSize.y = currentSize.x * aRatio;
5482 }
5483
5484 // Bound the resulting size
5485 if ((par_pos.y + currentSize.y) > par_size.y)
5486 currentSize.y = par_size.y - par_pos.y;
5487
5488 if ((par_pos.x + currentSize.x) > par_size.x)
5489 currentSize.x = par_size.x - par_pos.x;
5490
5491 // not too small
5492 currentSize.x = wxMax(currentSize.x, 150);
5493 currentSize.y = wxMax(currentSize.y, 150);
5494
5495 // Try a manual layout of the window, to estimate a good primary size..
5496
5497 // vertical
5498 if (itemBoxSizer->GetOrientation() == wxVERTICAL) {
5499 int total_y = 0;
5500 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5501 DashboardInstrument *inst =
5502 m_ArrayOfInstrument.Item(i)->m_pInstrument;
5503 wxSize is =
5504 inst->GetSize(itemBoxSizer->GetOrientation(), currentSize);
5505 total_y += is.y;
5506 }
5507
5508 currentSize.y = total_y;
5509 }
5510
5511 pane.FloatingSize(currentSize);
5512
5513 // Reshow the window
5514 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5515 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5516 inst->Show();
5517 }
5518
5519 m_pauimgr->Update();
5520
5521 m_binPinch = false;
5522 m_binResize = false;
5523
5524 break;
5525 }
5526
5527 case GestureCanceled:
5528 m_binPinch = false;
5529 m_binResize = false;
5530 break;
5531
5532 default:
5533 break;
5534 }
5535}
5536
5537void DashboardWindow::OnEvtPanGesture(wxQT_PanGestureEvent &event) {
5538 if (m_binPinch) return;
5539
5540 if (m_binResize) return;
5541
5542 int x = event.GetOffset().x;
5543 int y = event.GetOffset().y;
5544
5545 int lx = event.GetLastOffset().x;
5546 int ly = event.GetLastOffset().y;
5547
5548 int dx = x - lx;
5549 int dy = y - ly;
5550
5551 switch (event.GetState()) {
5552 case GestureStarted:
5553 if (m_binPan) break;
5554
5555 m_binPan = true;
5556 break;
5557
5558 case GestureUpdated:
5559 if (m_binPan) {
5560 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5561 wxPoint par_pos_old = ClientToScreen(wxPoint(0, 0)); // GetPosition();
5562
5563 wxPoint par_pos = par_pos_old;
5564 par_pos.x += dx;
5565 par_pos.y += dy;
5566
5567 par_pos.x = wxMax(par_pos.x, 0);
5568 par_pos.y = wxMax(par_pos.y, 0);
5569
5570 wxSize mySize = GetSize();
5571
5572 if ((par_pos.y + mySize.y) > par_size.y)
5573 par_pos.y = par_size.y - mySize.y;
5574
5575 if ((par_pos.x + mySize.x) > par_size.x)
5576 par_pos.x = par_size.x - mySize.x;
5577
5578 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5579 pane.FloatingPosition(par_pos).Float();
5580 m_pauimgr->Update();
5581 }
5582 break;
5583
5584 case GestureFinished:
5585 if (m_binPan) {
5586 }
5587 m_binPan = false;
5588
5589 break;
5590
5591 case GestureCanceled:
5592 m_binPan = false;
5593 break;
5594
5595 default:
5596 break;
5597 }
5598}
5599
5600void DashboardWindow::OnMouseEvent(wxMouseEvent &event) {
5601 if (m_binPinch) return;
5602
5603 if (m_binResize) {
5604 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5605 wxSize currentSize = wxSize(pane.floating_size.x, pane.floating_size.y);
5606 double aRatio = (double)currentSize.y / (double)currentSize.x;
5607
5608 wxSize par_size = GetOCPNCanvasWindow()->GetClientSize();
5609 wxPoint par_pos = wxPoint(pane.floating_pos.x, pane.floating_pos.y);
5610
5611 if (event.LeftDown()) {
5612 m_resizeStartPoint = event.GetPosition();
5613 m_resizeStartSize = currentSize;
5614 m_binResize2 = true;
5615 }
5616
5617 if (m_binResize2) {
5618 if (event.Dragging()) {
5619 wxPoint p = event.GetPosition();
5620
5621 wxSize dragSize = m_resizeStartSize;
5622
5623 dragSize.y += p.y - m_resizeStartPoint.y;
5624 dragSize.x += p.x - m_resizeStartPoint.x;
5625 ;
5626
5627 if ((par_pos.y + dragSize.y) > par_size.y)
5628 dragSize.y = par_size.y - par_pos.y;
5629
5630 if ((par_pos.x + dragSize.x) > par_size.x)
5631 dragSize.x = par_size.x - par_pos.x;
5632
5634 // dragSize.x = dragSize.y / aRatio;
5635
5636 // not too small
5637 dragSize.x = wxMax(dragSize.x, 150);
5638 dragSize.y = wxMax(dragSize.y, 150);
5639
5640 pane.FloatingSize(dragSize);
5641 m_pauimgr->Update();
5642 }
5643
5644 if (event.LeftUp()) {
5645 wxPoint p = event.GetPosition();
5646
5647 wxSize dragSize = m_resizeStartSize;
5648
5649 dragSize.y += p.y - m_resizeStartPoint.y;
5650 dragSize.x += p.x - m_resizeStartPoint.x;
5651 ;
5652
5653 if ((par_pos.y + dragSize.y) > par_size.y)
5654 dragSize.y = par_size.y - par_pos.y;
5655
5656 if ((par_pos.x + dragSize.x) > par_size.x)
5657 dragSize.x = par_size.x - par_pos.x;
5658
5659 // not too small
5660 dragSize.x = wxMax(dragSize.x, 150);
5661 dragSize.y = wxMax(dragSize.y, 150);
5662 /*
5663 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++
5664 ) { DashboardInstrument* inst =
5665 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Show();
5666 }
5667 */
5668 pane.FloatingSize(dragSize);
5669 m_pauimgr->Update();
5670
5671 m_binResize = false;
5672 m_binResize2 = false;
5673 }
5674 }
5675 }
5676}
5677#endif
5678
5679void DashboardWindow::OnSize(wxSizeEvent &event) {
5680 event.Skip();
5681 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
5682 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
5683 inst->SetMinSize(
5684 inst->GetSize(itemBoxSizer->GetOrientation(), GetClientSize()));
5685 }
5686 Layout();
5687 Refresh();
5688 // Capture the user adjusted docked Dashboard size
5689 this->m_Container->m_best_size = event.GetSize();
5690}
5691
5692void DashboardWindow::OnContextMenu(wxContextMenuEvent &event) {
5693 wxMenu *contextMenu = new wxMenu();
5694
5695#ifdef __WXQT__
5696 wxFont *pf = OCPNGetFont(_("Menu"));
5697
5698 // add stuff
5699 wxMenuItem *item1 =
5700 new wxMenuItem(contextMenu, ID_DASH_PREFS, _("Preferences..."));
5701 item1->SetFont(*pf);
5702 contextMenu->Append(item1);
5703
5704 wxMenuItem *item2 =
5705 new wxMenuItem(contextMenu, ID_DASH_RESIZE, _("Resize..."));
5706 item2->SetFont(*pf);
5707 contextMenu->Append(item2);
5708
5709#else
5710
5711 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5712 if (pane.IsOk() && pane.IsDocked()) {
5713 contextMenu->Append(ID_DASH_UNDOCK, _("Undock"));
5714 }
5715 wxMenuItem *btnVertical =
5716 contextMenu->AppendRadioItem(ID_DASH_VERTICAL, _("Vertical"));
5717 btnVertical->Check(itemBoxSizer->GetOrientation() == wxVERTICAL);
5718 wxMenuItem *btnHorizontal =
5719 contextMenu->AppendRadioItem(ID_DASH_HORIZONTAL, _("Horizontal"));
5720 btnHorizontal->Check(itemBoxSizer->GetOrientation() == wxHORIZONTAL);
5721 contextMenu->AppendSeparator();
5722
5723 m_plugin->PopulateContextMenu(contextMenu);
5724
5725 contextMenu->AppendSeparator();
5726 contextMenu->Append(ID_DASH_PREFS, _("Preferences..."));
5727
5728#endif
5729
5730 PopupMenu(contextMenu);
5731 delete contextMenu;
5732}
5733
5734void DashboardWindow::OnContextMenuSelect(wxCommandEvent &event) {
5735 if (event.GetId() < ID_DASH_PREFS) { // Toggle dashboard visibility
5736 if (m_plugin->GetDashboardWindowShownCount() > 1 || event.IsChecked())
5737 m_plugin->ShowDashboard(event.GetId() - 1, event.IsChecked());
5738 else
5739 m_plugin->ShowDashboard(event.GetId() - 1, true);
5740
5741 SetToolbarItemState(m_plugin->GetToolbarItemId(),
5742 m_plugin->GetDashboardWindowShownCount() != 0);
5743 }
5744
5745 switch (event.GetId()) {
5746 case ID_DASH_PREFS: {
5747 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5748 // Capture the dashboard's floating_pos before update.
5749 wxPoint fp = pane.floating_pos;
5750 m_plugin->ShowPreferencesDialog(this);
5751
5752 /*
5753 This function sets the correct size of the edited dashboard with the
5754 previous floating position instead of the default that is otherwise used.
5755 If the panel is docked the size is instead specified by the docking logic
5756 */
5757 if (!pane.IsDocked())
5758 ChangePaneOrientation(GetSizerOrientation(), true, fp.x, fp.y);
5759
5760 if (g_bUseInternSumLog) m_plugin->UpdateSumLog(false);
5761 return; // Does it's own save.
5762 }
5763 case ID_DASH_RESIZE: {
5764 /*
5765 for( unsigned int i=0; i<m_ArrayOfInstrument.size(); i++ ) {
5766 DashboardInstrument* inst =
5767 m_ArrayOfInstrument.Item(i)->m_pInstrument; inst->Hide();
5768 }
5769 */
5770 m_binResize = true;
5771
5772 return;
5773 }
5774 case ID_DASH_VERTICAL: {
5775 ChangePaneOrientation(wxVERTICAL, true);
5776 m_Container->m_sOrientation = "V";
5777 break;
5778 }
5779 case ID_DASH_HORIZONTAL: {
5780 ChangePaneOrientation(wxHORIZONTAL, true);
5781 m_Container->m_sOrientation = "H";
5782 break;
5783 }
5784 case ID_DASH_UNDOCK: {
5785 ChangePaneOrientation(GetSizerOrientation(), true);
5786 return; // Nothing changed so nothing need be saved
5787 }
5788 }
5789 m_plugin->SaveConfig();
5790}
5791
5792void DashboardWindow::SetColorScheme(PI_ColorScheme cs) {
5793 DimeWindow(this);
5794
5795 // Improve appearance, especially in DUSK or NIGHT palette
5796 wxColour col = g_BackgroundColor;
5797
5798 if (!g_ForceBackgroundColor) GetGlobalColor("DASHL", &col);
5799 SetBackgroundColour(col);
5800
5801 Refresh(false);
5802}
5803
5804void DashboardWindow::ChangePaneOrientation(int orient, bool updateAUImgr,
5805 int fpx, int fpy) {
5806 m_pauimgr->DetachPane(this);
5807 SetSizerOrientation(orient);
5808 bool vertical = orient == wxVERTICAL;
5809 // wxSize sz = GetSize( orient, wxDefaultSize );
5810 wxSize sz = GetMinSize();
5811 // We must change Name to reset AUI perpective
5812 m_Container->m_sName = MakeName();
5813 m_pauimgr->AddPane(this, wxAuiPaneInfo()
5814 .Name(m_Container->m_sName)
5815 .Caption(m_Container->m_sCaption)
5816 .CaptionVisible(true)
5817 .TopDockable(!vertical)
5818 .BottomDockable(!vertical)
5819 .LeftDockable(vertical)
5820 .RightDockable(vertical)
5821 .MinSize(sz)
5822 .BestSize(sz)
5823 .FloatingSize(sz)
5824 .FloatingPosition(fpx, fpy)
5825 .Float()
5826 .Show(m_Container->m_bIsVisible));
5827
5828#ifdef __ANDROID__
5829 wxAuiPaneInfo &pane = m_pauimgr->GetPane(this);
5830 pane.Dockable(false);
5831#endif
5832
5833 if (updateAUImgr) m_pauimgr->Update();
5834}
5835
5836void DashboardWindow::SetSizerOrientation(int orient) {
5837 itemBoxSizer->SetOrientation(orient);
5838 /* We must reset all MinSize to ensure we start with new default */
5839 wxWindowListNode *node = GetChildren().GetFirst();
5840 while (node) {
5841 node->GetData()->SetMinSize(wxDefaultSize);
5842 node = node->GetNext();
5843 }
5844 SetMinSize(wxDefaultSize);
5845 Fit();
5846 SetMinSize(itemBoxSizer->GetMinSize());
5847}
5848
5849int DashboardWindow::GetSizerOrientation() {
5850 return itemBoxSizer->GetOrientation();
5851}
5852
5853bool isArrayIntEqual(const wxArrayInt &l1, const wxArrayOfInstrument &l2) {
5854 if (l1.GetCount() != l2.GetCount()) return false;
5855
5856 for (size_t i = 0; i < l1.GetCount(); i++)
5857 if (l1.Item(i) != l2.Item(i)->m_ID) return false;
5858
5859 return true;
5860}
5861
5862bool DashboardWindow::isInstrumentListEqual(const wxArrayInt &list) {
5863 return isArrayIntEqual(list, m_ArrayOfInstrument);
5864}
5865
5866void DashboardWindow::SetInstrumentList(
5867 wxArrayInt list, wxArrayOfInstrumentProperties *InstrumentPropertyList) {
5868 /* options
5869 ID_DBP_D_SOG: config max value, show STW optional
5870 ID_DBP_D_COG: +SOG +HDG? +BRG?
5871 ID_DBP_D_AWS: config max value. Two arrows for AWS+TWS?
5872 ID_DBP_D_VMG: config max value
5873 ID_DBP_I_DPT: config unit (meter, feet, fathoms)
5874 ID_DBP_D_DPT: show temp optional
5875 // compass: use COG or HDG
5876 // velocity range
5877 // rudder range
5878
5879 */
5880 InstrumentProperties *Properties;
5881 m_ArrayOfInstrument.Clear();
5882 itemBoxSizer->Clear(true);
5883 for (size_t i = 0; i < list.GetCount(); i++) {
5884 int id = list.Item(i);
5885 Properties = NULL;
5886 for (size_t j = 0; j < InstrumentPropertyList->GetCount(); j++) {
5887 if (InstrumentPropertyList->Item(j)->m_aInstrument == id &&
5888 InstrumentPropertyList->Item(j)->m_Listplace == (int)i) {
5889 Properties = InstrumentPropertyList->Item(j);
5890 break;
5891 }
5892 }
5893 DashboardInstrument *instrument = NULL;
5894 switch (id) {
5895 case ID_DBP_I_POS:
5896 instrument = new DashboardInstrument_Position(
5897 this, wxID_ANY, getInstrumentCaption(id), Properties);
5898 break;
5899 case ID_DBP_I_SOG:
5900 instrument = new DashboardInstrument_Single(
5901 this, wxID_ANY, getInstrumentCaption(id), Properties,
5902 OCPN_DBP_STC_SOG, "%5.1f");
5903 break;
5904 case ID_DBP_D_SOG:
5905 instrument = new DashboardInstrument_Speedometer(
5906 this, wxID_ANY, getInstrumentCaption(id), Properties,
5907 OCPN_DBP_STC_SOG, 0, g_iDashSpeedMax);
5908 ((DashboardInstrument_Dial *)instrument)
5909 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5910 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5911 // DIAL_MARKER_SIMPLE, 5);
5912 ((DashboardInstrument_Dial *)instrument)
5913 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5914 ((DashboardInstrument_Dial *)instrument)
5915 ->SetOptionExtraValue(OCPN_DBP_STC_STW, "STW %.1f",
5916 DIAL_POSITION_BOTTOMMIDDLE);
5917 break;
5918 case ID_DBP_D_STW:
5919 instrument = new DashboardInstrument_Speedometer(
5920 this, wxID_ANY, getInstrumentCaption(id), Properties,
5921 OCPN_DBP_STC_STW, 0, g_iDashSpeedMax);
5922 ((DashboardInstrument_Dial *)instrument)
5923 ->SetOptionLabel(g_iDashSpeedMax / 20 + 1, DIAL_LABEL_HORIZONTAL);
5924 //(DashboardInstrument_Dial *)instrument->SetOptionMarker(0.1,
5925 // DIAL_MARKER_SIMPLE, 5);
5926 ((DashboardInstrument_Dial *)instrument)
5927 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
5928 ((DashboardInstrument_Dial *)instrument)
5929 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, "SOG %.1f",
5930 DIAL_POSITION_BOTTOMMIDDLE);
5931 break;
5932 case ID_DBP_I_COG:
5933 instrument = new DashboardInstrument_Single(
5934 this, wxID_ANY, getInstrumentCaption(id), Properties,
5935 OCPN_DBP_STC_COG, "%03.0f");
5936 break;
5937 case ID_DBP_M_COG:
5938 instrument = new DashboardInstrument_Single(
5939 this, wxID_ANY, getInstrumentCaption(id), Properties,
5940 OCPN_DBP_STC_MCOG, "%03.0f");
5941 break;
5942 case ID_DBP_D_COG:
5943 instrument = new DashboardInstrument_Compass(
5944 this, wxID_ANY, getInstrumentCaption(id), Properties,
5946 ((DashboardInstrument_Dial *)instrument)
5947 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5948 ((DashboardInstrument_Dial *)instrument)
5949 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5950 ((DashboardInstrument_Dial *)instrument)
5951 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, "SOG\n%.2f",
5952 DIAL_POSITION_BOTTOMLEFT);
5953 break;
5954 case ID_DBP_D_HDT:
5955 instrument = new DashboardInstrument_Compass(
5956 this, wxID_ANY, getInstrumentCaption(id), Properties,
5958 ((DashboardInstrument_Dial *)instrument)
5959 ->SetOptionMarker(5, DIAL_MARKER_SIMPLE, 2);
5960 ((DashboardInstrument_Dial *)instrument)
5961 ->SetOptionLabel(30, DIAL_LABEL_ROTATED);
5962 ((DashboardInstrument_Dial *)instrument)
5963 ->SetOptionExtraValue(OCPN_DBP_STC_STW, "STW\n%.1f",
5964 DIAL_POSITION_BOTTOMLEFT);
5965 break;
5966 case ID_DBP_I_STW:
5967 instrument = new DashboardInstrument_Single(
5968 this, wxID_ANY, getInstrumentCaption(id), Properties,
5969 OCPN_DBP_STC_STW, "%.1f");
5970 break;
5971 case ID_DBP_I_HDT: // true heading
5972 // TODO: Option True or Magnetic
5973 instrument = new DashboardInstrument_Single(
5974 this, wxID_ANY, getInstrumentCaption(id), Properties,
5975 OCPN_DBP_STC_HDT, "%03.0f");
5976 break;
5977 case ID_DBP_I_HDM: // magnetic heading
5978 instrument = new DashboardInstrument_Single(
5979 this, wxID_ANY, getInstrumentCaption(id), Properties,
5980 OCPN_DBP_STC_HDM, "%03.0f");
5981 break;
5982 case ID_DBP_D_AW:
5983 case ID_DBP_D_AWA:
5984 instrument = new DashboardInstrument_Wind(this, wxID_ANY,
5985 getInstrumentCaption(id),
5986 Properties, OCPN_DBP_STC_AWA);
5987 ((DashboardInstrument_Dial *)instrument)
5988 ->SetOptionMainValue("%.0f", DIAL_POSITION_BOTTOMLEFT);
5989 ((DashboardInstrument_Dial *)instrument)
5990 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, "%.1f",
5991 DIAL_POSITION_INSIDE);
5992 break;
5993 case ID_DBP_I_AWS:
5994 instrument = new DashboardInstrument_Single(
5995 this, wxID_ANY, getInstrumentCaption(id), Properties,
5996 OCPN_DBP_STC_AWS, "%.1f");
5997 break;
5998 case ID_DBP_D_AWS:
5999 instrument = new DashboardInstrument_Speedometer(
6000 this, wxID_ANY, getInstrumentCaption(id), Properties,
6001 OCPN_DBP_STC_AWS, 0, 45);
6002 ((DashboardInstrument_Dial *)instrument)
6003 ->SetOptionLabel(5, DIAL_LABEL_HORIZONTAL);
6004 ((DashboardInstrument_Dial *)instrument)
6005 ->SetOptionMarker(1, DIAL_MARKER_SIMPLE, 5);
6006 ((DashboardInstrument_Dial *)instrument)
6007 ->SetOptionMainValue("A %.1f", DIAL_POSITION_BOTTOMLEFT);
6008 ((DashboardInstrument_Dial *)instrument)
6009 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, "T %.1f",
6010 DIAL_POSITION_BOTTOMRIGHT);
6011 break;
6012 case ID_DBP_D_TW: // True Wind angle +-180 degr on boat axis
6013 instrument = new DashboardInstrument_TrueWindAngle(
6014 this, wxID_ANY, getInstrumentCaption(id), Properties,
6016 ((DashboardInstrument_Dial *)instrument)
6017 ->SetOptionMainValue("%.0f", DIAL_POSITION_BOTTOMLEFT);
6018 ((DashboardInstrument_Dial *)instrument)
6019 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, "%.1f",
6020 DIAL_POSITION_INSIDE);
6021 break;
6022 case ID_DBP_D_AWA_TWA: // App/True Wind angle +-180 degr on boat axis
6023 instrument = new DashboardInstrument_AppTrueWindAngle(
6024 this, wxID_ANY, getInstrumentCaption(id), Properties,
6026 ((DashboardInstrument_Dial *)instrument)->SetCapFlag(OCPN_DBP_STC_TWA);
6027 ((DashboardInstrument_Dial *)instrument)
6028 ->SetOptionMainValue("%.0f", DIAL_POSITION_NONE);
6029 ((DashboardInstrument_Dial *)instrument)
6030 ->SetOptionExtraValue(OCPN_DBP_STC_TWS, "%.1f", DIAL_POSITION_NONE);
6031 ((DashboardInstrument_Dial *)instrument)
6032 ->SetOptionExtraValue(OCPN_DBP_STC_AWS, "%.1f", DIAL_POSITION_NONE);
6033 break;
6034 case ID_DBP_D_TWD: // True Wind direction
6035 instrument = new DashboardInstrument_WindCompass(
6036 this, wxID_ANY, getInstrumentCaption(id), Properties,
6038 ((DashboardInstrument_Dial *)instrument)
6039 ->SetOptionMainValue("%.0f", DIAL_POSITION_BOTTOMLEFT);
6040 ((DashboardInstrument_Dial *)instrument)
6041 ->SetOptionExtraValue(OCPN_DBP_STC_TWS2, "%.1f",
6042 DIAL_POSITION_INSIDE);
6043 break;
6044 case ID_DBP_I_ALTI:
6045 instrument = new DashboardInstrument_Single(
6046 this, wxID_ANY, getInstrumentCaption(id), Properties,
6047 OCPN_DBP_STC_ALTI, "%6.1f");
6048 break;
6049 case ID_DBP_D_ALTI:
6050 instrument = new DashboardInstrument_Altitude(
6051 this, wxID_ANY, getInstrumentCaption(id), Properties);
6052 break;
6053 case ID_DBP_I_DPT:
6054 instrument = new DashboardInstrument_Single(
6055 this, wxID_ANY, getInstrumentCaption(id), Properties,
6056 OCPN_DBP_STC_DPT, "%5.2f");
6057 break;
6058 case ID_DBP_D_DPT:
6059 instrument = new DashboardInstrument_Depth(
6060 this, wxID_ANY, getInstrumentCaption(id), Properties);
6061 break;
6062 case ID_DBP_I_TMP: // water temperature
6063 instrument = new DashboardInstrument_Single(
6064 this, wxID_ANY, getInstrumentCaption(id), Properties,
6065 OCPN_DBP_STC_TMP, "%2.1f");
6066 break;
6067 case ID_DBP_I_MDA: // barometric pressure
6068 instrument = new DashboardInstrument_Single(
6069 this, wxID_ANY, getInstrumentCaption(id), Properties,
6070 OCPN_DBP_STC_MDA, "%5.1f");
6071 break;
6072 case ID_DBP_D_MDA: // barometric pressure
6073 instrument = new DashboardInstrument_Speedometer(
6074 this, wxID_ANY, getInstrumentCaption(id), Properties,
6075 OCPN_DBP_STC_MDA, 938, 1088);
6076 ((DashboardInstrument_Dial *)instrument)
6077 ->SetOptionLabel(15, DIAL_LABEL_HORIZONTAL);
6078 ((DashboardInstrument_Dial *)instrument)
6079 ->SetOptionMarker(7.5, DIAL_MARKER_SIMPLE, 1);
6080 ((DashboardInstrument_Dial *)instrument)
6081 ->SetOptionMainValue("%5.1f", DIAL_POSITION_INSIDE);
6082 break;
6083 case ID_DBP_I_ATMP: // air temperature
6084 instrument = new DashboardInstrument_Single(
6085 this, wxID_ANY, getInstrumentCaption(id), Properties,
6086 OCPN_DBP_STC_ATMP, "%2.1f");
6087 break;
6088 case ID_DBP_I_VLW1: // Trip Log
6089 instrument = new DashboardInstrument_Single(
6090 this, wxID_ANY, getInstrumentCaption(id), Properties,
6091 OCPN_DBP_STC_VLW1, "%2.1f");
6092 break;
6093
6094 case ID_DBP_I_VLW2: // Sum Log
6095 instrument = new DashboardInstrument_Single(
6096 this, wxID_ANY, getInstrumentCaption(id), Properties,
6097 OCPN_DBP_STC_VLW2, "%2.1f");
6098 break;
6099
6100 case ID_DBP_I_TWA: // true wind angle
6101 instrument = new DashboardInstrument_Single(
6102 this, wxID_ANY, getInstrumentCaption(id), Properties,
6103 OCPN_DBP_STC_TWA, "%5.0f");
6104 break;
6105 case ID_DBP_I_TWD: // true wind direction
6106 instrument = new DashboardInstrument_Single(
6107 this, wxID_ANY, getInstrumentCaption(id), Properties,
6108 OCPN_DBP_STC_TWD, "%3.0f");
6109 break;
6110 case ID_DBP_I_TWS: // true wind speed
6111 instrument = new DashboardInstrument_Single(
6112 this, wxID_ANY, getInstrumentCaption(id), Properties,
6113 OCPN_DBP_STC_TWS, "%2.1f");
6114 break;
6115 case ID_DBP_I_AWA: // apparent wind angle
6116 instrument = new DashboardInstrument_Single(
6117 this, wxID_ANY, getInstrumentCaption(id), Properties,
6118 OCPN_DBP_STC_AWA, "%3.0f");
6119 break;
6120 case ID_DBP_I_VMGW: // VMG based on wind and STW
6121 instrument = new DashboardInstrument_Single(
6122 this, wxID_ANY, getInstrumentCaption(id), Properties,
6123 OCPN_DBP_STC_VMGW, "%2.1f");
6124 break;
6125 case ID_DBP_I_VMG:
6126 instrument = new DashboardInstrument_Single(
6127 this, wxID_ANY, getInstrumentCaption(id), Properties,
6128 OCPN_DBP_STC_VMG, "%5.1f");
6129 break;
6130 case ID_DBP_D_VMG:
6131 instrument = new DashboardInstrument_Speedometer(
6132 this, wxID_ANY, getInstrumentCaption(id), Properties,
6133 OCPN_DBP_STC_VMG, 0, g_iDashSpeedMax);
6134 ((DashboardInstrument_Dial *)instrument)
6135 ->SetOptionLabel(1, DIAL_LABEL_HORIZONTAL);
6136 ((DashboardInstrument_Dial *)instrument)
6137 ->SetOptionMarker(0.5, DIAL_MARKER_SIMPLE, 2);
6138 ((DashboardInstrument_Dial *)instrument)
6139 ->SetOptionExtraValue(OCPN_DBP_STC_SOG, "SOG\n%.1f",
6140 DIAL_POSITION_BOTTOMLEFT);
6141 break;
6142 case ID_DBP_I_RSA:
6143 instrument = new DashboardInstrument_Single(
6144 this, wxID_ANY, getInstrumentCaption(id), Properties,
6145 OCPN_DBP_STC_RSA, "%5.0f");
6146 break;
6147 case ID_DBP_D_RSA:
6148 instrument = new DashboardInstrument_RudderAngle(
6149 this, wxID_ANY, getInstrumentCaption(id), Properties);
6150 break;
6151 case ID_DBP_I_SAT:
6152 instrument = new DashboardInstrument_Single(
6153 this, wxID_ANY, getInstrumentCaption(id), Properties,
6154 OCPN_DBP_STC_SAT, "%5.0f");
6155 break;
6156 case ID_DBP_D_GPS:
6157 instrument = new DashboardInstrument_GPS(
6158 this, wxID_ANY, getInstrumentCaption(id), Properties);
6159 break;
6160 case ID_DBP_I_PTR:
6161 instrument = new DashboardInstrument_Position(
6162 this, wxID_ANY, getInstrumentCaption(id), Properties,
6164 break;
6165 case ID_DBP_I_GPSUTC:
6166 instrument = new DashboardInstrument_Clock(
6167 this, wxID_ANY, getInstrumentCaption(id), Properties);
6168 break;
6169 case ID_DBP_I_SUN:
6170 instrument = new DashboardInstrument_Sun(
6171 this, wxID_ANY, getInstrumentCaption(id), Properties);
6172 break;
6173 case ID_DBP_D_MON:
6174 instrument = new DashboardInstrument_Moon(
6175 this, wxID_ANY, getInstrumentCaption(id), Properties);
6176 break;
6177 case ID_DBP_D_WDH:
6178 instrument = new DashboardInstrument_WindDirHistory(
6179 this, wxID_ANY, getInstrumentCaption(id), Properties);
6180 break;
6181 case ID_DBP_D_BPH:
6182 instrument = new DashboardInstrument_BaroHistory(
6183 this, wxID_ANY, getInstrumentCaption(id), Properties);
6184 break;
6185 case ID_DBP_I_FOS:
6186 instrument = new DashboardInstrument_FromOwnship(
6187 this, wxID_ANY, getInstrumentCaption(id), Properties);
6188 break;
6189 case ID_DBP_I_PITCH:
6190 instrument = new DashboardInstrument_Single(
6191 this, wxID_ANY, getInstrumentCaption(id), Properties,
6192 OCPN_DBP_STC_PITCH, "%2.1f");
6193 break;
6194 case ID_DBP_I_HEEL:
6195 instrument = new DashboardInstrument_Single(
6196 this, wxID_ANY, getInstrumentCaption(id), Properties,
6197 OCPN_DBP_STC_HEEL, "%2.1f");
6198 break;
6199 // any clock display with "LCL" in the format string is converted from
6200 // UTC to local TZ
6201 case ID_DBP_I_SUNLCL:
6202 instrument = new DashboardInstrument_Sun(
6203 this, wxID_ANY, getInstrumentCaption(id), Properties,
6204 "%02i:%02i:%02i LCL");
6205 break;
6206 case ID_DBP_I_GPSLCL:
6207 instrument = new DashboardInstrument_Clock(
6208 this, wxID_ANY, getInstrumentCaption(id), Properties,
6209 OCPN_DBP_STC_CLK, "%02i:%02i:%02i LCL");
6210 break;
6211 case ID_DBP_I_CPULCL:
6212 instrument = new DashboardInstrument_CPUClock(
6213 this, wxID_ANY, getInstrumentCaption(id), Properties,
6214 "%02i:%02i:%02i LCL");
6215 break;
6216 case ID_DBP_I_HUM:
6217 instrument = new DashboardInstrument_Single(
6218 this, wxID_ANY, getInstrumentCaption(id), Properties,
6219 OCPN_DBP_STC_HUM, "%3.0f");
6220 break;
6221 case ID_DBP_I_WCC:
6222 instrument = new DashboardInstrument_Single(
6223 this, wxID_ANY, getInstrumentCaption(id), Properties,
6224 OCPN_DBP_STC_WCC, "%5.1f");
6225 break;
6226 }
6227 if (instrument) {
6228 instrument->instrumentTypeId = id;
6229 m_ArrayOfInstrument.Add(new DashboardInstrumentContainer(
6230 id, instrument, instrument->GetCapacity()));
6231 itemBoxSizer->Add(instrument, 0, wxEXPAND, 0);
6232 if (itemBoxSizer->GetOrientation() == wxHORIZONTAL) {
6233 itemBoxSizer->AddSpacer(5);
6234 }
6235 }
6236 }
6237
6238 // In the absense of any other hints, build the default instrument sizes by
6239 // taking the calculated with of the first (and succeeding) instruments as
6240 // hints for the next. So, best in default loads to start with an instrument
6241 // that accurately calculates its minimum width. e.g.
6242 // DashboardInstrument_Position
6243
6244 wxSize Hint = wxSize(DefaultWidth, DefaultWidth);
6245
6246 for (unsigned int i = 0; i < m_ArrayOfInstrument.size(); i++) {
6247 DashboardInstrument *inst = m_ArrayOfInstrument.Item(i)->m_pInstrument;
6248 inst->SetMinSize(inst->GetSize(itemBoxSizer->GetOrientation(), Hint));
6249 Hint = inst->GetMinSize();
6250 }
6251
6252 Fit();
6253 Layout();
6254 SetMinSize(itemBoxSizer->GetMinSize());
6255}
6256
6257void DashboardWindow::SendSentenceToAllInstruments(DASH_CAP st, double value,
6258 wxString unit) {
6259 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6260 if (m_ArrayOfInstrument.Item(i)->m_cap_flag.test(st))
6261 m_ArrayOfInstrument.Item(i)->m_pInstrument->SetData(st, value, unit);
6262 }
6263}
6264
6265void DashboardWindow::SendSatInfoToAllInstruments(int cnt, int seq,
6266 wxString talk,
6267 SAT_INFO sats[4]) {
6268 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6269 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_GPS)) &&
6270 dynamic_cast<DashboardInstrument_GPS *>(
6271 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6272 ((DashboardInstrument_GPS *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6273 ->SetSatInfo(cnt, seq, talk, sats);
6274 }
6275}
6276
6277void DashboardWindow::SendUtcTimeToAllInstruments(wxDateTime value) {
6278 for (size_t i = 0; i < m_ArrayOfInstrument.GetCount(); i++) {
6279 if ((m_ArrayOfInstrument.Item(i)->m_cap_flag.test(OCPN_DBP_STC_CLK)) &&
6280 dynamic_cast<DashboardInstrument_Clock *>(
6281 m_ArrayOfInstrument.Item(i)->m_pInstrument))
6282 // || m_ArrayOfInstrument.Item( i
6283 // )->m_pInstrument->IsKindOf( CLASSINFO(
6284 // DashboardInstrument_Sun ) )
6285 // || m_ArrayOfInstrument.Item( i
6286 // )->m_pInstrument->IsKindOf( CLASSINFO(
6287 // DashboardInstrument_Moon ) ) ) )
6288 ((DashboardInstrument_Clock *)m_ArrayOfInstrument.Item(i)->m_pInstrument)
6289 ->SetUtcTime(value);
6290 }
6291}
6292
6293// #include "wx/fontpicker.h"
6294
6295// #include "wx/fontdlg.h"
6296
6297// ============================================================================
6298// implementation
6299// ============================================================================
6300
6301// ----------------------------------------------------------------------------
6302// OCPNFontButton
6303// ----------------------------------------------------------------------------
6304
6305bool OCPNFontButton::Create(wxWindow *parent, wxWindowID id,
6306 const wxFontData &initial, const wxPoint &pos,
6307 const wxSize &size, long style,
6308 const wxValidator &validator,
6309 const wxString &name) {
6310 wxString label = (style & wxFNTP_FONTDESC_AS_LABEL)
6311 ? wxString()
6312 : // label will be updated by UpdateFont
6313 _("Choose font");
6314 label = name;
6315 // create this button
6316 if (!wxButton::Create(parent, id, label, pos, size, style, validator, name)) {
6317 wxFAIL_MSG("OCPNFontButton creation failed");
6318 return false;
6319 }
6320
6321 // and handle user clicks on it
6322 Connect(GetId(), wxEVT_BUTTON,
6323 wxCommandEventHandler(OCPNFontButton::OnButtonClick), NULL, this);
6324
6325 m_data = initial;
6326 m_selectedFont =
6327 initial.GetChosenFont().IsOk() ? initial.GetChosenFont() : *wxNORMAL_FONT;
6328 UpdateFont();
6329
6330 return true;
6331}
6332
6333void OCPNFontButton::OnButtonClick(wxCommandEvent &WXUNUSED(ev)) {
6334 // update the wxFontData to be shown in the dialog
6335 m_data.SetInitialFont(m_selectedFont);
6336 wxFont *pF = OCPNGetFont(_("Dialog"));
6337
6338#ifdef __WXGTK__
6339 // Use a smaller font picker dialog (ocpnGenericFontDialog) for small displays
6340 int display_height = wxGetDisplaySize().y;
6341 if (display_height < 800) {
6342 // create the font dialog and display it
6343 ocpnGenericFontDialog dlg(this, m_data);
6344 dlg.SetFont(*pF);
6345 if (dlg.ShowModal() == wxID_OK) {
6346 m_data = dlg.GetFontData();
6347 m_selectedFont = m_data.GetChosenFont();
6348 // fire an event
6349 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6350 GetEventHandler()->ProcessEvent(event);
6351 UpdateFont();
6352 }
6353 } else {
6354 // create the font dialog and display it
6355 wxFontDialog dlg(this, m_data);
6356 dlg.SetFont(*pF);
6357 if (dlg.ShowModal() == wxID_OK) {
6358 m_data = dlg.GetFontData();
6359 m_selectedFont = m_data.GetChosenFont();
6360 // fire an event
6361 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6362 GetEventHandler()->ProcessEvent(event);
6363 UpdateFont();
6364 }
6365 }
6366
6367#else // Not __GTK__
6368 // create the font dialog and display it
6369 wxFontDialog dlg(this, m_data);
6370 dlg.SetFont(*pF);
6371
6372#ifdef __WXQT__
6373 // Make sure that font dialog will fit on the screen without scrolling
6374 // We do this by setting the dialog font size "small enough" to show "n" lines
6375 wxSize proposed_size = GetParent()->GetSize();
6376 float n_lines = 30;
6377 float font_size = pF->GetPointSize();
6378
6379 if ((proposed_size.y / font_size) < n_lines) {
6380 float new_font_size = proposed_size.y / n_lines;
6381 wxFont *smallFont = new wxFont(*pF);
6382 smallFont->SetPointSize(new_font_size);
6383 dlg.SetFont(*smallFont);
6384 }
6385
6386 dlg.SetSize(GetParent()->GetSize());
6387 dlg.Centre();
6388#endif
6389
6390 if (dlg.ShowModal() == wxID_OK) {
6391 m_data = dlg.GetFontData();
6392 m_selectedFont = m_data.GetChosenFont();
6393
6394 // fire an event
6395 wxFontPickerEvent event(this, GetId(), m_selectedFont);
6396 GetEventHandler()->ProcessEvent(event);
6397
6398 UpdateFont();
6399 }
6400#endif
6401}
6402
6403void OCPNFontButton::UpdateFont() {
6404 if (!m_selectedFont.IsOk()) return;
6405
6406 // Leave black, until Instruments are modified to accept color fonts
6407 // SetForegroundColour(m_data.GetColour());
6408
6409 if (HasFlag(wxFNTP_USEFONT_FOR_LABEL)) {
6410 // use currently selected font for the label...
6411 wxButton::SetFont(m_selectedFont);
6412 wxButton::SetForegroundColour(GetSelectedColour());
6413 }
6414
6415 wxString label =
6416 wxString::Format("%s, %d", m_selectedFont.GetFaceName().c_str(),
6417 m_selectedFont.GetPointSize());
6418
6419 if (HasFlag(wxFNTP_FONTDESC_AS_LABEL)) {
6420 SetLabel(label);
6421 }
6422
6423 auto minsize = GetTextExtent(label);
6424 SetSize(minsize);
6425
6426 GetParent()->Layout();
6427 GetParent()->Fit();
6428}
6429
6430// Edit Dialog
6431
6432EditDialog::EditDialog(wxWindow *parent, InstrumentProperties &Properties,
6433 wxWindowID id, const wxString &title, const wxPoint &pos,
6434 const wxSize &size, long style)
6435 : wxDialog(parent, id, title, pos, size, style) {
6436 this->SetSizeHints(wxDefaultSize, wxDefaultSize);
6437
6438 wxBoxSizer *bSizer5;
6439 bSizer5 = new wxBoxSizer(wxVERTICAL);
6440
6441 wxFlexGridSizer *fgSizer2;
6442 fgSizer2 = new wxFlexGridSizer(0, 2, 0, 0);
6443 fgSizer2->SetFlexibleDirection(wxBOTH);
6444 fgSizer2->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED);
6445
6446 m_staticText1 = new wxStaticText(this, wxID_ANY, _("Title:"),
6447 wxDefaultPosition, wxDefaultSize, 0);
6448 m_staticText1->Wrap(-1);
6449 fgSizer2->Add(m_staticText1, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6450
6451 m_fontPicker2 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USTitleFont,
6452 wxDefaultPosition, wxDefaultSize);
6453 fgSizer2->Add(m_fontPicker2, 0, wxALL, 5);
6454
6455 m_staticText5 = new wxStaticText(this, wxID_ANY, _("Title background color:"),
6456 wxDefaultPosition, wxDefaultSize, 0);
6457 m_staticText5->Wrap(-1);
6458 fgSizer2->Add(m_staticText5, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6459
6460 m_colourPicker1 = new wxColourPickerCtrl(
6461 this, wxID_ANY, Properties.m_TitleBackgroundColour, wxDefaultPosition,
6462 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6463 fgSizer2->Add(m_colourPicker1, 0, wxALL, 5);
6464
6465 m_staticText2 = new wxStaticText(this, wxID_ANY, _("Data:"),
6466 wxDefaultPosition, wxDefaultSize, 0);
6467 m_staticText2->Wrap(-1);
6468 fgSizer2->Add(m_staticText2, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6469
6470 m_fontPicker4 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USDataFont,
6471 wxDefaultPosition, wxDefaultSize);
6472 fgSizer2->Add(m_fontPicker4, 0, wxALL, 5);
6473
6474 m_staticText6 = new wxStaticText(this, wxID_ANY, _("Data background color:"),
6475 wxDefaultPosition, wxDefaultSize, 0);
6476 m_staticText6->Wrap(-1);
6477 fgSizer2->Add(m_staticText6, 0, wxALL, 5);
6478
6479 m_colourPicker2 = new wxColourPickerCtrl(
6480 this, wxID_ANY, Properties.m_DataBackgroundColour, wxDefaultPosition,
6481 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6482 fgSizer2->Add(m_colourPicker2, 0, wxALL, 5);
6483
6484 m_staticText3 = new wxStaticText(this, wxID_ANY, _("Label:"),
6485 wxDefaultPosition, wxDefaultSize, 0);
6486 m_staticText3->Wrap(-1);
6487 fgSizer2->Add(m_staticText3, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6488
6489 m_fontPicker5 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USLabelFont,
6490 wxDefaultPosition, wxDefaultSize);
6491 fgSizer2->Add(m_fontPicker5, 0, wxALL, 5);
6492
6493 m_staticText4 = new wxStaticText(this, wxID_ANY, _("Small:"),
6494 wxDefaultPosition, wxDefaultSize, 0);
6495 m_staticText4->Wrap(-1);
6496 fgSizer2->Add(m_staticText4, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6497
6498 m_fontPicker6 = new wxFontPickerCtrl(this, wxID_ANY, Properties.m_USSmallFont,
6499 wxDefaultPosition, wxDefaultSize);
6500 fgSizer2->Add(m_fontPicker6, 0, wxALL, 5);
6501
6502 m_staticText9 = new wxStaticText(this, wxID_ANY, _("Arrow 1 Color :"),
6503 wxDefaultPosition, wxDefaultSize, 0);
6504 m_staticText9->Wrap(-1);
6505 fgSizer2->Add(m_staticText9, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6506
6507 m_colourPicker3 = new wxColourPickerCtrl(
6508 this, wxID_ANY, Properties.m_Arrow_First_Colour, wxDefaultPosition,
6509 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6510 fgSizer2->Add(m_colourPicker3, 0, wxALL, 5);
6511
6512 m_staticText10 = new wxStaticText(this, wxID_ANY, _("Arrow 2 Color :"),
6513 wxDefaultPosition, wxDefaultSize, 0);
6514 m_staticText10->Wrap(-1);
6515 fgSizer2->Add(m_staticText10, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
6516
6517 m_colourPicker4 = new wxColourPickerCtrl(
6518 this, wxID_ANY, Properties.m_Arrow_Second_Colour, wxDefaultPosition,
6519 wxDefaultSize, wxCLRP_DEFAULT_STYLE);
6520 fgSizer2->Add(m_colourPicker4, 0, wxALL, 5);
6521
6522 m_staticline1 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6523 wxDefaultSize, wxLI_HORIZONTAL);
6524 fgSizer2->Add(m_staticline1, 0, wxEXPAND | wxALL, 5);
6525
6526 m_staticline2 = new wxStaticLine(this, wxID_ANY, wxDefaultPosition,
6527 wxDefaultSize, wxLI_HORIZONTAL);
6528 fgSizer2->Add(m_staticline2, 0, wxEXPAND | wxALL, 5);
6529
6530 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6531
6532 fgSizer2->Add(0, 0, 1, wxEXPAND, 5);
6533
6534 m_staticText7 = new wxStaticText(this, wxID_ANY, wxEmptyString,
6535 wxDefaultPosition, wxDefaultSize, 0);
6536 m_staticText7->Wrap(-1);
6537 fgSizer2->Add(m_staticText7, 0, wxALL, 5);
6538
6539 m_button1 = new wxButton(this, wxID_ANY, _("Set default"), wxDefaultPosition,
6540 wxDefaultSize, 0);
6541 fgSizer2->Add(m_button1, 0, wxALL, 5);
6542
6543 fgSizer2->Add(0, 5, 1, wxEXPAND, 5);
6544
6545 fgSizer2->Add(5, 0, 1, wxEXPAND, 5);
6546
6547 bSizer5->Add(fgSizer2, 1, wxALL | wxEXPAND, 5);
6548
6549 m_sdbSizer3 = new wxStdDialogButtonSizer();
6550 m_sdbSizer3OK = new wxButton(this, wxID_OK);
6551 m_sdbSizer3->AddButton(m_sdbSizer3OK);
6552 m_sdbSizer3Cancel = new wxButton(this, wxID_CANCEL);
6553 m_sdbSizer3->AddButton(m_sdbSizer3Cancel);
6554 m_sdbSizer3->Realize();
6555
6556 bSizer5->Add(m_sdbSizer3, 0, 0, 1);
6557
6558 bSizer5->Add(0, 10, 0, wxEXPAND, 5);
6559
6560 this->SetSizer(bSizer5);
6561 this->Layout();
6562 bSizer5->Fit(this);
6563
6564 this->Centre(wxBOTH);
6565
6566 // Connect Events
6567 m_button1->Connect(wxEVT_COMMAND_BUTTON_CLICKED,
6568 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6569 this);
6570}
6571
6572EditDialog::~EditDialog() {
6573 // Disconnect Events
6574 m_button1->Disconnect(wxEVT_COMMAND_BUTTON_CLICKED,
6575 wxCommandEventHandler(EditDialog::OnSetdefault), NULL,
6576 this);
6577}
6578
6579void EditDialog::OnSetdefault(wxCommandEvent &event) {
6580 m_fontPicker2->SetSelectedFont(g_USFontTitle.GetChosenFont());
6581 m_fontPicker2->SetSelectedColour(g_USFontTitle.GetColour());
6582 m_fontPicker4->SetSelectedFont(g_USFontData.GetChosenFont());
6583 m_fontPicker4->SetSelectedColour(g_USFontData.GetColour());
6584 m_fontPicker5->SetSelectedFont(g_USFontLabel.GetChosenFont());
6585 m_fontPicker5->SetSelectedColour(g_USFontLabel.GetColour());
6586 m_fontPicker6->SetSelectedFont(g_USFontSmall.GetChosenFont());
6587 m_fontPicker6->SetSelectedColour(g_USFontSmall.GetColour());
6588 wxColour dummy;
6589 GetGlobalColor("DASHL", &dummy);
6590 m_colourPicker1->SetColour(dummy);
6591 GetGlobalColor("DASHB", &dummy);
6592 m_colourPicker2->SetColour(dummy);
6593 GetGlobalColor("DASHN", &dummy);
6594 m_colourPicker3->SetColour(dummy);
6595 GetGlobalColor("BLUE3", &dummy);
6596 m_colourPicker4->SetColour(dummy);
6597 Update();
6598}
6599
6600// Read or write SumLog data to config and update instrument if approropriate
6601// The sumlog value will be updated every minute when we are under way.
6602void dashboard_pi::UpdateSumLog(bool write) {
6603 if (write) {
6604 g_dSumLogNM += d_tripNM;
6605 // Write to config every 10 minutes to ensure that the sumlog value is
6606 // reasonably up to date even if O or the system stops wo SaveConfig().
6607 if (++confprint > 10) {
6608 wxFileConfig *logConf = (wxFileConfig *)m_pconfig;
6609 if (logConf) {
6610 logConf->SetPath("/PlugIns/Dashboard");
6611 logConf->Write("SumLogNM", g_dSumLogNM);
6612 logConf->Flush();
6613 }
6614 confprint = 0;
6615 }
6616 // If internal sumlog is used, update the instrument.
6617 if (g_bUseInternSumLog) {
6618 SendSentenceToAllInstruments(
6620 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6621 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6622 m_log_watchdog = 70; // We update sumLog every minute
6623 }
6624 } else {
6625 // Update sumlog instrument from config.
6626 // This will only run if we use internal sumlog.
6627 SendSentenceToAllInstruments(
6629 toUsrDistance_Plugin(g_dSumLogNM, g_iDashDistanceUnit),
6630 getUsrDistanceUnit_Plugin(g_iDashDistanceUnit));
6631 m_log_watchdog = no_nav_watchdog_timeout_ticks;
6632 }
6633}
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:95
#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.