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