OpenCPN Partial API docs
Loading...
Searching...
No Matches
instrument.cpp
Go to the documentation of this file.
1/**************************************************************************
2 * Copyright (C) 2010 by Jean-Eudes Onfray *
3 * Copyright (C) 2010 by David S. Register *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, see <https://www.gnu.org/licenses/>. *
17 **************************************************************************/
18
25#include <wx/wxprec.h>
26
27#include <cmath>
28
29#ifndef WX_PRECOMP
30#include <wx/wx.h>
31#endif
32
33#include "instrument.h"
34
35#ifdef __ANDROID__
36#include "qdebug.h"
37#endif
38
39// ColorScheme ... Try only make a simple darker in Night mode. this could be
40// made better.
41extern PI_ColorScheme aktuellColorScheme;
42
43wxColour GetColourSchemeBackgroundColour(wxColour co) {
44 wxColour ret_val = co;
45#if wxCHECK_VERSION(3, 1, 6)
46 unsigned int red = co.GetRed();
47 unsigned int Green = co.GetGreen();
48 unsigned int Blue = co.GetBlue();
49#else
50 unsigned int red = co.Red();
51 unsigned int Green = co.Green();
52 unsigned int Blue = co.Blue();
53#endif
54 switch (aktuellColorScheme) {
56 break;
58 break;
60 red *= .8;
61 Green *= .8;
62 Blue *= .8;
63 ret_val = wxColour(red, Green, Blue);
64 break;
66 red *= .5;
67 Green *= .5;
68 Blue *= .5;
69 ret_val = wxColour(red, Green, Blue);
70 break;
71 default:
72 break;
73 }
74 return ret_val;
75}
76
77wxColour GetColourSchemeFont(wxColour co) {
78 wxColour ret_val = co;
79#if wxCHECK_VERSION(3, 1, 6)
80 unsigned int red = co.GetRed();
81 unsigned int Green = co.GetGreen();
82 unsigned int Blue = co.GetBlue();
83#else
84 unsigned int red = co.Red();
85 unsigned int Green = co.Green();
86 unsigned int Blue = co.Blue();
87#endif
88 switch (aktuellColorScheme) {
90 break;
92 break;
94 red *= .8;
95 Green *= .8;
96 Blue *= .8;
97 // if (red + Green + Blue < 10) {
98 // red = Green = Blue = 50;
99 // }
100 ret_val = wxColour(red, Green, Blue);
101 break;
103 red *= .5;
104 Green *= .5;
105 Blue *= .5;
106 if (red + Green + Blue < 10) {
107 red = Green = Blue = 50;
108 }
109 ret_val = wxColour(red, Green, Blue);
110 break;
111 default:
112 break;
113 }
114 return ret_val;
115}
116
117//----------------------------------------------------------------
118//
119// Generic DashboardInstrument Implementation
120//
121//----------------------------------------------------------------
122
123DashboardInstrument::DashboardInstrument(wxWindow* pparent, wxWindowID id,
124 wxString title, DASH_CAP cap_flag,
125 InstrumentProperties* Properties)
126 : wxControl(pparent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) {
127 m_InstrumentSpacing = 0;
128 m_DataTextHeight = 0;
129 m_DataMargin = 0;
130 m_DataTop = -1;
131 m_TitleTop = 0;
132 m_DataRightAlign = false;
133 m_TitleRightAlign = false;
134 m_title = title;
135 m_Properties = Properties;
136 m_cap_flag.set(cap_flag);
137 m_popupWanted = false;
138
139 wxWindow::SetBackgroundStyle(wxBG_STYLE_CUSTOM);
140 SetDrawSoloInPane(false);
141 InitTitleSize();
142 wxWindow::Connect(
143 wxEVT_ERASE_BACKGROUND,
144 wxEraseEventHandler(DashboardInstrument::OnEraseBackground));
145 wxWindow::Connect(wxEVT_PAINT,
146 wxPaintEventHandler(DashboardInstrument::OnPaint));
147
148 // On OSX, there is an orphan mouse event that comes from the automatic
149 // exEVT_CONTEXT_MENU synthesis on the main wxWindow mouse handler.
150 // The event goes to an instrument window (here) that may have been deleted
151 // by the preferences dialog. Result is NULL deref. Solution: Handle
152 // right-click here, and DO NOT skip() Strangely, this does not work for
153 // GTK... See: http://trac.wxwidgets.org/ticket/15417
154
155#if defined(__WXOSX__) || defined(__WXQT__)
156 Connect(wxEVT_RIGHT_DOWN,
157 wxMouseEventHandler(DashboardInstrument::MouseEvent), NULL, this);
158#endif
159
160#ifdef HAVE_WX_GESTURE_EVENTS
161 if (!EnableTouchEvents(wxTOUCH_PRESS_GESTURES)) {
162 wxLogError("Failed to enable touch events on dashboard Instrument");
163 }
164
165 Bind(wxEVT_LONG_PRESS, &DashboardInstrument::OnLongPress, this);
166 Bind(wxEVT_LEFT_UP, &DashboardInstrument::OnLeftUp, this);
167#endif
168}
169
170#ifdef HAVE_WX_GESTURE_EVENTS
171void DashboardInstrument::OnLongPress(wxLongPressEvent& event) {
172 /* we defer the popup menu call upon the leftup event
173 else the menu disappears immediately,
174 */
175 m_popupWanted = true;
176}
177#endif
178
179void DashboardInstrument::OnLeftUp(wxMouseEvent& event) {
180 wxPoint pos = event.GetPosition();
181
182 if (!m_popupWanted) {
183 wxMouseEvent ev(wxEVT_LEFT_UP);
184 ev.m_x = pos.x;
185 ev.m_y = pos.y;
186 GetParent()->GetEventHandler()->AddPendingEvent(ev);
187 return;
188 }
189
190 m_popupWanted = false;
191 wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, this->GetId(),
192 this->ClientToScreen(event.GetPosition()));
193 evtCtx.SetEventObject(this);
194 GetParent()->GetEventHandler()->AddPendingEvent(evtCtx);
195}
196
197void DashboardInstrument::MouseEvent(wxMouseEvent& event) {
198 if (event.GetEventType() == wxEVT_RIGHT_DOWN) {
199 wxContextMenuEvent evtCtx(wxEVT_CONTEXT_MENU, this->GetId(),
200 this->ClientToScreen(event.GetPosition()));
201 evtCtx.SetEventObject(this);
202 GetParent()->GetEventHandler()->AddPendingEvent(evtCtx);
203 }
204}
205
206CapType DashboardInstrument::GetCapacity() { return m_cap_flag; }
207void DashboardInstrument::SetDrawSoloInPane(bool value) {
208 m_drawSoloInPane = value;
209}
210void DashboardInstrument::OnEraseBackground(wxEraseEvent& WXUNUSED(evt)) {
211 // intentionally empty
212}
213
214void DashboardInstrument::InitDataTextHeight(const wxString& sampleText,
215 int& sampleWidth) {
216 wxClientDC dc(this);
217 wxFont f;
218
219 if (m_Properties) {
220 f = m_Properties->m_DataFont.GetChosenFont();
221 } else {
222 f = g_pFontData->GetChosenFont();
223 }
224 dc.GetTextExtent(sampleText, &sampleWidth, &m_DataTextHeight, nullptr,
225 nullptr, &f);
226}
227
228void DashboardInstrument::InitTitleSize() {
229 wxClientDC dc(this);
230 wxFont f;
231 m_InstrumentSpacing = g_iInstrumentSpacing;
232
233 if (m_Properties) {
234 if (!m_Properties->m_Title.IsEmpty()) m_title = m_Properties->m_Title;
235 f = m_Properties->m_TitleFont.GetChosenFont();
236 if (m_Properties->m_InstrumentSpacing >= 0)
237 m_InstrumentSpacing = m_Properties->m_InstrumentSpacing;
238 } else {
239 f = g_pFontTitle->GetChosenFont();
240 }
241 dc.GetTextExtent(m_title, &m_TitleWidth, &m_TitleHeight, nullptr, nullptr,
242 &f);
243}
244
245void DashboardInstrument::InitTitleAndDataPosition(int drawHeight) {
246 m_DataRightAlign = (g_DataAlignment & wxALIGN_RIGHT) != 0;
247 m_DataMargin = g_iDataMargin;
248
249 if (m_Properties) {
250 if (m_Properties->m_DataAlignment != wxALIGN_INVALID)
251 m_DataRightAlign = (m_Properties->m_DataAlignment & wxALIGN_RIGHT) != 0;
252 if (m_Properties->m_DataMargin >= 0)
253 m_DataMargin = m_Properties->m_DataMargin;
254 }
255
256 m_TitleRightAlign = (g_TitleAlignment & wxALIGN_RIGHT) != 0;
257 m_TitleTop = static_cast<int>(m_DataTextHeight * g_TitleVerticalOffset);
258 m_DataTop = m_TitleHeight;
259 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
260 m_TitleTop = drawHeight + (m_DataTextHeight * g_TitleVerticalOffset);
261 m_DataTop = 0;
262 }
263}
264
265int DashboardInstrument::GetFullHeight(int drawHeight) {
266 int h = m_TitleTop + m_TitleHeight + drawHeight + m_InstrumentSpacing;
267 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
268 h = m_TitleTop + m_TitleHeight + m_InstrumentSpacing;
269 }
270
271 return h;
272}
273
274int DashboardInstrument::GetDataBottom(int clientHeight) {
275 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
276 return clientHeight - m_TitleHeight - m_InstrumentSpacing - 3;
277 }
278 return clientHeight - m_InstrumentSpacing;
279}
280
281void DashboardInstrument::SetDataFont(wxGCDC* dc) {
282 wxFont f;
283
284 if (m_Properties) {
285 f = m_Properties->m_DataFont.GetChosenFont();
286 dc->SetFont((f));
287 dc->SetTextForeground(
288 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()));
289 } else {
290 f = g_pFontData->GetChosenFont();
291 dc->SetFont((f));
292 dc->SetTextForeground(GetColourSchemeFont(g_pFontData->GetColour()));
293 }
294}
295
296void DashboardInstrument::OnPaint(wxPaintEvent& WXUNUSED(event)) {
297 wxAutoBufferedPaintDC pdc(this);
298 if (!pdc.IsOk()) {
299 wxLogMessage(
300 "DashboardInstrument::OnPaint() fatal: "
301 "wxAutoBufferedPaintDC.IsOk() false.");
302 return;
303 }
304
305 wxSize size = GetClientSize();
306 if (size.x == 0 || size.y == 0) {
307 wxLogMessage("DashboardInstrument::OnPaint() fatal: Zero size DC.");
308 return;
309 }
310
311#if wxUSE_GRAPHICS_CONTEXT
312 wxGCDC dc(pdc);
313#else
314 wxDC& dc(pdc);
315#endif
316 wxColour cl;
317 if (m_Properties) {
318 dc.SetBackground(
319 GetColourSchemeBackgroundColour(m_Properties->m_DataBackgroundColour));
320 } else {
321 GetGlobalColor("DASHB", &cl);
322 dc.SetBackground(cl);
323 }
324#ifdef __WXGTK__
325 dc.SetBrush(cl);
326 dc.SetPen(*wxTRANSPARENT_PEN);
327 dc.DrawRectangle(0, 0, size.x, size.y);
328#endif
329 dc.Clear();
330
331 Draw(&dc);
332
333 if (!m_drawSoloInPane) {
334 wxPen pen;
335 pen.SetStyle(wxPENSTYLE_SOLID);
336 if (m_Properties) {
337 pen.SetColour(GetColourSchemeBackgroundColour(
338 m_Properties->m_TitleBackgroundColour));
339 dc.SetPen(pen);
340 dc.SetBrush(GetColourSchemeBackgroundColour(
341 m_Properties->m_TitleBackgroundColour));
342 } else {
343 GetGlobalColor("DASHL", &cl);
344 pen.SetColour(cl);
345 dc.SetPen(pen);
346 dc.SetBrush(cl);
347 }
348
349 dc.DrawRoundedRectangle(0, m_TitleTop, size.x, m_TitleHeight, 3);
350 wxFont f;
351 if (m_Properties) {
352 f = m_Properties->m_TitleFont.GetChosenFont();
353 dc.SetFont(f);
354 dc.SetTextForeground(
355 GetColourSchemeFont(m_Properties->m_TitleFont.GetColour()));
356 dc.SetTextBackground(GetColourSchemeBackgroundColour(
357 m_Properties->m_TitleBackgroundColour));
358 } else {
359 f = g_pFontTitle->GetChosenFont();
360 dc.SetFont(f);
361 dc.SetTextForeground(GetColourSchemeFont(g_pFontTitle->GetColour()));
362 GetGlobalColor("DASHL", &cl);
363 dc.SetTextBackground(cl);
364 }
365 // GetGlobalColor("DASHF", &cl);
366 // dc.SetTextForeground(cl);
367 if (m_TitleRightAlign) {
368 dc.DrawText(m_title,
369 GetClientSize().GetWidth() - m_TitleWidth - g_iTitleMargin,
370 m_TitleTop);
371 } else {
372 dc.DrawText(m_title, g_iTitleMargin, m_TitleTop);
373 }
374 }
375}
376
377//----------------------------------------------------------------
378//
379// DashboardInstrument_Simple Implementation
380//
381//----------------------------------------------------------------
382
383DashboardInstrument_Single::DashboardInstrument_Single(
384 wxWindow* pparent, wxWindowID id, wxString title,
385 InstrumentProperties* Properties, DASH_CAP cap_flag, wxString format)
386 : DashboardInstrument(pparent, id, title, cap_flag, Properties) {
387 m_format = format;
388 m_data = "---";
389}
390
391wxSize DashboardInstrument_Single::GetSize(int orient, wxSize hint) {
392 InitTitleSize();
393 int w;
394 InitDataTextHeight("000", w);
395
396 int drawHeight =
397 static_cast<int>(m_DataTextHeight * (1 + g_TitleVerticalOffset));
398 InitTitleAndDataPosition(drawHeight);
399 int h = GetFullHeight(drawHeight);
400
401 if (orient == wxHORIZONTAL) {
402 return wxSize(wxMax(w + m_DataMargin, DefaultWidth), wxMax(hint.y, h));
403 } else {
404 return wxSize(wxMax(hint.x, wxMax(w + m_DataMargin, DefaultWidth)), h);
405 }
406}
407
408void DashboardInstrument_Single::Draw(wxGCDC* dc) {
409 SetDataFont(dc);
410
411 int x1;
412 if (m_DataMargin < 0)
413 m_DataMargin = m_TitleHeight; // Use default, if not initialized properly
414 x1 = m_DataMargin;
415
416 if (m_DataRightAlign) {
417 int w, h;
418 dc->GetTextExtent(m_data, &w, &h, nullptr, nullptr);
419 x1 = GetClientSize().GetWidth() - w - m_DataMargin;
420 }
421
422 dc->DrawText(m_data, x1, m_DataTop);
423}
424
425void DashboardInstrument_Single::SetData(DASH_CAP st, double data,
426 wxString unit) {
427 if (m_cap_flag.test(st)) {
428 if (!std::isnan(data)) {
429 bool showUnit =
430 (m_Properties ? (m_Properties->m_ShowUnit == 1) : g_bShowUnit);
431 wxString format =
432 (m_Properties && m_Properties->m_Format != "" ? m_Properties->m_Format
433 : m_format);
434 if (unit == "C")
435 m_data = wxString::Format(format, data) +
436 (showUnit ? DEGREE_SIGN + "C" : "");
437 else if (unit == "\u00B0")
438 m_data = wxString::Format(format, data) + (showUnit ? DEGREE_SIGN : "");
439 else if (unit == "\u00B0T")
440 m_data = wxString::Format(format, data) +
441 (showUnit ? DEGREE_SIGN + _(" true") : "");
442 else if (unit == "\u00B0M")
443 m_data = wxString::Format(format, data) +
444 (showUnit ? DEGREE_SIGN + _(" mag") : "");
445 else if (unit == "\u00B0L")
446 m_data = ">" + wxString::Format(format, data) +
447 (showUnit ? DEGREE_SIGN : "");
448 else if (unit == "\u00B0R")
449 m_data = wxString::Format(format, data) +
450 (showUnit ? DEGREE_SIGN + "<" : "");
451 else if (unit == "N") // Knots
452 m_data = wxString::Format(format, data) + (showUnit ? " Kts" : "");
453 /* maybe in the future ...
454 else if (unit == "M") // m/s
455 m_data = wxString::Format(m_format, data)+" m/s";
456 else if (unit == "K") // km/h
457 m_data = wxString::Format(m_format, data)+" km/h";
458 ... to be completed
459 */
460 else
461 m_data = wxString::Format(format, data) + (showUnit ? " " + unit : "");
462 } else
463 m_data = "---";
464
465 Refresh();
466 }
467}
468
469//----------------------------------------------------------------
470//
471// DashboardInstrument_Position Implementation
472//
473//----------------------------------------------------------------
474
475DashboardInstrument_Position::DashboardInstrument_Position(
476 wxWindow* pparent, wxWindowID id, wxString title,
477 InstrumentProperties* Properties, DASH_CAP cap_flag1, DASH_CAP cap_flag2)
478 : DashboardInstrument(pparent, id, title, cap_flag1, Properties) {
479 m_cap_flag.set(cap_flag2);
480
481 m_data1 = "---";
482 m_data2 = "---";
483 m_cap_flag1 = cap_flag1;
484 m_cap_flag2 = cap_flag2;
485}
486
487wxSize DashboardInstrument_Position::GetSize(int orient, wxSize hint) {
488 InitTitleSize();
489 int w;
490 InitDataTextHeight("000 00.0000 W", w);
491
492 int drawHeight = static_cast<int>(m_DataTextHeight * 2 +
493 m_DataTextHeight * g_TitleVerticalOffset);
494 InitTitleAndDataPosition(drawHeight);
495 int h = GetFullHeight(drawHeight);
496
497 if (orient == wxHORIZONTAL) {
498 return wxSize(wxMax(w + m_DataMargin, DefaultWidth), wxMax(hint.y, h));
499 } else {
500 return wxSize(wxMax(hint.x, wxMax(w + m_DataMargin, DefaultWidth)), h);
501 }
502}
503
504void DashboardInstrument_Position::Draw(wxGCDC* dc) {
505 SetDataFont(dc);
506
507 int x1, x2;
508 x1 = x2 = m_DataMargin;
509
510 if (m_DataRightAlign) {
511 int w, h;
512 dc->GetTextExtent(m_data1, &w, &h, nullptr, nullptr);
513 x1 = GetClientSize().GetWidth() - w - m_DataMargin;
514 dc->GetTextExtent(m_data2, &w, &h, nullptr, nullptr);
515 x2 = GetClientSize().GetWidth() - w - m_DataMargin;
516 }
517
518 dc->DrawText(m_data1, x1, m_DataTop);
519 dc->DrawText(m_data2, x2, m_DataTop + m_DataTextHeight);
520}
521
522void DashboardInstrument_Position::SetData(DASH_CAP st, double data,
523 wxString unit) {
524 if (std::isnan(data)) return;
525 if (st == m_cap_flag1) {
526 m_data1 = toSDMM(1, data);
527 m_data1[0] = ' ';
528 } else if (st == m_cap_flag2) {
529 m_data2 = toSDMM(2, data);
530 } else
531 return;
532 Refresh();
533}
534
535/**************************************************************************/
536/* Some assorted utilities */
537/**************************************************************************/
538
539wxString toSDMM(int NEflag, double a) {
540 short neg = 0;
541 int d;
542 long m;
543
544 if (a < 0.0) {
545 a = -a;
546 neg = 1;
547 }
548 d = (int)a;
549 m = (long)((a - (double)d) * 60000.0);
550
551 if (neg) d = -d;
552
553 wxString s;
554
555 if (!NEflag)
556 s.Printf("%d %02ld.%03ld'", d, m / 1000, m % 1000);
557 else {
558 if (NEflag == 1) {
559 char c = 'N';
560
561 if (neg) {
562 d = -d;
563 c = 'S';
564 }
565
566 s.Printf("%03d %02ld.%03ld %c", d, m / 1000, (m % 1000), c);
567 } else if (NEflag == 2) {
568 char c = 'E';
569
570 if (neg) {
571 d = -d;
572 c = 'W';
573 }
574 s.Printf("%03d %02ld.%03ld %c", d, m / 1000, (m % 1000), c);
575 }
576 }
577 return s;
578}
Dashboard instrument base class.
DASH_CAP
Definition instrument.h:77
PI_ColorScheme
Color schemes for different lighting conditions.
@ PI_GLOBAL_COLOR_SCHEME_DAY
Day color scheme, optimized for bright ambient light.
@ PI_GLOBAL_COLOR_SCHEME_NIGHT
Night/dark color scheme, optimized for dark conditions with minimal impact on night vision.
@ PI_GLOBAL_COLOR_SCHEME_RGB
RGB color scheme, unmodified colors.
@ PI_GLOBAL_COLOR_SCHEME_DUSK
Dusk color scheme, optimized for low ambient light.