/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified 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.

   JUCE 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 JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#include "../../../../juce_core/basics/juce_StandardHeader.h"

BEGIN_JUCE_NAMESPACE

#include "juce_MidiKeyboardComponent.h"
#include "../../graphics/brushes/juce_GradientBrush.h"


//==============================================================================
class MidiKeyboardUpDownButton  : public Button
{
public:
    MidiKeyboardUpDownButton (MidiKeyboardComponent* const owner_,
                              const int delta_)
        : Button (String::empty),
          owner (owner_),
          delta (delta_)
    {
        setOpaque (true);
    }

    ~MidiKeyboardUpDownButton()
    {
    }

    void clicked()
    {
        int note = owner->getLowestVisibleKey();

        if (delta < 0)
            note = (note - 1) / 12;
        else
            note = note / 12 + 1;

        owner->setLowestVisibleKey (note * 12);
    }

    void paintButton (Graphics& g,
                      bool isMouseOverButton,
                      bool isButtonDown)
    {
        owner->drawUpDownButton (g, getWidth(), getHeight(),
                                 isMouseOverButton, isButtonDown,
                                 delta > 0);
    }

private:
    MidiKeyboardComponent* const owner;
    const int delta;

    MidiKeyboardUpDownButton (const MidiKeyboardUpDownButton&);
    const MidiKeyboardUpDownButton& operator= (const MidiKeyboardUpDownButton&);
};

//==============================================================================
MidiKeyboardComponent::MidiKeyboardComponent (MidiKeyboardState& state_,
                                              const Orientation orientation_)
    : state (state_),
      xOffset (0),
      blackNoteLength (1),
      keyWidth (16),
      orientation (orientation_),
      midiChannel (1),
      midiInChannelMask (0xffff),
      velocity (1.0f),
      noteUnderMouse (-1),
      mouseDownNote (-1),
      rangeStart (0),
      rangeEnd (127),
      firstKey (12 * 4),
      canScroll (true),
      mouseDragging (false),
      keyPresses (4),
      keyPressNotes (16),
      keyMappingOctave (6)
{
    mouseOverKeyColour = Colours::yellow.withAlpha (0.5f);
    keyDownColour = Colours::yellow.darker();
    upDownButtonBackground = Colours::lightgrey;
    upDownButtonArrow = Colours::black;

    addChildComponent (scrollDown = new MidiKeyboardUpDownButton (this, -1));
    addChildComponent (scrollUp   = new MidiKeyboardUpDownButton (this, 1));

    // initialise with a default set of querty key-mappings..
    const char* const keymap = "awsedftgyhujkolp;";

    for (int i = String (keymap).length(); --i >= 0;)
        setKeyPressForNote (KeyPress (keymap[i], 0), i);

    setOpaque (true);
    setWantsKeyboardFocus (true);

    state.addListener (this);
}

MidiKeyboardComponent::~MidiKeyboardComponent()
{
    state.removeListener (this);
    jassert (mouseDownNote < 0 && keysPressed.countNumberOfSetBits() == 0); // leaving stuck notes!

    deleteAllChildren();
}

//==============================================================================
void MidiKeyboardComponent::setKeyWidth (const float widthInPixels)
{
    keyWidth = widthInPixels;
    resized();
}

void MidiKeyboardComponent::setOrientation (const Orientation newOrientation)
{
    if (orientation != newOrientation)
    {
        orientation = newOrientation;
        resized();
    }
}

void MidiKeyboardComponent::setAvailableRange (const int lowestNote,
                                               const int highestNote)
{
    jassert (lowestNote >= 0 && lowestNote <= 127);
    jassert (highestNote >= 0 && highestNote <= 127);
    jassert (lowestNote <= highestNote);

    if (rangeStart != lowestNote || rangeEnd != highestNote)
    {
        rangeStart = jlimit (0, 127, lowestNote);
        rangeEnd = jlimit (0, 127, highestNote);
        firstKey = jlimit (rangeStart, rangeEnd, firstKey);
        resized();
    }
}

