OpenCPN Partial API docs
Loading...
Searching...
No Matches
TCWin.cpp
1// For compilers that support precompilation, includes "wx.h".
2#include <wx/wxprec.h>
3
4#include <wx/listctrl.h>
5#include <wx/choice.h>
6
7#include "TCWin.h"
8#include "timers.h"
9#include "chcanv.h"
10#include "tide_time.h"
11#include "tcmgr.h"
12#include "dychart.h"
13#include "model/cutil.h"
14#include "FontMgr.h"
15#include "model/wx28compat.h"
16#include "OCPNPlatform.h"
17#include "RolloverWin.h"
18#include "navutil.h"
19#include "gui_lib.h"
20#include "ocpn_frame.h"
21
22extern ColorScheme global_color_scheme;
23extern int gpIDXn;
24extern TCMgr *ptcmgr;
25extern wxString g_locale;
26extern OCPNPlatform *g_Platform;
27extern MyConfig *pConfig;
28extern OCPNPlatform *g_Platform;
29
30int g_tcwin_scale;
31
32enum { ID_TCWIN_NX, ID_TCWIN_PR };
33
34enum { TIDE_PLOT, CURRENT_PLOT };
35
36#include <wx/listimpl.cpp>
37WX_DEFINE_LIST(SplineList);
38
39BEGIN_EVENT_TABLE(TCWin, wxWindow)
40EVT_PAINT(TCWin::OnPaint)
41EVT_SIZE(TCWin::OnSize)
42EVT_MOTION(TCWin::MouseEvent)
43EVT_BUTTON(wxID_OK, TCWin::OKEvent)
44EVT_BUTTON(ID_TCWIN_NX, TCWin::NXEvent)
45EVT_BUTTON(ID_TCWIN_PR, TCWin::PREvent)
46EVT_CLOSE(TCWin::OnCloseWindow)
47EVT_TIMER(TCWININF_TIMER, TCWin::OnTCWinPopupTimerEvent)
48EVT_TIMER(TCWIN_TIME_INDICATOR_TIMER, TCWin::OnTimeIndicatorTimer)
49END_EVENT_TABLE()
50
51// Define a constructor
52extern wxDateTime gTimeSource;
53TCWin::TCWin(ChartCanvas *parent, int x, int y, void *pvIDX) {
54 m_created = false;
55 xSpot = 0;
56 ySpot = 0;
57
58 m_pTCRolloverWin = NULL;
59
60 long wstyle = wxCLIP_CHILDREN | wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER |
61 wxFRAME_FLOAT_ON_PARENT;
62
63 pParent = parent;
64 m_x = x;
65 m_y = y;
66
67 RecalculateSize();
68
69 // Read the config file to get the user specified time zone.
70 if (pConfig) {
71 pConfig->SetPath(_T ( "/Settings/Others" ));
72 pConfig->Read(_T ( "TCWindowTimeZone" ), &m_tzoneDisplay, 0);
73 }
74
75 wxFrame::Create(parent, wxID_ANY, wxString(_T ( "" )), m_position, m_tc_size,
76 wstyle);
77
78 m_created = true;
79 wxFont *qFont = GetOCPNScaledFont(_("Dialog"));
80 SetFont(*qFont);
81
82 pIDX = (IDX_entry *)pvIDX;
83
84 // Set up plot type
85 if (strchr("Tt", pIDX->IDX_type)) {
86 m_plot_type = TIDE_PLOT;
87 SetTitle(wxString(_("Tide")));
88
89 } else {
90 m_plot_type = CURRENT_PLOT;
91 SetTitle(wxString(_("Current")));
92 }
93
94 int sx, sy;
95 GetClientSize(&sx, &sy);
96
97 SetTimeFactors();
98
99 btc_valid = false;
100
101 wxString *TClist = NULL;
102 m_tList = new wxListCtrl(this, -1, wxPoint(sx * 65 / 100, 11),
103 wxSize((sx * 32 / 100), (sy * 20 / 100)),
104 wxLC_REPORT | wxLC_NO_HEADER);
105
106 // Add first column
107 wxListItem col0;
108 col0.SetId(0);
109 col0.SetText(_T(""));
110 col0.SetAlign(wxLIST_FORMAT_LEFT);
111 col0.SetWidth(sx * 30 / 100);
112 m_tList->InsertColumn(0, col0);
113
114 // Measure the size of a generic button, with label
115 wxButton *test_button =
116 new wxButton(this, wxID_OK, _("OK"), wxPoint(-1, -1), wxDefaultSize);
117 test_button->GetSize(&m_tsx, &m_tsy);
118 delete test_button;
119
120 // In the interest of readability, if the width of the dialog is too narrow,
121 // simply skip showing the "Hi/Lo" list control.
122
123 if ((m_tsy * 15) > sx) m_tList->Hide();
124
125 OK_button = new wxButton(this, wxID_OK, _("OK"),
126 wxPoint(sx - (2 * m_tsy + 10), sy - (m_tsy + 10)),
127 wxDefaultSize);
128
129 PR_button = new wxButton(this, ID_TCWIN_PR, _("Prev"),
130 wxPoint(10, sy - (m_tsy + 10)), wxSize(-1, -1));
131
132 wxSize texc_size = wxSize((sx * 60 / 100), (sy * 29 / 100));
133 if (!m_tList->IsShown()) {
134 texc_size = wxSize((sx * 90 / 100), (sy * 29 / 100));
135 }
136
137 m_ptextctrl =
138 new wxTextCtrl(this, -1, _T(""), wxPoint(sx * 3 / 100, 6), texc_size,
139 wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP);
140 int bsx, bsy, bpx, bpy;
141 PR_button->GetSize(&bsx, &bsy);
142 PR_button->GetPosition(&bpx, &bpy);
143
144 NX_button =
145 new wxButton(this, ID_TCWIN_NX, _("Next"),
146 wxPoint(bpx + bsx + 5, sy - (m_tsy + 10)), wxSize(-1, -1));
147
148 wxString m_choiceTimezoneChoices[] = {_("LMT@Station"), _("UTC")};
149 int m_choiceTimezoneNChoices =
150 sizeof(m_choiceTimezoneChoices) / sizeof(wxString);
151 m_choiceTimezone = new wxChoice(
152 this, wxID_ANY, wxPoint((sx - (bsx * 2)) / 2, sy - (m_tsy * 12 / 10)),
153 wxSize(2 * bsx, bsy), m_choiceTimezoneNChoices, m_choiceTimezoneChoices,
154 0);
155 m_choiceTimezone->SetToolTip(
156 _("Select whether tide times are shown in UTC or "
157 "Local Mean Time (LMT) at the station"));
158 m_choiceSize_x = bsx * 2;
159
160 m_choiceTimezone->SetSelection(m_tzoneDisplay);
161 m_choiceTimezone->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
162 wxCommandEventHandler(TCWin::TimezoneOnChoice),
163 NULL, this);
164
165 m_TCWinPopupTimer.SetOwner(this, TCWININF_TIMER);
166
167 // Timer for refreshing time indicators (red line moves with current time)
168 m_TimeIndicatorTimer.SetOwner(this, TCWIN_TIME_INDICATOR_TIMER);
169 m_TimeIndicatorTimer.Start(60000, false); // Refresh every 60 seconds
170
171 wxScreenDC dc;
172 int text_height;
173 dc.SetFont(*qFont);
174 dc.GetTextExtent(_T("W"), NULL, &text_height);
175 m_refTextHeight = text_height;
176 m_button_height = m_tsy;
177
178 // Build graphics tools
179
180 wxFont *dlg_font = FontMgr::Get().GetFont(_("Dialog"));
181 int dlg_font_size = dlg_font->GetPointSize();
182#if defined(__WXOSX__) || defined(__WXGTK3__)
183 // Support scaled HDPI displays.
184 dlg_font_size /= GetContentScaleFactor();
185#endif
186
187 pSFont = FontMgr::Get().FindOrCreateFont(
188 dlg_font_size - 2, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
189 wxFONTWEIGHT_NORMAL, FALSE, wxString(_T ( "Arial" )));
190 pSMFont = FontMgr::Get().FindOrCreateFont(
191 dlg_font_size - 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
192 wxFONTWEIGHT_NORMAL, FALSE, wxString(_T ( "Arial" )));
193 pMFont = FontMgr::Get().FindOrCreateFont(
194 dlg_font_size, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD,
195 FALSE, wxString(_T ( "Arial" )));
196 pLFont = FontMgr::Get().FindOrCreateFont(
197 dlg_font_size + 1, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL,
198 wxFONTWEIGHT_BOLD, FALSE, wxString(_T ( "Arial" )));
199
200 // Secondary grid
201 pblack_1 = wxThePenList->FindOrCreatePen(
202 this->GetForegroundColour(), wxMax(1, (int)(m_tcwin_scaler + 0.5)),
203 wxPENSTYLE_SOLID);
204 // Primary grid
205 pblack_2 = wxThePenList->FindOrCreatePen(
206 this->GetForegroundColour(), wxMax(2, (int)(2 * m_tcwin_scaler + 0.5)),
207 wxPENSTYLE_SOLID);
208 // Tide hours outline
209 pblack_3 = wxThePenList->FindOrCreatePen(
210 wxSystemSettings::GetColour(wxSYS_COLOUR_BTNSHADOW),
211 wxMax(1, (int)(m_tcwin_scaler + 0.5)), wxPENSTYLE_SOLID);
212 // System time vertical line
213 pred_2 = wxThePenList->FindOrCreatePen(
214 wxColor(230, 54, 54), wxMax(4, (int)(4 * m_tcwin_scaler + 0.5)),
215 wxPENSTYLE_SOLID);
216 // Selected time vertical line (from GRIB plugin or timeline widget)
217 pred_time = wxThePenList->FindOrCreatePen(
218 wxColour(0, 100, 255), wxMax(4, (int)(4 * m_tcwin_scaler + 0.5)),
219 wxPENSTYLE_DOT);
220 // Graph background
221 pltgray = wxTheBrushList->FindOrCreateBrush(this->GetBackgroundColour(),
222 wxBRUSHSTYLE_SOLID);
223 // Tide hours background
224 pltgray2 = wxTheBrushList->FindOrCreateBrush(this->GetBackgroundColour(),
225 wxBRUSHSTYLE_SOLID);
226 pgraph = wxThePenList->FindOrCreatePen(
227 wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT),
228 wxMax(1, (int)(m_tcwin_scaler + 0.5)), wxPENSTYLE_SOLID);
229
230 DimeControl(this);
231
232 // Fill in some static text control information
233
234 // Tide station information
235 m_ptextctrl->Clear();
236
237 wxString locn(pIDX->IDX_station_name, wxConvUTF8);
238 wxString locna, locnb;
239 if (locn.Contains(wxString(_T ( "," )))) {
240 locna = locn.BeforeFirst(',');
241 locnb = locn.AfterFirst(',');
242 } else {
243 locna = locn;
244 locnb.Empty();
245 }
246
247 // write the first line
248 wxTextAttr style;
249 style.SetFont(*pLFont);
250 m_ptextctrl->SetDefaultStyle(style);
251
252 m_ptextctrl->AppendText(locna);
253 m_ptextctrl->AppendText(_T("\n"));
254
255 style.SetFont(*pSMFont);
256 m_ptextctrl->SetDefaultStyle(style);
257
258 if (!locnb.IsEmpty()) m_ptextctrl->AppendText(locnb);
259 m_ptextctrl->AppendText(_T("\n"));
260
261 // Reference to the master station
262 if (('t' == pIDX->IDX_type) || ('c' == pIDX->IDX_type)) {
263 wxString mref(pIDX->IDX_reference_name, wxConvUTF8);
264 mref.Prepend(_T(" "));
265
266 m_ptextctrl->AppendText(_("Reference Station :"));
267 m_ptextctrl->AppendText(_T("\n"));
268
269 m_ptextctrl->AppendText(mref);
270 m_ptextctrl->AppendText(_T("\n"));
271
272 } else {
273 m_ptextctrl->AppendText(_T("\n"));
274 }
275
276 // Show the data source
277 wxString dsource(pIDX->source_ident, wxConvUTF8);
278 dsource.Prepend(_T(" "));
279
280 m_ptextctrl->AppendText(_("Data Source :"));
281 m_ptextctrl->AppendText(_T("\n"));
282
283 m_ptextctrl->AppendText(dsource);
284
285 m_ptextctrl->ShowPosition(0);
286}
287
288TCWin::~TCWin() {
289 m_TimeIndicatorTimer.Stop();
290 pParent->Refresh(false);
291}
292
293void TCWin::SetTimeFactors() {
294 // Figure out this computer timezone minute offset
295 wxDateTime this_now = gTimeSource;
296 bool cur_time = !gTimeSource.IsValid();
297
298 if (cur_time) {
299 this_now = wxDateTime::Now();
300 }
301 wxDateTime this_gmt = this_now.ToGMT();
302
303#if wxCHECK_VERSION(2, 6, 2)
304 wxTimeSpan diff = this_now.Subtract(this_gmt);
305#else
306 wxTimeSpan diff = this_gmt.Subtract(this_now);
307#endif
308
309 m_diff_mins = diff.GetMinutes();
310
311 // Correct a bug in wx3.0.2
312 // If the system TZ happens to be GMT, with DST active (e.g.summer in
313 // London), then wxDateTime returns incorrect results for toGMT() method
314#if wxCHECK_VERSION(3, 0, 2)
315 if (m_diff_mins == 0 && this_now.IsDST()) m_diff_mins += 60;
316#endif
317
318 int station_offset = ptcmgr->GetStationTimeOffset(pIDX);
319
320 m_stationOffset_mins = station_offset;
321 if (this_now.IsDST()) {
322 m_stationOffset_mins += 60;
323 }
324
325 // Correct a bug in wx3.0.2
326 // If the system TZ happens to be GMT, with DST active (e.g.summer in
327 // London), then wxDateTime returns incorrect results for toGMT() method
328#if wxCHECK_VERSION(3, 0, 2)
329// if( this_now.IsDST() )
330// m_corr_mins +=60;
331#endif
332
333 // Establish the inital drawing day as today, in the timezone of the
334 // station
335 m_graphday = this_gmt;
336
337 int day_gmt = this_gmt.GetDayOfYear();
338
339 time_t ttNow = this_now.GetTicks();
340 time_t tt_at_station =
341 ttNow - (m_diff_mins * 60) + (m_stationOffset_mins * 60);
342 wxDateTime atStation(tt_at_station);
343 int day_at_station = atStation.GetDayOfYear();
344
345 if (day_gmt > day_at_station) {
346 wxTimeSpan dt(24, 0, 0, 0);
347 m_graphday.Subtract(dt);
348 } else if (day_gmt < day_at_station) {
349 wxTimeSpan dt(24, 0, 0, 0);
350 m_graphday.Add(dt);
351 }
352
353 wxDateTime graphday_00 = m_graphday; // this_gmt;
354 graphday_00.ResetTime();
355 time_t t_graphday_00 = graphday_00.GetTicks();
356
357 // Correct a Bug in wxWidgets time support
358 // if( !graphday_00.IsDST() && m_graphday.IsDST() ) t_graphday_00 -= 3600;
359 // if( graphday_00.IsDST() && !m_graphday.IsDST() ) t_graphday_00 += 3600;
360
361 m_t_graphday_GMT = t_graphday_00;
362
363 btc_valid = false; // Force re-calculation
364}
365
366void TCWin::TimezoneOnChoice(wxCommandEvent &event) {
367 m_tzoneDisplay = m_choiceTimezone->GetSelection();
368 SetTimeFactors();
369
370 Refresh();
371}
372
373void TCWin::RecalculateSize() {
374 wxSize parent_size(2000, 2000);
375 if (pParent) parent_size = pParent->GetClientSize();
376
377 int unscaledheight = 600;
378 int unscaledwidth = 650;
379
380 // value of m_tcwin_scaler should be about unity on a 100 dpi display,
381 // when scale parameter g_tcwin_scale is 100
382 // parameter g_tcwin_scale is set in config file as value of
383 // TideCurrentWindowScale
384 g_tcwin_scale = wxMax(g_tcwin_scale, 10); // sanity check on g_tcwin_scale
385 m_tcwin_scaler = g_Platform->GetDisplayDPmm() * 0.254 * g_tcwin_scale / 100.0;
386
387 m_tc_size.x = (int)(unscaledwidth * m_tcwin_scaler + 0.5);
388 m_tc_size.y = (int)(unscaledheight * m_tcwin_scaler + 0.5);
389
390 m_tc_size.x = wxMin(m_tc_size.x, parent_size.x);
391 m_tc_size.y = wxMin(m_tc_size.y, parent_size.y);
392
393 int xc = m_x + 8;
394 int yc = m_y;
395
396 // Arrange for tcWindow to be always totally visible
397 // by shifting left and/or up
398 if ((m_x + 8 + m_tc_size.x) > parent_size.x) xc = xc - m_tc_size.x - 16;
399 if ((m_y + m_tc_size.y) > parent_size.y) yc = yc - m_tc_size.y;
400
401 // Don't let the window origin move out of client area
402 if (yc < 0) yc = 0;
403 if (xc < 0) xc = 0;
404
405 if (pParent) pParent->ClientToScreen(&xc, &yc);
406 m_position = wxPoint(xc, yc);
407
408 if (m_created) {
409 SetSize(m_tc_size);
410 Move(m_position);
411 }
412}
413
414void TCWin::OKEvent(wxCommandEvent &event) {
415 Hide();
416 pParent->pCwin = NULL;
417 --gpIDXn;
418 delete m_pTCRolloverWin;
419 delete m_tList;
420 pParent->Refresh(false);
421
422 // Update the config file to set the user specified time zone.
423 if (pConfig) {
424 pConfig->SetPath(_T ( "/Settings/Others" ));
425 pConfig->Write(_T ( "TCWindowTimeZone" ), m_tzoneDisplay);
426 }
427
428 Destroy(); // that hurts
429}
430
431void TCWin::OnCloseWindow(wxCloseEvent &event) {
432 Hide();
433 pParent->pCwin = NULL;
434 --gpIDXn;
435 delete m_pTCRolloverWin;
436 delete m_tList;
437
438 // Update the config file to set the user specified time zone.
439 if (pConfig) {
440 pConfig->SetPath(_T ( "/Settings/Others" ));
441 pConfig->Write(_T ( "TCWindowTimeZone" ), m_tzoneDisplay);
442 }
443
444 Destroy(); // that hurts
445}
446
447void TCWin::NXEvent(wxCommandEvent &event) {
448 wxTimeSpan dt(24, 0, 0, 0);
449 m_graphday.Add(dt);
450 wxDateTime dm = m_graphday;
451
452 wxDateTime graphday_00 = dm.ResetTime();
453 time_t t_graphday_00 = graphday_00.GetTicks();
454
455 if (!graphday_00.IsDST() && m_graphday.IsDST()) t_graphday_00 -= 3600;
456 if (graphday_00.IsDST() && !m_graphday.IsDST()) t_graphday_00 += 3600;
457
458 m_t_graphday_GMT = t_graphday_00;
459
460 btc_valid = false;
461 Refresh();
462}
463
464void TCWin::PREvent(wxCommandEvent &event) {
465 wxTimeSpan dt(-24, 0, 0, 0);
466 m_graphday.Add(dt);
467 wxDateTime dm = m_graphday;
468
469 wxDateTime graphday_00 = dm.ResetTime();
470 time_t t_graphday_00 = graphday_00.GetTicks();
471
472 if (!graphday_00.IsDST() && m_graphday.IsDST()) t_graphday_00 -= 3600;
473 if (graphday_00.IsDST() && !m_graphday.IsDST()) t_graphday_00 += 3600;
474
475 m_t_graphday_GMT = t_graphday_00;
476
477 btc_valid = false;
478 Refresh();
479}
480
481void TCWin::RePosition(void) {
482 // Position the window
483 double lon = pIDX->IDX_lon;
484 double lat = pIDX->IDX_lat;
485
486 wxPoint r;
487 pParent->GetCanvasPointPix(lat, lon, &r);
488 pParent->ClientToScreen(&r.x, &r.y);
489 Move(r);
490}
491
492void TCWin::OnPaint(wxPaintEvent &event) {
493 if (!IsShown()) {
494 return;
495 }
496 int x, y;
497 int i;
498 char sbuf[100];
499 int w;
500 float tcmax, tcmin;
501
502 if (m_graph_rect.x == 0) return;
503
504 GetClientSize(&x, &y);
505 // qDebug() << "OnPaint" << x << y;
506
507#if 0
508 // establish some graphic element sizes/locations
509 int x_graph = x * 1 / 10;
510 int y_graph = y * 32 / 100;
511 int x_graph_w = x * 8 / 10;
512 int y_graph_h = (y * .7) - (3 * m_button_height);
513 m_graph_rect = wxRect(x_graph, y_graph, x_graph_w, y_graph_h);
514
515 wxSize texc_size = wxSize( ( x * 60 / 100 ), ( y *29 / 100 ) );
516 if( !m_tList->IsShown()){
517 texc_size = wxSize( ( x * 90 / 100 ), ( y *29 / 100 ) );
518 }
519
520 m_ptextctrl->SetSize(texc_size);
521#endif
522
523 wxPaintDC dc(this);
524
525 wxString tlocn(pIDX->IDX_station_name, wxConvUTF8);
526
527 // Adjust colors with current color scheme
528 // We use window class colors for that, they are modified by DimeControl
529 // depending on the current color scheme
530 pblack_1->SetColour(this->GetForegroundColour());
531 pblack_2->SetColour(this->GetForegroundColour());
532 pltgray->SetColour(this->GetBackgroundColour());
533 pltgray2->SetColour(this->GetBackgroundColour());
534 pred_2->SetColour(GetDimedColor(wxColor(230, 54, 54)));
535 pred_time->SetColour(GetDimedColor(wxColour(0, 100, 255)));
536
537 // if(1/*bForceRedraw*/)
538 {
539 int x_textbox = x * 5 / 100;
540 int y_textbox = 6;
541
542 int x_textbox_w = x * 51 / 100;
543 int y_textbox_h = y * 25 / 100;
544
545 // box the location text & tide-current table
546 dc.SetPen(*pblack_3);
547 dc.SetBrush(*pltgray2);
548 dc.DrawRoundedRectangle(x_textbox, y_textbox, x_textbox_w, y_textbox_h,
549 4); // location text box
550
551 if (m_tList->IsShown()) {
552 wxRect tab_rect = m_tList->GetRect();
553 dc.DrawRoundedRectangle(tab_rect.x - 4, y_textbox, tab_rect.width + 8,
554 y_textbox_h, 4); // tide-current table box
555 }
556
557 // Box the graph
558 dc.SetPen(*pblack_1);
559 dc.SetBrush(*pltgray);
560 dc.DrawRectangle(m_graph_rect.x, m_graph_rect.y, m_graph_rect.width,
561 m_graph_rect.height);
562
563 // On some platforms, we cannot draw rotated text.
564 // So, reduce the complexity of horizontal axis time labels
565#ifndef __WXMSW__
566 const int hour_delta = 4;
567#else
568 const int hour_delta = 1;
569#endif
570
571 int hour_start = 0;
572 // if(m_tzoneDisplay == 1){ // UTC
573 // hour_start = m_diff_mins / 60;
574 // }
575
576 // Horizontal axis
577 dc.SetFont(*pSFont);
578 for (i = 0; i < 25; i++) {
579 int xd = m_graph_rect.x + ((i)*m_graph_rect.width / 25);
580 if (hour_delta != 1) {
581 if (i % hour_delta == 0) {
582 dc.SetPen(*pblack_2);
583 dc.DrawLine(xd, m_graph_rect.y, xd,
584 m_graph_rect.y + m_graph_rect.height + 5);
585 char sbuf[16];
586 int hour_show = hour_start + i;
587 if (hour_show >= 24) hour_show -= 24;
588 sprintf(sbuf, "%02d", hour_show);
589 int x_shim = -20;
590 dc.DrawText(wxString(sbuf, wxConvUTF8),
591 xd + x_shim + (m_graph_rect.width / 25) / 2,
592 m_graph_rect.y + m_graph_rect.height + 8);
593 } else {
594 dc.SetPen(*pblack_1);
595 dc.DrawLine(xd, m_graph_rect.y, xd,
596 m_graph_rect.y + m_graph_rect.height + 5);
597 }
598 } else {
599 dc.SetPen(*pblack_1);
600 dc.DrawLine(xd, m_graph_rect.y, xd,
601 m_graph_rect.y + m_graph_rect.height + 5);
602 wxString sst;
603 sst.Printf(_T("%02d"), i);
604 dc.DrawRotatedText(sst, xd + (m_graph_rect.width / 25) / 2,
605 m_graph_rect.y + m_graph_rect.height + 8, 270.);
606 }
607 }
608
609 // Time indicators - system time and "selected" time (e.g. GRIB time)
610 wxDateTime system_now = wxDateTime::Now();
611
612 wxDateTime this_now = gTimeSource;
613 bool cur_time = !gTimeSource.IsValid();
614 if (cur_time) this_now = wxDateTime::Now();
615
616 // Always draw system time indicator (solid red line)
617 time_t t_system_now = system_now.GetTicks();
618 t_system_now -= m_diff_mins * 60;
619 if (m_tzoneDisplay == 0) // LMT @ Station
620 t_system_now += m_stationOffset_mins * 60;
621
622 float t_system_ratio =
623 m_graph_rect.width * (t_system_now - m_t_graphday_GMT) / (25 * 3600.0f);
624 // Eliminate line outside the graph (in that case put it outside the window)
625 int x_system = (t_system_ratio < 0 || t_system_ratio > m_graph_rect.width)
626 ? -1
627 : m_graph_rect.x + (int)t_system_ratio;
628
629 if (x_system >= 0) {
630 dc.SetPen(*pred_2); // solid red line for system time
631 dc.DrawLine(x_system, m_graph_rect.y, x_system,
632 m_graph_rect.y + m_graph_rect.height);
633 }
634
635 // Draw "selected time" indicator (from timeline widget) if different from
636 // system time.
637 if (gTimeSource.IsValid()) {
638 time_t t_selected_time = gTimeSource.GetTicks();
639
640 // Only draw "selected time" indicator if it's significantly different
641 // from system time.
642 if (abs(t_selected_time - t_system_now) > 300) {
643 t_selected_time -= m_diff_mins * 60;
644 if (m_tzoneDisplay == 0) // LMT @ Station
645 t_selected_time += m_stationOffset_mins * 60;
646
647 float t_selected_time_ratio = m_graph_rect.width *
648 (t_selected_time - m_t_graphday_GMT) /
649 (25 * 3600.0f);
650
651 int x_selected_time = (t_selected_time_ratio < 0 ||
652 t_selected_time_ratio > m_graph_rect.width)
653 ? -1
654 : m_graph_rect.x + (int)t_selected_time_ratio;
655
656 if (x_selected_time >= 0) {
657 // Create dashed blue pen for "selected time".
658 dc.SetPen(*pred_time);
659 dc.DrawLine(x_selected_time, m_graph_rect.y, x_selected_time,
660 m_graph_rect.y + m_graph_rect.height);
661 }
662 wxLogMessage("TCWin::OnPaint: Selected time indicator drawn at %d",
663 x_selected_time);
664 }
665 }
666 dc.SetPen(*pblack_1);
667
668 // Build the array of values, capturing max and min and HW/LW list
669
670 if (!btc_valid) {
671 float dir;
672 tcmax = -10;
673 tcmin = 10;
674 float val = -100;
675 m_tList->DeleteAllItems();
676 int list_index = 0;
677 bool wt = false;
678
679 wxBeginBusyCursor();
680
681 // The tide/current modules calculate values based on PC local time
682 // We want UTC, so adjust accordingly
683 int tt_localtz = m_t_graphday_GMT + (m_diff_mins * 60);
684 // then eventually we could need LMT at station
685 if (m_tzoneDisplay == 0)
686 tt_localtz -= m_stationOffset_mins * 60; // LMT at station
687
688 // get tide flow sens ( flood or ebb ? )
689 ptcmgr->GetTideFlowSens(tt_localtz, BACKWARD_TEN_MINUTES_STEP,
690 pIDX->IDX_rec_num, tcv[0], val, wt);
691
692 for (i = 0; i < 26; i++) {
693 int tt = tt_localtz + (i * FORWARD_ONE_HOUR_STEP);
694
695 ptcmgr->GetTideOrCurrent(tt, pIDX->IDX_rec_num, tcv[i], dir);
696 tt_tcv[i] = tt; // store the corresponding time_t value
697 if (tcv[i] > tcmax) tcmax = tcv[i];
698
699 if (tcv[i] < tcmin) tcmin = tcv[i];
700 if (TIDE_PLOT == m_plot_type) {
701 if (!((tcv[i] > val) == wt) && (i > 0)) // if tide flow sense change
702 {
703 float tcvalue; // look backward for HW or LW
704 time_t tctime;
705 ptcmgr->GetHightOrLowTide(tt, BACKWARD_TEN_MINUTES_STEP,
706 BACKWARD_ONE_MINUTES_STEP, tcv[i], wt,
707 pIDX->IDX_rec_num, tcvalue, tctime);
708 if (tctime > tt_localtz) { // Only show events visible in graphic
709 // presently shown
710 wxDateTime tcd; // write date
711 wxString s, s1;
712 tcd.Set(tctime - (m_diff_mins * 60));
713 if (m_tzoneDisplay == 0) // LMT @ Station
714 tcd.Set(tctime + (m_stationOffset_mins - m_diff_mins) * 60);
715
716 s.Printf(tcd.Format(_T("%H:%M ")));
717 s1.Printf(_T("%05.2f "), tcvalue); // write value
718 s.Append(s1);
719 Station_Data *pmsd = pIDX->pref_sta_data; // write unit
720 if (pmsd) s.Append(wxString(pmsd->units_abbrv, wxConvUTF8));
721 s.Append(_T(" "));
722 (wt) ? s.Append(_("HW")) : s.Append(_("LW")); // write HW or LT
723
724 wxListItem li;
725 li.SetId(list_index);
726 li.SetAlign(wxLIST_FORMAT_LEFT);
727 li.SetText(s);
728 li.SetColumn(0);
729 m_tList->InsertItem(li);
730 list_index++;
731 }
732 wt = !wt; // change tide flow sens
733 }
734 val = tcv[i];
735 }
736 if (CURRENT_PLOT == m_plot_type) {
737 wxDateTime thx; // write date
738 wxString s, s1;
739
740 thx.Set((time_t)tt - (m_diff_mins * 60));
741 if (m_tzoneDisplay == 0) // LMT @ Station
742 thx.Set((time_t)tt + (m_stationOffset_mins - m_diff_mins) * 60);
743
744 s.Printf(thx.Format(_T("%H:%M ")));
745 s1.Printf(_T("%05.2f "), fabs(tcv[i])); // write value
746 s.Append(s1);
747 Station_Data *pmsd = pIDX->pref_sta_data; // write unit
748 if (pmsd) s.Append(wxString(pmsd->units_abbrv, wxConvUTF8));
749 s1.Printf(_T(" %03.0f"), dir); // write direction
750 s.Append(s1);
751
752 wxListItem li;
753 li.SetId(list_index);
754 li.SetAlign(wxLIST_FORMAT_LEFT);
755 li.SetText(s);
756 li.SetColumn(0);
757 m_tList->InsertItem(li);
758 list_index++;
759 }
760 }
761
762 wxEndBusyCursor();
763
764 // Set up the vertical parameters based on Tide or Current plot
765 if (CURRENT_PLOT == m_plot_type) {
766 it = std::max(abs((int)tcmin - 1), abs((int)tcmax + 1));
767 ib = -it;
768
769 im = 2 * it;
770 m_plot_y_offset = m_graph_rect.height / 2;
771 val_off = 0;
772 } else {
773 ib = (int)tcmin;
774 if (tcmin < 0) ib -= 1;
775 it = (int)tcmax + 1;
776
777 im = it - ib; // abs ( ib ) + abs ( it );
778 m_plot_y_offset = (m_graph_rect.height * (it - ib)) / im;
779 val_off = ib;
780 }
781
782 // Arrange to skip some lines and legends if there are too many for the
783 // vertical space we have
784 int height_stext;
785 dc.GetTextExtent(_T("1"), NULL, &height_stext);
786 float available_lines = (float)m_graph_rect.height / height_stext;
787 i_skip = (int)ceil(im / available_lines);
788
789 if (CURRENT_PLOT == m_plot_type && i_skip != 1) {
790 // Adjust steps so slack current "0" line is always drawn on graph
791 ib -= it % i_skip;
792 it = -ib;
793 im = 2 * it;
794 }
795
796 // Build spline list of points
797
798 m_sList.DeleteContents(true);
799 m_sList.Clear();
800
801 for (i = 0; i < 26; i++) {
802 wxPoint *pp = new wxPoint;
803 pp->x = m_graph_rect.x + ((i)*m_graph_rect.width / 25);
804 pp->y = m_graph_rect.y + (m_plot_y_offset) -
805 (int)((tcv[i] - val_off) * m_graph_rect.height / im);
806
807 m_sList.Append(pp);
808 }
809
810 btc_valid = true;
811 }
812
813 // Graph legend
814 dc.SetTextForeground(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
815
816 // Vertical Axis
817
818 i = ib;
819 while (i < it + 1) {
820 int yd = m_graph_rect.y + (m_plot_y_offset) -
821 ((i - val_off) * m_graph_rect.height / im);
822
823 if ((m_plot_y_offset + m_graph_rect.y) == yd)
824 dc.SetPen(*pblack_2);
825 else
826 dc.SetPen(*pblack_1);
827
828 dc.DrawLine(m_graph_rect.x, yd, m_graph_rect.x + m_graph_rect.width, yd);
829 snprintf(sbuf, 99, "%d", i);
830 dc.DrawText(wxString(sbuf, wxConvUTF8), m_graph_rect.x - 20, yd - 5);
831 i += i_skip;
832 }
833
834 // Draw the Value curve
835#if wxCHECK_VERSION(2, 9, 0)
836 wxPointList *list = (wxPointList *)&m_sList;
837#else
838 wxList *list = (wxList *)&m_sList;
839#endif
840
841 dc.SetPen(*pgraph);
842#if wxUSE_SPLINES
843 dc.DrawSpline(list);
844#else
845 dc.DrawLines(list);
846#endif
847 // More Info
848
849 if (m_tzoneDisplay == 0) {
850 int station_offset = ptcmgr->GetStationTimeOffset(pIDX);
851 int h = station_offset / 60;
852 int m = station_offset - (h * 60);
853 if (m_graphday.IsDST()) h += 1;
854 m_stz.Printf(_T("UTC %+03d:%02d"), h, m);
855
856 // Make the "nice" (for the US) station time-zone string, brutally by
857 // hand
858 double lat = ptcmgr->GetStationLat(pIDX);
859
860 if (lat > 20.0) {
861 wxString mtz;
862 switch (ptcmgr->GetStationTimeOffset(pIDX)) {
863 case -240:
864 mtz = _T( "AST" );
865 break;
866 case -300:
867 mtz = _T( "EST" );
868 break;
869 case -360:
870 mtz = _T( "CST" );
871 break;
872 }
873
874 if (mtz.Len()) {
875 if (m_graphday.IsDST()) mtz[1] = 'D';
876 m_stz = mtz;
877 }
878 }
879 }
880
881 else
882 m_stz = _T("UTC");
883
884 int h;
885 dc.SetFont(*pSFont);
886 dc.GetTextExtent(m_stz, &w, &h);
887 dc.DrawText(m_stz, x / 2 - w / 2,
888 y - (m_button_height * 15 / 10) - (m_refTextHeight * 2));
889
890 wxString sdate;
891 if (g_locale == _T("en_US"))
892 sdate = m_graphday.Format(_T ( "%A %b %d, %Y" ));
893 else
894 sdate = m_graphday.Format(_T ( "%A %d %b %Y" ));
895
896 dc.SetFont(*pMFont);
897 dc.GetTextExtent(sdate, &w, &h);
898 dc.DrawText(sdate, x / 2 - w / 2,
899 y - (m_button_height * 15 / 10) - (m_refTextHeight * 1));
900
901 Station_Data *pmsd = pIDX->pref_sta_data;
902 if (pmsd) {
903 dc.GetTextExtent(wxString(pmsd->units_conv, wxConvUTF8), &w, &h);
904 dc.DrawRotatedText(wxString(pmsd->units_conv, wxConvUTF8), 5,
905 m_graph_rect.y + m_graph_rect.height / 2 + w / 2, 90.);
906 }
907
908 // Show flood and ebb directions
909 if ((strchr("c", pIDX->IDX_type)) || (strchr("C", pIDX->IDX_type))) {
910 dc.SetFont(*pSFont);
911
912 wxString fdir;
913 fdir.Printf(_T("%03d"), pIDX->IDX_flood_dir);
914 dc.DrawText(fdir, m_graph_rect.x + m_graph_rect.width + 4,
915 m_graph_rect.y + m_graph_rect.height * 1 / 4);
916
917 wxString edir;
918 edir.Printf(_T("%03d"), pIDX->IDX_ebb_dir);
919 dc.DrawText(edir, m_graph_rect.x + m_graph_rect.width + 4,
920 m_graph_rect.y + m_graph_rect.height * 3 / 4);
921 }
922
923 // Today or tomorrow
924 if ((m_button_height * 15) < x && cur_time) { // large enough horizontally?
925 wxString sday;
926
927 int day = m_graphday.GetDayOfYear();
928 if (m_graphday.GetYear() == this_now.GetYear()) {
929 if (day == this_now.GetDayOfYear())
930 sday.Append(_("Today"));
931 else if (day == this_now.GetDayOfYear() + 1)
932 sday.Append(_("Tomorrow"));
933 else
934 sday.Append(m_graphday.GetWeekDayName(m_graphday.GetWeekDay()));
935 } else if (m_graphday.GetYear() == this_now.GetYear() + 1 &&
936 day == this_now.Add(wxTimeSpan::Day()).GetDayOfYear())
937 sday.Append(_("Tomorrow"));
938
939 dc.SetFont(*pSFont);
940 dc.GetTextExtent(sday, &w, &h);
941 dc.DrawText(sday, 55 - w / 2,
942 y - (m_button_height * 15 / 10) - (m_refTextHeight * 1));
943 }
944
945 // Render "Spot of interest"
946 double spotDim = 4 * g_Platform->GetDisplayDPmm();
947
948 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(
949 GetGlobalColor(_T ( "YELO1" )), wxBRUSHSTYLE_SOLID));
950 dc.SetPen(wxPen(GetGlobalColor(_T ( "URED" )),
951 wxMax(2, 0.5 * g_Platform->GetDisplayDPmm())));
952 dc.DrawRoundedRectangle(xSpot - spotDim / 2, ySpot - spotDim / 2, spotDim,
953 spotDim, spotDim / 2);
954
955 dc.SetBrush(*wxTheBrushList->FindOrCreateBrush(
956 GetGlobalColor(_T ( "UBLCK" )), wxBRUSHSTYLE_SOLID));
957 dc.SetPen(wxPen(GetGlobalColor(_T ( "UBLCK" )), 1));
958
959 double ispotDim = spotDim / 5.;
960 dc.DrawRoundedRectangle(xSpot - ispotDim / 2, ySpot - ispotDim / 2,
961 ispotDim, ispotDim, ispotDim / 2);
962 }
963}
964
965void TCWin::OnSize(wxSizeEvent &event) {
966 if (!m_created) return;
967
968 int x, y;
969 GetClientSize(&x, &y);
970
971 // establish some graphic element sizes/locations
972 int x_graph = x * 1 / 10;
973 int y_graph = y * 32 / 100;
974 int x_graph_w = x * 8 / 10;
975 int y_graph_h =
976 (y * 65 / 100) - (m_button_height * 15 / 10) - (m_refTextHeight * 2);
977 y_graph_h =
978 wxMax(y_graph_h, 2); // ensure minimum size is positive, at least.
979
980 m_graph_rect = wxRect(x_graph, y_graph, x_graph_w, y_graph_h);
981
982 // In the interest of readability, if the width of the dialog is too narrow,
983 // simply skip showing the "Hi/Lo" list control.
984
985 if ((m_tsy * 15) > x)
986 m_tList->Hide();
987 else {
988 m_tList->Move(wxPoint(x * 65 / 100, 11));
989 m_tList->Show();
990 }
991
992 wxSize texc_size = wxSize((x * 60 / 100), (y * 29 / 100));
993 if (!m_tList->IsShown()) {
994 texc_size = wxSize((x * 90 / 100), (y * 29 / 100));
995 }
996 m_ptextctrl->SetSize(texc_size);
997
998#ifdef __WXOSX__
999 OK_button->Move(
1000 wxPoint(x - (4 * m_button_height + 10), y - (m_button_height * 12 / 10)));
1001#else
1002 OK_button->Move(
1003 wxPoint(x - (3 * m_button_height + 10), y - (m_button_height * 12 / 10)));
1004#endif
1005 PR_button->Move(wxPoint(10, y - (m_button_height + 10)));
1006
1007 m_choiceTimezone->Move(
1008 wxPoint(x / 2 - m_choiceSize_x / 2, y - (m_button_height * 12 / 10)));
1009
1010 int bsx, bsy, bpx, bpy;
1011 PR_button->GetSize(&bsx, &bsy);
1012 PR_button->GetPosition(&bpx, &bpy);
1013
1014 NX_button->Move(wxPoint(bpx + bsx + 5, y - (m_button_height + 10)));
1015
1016 btc_valid = false;
1017
1018 Refresh(true);
1019 Update();
1020}
1021
1022void TCWin::MouseEvent(wxMouseEvent &event) {
1023 event.GetPosition(&curs_x, &curs_y);
1024
1025 if (!m_TCWinPopupTimer.IsRunning())
1026 m_TCWinPopupTimer.Start(20, wxTIMER_ONE_SHOT);
1027}
1028
1029void TCWin::OnTCWinPopupTimerEvent(wxTimerEvent &event) {
1030 int x, y;
1031 bool ShowRollover;
1032
1033 GetClientSize(&x, &y);
1034 wxRegion cursorarea(m_graph_rect);
1035 if (cursorarea.Contains(curs_x, curs_y)) {
1036 ShowRollover = true;
1037 SetCursor(*pParent->pCursorCross);
1038 if (NULL == m_pTCRolloverWin) {
1039 m_pTCRolloverWin = new RolloverWin(this, -1, false);
1040 // doesn't really work, mouse positions are relative to rollover window
1041 // not this window.
1042 // effect: hide rollover window if mouse on rollover
1043 m_pTCRolloverWin->SetMousePropogation(1);
1044 m_pTCRolloverWin->Hide();
1045 }
1046 float t, d;
1047 wxString p, s;
1048 // set time on x cursor position
1049 t = (25 / ((float)x * 8 / 10)) * ((float)curs_x - ((float)x * 1 / 10));
1050
1051 int tt = m_t_graphday_GMT + (int)(t * 3600);
1052 time_t ths = tt;
1053
1054 wxDateTime thd;
1055 thd.Set(ths);
1056 p.Printf(thd.Format(_T("%Hh %Mmn")));
1057 p.Append(_T("\n"));
1058
1059 // The tide/current modules calculate values based on PC local time
1060 // We want UTC, so adjust accordingly
1061 int tt_localtz = m_t_graphday_GMT + (m_diff_mins * 60);
1062
1063 int ttv = tt_localtz + (int)(t * 3600);
1064 if (m_tzoneDisplay == 0) {
1065 ttv -= m_stationOffset_mins * 60; // LMT at station
1066 }
1067
1068 time_t tts = ttv;
1069
1070 // set tide level or current speed at that time
1071 ptcmgr->GetTideOrCurrent(tts, pIDX->IDX_rec_num, t, d);
1072 s.Printf(_T("%3.2f "), (t < 0 && CURRENT_PLOT == m_plot_type)
1073 ? -t
1074 : t); // always positive if current
1075 p.Append(s);
1076
1077 // set unit
1078 Station_Data *pmsd = pIDX->pref_sta_data;
1079 if (pmsd) p.Append(wxString(pmsd->units_abbrv, wxConvUTF8));
1080
1081 // set current direction
1082 if (CURRENT_PLOT == m_plot_type) {
1083 s.Printf("%3.0f%c", d, 0x00B0);
1084 p.Append(_T("\n"));
1085 p.Append(s);
1086 }
1087
1088 // set rollover area size
1089 wxSize win_size;
1090 win_size.Set(x * 90 / 100, y * 80 / 100);
1091
1092 m_pTCRolloverWin->SetString(p);
1093 m_pTCRolloverWin->SetBestPosition(curs_x, curs_y, 1, 1, TC_ROLLOVER,
1094 win_size);
1095 m_pTCRolloverWin->SetBitmap(TC_ROLLOVER);
1096 m_pTCRolloverWin->Refresh();
1097 m_pTCRolloverWin->Show();
1098
1099 // Mark the actual spot on the curve
1100 // x value is clear...
1101 // Find the point in the window that is used for the curev rendering,
1102 // rounding as necessary
1103
1104 int idx = 1; // in case m_graph_rect.width is weird ie ppx never > curs_x
1105 for (int i = 0; i < 26; i++) {
1106 float ppx = m_graph_rect.x + ((i)*m_graph_rect.width / 25.f);
1107 if (ppx > curs_x) {
1108 idx = i;
1109 break;
1110 }
1111 }
1112
1113 wxPointList *list = (wxPointList *)&m_sList;
1114 wxPoint *a = list->Item(idx - 1)->GetData();
1115 wxPoint *b = list->Item(idx)->GetData();
1116
1117 float pct = (curs_x - a->x) / (float)((b->x - a->x));
1118 float dy = pct * (b->y - a->y);
1119
1120 ySpot = a->y + dy;
1121 xSpot = curs_x;
1122
1123 Refresh(true);
1124
1125 } else {
1126 SetCursor(*pParent->pCursorArrow);
1127 ShowRollover = false;
1128 }
1129
1130 if (m_pTCRolloverWin && m_pTCRolloverWin->IsShown() && !ShowRollover) {
1131 m_pTCRolloverWin->Hide();
1132 }
1133}
1134
1135void TCWin::OnTimeIndicatorTimer(wxTimerEvent &event) {
1136 // Refresh to update the red line (system time indicator)
1137 Refresh(false);
1138}
ChartCanvas - Main chart display and interaction component.
Definition chcanv.h:153
bool GetCanvasPointPix(double rlat, double rlon, wxPoint *r)
Convert latitude/longitude to canvas pixel coordinates (physical pixels) rounded to nearest integer.
Definition chcanv.cpp:4568
wxFont * FindOrCreateFont(int point_size, wxFontFamily family, wxFontStyle style, wxFontWeight weight, bool underline=false, const wxString &facename=wxEmptyString, wxFontEncoding encoding=wxFONTENCODING_DEFAULT)
Creates or finds a matching font in the font cache.
Definition FontMgr.cpp:467
wxFont * GetFont(const wxString &TextElement, int requested_font_size=0)
Gets a font object for a UI element.
Definition FontMgr.cpp:203
Represents an index entry for tidal and current data.
Definition IDX_entry.h:49
char IDX_type
Entry type identifier "TCtcIUu".
Definition IDX_entry.h:61
int IDX_flood_dir
Flood current direction (in degrees)
Definition IDX_entry.h:73
char IDX_station_name[MAXNAMELEN]
Name of the tidal or current station.
Definition IDX_entry.h:63
int IDX_ebb_dir
Ebb current direction (in degrees)
Definition IDX_entry.h:74
double IDX_lat
Latitude of the station (in degrees, +North)
Definition IDX_entry.h:65
double IDX_lon
Longitude of the station (in degrees, +East)
Definition IDX_entry.h:64
Station_Data * pref_sta_data
Pointer to the reference station data.
Definition IDX_entry.h:97
int IDX_rec_num
Record number for multiple entries with same name.
Definition IDX_entry.h:60
Provides platform-specific support utilities for OpenCPN.
Definition tcmgr.h:88
Definition TCWin.h:46
wxFont * GetOCPNScaledFont(wxString item, int default_size)
Retrieves a font from FontMgr, optionally scaled for physical readability.
Definition gui_lib.cpp:54
General purpose GUI support.