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