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