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