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