Files
lmms/src/gui/PianoView.cpp
Umcaruje 27eaa4d512 Make scrollbars consistent over the software; Add padding to the scrollbars; Make the PianoView background themeable (#2807)
* Make scrollbars consistent over the software; Add padding to the scrollbars; Make the PianoView background themeable

* Make the scrollbar hover color brighter
2016-06-03 12:21:27 +02:00

899 lines
23 KiB
C++

/*
* PianoView.cpp - implementation of piano-widget used in instrument-track-window
* for testing + according model class
*
* Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
*
* This file is part of LMMS - http://lmms.io
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
/** \file piano.cpp
* \brief A piano keyboard to play notes on in the instrument plugin window.
*/
/*
* \mainpage Instrument plugin keyboard display classes
*
* \section introduction Introduction
*
* \todo fill this out
* \todo write isWhite inline function and replace throughout
*/
#include <QCursor>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPainter>
#include <QVBoxLayout>
#include "PianoView.h"
#include "Piano.h"
#include "CaptionMenu.h"
#include "embed.h"
#include "Engine.h"
#include "gui_templates.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "StringPairDrag.h"
#include "MainWindow.h"
#include "MidiEvent.h"
#include "templates.h"
#include "update_event.h"
/*! The scale of C Major - white keys only.
*/
Keys WhiteKeys[] =
{
Key_C, Key_D, Key_E, Key_F, Key_G, Key_A, Key_H
} ;
QPixmap * PianoView::s_whiteKeyPm = NULL; /*!< A white key released */
QPixmap * PianoView::s_blackKeyPm = NULL; /*!< A black key released */
QPixmap * PianoView::s_whiteKeyPressedPm = NULL; /*!< A white key pressed */
QPixmap * PianoView::s_blackKeyPressedPm = NULL; /*!< A black key pressed */
const int PIANO_BASE = 11; /*!< The height of the root note display */
const int PW_WHITE_KEY_WIDTH = 10; /*!< The width of a white key */
const int PW_BLACK_KEY_WIDTH = 8; /*!< The width of a black key */
const int PW_WHITE_KEY_HEIGHT = 57; /*!< The height of a white key */
const int PW_BLACK_KEY_HEIGHT = 38; /*!< The height of a black key */
const int LABEL_TEXT_SIZE = 7; /*!< The height of the key label text */
/*! \brief Create a new keyboard display view
*
* \param _parent the parent instrument plugin window
* \todo are the descriptions of the m_startkey and m_lastkey properties correct?
*/
PianoView::PianoView( QWidget * _parent ) :
QWidget( _parent ), /*!< Our parent */
ModelView( NULL, this ), /*!< Our view Model */
m_piano( NULL ), /*!< Our piano Model */
m_startKey( Key_C + Octave_3*KeysPerOctave ), /*!< The first key displayed? */
m_lastKey( -1 ) /*!< The last key displayed? */
{
if( s_whiteKeyPm == NULL )
{
s_whiteKeyPm = new QPixmap( embed::getIconPixmap( "white_key" ) );
}
if( s_blackKeyPm == NULL )
{
s_blackKeyPm = new QPixmap( embed::getIconPixmap( "black_key" ) );
}
if( s_whiteKeyPressedPm == NULL )
{
s_whiteKeyPressedPm = new QPixmap( embed::getIconPixmap( "white_key_pressed" ) );
}
if ( s_blackKeyPressedPm == NULL )
{
s_blackKeyPressedPm = new QPixmap( embed::getIconPixmap( "black_key_pressed" ) );
}
setAttribute( Qt::WA_OpaquePaintEvent, true );
setFocusPolicy( Qt::StrongFocus );
setMaximumWidth( WhiteKeysPerOctave * NumOctaves * PW_WHITE_KEY_WIDTH );
// create scrollbar at the bottom
m_pianoScroll = new QScrollBar( Qt::Horizontal, this );
m_pianoScroll->setSingleStep( 1 );
m_pianoScroll->setPageStep( 20 );
m_pianoScroll->setValue( Octave_3 * WhiteKeysPerOctave );
// and connect it to this widget
connect( m_pianoScroll, SIGNAL( valueChanged( int ) ),
this, SLOT( pianoScrolled( int ) ) );
// create a layout for ourselves
QVBoxLayout * layout = new QVBoxLayout( this );
layout->setSpacing( 0 );
layout->setMargin( 0 );
layout->addSpacing( PIANO_BASE+PW_WHITE_KEY_HEIGHT );
layout->addWidget( m_pianoScroll );
}
/*! \brief Destroy this piano display view
*
*/
PianoView::~PianoView()
{
}
/*! \brief Map a keyboard key being pressed to a note in our keyboard view
*
* \param _k The keyboard scan code of the key being pressed.
* \todo check the scan codes for ',' = c, 'L' = c#, '.' = d, ':' = d#,
* '/' = d, '[' = f', '=' = f'#, ']' = g' - Paul's additions
*/
int PianoView::getKeyFromKeyEvent( QKeyEvent * _ke )
{
#ifdef LMMS_BUILD_APPLE
const int k = _ke->nativeVirtualKey();
#else
const int k = _ke->nativeScanCode();
#endif
#ifdef LMMS_BUILD_WIN32
switch( k )
{
case 44: return 0; // Z = C
case 31: return 1; // S = C#
case 45: return 2; // X = D
case 32: return 3; // D = D#
case 46: return 4; // C = E
case 47: return 5; // V = F
case 34: return 6; // G = F#
case 48: return 7; // B = G
case 35: return 8; // H = G#
case 49: return 9; // N = A
case 36: return 10; // J = A#
case 50: return 11; // M = B
case 51: return 12; // , = c
case 38: return 13; // L = c#
case 52: return 14; // . = d
case 39: return 15; // ; = d#
//case 86: return 16; // / = e
case 53: return 16; // / = e
case 16: return 12; // Q = c
case 3: return 13; // 2 = c#
case 17: return 14; // W = d
case 4: return 15; // 3 = d#
case 18: return 16; // E = e
case 19: return 17; // R = f
case 6: return 18; // 5 = f#
case 20: return 19; // T = g
case 7: return 20; // 6 = g#
case 21: return 21; // Y = a
case 8: return 22; // 7 = a#
case 22: return 23; // U = b
case 23: return 24; // I = c'
case 10: return 25; // 9 = c'#
case 24: return 26; // O = d'
case 11: return 27; // 0 = d'#
case 25: return 28; // P = e'
case 26: return 29; // [
case 13: return 30; // =
case 27: return 31; // ]
}
#endif
#if defined(LMMS_BUILD_LINUX) || defined(LMMS_BUILD_OPENBSD)
switch( k )
{
case 52: return 0; // Z = C
case 39: return 1; // S = C#
case 53: return 2; // X = D
case 40: return 3; // D = D#
case 54: return 4; // C = E
case 55: return 5; // V = F
case 42: return 6; // G = F#
case 56: return 7; // B = G
case 43: return 8; // H = G#
case 57: return 9; // N = A
case 44: return 10; // J = A#
case 58: return 11; // M = B
case 59: return 12; // , = c
case 46: return 13; // L = c#
case 60: return 14; // . = d
case 47: return 15; // ; = d#
case 61: return 16; // / = e
case 24: return 12; // Q = c
case 11: return 13; // 2 = c#
case 25: return 14; // W = d
case 12: return 15; // 3 = d#
case 26: return 16; // E = e
case 27: return 17; // R = f
case 14: return 18; // 5 = f#
case 28: return 19; // T = g
case 15: return 20; // 6 = g#
case 29: return 21; // Y = a
case 16: return 22; // 7 = a#
case 30: return 23; // U = b
case 31: return 24; // I = c'
case 18: return 25; // 9 = c'#
case 32: return 26; // O = d'
case 19: return 27; // 0 = d'#
case 33: return 28; // P = e'
case 34: return 29; // [
case 21: return 30; // =
case 35: return 31; // ]
}
#endif
#ifdef LMMS_BUILD_APPLE
switch( k )
{
case 6: return 0; // Z = C
case 1: return 1; // S = C#
case 7: return 2; // X = D
case 2: return 3; // D = D#
case 8: return 4; // C = E
case 9: return 5; // V = F
case 5: return 6; // G = F#
case 11: return 7; // B = G
case 4: return 8; // H = G#
case 45: return 9; // N = A
case 38: return 10; // J = A#
case 46: return 11; // M = B
case 43: return 12; // , = c
case 37: return 13; // L = c#
case 47: return 14; // . = d
case 41: return 15; // ; = d#
case 44: return 16; // / = e
case 12: return 12; // Q = c
case 19: return 13; // 2 = c#
case 13: return 14; // W = d
case 20: return 15; // 3 = d#
case 14: return 16; // E = e
case 15: return 17; // R = f
case 23: return 18; // 5 = f#
case 17: return 19; // T = g
case 22: return 20; // 6 = g#
case 16: return 21; // Y = a
case 26: return 22; // 7 = a#
case 32: return 23; // U = b
case 34: return 24; // I = c'
case 25: return 25; // 9 = c'#
case 31: return 26; // O = d'
case 29: return 27; // 0 = d'#
case 35: return 28; // P = e'
}
#endif
return -100;
}
/*! \brief Register a change to this piano display view
*
*/
void PianoView::modelChanged()
{
m_piano = castModel<Piano>();
if( m_piano != NULL )
{
connect( m_piano->instrumentTrack()->baseNoteModel(), SIGNAL( dataChanged() ),
this, SLOT( update() ) );
}
}
// gets the key from the given mouse-position
/*! \brief Get the key from the mouse position in the piano display
*
* First we determine it roughly by the position of the point given in
* white key widths from our start. We then add in any black keys that
* might have been skipped over (they take a key number, but no 'white
* key' space). We then add in our starting key number.
*
* We then determine whether it was a black key that was pressed by
* checking whether it was within the vertical range of black keys.
* Black keys sit exactly between white keys on this keyboard, so
* we then shift the note down or up if we were in the left or right
* half of the white note. We only do this, of course, if the white
* note has a black key on that side, so to speak.
*
* This function returns const because there is a linear mapping from
* the point given to the key returned that never changes.
*
* \param _p The point that the mouse was pressed.
*/
int PianoView::getKeyFromMouse( const QPoint & _p ) const
{
int key_num = (int)( (float) _p.x() / (float) PW_WHITE_KEY_WIDTH );
for( int i = 0; i <= key_num; ++i )
{
if ( Piano::isBlackKey( m_startKey+i ) )
{
++key_num;
}
}
key_num += m_startKey;
// is it a black key?
if( _p.y() < PIANO_BASE + PW_BLACK_KEY_HEIGHT )
{
// then do extra checking whether the mouse-cursor is over
// a black key
if( key_num > 0 && Piano::isBlackKey( key_num-1 ) &&
_p.x() % PW_WHITE_KEY_WIDTH <=
( PW_WHITE_KEY_WIDTH / 2 ) -
( PW_BLACK_KEY_WIDTH / 2 ) )
{
--key_num;
}
if( key_num < NumKeys - 1 && Piano::isBlackKey( key_num+1 ) &&
_p.x() % PW_WHITE_KEY_WIDTH >=
( PW_WHITE_KEY_WIDTH -
PW_BLACK_KEY_WIDTH / 2 ) )
{
++key_num;
}
}
// some range-checking-stuff
return tLimit( key_num, 0, NumKeys - 1 );
}
// handler for scrolling-event
/*! \brief Handle the scrolling on the piano display view
*
* We need to update our start key position based on the new position.
*
* \param _new_pos the new key position.
*/
void PianoView::pianoScrolled( int _new_pos )
{
m_startKey = WhiteKeys[_new_pos % WhiteKeysPerOctave]+
( _new_pos / WhiteKeysPerOctave ) * KeysPerOctave;
update();
}
/*! \brief Handle a context menu selection on the piano display view
*
* \param _me the ContextMenuEvent to handle.
* \todo Is this right, or does this create the context menu?
*/
void PianoView::contextMenuEvent( QContextMenuEvent * _me )
{
if( _me->pos().y() > PIANO_BASE || m_piano == NULL )
{
QWidget::contextMenuEvent( _me );
return;
}
CaptionMenu contextMenu( tr( "Base note" ) );
AutomatableModelView amv( m_piano->instrumentTrack()->baseNoteModel(), &contextMenu );
amv.addDefaultActions( &contextMenu );
contextMenu.exec( QCursor::pos() );
}
// handler for mouse-click-event
/*! \brief Handle a mouse click on this piano display view
*
* We first determine the key number using the getKeyFromMouse() method.
*
* If we're below the 'root key selection' area,
* we set the volume of the note to be proportional to the vertical
* position on the keyboard - lower down the key is louder, within the
* boundaries of the (white or black) key pressed. We then tell the
* instrument to play that note, scaling for MIDI max loudness = 127.
*
* If we're in the 'root key selection' area, of course, we set the
* root key to be that key.
*
* We finally update ourselves to show the key press
*
* \param _me the mouse click to handle.
*/
void PianoView::mousePressEvent( QMouseEvent * _me )
{
if( _me->button() == Qt::LeftButton && m_piano != NULL )
{
// get pressed key
int key_num = getKeyFromMouse( _me->pos() );
if( _me->pos().y() > PIANO_BASE )
{
int y_diff = _me->pos().y() - PIANO_BASE;
int velocity = (int)( ( float ) y_diff /
( Piano::isWhiteKey( key_num ) ?
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
(float) m_piano->instrumentTrack()->midiPort()->baseVelocity() );
if( y_diff < 0 )
{
velocity = 0;
}
else if( y_diff >
( Piano::isWhiteKey( key_num ) ?
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
{
velocity = m_piano->instrumentTrack()->midiPort()->baseVelocity();
}
// set note on
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, -1, key_num, velocity ) );
m_piano->setKeyState( key_num, true );
m_lastKey = key_num;
emit keyPressed( key_num );
}
else
{
if( _me->modifiers() & Qt::ControlModifier )
{
new StringPairDrag( "automatable_model",
QString::number( m_piano->instrumentTrack()->baseNoteModel()->id() ),
QPixmap(), this );
_me->accept();
}
else
{
m_piano->instrumentTrack()->baseNoteModel()->setInitValue( (float) key_num );
emit baseNoteChanged();
}
}
// and let the user see that he pressed a key... :)
update();
}
}
// handler for mouse-release-event
/*! \brief Handle a mouse release event on the piano display view
*
* If a key was pressed by the in the mousePressEvent() function, we
* turn the note off.
*
* \param _me the mousePressEvent to handle.
*/
void PianoView::mouseReleaseEvent( QMouseEvent * )
{
if( m_lastKey != -1 )
{
if( m_piano != NULL )
{
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, m_lastKey, 0 ) );
m_piano->setKeyState( m_lastKey, false );
}
// and let the user see that he released a key... :)
update();
m_lastKey = -1;
}
}
// handler for mouse-move-event
/*! \brief Handle a mouse move event on the piano display view
*
* This handles the user dragging the mouse across the keys. It uses
* code from mousePressEvent() and mouseReleaseEvent(), also correcting
* for if the mouse movement has stayed within one key and if the mouse
* has moved outside the vertical area of the keyboard (which is still
* allowed but won't make the volume go up to 11).
*
* \param _me the ContextMenuEvent to handle.
* \todo Paul Wayper thinks that this code should be refactored to
* reduce or remove the duplication between this, the mousePressEvent()
* and mouseReleaseEvent() methods.
*/
void PianoView::mouseMoveEvent( QMouseEvent * _me )
{
if( m_piano == NULL )
{
return;
}
int key_num = getKeyFromMouse( _me->pos() );
int y_diff = _me->pos().y() - PIANO_BASE;
int velocity = (int)( (float) y_diff /
( Piano::isWhiteKey( key_num ) ?
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) *
(float) m_piano->instrumentTrack()->midiPort()->baseVelocity() );
// maybe the user moved the mouse-cursor above or under the
// piano-widget while holding left button so check that and
// correct volume if necessary
if( y_diff < 0 )
{
velocity = 0;
}
else if( y_diff >
( Piano::isWhiteKey( key_num ) ?
PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) )
{
velocity = m_piano->instrumentTrack()->midiPort()->baseVelocity();
}
// is the calculated key different from current key? (could be the
// user just moved the cursor one pixel left but on the same key)
if( key_num != m_lastKey )
{
if( m_lastKey != -1 )
{
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, m_lastKey, 0 ) );
m_piano->setKeyState( m_lastKey, false );
m_lastKey = -1;
}
if( _me->buttons() & Qt::LeftButton )
{
if( _me->pos().y() > PIANO_BASE )
{
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOn, -1, key_num, velocity ) );
m_piano->setKeyState( key_num, true );
m_lastKey = key_num;
}
else
{
m_piano->instrumentTrack()->baseNoteModel()->setInitValue( (float) key_num );
}
}
// and let the user see that he pressed a key... :)
update();
}
else if( m_piano->isKeyPressed( key_num ) )
{
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiKeyPressure, -1, key_num, velocity ) );
}
}
/*! \brief Handle a key press event on the piano display view
*
* We determine our key number from the getKeyFromKeyEvent() method,
* and pass the event on to the piano's handleKeyPress() method if
* auto-repeat is off.
*
* \param _ke the KeyEvent to handle.
*/
void PianoView::keyPressEvent( QKeyEvent * _ke )
{
const int key_num = getKeyFromKeyEvent( _ke ) +
( DefaultOctave - 1 ) * KeysPerOctave;
if( _ke->isAutoRepeat() == false && key_num > -1 )
{
if( m_piano != NULL )
{
m_piano->handleKeyPress( key_num );
_ke->accept();
update();
}
}
else
{
_ke->ignore();
}
}
/*! \brief Handle a key release event on the piano display view
*
* The same logic as the keyPressEvent() method.
*
* \param _ke the KeyEvent to handle.
*/
void PianoView::keyReleaseEvent( QKeyEvent * _ke )
{
const int key_num = getKeyFromKeyEvent( _ke ) +
( DefaultOctave - 1 ) * KeysPerOctave;
if( _ke->isAutoRepeat() == false && key_num > -1 )
{
if( m_piano != NULL )
{
m_piano->handleKeyRelease( key_num );
_ke->accept();
update();
}
}
else
{
_ke->ignore();
}
}
/*! \brief Handle the focus leaving the piano display view
*
* Turn off all notes if we lose focus.
*
* \todo Is there supposed to be a parameter given here?
*/
void PianoView::focusOutEvent( QFocusEvent * )
{
if( m_piano == NULL )
{
return;
}
// focus just switched to another control inside the instrument track
// window we live in?
if( parentWidget()->parentWidget()->focusWidget() != this &&
parentWidget()->parentWidget()->focusWidget() != NULL &&
!parentWidget()->parentWidget()->
focusWidget()->inherits( "QLineEdit" ) )
{
// then reclaim keyboard focus!
setFocus();
return;
}
// if we loose focus, we HAVE to note off all running notes because
// we don't receive key-release-events anymore and so the notes would
// hang otherwise
for( int i = 0; i < NumKeys; ++i )
{
m_piano->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, i, 0 ) );
m_piano->setKeyState( i, false );
}
update();
}
/*! \brief update scrollbar range after resize
*
* After resizing we need to adjust range of scrollbar for not allowing
* to scroll too far to the right.
*
* \param _event resize-event object (unused)
*/
void PianoView::resizeEvent( QResizeEvent * _event )
{
QWidget::resizeEvent( _event );
m_pianoScroll->setRange( 0, WhiteKeysPerOctave * NumOctaves -
(int) ceil( (float) width() /
PW_WHITE_KEY_WIDTH ) );
}
/*! \brief Convert a key number to an X coordinate in the piano display view
*
* We can immediately discard the trivial case of when the key number is
* less than our starting key. We then iterate through the keys from the
* start key to this key, adding the width of each key as we go. For
* black keys, and the first white key if there is no black key between
* two white keys, we add half a white key width; for that second white
* key, we add a whole width. That takes us to the boundary of a white
* key - subtract half a width to get to the middle.
*
* \param _key_num the keyboard key to translate
* \todo is this description of what the method does correct?
* \todo replace the final subtract with initialising x to width/2.
*/
int PianoView::getKeyX( int _key_num ) const
{
int k = m_startKey;
if( _key_num < m_startKey )
{
return ( _key_num - k ) * PW_WHITE_KEY_WIDTH / 2;
}
int x = 0;
int white_cnt = 0;
while( k <= _key_num )
{
if( Piano::isWhiteKey( k ) )
{
++white_cnt;
if( white_cnt > 1 )
{
x += PW_WHITE_KEY_WIDTH;
}
else
{
x += PW_WHITE_KEY_WIDTH/2;
}
}
else
{
white_cnt = 0;
x += PW_WHITE_KEY_WIDTH/2;
}
++k;
}
x -= PW_WHITE_KEY_WIDTH / 2;
return x;
}
/*! \brief Paint the piano display view in response to an event
*
* This method draws the piano and the 'root note' base. It draws
* the base first, then all the white keys, then all the black keys.
*
* \todo Is there supposed to be a parameter given here?
*/
void PianoView::paintEvent( QPaintEvent * )
{
QPainter p( this );
// set smaller font for printing number of every octave
p.setFont( pointSize<LABEL_TEXT_SIZE>( p.font() ) );
// draw bar above the keyboard (there will be the labels
// for all C's)
p.fillRect( QRect( 0, 1, width(), PIANO_BASE-2 ), p.background() );
// draw the line above the keyboard
p.setPen( Qt::black );
p.drawLine( 0, 0, width(), 0 );
p.drawLine( 0, PIANO_BASE-1, width(), PIANO_BASE-1 );
p.setPen( Qt::white );
const int base_key = ( m_piano != NULL ) ?
m_piano->instrumentTrack()->baseNoteModel()->value() : 0;
QColor baseKeyColor = QApplication::palette().color( QPalette::Active,
QPalette::BrightText );
if( Piano::isWhiteKey( base_key ) )
{
p.fillRect( QRect( getKeyX( base_key ), 1, PW_WHITE_KEY_WIDTH-1,
PIANO_BASE-2 ), baseKeyColor );
}
else
{
p.fillRect( QRect( getKeyX( base_key ) + 1, 1,
PW_BLACK_KEY_WIDTH - 1, PIANO_BASE - 2 ), baseKeyColor);
}
int cur_key = m_startKey;
// draw all white keys...
for( int x = 0; x < width(); )
{
while( Piano::isBlackKey( cur_key ) )
{
++cur_key;
}
// draw pressed or not pressed key, depending on state of
// current key
if( m_piano && m_piano->isKeyPressed( cur_key ) )
{
p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPressedPm );
}
else
{
p.drawPixmap( x, PIANO_BASE, *s_whiteKeyPm );
}
x += PW_WHITE_KEY_WIDTH;
if( (Keys) (cur_key%KeysPerOctave) == Key_C )
{
// label key of note C with "C" and number of current
// octave
p.drawText( x - PW_WHITE_KEY_WIDTH, LABEL_TEXT_SIZE + 2,
QString( "C" ) + QString::number(
cur_key / KeysPerOctave, 10 ) );
}
++cur_key;
}
// reset all values, because now we're going to draw all black keys
cur_key = m_startKey;
int white_cnt = 0;
int startKey = m_startKey;
if( startKey > 0 && Piano::isBlackKey( (Keys)(--startKey) ) )
{
if( m_piano && m_piano->isKeyPressed( startKey ) )
{
p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm );
}
else
{
p.drawPixmap( 0 - PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm );
}
}
// now draw all black keys...
for( int x = 0; x < width(); )
{
if( Piano::isBlackKey( cur_key ) )
{
// draw pressed or not pressed key, depending on
// state of current key
if( m_piano && m_piano->isKeyPressed( cur_key ) )
{
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPressedPm );
}
else
{
p.drawPixmap( x + PW_WHITE_KEY_WIDTH / 2, PIANO_BASE, *s_blackKeyPm );
}
x += PW_WHITE_KEY_WIDTH;
white_cnt = 0;
}
else
{
// simple workaround for increasing x if there were two
// white keys (e.g. between E and F)
++white_cnt;
if( white_cnt > 1 )
{
x += PW_WHITE_KEY_WIDTH;
}
}
++cur_key;
}
}