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