text:
    /*
  ==============================================================================

    MidiSettingsPanel.cpp
    Created: 13 Nov 2019 7:09:59pm
    Author:  Vincenzo Sicurella

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

#include "MidiSettingsPanel.h"

MidiSettingsPanel::MidiSettingsPanel(SvkPluginState* pluginStateIn)
    : SvkSettingsPanel("MidiSettingsPanel", pluginStateIn,
        {
            "Filters",
            "Devices"
        },
        {
            IDs::periodShift,
            IDs::transposeAmt,
            IDs::keyboardMidiChannel,
            IDs::midiInputName,
            IDs::midiOutputName
        },
        {
            SvkControlProperties(ControlTypeNames::SliderControl, "Period Shift", true, 0, 0, -10, 10),
            SvkControlProperties(ControlTypeNames::SliderControl, "Transpose", true, 0, 0, -128, 128),
            SvkControlProperties(ControlTypeNames::SliderControl, "MIDI Channel Out", true, 0, 1, 1, 16),
            SvkControlProperties(ControlTypeNames::MenuControl, "Midi Input Device", true, 1),
            SvkControlProperties(ControlTypeNames::MenuControl, "Midi Output Device", true, 1)
        }
    )
{
    Array<Identifier> labelledControlIDs =
    {
        IDs::periodShift,
        IDs::transposeAmt,
        IDs::keyboardMidiChannel,
        IDs::midiInputName,
        IDs::midiOutputName
    };

    for (auto id : labelledControlIDs)
    {
        idToLabelledControl.set(id, static_cast<LabelledComponent*>(idToControl[id]));
    }

    midiProcessor = pluginState->getMidiProcessor();

    idToLabelledControl[IDs::periodShift]->setComponentSize(100, 24);
    periodShiftSlider = LabelledComponent::getComponentPointer<Slider>(idToLabelledControl[IDs::periodShift]);
    periodShiftSlider->setSliderStyle(Slider::SliderStyle::IncDecButtons);
    periodShiftSlider->setTextBoxStyle(Slider::TextBoxLeft, false, 40, 24);
    periodShiftSlider->setValue(midiProcessor->getPeriodShift());
    periodShiftSlider->addListener(this);

    idToLabelledControl[IDs::transposeAmt]->setComponentSize(100, 24);
    transposeSlider = LabelledComponent::getComponentPointer<Slider>(idToLabelledControl[IDs::transposeAmt]);
    transposeSlider->setSliderStyle(Slider::SliderStyle::IncDecButtons);
    transposeSlider->setTextBoxStyle(Slider::TextBoxLeft, false, 40, 24);
    transposeSlider->setValue(midiProcessor->getTransposeAmt());
    transposeSlider->addListener(this);

    idToLabelledControl[IDs::keyboardMidiChannel]->setComponentSize(100, 24);
    midiChannelSlider = LabelledComponent::getComponentPointer<Slider>(idToLabelledControl[IDs::keyboardMidiChannel]);
    midiChannelSlider->setSliderStyle(Slider::SliderStyle::IncDecButtons);
    midiChannelSlider->setTextBoxStyle(Slider::TextBoxLeft, false, 40, 24);
    midiChannelSlider->setValue(midiProcessor->getMidiChannelOut());
    midiChannelSlider->addListener(this);

    // Setup device settings if on standalone version, or hide
    if (JUCEApplication::isStandaloneApp())
    {
        inputBoxLabelled = static_cast<LabelledComponent*>(idToControl[IDs::midiInputName]);
        inputBoxLabelled->setComponentSize(320, 24);
        inputBox = LabelledComponent::getComponentPointer<ComboBox>(inputBoxLabelled);
        inputBox->addListener(this);

        outputBoxLabelled = static_cast<LabelledComponent*>(idToControl[IDs::midiOutputName]);
        outputBoxLabelled->setComponentSize(320, 24);
        outputBox = LabelledComponent::getComponentPointer<ComboBox>(outputBoxLabelled);
        outputBox->addListener(this);
    }
    else
    {
        controls.removeObject(idToControl[IDs::midiInputName], true);
        controls.removeObject(idToControl[IDs::midiOutputName], true);

        sectionHeaderLabels.remove(1);
        flexParent.items.remove(1);
    }
}

MidiSettingsPanel::~MidiSettingsPanel()
{
}

void MidiSettingsPanel::visibilityChanged()
{
    if (isVisible() && JUCEApplication::isStandaloneApp())
    {
        refreshDevices();
        startTimer(1000);
    }
    else
    {
        stopTimer();
    }
}

void MidiSettingsPanel::sliderValueChanged(Slider* sliderThatChanged)
{
    if (sliderThatChanged == periodShiftSlider)
    {
        midiProcessor->setPeriodShift(sliderThatChanged->getValue());
    }

    else if (sliderThatChanged == transposeSlider)
    {
        midiProcessor->setTransposeAmt(sliderThatChanged->getValue());
    }

    else if (sliderThatChanged == periodShiftSlider)
    {
        midiProcessor->setMidiChannelOut(sliderThatChanged->getValue());
    }
}

void MidiSettingsPanel::comboBoxChanged(ComboBox* comboBoxThatChanged)
{
    // Midi Input Changed
    if (inputBox == comboBoxThatChanged)
    {
        MidiDeviceInfo& device = availableIns.getReference(inputBox->getSelectedId() - 1);
        DBG("Midi Input Selected: " + device.name);
        pluginState->getMidiProcessor()->setMidiInput(device.identifier);
    }

    // Midi Output Changed
    else if (outputBox == comboBoxThatChanged)
    {
        MidiDeviceInfo& device = availableOuts.getReference(outputBox->getSelectedId() - 1);
        DBG("Midi Output Selected: " + device.name);
        pluginState->getMidiProcessor()->setMidiOutput(device.identifier);
    }
}

void MidiSettingsPanel::timerCallback()
{
    refreshDevices();
}

void MidiSettingsPanel::refreshDevices()
{
    Array<MidiDeviceInfo> inputDevices = MidiInput::getAvailableDevices();

    if (availableIns != inputDevices && !inputBox->isPopupActive())
    {
        availableIns = inputDevices;
        inputBox->clear(dontSendNotification);
        inputBox->setText(pluginState->getMidiProcessor()->getInputName(), dontSendNotification);

        int i = 1;
        for (auto device : availableIns)
        {
            inputBox->addItem(device.name, i++);
        }
    }

    Array<MidiDeviceInfo> outputDevices = MidiOutput::getAvailableDevices();

    if (availableOuts != outputDevices && !outputBox->isPopupActive())
    {
        availableOuts = outputDevices;
        outputBox->clear(dontSendNotification);
        outputBox->setText(pluginState->getMidiProcessor()->getOutputName(), dontSendNotification);

        int i = 1;
        for (auto device : availableOuts)
        {
            outputBox->addItem(device.name, i++);
        }
    }
}


text:
/*
  ==============================================================================

    MappingSettingsPanel.cpp
    Created: 7 Oct 2020 10:00:18pm
    Author:  Vincenzo

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

#include "MappingSettingsPanel.h"

MappingSettingsPanel::MappingSettingsPanel(SvkPluginState* pluginStateIn)
    : SvkSettingsPanel(
        "MappingSettings", pluginStateIn,
        {
            "Current Mapping",
            ""
            //"Advanced Mapping"
        },
        {
            IDs::noteMapNode,
            Identifier("Message")
        },
        {
            SvkControlProperties(ControlTypeNames::GenericControl, "Current Mapping", false, 0),
            SvkControlProperties(ControlTypeNames::GenericControl, "New features message", false, 1)
            //SvkControlProperties(ControlTypeNames::GenericControl, "Advanced Mapping Note", false, 1)
        }
    )
{
    currentMappingLabel = static_cast<Label*>(getSectionFlexBox(0)->items.getReference(0).associatedComponent);

    noteMapEditor = new NoteMapEditor(*pluginState->getMidiInputFilterMap());
    controls.set(0, noteMapEditor, true);
    addAndMakeVisible(noteMapEditor);
    getSectionFlexBox(0)->items.getReference(1).associatedComponent = noteMapEditor;

    mappingModeChanged(pluginState->getMappingMode());

    pluginState->addListener(this);

    Label* msg = new Label("Message", "Currently, you can only edit the key number. More mapping features on the way! :)");
    controls.set(1, msg, true);
    addAndMakeVisible(msg);
    getSectionFlexBox(1)->items.getReference(0).associatedComponent = msg;

    flexParent.items.getReference(0).maxWidth = 400;
}

MappingSettingsPanel::~MappingSettingsPanel()
{
    pluginState->removeListener(this);
}

void MappingSettingsPanel::visibilityChanged()
{
    if (!isVisible())
    {
        noteMapEditor->removeAllListeners();
    }
}

void MappingSettingsPanel::mappingModeChanged(int mappingModeId)
{
    // Mapping off
    if (mappingModeId == 1)
    {
        noteMapEditor->setEnabled(false);
        currentMappingLabel->setText("Current Mapping (No mapping active)", dontSendNotification);
        noteMapEditor->resetMapping(pluginState->getMidiProcessor()->getManualNoteMap(), false);
    }
    // Auto Map
    else if (mappingModeId == 2)
    {
        noteMapEditor->setEnabled(false);
        currentMappingLabel->setText("Current Mapping (Auto Map / Read-only)", dontSendNotification);
    }
    // Manual Map
    else if (mappingModeId == 3)
    {
        noteMapEditor->setEnabled(true);
        currentMappingLabel->setText("Current Mapping", dontSendNotification);
    }
}

void MappingSettingsPanel::inputMappingChanged(NoteMap& newNoteMap)
{
    noteMapEditor->resetMapping(newNoteMap, false);
}

void MappingSettingsPanel::mappingEditorChanged(NoteMap& newNoteMap)
{
    pluginState->setMidiInputMap(newNoteMap, true, false);
}

void MappingSettingsPanel::registerEditorListener(MappingEditor::Listener* listenerIn)
{
    noteMapEditor->addListener(listenerIn);
}

repo_name: vsicurella/SuperVirtualKeyboard

mime_type: text/x-c


text:
/*
  ==============================================================================

    GeneralSettingsPanel.h
    Created: 3 Sep 2020 9:11:33pm
    Author:  Vincenzo Sicurella

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

#pragma once

#include "SvkSettingsPanel.h"

class GeneralSettingsPanel : public SvkSettingsPanel
{

public:

    GeneralSettingsPanel(SvkPluginState*);
    ~GeneralSettingsPanel() override;

    void directoryChanged(DirectoryBrowserComponent*, File) override;

private:

    LabelledComponent* presetLabel;
    LabelledComponent* modeLabel;
    LabelledComponent* settingsLabel;

    DirectoryBrowserComponent* presetDirectoryBrowser;
    DirectoryBrowserComponent* modeDirectoryBrowser;
    DirectoryBrowserComponent* settingsDirectoryBrowser;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (GeneralSettingsPanel)
};


text:
/*
  ==============================================================================

    KeyboardSettingsPanel.cpp
    Created: 13 Nov 2019 7:09:31pm
    Author:  Vincenzo Sicurella

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

#include "KeyboardSettingsPanel.h"

KeyboardSettingsPanel::KeyboardSettingsPanel(SvkPluginState* pluginStateIn)
    : SvkSettingsPanel("KeyboardSettingsPanel", pluginStateIn,
        {
            "Keyboard settings"
        },
        {
            IDs::keyboardKeysStyle,
            IDs::keyboardHighlightStyle,
            IDs::pianoWHRatio,
            IDs::pianoKeyShowNumber
        },
        {
            SvkControlProperties(ControlTypeNames::MenuControl, "Key Layout Style", true),
            SvkControlProperties(ControlTypeNames::MenuControl, "Key Highlight Style", true),
            SvkControlProperties(ControlTypeNames::SliderControl, "Key Proportion", true),
            SvkControlProperties(ControlTypeNames::ToggleControl, "Show Key Numbers")
        }
    )
{
    layoutLabel = static_cast<LabelledComponent*>(controls[0]);
    layoutLabel->setComponentSize(136, controlMinHeight);

    keyLayoutBox = LabelledComponent::getComponentPointer<ComboBox>(layoutLabel);
    keyLayoutBox->addListener(this);
    keyLayoutBox->setEditableText(false);
    keyLayoutBox->setJustificationType(Justification::centredLeft);
    keyLayoutBox->setTextWhenNothingSelected(String());
    keyLayoutBox->setTextWhenNoChoicesAvailable(TRANS("(no choices)"));
    keyLayoutBox->addItem(TRANS("Nested Right"), 1);
    keyLayoutBox->addItem(TRANS("Nested Center"), 2);
    keyLayoutBox->addItem(TRANS("Flat"), 3);
    keyLayoutBox->addItem(TRANS("Adjacent"), 4);

    highlightLabel = static_cast<LabelledComponent*>(controls[1]);
    highlightLabel->setComponentSize(96, controlMinHeight);

    keyHighlightBox = LabelledComponent::getComponentPointer<ComboBox>(highlightLabel);
    keyHighlightBox->addListener(this);
    keyHighlightBox->setEditableText(false);
    keyHighlightBox->setJustificationType(Justification::centredLeft);
    keyHighlightBox->setTextWhenNothingSelected(String());
    keyHighlightBox->setTextWhenNoChoicesAvailable(TRANS("(no choices)"));
    keyHighlightBox->addItem(TRANS("Full Key"), 1);
    keyHighlightBox->addItem(TRANS("Inside"), 2);
    keyHighlightBox->addItem(TRANS("Border"), 3);
    keyHighlightBox->addItem(TRANS("Circles"), 4);
    keyHighlightBox->addItem(TRANS("Squares"), 5);
    keyHighlightBox->addListener(this);

    ratioLabel = static_cast<LabelledComponent*>(controls[2]);
    ratioLabel->setComponentSize(225, controlMinHeight);
    flexSections.getReference(0).items.getReference(2).minWidth = ratioLabel->getWidth();

    keyRatioSlider = LabelledComponent::getComponentPointer<Slider>(ratioLabel);
    keyRatioSlider->setRange(0.01, 1, 0.01);
    keyRatioSlider->addListener(this);

    showNoteNumbers = static_cast<TextButton*>(controls[3]);
    showNoteNumbers->setSize(Font().getStringWidth(showNoteNumbers->getButtonText()) + 5, controlMinHeight);
    showNoteNumbers->addListener(this);
}

void KeyboardSettingsPanel::comboBoxChanged(ComboBox* boxThatChanged)
{
    if (virtualKeyboard)
    {
        if (boxThatChanged == keyLayoutBox)
        {
            virtualKeyboard->setKeyStyle(boxThatChanged->getSelectedId());
            virtualKeyboard->resized();
        }

        else if (boxThatChanged == keyHighlightBox)
        {
            virtualKeyboard->setHighlightStyle(boxThatChanged->getSelectedId());
        }
    }
}

void KeyboardSettingsPanel::sliderValueChanged(Slider* sliderChanged)
{
    if (virtualKeyboard)
    {
        if (sliderChanged == keyRatioSlider)
        {
            virtualKeyboard->setKeySizeRatio(sliderChanged->getValue(), false);
            getParentComponent()->getParentComponent()->resized();
        }
    }
}

void KeyboardSettingsPanel::buttonClicked(Button* clickedButton)
{
    if (virtualKeyboard)
    {
        if (clickedButton == showNoteNumbers)
        {
            virtualKeyboard->setShowNoteNumbers(clickedButton->getToggleState());
        }
    }
}

void KeyboardSettingsPanel::setKeyboardPointer(VirtualKeyboard::Keyboard* keyboardPointer)
{
    virtualKeyboard = keyboardPointer;

    keyLayoutBox->setSelectedId(virtualKeyboard->getKeyPlacementStyle(), dontSendNotification);
    keyHighlightBox->setSelectedId(virtualKeyboard->getHighlightStyle(), dontSendNotification);
    keyRatioSlider->setValue(virtualKeyboard->getKeySizeRatio(), dontSendNotification);
    showNoteNumbers->setToggleState(virtualKeyboard->isShowingNoteNumbers(), dontSendNotification);
}


text:
/*
  ==============================================================================

    ModeSettingsPanel.h
    Created: 26 Sep 2020 1:59:11pm
    Author:  Vincenzo

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

#pragma once

#include "SvkSettingsPanel.h"

class ModeSettingsPanel : public SvkSettingsPanel
{
public:

    ModeSettingsPanel(SvkPluginState*);
    ~ModeSettingsPanel();

};


text:
/*
  ==============================================================================

    GeneralSettingsPanel.cpp
    Created: 13 Nov 2019 7:11:33pm
    Author:  Vincenzo Sicurella

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

#include "GeneralSettingsPanel.h"

GeneralSettingsPanel::GeneralSettingsPanel(SvkPluginState* pluginStateIn)
    : SvkSettingsPanel("GeneralSettingsPanel", pluginStateIn, { "File Directories" },
        {
            IDs::presetDirectory,
            IDs::modeDirectory,
            IDs::settingsDirectory
        },
        {
            SvkControlProperties(ControlTypeNames::DirectoryControl, "Preset Directory", true),
            SvkControlProperties(ControlTypeNames::DirectoryControl, "Mode Directory", true),
            SvkControlProperties(ControlTypeNames::DirectoryControl, "Settings Directory", true)
        },
        FlexBox(), {}
    )
{
    presetLabel = static_cast<LabelledComponent*>(idToControl[IDs::presetDirectory]);
    presetLabel->setComponentSize(320, 24);

    presetDirectoryBrowser = LabelledComponent::getComponentPointer<DirectoryBrowserComponent>(presetLabel);
    presetDirectoryBrowser->setText(pluginState->getPluginSettings()->getPresetPath());
    presetDirectoryBrowser->addListener(this);

    modeLabel = static_cast<LabelledComponent*>(idToControl[IDs::modeDirectory]);
    modeLabel->setComponentSize(320, 24);

    modeDirectoryBrowser = LabelledComponent::getComponentPointer<DirectoryBrowserComponent>(modeLabel);
    modeDirectoryBrowser->setText(pluginState->getPluginSettings()->getModePath());
    modeDirectoryBrowser->addListener(this);

    settingsLabel = static_cast<LabelledComponent*>(idToControl[IDs::settingsDirectory]);
    settingsLabel->setComponentSize(320, 24);

    settingsDirectoryBrowser = LabelledComponent::getComponentPointer<DirectoryBrowserComponent>(settingsLabel);
    settingsDirectoryBrowser->setText(pluginState->getPluginSettings()->getSettingsPath());
    settingsDirectoryBrowser->addListener(this);

    setSize(100, 100);
}

GeneralSettingsPanel::~GeneralSettingsPanel()
{

}

void GeneralSettingsPanel::directoryChanged(DirectoryBrowserComponent* browser, File directorySelected)
{
    if (browser == presetLabel->get())
    {
        pluginState->getPluginSettings()->setPresetDirectory(directorySelected);
    }

    else if (browser == modeLabel->get())
    {
        pluginState->getPluginSettings()->setModeDirectory(directorySelected);
    }

    else if (browser == settingsLabel->get())
    {
        pluginState->getPluginSettings()->setSettingsDirectory(directorySelected);
    }
}


text:
/*
  ==============================================================================

    KeyboardSettingsPanel.h
    Created: 13 Nov 2019 7:09:31pm
    Author:  Vincenzo Sicurella

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

#pragma once

#include "SvkSettingsPanel.h"

class KeyboardSettingsPanel : public SvkSettingsPanel
{

public:

    KeyboardSettingsPanel(SvkPluginState*);

    void comboBoxChanged(ComboBox*) override;
    void sliderValueChanged(Slider*) override;
    void buttonClicked(Button*) override;

    void setKeyboardPointer(VirtualKeyboard::Keyboard*) override;

private:

    LabelledComponent* layoutLabel;
    LabelledComponent* highlightLabel;
    LabelledComponent* ratioLabel;

    ComboBox* keyLayoutBox;
    ComboBox* keyHighlightBox;
    Slider* keyRatioSlider;

    TextButton* showNoteNumbers;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KeyboardSettingsPanel)
};


text:
/*
  ==============================================================================

    ControlSettingsPanel.cpp
    Created: 13 Nov 2019 7:11:08pm
    Author:  Vincenzo Sicurella

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

#include "ControlSettingsPanel.h"

ControlSettingsPanel::ControlSettingsPanel(AudioProcessorValueTreeState& processorTreeIn)
    : SvkUiPanel(processorTreeIn)
{

}

ControlSettingsPanel::~ControlSettingsPanel()
{
}

void ControlSettingsPanel::connectToProcessor()
{

}

void ControlSettingsPanel::paint(Graphics& g)
{

}

void ControlSettingsPanel::resized()
{

}


text:
/*
  ==============================================================================

    DebugSettingsPanel.h
    Created: 14 Nov 2019 11:46:31pm
    Author:  Vincenzo Sicurella

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

#pragma once
#include "SvkSettingsPanel.h"

class DebugSettingsPanel : public SvkSettingsPanel, public Timer
{

public:

    DebugSettingsPanel(SvkPluginState*);

    ~DebugSettingsPanel();

    //void paint(Graphics& g) override;
    //void resized() override;

    void timerCallback() override;

private:

    SvkPluginState* pluginState;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DebugSettingsPanel)
};


text:
/*
  ==============================================================================

    DebugSettingsPanel.cpp
    Created: 14 Nov 2019 11:46:31pm
    Author:  Vincenzo Sicurella

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

#include "DebugSettingsPanel.h"

DebugSettingsPanel::DebugSettingsPanel(SvkPluginState* pluginStateIn)
    : SvkSettingsPanel("DebugSettingsPanel", pluginState, { "" }, {}, {}),
      pluginState(pluginStateIn)
{
    setSize(100, 100);
}

DebugSettingsPanel::~DebugSettingsPanel()
{
}
//
//void DebugSettingsPanel::connectToProcessor()
//{
//    Label* lbl;
//    Component* c;
//    Slider* s;
//    String paramName;
//    RangedAudioParameter* param;
//    AudioParameterInt* api;
//
//    for (int i = 0; i < paramIDs->size(); i++)
//    {
//        paramName = paramIDs->getUnchecked(i);
//        param = processorTree.getParameter(paramName);
//
//        lbl = labels.add(new Label("Lbl" + paramName, param->name));
//        lbl->setColour(Label::textColourId, Colours::black);
//        addAndMakeVisible(lbl);
//
//        const NormalisableRange<float>& range = param->getNormalisableRange();
//        s = new Slider("Sld" + paramName);
//        c = controls.add(s);
//
//        s->setSliderStyle(Slider::LinearHorizontal);
//
//        api = dynamic_cast<AudioParameterInt*>(param);
//        if (api)
//        {
//            s->setNormalisableRange(NormalisableRange<double>(range.start, range.end, 1));
//            s->setSliderStyle(Slider::IncDecButtons);
//            s->setValue(api->get(), dontSendNotification);
//        }
//        else
//        {
//            s->setNormalisableRange(NormalisableRange<double>(range.start, range.end, 0.01f));
//            s->setValue(param->getValue(), dontSendNotification);
//        }
//
//        s->setScrollWheelEnabled(false);
//        s->setTextBoxStyle (Slider::TextBoxLeft, false, 60, 20);
//        s->setColour(Slider::textBoxTextColourId, Colours::black);
//        s->addListener (this);
//
//        sliderAttachments.add(new SliderAttachment(processorTree, paramName, *s));
//        addAndMakeVisible(s);
//    }
//}
//
//void DebugSettingsPanel::paint(Graphics& g)
//{
//    g.setColour(Colours::palegreen);
//    g.fillAll();
//
//    if (controlIdentifiers.size() == 0)
//    {
//        g.setColour(Colours::black);
//        g.drawText("No parameters", getWidth()/2, getHeight()/2,
//                   getWidth(), getHeight(), Justification::centred);
//    }
//}

//
//void DebugSettingsPanel::sliderValueChanged(Slider *slider)
//{
//    String paramName = slider->getName().substring(3);
//    RangedAudioParameter* param = processorTree.getParameter(paramName);
//    AudioParameterInt* api;
//
//    if (paramName == IDs::pianoWHRatio.toString())
//    {
//        //param->setValue(slider->getValue());
//        //pluginState->getKeyboard()->setKeySizeRatio(param->getValue());
//    }
//
//    else if (paramName == IDs::keyboardNumRows.toString())
//    {
//        //api = dynamic_cast<AudioParameterInt*>(param);
//        //*api = slider->getValue();
//        //pluginState->getKeyboard()->setNumRows(api->get());
//    }
//
//}
//

void DebugSettingsPanel::timerCallback()
{

}


text:
/*
  ==============================================================================

    MidiSettingsPanel.h
    Created: 13 Nov 2019 7:09:59pm
    Author:  Vincenzo Sicurella

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

#pragma once

#include "SvkSettingsPanel.h"

class MidiSettingsPanel : public SvkSettingsPanel, public Timer
{

public:

    MidiSettingsPanel(SvkPluginState*);
    ~MidiSettingsPanel();

    void visibilityChanged() override;

    void sliderValueChanged(Slider* sliderThatChanged) override;

    void comboBoxChanged(ComboBox* comboBoxThatChanged) override;

    void timerCallback() override;

    void refreshDevices();

private:

    HashMap<Identifier, LabelledComponent*, IDasStringHash> idToLabelledControl;

    LabelledComponent* inputBoxLabelled;
    LabelledComponent* outputBoxLabelled;
    ComboBox* inputBox;
    ComboBox* outputBox;

    Slider* periodShiftSlider;
    Slider* transposeSlider;
    Slider* midiChannelSlider;

    Array<MidiDeviceInfo> availableIns;
    Array<MidiDeviceInfo> availableOuts;

    SvkMidiProcessor* midiProcessor;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiSettingsPanel)
};


text:
/*
  ==============================================================================

    ControlSettingsPanel.h
    Created: 13 Nov 2019 7:11:08pm
    Author:  Vincenzo Sicurella

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

#pragma once

#include "../../../PluginState.h"

class ControlSettingsPanel : public Component
{

public:

    ControlSettingsPanel(AudioProcessorValueTreeState& processorTreeIn);

    ~ControlSettingsPanel();

    //void connectToProcessor() override;

    void paint(Graphics& g) override;
    void resized() override;

private:

    SvkPluginState* pluginState;

    OwnedArray<ButtonAttachment> buttonAttachments;
    OwnedArray<ComboBoxAttachment> comboBoxAttachments;
    OwnedArray<SliderAttachment> sliderAttachments;

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ControlSettingsPanel)
};


text:
/*
  ==============================================================================

    SvkSettingsPanel.h
    Created: 5 Sep 2020 4:08:42pm
    Author:  Vincenzo

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

#pragma once
#include "../../../PluginState.h"
#include "../DirectoryBrowserComponent.h"
#include "../LabelledComponent.h"
#include "../VirtualKeyboard/KeyboardComponent.h"

#define defaultSectionFlexBox FlexBox(FlexBox::Direction::column, FlexBox::Wrap::wrap, FlexBox::AlignContent::stretch, FlexBox::AlignItems::flexStart, FlexBox::JustifyContent::spaceAround)
#define defaultSectionFlexItem FlexItem(250, 24).withFlex(1.0f).withMargin(FlexItem::Margin(5, 0, 5, 5))
#define defaultSectionAsFlexItem FlexItem(100, 100).withFlex(1.0f);

class SvkSettingsPanel : public Component,
    protected Slider::Listener,
    protected Button::Listener,
    protected ComboBox::Listener,
    protected DirectoryBrowserComponent::Listener
{

public:

    enum ControlTypeNames
    {
        SliderControl = 0,
        ToggleControl,
        MenuControl,
        DirectoryControl,
        GenericControl
    };

    struct SvkControlProperties
    {
        int controlType;
        String controlName;
        bool controlLabelled;
        int sectionNum;
        var defaultValue;
        var minValue;
        var maxValue;
        var increment;

        SvkControlProperties(
            int typeIn = 1, String nameIn = "", bool labelled = false, int sectionNumIn = 0,
            var defaultIn = var(), var minIn = 0, var maxIn = 1, var incrementIn = 1
        ) :
            controlType(typeIn), controlName(nameIn), controlLabelled(labelled), sectionNum(sectionNumIn),
            defaultValue(defaultIn), minValue(minIn), maxValue(maxIn), increment(incrementIn)
        {};
    };

public:

    SvkSettingsPanel(
        String                       panelName,
        SvkPluginState*              pluginStateIn,
        StringArray                  sectionNamesIn,
        Array<Identifier>            controlIdsIn,
        Array<SvkControlProperties>  controlTypesIn,
        FlexBox                      flexParentStyle = FlexBox(),
        Array<FlexBox>               sectionBoxStyle = Array<FlexBox>(),
        Array<FlexItem>              sectionItemsStyle = Array<FlexItem>()
    ) :
        pluginState(pluginStateIn),
        sectionNames(sectionNamesIn),
        controlIdentifiers(controlIdsIn),
        controlTypes(controlTypesIn),
        flexParent(flexParentStyle)
    {
        setName(panelName);
        numSections = sectionNames.size();

        flexParent.items.resize(numSections);
        FlexItem sectionDefault = defaultSectionAsFlexItem;
        flexParent.items.fill(sectionDefault);

        // Combine given and default FlexBox styles
        flexSections.addArray(sectionBoxStyle);
        while (flexSections.size() < numSections)
            flexSections.add(defaultSectionFlexBox);

        // Combine given and default FlexItem styles
        sectionsDefaultFlexItems.addArray(sectionItemsStyle);
        while (sectionsDefaultFlexItems.size() < numSections)
            sectionsDefaultFlexItems.add(defaultSectionFlexItem);

        setupSections();
        setupControls();
    }

    virtual ~SvkSettingsPanel()
    {

    }

    virtual void paint(Graphics& g) override
    {
        //g.fillAll(Colours::darkgrey);
        //g.setColour(Colours::blueviolet);

        //float x = 0;
        //for (int i = 0; i < flexParent.items.size(); i++)
        //{
        //    FlexItem& item = flexParent.items.getReference(i);
        //    Rectangle<float> rect = { x, 0, item.width, item.height };
        //    x += item.width;

        //    g.fillRoundedRectangle(rect.reduced(3, 3), 3.0f);
        //}

        g.setColour(Colour(0xff323e44).darker());

        for (auto label : sectionHeaderLabels)
        {
            g.fillRoundedRectangle(label->getBounds().reduced(2.0f, 0).toFloat(), 5.0f);
        }
    }

    void resized() override
    {
        flexParent.performLayout(getLocalBounds());
    }

    virtual void setKeyboardPointer(VirtualKeyboard::Keyboard* keyboardPointer)
    {
        virtualKeyboard = keyboardPointer;
    }

    virtual void refreshPanel() {};

    //=============================================================================================================

    virtual void sliderValueChanged(Slider*) override {};

    virtual void buttonClicked(Button*) override {};

    virtual void comboBoxChanged(ComboBox*) override {};

    virtual void directoryChanged(DirectoryBrowserComponent*, File) override {};

protected:

    FlexBox* getSectionFlexBox(int sectionNum)
    {
        return flexParent.items.getReference(jmin(sectionNum, numSections - 1)).associatedFlexBox;
    }

    FlexItem* getControlFlexItem(int controlNum)
    {
        FlexBox* sectionBox = getSectionFlexBox(controlTypes[controlNum].sectionNum);
        for (int i = 0; i < sectionBox->items.size(); i++)
        {
            FlexItem* item = &sectionBox->items.getReference(i);
            if (item->associatedComponent == controls[controlNum])
                return item;
        }

        return nullptr;
    }

    bool setControlFlexItem(int controlNum, FlexItem flexItemIn)
    {
        FlexBox* sectionBox = getSectionFlexBox(controlTypes[controlNum].sectionNum);
        for (int i = 0; i < sectionBox->items.size(); i++)
        {
            if (sectionBox->items.getReference(i).associatedComponent == controls[controlNum])
            {
                sectionBox->items.set(i, flexItemIn);
                return true;
            }
        }
        return false;
    }

private:

    void setupSections()
    {
        // Associate section flexboxes with parent FlexItems and add section headers
        for (int i = 0; i < numSections; i++)
        {
            FlexBox& section = flexSections.getReference(i);
            flexParent.items.getReference(i).associatedFlexBox = &section;

            String header = sectionNames[i];
            if (header.length() > 0)
            {
                Label* l = sectionHeaderLabels.add(new Label(getName() + "_Header" + String(i), header));

                addAndMakeVisible(l);
                section.items.add(FlexItem(225, 24, *l));
            }
        }
    }

    void setupControls()
    {
        int numControls = jmin(controlIdentifiers.size(), controlTypes.size());

        for (int i = 0; i < numControls; i++)
        {
            Identifier id = controlIdentifiers[i];
            SvkControlProperties properties = controlTypes[i];

            Slider* slider = nullptr;
            Component* control = nullptr;

            switch (properties.controlType)
            {
            case ControlTypeNames::SliderControl:
                slider = new Slider(properties.controlName);
                slider->setRange(properties.minValue, properties.maxValue, properties.increment);
                slider->setValue(properties.defaultValue, dontSendNotification);
                control = slider;
                break;

            case ControlTypeNames::ToggleControl:
                control = new ToggleButton(properties.controlName);
                break;

            case ControlTypeNames::MenuControl:
                control = new ComboBox(properties.controlName);
                break;

            case ControlTypeNames::DirectoryControl:
                control = new DirectoryBrowserComponent(properties.controlName);
                break;

            default:
                control = new Component(properties.controlName);
            }

            if (control)
            {
                if (properties.controlLabelled)
                {
                    control = new LabelledComponent(*control, properties.controlName + ":");
                }

                controls.add(control);

                int sectionNum = jmin(properties.sectionNum, numSections - 1);
                FlexBox& section = flexSections.getReference(sectionNum);
                FlexItem item = sectionsDefaultFlexItems[sectionNum];
                item.associatedComponent = control;

                section.items.add(item);
                addAndMakeVisible(control);
                idToControl.set(id, control);
            }
        }
    }

protected:

    SvkPluginState* pluginState;

    StringArray sectionNames;
    int numSections;

    Array<Identifier> controlIdentifiers;
    Array<SvkControlProperties> controlTypes;

    OwnedArray<Label> sectionHeaderLabels;
    Array<FlexBox> flexSections;
    FlexBox flexParent;

    Array<FlexItem> sectionsDefaultFlexItems;

    OwnedArray<Component> controls;
    HashMap<Component*, Label*> controlLabels;
    HashMap<Identifier, Component*, IDasStringHash> idToControl;

    int controlMinWidth = 250;
    int controlMinHeight = 24;

    VirtualKeyboard::Keyboard* virtualKeyboard = nullptr;
};


text:
/*
  ==============================================================================

    ColourSettingsPanel.h
    Created: 13 Sep 2020 2:41:09pm
    Author:  Vincenzo

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

#pragma once

#include "SvkSettingsPanel.h"
#include "../ColourLibraryComponent.h"

class FocussedColourSelector : public FocussedComponent, public ColourSelector, private ChangeListener
{
public:

    FocussedColourSelector(Component* componentToFocusOn = nullptr)
        : FocussedComponent(componentToFocusOn), ColourSelector(
            ColourSelector::ColourSelectorOptions::showColourAtTop
            + ColourSelector::ColourSelectorOptions::editableColour
            + ColourSelector::ColourSelectorOptions::showColourspace
            , 4, 0)
    {
        setName("FocussedColourSelector");

        // Called when colour changes
        addChangeListener(this);
    };

    ~FocussedColourSelector() {};

    void setComponentToFocusOn(Component* componentPtr) override
    {
        FocussedComponent::setComponentToFocusOn(componentPtr);

        PaintSwatch* swatch = dynamic_cast<PaintSwatch*>(componentPtr);
        if (swatch)
            setCurrentColour(swatch->getCurrentColour());
    }

private:

    void changeListenerCallback(ChangeBroadcaster* source) override
    {
        FocusableComponent* focussed = dynamic_cast<FocusableComponent*>(currentFocus);

        if (focussed)
            focussed->performFocusFunction(getCurrentColour().toString());

        focusserCallback(currentFocus, getCurrentColour().toString());
    }
};

class ColourSettingsPanel : public SvkSettingsPanel
{
public:

    ColourSettingsPanel(SvkPluginState*);

    ~ColourSettingsPanel() override;

    void resized() override;

    void buttonClicked(Button*) override;

    void modifierKeysChanged(const ModifierKeys&) override;

    void mouseMove(const MouseEvent& event) override;

    void mouseDown(const MouseEvent& event) override;

    void mouseUp(const MouseEvent& event) override;

    void setKeyboardPointer(VirtualKeyboard::Keyboard* keyboardPointer) override;

    void refreshPanel() override;

    ColourSelector* getColourSelector();

private:

    void checkModifiers(const ModifierKeys& keys);

private:

    FocussedColourSelector* colourSelector;

    ColourLibraryComponent* keyColourLibrary;
    ColourLibraryComponent* noteOnColourLibrary;

    const int paintTypeRadioGroup = 100;

    TextButton* degreePaintButton;
    TextButton* layerPaintButton;
    TextButton* keyPaintButton;

    TextButton* resetColourToggle;
    bool userClickedReset = false;

    bool ctrlHeld = false;
    bool shiftHeld = false;
    bool altOptionHeld = false;

};


text:
/*
  ==============================================================================

    ModeSettingsPanel.cpp
    Created: 26 Sep 2020 1:59:11pm
    Author:  Vincenzo

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

#include "ModeSettingsPanel.h"


text:
/*
  ==============================================================================

    KeyboardController.cpp
    Created: 1 Sep 2019 8:22:03pm
    Author:  Vincenzo

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

#include "KeyboardController.h"


text:
/*
  ==============================================================================

    KeyboardController.h
    Created: 1 Sep 2019 8:22:03pm
    Author:  Vincenzo

    Will be used for higher level keyboard controlling.

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

#pragma once

#include "../../../../JuceLibraryCode/JuceHeader.h"
#include "KeyboardComponent.h"
#include "../../../PluginIDs.h"

namespace VirtualKeyboard
{

    class SvkController : public MidiKeyboardStateListener
    {
    public:

        SvkController(Keyboard* keyboardIn);
        ~SvkController();

        ValueTree getNode();
        void restoreNode(ValueTree nodeIn);

        Keyboard* getKeyboard();

        void setInputSource(MidiKeyboardState* sourceIn);
        void setInputFilteredSource(MidiKeyboardState* sourceIn);

        // Keyboard Actions
        void triggerNoteOn(int midiChannel, int midiNote, float velocity);
        void triggerNoteOff(int midiChannel, int midiNote);
        void allNotesOff();

        // Interfacing
        void setMidiChannelOut(int midiChannelOutIn);
        void setNoteColor(int midiNoteIn, Colour colorIn);
        void setDegreeColor(int degreeIn, Colour colorIn);
        void setChannelTriggeredColor(int midiChannelIn, Colour colorIn);

        void handleNoteOn(MidiKeyboardState* source, int midiChannel, int midiNote, float velocity) override;
        void handleNoteOff(MidiKeyboardState* source, int midiChannel, int midiNote, float velocity) override;

    private:

        Keyboard* keyboard;

        MidiKeyboardState* input;
        MidiKeyboardState* inputFiltered;

        Array<Key> keyProperties;
        ValueTree keyNodes;

        bool showNoteNumbers = false;
        bool showNoteNames = false;
        bool showInput = true;
        bool showInputFiltered = false;

        Array<Colour> colorsDegrees;

        Array<Point<int>> offsetsByOrder;
        Array<Point<int>> offsetsByDegree;

        Array<Point<float>> ratiosByOrder;
        Array<Point<float>> ratiosByDegree;
    };
};


text:
/*
  ==============================================================================

    KeyboardViewPort.h
    Created: 31 Oct 2019 2:41:37pm
    Author:  Vincenzo Sicurella

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

#pragma once

#include "../../../../JuceLibraryCode/JuceHeader.h"
#include "KeyboardComponent.h"

using namespace VirtualKeyboard;

class KeyboardViewport : public Viewport,
    private Button::Listener
{
public:

    KeyboardViewport(VirtualKeyboard::Keyboard* keyboardIn, const String& nameIn = String(), int scrollingModeIn = 1, int scrollingStyleIn = 1);

    void viewedComponentChanged(Component*) override;

    void visibleAreaChanged(const Rectangle<int>&) override;

    int getStepSmall();
    int getStepLarge();

    int getButtonWidth();
    bool isShowingButtons();

    float getCenterKeyProportion() const;

    void setButtonWidth(int widthIn);

    void setScrollingMode(int modeIdIn);
    void setScrollingStyle(int styleIdIn);

    void stepSmallForward();
    void stepSmallBackward();
    void stepLargeForward();
    void stepLargeBackward();

    void centerOnKey(int keyNumberIn);
    void centerOnKey(float keyNumberProportionIn);

    void resizeKeyboard();

    void resized() override;

    void buttonClicked(Button* button) override;

    enum ScrollingMode
    {
        NoScrolling = 0,
        Smooth,
        Stepped,
        SmoothWithButtons
    };

    enum ScrollingStyle
    {
        Hidden = 0,
        Bar,
        KeyboardView,
        Dots
    };

private:

    // returns altitude
    int drawAngleBracket(Graphics& g, bool isRightPointing, int sideLength, int xCenter, int yCenter, float thickness = 1.0f);

    void redrawButtons(int heightIn);

private:

    int stepSmall = 0;
    int stepLarge = 0;

    int buttonWidth = 16;

    int scrollingModeSelected = 1;
    int scrollingStyleSelected = 1;

    std::unique_ptr<ImageButton> stepLeftSmall;
    std::unique_ptr<ImageButton> stepLeftLarge;
    std::unique_ptr<ImageButton> stepRightSmall;
    std::unique_ptr<ImageButton> stepRightLarge;

    Keyboard* keyboard;
};


text:
/*
 ==============================================================================

 ViewPianoComponent.h
 Created: 14 Mar 2019 4:50:31pm
 Author:  Vincenzo

 A keyboard where keys can be rearranged and other stylization options.

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

#pragma once

#include "../../../../JuceLibraryCode/JuceHeader.h"
#include "../../../CommonFunctions.h"
#include "../../../Constants.h"
#include "../../../PluginIDs.h"
#include "../../../Midi/MidiProcessor.h"
#include "../../../Structures/Mode.h"
#include "../../../Structures/MappingHelper.h"
#include "VirtualKeyboardKey.h"
#include "VirtualKeyboardGrid.h"
#include "VirtualKeyboardGraphics.h"

#include <iostream>

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

namespace VirtualKeyboard
{
    class Keyboard :
        public Component,
        public MidiKeyboardState,
        public MidiKeyboardStateListener
        // private Timer
    {

    public:
        //===============================================================================================

        Keyboard();
        Keyboard(ValueTree keyboardNodeIn, Mode* modeIn=nullptr, NoteMap* inputFilterMapIn = nullptr);
        ~Keyboard();

        //===============================================================================================

        void restoreNode(ValueTree keyboardNodeIn, bool reinitializeKeys = true, bool resetIfInvalid=false);

        void reset();

        void initializeKeys(int size=128);

        //===============================================================================================

        void paint(juce::Graphics& g) override;

        void paintOverChildren(juce::Graphics& g) override;

        void resized() override;

        //===============================================================================================

        void mouseMove(const MouseEvent& e) override;

        void mouseExit(const MouseEvent& e) override;

        void mouseDown(const MouseEvent& e) override;

        void mouseDrag(const MouseEvent& e) override;

        void mouseUp(const MouseEvent& e) override;

        //===============================================================================================

        bool keyStateChanged(bool isKeyDown) override;

        bool keyPressed(const KeyPress& key) override;

        void modifierKeysChanged(const ModifierKeys& modifiers) override;

        //===============================================================================================

        // void timerCallback() override;

        //===============================================================================================

        /*
            Returns Keyboard settings node
        */
        ValueTree getNode();

        /*
            Returns a struct with the given key's data.
        */
        Key* getKey(int keyNumIn);

        /*
            Returns a value tree with the given key's data.
        */
        ValueTree getKeyNode(int keyNumIn);

        /*
            Returns an array of pointers to the keys corresponding to the given order.
        */
        Array<Key*> getKeysByOrder(int orderIn);

        /*
        Returns an array of pointers to the keys corresponding to the given order.
        */
        Array<Key*> getKeysByScaleDegree(int degreeIn);

        /*
        Returns an array of pointers to the keys corresponding to the given order.
        */
        Array<Key*> getKeysByModalDegree(int degreeIn);

        /*
            Returns the number of the last key clicked by the user.
        */
        int getLastKeyClicked();

        /*
            Returns the area of the given key relative to the top left corner of the full keyboard.
        */
        Rectangle<int> getKeyArea(int midiNoteIn);

        /*
            Returns the area of the given key relative to the top left corner of the viewport.
        */
        Rectangle<int> getKeyAreaRelative(int midiNoteIn);

        /*
            Returns the key width in pixels.
        */
        int getKeyWidth();

        /*
            Returns the key height in pixels.
        */
        int getKeyHeight();

        /*
            Returns the piano width from given height
        */
        int getPianoWidth(int heightIn);

        /*
            Returns the current proportion of key width to height.
        */
        float getKeySizeRatio();

        /*
            Returns the current size of the keys within the given order.
        */
        Point<int> getKeyOrderSize(int orderIn);

        /*
            Returns the current size of the keys within the given scale degree.
        */
        Point<int> getKeyDegreeSize(int degreeIn);

        /*
            Returns the key which holds the given point relative to the top left corner of the component.
        */
        Key* getKeyFromPosition(Point<int> posIn);

        /*
            Returns the key which holds the given MouseEvent point relative to the top left corner of the component.
        */
        Key* getKeyFromPositionMouseEvent(const MouseEvent& e);

        /*
            Returns the velocity for a given key and point relative to the key's top left corner
        */
        float getKeyVelocity(Key* keyIn, Point<int> posIn);

        /*
            Returns the Midi Channel the keyboard is sending output on.
        */
        int getMidiChannelOut() const;

        /*
            Returns the current mode size
        */
        int getModeSize() const;

        //===============================================================================================

        /*
            Returns the UI mode of which the keyboard is in.
        */
        int getUIMode();

        /*
            Returns the orientation of which the keyboard is in.
        */
        int getOrientation();

        /*
            Returns the style of which the keys are nested.
        */
        int getKeyPlacementStyle();

        /*
            Returns whether or not note numbers are showing
        */
        bool isShowingNoteNumbers();

        /*
            Returns whether or not filtered note numbers are showing
        */
        bool isShowingFilteredNumbers();

        /*
            Returns whether or not note names are showing
        */

        bool isShowingNoteNames();

        /*
            Returns the style of which the keys get highlighted.
        */
        int getHighlightStyle();

        /*
            Returns the style of note velocity.
        */
        int getVelocityStyle();

        /*
            Returns the value of velocity if in fixed mode.
        */
        float getVelocityFixed();

        /*
            Returns whether or not Midi input is being scaled to the selected velocity style
        */
        bool isInputVelocityScaled();

        /*
            Returns the scrolling style selected
        */
        int getScrollingStyle();

        //===============================================================================================

        /*
            Rearranges the keys to fit the current mode, without changing other key data.
        */
        void applyMode(Mode* modeIn, bool resize = false);

        /*
            Applies the key data to the keyboard so that it matches the data passed in.
        */
        void applyKeyData(ValueTree keyDataTreeIn);

        /*
        Allows the keyboard to listen the filtered midi input
        */
        void displayKeyboardState(MidiKeyboardState* keyboardStateIn);

        //===============================================================================================

        /*
            Sets the keyboards UI mode.
        */
        void setUIMode(int uiModeIn);

        /*
            Sets the orientation of the keyboard.
        */
        void setOrientation(int orientationIn);

        /*
            Sets the amount of rows to split the keyboard into
         */
        void setNumRows(int numRowsIn);

        /*
            Sets the style of which the keys are nested in.
        */
        void setKeyStyle(int placementIn);

        /*
            Set the way the keys are highlighted
        */
        void setHighlightStyle(int styleIn);

        /*
        Sets the velocity behavior of the keyboard
        */
        void setVelocityBehavior(int behaviorNumIn, bool scaleInputVelocity = false);

        /*
        Sets the scrolling style of the keyboard.
        */
        void setScrollingStyle(int scrollingStyleIn);

        /*
            Set the Midi Channel that the keyboard outputs on clicks.
        */
        void setMidiChannelOut(int midiChannelOutIn);

        /*
            Set the fixed output velocity
        */
        void setVelocityFixed(float velocityIn);

        /*
            Sets whether or not midi input velocity should be scaled
        */
        void setInputVelocityScaled(bool shouldBeScaled);

        /*
            Set whether note numbers are showing
        */
        void setShowNoteNumbers(bool shouldShowNumbers);

        /*
            Set whether filtered note numbers are showing
        */
        void setShowFilteredNumbers(bool shouldShowNumbers);

        /*
            Set whether pitch names are shown
        */
        void setShowNoteLabels(bool shouldShowPitchNames);

        // might want to restructure these so this is not necessary
        void setLastKeyClicked(int keyNumIn);

        /*
            Set the Input NoteMap being used.
        */
        void setInputNoteMap(NoteMap& noteMapIn);

        //===============================================================================================

        /*
            Adds a reference to a mapping helper for manual mapping
        */
        void setMappingHelper(MappingHelper*);

        /*
            Hightlights the given key, or removes highlight if false is passed in.
        */
        void highlightKey(int keyNumberIn, bool highlightOn = true);

        //===============================================================================================

        /*
            Returns a pointer to the individual key colors
        */
        Array<Colour>* getKeyIndividualColours();

        /*
            Returns the color of the given key.
        */
        Colour getKeyColor(int keyNumIn);

        /*
            Returns a pointer to the key layer colors
        */
        Array<Colour>* getKeyLayerColours();

        /*
            Returns the current key layer of the given order.
        */
        Colour getKeyLayerColor(int orderIn);

        /*
            Returns a pointer to the key scale degree colors
        */
        Array<Colour>* getKeyDegreeColours();

        /*
            Returns the current key color of the given scale degree.
        */
        Colour getKeyDegreeColor(int degIn);

        /*
            Returns a pointer to the note on colors by midi channel
        */
        Array<Colour>* getKeyNoteOnColours();

        //===============================================================================================

        /*
            Sets the display color of all the keys
        */
        void updateKeyColors(bool writeToNode = true);

        /*
            Resets the color of all the keys in the given order to the current order color.
        */
        void resetLayerColors();

        /*
            Resets the color of all the keys of the scale degree to the current order color.
        */
        void resetDegreeColors();

        /*
            Reset all keys to the current colors of their orders.
        */
        void resetKeyColors();

        //===============================================================================================

        /*
            Sets the color of all keys of the given scale degree
        */
        void setKeyDegreeColor(int scaleDegreeIn, Colour colourIn);

        /*
            Sets the color of all keys of the given layer
        */
        void setKeyLayerColor(int layerNumIn, Colour colourIn);

        /*
            Sets the color of the given key
        */
        void setKeyColor(int keyNumIn, Colour colourIn);

        //===============================================================================================

        /*
            Sets the proportion of the keys' width to height. Default is 0.25.
        */
        void setKeySizeRatio(float keySizeRatioIn, bool resizeSelf = true);

        /*
            Sets the width of the keys to a given size and conforms the keyboard to the current proportions.
        */
        void setKeyWidthSize(int widthSizeIn);

        /*
            Sets a scaling factor for different orders of keys.
        */

        void setKeyOrderSizeScalar(float scalarIn);

        //===============================================================================================

        /*
            Turns all notes off with the selected midi channel
        */
        void allNotesOff();

        /*
            Turns all notes off except for the last one clicked.
        */
        void isolateLastNote();

        /*
            Will resend a Note On message for all notes currently on.
        */
        void retriggerNotes();

        /*
            Will trigger a midi message for the key number given
        */
        void triggerKey(int keyNumberIn, bool doNoteOn = true, float velocity = 1);

        /*
            Will trigger a midi messages for the key numbers given with the given velocity.
        */
        void triggerKeys(Array<int> keyNumbers, bool doNoteOn = true, float velocity = 1);

        /*
            Returns the order of which all held notes are a part of.
            Values less than 0 mean the notes are a part of different orders
        */
        int getOrderOfNotesOn();

        /*
            Transposes the given key (if on) to a certain amount of modal steps.
        */
        int transposeKeyModally(int keyNumIn, int stepsIn);

        /*
            Transposes the given key (if on) to a certain amount of scale degrees.
        */
        int transposeKeyChromatically(int keyNumIn, int degreesIn);

        /*
            Transposes the keys on to a certain amount of modal steps.
            If needsSameOrder is true, this will only work if all keys are in the same order.
            If it is false, then the keys in higher orders will be transposed the same amount of degrees
            of the modal steps of the lowest note if useLastClickedRoot is false, or the last clicked note
            if that is true.
        */
        void transposeKeysOnModally(int modalStepsIn, bool needsSameOrder=true, bool useLastClickedRoot=false);

        /*
            Transposes the keys on to a certain amount of scale degrees.
        */
        void transposeKeysOnChromatically(int modalStepsIn);

        //===============================================================================================

        void handleNoteOn(MidiKeyboardState* source, int midiChannel, int midiNote, float velocity) override;

        void handleNoteOff(MidiKeyboardState* source, int midiChannel, int midiNote, float velocity) override;

        //===============================================================================================

    private:

        // Functionality
        UndoManager* undo;
        KeyboardPositioner keyPositioner;
        MidiKeyboardState* externalKeyboardToDisplay = nullptr;
        NoteMap noteMapOnDisplay;
        bool hasDirtyKeys = true;

        MidiBuffer buffer;

        MappingHelper* manualMappingHelper;
        Array<Key*> highlightedKeys;

        //Array<Path> keyboardKeysPath;

        // Parameters
        int uiModeSelected = 0;
        int orientationSelected = 0;
        int keyPlacementSelected = 1;
        int highlightSelected = 1;
        int velocitySelected = 1;
        int scrollingSelected = 1;

        int numRows = 1;

        bool showNoteLabels = false;
        bool showNoteNumbers = false;
        bool showFilteredNoteNums = false;

        int midiChannelOut = 1;
        float velocityFixed = 1;
        bool scaleMidiInputVelocity = false;

        float keySizeRatio = 0.25f;

        // Data
        ValueTree pianoNode;
        ValueTree keyTreeNode;
        OwnedArray<Key> keys;
        Array<Colour> keyColorsOrders;
        Array<Colour> keyColorsDegrees;
        Array<Colour> keyColorsIndividual;
        Array<Colour> keyOnColorsByChannel;

        Mode* mode;
        Mode modeDefault = Mode(STD_TUNING_MODE_NODE, true);

        Array<Colour> colorsDefaultOrders = {
            Colours::white,
            Colours::black,
            Colours::crimson.withSaturation(1.0f),
            Colours::cornflowerblue.withSaturation(0.8f),
            Colours::mediumseagreen.withSaturation(0.9f),
            Colours::gold.withBrightness(0.75f),
            Colours::mediumpurple,
            Colours::orangered,
            Colours::saddlebrown
        };

        // Properties
        int keyWidth = 50;
        int keyHeight = 200;

        float layerKeysWidthRatio = 0.8f;
        float layerKeysHeightRatio = 0.55f;

        int lastKeyOver = 0;
        int lastKeyClicked = 0;

        int numOrder0Keys = 128;
        int pianoWidth;
        int minWindowHeight;

        int modeSize;

        Array<Array<int>> keysOrder;
        Array<Array<int>> keysScaleDegree;
        Array<Array<int>> keysModalDegree;
        Array<int> keysOn;
        Array<int> keysByMouseTouch;

        // Locks
        bool rightMouseHeld = false;
        bool shiftHeld = false;
        bool altHeld = false;
        bool ctrlHeld = false;
        bool upHeld = false;
        bool downHeld = false;
        bool leftHeld = false;
        bool rightHeld = false;
        bool spaceHeld = false;

        bool displayIsReady = false;

        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Keyboard)
    };
}


