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