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 if (col > -1 && (col == 0 || GetColLabelValue(col).BeforeFirst('-') !=
131 GetColLabelValue(col - 1).BeforeFirst('-')))
132 dc.SetPen(wxPen(*wxBLACK, 4));
133 dc.DrawLine(GetColLeft(col) - 1, 0, GetColLeft(col) - 1, m_colLabelHeight);
134 if (col == m_numCols - 1) {
135 dc.SetPen(wxPen(*wxBLACK, 4));
136 dc.DrawLine(GetColRight(col), 0, GetColRight(col), m_colLabelHeight);
137 }
138 // then draw label
139 dc.DrawLabel(GetColLabelValue(col), tRect,
140 wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
141}
142
143void CustomGrid::DrawRowLabel(wxDC& dc, int row) {
144 // init dc font and colours
145 dc.SetFont(m_labelFont);
146 dc.SetPen(GetDefaultGridLinePen());
147 dc.SetBrush(wxBrush(m_labelBackgroundColour, wxBRUSHSTYLE_SOLID));
148 int w = dc.GetTextExtent(_T("Speed")).x;
149 wxString label1, label2;
150 label1 = GetRowLabelValue(row).BeforeFirst(',', &label2);
151 bool pline = true;
152 // row is the first of 3 for the same parameter (wind ... waves ...)
153 if (GetNumberRows() > row + 2 &&
154 label1 == GetRowLabelValue(row + 2).BeforeFirst(',')) {
155 pline = false;
156 if (IsRowVisible(row + 2)) label1 = _T(" ");
157 }
158 // row is the second of 3 or the first of 2
159 else if (GetNumberRows() > row + 1 &&
160 label1 == GetRowLabelValue(row + 1).BeforeFirst(',')) {
161 pline = false;
162 if (row > 0 &&
163 label1 == GetRowLabelValue(row - 1).BeforeFirst(',')) { // second of 3
164 if (!IsRowVisible(row + 1)) label1 = _T(" ");
165 }
166 }
167 // row is the last of 3
168 else if (row > 1 && label1 == GetRowLabelValue(row - 2).BeforeFirst(',')) {
169 if (IsRowVisible(row - 1)) label1 = _T(" ");
170 }
171 // row is the last of 2
172 else if (row > 0 && label1 == GetRowLabelValue(row - 1).BeforeFirst(',')) {
173 if (IsRowVisible(row - 1)) label1 = _T(" ");
174 }
175 // draw first part of the label
176 wxRect aRect(5, GetRowTop(row), m_rowLabelWidth - w, GetRowHeight(row));
177 dc.DrawLabel(label1, aRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
178 // draw second part of the label
179 wxRect bRect(m_rowLabelWidth - w, GetRowTop(row), w, GetRowHeight(row));
180 dc.SetFont(wxFont(m_labelFont).Scale(0.85));
181 dc.DrawLabel(label2, bRect, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL);
182 // draw row lines around labels
183 if (pline)
184 dc.DrawLine(0, GetRowBottom(row) - 1, m_rowLabelWidth,
185 GetRowBottom(row) - 1);
186 dc.DrawLine(0, GetRowTop(row), 0, GetRowBottom(row));
187 dc.DrawLine(m_rowLabelWidth - 1, GetRowTop(row), m_rowLabelWidth - 1,
188 GetRowBottom(row));
189}
190
191void CustomGrid::DrawCornerLabel(wxDC& dc) {
192 dc.SetPen(GetDefaultGridLinePen());
193 dc.SetBrush(wxBrush(m_labelBackgroundColour, wxBRUSHSTYLE_SOLID));
194 wxRect rect(0, 0, m_rowLabelWidth, m_colLabelHeight);
195 dc.DrawRectangle(rect);
197 double hc = m_colLabelHeight;
198 double hb = wxBitmap(now).GetHeight();
199 double scfac = ((hc / hb) * 4) / 4;
200 wxBitmap bmp = m_gParent->GetScaledBitmap(wxBitmap(now), _T("now"), scfac);
201 // center bitmap
202 int x = (m_rowLabelWidth - bmp.GetWidth()) / 2;
203 int y = (m_colLabelHeight == bmp.GetHeight())
204 ? 0
205 : wxMax(0, (m_colLabelHeight - bmp.GetHeight()) / 2);
206 dc.DrawBitmap(bmp, x, y);
207}
208
209void CustomGrid::OnScroll(wxScrollEvent& event) {
210 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
211 event.Skip();
212}
213
214void CustomGrid::OnRefreshTimer(wxTimerEvent& event) { ForceRefresh(); }
215
216void CustomGrid::OnResize(wxSizeEvent& event) {
217 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
218 event.Skip();
219}
220
221void CustomGrid::OnLabeClick(wxGridEvent& event) {
222 int row = event.GetRow();
223 int col = event.GetCol();
224 wxPoint p = event.GetPosition();
225 ClearSelection();
226 if (row == wxNOT_FOUND && event.GetCol() == wxNOT_FOUND) { // corner label
227 // find the first visible row/col
228 int frow = 0, fcol = 0;
229 GetFirstVisibleCell(frow, fcol);
230 // then scroll as requested;
231 MakeCellVisible(frow, m_numCols - 1);
232 MakeCellVisible(frow, m_gParent->m_pIndex);
233 } else if (row != wxNOT_FOUND && col == wxNOT_FOUND) { // numerical row label
234 int idx = GetRowIndex(row);
235 if (idx != wxNOT_FOUND) {
236 if (m_IsDigit.GetChar(idx) == 'X')
237 m_IsDigit.SetChar(idx, '.');
238 else
239 m_IsDigit.SetChar(idx, 'X');
240 for (int c = 0; c < m_numCols; c++) {
241 double value = m_NumRowVal[idx][c];
242 /*Current direction is generally reported as the "flow" direction, which
243 * is opposite from wind convention. So, adjust.*/
244 if (idx == R_CURRENT && m_IsDigit.GetChar(idx) == 'X' &&
245 value != GRIB_NOTDEF) {
246 value += 180;
247 if (value >= 360) value -= 360;
248 if (value < 0) value += 360;
249 }
250 SetCellRenderer(
251 row, c, new CustomRenderer(value, m_IsDigit.GetChar(idx) == 'X'));
252 }
253 m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
254 }
255 }
256}
257
258int CustomGrid::GetRowIndex(int row) {
259 int idx = wxNOT_FOUND;
260 for (unsigned int i = 0; i < m_NumRow.size(); i++) {
261 if (m_NumRow[i] == row) idx = i;
262 }
263 return idx;
264}
265
266void CustomGrid::SetNumericalRow(int row, int col, int datatype, double value) {
267 m_NumRow[datatype] = row;
268 m_NumRowVal[datatype].push_back(value);
269 /*Current direction is generally reported as the "flow" direction,which is
270 * opposite from wind convention. So, adjust.*/
271 if (datatype == R_CURRENT && m_IsDigit.GetChar(datatype) == 'X' &&
272 value != GRIB_NOTDEF) {
273 value += 180;
274 if (value >= 360) value -= 360;
275 if (value < 0) value += 360;
276 }
277 SetCellRenderer(
278 row, col, new CustomRenderer(value, m_IsDigit.GetChar(datatype) == 'X'));
279}
280
281void CustomGrid::OnMouseEvent(wxMouseEvent& event) {
282 static wxPoint s_pevt;
283 wxPoint pevt = event.GetPosition();
284#ifdef __WXOSX__
285 if (!m_bLeftDown && event.LeftIsDown()) {
286 m_bLeftDown = true;
287 s_pevt = pevt;
288 } else if (m_bLeftDown && !event.LeftIsDown()) {
289 m_bLeftDown = false;
290 if (HasCapture()) ReleaseMouse();
291 }
292#else
293 if (event.LeftDown()) s_pevt = pevt;
294 if (event.LeftUp()) {
295 if (HasCapture()) ReleaseMouse();
296 }
297#endif
298 if (event.Dragging()) {
299 int frow, fcol, lrow, lcol;
300 GetFirstVisibleCell(frow, fcol);
301 GetLastVisibleCell(lrow, lcol);
302 if (pevt != s_pevt) {
303 bool rfh = false;
304 int diff = pevt.x - s_pevt.x;
305 // scrolling right
306 if (diff > SCROLL_SENSIBILITY) {
307 s_pevt.x = pevt.x;
308 if (fcol > 0) {
309 MakeCellVisible(frow, fcol - 1);
310 rfh = true;
311 }
312 }
313 // scrolling left
314 else if (-diff > SCROLL_SENSIBILITY) {
315 s_pevt.x = pevt.x;
316 if (lcol < m_numCols - 1) {
317 MakeCellVisible(frow, lcol + 1);
318 rfh = true;
319 }
320 }
321 // scrolling down
322 diff = pevt.y - s_pevt.y;
323 if (diff > SCROLL_SENSIBILITY) {
324 s_pevt.y = pevt.y;
325 if (frow > 0) {
326 MakeCellVisible(frow - 1, fcol);
327 rfh = true;
328 }
329 }
330 // scrolling up
331 else if (-diff > SCROLL_SENSIBILITY) {
332 s_pevt.y = pevt.y;
333 if (lrow < m_numRows - 1) {
334 MakeCellVisible(lrow + 1, fcol);
335 MakeCellVisible(frow + 1,
336 fcol); // workaroud for what seems curious moving 2
337 // rows instead of 1 in previous function
338 rfh = true;
339 }
340 }
341 if (rfh) m_tRefreshTimer.Start(10, wxTIMER_ONE_SHOT);
342 }
343 }
344}
345
346bool CustomGrid::IsRowVisible(int row) {
347 for (int i = 0; i < m_numCols; i++) {
348 if (IsVisible(row, i, false)) return true;
349 }
350 return false;
351}
352
353// find the first top/left visible cell coords
354void CustomGrid::GetFirstVisibleCell(int& frow, int& fcol) {
355 bool vis = false;
356 frow = 0;
357 for (fcol = 0; fcol < m_numCols; fcol++) {
358 for (frow = 0; frow < m_numRows; frow++) {
359 if (IsVisible(frow, fcol)) { // find the first row/col
360 vis = true;
361 break;
362 }
363 }
364 if (vis) break;
365 }
366}
367
368// find the visible cell coords
369void CustomGrid::GetLastVisibleCell(int& lrow, int& lcol) {
370 bool vis = false;
371 lrow = wxMax(m_numRows - 1, 0);
372 for (lcol = wxMax(m_numCols - 1, 0); lcol > -1; lcol--) {
373 for (lrow = m_numRows - 1; lrow > -1; lrow--) {
374 if (IsVisible(lrow, lcol)) {
375 vis = true;
376 break;
377 }
378 }
379 if (vis) break;
380 }
381}
382
383//------------------------------------------------------------------------------
384// custom renderer
385//------------------------------------------------------------------------------
386void CustomRenderer::Draw(wxGrid& grid, wxGridCellAttr& attr, wxDC& dc,
387 const wxRect& rect, int row, int col,
388 bool isSelected) {
389 dc.SetPen(wxPen(attr.GetBackgroundColour(), 1));
390 dc.SetBrush(wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
391 dc.DrawRectangle(rect);
392 if (m_IsDigit || m_dDir == GRIB_NOTDEF) { // digital format
393 wxString text(wxEmptyString);
394 if (m_dDir != GRIB_NOTDEF) text.Printf(_T("%03d%c"), (int)m_dDir, 0x00B0);
395 dc.DrawLabel(text, rect,
396 wxALIGN_CENTER_HORIZONTAL | wxALIGN_CENTER_VERTICAL);
397 } else { // graphical format
398 double si = sin((m_dDir - 90) * M_PI / 180.);
399 double co = cos((m_dDir - 90) * M_PI / 180.);
400
401 int i = rect.GetTopLeft().x + (rect.GetWidth() / 2);
402 int j = rect.GetTopLeft().y + (rect.GetHeight() / 2);
403
404 int arrowSize = rect.GetHeight() - 3;
405 int dec = -arrowSize / 2;
406
407#if wxUSE_GRAPHICS_CONTEXT
408 wxGraphicsContext* gdc;
409 wxClientDC* cdc = new wxClientDC(dynamic_cast<wxWindow*>(&grid));
410 cdc = dynamic_cast<wxClientDC*>(&dc);
411 if (cdc) {
412 gdc = wxGraphicsContext::Create(*cdc);
413#ifdef __WXGTK__
414 /*platforms don't manage the same way the gdc origin
415 for linux, we have to re-compute the good one.
416 To DO : verify it works on all other plateforms (done for MSW*/
417 bool vis = false;
418 int r = 0;
419 for (int c = 0; c < grid.GetNumberCols(); c++) {
420 for (r = 0; r < grid.GetNumberRows(); r++) {
421 if (grid.IsVisible(r, c)) { // find the first row/col
422 vis = true;
423 i -= (c * grid.GetColSize(0));
424 j -= (r * grid.GetRowHeight(0));
425 break;
426 }
427 }
428 if (vis) break;
429 }
430#endif
431 gdc->SetPen(wxPen(attr.GetTextColour(), 3));
432 gdc->SetBrush(wxBrush(attr.GetBackgroundColour(), wxBRUSHSTYLE_SOLID));
433
434 double ii, jj, kk, ll;
435 GetArrowsPoints(si, co, i, j, dec, 0, dec + arrowSize, 0, ii, jj, kk, ll);
436 gdc->StrokeLine(ii, jj, kk, ll);
437 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, 3, ii, jj, kk, ll);
438 gdc->StrokeLine(ii, jj, kk, ll);
439 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, -3, ii, jj, kk, ll);
440 gdc->StrokeLine(ii, jj, kk, ll);
441 delete gdc;
442 } else
443#endif
444 {
445 dc.SetPen(wxPen(attr.GetTextColour(), 3));
446 double ii, jj, kk, ll;
447 GetArrowsPoints(si, co, i, j, dec, 0, dec + arrowSize, 0, ii, jj, kk, ll);
448 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
449 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, 3, ii, jj, kk, ll);
450 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
451 GetArrowsPoints(si, co, i, j, dec - 3, 0, dec + 5, -3, ii, jj, kk, ll);
452 dc.DrawLine((int)ii, (int)jj, (int)kk, (int)ll);
453 }
454 }
455}
456
457void CustomRenderer::GetArrowsPoints(double si, double co, int di, int dj,
458 int i, int j, int k, int l, double& ii,
459 double& jj, double& kk, double& ll) {
460 ii = (i * co - j * si + 0.5) + di;
461 jj = (i * si + j * co + 0.5) + dj;
462 kk = (k * co - l * si + 0.5) + di;
463 ll = (k * si + l * co + 0.5) + dj;
464}
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.