text:
/*
  ==============================================================================

    KeyboardViewPort.cpp
    Created: 31 Oct 2019 2:41:37pm
    Author:  Vincenzo Sicurella

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

#include "KeyboardViewport.h"

KeyboardViewport::KeyboardViewport(VirtualKeyboard::Keyboard* keyboardIn, const String& nameIn, int scrollingModeIn, int scrollingStyleIn)
: Viewport(nameIn)
{
    stepRightLarge.reset(new ImageButton());
    stepRightLarge->addListener(this);
    addChildComponent(stepRightLarge.get());

    stepRightSmall.reset(new ImageButton());
    stepRightSmall->addListener(this);
    addChildComponent(stepRightSmall.get());

    stepLeftLarge.reset(new ImageButton());
    stepLeftLarge->addListener(this);
    addChildComponent(stepLeftLarge.get());

    stepLeftSmall.reset(new ImageButton());
    stepLeftSmall->addListener(this);
    addChildComponent(stepLeftSmall.get());

    setScrollingMode(scrollingModeIn);
    setScrollingStyle(scrollingStyleIn);

    keyboard = keyboardIn;
    setViewedComponent(keyboard);
}

void KeyboardViewport::viewedComponentChanged(Component* newComponent)
{
    keyboard = dynamic_cast<Keyboard*>(newComponent);
    resizeKeyboard();
}

void KeyboardViewport::visibleAreaChanged(const Rectangle<int>&)
{
    if (keyboard->getHeight() > getMaximumVisibleHeight())
    {
        resizeKeyboard();
    }
}

int KeyboardViewport::getStepSmall()
{
    return stepSmall;
}

int KeyboardViewport::getStepLarge()
{
    return stepLarge;
}

int KeyboardViewport::getButtonWidth()
{
    if (isShowingButtons())
        return buttonWidth;

    return 0;
}

bool KeyboardViewport::isShowingButtons()
{
    return scrollingModeSelected > 1;
}

float KeyboardViewport::getCenterKeyProportion() const
{
    int center = getViewPositionX() + getMaximumVisibleWidth() / 2;
    Key* key = keyboard->getKeyFromPosition({ center, 0 });
    if (key)
        return key->keyNumber + (center % key->getWidth()) / (float)key->getWidth();

    return -1;
}

void KeyboardViewport::setButtonWidth(int widthIn)
{
    buttonWidth = widthIn;
}

void KeyboardViewport::setScrollingMode(int modeIdIn)
{
    scrollingModeSelected = modeIdIn;

    if (scrollingModeSelected > 1)
    {
        stepRightLarge->setVisible(true);
        stepRightSmall->setVisible(true);
        stepLeftLarge->setVisible(true);
        stepLeftSmall->setVisible(true);
    }
    else
    {
        stepRightLarge->setVisible(false);
        stepRightSmall->setVisible(false);
        stepLeftLarge->setVisible(false);
        stepLeftSmall->setVisible(false);
    }

}
void KeyboardViewport::setScrollingStyle(int styleIdIn)
{
    scrollingStyleSelected = styleIdIn;
    // do stuff
}

void KeyboardViewport::stepSmallForward()
{
    Viewport::setViewPosition(getViewPositionX() + stepSmall, getViewPositionY());
}

void KeyboardViewport::stepSmallBackward()
{
    Viewport::setViewPosition(getViewPositionX() - stepSmall, getViewPositionY());
}

void KeyboardViewport::stepLargeForward()
{
    Viewport::setViewPosition(getViewPositionX() + stepLarge, getViewPositionY());
}

void KeyboardViewport::stepLargeBackward()
{
    Viewport::setViewPosition(getViewPositionX() - stepLarge, getViewPositionY());
}

void KeyboardViewport::centerOnKey(int keyNumberIn)
{
    Key* key = keyboard->getKey(keyNumberIn);
    if (key)
    {
        int position = key->getBoundsInParent().getX() - getMaximumVisibleWidth() / 2;
        Viewport::setViewPosition(position, getViewPositionY());
    }
}

void KeyboardViewport::centerOnKey(float keyNumberProportionIn)
{
    Key* key = keyboard->getKey((int)keyNumberProportionIn);
    if (key)
    {
        int proportion = (keyNumberProportionIn - (int)keyNumberProportionIn) * key->getWidth();
        int position = key->getBoundsInParent().getX() + proportion - getMaximumVisibleWidth() / 2;
        Viewport::setViewPosition(position, getViewPositionY());
    }
}

int KeyboardViewport::drawAngleBracket(Graphics& g, bool isRightPointing, int sideLength, int xCenter, int yCenter, float thickness)
{
    float halfSide = sideLength / 2.0f;
    float altitude = sqrtf(powf(sideLength, 2.0f) - powf(halfSide, 2.0f));
    float halfAltitude = altitude / 2.0f;

    int x1 = xCenter - halfAltitude;
    int x2 = xCenter + halfAltitude;
    int y1 = yCenter + halfSide;
    int y2 = yCenter - halfSide;

    if (isRightPointing)
    {
        g.drawLine(Line<float>(x1, y1, x2, yCenter), thickness);
        g.drawLine(Line<float>(x1, y2, x2, yCenter), thickness);
    }
    else
    {
        g.drawLine(Line<float>(x2, y1, x1, yCenter), thickness);
        g.drawLine(Line<float>(x2, y2, x1, yCenter), thickness);
    }

    return altitude;
}

void KeyboardViewport::redrawButtons(int heightIn)
{
    Image singleBracketImage = Image(Image::PixelFormat::ARGB, buttonWidth, heightIn, false);
    Image doubleBracketImage = singleBracketImage.createCopy();
    Image singleBracketFlipped = singleBracketImage.createCopy();
    Image doubleBracketFlipped = singleBracketImage.createCopy();

    Colour buttonColour = Colours::steelblue.darker().withSaturation(0.33f);
    Colour bracketColour = Colours::black;
    Colour mouseOverColor = Colours::lightgrey.withAlpha(0.33f);
    Colour mouseClickColor = Colours::darkgrey.withAlpha(0.75f);

    int sideLength = round(buttonWidth * 0.33f);
    int altitude;
    int xCenter = buttonWidth / 2;
    int yCenter = getHeight() / 4;
    int doubleSeparation = 3;

    Graphics g1(singleBracketImage);
    g1.setColour(buttonColour);
    g1.fillAll();
    g1.setColour(bracketColour);
    altitude = drawAngleBracket(g1, true, sideLength, xCenter, yCenter, 1);

    Graphics g2(doubleBracketImage);
    g2.setColour(buttonColour);
    g2.fillAll();
    g2.setColour(bracketColour);
    drawAngleBracket(g2, true, sideLength, xCenter - doubleSeparation, yCenter, 1);
    drawAngleBracket(g2, true, sideLength, xCenter + doubleSeparation, yCenter, 1);

    Graphics g3(singleBracketFlipped);
    g3.setColour(buttonColour);
    g3.fillAll();
    g3.setColour(bracketColour);
    altitude = drawAngleBracket(g3, false, sideLength, xCenter, yCenter, 1);

    Graphics g4(doubleBracketFlipped);
    g4.setColour(buttonColour);
    g4.fillAll();
    g4.setColour(bracketColour);
    drawAngleBracket(g4, false, sideLength, xCenter - doubleSeparation, yCenter, 1);
    drawAngleBracket(g4, false, sideLength, xCenter + doubleSeparation, yCenter, 1);

    stepLeftSmall->setImages(false, false, false,
        singleBracketImage, 1.0f, Colours::transparentBlack,
        singleBracketImage, 1.0f, mouseOverColor,
        singleBracketImage, 1.0f, mouseClickColor);

    stepLeftLarge->setImages(false, false, false,
        doubleBracketImage, 1.0f, Colours::transparentBlack,
        doubleBracketImage, 1.0f, mouseOverColor,
        doubleBracketImage, 1.0f, mouseClickColor);

    stepRightSmall->setImages(false, false, false,
        singleBracketFlipped, 1.0f, Colours::transparentBlack,
        singleBracketFlipped, 1.0f, mouseOverColor,
        singleBracketFlipped, 1.0f, mouseClickColor);

    stepRightLarge->setImages(false, false, false,
        doubleBracketFlipped, 1.0f, Colours::transparentBlack,
        doubleBracketFlipped, 1.0f, mouseOverColor,
        doubleBracketFlipped, 1.0f, mouseClickColor);
}

void KeyboardViewport::resizeKeyboard()
{
    int heightWithScrollbar = getHeight() - getScrollBarThickness();
    keyboard->setBounds(0, 0, keyboard->getPianoWidth(heightWithScrollbar), heightWithScrollbar);
}

void KeyboardViewport::resized()
{
    Viewport::resized();

    stepSmall = keyboard->getWidth() / keyboard->getKeysByOrder(0).size();
    stepLarge = stepSmall * keyboard->getModeSize();

    // draw step buttons
    if (scrollingModeSelected > 1)
    {
        int halfHeight = round(getMaximumVisibleHeight() / 2.0f);
        redrawButtons(halfHeight);

        stepRightLarge->setBounds(0, 0, buttonWidth, halfHeight);
        stepRightSmall->setBounds(0, halfHeight, buttonWidth, halfHeight);
        stepLeftLarge->setBounds(getWidth() - buttonWidth, 0, buttonWidth, halfHeight);
        stepLeftSmall->setBounds(getWidth() - buttonWidth, halfHeight, buttonWidth, halfHeight);
    }
}

void KeyboardViewport::buttonClicked(Button* button)
{
    if (button == stepRightLarge.get())
    {
        stepLargeBackward();
    }
    else if (button == stepRightSmall.get())
    {
        stepSmallBackward();
    }
    else if (button == stepLeftLarge.get())
    {
        stepLargeForward();
    }
    else if (button == stepLeftSmall.get())
    {
        stepSmallForward();
    }
}


text:
/*
  ==============================================================================

    VirtualKeyboardGraphics.cpp
    Created: 25 Jul 2020 1:49:41am
    Author:  Vincenzo

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

#include "VirtualKeyboardGraphics.h"

VirtualKeyboard::KeyGraphics::KeyGraphics(const Mode& modeIn, int baseKeyHeightIn, int baseKeyWidthIn, float heightLayerMultIn, float widthLayerMultIn)
    :    mode(modeIn),
        baseKeyHeight(baseKeyHeightIn),
        baseKeyWidth(baseKeyWidthIn),
        heightLayerMult(heightLayerMultIn),
        widthLayerMult(widthLayerMultIn)
{

}

void VirtualKeyboard::KeyGraphics::setBaseKeyWidth(int keyWidthIn)
{
    baseKeyWidth = keyWidthIn;
}

void VirtualKeyboard::KeyGraphics::setBaseKeyHeight(int keyHeightIn)
{
    baseKeyHeight = keyHeightIn;
}

void VirtualKeyboard::KeyGraphics::setWidthMultiplier(float widthMultIn)
{
    widthLayerMult = widthMultIn;
}

void VirtualKeyboard::KeyGraphics::setHeightMultiplier(float heightMultIn)
{
    heightLayerMult = heightMultIn;
}

void VirtualKeyboard::KeyGraphics::setKeyPlacementStyle(VirtualKeyboard::KeyPlacementType placementTypeIn)
{
    keyPlacement = placementTypeIn;
}

Array<Path> VirtualKeyboard::KeyGraphics::getKeyboardOctave(int scaleDegreeOffset)
{
    Array<Path> octavePathOut;

    int scaleIndex = mode.getRootNote() + scaleDegreeOffset;
    for (auto step : mode.getSteps())
    {
        for (int i = 0; i < step; i++)
        {
            octavePathOut.add(getKey(scaleIndex++));
        }
    }

    return octavePathOut;
}

Array<Path> VirtualKeyboard::KeyGraphics::getFullKeyboard()
{
    Array<Path> keyboardPathOut;

    //Array<Path> keyboardPeriod = getKeyboardOctave();
    //Array<Path> keyboardBegin = getKeyboardOctave(-(mode.getRootNote() * 2));
    //Array<Path> keyboardEnd = getKeyboardOctave(128 - mode.getRootNote() - mode.getScaleSize());

    //keyboardPathOut.addArray(keyboardBegin, mode.getRootNote() & mode.getScaleSize());
    //
    //for (int period = 1; period < 128 / mode.getScaleSize(); period++)
    //{
    //    keyboardPathOut.addArray(keyboardPeriod);
    //}

    //int endStart = (128 - mode.getRootNote() % mode.getScaleSize());
    //keyboardPathOut.addArray(keyboardEnd, endStart, mode.getScaleSize() - endStart);

    for (int i = 0; i < 128; i++)
        keyboardPathOut.add(getKey(i));

    return keyboardPathOut;
}

Path VirtualKeyboard::KeyGraphics::getKey(int keyNumber)
{
    int order = mode.getOrders()[keyNumber];
    int step = mode.getStepsOfOrders()[keyNumber];

    int prevOrder = mode.getOrders()[keyNumber - 1];
    int prevStep = mode.getStepsOfOrders()[keyNumber - 1];

    int nextOrder = mode.getOrders()[keyNumber + 1];
    int nextStep = mode.getStepsOfOrders()[keyNumber + 1];

    // get key style
    int keyShape = 0;
    if (order == 0)
    {
        // has left notch
        if (prevStep > 1)
        {
            keyShape += VirtualKeyboard::NotchPrevious;
        }

        // has right notch
        if (nextStep > 1)
        {
            keyShape += VirtualKeyboard::NotchNext;
        }
    }
    // Not a top layer key
    else if (order < step - 1)
    {
        if (keyPlacement == VirtualKeyboard::nestedCenter)
            keyShape = VirtualKeyboard::nestedCenter;
        else
            keyShape += VirtualKeyboard::NotchNext;
    }
    else
    {
        // Key is a rectangle
    }

    float keyWidth = baseKeyWidth - (order * baseKeyWidth * widthLayerMult);
    float keyHeight = baseKeyHeight - (order * baseKeyHeight * heightLayerMult);

    Array<Point<float>> keyPathPoints;
    keyPathPoints.add({ 0, keyHeight });
    keyPathPoints.add({ keyWidth, keyHeight });

    if (keyShape & VirtualKeyboard::NotchPrevious != 0)
    {
        float notchWidth = (baseKeyWidth - (prevOrder * baseKeyWidth * widthLayerMult)) / 2.0f;
        float prevKeyHeight = baseKeyHeight - (prevOrder * baseKeyHeight * heightLayerMult);

        keyPathPoints.insert(0, { 0, prevKeyHeight });
        keyPathPoints.insert(0, { notchWidth, prevKeyHeight });
        keyPathPoints.insert(0, { notchWidth, 0 });
    }
    else
    {
        keyPathPoints.insert(0, { 0, 0 });
    }

    if (keyShape & VirtualKeyboard::NotchNext != 0)
    {
        float notchX = keyWidth - (baseKeyWidth - (nextOrder * baseKeyWidth * widthLayerMult)) / 2.0f;
        float nextKeyHeight = baseKeyHeight - (nextOrder * baseKeyHeight * heightLayerMult);

        keyPathPoints.add({ keyWidth, nextKeyHeight });
        keyPathPoints.add({ notchX, nextKeyHeight });
        keyPathPoints.add({ notchX, 0 });
    }
    else
    {
        keyPathPoints.add({ keyWidth, 0 });
    }

    if (keyShape == VirtualKeyboard::NotchCenter)
    {
        float nextKeyWidth = (baseKeyWidth - (nextOrder * baseKeyWidth * widthLayerMult)) / 2.0f;
        float notchX0 = (keyWidth - nextKeyWidth) / 2;
        float notchX1 = keyWidth - notchX0;
        float nextKeyHeight = baseKeyHeight - (nextOrder * baseKeyHeight * heightLayerMult);

        keyPathPoints.insert(0, { 0, 0 });
        keyPathPoints.insert(0, { notchX0, 0 });
        keyPathPoints.insert(0, { notchX0, nextKeyHeight });
        keyPathPoints.add({ keyWidth, 0 });
        keyPathPoints.add({ notchX1, 0 });
        keyPathPoints.add({ notchX1, nextKeyHeight });
    }

    // Build Path
    Path keyPath;
    keyPath.startNewSubPath(keyPathPoints.removeAndReturn(0));
    for (auto point : keyPathPoints)
    {
        keyPath.lineTo(point);
    }

    keyPath.closeSubPath();

    return keyPath;
}


text:
/*
  ==============================================================================

    VirtualKeyboardKey.cpp
    Created: 17 Apr 2019 2:11:53pm
    Author:  Vincenzo

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

#include "VirtualKeyboardKey.h"

using namespace VirtualKeyboard;

Key::Key()
{
    setName("Key");
    node = ValueTree(IDs::pianoKeyNode);
    setDisplayColor(Colours::transparentBlack);
}

Key::Key(int keyNumIn)
: Key()
{
    keyNumber = keyNumIn;
}

Key::Key(int keyNumIn, int orderIn, int scaleDegreeIn, int modeDegreeIn, int stepIn,
    String pitchNameIn, int widthModIn, int heightModIn, int xOff, int yOff,
    bool showNoteNumIn, bool showNoteNameIn, Colour colorIn)
: Key()
{
    keyNumber = keyNumIn;
    order = orderIn;
    scaleDegree = scaleDegreeIn;
    modeDegree = modeDegreeIn;
    step = stepIn;
    pitchName = pitchNameIn;
    widthMod = widthModIn;
    heightMod = heightModIn;
    xOffset = xOff;
    yOffset = yOff;
    showNoteNumber = showNoteNumIn;
    showNoteLabel = showNoteNameIn;
    color = colorIn;
}

Key::Key(ValueTree keyNodeIn)
    : Key()
{
    if (keyNodeIn.hasType(IDs::pianoKeyNode))
    {
        applyParameters(keyNodeIn);
        node = keyNodeIn;
    }
}

Key::Key(const Key& keyToCopy)
: Key(keyToCopy.keyNumber, keyToCopy.order, keyToCopy.scaleDegree, keyToCopy.modeDegree, keyToCopy.step,
      keyToCopy.pitchName, keyToCopy.widthMod, keyToCopy.heightMod, keyToCopy.xOffset, keyToCopy.yOffset,
      keyToCopy.showNoteNumber, keyToCopy.showNoteLabel, keyToCopy.color)
{
    node = keyToCopy.node.createCopy();
}

void Key::setPath(Path keyPathIn)
{
    keyPath = keyPathIn;
}

void Key::applyParameters(ValueTree nodeIn)
{
    if (nodeIn.hasType(IDs::pianoKeyNode))
    {
        Identifier id;
        for (int i = 0; i < nodeIn.getNumProperties(); i++)
        {
            id = nodeIn.getPropertyName(i);

            if (id == IDs::pianoKeyNumber)
                keyNumber = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyWidthMod)
                widthMod = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyHeightMod)
                heightMod = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyXOffset)
                xOffset = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyYOffset)
                yOffset = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyShowNumber)
                showNoteNumber = nodeIn.getProperty(id);
            else if (id == IDs::keyboardShowsNoteLabels)
                showNoteLabel = nodeIn.getProperty(id);
            else if (id == IDs::pianoKeyColorsNode)
                color = Colour::fromString(nodeIn.getProperty(id).toString());
        }

        node.copyPropertiesAndChildrenFrom(nodeIn, nullptr);
    }
}

Colour Key::getDisplayColor() const
{
    return color;
}

void Key::paint(Graphics& g)
{
    g.fillAll(color);

    Colour colorToUse = color;

    if (isClicked || isMouseOver() || exPressed)
    {
        float contrast = 0;
        if (isClicked)
            contrast = 0.7f;
        else if (isMouseOver())
            contrast = 0.25f;

        if (exPressed)
            colorToUse = exInputColor;
        else
            colorToUse = color.contrasting(contrast);

        if (colorToUse.getPerceivedBrightness() < 0.6667f)
            g.setColour(colorToUse.contrasting(0.333f));

        g.setColour(colorToUse);

        // Full or Inside
        if (highlightStyleId < 3)
        {
            Rectangle<int> bounds = getLocalBounds();

            if (highlightStyleId == 2)
            {
                int margin = sqrt(getWidth() * getHeight()) * 0.07;
                bounds.reduce(margin, margin);
            }

            g.fillRect(bounds);
        }

        // Border
        else if (highlightStyleId == 3)
        {
            float margin = sqrt(getWidth() * getHeight()) * 0.07f;
            g.drawRect(getLocalBounds(), margin);
        }

        else
        {
            float margin = sqrt(getWidth() * getHeight()) * 0.05f;
            float boundsWidth = getWidth() - margin * 2;
            Rectangle<float> shapeBounds = { margin, getHeight() - margin - boundsWidth, boundsWidth, boundsWidth };

            // Circle
            if (highlightStyleId == 4)
                g.fillEllipse(shapeBounds);

            // Square
            else
                g.fillRect(shapeBounds);
        }
    }

    if (order == 0)
    {
        if (colorToUse.getPerceivedBrightness() < 0.6667f)
            g.setColour(colorToUse.contrasting(0.333f));

        else
            g.setColour(Colours::black);

        Rectangle<int> outline = getLocalBounds();
        g.drawRect(outline, 1.0f);
    }

    if (showNoteNumber)
    {
        g.setColour(colorToUse.contrasting());
        String numTxt = String(keyNumber);
        Rectangle<int> txtArea = getLocalBounds();
        txtArea.removeFromBottom(6);
        g.drawText(numTxt, txtArea,    Justification::centredBottom);
    }

    isDirty = false;
}

void Key::setDisplayColor(Colour colorIn)
{
    color = colorIn;
    node.setProperty(IDs::pianoKeyColorsNode, color.toString(), nullptr);
    repaint();
}


text:
/*
  ==============================================================================

    VirtualKeyboardGraphics.h
    Created: 25 Jul 2020 1:49:41am
    Author:  Vincenzo

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

#pragma once
#include <JuceHeader.h>
#include "../../../Structures/Mode.h"
#include "../../../PluginModes.h"

namespace VirtualKeyboard
{
    class KeyGraphics
    {
    public:

        KeyGraphics(const Mode& modeIn, int baseKeyHeightIn, int baseKeyWidthIn, float heightLayerMultIn, float widthLayerMultIn);

        void setBaseKeyWidth(int keyWidthIn);

        void setBaseKeyHeight(int keyHeightIn);

        void setWidthMultiplier(float widthMultIn);

        void setHeightMultiplier(float heightMultIn);

        void setKeyPlacementStyle(VirtualKeyboard::KeyPlacementType placementTypeIn);

        Array<Path> getKeyboardOctave(int scaleDegreeOffset = 0);

        Array<Path> getFullKeyboard();

        Path getKey(int keyNumber);

    private:
        const Mode& mode;

        int baseKeyHeight;
        int baseKeyWidth;
        float heightLayerMult;
        float widthLayerMult;

        VirtualKeyboard::KeyPlacementType keyPlacement = VirtualKeyboard::nestedRight;
    };
}


text:
/*
 ==============================================================================

 VirtualKeyboardGrid.h
 Created: 17 Apr 2019 2:12:23pm
 Author:  Vincenzo

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

#pragma once

#include "../../../PluginModes.h"
#include "../../../Structures/FractionalGrid.h"
#include "../../../Structures/Mode.h"
#include "VirtualKeyboardKey.h"

namespace VirtualKeyboard
{
    class KeyboardPositioner
    {
        Component* parent;

        int numModeKeys;
        int keyGap = 0;
        int keyPlacement = 1;
        float baseWidthRatio = 0.8f;
        float baseHeightRatio = 0.55f;

        float keyWidth = 0;

    public:

        KeyboardPositioner(Component* parentIn, int numModeKeysIn = 128);
        ~KeyboardPositioner() {};

        void parentResized();

        float getKeyWidth() const;

        int getKeyGap() const;

        void setNumModeKeys(int numKeysIn);

        void setKeyGap(int keyGapIn);

        void setKeyPlacement(int keyPlacementTypeIn);

        void setLayerKeysWidthRatio(float widthRatioIn);
        void setLayerKeysHeightRatio(float widthRatioIn);

        void resizeKey(Key& key);
        void placeKey(Key& key);
    };
}


text:
/*
 ==============================================================================

 VirtualKeyboardKey.h
 Created: 17 Apr 2019 2:11:53pm
 Author:  Vincenzo

 Struct for holding virtual keyboard key data.

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

#pragma once
#include "../JuceLibraryCode/JuceHeader.h"
#include "../../../PluginIDs.h"
#include "../../../PluginModes.h"

namespace VirtualKeyboard
{
    class Key : public Component
    {

    public:

        Key();
        Key(int keyNumIn);
        Key(int keyNumIn, int orderIn, int scaleDegreeIn, int modeDegreeIn, int stepIn,
            String pitchNameIn = "", int widthModIn = 0, int heightModIn = 0, int xOff = 0, int yOff = 0,
            bool showNoteNumIn = false, bool showNoteNameIn = false, Colour colorIn = Colours::transparentBlack);
        Key(ValueTree keyNodeIn);
        Key(const Key& keyToCopy);

        Colour getDisplayColor() const;

        void setDisplayColor(Colour colorIn);

        void applyParameters(ValueTree nodeIn);

        void setPath(Path keyPathIn);

        void paint(juce::Graphics& g) override;

        ValueTree node;

        Path keyPath;

        // Properties
        int keyNumber = 0;
        int order = 0; // front most keys are 0, "black" keys are 1, etc
        int scaleDegree = 0; // corresponds to chromatic degree in the period
        float modeDegree = 0; // fractional degree. in Meantone[7] in 12edo set in C, F# would be 3.5
        int step = 0; // the interval step that this note is a part of

        float velocity = 0; // recorded when key is clicked
        bool isClicked = false; // clicked by mouse
        bool exPressed = false; // pressed by midi input
        Colour exInputColor; // for midi controller input, will use this color if it's opaque
        bool isDirty = true;

        // Parameters
        String pitchName = "";
        float widthMod = -1;
        float heightMod = -1;
        float xOffset = 0;
        float yOffset = 0;
        bool showNoteNumber = false;
        bool showNoteLabel = false;
        float velocityFixed = 1;

        Colour color = Colours::transparentBlack;
        int highlightStyleId = HighlightStyle::full;
    };
}