OpenCPN Partial API docs
piano.cpp
1 /******************************************************************************
2  *
3  * Project: OpenCPN
4  * Purpose: Chart Bar Window
5  * Author: David Register
6  *
7  ***************************************************************************
8  * Copyright (C) 2010 by David S. Register *
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  * This program is distributed in the hope that it will be useful, *
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18  * GNU General Public License for more details. *
19  * *
20  * You should have received a copy of the GNU General Public License *
21  * along with this program; if not, write to the *
22  * Free Software Foundation, Inc., *
23  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24  ***************************************************************************
25  *
26  *
27  */
28 
29 #include <wx/wxprec.h>
30 
31 #ifndef WX_PRECOMP
32 #include <wx/wx.h>
33 #endif // precompiled headers
34 #include "dychart.h"
35 
36 #include "model/config_vars.h"
37 #include "model/cutil.h"
38 #include "model/wx28compat.h"
39 
40 #include "chcanv.h"
41 #include "piano.h"
42 #include "chartdb.h"
43 #include "chartbase.h"
44 #include "styles.h"
45 #include "ocpndc.h"
46 #include "OCPNPlatform.h"
47 #include "color_handler.h"
48 #include "ocpn_plugin.h"
49 #include "ocpn_frame.h"
50 
51 #ifdef __OCPN__ANDROID__
52 #include "qdebug.h"
53 #include "androidUTIL.h"
54 #endif
55 
56 #ifdef ocpnUSE_GL
57 #include "glChartCanvas.h"
58 #endif
59 
60 #include <wx/arrimpl.cpp>
61 WX_DEFINE_OBJARRAY(RectArray);
62 
63 //------------------------------------------------------------------------------
64 // External Static Storage
65 //------------------------------------------------------------------------------
66 extern ChartDB *ChartData;
67 extern ocpnStyle::StyleManager *g_StyleManager;
68 extern int g_GUIScaleFactor;
69 extern bool g_bopengl;
70 extern float g_toolbar_scalefactor;
71 
72 extern OCPNPlatform *g_Platform;
73 extern MyFrame *gFrame;
74 extern BasePlatform *g_BasePlatform;
75 
76 //------------------------------------------------------------------------------
77 // Piano Window Implementation
78 //------------------------------------------------------------------------------
79 BEGIN_EVENT_TABLE(Piano, wxEvtHandler)
80 EVT_TIMER(PIANO_EVENT_TIMER, Piano::onTimerEvent)
81 END_EVENT_TABLE()
82 
83 // Define a constructor
84 Piano::Piano(ChartCanvas *parent) {
85  m_parentCanvas = parent;
86 
87  m_index_last = -1;
88  m_iactive = -1;
89 
90  m_hover_icon_last = -1;
91  m_hover_last = -1;
92  m_brounded = false;
93  m_bBusy = false;
94  m_gotPianoDown = false;
95 
96  m_nRegions = 0;
97  m_width = 0;
98 
99  //> SetBackgroundStyle( wxBG_STYLE_CUSTOM ); // on WXMSW, this prevents
100  //flashing on color scheme change
101 
102  m_pVizIconBmp = NULL;
103  m_pInVizIconBmp = NULL;
104  m_pPolyIconBmp = NULL;
105  m_pSkewIconBmp = NULL;
106  m_pTmercIconBmp = NULL;
107 
108  SetColorScheme(GLOBAL_COLOR_SCHEME_RGB); // default
109 
110  m_eventTimer.SetOwner(this, PIANO_EVENT_TIMER);
111 
112  m_tex = m_tex_piano_height = 0;
113  m_piano_mode = PIANO_MODE_LEGACY;
114 }
115 
116 Piano::~Piano() {
117  if (m_pInVizIconBmp) delete m_pInVizIconBmp;
118  if (m_pPolyIconBmp) delete m_pPolyIconBmp;
119  if (m_pSkewIconBmp) delete m_pSkewIconBmp;
120  if (m_pTmercIconBmp) delete m_pTmercIconBmp;
121  if (m_pVizIconBmp) delete m_pVizIconBmp;
122 }
123 
124 void Piano::Paint(int y, wxDC &dc, wxDC *shapeDC) {
125  ocpnDC odc(dc);
126  Paint(y, odc, shapeDC);
127 }
128 
129 void Piano::Paint(int y, ocpnDC &dc, wxDC *shapeDC) {
130  if (shapeDC) {
131  shapeDC->SetBackground(*wxBLACK_BRUSH);
132  shapeDC->SetBrush(*wxWHITE_BRUSH);
133  shapeDC->SetPen(*wxWHITE_PEN);
134  shapeDC->Clear();
135  }
136 
137  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
138  if (!style->chartStatusWindowTransparent) {
139  dc.SetPen(*wxTRANSPARENT_PEN);
140  dc.SetBrush(m_backBrush);
141  dc.DrawRectangle(0, y, m_parentCanvas->GetClientSize().x, GetHeight());
142  }
143 
144  // Create the Piano Keys
145 
146  int nKeys = m_composite_array.size();
147 
148  wxPen ppPen(GetGlobalColor(_T("CHBLK")), 1, wxPENSTYLE_SOLID);
149  dc.SetPen(ppPen);
150 
151  for (int i = 0; i < nKeys; i++) {
152  int chart_family = m_composite_array[i].chart_family;
153  int chart_type = m_composite_array[i].chart_type;
154 
155  bool selected = IsAnyActiveChartInPianoKeyElement(m_composite_array[i]);
156 
157  if (chart_type == CHART_TYPE_CM93 ||
158  chart_type == CHART_TYPE_CM93COMP) {
159  if (selected)
160  dc.SetBrush(m_scBrush);
161  else
162  dc.SetBrush(m_cBrush);
163  } else if (chart_type == CHART_TYPE_MBTILES) {
164  if (selected)
165  dc.SetBrush(m_tileBrush);
166  else
167  dc.SetBrush(m_utileBrush);
168  } else if (chart_family == CHART_FAMILY_VECTOR) {
169  if (selected)
170  dc.SetBrush(m_svBrush);
171  else
172  dc.SetBrush(m_vBrush);
173  } else { // Raster Chart
174  if (selected)
175  dc.SetBrush(m_srBrush);
176  else
177  dc.SetBrush(m_rBrush);
178  }
179 
180  if (m_bBusy) dc.SetBrush(m_unavailableBrush);
181 
182  wxRect box = KeyRect[i];
183  box.y += y;
184 
185  if (m_brounded) {
186  dc.DrawRoundedRectangle(box.x, box.y, box.width, box.height,
187  box.height / 5);
188  if (shapeDC)
189  shapeDC->DrawRoundedRectangle(box.x, box.y, box.width, box.height,
190  box.height / 5);
191  } else {
192  dc.DrawRectangle(box.x, box.y, box.width, box.height);
193  if (shapeDC) shapeDC->DrawRectangle(box);
194  }
195 
196  if (IsAllEclipsedChartInPianoKeyElement(m_composite_array[i])) {
197  //if (InArray(m_eclipsed_index_array, key_db_index)) {
198  dc.SetBrush(m_backBrush);
199  int w = 3;
200  dc.DrawRoundedRectangle(box.x + w, box.y + w, box.width - (2 * w),
201  box.height - (2 * w), box.height / 5 - 1);
202  }
203 
204 #if 0
205  // Look in the current noshow array for this index
206  if (InArray(m_noshow_index_array, key_db_index) && m_pInVizIconBmp &&
207  m_pInVizIconBmp->IsOk())
208  dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pInVizIconBmp),
209  box.x + 4, box.y + 3, false);
210 
211  // Look in the current skew array for this index
212  if (InArray(m_skew_index_array, key_db_index) && m_pSkewIconBmp &&
213  m_pSkewIconBmp->IsOk())
214  dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pSkewIconBmp),
215  box.x + box.width - m_pSkewIconBmp->GetWidth() - 4,
216  box.y + 2, false);
217 
218  // Look in the current tmerc array for this index
219  if (InArray(m_tmerc_index_array, key_db_index) && m_pTmercIconBmp &&
220  m_pTmercIconBmp->IsOk())
221  dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pTmercIconBmp),
222  box.x + box.width - m_pTmercIconBmp->GetWidth() - 4,
223  box.y + 2, false);
224 
225  // Look in the current poly array for this index
226  if (InArray(m_poly_index_array, key_db_index) && m_pPolyIconBmp &&
227  m_pPolyIconBmp->IsOk())
228  dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pPolyIconBmp),
229  box.x + box.width - m_pPolyIconBmp->GetWidth() - 4,
230  box.y + 2, false);
231 #endif
232  }
233 }
234 
235 #if 0
236 static void SetColor(unsigned char color[4], const wxBrush &brush)
237 {
238  const wxColour &c = brush.GetColour();
239  color[0] = c.Red(), color[1] = c.Green(), color[2] = c.Blue(), color[3] = 255;
240 }
241 #endif
242 
243 // build a texture to hold minimum sized rectangles and icons used to render the
244 // chart bar this texture is only updated if the color scheme or chart bar
245 // height change
246 void Piano::BuildGLTexture() {
247 #ifdef ocpnUSE_GL
248 
249  // Defer building until auxiliary bitmaps have been loaded
250  if (!m_pInVizIconBmp || !m_pTmercIconBmp || !m_pSkewIconBmp ||
251  !m_pPolyIconBmp)
252  return;
253 
254  int h = GetHeight();
255 
256  wxBrush tbackBrush; // transparent back brush
257  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
258  if (style->chartStatusWindowTransparent)
259  tbackBrush = wxColour(1, 1, 1);
260  else
261  tbackBrush = m_backBrush;
262 
263  wxBrush brushes[] = {m_scBrush, m_cBrush, m_svBrush,
264  m_vBrush, m_srBrush, m_rBrush,
265  m_tileBrush, m_utileBrush, m_unavailableBrush};
266 
267  m_ref = h;
268  m_pad = h / 7; // spacing between buttons
269  m_radius = h / 4;
270  m_texPitch = ((2 * m_ref) + (2 * m_pad));
271 
272  m_tex_piano_height = h;
273  m_texw = m_texPitch * 3;
274 
275  m_texh = ((sizeof brushes) / (sizeof *brushes)) * h;
276  m_texh += 4 * m_ref; // for icons;
277 
278  m_texh = NextPow2(m_texh);
279  m_texw = NextPow2(m_texw);
280 
281  if (!m_tex) glGenTextures(1, (GLuint *)&m_tex);
282 
283  glBindTexture(GL_TEXTURE_2D, m_tex);
284  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
285  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
286 
287  wxBitmap bmp(m_texw, m_texh);
288  wxMemoryDC dc(bmp);
289 
290  dc.SetPen(*wxTRANSPARENT_PEN);
291  dc.SetBrush(tbackBrush);
292  dc.DrawRectangle(0, 0, m_texw, m_texh);
293 
294  // 0.5 mm nominal, but not less than 1 pixel
295  double nominal_line_width_pix = floor(g_Platform->GetDisplayDPmm() / 2.0);
296  nominal_line_width_pix *= OCPN_GetWinDIPScaleFactor();
297  nominal_line_width_pix = wxMax(1.0, nominal_line_width_pix);
298 
299  // draw the needed rectangles
300  wxPen ppPen(GetGlobalColor(_T("CHBLK")), nominal_line_width_pix,
301  wxPENSTYLE_SOLID);
302  dc.SetPen(ppPen);
303  for (unsigned int b = 0; b < (sizeof brushes) / (sizeof *brushes); b++) {
304  unsigned int x = 0, y = h * b;
305 
306  dc.SetBrush(brushes[b]);
307 
308  int v = 2;
309  dc.DrawRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v);
310 
311  x += m_texPitch;
312  dc.DrawRoundedRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v, m_radius);
313 
314  int w = m_ref / 6; // border width of eclipsed chart
315 
316  x += m_texPitch;
317  dc.DrawRoundedRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v, m_radius);
318  dc.SetBrush(m_backBrush);
319  dc.DrawRoundedRectangle(x + m_pad + w, y + v + w, (2 * m_ref) - (2 * w),
320  h - 2 * v - 2 * w,
321  m_radius * (h - 2 * v - 2 * w) /
322  (h - 2 * v)); // slightly smaller radius
323  }
324 
325  dc.SelectObject(wxNullBitmap);
326 
327  wxImage image = bmp.ConvertToImage();
328 
329  unsigned char *data = new unsigned char[4 * m_texw * m_texh], *d = data,
330  *e = image.GetData(), *a = image.GetAlpha();
331  for (unsigned int i = 0; i < m_texw * m_texh; i++) {
332  if (style->chartStatusWindowTransparent && e[0] == 1 && e[1] == 1 &&
333  e[2] == 1)
334  d[3] = 0;
335  else
336  d[3] = 255;
337 
338  memcpy(d, e, 3), d += 4, e += 3;
339  }
340 
341  glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texw, m_texh, 0, GL_RGBA,
342  GL_UNSIGNED_BYTE, data);
343  delete[] data;
344 
345  // put the bitmaps in below
346  wxBitmap *bitmaps[] = {m_pInVizIconBmp, m_pTmercIconBmp, m_pSkewIconBmp,
347  m_pPolyIconBmp};
348 
349  for (unsigned int i = 0; i < 4; i++) {
350  int iw = bitmaps[i]->GetWidth(), ih = bitmaps[i]->GetHeight();
351 
352  wxImage im = bitmaps[i]->ConvertToImage();
353  unsigned char *data = new unsigned char[4 * iw * ih], *d = data,
354  *e = im.GetData(), *a = im.GetAlpha();
355  for (int j = 0; j < iw * ih; j++) {
356  memcpy(d, e, 3), d += 3, e += 3;
357  *d = *a, d++, a++;
358  }
359 
360  int off = ((sizeof brushes) / (sizeof *brushes)) * h + m_ref * i;
361  glTexSubImage2D(GL_TEXTURE_2D, 0, 0, off, iw, ih, GL_RGBA, GL_UNSIGNED_BYTE,
362  data);
363  delete[] data;
364  }
365 #endif
366 }
367 
368 void Piano::DrawGL(int off) {
369  return DrawGLSL(off);
370 }
371 void Piano::DrawGLSL(int off) {
372 #ifdef ocpnUSE_GL
373  unsigned int w = m_parentCanvas->GetClientSize().x *
374  m_parentCanvas->GetContentScaleFactor();
375  int h = GetHeight();
376  unsigned int endx = 0;
377 
378  if (static_cast<int>(m_tex_piano_height) != h) BuildGLTexture();
379 
380  if (static_cast<int>(m_tex_piano_height) != h) return;
381 
382  int y1 = off, y2 = y1 + h;
383 
384  int nKeys = m_composite_array.size();
385 
386  // we could cache the coordinates and recompute only when the piano hash
387  // changes, but the performance is already fast enough at this point
388  float *texcoords = new float[(nKeys * 3 + 1) * 4 * 2],
389  *coords = new float[(nKeys * 3 + 1) * 4 * 2];
390 
391  int tc = 0, vc = 0;
392 
393  // draw the keys
394  for (int i = 0; i < nKeys; i++) {
395  int b;
396  int chart_family = m_composite_array[i].chart_family;
397  int chart_type = m_composite_array[i].chart_type;
398 
399  if (chart_type == CHART_TYPE_CM93 || chart_type == CHART_TYPE_CM93COMP)
400  b = 0;
401  else if (chart_type == CHART_TYPE_MBTILES)
402  b = 6;
403  else if (chart_family == CHART_FAMILY_VECTOR)
404  b = 2;
405  else // Raster Chart
406  b = 4;
407 
408  if (!IsAnyActiveChartInPianoKeyElement(m_composite_array[i]))
409  b++; // render color inactive
410 
411  wxRect box = KeyRect[i];
412  float y = h * b, v1 = (y + .5) / m_texh, v2 = (y + h - .5) / m_texh;
413 
414  // texcord contains the texture pixel coordinates in the texture for the
415  // three rectangle parts
416  const float texcord[6] = {0,
417  (float)m_ref - 1,
418  (float)m_ref,
419  (float)m_ref,
420  (float)m_ref + 1,
421  (float)m_texPitch - 1};
422 
423  int uindex;
424  if (m_brounded) {
425  if (IsAllEclipsedChartInPianoKeyElement(m_composite_array[i]))
426  uindex = 2;
427  else
428  uindex = 1;
429  } else
430  uindex = 0;
431 
432  // if the chart is too narrow.. we maybe render the "wrong" rectangle
433  // because it can be thinner
434  int x1 = box.x, x2 = x1 + box.width, w = 2 * uindex + 1;
435  while (x1 + w > x2 - w && uindex > 0) uindex--, w -= 2;
436 
437  // the minimal width rectangles are texture mapped to the
438  // width needed by mapping 3 quads: left middle and right
439  int x[6] = {x1 - 3, x1 + m_ref, x2 - m_ref, x2 + 3};
440 
441  // adjust for very narrow keys
442  if (x[1] > x[2]) {
443  int avg = (x[1] + x[2]) / 2;
444  x[1] = x[2] = avg;
445  }
446 
447  for (int i = 0; i < 3; i++) {
448  float u1 = ((uindex * m_texPitch) + texcord[2 * i] + .5) / m_texw,
449  u2 = ((uindex * m_texPitch) + texcord[2 * i + 1] + .5) / m_texw;
450  int x1 = x[i], x2 = x[i + 1];
451  texcoords[tc++] = u1, texcoords[tc++] = v1, coords[vc++] = x1,
452  coords[vc++] = y1;
453  texcoords[tc++] = u2, texcoords[tc++] = v1, coords[vc++] = x2,
454  coords[vc++] = y1;
455  texcoords[tc++] = u2, texcoords[tc++] = v2, coords[vc++] = x2,
456  coords[vc++] = y2;
457  texcoords[tc++] = u1, texcoords[tc++] = v2, coords[vc++] = x1,
458  coords[vc++] = y2;
459  }
460  endx = x[3];
461  }
462 
463  // if not transparent, fill the rest of the chart bar with the background
464  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
465  if (!style->chartStatusWindowTransparent && endx < w) {
466  texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = endx,
467  coords[vc++] = y1;
468  texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = w,
469  coords[vc++] = y1;
470  texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = w,
471  coords[vc++] = y2;
472  texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = endx,
473  coords[vc++] = y2;
474  }
475 
476  glBindTexture(GL_TEXTURE_2D, m_tex);
477 
478  glEnable(GL_TEXTURE_2D);
479 
480  glEnable(GL_BLEND);
481  m_parentCanvas->GetglCanvas()->RenderTextures(
482  m_parentCanvas->GetglCanvas()->m_gldc, coords, texcoords, vc / 2,
483  m_parentCanvas->GetpVP());
484  glDisable(GL_BLEND);
485 
486  glDisable(GL_BLEND);
487 
488  // draw the bitmaps, if any
489  if (GetPianoMode() == PIANO_MODE_LEGACY) {
490  vc = tc = 0;
491  for (int i = 0; i < nKeys; i++) {
492  int key_db_index = m_composite_array[i].dbindex_list[0];
493 
494  if (-1 == key_db_index) continue;
495 
496  wxRect box = KeyRect[i];
497 
498  wxBitmap *bitmaps[] = {m_pInVizIconBmp, m_pTmercIconBmp, m_pSkewIconBmp,
499  m_pPolyIconBmp};
500  int index;
501  if (InArray(m_noshow_index_array, key_db_index))
502  index = 0;
503  else {
504  if (InArray(m_skew_index_array, key_db_index))
505  index = 2;
506  else if (InArray(m_tmerc_index_array, key_db_index))
507  index = 1;
508  else if (InArray(m_poly_index_array, key_db_index))
509  index = 3;
510  else
511  continue;
512  }
513 
514  int x1, y1, iw = bitmaps[index]->GetWidth(),
515  ih = bitmaps[index]->GetHeight();
516  if (InArray(m_noshow_index_array, key_db_index))
517  x1 = box.x + 4, y1 = box.y + 3;
518  else
519  x1 = box.x + box.width - iw - 4, y1 = box.y + 2;
520 
521  y1 += off;
522  int x2 = x1 + iw, y2 = y1 + ih;
523 
524  wxBrush brushes[] = {m_scBrush, m_cBrush, m_svBrush,
525  m_vBrush, m_srBrush, m_rBrush,
526  m_tileBrush, m_utileBrush, m_unavailableBrush};
527 
528  float yoff = ((sizeof brushes) / (sizeof *brushes)) * h + 16 * index;
529  float u1 = 0, u2 = (float)iw / m_texw;
530  float v1 = yoff / m_texh, v2 = (yoff + ih) / m_texh;
531 
532  texcoords[tc++] = u1, texcoords[tc++] = v1, coords[vc++] = x1,
533  coords[vc++] = y1;
534  texcoords[tc++] = u2, texcoords[tc++] = v1, coords[vc++] = x2,
535  coords[vc++] = y1;
536  texcoords[tc++] = u2, texcoords[tc++] = v2, coords[vc++] = x2,
537  coords[vc++] = y2;
538  texcoords[tc++] = u1, texcoords[tc++] = v2, coords[vc++] = x1,
539  coords[vc++] = y2;
540 
541  glEnable(GL_TEXTURE_2D);
542  glBindTexture(GL_TEXTURE_2D, m_tex);
543  glEnable(GL_BLEND);
544 
545  m_parentCanvas->GetglCanvas()->RenderTextures(
546  m_parentCanvas->GetglCanvas()->m_gldc, coords, texcoords, vc / 2,
547  m_parentCanvas->GetpVP());
548  }
549  }
550 
551  glDisable(GL_BLEND);
552  glDisable(GL_TEXTURE_2D);
553  delete[] texcoords;
554  delete[] coords;
555 #endif //gl
556 }
557 
558 void Piano::SetColorScheme(ColorScheme cs) {
559  // Recreate the local brushes
560 
561  m_backBrush = wxBrush(GetGlobalColor(_T("UIBDR")), wxBRUSHSTYLE_SOLID);
562 
563  m_rBrush = wxBrush(GetGlobalColor(_T("BLUE2")),
564  wxBRUSHSTYLE_SOLID); // Raster Chart unselected
565  m_srBrush =
566  wxBrush(GetGlobalColor(_T("BLUE1")), wxBRUSHSTYLE_SOLID); // and selected
567 
568  m_vBrush = wxBrush(GetGlobalColor(_T("GREEN2")),
569  wxBRUSHSTYLE_SOLID); // Vector Chart unselected
570  m_svBrush = wxBrush(GetGlobalColor(_T("GREEN1")),
571  wxBRUSHSTYLE_SOLID); // and selected
572 
573  m_utileBrush = wxBrush(GetGlobalColor(_T("VIO01")),
574  wxBRUSHSTYLE_SOLID); // MBTiles Chart unselected
575  m_tileBrush =
576  wxBrush(GetGlobalColor(_T("VIO02")), wxBRUSHSTYLE_SOLID); // and selected
577 
578  m_cBrush = wxBrush(GetGlobalColor(_T("YELO2")),
579  wxBRUSHSTYLE_SOLID); // CM93 Chart unselected
580  m_scBrush =
581  wxBrush(GetGlobalColor(_T("YELO1")), wxBRUSHSTYLE_SOLID); // and selected
582 
583  m_unavailableBrush = wxBrush(GetGlobalColor(_T("UINFD")),
584  wxBRUSHSTYLE_SOLID); // and unavailable
585 
586  m_tex_piano_height = 0; // force texture to update
587 }
588 
589 void Piano::ShowBusy(bool busy) {
590  m_bBusy = busy;
591  // Refresh( true );
592  // Update();
593 }
594 
595 bool Piano::IsAnyActiveChartInPianoKeyElement(PianoKeyElement &pke){
596  for (auto &index : m_active_index_array){
597  auto found = find(pke.dbindex_list.begin(), pke.dbindex_list.end(), index);
598  if (found != pke.dbindex_list.end())
599  return true;
600  }
601  return false;
602 }
603 
604 bool Piano::IsAllEclipsedChartInPianoKeyElement(PianoKeyElement &pke){
605  bool bfound_all = true;
606  for (auto &index : pke.dbindex_list){
607  auto found = find(m_eclipsed_index_array.begin(), m_eclipsed_index_array.end(), index);
608  if (found == m_eclipsed_index_array.end())
609  bfound_all = false;
610  }
611  return bfound_all;
612 }
613 
614 void Piano::SetKeyArray(std::vector<int> &center_array, std::vector<int> &full_array) {
615  // Measure the available space for keys, and so decide on mode
616  // To account for scaling, etc., measure in parent base character size.
617  if (center_array.size()) {
618  int refd = m_parentCanvas->GetCharWidth();
619 
620  int nkeys = center_array.size();
621  int key_width = (m_width_avail / refd) / nkeys;
622  if (key_width < 8) {
623  m_piano_mode = PIANO_MODE_COMPOSITE;
624  m_key_array = full_array;
625  } else {
626  m_piano_mode = PIANO_MODE_LEGACY;
627  m_key_array = center_array;
628  }
629  } else {
630  m_piano_mode = PIANO_MODE_LEGACY;
631  m_key_array.clear();
632  }
633 
634 
635  // Create the composite array
636  m_composite_array.clear();
637 
638  if (m_piano_mode == PIANO_MODE_COMPOSITE) {
639  for (size_t i = 0; i < m_key_array.size(); i++) {
640  const ChartTableEntry &cte = ChartData->GetChartTableEntry(m_key_array[i]);
641  int scale = cte.GetScale();
642  auto order = std::pow(10, std::floor(std::log10(scale)));
643  scale = std::ceil(scale / order) * order;
644 
645  int chart_type = cte.GetChartType();
646  int chart_family = cte.GetChartFamily();
647 
648  // Perform scale compositing only for vector-family charts
649  // and Raster Family charts excluding MBTiles..
650  // All other families/types retain legacy piano keys, implemented as
651  // PianoKeyElement with only one chart in the array.
652  if ((cte.GetChartFamily() == CHART_FAMILY_VECTOR) ||
653  ((cte.GetChartFamily() == CHART_FAMILY_RASTER) &&
654  (cte.GetChartType() != CHART_TYPE_MBTILES))) {
655  auto predicate = [scale, chart_family](const PianoKeyElement &pke) {
656  return ((scale == pke.chart_scale) && (chart_family == pke.chart_family));
657  };
658  auto found = find_if(m_composite_array.begin(), m_composite_array.end(),
659  predicate);
660  if (found == m_composite_array.end()) { // need a new PianoKeyElement
661  PianoKeyElement new_pke;
662  new_pke.chart_scale = scale;
663  new_pke.chart_family = (ChartFamilyEnum)cte.GetChartFamily();
664  new_pke.chart_type = (ChartTypeEnum)cte.GetChartType();
665  new_pke.dbindex_list.push_back(m_key_array[i]);
666  m_composite_array.push_back(new_pke);
667  } else {
668  PianoKeyElement &ex_pke = *found;
669  ex_pke.dbindex_list.push_back(m_key_array[i]);
670  }
671  } else {
672  PianoKeyElement new_pke;
673  new_pke.chart_scale = scale;
674  new_pke.chart_family = (ChartFamilyEnum)cte.GetChartFamily();
675  new_pke.chart_type = (ChartTypeEnum)cte.GetChartType();
676  new_pke.dbindex_list.push_back(m_key_array[i]);
677  m_composite_array.push_back(new_pke);
678  }
679  }
680  }
681  else {
682  for (size_t i = 0; i < m_key_array.size(); i++) {
683  const ChartTableEntry &cte = ChartData->GetChartTableEntry(m_key_array[i]);
684  int scale = cte.GetScale();
685  int chart_type = cte.GetChartType();
686  PianoKeyElement new_pke;
687  new_pke.chart_scale = scale;
688  new_pke.chart_family = (ChartFamilyEnum)cte.GetChartFamily();
689  new_pke.chart_type = (ChartTypeEnum)cte.GetChartType();
690  new_pke.dbindex_list.push_back(m_key_array[i]);
691  m_composite_array.push_back(new_pke);
692  }
693  }
694 
695  // Sort the composite array
696  std::sort(m_composite_array.begin(),m_composite_array.end(),
697  [](PianoKeyElement &a, PianoKeyElement &b){ return a.chart_scale < b.chart_scale; });
698 
699 }
700 
701 void Piano::SetNoshowIndexArray(std::vector<int> array) {
702  m_noshow_index_array = array;
703 }
704 
705 void Piano::AddNoshowIndexArray(std::vector<int> array) {
706  for (unsigned int i = 0; i < array.size(); i++) {
707  m_noshow_index_array.push_back(array[i]);
708  }
709 }
710 
711 void Piano::SetActiveKeyArray(std::vector<int> array) {
712  m_active_index_array = array;
713 }
714 
715 void Piano::SetEclipsedIndexArray(std::vector<int> array) {
716  m_eclipsed_index_array = array;
717 }
718 
719 void Piano::SetSkewIndexArray(std::vector<int> array) {
720  m_skew_index_array = array;
721 }
722 
723 void Piano::SetTmercIndexArray(std::vector<int> array) {
724  m_tmerc_index_array = array;
725 }
726 
727 void Piano::SetPolyIndexArray(std::vector<int> array) {
728  m_poly_index_array = array;
729 }
730 
731 bool Piano::InArray(std::vector<int> &array, int key) {
732  for (unsigned int ino = 0; ino < array.size(); ino++)
733  if (array[ino] == key) return true;
734  return false;
735 }
736 
737 wxString Piano::GetStateHash() {
738  wxString hash;
739 
740  for (unsigned int i = 0; i < m_key_array.size(); i++) {
741  wxString a;
742  a.Printf(_T("%dK"), m_key_array[i]);
743  hash += a;
744  }
745  for (unsigned int i = 0; i < m_noshow_index_array.size(); i++) {
746  wxString a;
747  a.Printf(_T("%dN"), m_noshow_index_array[i]);
748  hash += a;
749  }
750  for (unsigned int i = 0; i < m_active_index_array.size(); i++) {
751  wxString a;
752  a.Printf(_T("%dA"), m_active_index_array[i]);
753  hash += a;
754  }
755  for (unsigned int i = 0; i < m_eclipsed_index_array.size(); i++) {
756  wxString a;
757  a.Printf(_T("%dE"), m_eclipsed_index_array[i]);
758  hash += a;
759  }
760  for (unsigned int i = 0; i < m_skew_index_array.size(); i++) {
761  wxString a;
762  a.Printf(_T("%dW"), m_skew_index_array[i]);
763  hash += a;
764  }
765  for (unsigned int i = 0; i < m_tmerc_index_array.size(); i++) {
766  wxString a;
767  a.Printf(_T("%dM"), m_tmerc_index_array[i]);
768  hash += a;
769  }
770  for (unsigned int i = 0; i < m_poly_index_array.size(); i++) {
771  wxString a;
772  a.Printf(_T("%dP"), m_poly_index_array[i]);
773  hash += a;
774  }
775 
776  return hash;
777 }
778 
779 wxString &Piano::GenerateAndStoreNewHash() {
780  m_hash = GetStateHash();
781  return m_hash;
782 }
783 
784 wxString &Piano::GetStoredHash() { return m_hash; }
785 
786 void Piano::FormatKeys(void) {
787  ocpnStyle::Style *style = g_StyleManager->GetCurrentStyle();
788  int width = m_parentCanvas->GetClientSize().x;
789 #ifdef __WXOSX__
790  if (g_bopengl)
791  width *= m_parentCanvas->GetContentScaleFactor();
792 #else
793  width *= m_parentCanvas->GetContentScaleFactor();
794 #endif
795 
796  // Estimate size of horizontal MUIBar
797  wxSize mui_tool_size = g_StyleManager->GetCurrentStyle()->GetToolSize();
798  // MuiBar has boosted the MUIButton default size by 125%
799  mui_tool_size = wxSize(mui_tool_size.x * 1.25, mui_tool_size.y * 1.25);
800  // Muibar horizontal is about 8 "icons" wide.
801  int mui_bar_width_est = mui_tool_size.x * 8 * g_toolbar_scalefactor;
802 
803  if (m_parentCanvas->GetClientSize().x < m_parentCanvas->GetClientSize().y){
804  //portrait mode, on a phone or tablet, etc.
805  width *= 0.6;
806  }
807  else {
808  width *= 0.6;
809  //width = wxMin(width, m_parentCanvas->GetClientSize().x - mui_bar_width_est);
810  }
811  //width = wxMax(width, mui_bar_width_est);
812 
813  // Max width available
814  m_width_avail = width;
815 
816  int height = GetHeight();
817 
818  int nKeys = m_composite_array.size();
819  int kw = style->chartStatusIconWidth;
820  if (nKeys) {
821  if (!kw) kw = width / nKeys;
822 
823  //kw = wxMin(kw, (width * 3 / 4) / nKeys);
824  kw = wxMax(kw, 6);
825 
826  // Build the Key Regions
827 
828  KeyRect.clear();
829  m_width = 0;
830  for (int i = 0; i < nKeys; i++) {
831  wxRect r((i * kw) + 3, 2, kw - 6, height - 4);
832  KeyRect.push_back(r);
833  m_width = r.x + r.width;
834  }
835  }
836  m_nRegions = nKeys;
837 }
838 
839 wxPoint Piano::GetKeyOrigin(int key_index) {
840  if ((key_index >= 0) && (key_index <= (int)m_key_array.size() - 1)) {
841  wxRect box = KeyRect[key_index];
842  return wxPoint(box.x, box.y);
843  } else
844  return wxPoint(-1, -1);
845 }
846 
847 bool Piano::MouseEvent(wxMouseEvent &event) {
848  int x, y;
849  event.GetPosition(&x, &y);
850 #ifdef __WXOSX__
851  if (g_bopengl){
852  x *= OCPN_GetDisplayContentScaleFactor();
853  y *= OCPN_GetDisplayContentScaleFactor();
854  }
855 #else
856  y *= OCPN_GetDisplayContentScaleFactor();
857 #endif
858  int ytop = m_parentCanvas->GetCanvasHeight() - GetHeight();
859 #ifdef __WXOSX__
860  if (!g_bopengl)
861  ytop = m_parentCanvas->GetClientSize().y - GetHeight();
862 #endif
863 
864  if (event.Leaving() || (y < ytop) || (x > GetWidth())) {
865  if (m_bleaving) return false;
866  m_bleaving = true;
867  } else {
868  m_bleaving = false;
869  }
870 
871  // Check the regions
872  int sel_index = -1;
873  int sel_dbindex = -1;
874 
875  for (int i = 0; i < m_nRegions; i++) {
876  if (KeyRect[i].Contains(x, 6)) {
877  sel_index = i;
878  sel_dbindex = m_key_array[i];
879  break;
880  }
881  }
882 
883  if (g_btouch) {
884  if (event.LeftDown()) {
885  if (-1 != sel_index) {
886  m_action = DEFERRED_KEY_CLICK_DOWN;
887  ShowBusy(true);
888  m_eventTimer.Start(10, wxTIMER_ONE_SHOT);
889  }
890  }
891  if (event.LeftUp()) {
892  if (-1 != sel_index) {
893  m_click_sel_index = sel_index;
894  if (!m_eventTimer.IsRunning()) {
895  m_action = DEFERRED_KEY_CLICK_UP;
896  m_eventTimer.Start(10, wxTIMER_ONE_SHOT);
897  }
898  }
899  } else if (event.RightDown()) {
900  if (sel_index != m_hover_last) {
901  if (sel_index >= 0)
902  m_parentCanvas->HandlePianoRollover(sel_index,
903  m_composite_array[sel_index].dbindex_list,
904  m_composite_array[sel_index].dbindex_list.size(),
905  m_composite_array[sel_index].chart_scale );
906  m_hover_last = sel_index;
907 
908  // m_action = INFOWIN_TIMEOUT;
909  // m_eventTimer.Start(3000, wxTIMER_ONE_SHOT);
910  }
911  } else if (event.ButtonUp()) {
912  m_parentCanvas->ClearPianoRollover();
913  ResetRollover();
914  }
915  } else {
916  if (m_bleaving) {
917  m_parentCanvas->ClearPianoRollover();
918  ResetRollover();
919  } else if (event.LeftDown()) {
920  if (-1 != sel_index) {
921  m_parentCanvas->HandlePianoClick(sel_index,
922  m_composite_array[sel_index].dbindex_list);
923 
924  m_parentCanvas->Raise();
925  } else
926  return false;
927  } else if (event.RightDown()) {
928  if (-1 != sel_index) {
929  m_parentCanvas->HandlePianoRClick(x, y, sel_index,
930  m_composite_array[sel_index].dbindex_list);
931  m_parentCanvas->Raise();
932  } else
933  return false;
934  } else if (!event.ButtonUp()) {
935  if (sel_index != m_hover_last) {
936  if (sel_index >= 0)
937  m_parentCanvas->HandlePianoRollover(sel_index,
938  m_composite_array[sel_index].dbindex_list,
939  m_composite_array[sel_index].dbindex_list.size(),
940  m_composite_array[sel_index].chart_scale );
941 
942  m_hover_last = sel_index;
943  }
944  }
945  }
946 
947  return true;
948 
949  /*
950  Todo:
951  Could do something like this to better encapsulate the pianowin
952  Allows us to get rid of global statics...
953 
954  wxCommandEvent ev(MyPianoEvent); // Private event
955  ..set up event to specify action...SelectChart, etc
956  ::PostEvent(pEventReceiver, ev); // event receiver passed to ctor
957 
958  */
959 }
960 
961 void Piano::ResetRollover(void) {
962  m_index_last = -1;
963  m_hover_icon_last = -1;
964  m_hover_last = -1;
965  m_gotPianoDown = false;
966 }
967 
968 int Piano::GetHeight() {
969  int height = 22 ;
970 #ifdef __WXOSX__
971  if (g_bopengl)
972  height *= m_parentCanvas->GetContentScaleFactor();
973 #endif
974  if (g_btouch) {
975  double size_mult = exp(g_GUIScaleFactor * 0.0953101798043); // ln(1.1)
976  height *= size_mult;
977  height = wxMin(height, 100); // absolute boundaries
978  height = wxMax(height, 10);
979  }
980  height *= g_Platform->GetDisplayDensityFactor();
981 
982 #ifdef __OCPN__ANDROID__
983  height = wxMax(height, 4 * g_Platform->GetDisplayDPmm());
984 #endif
985 
986  height /= g_BasePlatform->GetDisplayDIPMult(gFrame);
987 
988  return height;
989 }
990 
991 int Piano::GetWidth() { return m_width; }
992 
993 void Piano::onTimerEvent(wxTimerEvent &event) {
994  switch (m_action) {
995  case DEFERRED_KEY_CLICK_DOWN:
996  m_gotPianoDown = true;
997  break;
998  case DEFERRED_KEY_CLICK_UP:
999  ShowBusy(false);
1000  if ((m_hover_last >= 0) || !m_gotPianoDown) { // turn it off, and return
1001  m_parentCanvas->ClearPianoRollover();
1002  ResetRollover();
1003  } else {
1004  m_parentCanvas->HandlePianoClick(m_click_sel_index,
1005  m_composite_array[m_click_sel_index].dbindex_list);
1006  m_gotPianoDown = false;
1007  }
1008  break;
1009  case INFOWIN_TIMEOUT:
1010  m_parentCanvas->ClearPianoRollover();
1011  ResetRollover();
1012  break;
1013  default:
1014  break;
1015  }
1016 }
Definition: piano.h:71
Definition: ocpndc.h:58
Definition: Quilt.cpp:867