43#include "wx28compat.h"
44#include "OCPNPlatform.h"
45#include "color_handler.h"
46#include "ocpn_plugin.h"
47#include "ocpn_frame.h"
49#ifdef __OCPN__ANDROID__
51#include "androidUTIL.h"
55#include "glChartCanvas.h"
58#include <wx/arrimpl.cpp>
59WX_DEFINE_OBJARRAY(RectArray);
67extern int g_GUIScaleFactor;
77BEGIN_EVENT_TABLE(
Piano, wxEvtHandler)
78EVT_TIMER(PIANO_EVENT_TIMER, Piano::onTimerEvent)
83 m_parentCanvas = parent;
88 m_hover_icon_last = -1;
92 m_gotPianoDown =
false;
100 m_pVizIconBmp = NULL;
101 m_pInVizIconBmp = NULL;
102 m_pPolyIconBmp = NULL;
103 m_pSkewIconBmp = NULL;
104 m_pTmercIconBmp = NULL;
106 SetColorScheme(GLOBAL_COLOR_SCHEME_RGB);
108 m_eventTimer.SetOwner(
this, PIANO_EVENT_TIMER);
110 m_tex = m_tex_piano_height = 0;
114 if (m_pInVizIconBmp)
delete m_pInVizIconBmp;
115 if (m_pPolyIconBmp)
delete m_pPolyIconBmp;
116 if (m_pSkewIconBmp)
delete m_pSkewIconBmp;
117 if (m_pTmercIconBmp)
delete m_pTmercIconBmp;
118 if (m_pVizIconBmp)
delete m_pVizIconBmp;
121void Piano::Paint(
int y, wxDC &dc, wxDC *shapeDC) {
123 Paint(y, odc, shapeDC);
126void Piano::Paint(
int y,
ocpnDC &dc, wxDC *shapeDC) {
128 shapeDC->SetBackground(*wxBLACK_BRUSH);
129 shapeDC->SetBrush(*wxWHITE_BRUSH);
130 shapeDC->SetPen(*wxWHITE_PEN);
135 if (!style->chartStatusWindowTransparent) {
136 dc.SetPen(*wxTRANSPARENT_PEN);
137 dc.SetBrush(m_backBrush);
138 dc.DrawRectangle(0, y, m_parentCanvas->GetClientSize().x, GetHeight());
143 int nKeys = m_key_array.size();
145 wxPen ppPen(GetGlobalColor(_T(
"CHBLK")), 1, wxPENSTYLE_SOLID);
148 for (
int i = 0; i < nKeys; i++) {
149 int key_db_index = m_key_array[i];
151 if (-1 == key_db_index)
continue;
153 bool selected = InArray(m_active_index_array, key_db_index);
155 if (ChartData->GetDBChartType(key_db_index) == CHART_TYPE_CM93 ||
156 ChartData->GetDBChartType(key_db_index) == CHART_TYPE_CM93COMP) {
158 dc.SetBrush(m_scBrush);
160 dc.SetBrush(m_cBrush);
161 }
else if (ChartData->GetDBChartType(key_db_index) == CHART_TYPE_MBTILES) {
163 dc.SetBrush(m_tileBrush);
165 dc.SetBrush(m_utileBrush);
166 }
else if (ChartData->GetDBChartFamily(key_db_index) ==
167 CHART_FAMILY_VECTOR) {
169 dc.SetBrush(m_svBrush);
171 dc.SetBrush(m_vBrush);
174 dc.SetBrush(m_srBrush);
176 dc.SetBrush(m_rBrush);
179 if (m_bBusy) dc.SetBrush(m_unavailableBrush);
181 wxRect box = KeyRect[i];
185 dc.DrawRoundedRectangle(box.x, box.y, box.width, box.height,
188 shapeDC->DrawRoundedRectangle(box.x, box.y, box.width, box.height,
191 dc.DrawRectangle(box.x, box.y, box.width, box.height);
192 if (shapeDC) shapeDC->DrawRectangle(box);
195 if (InArray(m_eclipsed_index_array, key_db_index)) {
196 dc.SetBrush(m_backBrush);
198 dc.DrawRoundedRectangle(box.x + w, box.y + w, box.width - (2 * w),
199 box.height - (2 * w), box.height / 5 - 1);
203 if (InArray(m_noshow_index_array, key_db_index) && m_pInVizIconBmp &&
204 m_pInVizIconBmp->IsOk())
205 dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pInVizIconBmp),
206 box.x + 4, box.y + 3,
false);
209 if (InArray(m_skew_index_array, key_db_index) && m_pSkewIconBmp &&
210 m_pSkewIconBmp->IsOk())
211 dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pSkewIconBmp),
212 box.x + box.width - m_pSkewIconBmp->GetWidth() - 4,
216 if (InArray(m_tmerc_index_array, key_db_index) && m_pTmercIconBmp &&
217 m_pTmercIconBmp->IsOk())
218 dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pTmercIconBmp),
219 box.x + box.width - m_pTmercIconBmp->GetWidth() - 4,
223 if (InArray(m_poly_index_array, key_db_index) && m_pPolyIconBmp &&
224 m_pPolyIconBmp->IsOk())
225 dc.DrawBitmap(ConvertTo24Bit(dc.GetBrush().GetColour(), *m_pPolyIconBmp),
226 box.x + box.width - m_pPolyIconBmp->GetWidth() - 4,
232static void SetColor(
unsigned char color[4],
const wxBrush &brush)
234 const wxColour &c = brush.GetColour();
235 color[0] = c.Red(), color[1] = c.Green(), color[2] = c.Blue(), color[3] = 255;
242void Piano::BuildGLTexture() {
246 if (!m_pInVizIconBmp || !m_pTmercIconBmp || !m_pSkewIconBmp ||
254 if (style->chartStatusWindowTransparent)
255 tbackBrush = wxColour(1, 1, 1);
257 tbackBrush = m_backBrush;
259 wxBrush brushes[] = {m_scBrush, m_cBrush, m_svBrush,
260 m_vBrush, m_srBrush, m_rBrush,
261 m_tileBrush, m_utileBrush, m_unavailableBrush};
266 m_texPitch = ((2 * m_ref) + (2 * m_pad));
268 m_tex_piano_height = h;
269 m_texw = m_texPitch * 3;
271 m_texh = ((
sizeof brushes) / (
sizeof *brushes)) * h;
274 m_texh = NextPow2(m_texh);
275 m_texw = NextPow2(m_texw);
277 if (!m_tex) glGenTextures(1, &m_tex);
279 glBindTexture(GL_TEXTURE_2D, m_tex);
280 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
281 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
283 wxBitmap bmp(m_texw, m_texh);
286 dc.SetPen(*wxTRANSPARENT_PEN);
287 dc.SetBrush(tbackBrush);
288 dc.DrawRectangle(0, 0, m_texw, m_texh);
291 double nominal_line_width_pix = floor(g_Platform->GetDisplayDPmm() / 2.0);
292 nominal_line_width_pix *= OCPN_GetWinDIPScaleFactor();
293 nominal_line_width_pix = wxMax(1.0, nominal_line_width_pix);
296 wxPen ppPen(GetGlobalColor(_T(
"CHBLK")), nominal_line_width_pix,
299 for (
unsigned int b = 0; b < (
sizeof brushes) / (
sizeof *brushes); b++) {
300 unsigned int x = 0, y = h * b;
302 dc.SetBrush(brushes[b]);
305 dc.DrawRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v);
308 dc.DrawRoundedRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v, m_radius);
313 dc.DrawRoundedRectangle(x + m_pad, y + v, 2 * m_ref, h - 2 * v, m_radius);
314 dc.SetBrush(m_backBrush);
315 dc.DrawRoundedRectangle(x + m_pad + w, y + v + w, (2 * m_ref) - (2 * w),
317 m_radius * (h - 2 * v - 2 * w) /
321 dc.SelectObject(wxNullBitmap);
323 wxImage image = bmp.ConvertToImage();
325 unsigned char *data =
new unsigned char[4 * m_texw * m_texh], *d = data,
326 *e = image.GetData(), *a = image.GetAlpha();
327 for (
unsigned int i = 0; i < m_texw * m_texh; i++) {
328 if (style->chartStatusWindowTransparent && e[0] == 1 && e[1] == 1 &&
334 memcpy(d, e, 3), d += 4, e += 3;
337 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_texw, m_texh, 0, GL_RGBA,
338 GL_UNSIGNED_BYTE, data);
342 wxBitmap *bitmaps[] = {m_pInVizIconBmp, m_pTmercIconBmp, m_pSkewIconBmp,
345 for (
unsigned int i = 0; i < 4; i++) {
346 int iw = bitmaps[i]->GetWidth(), ih = bitmaps[i]->GetHeight();
348 wxImage im = bitmaps[i]->ConvertToImage();
349 unsigned char *data =
new unsigned char[4 * iw * ih], *d = data,
350 *e = im.GetData(), *a = im.GetAlpha();
351 for (
int j = 0; j < iw * ih; j++) {
352 memcpy(d, e, 3), d += 3, e += 3;
356 int off = ((
sizeof brushes) / (
sizeof *brushes)) * h + m_ref * i;
357 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, off, iw, ih, GL_RGBA, GL_UNSIGNED_BYTE,
364void Piano::DrawGL(
int off) {
366 unsigned int w = m_parentCanvas->GetClientSize().x * m_parentCanvas->GetContentScaleFactor();
370 if (m_tex_piano_height != h) BuildGLTexture();
372 if (m_tex_piano_height != h)
return;
374 int y1 = off, y2 = y1 + h;
376 int nKeys = m_key_array.size();
380 float *texcoords =
new float[(nKeys * 3 + 1) * 4 * 2],
381 *coords =
new float[(nKeys * 3 + 1) * 4 * 2];
386 for (
int i = 0; i < nKeys; i++) {
387 int key_db_index = m_key_array[i];
390 if (ChartData->GetDBChartType(key_db_index) == CHART_TYPE_CM93 ||
391 ChartData->GetDBChartType(key_db_index) == CHART_TYPE_CM93COMP)
393 else if (ChartData->GetDBChartType(key_db_index) == CHART_TYPE_MBTILES)
395 else if (ChartData->GetDBChartFamily(key_db_index) == CHART_FAMILY_VECTOR)
400 if (!InArray(m_active_index_array, key_db_index)) b++;
402 wxRect box = KeyRect[i];
403 float y = h * b, v1 = (y + .5) / m_texh, v2 = (y + h - .5) / m_texh;
407 const float texcord[6] = {0,
412 (float)m_texPitch - 1};
415 if (InArray(m_eclipsed_index_array, key_db_index))
424 int x1 = box.x, x2 = x1 + box.width, w = 2 * uindex + 1;
425 while (x1 + w > x2 - w && uindex > 0) uindex--, w -= 2;
429 int x[6] = {x1 - 3, x1 + m_ref, x2 - m_ref, x2 + 3};
433 int avg = (x[1] + x[2]) / 2;
437 for (
int i = 0; i < 3; i++) {
438 float u1 = ((uindex * m_texPitch) + texcord[2 * i] + .5) / m_texw,
439 u2 = ((uindex * m_texPitch) + texcord[2 * i + 1] + .5) / m_texw;
440 int x1 = x[i], x2 = x[i + 1];
441 texcoords[tc++] = u1, texcoords[tc++] = v1, coords[vc++] = x1,
443 texcoords[tc++] = u2, texcoords[tc++] = v1, coords[vc++] = x2,
445 texcoords[tc++] = u2, texcoords[tc++] = v2, coords[vc++] = x2,
447 texcoords[tc++] = u1, texcoords[tc++] = v2, coords[vc++] = x1,
455 if (!style->chartStatusWindowTransparent && endx < w) {
456 texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = endx,
458 texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = w,
460 texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = w,
462 texcoords[tc++] = 0, texcoords[tc++] = 0, coords[vc++] = endx,
466 glBindTexture(GL_TEXTURE_2D, m_tex);
468#if not defined(USE_ANDROID_GLES2) && not defined(ocpnUSE_GLSL)
469 if (style->chartStatusWindowTransparent) {
470 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
471 glColor4ub(255, 255, 255,
475 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
478 glEnable(GL_TEXTURE_2D);
480#if defined(USE_ANDROID_GLES2) || defined(ocpnUSE_GLSL)
482 m_parentCanvas->GetglCanvas()->RenderTextures(
483 m_parentCanvas->GetglCanvas()->m_gldc,
484 coords, texcoords, vc / 2, m_parentCanvas->GetpVP());
488 glEnableClientState(GL_VERTEX_ARRAY);
489 glEnableClientState(GL_TEXTURE_COORD_ARRAY);
491 glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
492 glVertexPointer(2, GL_FLOAT, 0, coords);
493 glDrawArrays(GL_QUADS, 0, vc / 2);
497 for (
int i = 0; i < nKeys; i++) {
498 int key_db_index = m_key_array[i];
500 if (-1 == key_db_index)
continue;
502 wxRect box = KeyRect[i];
504 wxBitmap *bitmaps[] = {m_pInVizIconBmp, m_pTmercIconBmp, m_pSkewIconBmp,
507 if (InArray(m_noshow_index_array, key_db_index))
510 if (InArray(m_skew_index_array, key_db_index))
512 else if (InArray(m_tmerc_index_array, key_db_index))
514 else if (InArray(m_poly_index_array, key_db_index))
520 int x1, y1, iw = bitmaps[index]->GetWidth(),
521 ih = bitmaps[index]->GetHeight();
522 if (InArray(m_noshow_index_array, key_db_index))
523 x1 = box.x + 4, y1 = box.y + 3;
525 x1 = box.x + box.width - iw - 4, y1 = box.y + 2;
528 int x2 = x1 + iw, y2 = y1 + ih;
530 wxBrush brushes[] = {m_scBrush, m_cBrush, m_svBrush,
531 m_vBrush, m_srBrush, m_rBrush,
532 m_tileBrush, m_utileBrush, m_unavailableBrush};
534 float yoff = ((
sizeof brushes) / (
sizeof *brushes)) * h + 16 * index;
535 float u1 = 0, u2 = (float)iw / m_texw;
536 float v1 = yoff / m_texh, v2 = (yoff + ih) / m_texh;
538 texcoords[tc++] = u1, texcoords[tc++] = v1, coords[vc++] = x1,
540 texcoords[tc++] = u2, texcoords[tc++] = v1, coords[vc++] = x2,
542 texcoords[tc++] = u2, texcoords[tc++] = v2, coords[vc++] = x2,
544 texcoords[tc++] = u1, texcoords[tc++] = v2, coords[vc++] = x1,
548 glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
550 glTexCoordPointer(2, GL_FLOAT, 0, texcoords);
551 glVertexPointer(2, GL_FLOAT, 0, coords);
552 glDrawArrays(GL_QUADS, 0, vc / 2);
557#if not defined(USE_ANDROID_GLES2) && not defined(ocpnUSE_GLSL)
558 glDisableClientState(GL_TEXTURE_COORD_ARRAY);
559 glDisableClientState(GL_VERTEX_ARRAY);
564 glDisable(GL_TEXTURE_2D);
568void Piano::SetColorScheme(ColorScheme cs) {
571 m_backBrush = wxBrush(GetGlobalColor(_T(
"UIBDR")), wxBRUSHSTYLE_SOLID);
573 m_rBrush = wxBrush(GetGlobalColor(_T(
"BLUE2")),
576 wxBrush(GetGlobalColor(_T(
"BLUE1")), wxBRUSHSTYLE_SOLID);
578 m_vBrush = wxBrush(GetGlobalColor(_T(
"GREEN2")),
580 m_svBrush = wxBrush(GetGlobalColor(_T(
"GREEN1")),
583 m_utileBrush = wxBrush(GetGlobalColor(_T(
"VIO01")),
586 wxBrush(GetGlobalColor(_T(
"VIO02")), wxBRUSHSTYLE_SOLID);
588 m_cBrush = wxBrush(GetGlobalColor(_T(
"YELO2")),
591 wxBrush(GetGlobalColor(_T(
"YELO1")), wxBRUSHSTYLE_SOLID);
593 m_unavailableBrush = wxBrush(GetGlobalColor(_T(
"UINFD")),
596 m_tex_piano_height = 0;
599void Piano::ShowBusy(
bool busy) {
605void Piano::SetKeyArray(std::vector<int> array) { m_key_array = array; }
607void Piano::SetNoshowIndexArray(std::vector<int> array) {
608 m_noshow_index_array = array;
611void Piano::AddNoshowIndexArray(std::vector<int> array) {
612 for (
unsigned int i = 0; i < array.size(); i++) {
613 m_noshow_index_array.push_back(array[i]);
617void Piano::SetActiveKeyArray(std::vector<int> array) {
618 m_active_index_array = array;
621void Piano::SetEclipsedIndexArray(std::vector<int> array) {
622 m_eclipsed_index_array = array;
625void Piano::SetSkewIndexArray(std::vector<int> array) {
626 m_skew_index_array = array;
629void Piano::SetTmercIndexArray(std::vector<int> array) {
630 m_tmerc_index_array = array;
633void Piano::SetPolyIndexArray(std::vector<int> array) {
634 m_poly_index_array = array;
637bool Piano::InArray(std::vector<int> &array,
int key) {
638 for (
unsigned int ino = 0; ino < array.size(); ino++)
639 if (array[ino] == key)
return true;
643wxString Piano::GetStateHash() {
646 for (
unsigned int i = 0; i < m_key_array.size(); i++) {
648 a.Printf(_T(
"%dK"), m_key_array[i]);
651 for (
unsigned int i = 0; i < m_noshow_index_array.size(); i++) {
653 a.Printf(_T(
"%dN"), m_noshow_index_array[i]);
656 for (
unsigned int i = 0; i < m_active_index_array.size(); i++) {
658 a.Printf(_T(
"%dA"), m_active_index_array[i]);
661 for (
unsigned int i = 0; i < m_eclipsed_index_array.size(); i++) {
663 a.Printf(_T(
"%dE"), m_eclipsed_index_array[i]);
666 for (
unsigned int i = 0; i < m_skew_index_array.size(); i++) {
668 a.Printf(_T(
"%dW"), m_skew_index_array[i]);
671 for (
unsigned int i = 0; i < m_tmerc_index_array.size(); i++) {
673 a.Printf(_T(
"%dM"), m_tmerc_index_array[i]);
676 for (
unsigned int i = 0; i < m_poly_index_array.size(); i++) {
678 a.Printf(_T(
"%dP"), m_poly_index_array[i]);
685wxString &Piano::GenerateAndStoreNewHash() {
686 m_hash = GetStateHash();
690wxString &Piano::GetStoredHash() {
return m_hash; }
692void Piano::FormatKeys(
void) {
694 int width = m_parentCanvas->GetClientSize().x * m_parentCanvas->GetContentScaleFactor();
695 int height = GetHeight();
696 width *= g_btouch ? 0.98f : 0.6f;
698 int nKeys = m_key_array.size();
699 int kw = style->chartStatusIconWidth;
701 if (!kw) kw = width / nKeys;
703 kw = wxMin(kw, (width * 3 / 4) / nKeys);
710 for (
int i = 0; i < nKeys; i++) {
711 wxRect r((i * kw) + 3, 2, kw - 6, height - 4);
712 KeyRect.push_back(r);
713 m_width = r.x + r.width;
719wxPoint Piano::GetKeyOrigin(
int key_index) {
720 if ((key_index >= 0) && (key_index <= (
int)m_key_array.size() - 1)) {
721 wxRect box = KeyRect[key_index];
722 return wxPoint(box.x, box.y);
724 return wxPoint(-1, -1);
727bool Piano::MouseEvent(wxMouseEvent &event) {
729 event.GetPosition(&x, &y);
730 x *= OCPN_GetDisplayContentScaleFactor();
731 y *= OCPN_GetDisplayContentScaleFactor();
733 if (event.Leaving() || y < m_parentCanvas->GetCanvasHeight() - GetHeight()) {
734 if (m_bleaving)
return false;
742 int sel_dbindex = -1;
744 for (
int i = 0; i < m_nRegions; i++) {
745 if (KeyRect[i].Contains(x, 6)) {
747 sel_dbindex = m_key_array[i];
753 if (event.LeftDown()) {
754 if (-1 != sel_index) {
755 m_action = DEFERRED_KEY_CLICK_DOWN;
757 m_eventTimer.Start(10, wxTIMER_ONE_SHOT);
760 if (event.LeftUp()) {
761 if (-1 != sel_index) {
762 m_click_sel_index = sel_index;
763 m_click_sel_dbindex = sel_dbindex;
764 if (!m_eventTimer.IsRunning()) {
765 m_action = DEFERRED_KEY_CLICK_UP;
766 m_eventTimer.Start(10, wxTIMER_ONE_SHOT);
769 }
else if (event.RightDown()) {
770 if (sel_index != m_hover_last) {
771 m_parentCanvas->HandlePianoRollover(sel_index, sel_dbindex);
772 m_hover_last = sel_index;
777 }
else if (event.ButtonUp()) {
778 m_parentCanvas->HandlePianoRollover(-1, -1);
783 m_parentCanvas->HandlePianoRollover(-1, -1);
785 }
else if (event.LeftDown()) {
786 if (-1 != sel_index) {
787 m_parentCanvas->HandlePianoClick(sel_index, sel_dbindex);
788 m_parentCanvas->Raise();
791 }
else if (event.RightDown()) {
792 if (-1 != sel_index) {
793 m_parentCanvas->HandlePianoRClick(x, y, sel_index, sel_dbindex);
794 m_parentCanvas->Raise();
797 }
else if (!event.ButtonUp()) {
798 if (sel_index != m_hover_last) {
799 m_parentCanvas->HandlePianoRollover(sel_index, sel_dbindex);
800 m_hover_last = sel_index;
819void Piano::ResetRollover(
void) {
821 m_hover_icon_last = -1;
823 m_gotPianoDown =
false;
826int Piano::GetHeight() {
827 int height = 22 * m_parentCanvas->GetContentScaleFactor();
829 double size_mult = exp(g_GUIScaleFactor * 0.0953101798043);
831 height = wxMin(height, 100);
832 height = wxMax(height, 10);
834 height *= g_Platform->GetDisplayDensityFactor();
836#ifdef __OCPN__ANDROID__
837 height = wxMax(height, 4 * g_Platform->GetDisplayDPmm());
840 height /= g_BasePlatform->GetDisplayDIPMult(gFrame);
845int Piano::GetWidth() {
return m_width; }
847void Piano::onTimerEvent(wxTimerEvent &event) {
849 case DEFERRED_KEY_CLICK_DOWN:
850 m_gotPianoDown =
true;
852 case DEFERRED_KEY_CLICK_UP:
854 if ((m_hover_last >= 0) || !m_gotPianoDown) {
855 m_parentCanvas->HandlePianoRollover(-1, -1);
858 m_parentCanvas->HandlePianoClick(m_click_sel_index,
859 m_click_sel_dbindex);
860 m_gotPianoDown =
false;
863 case INFOWIN_TIMEOUT:
864 m_parentCanvas->HandlePianoRollover(-1, -1);