OpenCPN Partial API docs
Loading...
Searching...
No Matches
CustomGrid.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
23#include "CustomGrid.h"
24
25#include <wx/graphics.h>
26#include "GribTable.h"
27#include "folder.xpm"
28
29#define SCROLL_SENSIBILITY 20
30
31//------------------------------------------------------------------------------
32// custom grid implementation
33//------------------------------------------------------------------------------
34CustomGrid::CustomGrid(wxWindow* parent, wxWindowID id, const wxPoint& pos,
35 const wxSize& size, long style, const wxString& name)
36 : wxGrid(parent, id, pos, size, style, name) {
37 // create grid
38 SetTable(new wxGridStringTable(0, 0), true, wxGridSelectRows);
39 // some general settings
40 EnableEditing(false);
41 EnableGridLines(true);
42 EnableDragGridSize(false);
43 SetMargins(0, 0);
44 EnableDragColMove(false);
45 EnableDragColSize(false);
46 EnableDragRowSize(false);
47 // init rows pref
48 wxFileConfig* pConf = GetOCPNConfigObject();
49 if (pConf) {
50 pConf->SetPath(_T("/Settings/GRIB"));
51 m_IsDigit = pConf->Read(_T("GribDataTableRowPref"), _T("XXX"));
52 }
53 if (m_IsDigit.Len() != wxString(_T("XXX")).Len()) m_IsDigit = _T("XXX");
54 // create structure for all numerical rows
55 for (unsigned int i = 0; i < m_IsDigit.Len(); i++) {
56 m_NumRow.push_back(wxNOT_FOUND);
57 m_NumRowVal.push_back(std::vector<double>());
58 }
59 // init labels attr
60 wxFont labelfont = GetOCPNGUIScaledFont_PlugIn(_("Dialog")).MakeBold();
61 SetLabelFont(labelfont);
62 wxColour colour = wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW);
63 if (colour.Red() > 128) {
64 GetGlobalColor(_T("DILG0"), &colour);
65 GetGlobalColor(_T("GREEN1"), &m_greenColour);
66 GetGlobalColor(_T("DILG1"), &m_greyColour);
67 } else {
68 GetGlobalColor(_T("GREEN2"), &m_greenColour);
69 m_greyColour = colour;
70 }
71 SetLabelBackgroundColour(colour);
72 // set row label size
73 int w;
74 GetTextExtent(_T("Ab"), &w, nullptr, 0, 0, &labelfont);
75 double x = (double)w * 6.5;
76 SetRowLabelSize((int)x);
77
78#ifdef __WXOSX__
79 m_bLeftDown = false;
80#endif
81
82 // connect events at dialog level
83 Connect(wxEVT_SCROLLWIN_THUMBTRACK,
84 wxScrollEventHandler(CustomGrid::OnScroll), nullptr, this);
85 Connect(wxEVT_SIZE, wxSizeEventHandler(CustomGrid::OnResize), nullptr, this);
86 Connect(wxEVT_GRID_LABEL_LEFT_CLICK,
87 wxGridEventHandler(CustomGrid::OnLabeClick), nullptr, this);
88 // connect events at grid level
89 GetGridWindow()->Connect(wxEVT_LEFT_DOWN,
90 wxMouseEventHandler(CustomGrid::OnMouseEvent),
91 nullptr, this);
92 GetGridWindow()->Connect(wxEVT_LEFT_UP,
93 wxMouseEventHandler(CustomGrid::OnMouseEvent),
94 nullptr, this);
95 GetGridWindow()->Connect(wxEVT_MOTION,
96 wxMouseEventHandler(CustomGrid::OnMouseEvent),
97 nullptr, this);
98 // timer event
99 m_tRefreshTimer.Connect(wxEVT_TIMER,
100 wxTimerEventHandler(CustomGrid::OnRefreshTimer),
101 nullptr, this);
102}
103
104CustomGrid::~CustomGrid() {
105 wxFileConfig* pConf = GetOCPNConfigObject();
106 if (pConf) {
107 pConf->SetPath(_T ( "/Settings/GRIB" ));
108 pConf->Write(_T ( "GribDataTableRowPref" ), m_IsDigit);
109 }
110 m_NumRowVal.clear();
111 m_NumRow.clear();
112}
113
114void CustomGrid::DrawColLabel(wxDC& dc, int col) {
115 // init dc font and colours
116 dc.SetFont(m_labelFont);
117 if (col == m_gParent->m_pIndex) {
118 dc.SetBrush(wxBrush(m_greenColour, wxBRUSHSTYLE_SOLID));
119 dc.SetPen(wxPen(m_greenColour, 1));
120 } else {
121 dc.SetBrush(wxBrush(m_labelBackgroundColour, wxBRUSHSTYLE_SOLID));
122 dc.SetPen(wxPen(m_labelBackgroundColour, 1));
123 }
124 // draw retangle
125 wxRect tRect(GetColLeft(col), 1, GetColWidth(col) - 2, m_colLabelHeight - 2);
126 dc.DrawRectangle(tRect);
127 // draw lines aroud label
128 dc.SetPen(GetDefaultGridLinePen());
129 dc.DrawLine(GetColLeft(col) - 1, 0, GetColRight(col), 0);
130 // Increase line width to highlight "day" change at midnight
131 if (col > -1 && (col == 0 || GetColLabelValue(col).BeforeFirst(' ') !=
132 GetColLabelValue(col - 1).BeforeFirst(' ')))
133 dc.SetPen(wxPen(*wxBLACK, 4));
134 dc.DrawLine(GetColLeft(col) - 1, 0, GetColLeft(col) - 1, m_colLabelHeight);
135 if (col == m_numCols - 1) {
136 dc.SetPen(wxPen(*wxBLACK, 4));
137 dc.DrawLine(GetColRight(col), 0, GetColRight(col), m_colLabelHeight);
138 }
139 // then draw label
140 dc.DrawLabel(GetColLabelValue(col), tRect,
141 wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
142}
143
144void CustomGrid::DrawRowLabel(wxDC& dc, int row) {
145 // init dc font and colours
146 dc.SetFont(m_labelFont);
147 dc.SetPen(GetDefaultGridLinePen());
148 dc.SetBrush(wxBrush(m_labelBackgroundColour, wxBRUSHSTYLE_SOLID));
149 int w = dc.GetTextExtent(_T("Speed")).x;
150 wxString label1, label2;
151 label1 = GetRowLabelValue(row).BeforeFirst(',', &label2);
152 bool pline = true;
153 // row is the first of 3 for the same parameter (wind ... waves ...)
154 if (GetNumberRows() > row + 2 &&
155 label1 == GetRowLabelValue(row + 2).BeforeFirst(',')) {
156 pline = false;
157 if (IsRowVisible(row + 2)) label1 = _T(" ");
158 }
159 // row is the second of 3 or the first of 2
160 else if (GetNumberRows() > row + 1 &&
161 label1 == GetRowLabelValue(row + 1).BeforeFirst(',')) {
162 pline = false;
163 if (row > 0 &&
164 label1 == GetRowLabelValue(row - 1).BeforeFirst(',')) { // second of 3
165 if (!IsRowVisible(row + 1)) label1 = _T(" ");
166 }
167 }
168 // row is the last of 3
169 else if (row > 1 && label1 == GetRowLabelValue(row - 2).BeforeFirst(',')) {
170 if (IsRowVisible(row - 1)) label1 = _T(" ");
171 }
172 // row is the last of 2
173 else if (row > 0 && label1 == GetRowLabelValue(row - 1).BeforeFirst(',')) {
174 if (IsRowVisible(row - 1)) label1 = _T(" ");
175 }
176 // draw first part of the label
177 wxRect aRect(5, GetRowTop(row), m_rowLabelWidth - w, GetRowHeight(row));
178 dc.DrawLabel(label1, aRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
179 // draw second part of the label
180 wxRect bRect(m_rowLabelWidth - w, GetRowTop(row), w, GetRowHeight(row));
181 dc.SetFont(wxFont(m_labelFont).Scale(0.85));
182 dc.DrawLabel(label2, bRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
183 // draw row lines around labels
184 if (pline)
185 dc.DrawLine(0, GetRowBottom(row) - 1, m_rowLabelWidth,
186 GetRowBottom(row) - 1);
187 dc.DrawLine(0, GetRowTop(row), 0, GetRowBottom(row));
188 dc.DrawLine(m_rowLabelWidth - 1, GetRowTop(row), m_rowLabelWidth - 1,
189 GetRowBottom(row));
190}
191
192void CustomGrid::DrawCornerLabel(wxDC& dc) {
193 dc.SetPen(GetDefaultGridLinePen());
194 dc.SetBrush(wxBrush(m_labelBackgroundColour, wxBRUSHSTYLE_SOLID));
195 wxRect rect(0, 0, m_rowLabelWidth, m_colLabelHeight);
196 dc.DrawRectangle(rect);
198 double hc = m_colLabelHeight;
199 double hb = wxBitmap(now).GetHeight();
200 double scfac = ((hc / hb) * 4) / 4;
201 wxBitmap bmp = m_gParent->GetScaledBitmap(wxBitmap(now), _T("now"), scfac);
202 // center bitmap
203 int x = (m_rowLabelWidth - bmp.GetWidth()) / 2;
204 int y = (m_colLabelHeight == bmp.GetHeight())
205 ? 0
206 : wxMax(0, (m_colLabelHeight - bmp.GetHeight()) / 2);
207 dc.DrawBitmap(bmp, x, y);
208}
209
210void CustomGrid::OnScroll(wxScrollEvent& event) {
211 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
212 event.Skip();
213}
214
215void CustomGrid::OnRefreshTimer(wxTimerEvent& event) { ForceRefresh(); }
216
217void CustomGrid::OnResize(wxSizeEvent& event) {
218 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
219 event.Skip();
220}
221
222void CustomGrid::OnLabeClick(wxGridEvent& event) {
223 int row = event.GetRow();
224 int col = event.GetCol();
225 wxPoint p = event.GetPosition();
226 ClearSelection();
227 if (row == wxNOT_FOUND && event.GetCol() == wxNOT_FOUND) { // corner label
228 // find the first visible row/col
229 int frow = 0, fcol = 0;
230 GetFirstVisibleCell(frow, fcol);
231 // then scroll as requested;
232 MakeCellVisible(frow, m_numCols - 1);
233 MakeCellVisible(frow, m_gParent->m_pIndex);
234 } else if (row != wxNOT_FOUND && col == wxNOT_FOUND) { // numerical row label
235 int idx = GetRowIndex(row);
236 if (idx != wxNOT_FOUND) {
237 if (m_IsDigit.GetChar(idx) == 'X')
238 m_IsDigit.SetChar(idx, '.');
239 else
240 m_IsDigit.SetChar(idx, 'X');
241 for (int c = 0; c < m_numCols; c++) {
242 double value = m_NumRowVal[idx][c];
243 /*Current direction is generally reported as the "flow" direction, which
244 * is opposite from wind convention. So, adjust.*/
245 if (idx == R_CURRENT && m_IsDigit.GetChar(idx) == 'X' &&
246 value != GRIB_NOTDEF) {
247 value += 180;
248 if (value >= 360) value -= 360;
249 if (value < 0) value += 360;
250 }
251 SetCellRenderer(
252 row, c, new CustomRenderer(value, m_IsDigit.GetChar(idx) == 'X'));
253 }
254 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
255 }
256 }
257}
258
259int CustomGrid::GetRowIndex(int row) {
260 int idx = wxNOT_FOUND;
261 for (unsigned int i = 0; i < m_NumRow.size(); i++) {
262 if (m_NumRow[i] == row) idx = i;
263 }
264 return idx;
265}
266
267void CustomGrid::SetNumericalRow(int row, int col, int datatype, double value) {
268 m_NumRow[datatype] = row;
269 m_NumRowVal[datatype].push_back(value);
270 /*Current direction is generally reported as the "flow" direction,which is
271 * opposite from wind convention. So, adjust.*/
272 if (datatype == R_CURRENT && m_IsDigit.GetChar(datatype) == 'X' &&
273 value != GRIB_NOTDEF) {
274 value += 180;
275 if (value >= 360) value -= 360;
276 if (value < 0) value += 360;
277 }
278 SetCellRenderer(
279 row, col, new CustomRenderer(value, m_IsDigit.GetChar(datatype) == 'X'));
280}
281
282void CustomGrid::OnMouseEvent(wxMouseEvent& event) {
283 static wxPoint s_pevt;
284 wxPoint pevt = event.GetPosition();
285#ifdef __WXOSX__
286 if (!m_bLeftDown && event.LeftIsDown()) {
287 m_bLeftDown = true;
288 s_pevt = pevt;
289 } else if (m_bLeftDown && !event.LeftIsDown()) {
290 m_bLeftDown = false;
291 if (HasCapture()) ReleaseMouse();
292 }
293#else
294 if (event.LeftDown()) s_pevt = pevt;
295 if (event.LeftUp()) {
296 if (HasCapture()) ReleaseMouse();
297 }
298#endif
299 if (event.Dragging()) {
300 int frow, fcol, lrow, lcol;
301 GetFirstVisibleCell(frow, fcol);
302 GetLastVisibleCell(lrow, lcol);
303 if (pevt != s_pevt) {
304 bool rfh = false;
305 int diff = pevt.x - s_pevt.x;
306 // scrolling right
307 if (diff > SCROLL_SENSIBILITY) {
308 s_pevt.x = pevt.x;
309 if (fcol > 0) {
310 MakeCellVisible(frow, fcol - 1);
311 rfh = true;
312 }
313 }
314 // scrolling left
315 else if (-diff > SCROLL_SENSIBILITY) {
316 s_pevt.x = pevt.x;
317 if (lcol < m_numCols - 1) {
318 MakeCellVisible(frow, lcol + 1);
319 rfh = true;
320 }
321 }
322 // scrolling down
323 diff = pevt.y - s_pevt.y;
324 if (diff > SCROLL_SENSIBILITY) {
325 s_pevt.y = pevt.y;
326 if (frow > 0) {
327 MakeCellVisible(frow - 1, fcol);
328 rfh = true;
329 }
330 }
331 // scrolling up
332 else if (-diff > SCROLL_SENSIBILITY) {
333 s_pevt.y = pevt.y;
334 if (lrow < m_numRows - 1) {
335 MakeCellVisible(lrow + 1, fcol);
336 MakeCellVisible(frow + 1,
337 fcol); // workaroud for what seems curious moving 2
338 // rows instead of 1 in previous function
339 rfh = true;
340 }
341 }
342 if (rfh) m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
343 }
344 }
345}
346
347bool CustomGrid::IsRowVisible(int row) {
348 for (int i = 0; i < m_numCols; i++) {
349 if (IsVisible(row, i, false)) return true;
350 }
351 return false;
352}
353
354// find the first top/left visible cell coords
355void CustomGrid::GetFirstVisibleCell(int& frow, int& fcol) {
356 bool vis = false;
357 frow = 0;
358 for (fcol = 0; fcol < m_numCols; fcol++) {
359 for (frow = 0; frow < m_numRows; frow++) {
360 if (IsVisible(frow, fcol)) { // find the first row/col
361 vis = true;
362 break;
363 }
364 }
365 if (vis) break;
366 }
367}
368
369// find the visible cell coords
370void CustomGrid::GetLastVisibleCell(int& lrow, int& lcol) {
371 bool vis = false;
372 lrow = wxMax(m_numRows - 1, 0);
373 for (lcol = wxMax(m_numCols - 1, 0); lcol > -1; lcol--) {
374 for (lrow = m_numRows - 1; lrow > -1; lrow--) {
375 if (IsVisible(lrow, lcol)) {
376 vis = true;
377 break;
378 }
379 }
380 if (vis) break;
381 }
382}
383
384//------------------------------------------------------------------------------
385// custom renderer
386//------------------------------------------------------------------------------
387void CustomRenderer::Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
388 const wxRect& rect, int row, int col,
389 bool isSelected) {
390 dc.SetPen(wxPen(attr.GetBackgroundColour(), 1));
391 dc.SetBrush(wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
392 dc.DrawRectangle(rect);
393 if (m_IsDigit || m_dDir == GRIB_NOTDEF) { // digital format
394 wxString text(wxEmptyString);
395 if (m_dDir != GRIB_NOTDEF) text.Printf(_T("%03d%c"), (int)m_dDir, 0x00B0);
396 dc.DrawLabel(text, rect,
397 wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
398 } else { // graphical format
399 double si = sin((m_dDir - 90) * M_PI / 180.);
400 double co = cos((m_dDir - 90) * M_PI / 180.);
401
402 int i = rect.GetTopLeft().x + (rect.GetWidth() / 2);
403 int j = rect.GetTopLeft().y + (rect.GetHeight() / 2);
404
405 int arrowSize = rect.GetHeight() - 3;
406 int dec = -arrowSize / 2;
407
408#if wxUSE_GRAPHICS_CONTEXT
409 wxGraphicsContext* gdc;
410 wxClientDC* cdc = new wxClientDC(dynamic_cast<wxWindow*>(&grid));
411 cdc = dynamic_cast<wxClientDC*>(&dc);
412 if (cdc) {
413 gdc = wxGraphicsContext::Create(*cdc);
414#ifdef __WXGTK__
415 /*platforms don't manage the same way the gdc origin
416 for linux, we have to re-compute the good one.
417 To DO : verify it works on all other plateforms (done for MSW*/
418 bool vis = false;
419 int r = 0;
420 for (int c = 0; c < grid.GetNumberCols(); c++) {
421 for (r = 0; r < grid.GetNumberRows(); r++) {
422 if (grid.IsVisible(r, c)) { // find the first row/col
423 vis = true;
424 i -= (c * grid.GetColSize(0));
425 j -= (r * grid.GetRowHeight(0));
426 break;
427 }
428 }
429 if (vis) break;
430 }
431#endif
432 gdc->SetPen(wxPen(attr.GetTextColour(), 3));
433 gdc->SetBrush(wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
434
435 double ii, jj, kk, ll;
436 GetArrowsPoints(si, co, i, j, dec, 0, dec + arrowSize, 0, ii, jj, kk, ll);
437 gdc->StrokeLine(ii, jj, kk, ll);
438 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, 3, ii, jj, kk, ll);
439 gdc->StrokeLine(ii, jj, kk, ll);
440 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, -3, ii, jj, kk, ll);
441 gdc->StrokeLine(ii, jj, kk, ll);
442 delete gdc;
443 } else
444#endif
445 {
446 dc.SetPen(wxPen(attr.GetTextColour(), 3));
447 double ii, jj, kk, ll;
448 GetArrowsPoints(si, co, i, j, dec, 0, dec + arrowSize, 0, ii, jj, kk, ll);
449 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
450 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, 3, ii, jj, kk, ll);
451 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
452 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, -3, ii, jj, kk, ll);
453 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
454 }
455 }
456}
457
458void CustomRenderer::GetArrowsPoints(double si, double co, int di, int dj,
459 int i, int j, int k, int l, double& ii,
460 double& jj, double& kk, double& ll) {
461 ii = (i * co - j * si + 0.5) + di;
462 jj = (i * si + j * co + 0.5) + dj;
463 kk = (k * co - l * si + 0.5) + di;
464 ll = (k * si + l * co + 0.5) + dj;
465}
Specialized Grid Control for GRIB Data Display.
GRIB Data Table View and Export Interface.
wxFont GetOCPNGUIScaledFont_PlugIn(wxString item)
Gets a uniquely scaled font copy for responsive UI elements.
wxFileConfig * GetOCPNConfigObject(void)
Gets OpenCPN's configuration object.