Renamed all automation related files and classes to match new coding style
[lmms.git] / src / gui / PianoView.cpp
blobbaeacea71b81978e6f9d04483633b97120a00a21
1 /*
2 * Piano.cpp - implementation of piano-widget used in instrument-track-window
3 * for testing + according model class
5 * Copyright (c) 2004-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
6 *
7 * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this program (see COPYING); if not, write to the
21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA.
26 /** \file piano.cpp
27 * \brief A piano keyboard to play notes on in the instrument plugin window.
31 * \mainpage Instrument plugin keyboard display classes
33 * \section introduction Introduction
35 * \todo fill this out
36 * \todo write isWhite inline function and replace throughout
40 #include <QtGui/QCursor>
41 #include <QtGui/QKeyEvent>
42 #include <QtGui/QMouseEvent>
43 #include <QtGui/QPainter>
44 #include <QtGui/QVBoxLayout>
47 #include "PianoView.h"
48 #include "caption_menu.h"
49 #include "embed.h"
50 #include "engine.h"
51 #include "gui_templates.h"
52 #include "InstrumentTrack.h"
53 #include "knob.h"
54 #include "string_pair_drag.h"
55 #include "MainWindow.h"
56 #include "midi.h"
57 #include "templates.h"
58 #include "update_event.h"
60 #ifdef Q_WS_X11
62 #include <X11/Xlib.h>
64 #endif
66 /*! The black / white order of keys as they appear on the keyboard.
68 const Piano::KeyTypes KEY_ORDER[] =
70 // C CIS D DIS
71 Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey,
72 // E F FIS G
73 Piano::WhiteKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey,
74 // GIS A B H
75 Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey
76 } ;
79 /*! The scale of C Major - white keys only.
81 Keys WhiteKeys[] =
83 Key_C, Key_D, Key_E, Key_F, Key_G, Key_A, Key_H
84 } ;
87 QPixmap * PianoView::s_whiteKeyPm = NULL; /*!< A white key released */
88 QPixmap * PianoView::s_blackKeyPm = NULL; /*!< A black key released */
89 QPixmap * PianoView::s_whiteKeyPressedPm = NULL; /*!< A white key pressed */
90 QPixmap * PianoView::s_blackKeyPressedPm = NULL; /*!< A black key pressed */
93 const int PIANO_BASE = 11; /*!< The height of the root note display */
94 const int PW_WHITE_KEY_WIDTH = 10; /*!< The width of a white key */
95 const int PW_BLACK_KEY_WIDTH = 8; /*!< The width of a black key */
96 const int PW_WHITE_KEY_HEIGHT = 57; /*!< The height of a white key */
97 const int PW_BLACK_KEY_HEIGHT = 38; /*!< The height of a black key */
98 const int LABEL_TEXT_SIZE = 7; /*!< The height of the key label text */
103 /*! \brief Create a new keyboard display view
105 * \param _parent the parent instrument plugin window
106 * \todo are the descriptions of the m_startkey and m_lastkey properties correct?
108 PianoView::PianoView( QWidget * _parent ) :
109 QWidget( _parent ), /*!< Our parent */
110 ModelView( NULL, this ), /*!< Our view Model */
111 m_piano( NULL ), /*!< Our piano Model */
112 m_startKey( Key_C + Octave_3*KeysPerOctave ), /*!< The first key displayed? */
113 m_lastKey( -1 ) /*!< The last key displayed? */
115 if( s_whiteKeyPm == NULL )
117 s_whiteKeyPm = new QPixmap( embed::getIconPixmap( "white_key" ) );
119 if( s_blackKeyPm == NULL )
121 s_blackKeyPm = new QPixmap( embed::getIconPixmap( "black_key" ) );
123 if( s_whiteKeyPressedPm == NULL )
125 s_whiteKeyPressedPm = new QPixmap( embed::getIconPixmap( "white_key_pressed" ) );
127 if ( s_blackKeyPressedPm == NULL )
129 s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( "black_key_pressed" ) );
132 setAttribute( Qt::WA_OpaquePaintEvent, true );
133 setFocusPolicy( Qt::StrongFocus );
134 setMaximumWidth( WhiteKeysPerOctave * NumOctaves * PW_WHITE_KEY_WIDTH );
136 // create scrollbar at the bottom
137 m_pianoScroll = new QScrollBar( Qt::Horizontal, this );
138 m_pianoScroll->setSingleStep( 1 );
139 m_pianoScroll->setPageStep( 20 );
140 m_pianoScroll->setValue( Octave_3 * WhiteKeysPerOctave );
142 // and connect it to this widget
143 connect( m_pianoScroll, SIGNAL( valueChanged( int ) ),
144 this, SLOT( pianoScrolled( int ) ) );
146 // create a layout for ourselves
147 QVBoxLayout * layout = new QVBoxLayout( this );
148 layout->setSpacing( 0 );
149 layout->setMargin( 0 );
150 layout->addSpacing( PIANO_BASE+PW_WHITE_KEY_HEIGHT );
151 layout->addWidget( m_pianoScroll );
158 /*! \brief Destroy this piano display view
161 PianoView::~PianoView()
168 /*! \brief Map a keyboard key being pressed to a note in our keyboard view
170 * \param _k The keyboard scan code of the key being pressed.
171 * \todo check the scan codes for ',' = c, 'L' = c#, '.' = d, ':' = d#,
172 * '/' = d, '[' = f', '=' = f'#, ']' = g' - Paul's additions
174 int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke )
176 #ifdef LMMS_BUILD_APPLE
177 const int k = _ke->nativeVirtualKey();
178 #else
179 const int k = _ke->nativeScanCode();
180 #endif
182 #ifdef LMMS_BUILD_WIN32
183 switch( k )
185 case 44: return 0; // Z = C
186 case 31: return 1; // S = C#
187 case 45: return 2; // X = D
188 case 32: return 3; // D = D#
189 case 46: return 4; // C = E
190 case 47: return 5; // V = F
191 case 34: return 6; // G = F#
192 case 48: return 7; // B = G
193 case 35: return 8; // H = G#
194 case 49: return 9; // N = A
195 case 36: return 10; // J = A#
196 case 50: return 11; // M = B
197 case 51: return 12; // , = c
198 case 38: return 13; // L = c#
199 case 52: return 14; // . = d
200 case 39: return 15; // ; = d#
201 case 86: return 16; // / = e
202 case 16: return 12; // Q = c
203 case 3: return 13; // 2 = c#
204 case 17: return 14; // W = d
205 case 4: return 15; // 3 = d#
206 case 18: return 16; // E = e
207 case 19: return 17; // R = f
208 case 6: return 18; // 5 = f#
209 case 20: return 19; // T = g
210 case 7: return 20; // 6 = g#
211 case 21: return 21; // Y = a
212 case 8: return 22; // 7 = a#
213 case 22: return 23; // U = b
214 case 23: return 24; // I = c'
215 case 10: return 25; // 9 = c'#
216 case 24: return 26; // O = d'
217 case 11: return 27; // 0 = d'#
218 case 25: return 28; // P = e'
220 #endif
221 #ifdef LMMS_BUILD_LINUX
222 switch( k )
224 case 52: return 0; // Z = C
225 case 39: return 1; // S = C#
226 case 53: return 2; // X = D
227 case 40: return 3; // D = D#
228 case 54: return 4; // C = E
229 case 55: return 5; // V = F
230 case 42: return 6; // G = F#
231 case 56: return 7; // B = G
232 case 43: return 8; // H = G#
233 case 57: return 9; // N = A
234 case 44: return 10; // J = A#
235 case 58: return 11; // M = B
236 case 59: return 12; // , = c
237 case 46: return 13; // L = c#
238 case 60: return 14; // . = d
239 case 47: return 15; // ; = d#
240 case 61: return 16; // / = e
241 case 24: return 12; // Q = c
242 case 11: return 13; // 2 = c#
243 case 25: return 14; // W = d
244 case 12: return 15; // 3 = d#
245 case 26: return 16; // E = e
246 case 27: return 17; // R = f
247 case 14: return 18; // 5 = f#
248 case 28: return 19; // T = g
249 case 15: return 20; // 6 = g#
250 case 29: return 21; // Y = a
251 case 16: return 22; // 7 = a#
252 case 30: return 23; // U = b
253 case 31: return 24; // I = c'
254 case 18: return 25; // 9 = c'#
255 case 32: return 26; // O = d'
256 case 19: return 27; // 0 = d'#
257 case 33: return 28; // P = e'
259 #endif
260 #ifdef LMMS_BUILD_APPLE
261 switch( k )
263 case 6: return 0; // Z = C
264 case 1: return 1; // S = C#
265 case 7: return 2; // X = D
266 case 2: return 3; // D = D#
267 case 8: return 4; // C = E
268 case 9: return 5; // V = F
269 case 5: return 6; // G = F#
270 case 11: return 7; // B = G
271 case 4: return 8; // H = G#
272 case 45: return 9; // N = A
273 case 38: return 10; // J = A#
274 case 46: return 11; // M = B
275 case 43: return 12; // , = c
276 case 37: return 13; // L = c#
277 case 47: return 14; // . = d
278 case 41: return 15; // ; = d#
279 case 44: return 16; // / = e
280 case 12: return 12; // Q = c
281 case 19: return 13; // 2 = c#
282 case 13: return 14; // W = d
283 case 20: return 15; // 3 = d#
284 case 14: return 16; // E = e
285 case 15: return 17; // R = f
286 case 23: return 18; // 5 = f#
287 case 17: return 19; // T = g
288 case 22: return 20; // 6 = g#
289 case 16: return 21; // Y = a
290 case 26: return 22; // 7 = a#
291 case 32: return 23; // U = b
292 case 34: return 24; // I = c'
293 case 25: return 25; // 9 = c'#
294 case 31: return 26; // O = d'
295 case 29: return 27; // 0 = d'#
296 case 35: return 28; // P = e'
298 #endif
300 return -100;
306 /*! \brief Register a change to this piano display view
309 void PianoView::modelChanged()
311 m_piano = castModel<Piano>();
312 if( m_piano != NULL )
314 connect( m_piano->m_instrumentTrack->baseNoteModel(),
315 SIGNAL( dataChanged() ), this, SLOT( update() ) );
323 // gets the key from the given mouse-position
324 /*! \brief Get the key from the mouse position in the piano display
326 * First we determine it roughly by the position of the point given in
327 * white key widths from our start. We then add in any black keys that
328 * might have been skipped over (they take a key number, but no 'white
329 * key' space). We then add in our starting key number.
331 * We then determine whether it was a black key that was pressed by
332 * checking whether it was within the vertical range of black keys.
333 * Black keys sit exactly between white keys on this keyboard, so
334 * we then shift the note down or up if we were in the left or right
335 * half of the white note. We only do this, of course, if the white
336 * note has a black key on that side, so to speak.
338 * This function returns const because there is a linear mapping from
339 * the point given to the key returned that never changes.
341 * \param _p The point that the mouse was pressed.
343 int PianoView::getKeyFromMouse( const QPoint & _p ) const
345 int key_num = (int)( (float) _p.x() / (float) PW_WHITE_KEY_WIDTH );
347 for( int i = 0; i <= key_num; ++i )
349 if( KEY_ORDER[( m_startKey+i ) % KeysPerOctave] ==
350 Piano::BlackKey )
352 ++key_num;
356 key_num += m_startKey;
358 // is it a black key?
359 if( _p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT )
361 // then do extra checking whether the mouse-cursor is over
362 // a black key
363 if( key_num > 0 && KEY_ORDER[(key_num-1 ) % KeysPerOctave] ==
364 Piano::BlackKey &&
365 _p.x() % PW_WHITE_KEY_WIDTH <=
366 ( PW_WHITE_KEY_WIDTH / 2 ) -
367 ( PW_BLACK_KEY_WIDTH / 2 ) )
369 --key_num;
371 if( key_num < NumKeys - 1 &&
372 KEY_ORDER[( key_num + 1 ) % KeysPerOctave] ==
373 Piano::BlackKey &&
374 _p.x() % PW_WHITE_KEY_WIDTH >=
375 ( PW_WHITE_KEY_WIDTH -
376 PW_BLACK_KEY_WIDTH / 2 ) )
378 ++key_num;
382 // some range-checking-stuff
383 return tLimit( key_num, 0, NumKeys - 1 );
389 // handler for scrolling-event
390 /*! \brief Handle the scrolling on the piano display view
392 * We need to update our start key position based on the new position.
394 * \param _new_pos the new key position.
396 void PianoView::pianoScrolled( int _new_pos )
398 m_startKey = WhiteKeys[_new_pos % WhiteKeysPerOctave]+
399 ( _new_pos / WhiteKeysPerOctave ) * KeysPerOctave;
401 update();
407 /*! \brief Handle a context menu selection on the piano display view
409 * \param _me the ContextMenuEvent to handle.
410 * \todo Is this right, or does this create the context menu?
412 void PianoView::contextMenuEvent( QContextMenuEvent * _me )
414 if( _me->pos().y() > PIANO_BASE || m_piano == NULL )
416 QWidget::contextMenuEvent( _me );
417 return;
420 captionMenu contextMenu( tr( "Base note" ) );
421 AutomatableModelView amv( m_piano->m_instrumentTrack->baseNoteModel(),
422 &contextMenu );
423 amv.addDefaultActions( &contextMenu );
424 contextMenu.exec( QCursor::pos() );
430 // handler for mouse-click-event
431 /*! \brief Handle a mouse click on this piano display view
433 * We first determine the key number using the getKeyFromMouse() method.
435 * If we're below the 'root key selection' area,
436 * we set the volume of the note to be proportional to the vertical
437 * position on the keyboard - lower down the key is louder, within the
438 * boundaries of the (white or black) key pressed. We then tell the
439 * instrument to play that note, scaling for MIDI max loudness = 127.
441 * If we're in the 'root key selection' area, of course, we set the
442 * root key to be that key.
444 * We finally update ourselves to show the key press
446 * \param _me the mouse click to handle.
448 void PianoView::mousePressEvent( QMouseEvent * _me )
450 if( _me->button() == Qt::LeftButton && m_piano != NULL )
452 // get pressed key
453 Uint32 key_num = getKeyFromMouse( _me->pos() );
454 if( _me->pos().y() > PIANO_BASE )
456 int y_diff = _me->pos().y() - PIANO_BASE;
457 int velocity = (int)( ( float ) y_diff /
458 ( ( KEY_ORDER[key_num % KeysPerOctave] ==
459 Piano::WhiteKey ) ?
460 PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
461 (float) MidiMaxVelocity );
462 if( y_diff < 0 )
464 velocity = 0;
466 else if( y_diff >
467 ( ( KEY_ORDER[key_num % KeysPerOctave] ==
468 Piano::WhiteKey ) ?
469 PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
471 velocity = MidiMaxVelocity;
473 // set note on
474 m_piano->m_instrumentTrack->processInEvent(
475 midiEvent( MidiNoteOn, 0, key_num,
476 velocity ),
477 midiTime() );
478 m_piano->m_pressedKeys[key_num] = true;
479 m_lastKey = key_num;
481 emit keyPressed( key_num );
483 else
485 if( _me->modifiers() & Qt::ControlModifier )
487 new stringPairDrag( "automatable_model",
488 QString::number( m_piano->
489 m_instrumentTrack->
490 baseNoteModel()->id() ),
491 QPixmap(), this );
492 _me->accept();
494 else
496 m_piano->m_instrumentTrack->
497 baseNoteModel()->
498 setInitValue( (float) key_num );
500 emit baseNoteChanged();
504 // and let the user see that he pressed a key... :)
505 update();
512 // handler for mouse-release-event
513 /*! \brief Handle a mouse release event on the piano display view
515 * If a key was pressed by the in the mousePressEvent() function, we
516 * turn the note off.
518 * \param _me the mousePressEvent to handle.
520 void PianoView::mouseReleaseEvent( QMouseEvent * )
522 if( m_lastKey != -1 )
524 if( m_piano != NULL )
526 m_piano->m_midiEvProc->processInEvent(
527 midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
528 midiTime() );
529 m_piano->m_pressedKeys[m_lastKey] = false;
532 // and let the user see that he released a key... :)
533 update();
535 m_lastKey = -1;
542 // handler for mouse-move-event
543 /*! \brief Handle a mouse move event on the piano display view
545 * This handles the user dragging the mouse across the keys. It uses
546 * code from mousePressEvent() and mouseReleaseEvent(), also correcting
547 * for if the mouse movement has stayed within one key and if the mouse
548 * has moved outside the vertical area of the keyboard (which is still
549 * allowed but won't make the volume go up to 11).
551 * \param _me the ContextMenuEvent to handle.
552 * \todo Paul Wayper thinks that this code should be refactored to
553 * reduce or remove the duplication between this, the mousePressEvent()
554 * and mouseReleaseEvent() methods.
556 void PianoView::mouseMoveEvent( QMouseEvent * _me )
558 if( m_piano == NULL )
560 return;
563 int key_num = getKeyFromMouse( _me->pos() );
564 int y_diff = _me->pos().y() - PIANO_BASE;
565 int velocity = (int)( (float) y_diff /
566 ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
567 PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
568 (float) MidiMaxVelocity );
569 // maybe the user moved the mouse-cursor above or under the
570 // piano-widget while holding left button so check that and
571 // correct volume if necessary
572 if( y_diff < 0 )
574 velocity = 0;
576 else if( y_diff >
577 ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ?
578 PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
580 velocity = MidiMaxVelocity;
583 // is the calculated key different from current key? (could be the
584 // user just moved the cursor one pixel left but on the same key)
585 if( key_num != m_lastKey )
587 if( m_lastKey != -1 )
589 m_piano->m_midiEvProc->processInEvent(
590 midiEvent( MidiNoteOff, 0, m_lastKey, 0 ),
591 midiTime() );
592 m_piano->m_pressedKeys[m_lastKey] = false;
593 m_lastKey = -1;
595 if( _me->buttons() & Qt::LeftButton )
597 if( _me->pos().y() > PIANO_BASE )
599 m_piano->m_midiEvProc->processInEvent(
600 midiEvent( MidiNoteOn, 0, key_num,
601 velocity ),
602 midiTime() );
603 m_piano->m_pressedKeys[key_num] = true;
604 m_lastKey = key_num;
606 else
608 m_piano->m_instrumentTrack->
609 baseNoteModel()->
610 setInitValue( (float) key_num );
613 // and let the user see that he pressed a key... :)
614 update();
616 else if( m_piano->m_pressedKeys[key_num] == true )
618 m_piano->m_midiEvProc->processInEvent(
619 midiEvent( MidiKeyPressure, 0, key_num,
620 velocity ),
621 midiTime() );
629 /*! \brief Handle a key press event on the piano display view
631 * We determine our key number from the getKeyFromKeyEvent() method,
632 * and pass the event on to the piano's handleKeyPress() method if
633 * auto-repeat is off.
635 * \param _ke the KeyEvent to handle.
637 void PianoView::keyPressEvent( QKeyEvent * _ke )
639 const int key_num = getKeyFromKeyEvent( _ke ) +
640 ( DefaultOctave - 1 ) * KeysPerOctave;
642 if( _ke->isAutoRepeat() == false && key_num > -1 )
644 if( m_piano != NULL )
646 m_piano->handleKeyPress( key_num );
647 update();
650 else
652 _ke->ignore();
659 /*! \brief Handle a key release event on the piano display view
661 * The same logic as the keyPressEvent() method.
663 * \param _ke the KeyEvent to handle.
665 void PianoView::keyReleaseEvent( QKeyEvent * _ke )
667 const int key_num = getKeyFromKeyEvent( _ke ) +
668 ( DefaultOctave - 1 ) * KeysPerOctave;
669 if( _ke->isAutoRepeat() == false && key_num > -1 )
671 if( m_piano != NULL )
673 m_piano->handleKeyRelease( key_num );
674 update();
677 else
679 _ke->ignore();
686 /*! \brief Handle the focus leaving the piano display view
688 * Turn off all notes if we lose focus.
690 * \todo Is there supposed to be a parameter given here?
692 void PianoView::focusOutEvent( QFocusEvent * )
694 if( m_piano == NULL )
696 return;
699 // focus just switched to another control inside the instrument track
700 // window we live in?
701 if( parentWidget()->parentWidget()->focusWidget() != this &&
702 parentWidget()->parentWidget()->focusWidget() != NULL &&
703 !parentWidget()->parentWidget()->
704 focusWidget()->inherits( "QLineEdit" ) )
706 // then reclaim keyboard focus!
707 setFocus();
708 return;
711 // if we loose focus, we HAVE to note off all running notes because
712 // we don't receive key-release-events anymore and so the notes would
713 // hang otherwise
714 for( int i = 0; i < NumKeys; ++i )
716 if( m_piano->m_pressedKeys[i] == true )
718 m_piano->m_midiEvProc->processInEvent(
719 midiEvent( MidiNoteOff, 0, i, 0 ),
720 midiTime() );
721 m_piano->m_pressedKeys[i] = false;
724 update();
730 /*! \brief update scrollbar range after resize
732 * After resizing we need to adjust range of scrollbar for not allowing
733 * to scroll too far to the right.
735 * \param _event resize-event object (unused)
737 void PianoView::resizeEvent( QResizeEvent * _event )
739 QWidget::resizeEvent( _event );
740 m_pianoScroll->setRange( 0, WhiteKeysPerOctave * NumOctaves -
741 (int) ceil( (float) width() /
742 PW_WHITE_KEY_WIDTH ) );
748 /*! \brief Convert a key number to an X coordinate in the piano display view
750 * We can immediately discard the trivial case of when the key number is
751 * less than our starting key. We then iterate through the keys from the
752 * start key to this key, adding the width of each key as we go. For
753 * black keys, and the first white key if there is no black key between
754 * two white keys, we add half a white key width; for that second white
755 * key, we add a whole width. That takes us to the boundary of a white
756 * key - subtract half a width to get to the middle.
758 * \param _key_num the keyboard key to translate
759 * \todo is this description of what the method does correct?
760 * \todo replace the final subtract with initialising x to width/2.
762 int PianoView::getKeyX( int _key_num ) const
764 int k = m_startKey;
765 if( _key_num < m_startKey )
767 return ( _key_num - k ) * PW_WHITE_KEY_WIDTH / 2;
770 int x = 0;
771 int white_cnt = 0;
773 while( k <= _key_num )
775 if( KEY_ORDER[k % KeysPerOctave] == Piano::WhiteKey )
777 ++white_cnt;
778 if( white_cnt > 1 )
780 x += PW_WHITE_KEY_WIDTH;
782 else
784 x += PW_WHITE_KEY_WIDTH/2;
787 else
789 white_cnt = 0;
790 x += PW_WHITE_KEY_WIDTH/2;
792 ++k;
795 x -= PW_WHITE_KEY_WIDTH / 2;
797 return x;
804 /*! \brief Paint the piano display view in response to an event
806 * This method draws the piano and the 'root note' base. It draws
807 * the base first, then all the white keys, then all the black keys.
809 * \todo Is there supposed to be a parameter given here?
811 void PianoView::paintEvent( QPaintEvent * )
813 QPainter p( this );
815 // set smaller font for printing number of every octave
816 p.setFont( pointSize<LABEL_TEXT_SIZE>( p.font() ) );
819 // draw blue bar above the actual keyboard (there will be the labels
820 // for all C's)
821 QLinearGradient g( 0, 0, 0, PIANO_BASE-3 );
822 g.setColorAt( 0, Qt::black );
823 g.setColorAt( 0.1, QColor( 96, 96, 96 ) );
824 g.setColorAt( 1, Qt::black );
825 p.fillRect( QRect( 0, 1, width(), PIANO_BASE-2 ), g );
827 // draw stuff above the actual keyboard
828 p.setPen( Qt::black );
829 p.drawLine( 0, 0, width(), 0 );
830 p.drawLine( 0, PIANO_BASE-1, width(), PIANO_BASE-1 );
832 p.setPen( Qt::white );
834 const int base_key = ( m_piano != NULL ) ?
835 m_piano->m_instrumentTrack->baseNoteModel()->value() : 0;
836 g.setColorAt( 0, QColor( 0, 96, 0 ) );
837 g.setColorAt( 0.1, QColor( 64, 255, 64 ) );
838 g.setColorAt( 1, QColor( 0, 96, 0 ) );
839 if( KEY_ORDER[base_key % KeysPerOctave] == Piano::WhiteKey )
841 p.fillRect( QRect( getKeyX( base_key ), 1, PW_WHITE_KEY_WIDTH-1,
842 PIANO_BASE-2 ), g );
844 else
846 p.fillRect( QRect( getKeyX( base_key ) + 1, 1,
847 PW_BLACK_KEY_WIDTH - 1, PIANO_BASE - 2 ), g );
851 int cur_key = m_startKey;
853 // draw all white keys...
854 for( int x = 0; x < width(); )
856 while( KEY_ORDER[cur_key%KeysPerOctave] != Piano::WhiteKey )
858 ++cur_key;
861 // draw pressed or not pressed key, depending on state of
862 // current key
863 if( m_piano && m_piano->m_pressedKeys[cur_key] == true )
865 p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPressedPm );
867 else
869 p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPm );
872 x += PW_WHITE_KEY_WIDTH;
874 if( (Keys) (cur_key%KeysPerOctave) == Key_C )
876 // label key of note C with "C" and number of current
877 // octave
878 p.drawText( x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
879 QString( "C" ) + QString::number(
880 cur_key / KeysPerOctave, 10 ) );
882 ++cur_key;
886 // reset all values, because now we're going to draw all black keys
887 cur_key = m_startKey;
888 int white_cnt = 0;
890 int s_key = m_startKey;
891 if( s_key > 0 &&
892 KEY_ORDER[(Keys)(--s_key) % KeysPerOctave] == Piano::BlackKey )
894 if( m_piano && m_piano->m_pressedKeys[s_key] == true )
896 p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE,
897 *s_blackKeyPressedPm );
899 else
901 p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE,
902 *s_blackKeyPm );
906 // now draw all black keys...
907 for( int x = 0; x < width(); )
909 if( KEY_ORDER[cur_key%KeysPerOctave] == Piano::BlackKey )
911 // draw pressed or not pressed key, depending on
912 // state of current key
913 if( m_piano && m_piano->m_pressedKeys[cur_key] == true )
915 p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2,
916 PIANO_BASE,
917 *s_blackKeyPressedPm );
919 else
921 p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2,
922 PIANO_BASE, *s_blackKeyPm );
924 x += PW_WHITE_KEY_WIDTH;
925 white_cnt = 0;
927 else
929 // simple workaround for increasing x if there were two
930 // white keys (e.g. between E and F)
931 ++white_cnt;
932 if( white_cnt > 1 )
934 x += PW_WHITE_KEY_WIDTH;
937 ++cur_key;
944 #include "moc_PianoView.cxx"