piano.cpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. /*
  2. This file is part of QTau
  3. Copyright (C) 2013-2018 Tobias "Tomoko" Platen <tplaten@posteo.de>
  4. Copyright (C) 2013 digited <https://github.com/digited>
  5. Copyright (C) 2010-2013 HAL@ShurabaP <https://github.com/haruneko>
  6. QTau is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. SPDX-License-Identifier: GPL-3.0+
  17. */
  18. #include "ui/piano.h"
  19. #include <qevent.h>
  20. #include <qpainter.h>
  21. #include <QDebug>
  22. #include "ui/Config.h"
  23. const int CONST_PIANO_LABELHEIGHT = 14;
  24. const int CONST_PIANO_LABELWIDTH = 50;
  25. qtauPiano::qtauPiano(QWidget *parent)
  26. : QWidget(parent),
  27. _offset(0, 0),
  28. _labelCache(nullptr),
  29. _pressedKey(-1),
  30. _hoveredKey(-1) {
  31. // setup widget
  32. setFocusPolicy(Qt::NoFocus);
  33. setMouseTracking(true);
  34. installEventFilter(this);
  35. }
  36. #if 0
  37. bool qtauPiano::eventFilter(QObject *object, QEvent *event)
  38. {
  39. //if(object==this && (event->type()==QEvent::Enter || event->type()==QEvent::Leave))
  40. // onHover(event->type()==QEvent::Enter);
  41. return false;
  42. }
  43. #endif
  44. //----------------------------------------------------------------
  45. inline void cacheLbl(QPainter &p, QRect &r, const QString &txt, int &vOff,
  46. const QColor &offClr, const QColor &onClr) {
  47. r.moveTopLeft(QPoint(0, vOff));
  48. p.setPen(offClr);
  49. p.drawText(r, txt);
  50. r.moveLeft(CONST_PIANO_LABELWIDTH);
  51. p.setPen(onClr);
  52. p.drawText(r, txt);
  53. vOff += CONST_PIANO_LABELHEIGHT;
  54. }
  55. inline void whiteLbl(QPainter &p, QRect &r, const QString &txt, int &vOff) {
  56. cacheLbl(p, r, txt, vOff, QColor(cdef_color_piano_lbl_wh),
  57. QColor(cdef_color_piano_lbl_wh_on));
  58. }
  59. inline void blackLbl(QPainter &p, QRect &r, const QString &txt, int &vOff) {
  60. cacheLbl(p, r, txt, vOff, QColor(cdef_color_piano_lbl_bl),
  61. QColor(cdef_color_piano_lbl_bl_on));
  62. }
  63. //----------------------------------------------------------------
  64. qtauPiano::~qtauPiano() {}
  65. void qtauPiano::setOffset(int voff) {
  66. if (_offset.y() != voff) {
  67. _offset.setY(voff);
  68. update();
  69. }
  70. }
  71. void qtauPiano::configure(const SNoteSetup &newSetup) {
  72. _ns = newSetup;
  73. update();
  74. }
  75. // move to midi_support.c
  76. bool isBlack(int num) {
  77. num = num % 12;
  78. if (num == 1 || num == 3 || num == 6 || num == 8 || num == 10) return true;
  79. return false;
  80. }
  81. //--------------------------------------------
  82. void qtauPiano::paintEvent(QPaintEvent *event) {
  83. (void)event;
  84. QVector<QRect> whites;
  85. QVector<QRect> blacks;
  86. double pixels_per_semitone = _ns.octHeight / 12.0;
  87. int basenote = (_ns.baseOctave + _ns.numOctaves) * 12 -
  88. 1; // basenote is a B in a flipped geometry
  89. for (int i = _ns.baseOctave * 12; i <= basenote; i++) {
  90. int w = this->width();
  91. QRect r(0, (basenote - i) * pixels_per_semitone, w, pixels_per_semitone);
  92. if (isBlack(i))
  93. blacks.append(r);
  94. else
  95. whites.append(r);
  96. }
  97. QPainter p(this);
  98. p.translate(-_offset);
  99. p.setBrush(Qt::white);
  100. p.drawRects(whites);
  101. p.setBrush(QColor(cdef_color_black_key_bg));
  102. p.drawRects(blacks);
  103. int w = this->width();
  104. for (int i = 12; i < 96; i++) {
  105. QRect r(0, (95 - i) * pixels_per_semitone, w, pixels_per_semitone);
  106. if (i % 12 == 0) // draw hover notename (transposed)
  107. {
  108. int oct = i / 12 - 2;
  109. p.drawText(r, "C" + QVariant(oct).toString());
  110. }
  111. }
  112. if (_pressedKey >= 0) {
  113. QRect r(0, (basenote - _pressedKey) * pixels_per_semitone, w,
  114. pixels_per_semitone);
  115. p.setBrush(Qt::blue);
  116. p.drawRect(r);
  117. }
  118. }
  119. void qtauPiano::resizeEvent(QResizeEvent *event) {
  120. emit heightChanged(event->size().height());
  121. }
  122. //------------ input handling ---------------
  123. void qtauPiano::mouseDoubleClickEvent(QMouseEvent *event) { (void)event; }
  124. void qtauPiano::mouseMoveEvent(QMouseEvent *event) { (void)event; }
  125. void qtauPiano::mousePressEvent(QMouseEvent *event) {
  126. double y = event->y() + _offset.y();
  127. double pixels_per_semitone = _ns.octHeight / 12.0;
  128. int basenote = (_ns.baseOctave + _ns.numOctaves) * 12 -
  129. 1; // basenote is a B in a flipped geometry
  130. int ngeom = y / pixels_per_semitone;
  131. int nn = basenote - ngeom;
  132. _pressedKey = nn;
  133. this->repaint();
  134. emit keyPressed(nn - 12);
  135. }
  136. void qtauPiano::mouseReleaseEvent(QMouseEvent *event) {
  137. (void)event;
  138. emit keyReleased(_pressedKey - 12);
  139. _pressedKey = -1; // invalid
  140. this->repaint();
  141. }
  142. void qtauPiano::wheelEvent(QWheelEvent *event) {
  143. emit scrolled(event->delta());
  144. }