2 * Pixmap Keyboard, a custom Qt4 widget
3 * Copyright (C) 2011-2013 Filipe Coelho <falktx@falktx.com>
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or 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.
15 * For a full copy of the GNU General Public License see the doc/GPL.txt file.
18 #include "pixmapkeyboard.hpp"
20 #include <QtCore/QTimer>
21 #include <QtGui/QKeyEvent>
22 #include <QtGui/QMouseEvent>
23 #include <QtGui/QPainter>
25 #ifndef HAVE_CPP11_SUPPORT
26 static std::map
<int, QRectF
> kMidiKey2RectMapHorizontal
;
28 static std::map
<int, QRectF
> kMidiKey2RectMapHorizontal
= {
29 {0, QRectF(0, 0, 18, 64)}, // C
30 {1, QRectF(13, 0, 11, 42)}, // C#
31 {2, QRectF(18, 0, 25, 64)}, // D
32 {3, QRectF(37, 0, 11, 42)}, // D#
33 {4, QRectF(42, 0, 18, 64)}, // E
34 {5, QRectF(60, 0, 18, 64)}, // F
35 {6, QRectF(73, 0, 11, 42)}, // F#
36 {7, QRectF(78, 0, 25, 64)}, // G
37 {8, QRectF(97, 0, 11, 42)}, // G#
38 {9, QRectF(102, 0, 25, 64)}, // A
39 {10, QRectF(121, 0, 11, 42)}, // A#
40 {11, QRectF(126, 0, 18, 64)} // B
44 #ifndef HAVE_CPP11_SUPPORT
45 static std::map
<int, QRectF
> kMidiKey2RectMapVertical
;
47 static std::map
<int, QRectF
> kMidiKey2RectMapVertical
= {
48 {11, QRectF(0, 0, 64, 18)}, // B
49 {10, QRectF(0, 14, 42, 7)}, // A#
50 {9, QRectF(0, 18, 64, 24)}, // A
51 {8, QRectF(0, 38, 42, 7)}, // G#
52 {7, QRectF(0, 42, 64, 24)}, // G
53 {6, QRectF(0, 62, 42, 7)}, // F#
54 {5, QRectF(0, 66, 64, 18)}, // F
55 {4, QRectF(0, 84, 64, 18)}, // E
56 {3, QRectF(0, 98, 42, 7)}, // D#
57 {2, QRectF(0, 102, 64, 24)}, // D
58 {1, QRectF(0, 122, 42, 7)}, // C#
59 {0, QRectF(0, 126, 64, 18)} // C
63 #ifndef HAVE_CPP11_SUPPORT
64 static std::map
<int, int> kMidiKeyboard2KeyMap
;
66 static const std::map
<int, int> kMidiKeyboard2KeyMap
= {
96 #ifndef HAVE_CPP11_SUPPORT
97 static QVector
<int> kBlackNotes
;
99 static const QVector
<int> kBlackNotes
= {1, 3, 6, 8, 10};
102 #ifndef HAVE_CPP11_SUPPORT
103 static const struct PixmapKeyboardInit
{
104 PixmapKeyboardInit() {
105 // kMidiKey2RectMapHorizontal
106 kMidiKey2RectMapHorizontal
[0] = QRectF(0, 0, 18, 64); // C
107 kMidiKey2RectMapHorizontal
[1] = QRectF(13, 0, 11, 42); // C#
108 kMidiKey2RectMapHorizontal
[2] = QRectF(18, 0, 25, 64); // D
109 kMidiKey2RectMapHorizontal
[3] = QRectF(37, 0, 11, 42); // D#
110 kMidiKey2RectMapHorizontal
[4] = QRectF(42, 0, 18, 64); // E
111 kMidiKey2RectMapHorizontal
[5] = QRectF(60, 0, 18, 64); // F
112 kMidiKey2RectMapHorizontal
[6] = QRectF(73, 0, 11, 42); // F#
113 kMidiKey2RectMapHorizontal
[7] = QRectF(78, 0, 25, 64); // G
114 kMidiKey2RectMapHorizontal
[8] = QRectF(97, 0, 11, 42); // G#
115 kMidiKey2RectMapHorizontal
[9] = QRectF(102, 0, 25, 64); // A
116 kMidiKey2RectMapHorizontal
[10] = QRectF(121, 0, 11, 42); // A#
117 kMidiKey2RectMapHorizontal
[11] = QRectF(126, 0, 18, 64); // B
119 // kMidiKey2RectMapVertical
120 kMidiKey2RectMapVertical
[11] = QRectF(0, 0, 64, 18); // B
121 kMidiKey2RectMapVertical
[10] = QRectF(0, 14, 42, 7); // A#
122 kMidiKey2RectMapVertical
[9] = QRectF(0, 18, 64, 24); // A
123 kMidiKey2RectMapVertical
[8] = QRectF(0, 38, 42, 7); // G#
124 kMidiKey2RectMapVertical
[7] = QRectF(0, 42, 64, 24); // G
125 kMidiKey2RectMapVertical
[6] = QRectF(0, 62, 42, 7); // F#
126 kMidiKey2RectMapVertical
[5] = QRectF(0, 66, 64, 18); // F
127 kMidiKey2RectMapVertical
[4] = QRectF(0, 84, 64, 18); // E
128 kMidiKey2RectMapVertical
[3] = QRectF(0, 98, 42, 7); // D#
129 kMidiKey2RectMapVertical
[2] = QRectF(0, 102, 64, 24); // D
130 kMidiKey2RectMapVertical
[1] = QRectF(0, 122, 42, 7); // C#
131 kMidiKey2RectMapVertical
[0] = QRectF(0, 126, 64, 18); // C
133 // kMidiKeyboard2KeyMap, 3th octave
134 kMidiKeyboard2KeyMap
[Qt::Key_Z
] = 48;
135 kMidiKeyboard2KeyMap
[Qt::Key_S
] = 49;
136 kMidiKeyboard2KeyMap
[Qt::Key_X
] = 50;
137 kMidiKeyboard2KeyMap
[Qt::Key_D
] = 51;
138 kMidiKeyboard2KeyMap
[Qt::Key_C
] = 52;
139 kMidiKeyboard2KeyMap
[Qt::Key_V
] = 53;
140 kMidiKeyboard2KeyMap
[Qt::Key_G
] = 54;
141 kMidiKeyboard2KeyMap
[Qt::Key_B
] = 55;
142 kMidiKeyboard2KeyMap
[Qt::Key_H
] = 56;
143 kMidiKeyboard2KeyMap
[Qt::Key_N
] = 57;
144 kMidiKeyboard2KeyMap
[Qt::Key_J
] = 58;
145 kMidiKeyboard2KeyMap
[Qt::Key_M
] = 59;
146 // kMidiKeyboard2KeyMap, 4th octave
147 kMidiKeyboard2KeyMap
[Qt::Key_Q
] = 60;
148 kMidiKeyboard2KeyMap
[Qt::Key_2
] = 61;
149 kMidiKeyboard2KeyMap
[Qt::Key_W
] = 62;
150 kMidiKeyboard2KeyMap
[Qt::Key_3
] = 63;
151 kMidiKeyboard2KeyMap
[Qt::Key_E
] = 64;
152 kMidiKeyboard2KeyMap
[Qt::Key_R
] = 65;
153 kMidiKeyboard2KeyMap
[Qt::Key_5
] = 66;
154 kMidiKeyboard2KeyMap
[Qt::Key_T
] = 67;
155 kMidiKeyboard2KeyMap
[Qt::Key_6
] = 68;
156 kMidiKeyboard2KeyMap
[Qt::Key_Y
] = 69;
157 kMidiKeyboard2KeyMap
[Qt::Key_7
] = 70;
158 kMidiKeyboard2KeyMap
[Qt::Key_U
] = 71;
167 } _pixmapKeyboardInitInit
;
170 PixmapKeyboard::PixmapKeyboard(QWidget
* parent
)
173 fPixmapMode(HORIZONTAL
),
175 fFont("Monospace", 7, QFont::Normal
),
180 fMidiMap(kMidiKey2RectMapHorizontal
)
182 setCursor(Qt::PointingHandCursor
);
186 void PixmapKeyboard::allNotesOff()
188 fEnabledKeys
.clear();
194 void PixmapKeyboard::sendNoteOn(int note
, bool sendSignal
)
196 if (0 <= note
&& note
<= 127 && ! fEnabledKeys
.contains(note
))
198 fEnabledKeys
.append(note
);
206 if (fEnabledKeys
.count() == 1)
210 void PixmapKeyboard::sendNoteOff(int note
, bool sendSignal
)
212 if (note
>= 0 && note
<= 127 && fEnabledKeys
.contains(note
))
214 fEnabledKeys
.removeOne(note
);
222 if (fEnabledKeys
.count() == 0)
226 void PixmapKeyboard::setMode(Orientation mode
, Color color
)
228 if (color
== COLOR_CLASSIC
)
230 fColorStr
= "classic";
232 else if (color
== COLOR_ORANGE
)
234 fColorStr
= "orange";
238 qCritical("PixmapKeyboard::setMode(%i, %i) - invalid color", mode
, color
);
239 return setMode(mode
);
242 if (mode
== HORIZONTAL
)
244 fMidiMap
= kMidiKey2RectMapHorizontal
;
245 fPixmap
.load(QString(":/bitmaps/kbd_h_%1.png").arg(fColorStr
));
246 fPixmapMode
= HORIZONTAL
;
247 fWidth
= fPixmap
.width();
248 fHeight
= fPixmap
.height() / 2;
250 else if (mode
== VERTICAL
)
252 fMidiMap
= kMidiKey2RectMapVertical
;
253 fPixmap
.load(QString(":/bitmaps/kbd_v_%1.png").arg(fColorStr
));
254 fPixmapMode
= VERTICAL
;
255 fWidth
= fPixmap
.width() / 2;
256 fHeight
= fPixmap
.height();
260 qCritical("PixmapKeyboard::setMode(%i, %i) - invalid mode", mode
, color
);
261 return setMode(HORIZONTAL
);
264 setOctaves(fOctaves
);
267 void PixmapKeyboard::setOctaves(int octaves
)
269 Q_ASSERT(octaves
>= 1 && octaves
<= 10);
273 else if (octaves
> 10)
278 if (fPixmapMode
== HORIZONTAL
)
280 setMinimumSize(fWidth
* fOctaves
, fHeight
);
281 setMaximumSize(fWidth
* fOctaves
, fHeight
);
283 else if (fPixmapMode
== VERTICAL
)
285 setMinimumSize(fWidth
, fHeight
* fOctaves
);
286 setMaximumSize(fWidth
, fHeight
* fOctaves
);
292 void PixmapKeyboard::handleMousePos(const QPoint
& pos
)
297 if (fPixmapMode
== HORIZONTAL
)
299 if (pos
.x() < 0 or pos
.x() > fOctaves
* 144)
301 int posX
= pos
.x() - 1;
302 octave
= posX
/ fWidth
;
303 keyPos
= QPointF(posX
% fWidth
, pos
.y());
305 else if (fPixmapMode
== VERTICAL
)
307 if (pos
.y() < 0 or pos
.y() > fOctaves
* 144)
309 int posY
= pos
.y() - 1;
310 octave
= fOctaves
- posY
/ fHeight
;
311 keyPos
= QPointF(pos
.x(), posY
% fHeight
);
316 if (fMidiMap
[1].contains(keyPos
)) // C#
318 else if (fMidiMap
[3].contains(keyPos
)) // D#
320 else if (fMidiMap
[6].contains(keyPos
)) // F#
322 else if (fMidiMap
[8].contains(keyPos
)) // G#
324 else if (fMidiMap
[10].contains(keyPos
))// A#
326 else if (fMidiMap
[0].contains(keyPos
)) // C
328 else if (fMidiMap
[2].contains(keyPos
)) // D
330 else if (fMidiMap
[4].contains(keyPos
)) // E
332 else if (fMidiMap
[5].contains(keyPos
)) // F
334 else if (fMidiMap
[7].contains(keyPos
)) // G
336 else if (fMidiMap
[9].contains(keyPos
)) // A
338 else if (fMidiMap
[11].contains(keyPos
))// B
347 if (fLastMouseNote
!= note
)
349 sendNoteOff(fLastMouseNote
);
353 else if (fLastMouseNote
!= -1)
354 sendNoteOff(fLastMouseNote
);
356 fLastMouseNote
= note
;
359 void PixmapKeyboard::keyPressEvent(QKeyEvent
* event
)
361 if (! event
->isAutoRepeat())
363 int qKey
= event
->key();
364 std::map
<int, int>::const_iterator it
= kMidiKeyboard2KeyMap
.find(qKey
);
365 if (it
!= kMidiKeyboard2KeyMap
.end())
366 sendNoteOn(it
->second
);
368 QWidget::keyPressEvent(event
);
371 void PixmapKeyboard::keyReleaseEvent(QKeyEvent
* event
)
373 if (! event
->isAutoRepeat())
375 int qKey
= event
->key();
376 std::map
<int, int>::const_iterator it
= kMidiKeyboard2KeyMap
.find(qKey
);
377 if (it
!= kMidiKeyboard2KeyMap
.end())
378 sendNoteOff(it
->second
);
380 QWidget::keyReleaseEvent(event
);
383 void PixmapKeyboard::mousePressEvent(QMouseEvent
* event
)
386 handleMousePos(event
->pos());
388 QWidget::mousePressEvent(event
);
391 void PixmapKeyboard::mouseMoveEvent(QMouseEvent
* event
)
393 handleMousePos(event
->pos());
394 QWidget::mouseMoveEvent(event
);
397 void PixmapKeyboard::mouseReleaseEvent(QMouseEvent
* event
)
399 if (fLastMouseNote
!= -1)
401 sendNoteOff(fLastMouseNote
);
404 QWidget::mouseReleaseEvent(event
);
407 void PixmapKeyboard::paintEvent(QPaintEvent
* event
)
409 QPainter
painter(this);
412 // -------------------------------------------------------------
413 // Paint clean keys (as background)
415 for (int octave
=0; octave
< fOctaves
; ++octave
)
419 if (fPixmapMode
== HORIZONTAL
)
420 target
= QRectF(fWidth
* octave
, 0, fWidth
, fHeight
);
421 else if (fPixmapMode
== VERTICAL
)
422 target
= QRectF(0, fHeight
* octave
, fWidth
, fHeight
);
426 QRectF source
= QRectF(0, 0, fWidth
, fHeight
);
427 painter
.drawPixmap(target
, fPixmap
, source
);
430 // -------------------------------------------------------------
431 // Paint (white) pressed keys
433 bool paintedWhite
= false;
435 for (int i
=0, count
=fEnabledKeys
.count(); i
< count
; ++i
)
437 int octave
, note
= fEnabledKeys
[i
];
438 const QRectF
& pos(_getRectFromMidiNote(note
));
440 if (_isNoteBlack(note
))
466 // cannot paint this note
469 if (fPixmapMode
== VERTICAL
)
470 octave
= fOctaves
- octave
- 1;
472 QRectF target
, source
;
474 if (fPixmapMode
== HORIZONTAL
)
476 target
= QRectF(pos
.x() + (fWidth
* octave
), 0, pos
.width(), pos
.height());
477 source
= QRectF(pos
.x(), fHeight
, pos
.width(), pos
.height());
479 else if (fPixmapMode
== VERTICAL
)
481 target
= QRectF(pos
.x(), pos
.y() + (fHeight
* octave
), pos
.width(), pos
.height());
482 source
= QRectF(fWidth
, pos
.y(), pos
.width(), pos
.height());
488 painter
.drawPixmap(target
, fPixmap
, source
);
491 // -------------------------------------------------------------
492 // Clear white keys border
496 for (int octave
=0; octave
< fOctaves
; ++octave
)
498 foreach (int note
, kBlackNotes
)
500 QRectF target
, source
;
501 const QRectF
& pos(_getRectFromMidiNote(note
));
503 if (fPixmapMode
== HORIZONTAL
)
505 target
= QRectF(pos
.x() + (fWidth
* octave
), 0, pos
.width(), pos
.height());
506 source
= QRectF(pos
.x(), 0, pos
.width(), pos
.height());
508 else if (fPixmapMode
== VERTICAL
)
510 target
= QRectF(pos
.x(), pos
.y() + (fHeight
* octave
), pos
.width(), pos
.height());
511 source
= QRectF(0, pos
.y(), pos
.width(), pos
.height());
516 painter
.drawPixmap(target
, fPixmap
, source
);
521 // -------------------------------------------------------------
522 // Paint (black) pressed keys
524 for (int i
=0, count
=fEnabledKeys
.count(); i
< count
; ++i
)
526 int octave
, note
= fEnabledKeys
[i
];
527 const QRectF
& pos(_getRectFromMidiNote(note
));
529 if (! _isNoteBlack(note
))
555 // cannot paint this note
558 if (fPixmapMode
== VERTICAL
)
559 octave
= fOctaves
- octave
- 1;
561 QRectF target
, source
;
563 if (fPixmapMode
== HORIZONTAL
)
565 target
= QRectF(pos
.x() + (fWidth
* octave
), 0, pos
.width(), pos
.height());
566 source
= QRectF(pos
.x(), fHeight
, pos
.width(), pos
.height());
568 else if (fPixmapMode
== VERTICAL
)
570 target
= QRectF(pos
.x(), pos
.y() + (fHeight
* octave
), pos
.width(), pos
.height());
571 source
= QRectF(fWidth
, pos
.y(), pos
.width(), pos
.height());
576 painter
.drawPixmap(target
, fPixmap
, source
);
579 // Paint C-number note info
580 painter
.setFont(fFont
);
581 painter
.setPen(Qt::black
);
583 for (int i
=0; i
< fOctaves
; ++i
)
585 if (fPixmapMode
== HORIZONTAL
)
586 painter
.drawText(i
* 144, 48, 18, 18, Qt::AlignCenter
, QString("C%1").arg(i
-1));
587 else if (fPixmapMode
== VERTICAL
)
588 painter
.drawText(45, (fOctaves
* 144) - (i
* 144) - 16, 18, 18, Qt::AlignCenter
, QString("C%1").arg(i
-1));
592 bool PixmapKeyboard::_isNoteBlack(int note
) const
594 const int baseNote
= note
% 12;
595 return kBlackNotes
.contains(baseNote
);
598 const QRectF
& PixmapKeyboard::_getRectFromMidiNote(int note
) const
600 const int baseNote
= note
% 12;
601 return fMidiMap
[baseNote
];