void MidiKeyboardComponent::setLowestVisibleKey (int noteNumber)
{
    noteNumber = jlimit (rangeStart, rangeEnd, noteNumber);

    if (noteNumber != firstKey)
    {
        firstKey = noteNumber;
        resized();
    }
}

void MidiKeyboardComponent::setScrollButtonsVisible (const bool canScroll_)
{
    if (canScroll != canScroll_)
    {
        canScroll = canScroll_;
        resized();
    }
}

void MidiKeyboardComponent::setColours (const Colour& mouseOverKeyColour_,
                                        const Colour& keyDownColour_,
                                        const Colour& upDownButtonBackground_,
                                        const Colour& upDownButtonArrow_)
{
    mouseOverKeyColour = mouseOverKeyColour_;
    keyDownColour = keyDownColour_;
    upDownButtonBackground = upDownButtonBackground_;
    upDownButtonArrow = upDownButtonArrow_;
    repaint();
}

//==============================================================================
void MidiKeyboardComponent::setMidiChannel (const int midiChannelNumber)
{
    jassert (midiChannelNumber > 0 && midiChannelNumber <= 16);

    if (midiChannel != midiChannelNumber)
    {
        resetAnyKeysInUse();
        midiChannel = jlimit (1, 16, midiChannelNumber);
    }
}

void MidiKeyboardComponent::setMidiChannelsToDisplay (const int midiChannelMask)
{
    midiInChannelMask = midiChannelMask;
    triggerAsyncUpdate();
}

void MidiKeyboardComponent::setVelocity (const float velocity_)
{
    jassert (velocity > 0 && velocity <= 1.0f);

    velocity = jlimit (0.0f, 1.0f, velocity_);
}

//==============================================================================
void MidiKeyboardComponent::getKeyPos (int midiNoteNumber, int& x, int& w) const
{
    jassert (midiNoteNumber >= rangeStart && midiNoteNumber <= rangeEnd);
    midiNoteNumber -= rangeStart;

    const float blackNoteWidth = 0.7f;

    const float notePos[] = { 0.0f, 1 - blackNoteWidth * 0.6f,
                              1.0f, 2 - blackNoteWidth * 0.4f,
                              2.0f, 3.0f, 4 - blackNoteWidth * 0.7f,
                              4.0f, 5 - blackNoteWidth * 0.5f,
                              5.0f, 6 - blackNoteWidth * 0.3f,
                              6.0f };

    const float widths[] = { 1.0f, blackNoteWidth,
                             1.0f, blackNoteWidth,
                             1.0f, 1.0f, blackNoteWidth,
                             1.0f, blackNoteWidth,
                             1.0f, blackNoteWidth,
                             1.0f };

    const int octave = midiNoteNumber / 12;
    const int note = midiNoteNumber % 12;

    x = roundFloatToInt (octave * 7.0f * keyWidth + notePos [note] * keyWidth) - xOffset;
    w = roundFloatToInt (widths [note] * keyWidth);
}

const uint8 whiteNotes[] = { 0, 2, 4, 5, 7, 9, 11 };
const uint8 blackNotes[] = { 1, 3, 6, 8, 10 };

int MidiKeyboardComponent::xyToNote (int x, int y)
{
    if (! reallyContains (x, y, false))
        return -1;

    if (orientation != horizontalKeyboard)
    {
        swapVariables (x, y);

        if (orientation == verticalKeyboardFacingLeft)
            y = getWidth() - y;
        else
            x = getHeight() - x;
    }

    return remappedXYToNote (x + xOffset, y);
}

int MidiKeyboardComponent::remappedXYToNote (int x, int y) const
{
    const int noteX = x % roundFloatToInt (keyWidth * 7);
    const int octaveStart = 12 * ((x - noteX) / roundFloatToInt (keyWidth * 7));

    if (y < blackNoteLength)
    {
        for (int i = 0; i < 5; ++i)
        {
            const int note = rangeStart + octaveStart + blackNotes [i];

            if (note >= rangeStart && note <= rangeEnd)
            {
                int kx, kw;
                getKeyPos (note, kx, kw);
                kx += xOffset;

                if (x >= kx && x < kx + kw)
                    return note;
            }
        }
    }

    int result = rangeStart + octaveStart + whiteNotes [noteX / roundFloatToInt (keyWidth)];

    if (result < rangeStart || result > rangeEnd)
        result = -1;

    return result;
}

