OpenCPN Partial API docs
Loading...
Searching...
No Matches
instrument.cpp
1/******************************************************************************
2 * $Id: instrument.cpp, v1.0 2010/08/30 SethDart Exp $
3 *
4 * Project: OpenCPN
5 * Purpose: Dashboard Plugin
6 * Author: Jean-Eudes Onfray
7 *
8 ***************************************************************************
9 * Copyright (C) 2010 by David S. Register *
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This program is distributed in the hope that it will be useful, *
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
19 * GNU General Public License for more details. *
20 * *
21 * You should have received a copy of the GNU General Public License *
22 * along with this program; if not, write to the *
23 * Free Software Foundation, Inc., *
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
25 ***************************************************************************
26 */
27
28#include "wx/wxprec.h"
29
30#ifndef WX_PRECOMP
31#include "wx/wx.h"
32#endif // precompiled headers
33#include <cmath>
34
35#include "instrument.h"
36
37#ifdef __OCPN__ANDROID__
38#include "qdebug.h"
39#endif
40
41// ColorScheme ... Try only make a simple darker in Night mode. this could be
42// made better.
43extern PI_ColorScheme aktuellColorScheme;
44
45wxColour GetColourSchemeBackgroundColour(wxColour co) {
46 wxColour ret_val = co;
47#if wxCHECK_VERSION(3, 1, 6)
48 unsigned int red = co.GetRed();
49 unsigned int Green = co.GetGreen();
50 unsigned int Blue = co.GetBlue();
51#else
52 unsigned int red = co.Red();
53 unsigned int Green = co.Green();
54 unsigned int Blue = co.Blue();
55#endif
56 switch (aktuellColorScheme) {
57 case PI_GLOBAL_COLOR_SCHEME_RGB:
58 break;
59 case PI_GLOBAL_COLOR_SCHEME_DAY:
60 break;
61 case PI_GLOBAL_COLOR_SCHEME_DUSK:
62 red *= .8;
63 Green *= .8;
64 Blue *= .8;
65 ret_val = wxColour(red, Green, Blue);
66 break;
67 case PI_GLOBAL_COLOR_SCHEME_NIGHT:
68 red *= .5;
69 Green *= .5;
70 Blue *= .5;
71 ret_val = wxColour(red, Green, Blue);
72 break;
73 default:
74 break;
75 }
76 return ret_val;
77}
78
79wxColour GetColourSchemeFont(wxColour co) {
80 wxColour ret_val = co;
81#if wxCHECK_VERSION(3, 1, 6)
82 unsigned int red = co.GetRed();
83 unsigned int Green = co.GetGreen();
84 unsigned int Blue = co.GetBlue();
85#else
86 unsigned int red = co.Red();
87 unsigned int Green = co.Green();
88 unsigned int Blue = co.Blue();
89#endif
90 switch (aktuellColorScheme) {
91 case PI_GLOBAL_COLOR_SCHEME_RGB:
92 break;
93 case PI_GLOBAL_COLOR_SCHEME_DAY:
94 break;
95 case PI_GLOBAL_COLOR_SCHEME_DUSK:
96 red *= .8;
97 Green *= .8;
98 Blue *= .8;
99 // if (red + Green + Blue < 10) {
100 // red = Green = Blue = 50;
101 // }
102 ret_val = wxColour(red, Green, Blue);
103 break;
104 case PI_GLOBAL_COLOR_SCHEME_NIGHT:
105 red *= .5;
106 Green *= .5;
107 Blue *= .5;
108 if (red + Green + Blue < 10) {
109 red = Green = Blue = 50;
110 }
111 ret_val = wxColour(red, Green, Blue);
112 break;
113 default:
114 break;
115 }
116 return ret_val;
117}
118
119//----------------------------------------------------------------
120//
121// Generic DashboardInstrument Implementation
122//
123//----------------------------------------------------------------
124
125DashboardInstrument::DashboardInstrument(wxWindow* pparent, wxWindowID id,
126 wxString title, DASH_CAP cap_flag,
127 InstrumentProperties* Properties)
128 : wxControl(pparent, id, wxDefaultPosition, wxDefaultSize, wxBORDER_NONE) {
129 m_InstrumentSpacing = 0;
130 m_DataTextHeight = 0;
131 m_DataMargin = 0;
132 m_DataTop = -1;
133 m_TitleTop = 0;
134 m_DataRightAlign = false;
135 m_TitleRightAlign = false;
136 m_title = title;
137 m_Properties = Properties;
138 m_cap_flag.set(cap_flag);
139 m_popupWanted = false;
140
141 SetBackgroundStyle(wxBG_STYLE_CUSTOM);
142 SetDrawSoloInPane(false);
143 InitTitleSize();
144 Connect(wxEVT_ERASE_BACKGROUND,
145 wxEraseEventHandler(DashboardInstrument::OnEraseBackground));
146 Connect(wxEVT_PAINT, 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 int w;
219
220 if (m_Properties) {
221 f = m_Properties->m_DataFont.GetChosenFont();
222 } else {
223 f = g_pFontData->GetChosenFont();
224 }
225 dc.GetTextExtent(sampleText, &sampleWidth, &m_DataTextHeight, 0, 0, &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, 0, 0, &f);
242}
243
244void DashboardInstrument::InitTitleAndDataPosition(int drawHeight) {
245 m_DataRightAlign = (g_DataAlignment & wxALIGN_RIGHT) != 0;
246 m_DataMargin = g_iDataMargin;
247
248 if (m_Properties) {
249 if (m_Properties->m_DataAlignment != wxALIGN_INVALID)
250 m_DataRightAlign = (m_Properties->m_DataAlignment & wxALIGN_RIGHT) != 0;
251 if (m_Properties->m_DataMargin >= 0)
252 m_DataMargin = m_Properties->m_DataMargin;
253 }
254
255 m_TitleRightAlign = (g_TitleAlignment & wxALIGN_RIGHT) != 0;
256 m_TitleTop = m_DataTextHeight * g_TitleVerticalOffset;
257 m_DataTop = m_TitleHeight;
258 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
259 m_TitleTop = drawHeight + (m_DataTextHeight * g_TitleVerticalOffset);
260 m_DataTop = 0;
261 }
262}
263
264int DashboardInstrument::GetFullHeight(int drawHeight) {
265 int h = m_TitleTop + m_TitleHeight + drawHeight + m_InstrumentSpacing;
266 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
267 h = m_TitleTop + m_TitleHeight + m_InstrumentSpacing;
268 }
269
270 return h;
271}
272
273int DashboardInstrument::GetDataBottom(int clientHeight) {
274 if ((g_TitleAlignment & wxALIGN_BOTTOM) != 0) {
275 return clientHeight - m_TitleHeight - m_InstrumentSpacing - 3;
276 }
277 return clientHeight - m_InstrumentSpacing;
278}
279
280void DashboardInstrument::SetDataFont(wxGCDC* dc) {
281 wxFont f;
282
283 if (m_Properties) {
284 f = m_Properties->m_DataFont.GetChosenFont();
285 dc->SetFont((f));
286 dc->SetTextForeground(
287 GetColourSchemeFont(m_Properties->m_DataFont.GetColour()));
288 } else {
289 f = g_pFontData->GetChosenFont();
290 dc->SetFont((f));
291 dc->SetTextForeground(GetColourSchemeFont(g_pFontData->GetColour()));
292 }
293}
294
295void DashboardInstrument::OnPaint(wxPaintEvent& WXUNUSED(event)) {
296 wxAutoBufferedPaintDC pdc(this);
297 if (!pdc.IsOk()) {
298 wxLogMessage(
299 _T("DashboardInstrument::OnPaint() fatal: ")
300 _T("wxAutoBufferedPaintDC.IsOk() false."));
301 return;
302 }
303
304 wxSize size = GetClientSize();
305 if (size.x == 0 || size.y == 0) {
306 wxLogMessage(_T("DashboardInstrument::OnPaint() fatal: Zero size DC."));
307 return;
308 }
309
310#if wxUSE_GRAPHICS_CONTEXT
311 wxGCDC dc(pdc);
312#else
313 wxDC& dc(pdc);
314#endif
315 wxColour cl;
316 if (m_Properties) {
317 dc.SetBackground(
318 GetColourSchemeBackgroundColour(m_Properties->m_DataBackgroundColour));
319 } else {
320 GetGlobalColor(_T("DASHB"), &cl);
321 dc.SetBackground(cl);
322 }
323#ifdef __WXGTK__
324 dc.SetBrush(cl);
325 dc.SetPen(*wxTRANSPARENT_PEN);
326 dc.DrawRectangle(0, 0, size.x, size.y);
327#endif
328 dc.Clear();
329
330 Draw(&dc);
331
332 if (!m_drawSoloInPane) {
333 wxPen pen;
334 pen.SetStyle(wxPENSTYLE_SOLID);
335 if (m_Properties) {
336 pen.SetColour(GetColourSchemeBackgroundColour(
337 m_Properties->m_TitleBackgroundColour));
338 dc.SetPen(pen);
339 dc.SetBrush(GetColourSchemeBackgroundColour(
340 m_Properties->m_TitleBackgroundColour));
341 } else {
342 GetGlobalColor(_T("DASHL"), &cl);
343 pen.SetColour(cl);
344 dc.SetPen(pen);
345 dc.SetBrush(cl);
346 }
347
348 dc.DrawRoundedRectangle(0, m_TitleTop, size.x, m_TitleHeight, 3);
349 wxFont f;
350 if (m_Properties) {
351 f = m_Properties->m_TitleFont.GetChosenFont();
352 dc.SetFont(f);
353 dc.SetTextForeground(
354 GetColourSchemeFont(m_Properties->m_TitleFont.GetColour()));
355 dc.SetTextBackground(GetColourSchemeBackgroundColour(
356 m_Properties->m_TitleBackgroundColour));
357 } else {
358 f = g_pFontTitle->GetChosenFont();
359 dc.SetFont(f);
360 dc.SetTextForeground(GetColourSchemeFont(g_pFontTitle->GetColour()));
361 GetGlobalColor(_T("DASHL"), &cl);
362 dc.SetTextBackground(cl);
363 }
364 // GetGlobalColor(_T("DASHF"), &cl);
365 // dc.SetTextForeground(cl);
366 if (m_TitleRightAlign) {
367 dc.DrawText(m_title,
368 GetClientSize().GetWidth() - m_TitleWidth - g_iTitleMargin,
369 m_TitleTop);
370 } else {
371 dc.DrawText(m_title, g_iTitleMargin, m_TitleTop);
372 }
373 }
374}
375
376//----------------------------------------------------------------
377//
378// DashboardInstrument_Simple Implementation
379//
380//----------------------------------------------------------------
381
382DashboardInstrument_Single::DashboardInstrument_Single(
383 wxWindow* pparent, wxWindowID id, wxString title,
384 InstrumentProperties* Properties, DASH_CAP cap_flag, wxString format)
385 : DashboardInstrument(pparent, id, title, cap_flag, Properties) {
386 m_format = format;
387 m_data = _T("---");
388}
389
390wxSize DashboardInstrument_Single::GetSize(int orient, wxSize hint) {
391 InitTitleSize();
392 int w;
393 InitDataTextHeight(_T("000"), w);
394
395 int drawHeight = m_DataTextHeight * (1 + g_TitleVerticalOffset);
396 InitTitleAndDataPosition(drawHeight);
397 int h = GetFullHeight(drawHeight);
398
399 if (orient == wxHORIZONTAL) {
400 return wxSize(wxMax(w + m_DataMargin, DefaultWidth), wxMax(hint.y, h));
401 } else {
402 return wxSize(wxMax(hint.x, wxMax(w + m_DataMargin, DefaultWidth)), h);
403 }
404}
405
406void DashboardInstrument_Single::Draw(wxGCDC* dc) {
407 SetDataFont(dc);
408
409 int x1;
410 if (m_DataMargin < 0)
411 m_DataMargin = m_TitleHeight; // Use default, if not initialized properly
412 x1 = m_DataMargin;
413
414 if (m_DataRightAlign) {
415 int w, h;
416 dc->GetTextExtent(m_data, &w, &h, 0, 0);
417 x1 = GetClientSize().GetWidth() - w - m_DataMargin;
418 }
419
420 dc->DrawText(m_data, x1, m_DataTop);
421}
422
423void DashboardInstrument_Single::SetData(DASH_CAP st, double data,
424 wxString unit) {
425 if (m_cap_flag.test(st)) {
426 if (!std::isnan(data)) {
427 bool showUnit =
428 (m_Properties ? (m_Properties->m_ShowUnit == 1) : g_bShowUnit);
429 wxString format =
430 (m_Properties && m_Properties->m_Format != "" ? m_Properties->m_Format
431 : m_format);
432 if (unit == _T("C"))
433 m_data = wxString::Format(format, data) +
434 (showUnit ? DEGREE_SIGN + _T("C") : "");
435 else if (unit == _T("\u00B0"))
436 m_data = wxString::Format(format, data) + (showUnit ? DEGREE_SIGN : "");
437 else if (unit == _T("\u00B0T"))
438 m_data = wxString::Format(format, data) +
439 (showUnit ? DEGREE_SIGN + _(" true") : "");
440 else if (unit == _T("\u00B0M"))
441 m_data = wxString::Format(format, data) +
442 (showUnit ? DEGREE_SIGN + _(" mag") : "");
443 else if (unit == _T("\u00B0L"))
444 m_data = _T(">") + wxString::Format(format, data) +
445 (showUnit ? DEGREE_SIGN : "");
446 else if (unit == _T("\u00B0R"))
447 m_data = wxString::Format(format, data) +
448 (showUnit ? DEGREE_SIGN + _T("<") : "");
449 else if (unit == _T("N")) // Knots
450 m_data =
451 wxString::Format(format, data) + (showUnit ? _T(" Kts") : _T(""));
452 /* maybe in the future ...
453 else if (unit == _T("M")) // m/s
454 m_data = wxString::Format(m_format, data)+_T(" m/s");
455 else if (unit == _T("K")) // km/h
456 m_data = wxString::Format(m_format, data)+_T(" km/h");
457 ... to be completed
458 */
459 else
460 m_data =
461 wxString::Format(format, data) + (showUnit ? _T(" ") + unit : "");
462 } else
463 m_data = _T("---");
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 = _T("---");
482 m_data2 = _T("---");
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(_T("000 00.0000 W"), w);
491
492 int drawHeight =
493 m_DataTextHeight * 2 + 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, 0, 0);
513 x1 = GetClientSize().GetWidth() - w - m_DataMargin;
514 dc->GetTextExtent(m_data2, &w, &h, 0, 0);
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(_T ( "%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(_T ( "%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(_T ( "%03d %02ld.%03ld %c" ), d, m / 1000, (m % 1000), c);
575 }
576 }
577 return s;
578}