/* * Piano.cpp - implementation of piano-widget used in instrument-track-window * for testing + according model class * * Copyright (c) 2004-2010 Tobias Doerffel * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * 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 #include #include #include #include #include "PianoView.h" #include "caption_menu.h" #include "embed.h" #include "engine.h" #include "gui_templates.h" #include "InstrumentTrack.h" #include "knob.h" #include "string_pair_drag.h" #include "MainWindow.h" #include "midi.h" #include "templates.h" #include "update_event.h" #ifdef Q_WS_X11 #include #endif /*! The black / white order of keys as they appear on the keyboard. */ const Piano::KeyTypes KEY_ORDER[] = { // C CIS D DIS Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, // E F FIS G Piano::WhiteKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey, // GIS A B H Piano::BlackKey, Piano::WhiteKey, Piano::BlackKey, Piano::WhiteKey } ; /*! 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 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' } #endif #ifdef LMMS_BUILD_LINUX 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' } #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(); if( m_piano != NULL ) { connect( m_piano->m_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( KEY_ORDER[( m_startKey+i ) % KeysPerOctave] == Piano::BlackKey ) { ++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 && KEY_ORDER[(key_num-1 ) % KeysPerOctave] == Piano::BlackKey && _p.x() % PW_WHITE_KEY_WIDTH <= ( PW_WHITE_KEY_WIDTH / 2 ) - ( PW_BLACK_KEY_WIDTH / 2 ) ) { --key_num; } if( key_num < NumKeys - 1 && KEY_ORDER[( key_num + 1 ) % KeysPerOctave] == Piano::BlackKey && _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->m_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 Uint32 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 / ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) * (float) MidiMaxVelocity ); if( y_diff < 0 ) { velocity = 0; } else if( y_diff > ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) ) { velocity = MidiMaxVelocity; } // set note on m_piano->m_instrumentTrack->processInEvent( midiEvent( MidiNoteOn, 0, key_num, velocity ), midiTime() ); m_piano->m_pressedKeys[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-> m_instrumentTrack-> baseNoteModel()->id() ), QPixmap(), this ); _me->accept(); } else { m_piano->m_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->m_midiEvProc->processInEvent( midiEvent( MidiNoteOff, 0, m_lastKey, 0 ), midiTime() ); m_piano->m_pressedKeys[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 / ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) * (float) MidiMaxVelocity ); // 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 > ( ( KEY_ORDER[key_num % KeysPerOctave] == Piano::WhiteKey ) ? PW_WHITE_KEY_HEIGHT : PW_BLACK_KEY_HEIGHT ) ) { velocity = MidiMaxVelocity; } // 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->m_midiEvProc->processInEvent( midiEvent( MidiNoteOff, 0, m_lastKey, 0 ), midiTime() ); m_piano->m_pressedKeys[m_lastKey] = false; m_lastKey = -1; } if( _me->buttons() & Qt::LeftButton ) { if( _me->pos().y() > PIANO_BASE ) { m_piano->m_midiEvProc->processInEvent( midiEvent( MidiNoteOn, 0, key_num, velocity ), midiTime() ); m_piano->m_pressedKeys[key_num] = true; m_lastKey = key_num; } else { m_piano->m_instrumentTrack-> baseNoteModel()-> setInitValue( (float) key_num ); } } // and let the user see that he pressed a key... :) update(); } else if( m_piano->m_pressedKeys[key_num] == true ) { m_piano->m_midiEvProc->processInEvent( midiEvent( MidiKeyPressure, 0, key_num, velocity ), midiTime() ); } } /*! \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->m_midiEvProc->processInEvent( midiEvent( MidiNoteOff, 0, i, 0 ), midiTime() ); m_piano->m_pressedKeys[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( KEY_ORDER[k % KeysPerOctave] == Piano::WhiteKey ) { ++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( p.font() ) ); // draw blue bar above the actual keyboard (there will be the labels // for all C's) QLinearGradient g( 0, 0, 0, PIANO_BASE-3 ); g.setColorAt( 0, Qt::black ); g.setColorAt( 0.1, QColor( 96, 96, 96 ) ); g.setColorAt( 1, Qt::black ); p.fillRect( QRect( 0, 1, width(), PIANO_BASE-2 ), g ); // draw stuff above the actual 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->m_instrumentTrack->baseNoteModel()->value() : 0; g.setColorAt( 0, QColor( 0, 96, 0 ) ); g.setColorAt( 0.1, QColor( 64, 255, 64 ) ); g.setColorAt( 1, QColor( 0, 96, 0 ) ); if( KEY_ORDER[base_key % KeysPerOctave] == Piano::WhiteKey ) { p.fillRect( QRect( getKeyX( base_key ), 1, PW_WHITE_KEY_WIDTH-1, PIANO_BASE-2 ), g ); } else { p.fillRect( QRect( getKeyX( base_key ) + 1, 1, PW_BLACK_KEY_WIDTH - 1, PIANO_BASE - 2 ), g ); } int cur_key = m_startKey; // draw all white keys... for( int x = 0; x < width(); ) { while( KEY_ORDER[cur_key%KeysPerOctave] != Piano::WhiteKey ) { ++cur_key; } // draw pressed or not pressed key, depending on state of // current key if( m_piano && m_piano->m_pressedKeys[cur_key] == true ) { 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 s_key = m_startKey; if( s_key > 0 && KEY_ORDER[(Keys)(--s_key) % KeysPerOctave] == Piano::BlackKey ) { if( m_piano && m_piano->m_pressedKeys[s_key] == true ) { 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( KEY_ORDER[cur_key%KeysPerOctave] == Piano::BlackKey ) { // draw pressed or not pressed key, depending on // state of current key if( m_piano && m_piano->m_pressedKeys[cur_key] == true ) { 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; } } #include "moc_PianoView.cxx"