//==============================================================================
void MidiKeyboardComponent::repaintNote (const int noteNum)
{
    if (noteNum >= rangeStart && noteNum <= rangeEnd)
    {
        int x, w;
        getKeyPos (noteNum, x, w);

        if (orientation == horizontalKeyboard)
            repaint (x, 0, w, getHeight());
        else if (orientation == verticalKeyboardFacingLeft)
            repaint (0, x, getWidth(), w);
        else if (orientation == verticalKeyboardFacingRight)
            repaint (0, getHeight() - x - w, getWidth(), w);
    }
}

void MidiKeyboardComponent::paint (Graphics& g)
{
    g.fillAll (Colours::white);

    int x, w, octave;

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int white = 0; white < 7; ++white)
        {
            const int noteNum = octave + whiteNotes [white];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                getKeyPos (noteNum, x, w);

                if (orientation == horizontalKeyboard)
                    drawWhiteNote (noteNum, g, x, 0, w, getHeight(), state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
                else if (orientation == verticalKeyboardFacingLeft)
                    drawWhiteNote (noteNum, g, 0, x, getWidth(), w, state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
                else if (orientation == verticalKeyboardFacingRight)
                    drawWhiteNote (noteNum, g, 0, getHeight() - x - w, getWidth(), w, state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
            }
        }
    }

    float x1 = 0.0f, y1 = 0.0f, x2 = 0.0f, y2 = 0.0f;

    if (orientation == verticalKeyboardFacingLeft)
    {
        x1 = getWidth() - 1.0f;
        x2 = getWidth() - 5.0f;
    }
    else if (orientation == verticalKeyboardFacingRight)
        x2 = 5.0f;
    else
        y2 = 5.0f;

    GradientBrush gb (Colours::black.withAlpha (0.3f), x1, y1,
                      Colours::transparentBlack, x2, y2, false);
    g.setBrush (&gb);

    getKeyPos (rangeEnd, x, w);
    x += w;

    if (orientation == verticalKeyboardFacingLeft)
        g.fillRect (getWidth() - 5, 0, 5, x);
    else if (orientation == verticalKeyboardFacingRight)
        g.fillRect (0, 0, 5, x);
    else
        g.fillRect (0, 0, x, 5);

    g.setColour (Colours::black.withAlpha (0.2f));

    if (orientation == verticalKeyboardFacingLeft)
        g.fillRect (0, 0, 1, x);
    else if (orientation == verticalKeyboardFacingRight)
        g.fillRect (getWidth() - 1, 0, 1, x);
    else
        g.fillRect (0, getHeight() - 1, x, 1);

    for (octave = 0; octave < 128; octave += 12)
    {
        for (int black = 0; black < 5; ++black)
        {
            const int noteNum = octave + blackNotes [black];

            if (noteNum >= rangeStart && noteNum <= rangeEnd)
            {
                getKeyPos (noteNum, x, w);

                if (orientation == horizontalKeyboard)
                    drawBlackNote (noteNum, g, x, 0, w, blackNoteLength, state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
                else if (orientation == verticalKeyboardFacingLeft)
                    drawBlackNote (noteNum, g, getWidth() - blackNoteLength, x, blackNoteLength, w, state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
                else if (orientation == verticalKeyboardFacingRight)
                    drawBlackNote (noteNum, g, 0, getHeight() - x - w, blackNoteLength, w, state.isNoteOnForChannels (midiInChannelMask, noteNum), noteUnderMouse == noteNum);
            }
        }
    }
}

void MidiKeyboardComponent::drawWhiteNote (int midiNoteNumber,
                                           Graphics& g, int x, int y, int w, int h,
                                           bool isDown, bool isOver)
{
    Colour c (Colours::transparentWhite);

    if (isDown)
        c = c.overlaidWith (keyDownColour);

    if (isOver)
        c = c.overlaidWith (mouseOverKeyColour);

    g.setColour (c);
    g.fillRect (x, y, w, h);

    const String text (getWhiteNoteText (midiNoteNumber));

    if (! text.isEmpty())
    {
        g.setColour (Colours::black);
        Font f (jmin (12.0f, keyWidth * 0.9f));
        f.setHorizontalScale (0.8f);
        g.setFont (f);
        Justification justification (Justification::centredBottom);

        if (orientation == verticalKeyboardFacingLeft)
            justification = Justification::centredLeft;
        else if (orientation == verticalKeyboardFacingRight)
            justification = Justification::centredRight;

        g.drawFittedText (text, x + 2, y + 2, w - 4, h - 4, justification, 1);
    }

    g.setColour (Colours::grey);

    if (orientation == horizontalKeyboard)
        g.fillRect (x, y, 1, h);
    else if (orientation == verticalKeyboardFacingLeft)
        g.fillRect (x, y, w, 1);
    else if (orientation == verticalKeyboardFacingRight)
        g.fillRect (x, y + h - 1, w, 1);

    if (midiNoteNumber == rangeEnd)
    {
        if (orientation == horizontalKeyboard)
            g.fillRect (x + w, y, 1, h);
        else if (orientation == verticalKeyboardFacingLeft)
            g.fillRect (x, y + h, w, 1);
        else if (orientation == verticalKeyboardFacingRight)
            g.fillRect (x, y - 1, w, 1);
    }
}

void MidiKeyboardComponent::drawBlackNote (int /*midiNoteNumber*/,
                                           Graphics& g, int x, int y, int w, int h,
                                           bool isDown, bool isOver)
{
    Colour c (Colours::black);

    if (isDown)
        c = c.overlaidWith (keyDownColour);

    if (isOver)
        c = c.overlaidWith (mouseOverKeyColour);

    g.setColour (c);
    g.fillRect (x, y, w, h);

    if (isDown)
    {
        g.setColour (Colours::black);
        g.drawRect (x, y, w, h);
    }
    else
    {
        const int xIndent = jmax (1, jmin (w, h) / 8);

        g.setColour (c.brighter());

        if (orientation == horizontalKeyboard)
            g.fillRect (x + xIndent, y, w - xIndent * 2, 7 * h / 8);
        else if (orientation == verticalKeyboardFacingLeft)
            g.fillRect (x + w / 8, y + xIndent, w - w / 8, h - xIndent * 2);
        else if (orientation == verticalKeyboardFacingRight)
            g.fillRect (x, y + xIndent, 7 * w / 8, h - xIndent * 2);
    }
}

const String MidiKeyboardComponent::getWhiteNoteText (const int midiNoteNumber)
{
    if (keyWidth > 14 && midiNoteNumber % 12 == 0)
        return T("C") + String (midiNoteNumber / 12 - 2);

    return String::empty;
}

void MidiKeyboardComponent::drawUpDownButton (Graphics& g, int w, int h,
                                              const bool isMouseOver,
                                              const bool isButtonDown,
                                              const bool movesOctavesUp)
{
    g.fillAll (upDownButtonBackground);

    float angle;

    if (orientation == MidiKeyboardComponent::horizontalKeyboard)
        angle = movesOctavesUp ? 0.0f : 0.5f;
    else if (orientation == MidiKeyboardComponent::verticalKeyboardFacingLeft)
        angle = movesOctavesUp ? 0.25f : 0.75f;
    else
        angle = movesOctavesUp ? 0.75f : 0.25f;

    Path path;
    path.lineTo (0.0f, 1.0f);
    path.lineTo (1.0f, 0.5f);
    path.closeSubPath();

    path.applyTransform (AffineTransform::identity
                            .rotated (float_Pi * 2.0f * angle, 0.5f, 0.5f));

    g.setColour (upDownButtonArrow.withAlpha (isButtonDown ? 1.0f : (isMouseOver ? 0.6f : 0.4f)));

    g.fillPath (path, path.getTransformToScaleToFit (1.0f, 1.0f,
                                                     w - 2.0f,
                                                     h - 2.0f,
                                                     true));
}

void MidiKeyboardComponent::resized()
{
    int w = getWidth();
    int h = getHeight();

    if (orientation != horizontalKeyboard)
        swapVariables (w, h);

    blackNoteLength = roundFloatToInt (h * 0.7f);

    bool showScrollButtons = false;

    if (canScroll)
    {
        int kx2, kw2;
        getKeyPos (rangeEnd, kx2, kw2);

        kx2 += kw2;

        if (firstKey != rangeStart)
        {
            int kx1, kw1;
            getKeyPos (rangeStart, kx1, kw1);

            if (kx2 - kx1 <= w)
            {
                firstKey = rangeStart;
                repaint();
            }
        }

        showScrollButtons = (firstKey > rangeStart || kx2 > w + xOffset * 2);
    }

    scrollDown->setVisible (showScrollButtons);
    scrollUp->setVisible (showScrollButtons);

    xOffset = 0;

    if (showScrollButtons)
    {
        const int scrollButtonW = jmin (12, w / 2);

        if (orientation == horizontalKeyboard)
        {
            scrollDown->setBounds (0, 0, scrollButtonW, getHeight());
            scrollUp->setBounds (getWidth() - scrollButtonW, 0, scrollButtonW, getHeight());
        }
        else if (orientation == verticalKeyboardFacingLeft)
        {
            scrollDown->setBounds (0, 0, getWidth(), scrollButtonW);
            scrollUp->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
        }
        else if (orientation == verticalKeyboardFacingRight)
        {
            scrollDown->setBounds (0, getHeight() - scrollButtonW, getWidth(), scrollButtonW);
            scrollUp->setBounds (0, 0, getWidth(), scrollButtonW);
        }

        int endOfLastKey, kw;
        getKeyPos (rangeEnd, endOfLastKey, kw);
        endOfLastKey += kw;

        const int spaceAvailable = w - scrollButtonW * 2;
        const int lastStartKey = remappedXYToNote (endOfLastKey - spaceAvailable, 0) + 1;

        if (lastStartKey >= 0 && firstKey > lastStartKey)
            firstKey = jlimit (rangeStart, rangeEnd, lastStartKey);

        getKeyPos (firstKey, xOffset, kw);
        xOffset -= scrollButtonW;
    }
    else
    {
        firstKey = rangeStart;
    }

    timerCallback();
    repaint();
}

//==============================================================================
void MidiKeyboardComponent::handleNoteOn (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/, float /*velocity*/)
{
    triggerAsyncUpdate();
}

void MidiKeyboardComponent::handleNoteOff (MidiKeyboardState*, int /*midiChannel*/, int /*midiNoteNumber*/)
{
    triggerAsyncUpdate();
}

void MidiKeyboardComponent::handleAsyncUpdate()
{
    for (int i = rangeStart; i <= rangeEnd; ++i)
    {
        if (keysCurrentlyDrawnDown[i] != state.isNoteOnForChannels (midiInChannelMask, i))
        {
            keysCurrentlyDrawnDown.setBit (i, state.isNoteOnForChannels (midiInChannelMask, i));
            repaintNote (i);
        }
    }
}

//==============================================================================
void MidiKeyboardComponent::resetAnyKeysInUse()
{
    if (keysPressed.countNumberOfSetBits() > 0 || mouseDownNote > 0)
    {
        state.allNotesOff (midiChannel);
        keysPressed.clear();
        mouseDownNote = -1;
    }
}

void MidiKeyboardComponent::updateNoteUnderMouse (int x, int y)
{
    const int newNote = xyToNote (x, y);

    if (noteUnderMouse != newNote)
    {
        if (mouseDownNote >= 0)
        {
            state.noteOff (midiChannel, mouseDownNote);
            mouseDownNote = -1;
        }

        if (mouseDragging && newNote >= 0)
        {
            state.noteOn (midiChannel, newNote, velocity);
            mouseDownNote = newNote;
        }

        repaintNote (noteUnderMouse);
        noteUnderMouse = newNote;
        repaintNote (noteUnderMouse);
    }
    else if (mouseDownNote >= 0 && ! mouseDragging)
    {
        state.noteOff (midiChannel, mouseDownNote);
        mouseDownNote = -1;
    }
}

void MidiKeyboardComponent::mouseMove (const MouseEvent& e)
{
    updateNoteUnderMouse (e.x, e.y);
    stopTimer();
}

void MidiKeyboardComponent::mouseDrag (const MouseEvent& e)
{
    updateNoteUnderMouse (e.x, e.y);
}

bool MidiKeyboardComponent::mouseDownOnKey (int /*midiNoteNumber*/, const MouseEvent&)
{
    return true;
}

void MidiKeyboardComponent::mouseDown (const MouseEvent& e)
{
    const int newNote = xyToNote (e.x, e.y);
    mouseDragging = false;

    if (newNote >= 0 && mouseDownOnKey (newNote, e))
    {
        repaintNote (noteUnderMouse);
        noteUnderMouse = -1;
        mouseDragging = true;

        updateNoteUnderMouse (e.x, e.y);
        startTimer (500);
    }
}

void MidiKeyboardComponent::mouseUp (const MouseEvent& e)
{
    mouseDragging = false;
    updateNoteUnderMouse (e.x, e.y);

    stopTimer();
}

void MidiKeyboardComponent::mouseEnter (const MouseEvent& e)
{
    updateNoteUnderMouse (e.x, e.y);
}

void MidiKeyboardComponent::mouseExit (const MouseEvent& e)
{
    updateNoteUnderMouse (e.x, e.y);
}

void MidiKeyboardComponent::mouseWheelMove (const MouseEvent&, float wheelIncrement)
{
    setLowestVisibleKey (getLowestVisibleKey() + roundFloatToInt (wheelIncrement * 5.0f));
}

void MidiKeyboardComponent::timerCallback()
{
    updateNoteUnderMouse (getMouseXRelative(),
                          getMouseYRelative());
}

//==============================================================================
void MidiKeyboardComponent::clearKeyMappings()
{
    resetAnyKeysInUse();
    keyPressNotes.clear();
    keyPresses.clear();
}

void MidiKeyboardComponent::setKeyPressForNote (const KeyPress& key,
                                                const int midiNoteOffsetFromC)
{
    removeKeyPressForNote (midiNoteOffsetFromC);

    keyPressNotes.add (midiNoteOffsetFromC);
    keyPresses.add (new KeyPress (key));
}

void MidiKeyboardComponent::removeKeyPressForNote (const int midiNoteOffsetFromC)
{
    for (int i = keyPressNotes.size(); --i >= 0;)
    {
        if (keyPressNotes.getUnchecked (i) == midiNoteOffsetFromC)
        {
            keyPressNotes.remove (i);
            keyPresses.remove (i);
        }
    }
}

void MidiKeyboardComponent::setKeyPressBaseOctave (const int newOctaveNumber)
{
    jassert (newOctaveNumber >= 0 && newOctaveNumber <= 10);

    keyMappingOctave = newOctaveNumber;
}

void MidiKeyboardComponent::keyStateChanged()
{
    bool keyPressUsed = false;

    for (int i = keyPresses.size(); --i >= 0;)
    {
        const KeyPress key (*keyPresses.getUnchecked (i));
        const int note = 12 * keyMappingOctave + keyPressNotes.getUnchecked (i);

        if (key.isCurrentlyDown())
        {
            if (! keysPressed [note])
            {
                keysPressed.setBit (note);
                state.noteOn (midiChannel, note, velocity);
                keyPressUsed = true;
            }
        }
        else
        {
            if (keysPressed [note])
            {
                keysPressed.clearBit (note);
                state.noteOff (midiChannel, note);
                keyPressUsed = true;
            }
        }
    }

    if (! keyPressUsed)
        Component::keyStateChanged();
}

void MidiKeyboardComponent::focusLost (FocusChangeType)
{
    resetAnyKeysInUse();
}


END_JUCE_NAMESPACE
