InstrumentListComponent::InstrumentListComponent (Sequencer* sequencer_) :
Component ("InstrumentListComponent"),
sequencer (sequencer_),
lastPattern (nullptr),
lastActiveInstrument (nullptr)
{
    Pattern* pattern = sequencer->getPattern();
    lastPattern = pattern;
    int numInstruments = pattern->getNumInstruments();    
    for (int i = 0; i < numInstruments; i++) {
        Instrument* instrument = pattern->getInstrumentAt (i);
        InstrumentComponent* instrumentComponent = new InstrumentComponent (instrument);
        addAndMakeVisible (instrumentComponent);
        instrumentComponents.add (instrumentComponent);
    }   
    
    for (int i = 0; i < numInstruments; i++) {
        ToggleButton* btn = new ToggleButton ((String)i);
        btn->setRadioGroupId (kRadioGroupId);
        btn->setColour (ToggleButton::textColourId, Colours::white);
        addAndMakeVisible (btn);
        btn->addListener (this);
        activeButtons.add (btn);
    }   
    
    startTimer (100);
}
void
SettingsWindow::setUpStandardFields(int & widthSoFar,
                                    int & heightSoFar)
{
    ODL_OBJENTER(); //####
    ODL_P2("widthSoFar = ", &widthSoFar, "heightSoFar = ", &heightSoFar); //####
    Component * content = getContentComponent();
    int         buttonHeight = GetButtonHeight();
    Point<int>  dimensions;
    size_t      numDescriptors = _descriptors.size();

    widthSoFar = heightSoFar = 0;
    _topText.setFont(_regularFont);
    if ((0 < numDescriptors) || _canSetEndpoint || _canSetPort || _canSetTag || _canUseModifier)
    {
        _topText.setText(String("The ") + _execType + " has one or more arguments, that need to be "
                         "provided before it can be launched.", dontSendNotification);
    }
    else
    {
        _topText.setText(String("The ") + _execType + " has no arguments or options, so it can be "
                         "launched right now.", dontSendNotification);
    }
    CommonVisuals::CalculateTextArea(dimensions, _regularFont, _topText.getText());
    _topText.setBounds(FormField::kButtonGap, FormField::kButtonGap + getTitleBarHeight(),
                       dimensions.getX() + FormField::kButtonGap, dimensions.getY());
    content->addAndMakeVisible(&_topText, 0);
    heightSoFar = _topText.getY() + _topText.getHeight() + FormField::kButtonGap;
    widthSoFar = jmax(widthSoFar, _topText.getX() + _topText.getWidth());
    if (_canSetEndpoint)
    {
        _endpointField = new CaptionedTextField(*this, _regularFont, _errorFont, 0,
                                                "(Optional) Endpoint to use", heightSoFar, false,
                                                false, NULL,
                                                new TextValidator(*_endpointDescriptor),
                                                kEndpointFieldName);

        _endpointField->addToComponent(content);
        widthSoFar = jmax(widthSoFar, _endpointField->getMinimumWidth());
        heightSoFar = (_endpointField->getY() + _endpointField->getHeight() +
                       (FormField::kButtonGap / 2));
    }
    if (_canSetPort)
    {
        _portField = new CaptionedTextField(*this, _regularFont, _errorFont, 0,
                                            "(Optional) Network port to use", heightSoFar, false,
                                            false, NULL, new TextValidator(*_portDescriptor),
                                            kPortFieldName);

        _portField->addToComponent(content);
        widthSoFar = jmax(widthSoFar, _portField->getMinimumWidth());
        heightSoFar = _portField->getY() + _portField->getHeight() + (FormField::kButtonGap / 2);
    }
    if (_canSetTag)
    {
        _tagField = new CaptionedTextField(*this, _regularFont, _errorFont, 0,
                                           String("(Optional) Tag for the ") + _execType,
                                           heightSoFar, false, false, NULL, NULL, kTagFieldName);

        _tagField->addToComponent(content);
        widthSoFar = jmax(widthSoFar, _tagField->getMinimumWidth());
        heightSoFar = _tagField->getY() + _tagField->getHeight() + (FormField::kButtonGap / 2);
    }
    if (_canUseModifier)
    {
        _tagModifierGroup = new GroupComponent("", "(Optional) Tag modifier");
        _tagModifierGroup->setBounds(FormField::kFieldInset, heightSoFar,
                                        widthSoFar - (FormField::kButtonGap +
                                                      FormField::kFieldInset),
                                        static_cast<int>(2 * _regularFont.getHeight()) +
                                        FormField::kButtonGap);
        for (int ii = 0; 4 >= ii; ++ii)
        {
            ToggleButton * tb = new ToggleButton(String(ii) + ((1 == ii) ? " byte" : " bytes"));

            _tagModifierGroup->addAndMakeVisible(tb);
            _tagModifierButtons.add(tb);
            tb->setComponentID(String(ii));
            tb->setRadioGroupId(kTagModifierGroupId);
            tb->setBounds(FormField::kFieldInset + (kTagModifierButtonWidth * ii),
                          FormField::kButtonGap + static_cast<int>(_regularFont.getHeight() / 2),
                          kTagModifierButtonWidth, static_cast<int>(_regularFont.getHeight()));
            tb->setTooltip("Modify tag with " + String(ii) + ((1 == ii) ?
                                                              " byte of the IP address" :
                                                              " bytes of the IP address"));
            if (0 == ii)
            {
                tb->setToggleState(true, dontSendNotification);
            }
        }
        content->addAndMakeVisible(_tagModifierGroup);
        heightSoFar = (_tagModifierGroup->getY() + _tagModifierGroup->getHeight() +
                       (FormField::kButtonGap / 2));
        widthSoFar = jmax(widthSoFar, _tagModifierGroup->getX() +
                          _tagModifierGroup->getWidth());
    }
    // Check for one or more file descriptors.
    for (size_t ii = 0; numDescriptors > ii; ++ii)
    {
        bool                                forOutput;
        Utilities::BaseArgumentDescriptor * aDescriptor = _descriptors[ii];

        if (aDescriptor && aDescriptor->isForFiles(forOutput))
        {
            _hasFileField = true;
            break;
        }

    }
    for (size_t ii = 0; numDescriptors > ii; ++ii)
    {
        Utilities::BaseArgumentDescriptor * aDescriptor = _descriptors[ii];

        if (aDescriptor)
        {
            String argName(aDescriptor->argumentName().c_str());
            String argDescription(aDescriptor->argumentDescription().c_str());

            if (aDescriptor->isExtra())
            {
                if (! _hasExtraArguments)
                {
                    _hasExtraArguments = true;
                    _extraArgRootName = argName;
                    _extraArgumentsGroup = new GroupComponent("", argDescription);
                    _extraArgumentsGroup->setBounds(FormField::kFieldInset, heightSoFar,
                                                    widthSoFar - (FormField::kButtonGap +
                                                                  FormField::kFieldInset),
                                                    static_cast<int>(_regularFont.getHeight()) +
                                                    FormField::kButtonGap);
                    content->addAndMakeVisible(_extraArgumentsGroup);
                    heightSoFar = (_extraArgumentsGroup->getY() +
                                   _extraArgumentsGroup->getHeight() +
                                   (FormField::kButtonGap / 2));
                    widthSoFar = jmax(widthSoFar, _extraArgumentsGroup->getX() +
                                      _extraArgumentsGroup->getWidth());
                    _addArgumentsButton = new TextButton(String("+ ") + argName);
                    _addArgumentsButton->setWantsKeyboardFocus(true);
                    _addArgumentsButton->setMouseClickGrabsKeyboardFocus(false);
                    _addArgumentsButton->setCommandToTrigger(NULL, kConfigurationAddField, false);
                    _addArgumentsButton->addListener(this);
                    _addArgumentsButton->changeWidthToFitText(buttonHeight);
                    content->addAndMakeVisible(_addArgumentsButton, 0);
                    _removeArgumentsButton = new TextButton(String("- ") + argName);
                    _removeArgumentsButton->setWantsKeyboardFocus(true);
                    _removeArgumentsButton->setMouseClickGrabsKeyboardFocus(false);
                    _removeArgumentsButton->setCommandToTrigger(NULL, kConfigurationRemoveField,
                                                                false);
                    _removeArgumentsButton->addListener(this);
                    _removeArgumentsButton->changeWidthToFitText(buttonHeight);
                    content->addChildComponent(_removeArgumentsButton);
                }
            }
            else if (aDescriptor->isBoolean())
            {
                String          descriptionPrefix(aDescriptor->isOptional() ? "(Optional) " :
                                                  "");
                CheckboxField * newField = new CheckboxField(_regularFont, ii,
                                                             descriptionPrefix +
                                                             argDescription, heightSoFar, argName);

                newField->setText(aDescriptor->getDefaultValue().c_str());
                _standardFields.add(newField);
                newField->addToComponent(content);
                widthSoFar = jmax(widthSoFar, newField->getMinimumWidth());
                heightSoFar = (newField->getY() + newField->getHeight() +
                               (FormField::kButtonGap / 2));
            }
            else
            {
                bool forFilePath;
                bool forOutput;

                if (aDescriptor->isForFiles(forOutput))
                {
                    forFilePath = true;
                }
                else
                {
                    forFilePath = false;
                }
                String               descriptionPrefix(aDescriptor->isOptional() ? "(Optional) " :
                                                       "");
                CaptionedTextField * newField = new CaptionedTextField(*this, _regularFont,
                                                                       _errorFont, ii,
                                                                       descriptionPrefix +
                                                                       argDescription, heightSoFar,
                                                                       false, forFilePath, this,
                                                                   new TextValidator(*aDescriptor),
                                                                       argName,
                                                                       aDescriptor->isPassword() ?
                                                                       CHAR_TO_USE_FOR_PASSWORD_ :
                                                                       0);

                newField->setText(aDescriptor->getDefaultValue().c_str());
                _standardFields.add(newField);
                newField->addToComponent(content);
                widthSoFar = jmax(widthSoFar, newField->getMinimumWidth());
                heightSoFar = (newField->getY() + newField->getHeight() +
                               (FormField::kButtonGap / 2));
            }
        }
    }
    _cancelButton.setWantsKeyboardFocus(true);
    _cancelButton.setMouseClickGrabsKeyboardFocus(false);
    _cancelButton.setCommandToTrigger(NULL, kConfigurationCancel, false);
    _cancelButton.addListener(this);
    _cancelButton.changeWidthToFitText(buttonHeight);
    _cancelButton.setTopLeftPosition(0, heightSoFar + FormField::kButtonGap);
    content->addAndMakeVisible(&_cancelButton, 0);
    _okButton.setWantsKeyboardFocus(true);
    _okButton.setMouseClickGrabsKeyboardFocus(false);
    _okButton.setCommandToTrigger(NULL, kConfigurationOK, false);
    _okButton.addListener(this);
    _okButton.changeWidthToFitText(buttonHeight);
    _okButton.setTopLeftPosition(0, heightSoFar + FormField::kButtonGap);
    content->addAndMakeVisible(&_okButton, 0);
    heightSoFar += buttonHeight;
    ODL_OBJEXIT(); //####
} // SettingsWindow::setUpStandardFields