// -*- c-basic-offset: 4 -*-

/*
    Rosegarden-4
    A sequencer and musical notation editor.

    This program is Copyright 2000-2006
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>

    The moral right of the authors to claim authorship of this work
    has been asserted.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include <iostream>
#include <cstdio>

#include <qdial.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qcheckbox.h>
#include <qregexp.h>
#include <qslider.h>
#include <qpushbutton.h>
#include <qsignalmapper.h>
#include <qwidgetstack.h>
#include <qsignalmapper.h>

#include <kcombobox.h>
#include <kglobal.h>
#include <kstddirs.h>
#include <klocale.h>
#include <kapp.h>
#include <ksqueezedtextlabel.h>

#include <algorithm>

#include "Midi.h"
#include "Instrument.h"
#include "MidiDevice.h"
#include "MappedStudio.h"
#include "ControlParameter.h"
#include "AudioLevel.h"

#include "audioplugindialog.h"
#include "instrumentparameterbox.h"
#include "audiopluginmanager.h"
#include "widgets.h"
#include "studiocontrol.h"
#include "rosegardenguidoc.h"
#include "studiowidgets.h"
#include "rosegardengui.h"

#include "rosestrings.h"
#include "rosedebug.h"

#include "studiocontrol.h"
#include "studiowidgets.h"
#include "widgets.h"

using Rosegarden::Instrument;
using Rosegarden::MidiDevice;

InstrumentParameterBox::InstrumentParameterBox(RosegardenGUIDoc *doc,
                                               QWidget *parent)
  : RosegardenParameterBox(i18n("Instrument"),
			   i18n("Instrument Parameters"),
			   parent),
      m_widgetStack(new QWidgetStack(this)),
      m_noInstrumentParameters(new QVBox(this)),
      m_midiInstrumentParameters(new MIDIInstrumentParameterPanel(doc, this)),
      m_audioInstrumentParameters(new AudioInstrumentParameterPanel(doc, this)),
      m_selectedInstrument(0),
      m_doc(doc),
      m_lastShowAdditionalControlsArg(false)
{
    m_widgetStack->setFont(m_font);
    m_noInstrumentParameters->setFont(m_font);
    m_midiInstrumentParameters->setFont(m_font);
    m_audioInstrumentParameters->setFont(m_font);

    bool contains = false;

    std::vector<InstrumentParameterBox*>::iterator it =
        instrumentParamBoxes.begin();

    for (; it != instrumentParamBoxes.end(); it++)
        if ((*it) == this)
            contains = true;

    if (!contains)
        instrumentParamBoxes.push_back(this);

    m_widgetStack->addWidget(m_midiInstrumentParameters);
    m_widgetStack->addWidget(m_audioInstrumentParameters);
    m_widgetStack->addWidget(m_noInstrumentParameters);

    m_midiInstrumentParameters->adjustSize();
    m_audioInstrumentParameters->adjustSize();
    m_noInstrumentParameters->adjustSize();

    connect(m_audioInstrumentParameters, SIGNAL(updateAllBoxes()),
            this, SLOT(slotUpdateAllBoxes()));

    connect(m_audioInstrumentParameters,
	    SIGNAL(instrumentParametersChanged(Rosegarden::InstrumentId)),
	    this,
	    SIGNAL(instrumentParametersChanged(Rosegarden::InstrumentId)));

    connect(m_audioInstrumentParameters,
	    SIGNAL(selectPlugin(QWidget *, Rosegarden::InstrumentId, int)),
	    this,
	    SIGNAL(selectPlugin(QWidget *, Rosegarden::InstrumentId, int)));

    connect(m_audioInstrumentParameters,
	    SIGNAL(showPluginGUI(Rosegarden::InstrumentId, int)),
	    this,
	    SIGNAL(showPluginGUI(Rosegarden::InstrumentId, int)));
    
    connect(m_midiInstrumentParameters, SIGNAL(updateAllBoxes()),
            this, SLOT(slotUpdateAllBoxes()));

    connect(m_midiInstrumentParameters, 
            SIGNAL(changeInstrumentLabel(Rosegarden::InstrumentId, QString)),
            this, SIGNAL(changeInstrumentLabel(Rosegarden::InstrumentId, QString)));

    connect(m_audioInstrumentParameters, 
            SIGNAL(changeInstrumentLabel(Rosegarden::InstrumentId, QString)),
            this, SIGNAL(changeInstrumentLabel(Rosegarden::InstrumentId, QString)));

    connect(m_midiInstrumentParameters,
	    SIGNAL(instrumentParametersChanged(Rosegarden::InstrumentId)),
	    this,
	    SIGNAL(instrumentParametersChanged(Rosegarden::InstrumentId)));

// Layout the groups left to right.

    QBoxLayout* layout = new QVBoxLayout(this);
    layout->addWidget(m_widgetStack);

}

InstrumentParameterPanel::InstrumentParameterPanel(RosegardenGUIDoc *doc, 
                                                   QWidget* parent)
    : QFrame(parent),
      m_instrumentLabel(new KSqueezedTextLabel(this)),
      m_selectedInstrument(0),
      m_doc(doc)
{
    QFontMetrics metrics(m_instrumentLabel->fontMetrics());
    int width25 = metrics.width("1234567890123456789012345");
      
    m_instrumentLabel->setFixedWidth(width25);
    m_instrumentLabel->setAlignment(Qt::AlignCenter);
}

void
InstrumentParameterPanel::setDocument(RosegardenGUIDoc* doc)
{
    m_doc = doc;
}

InstrumentParameterBox::~InstrumentParameterBox()
{
    // deregister this parameter box
    std::vector<InstrumentParameterBox*>::iterator it =
        instrumentParamBoxes.begin();

    for (; it != instrumentParamBoxes.end(); it++)
    {
        if ((*it) == this)
        {
            instrumentParamBoxes.erase(it);
            break;
        }
    }
}

QString
InstrumentParameterBox::getPreviousBox(RosegardenParameterArea::Arrangement arrangement) const
{
    return i18n("Track");
}

void
InstrumentParameterBox::setAudioMeter(float ch1, float ch2, float ch1r, float ch2r)
{
    m_audioInstrumentParameters->setAudioMeter(ch1, ch2, ch1r, ch2r);
}

void
InstrumentParameterBox::setDocument(RosegardenGUIDoc* doc)
{
    m_doc = doc;
    m_midiInstrumentParameters->setDocument(m_doc);
    m_audioInstrumentParameters->setDocument(m_doc);
}

void
InstrumentParameterBox::slotPluginSelected(Rosegarden::InstrumentId id, int index, int plugin)
{
    m_audioInstrumentParameters->slotPluginSelected(id, index, plugin);
}

void
InstrumentParameterBox::slotPluginBypassed(Rosegarden::InstrumentId id, int index, bool bypassState)
{
    m_audioInstrumentParameters->slotPluginBypassed(id, index, bypassState);
}

void
InstrumentParameterBox::useInstrument(Instrument *instrument)
{
    RG_DEBUG << "useInstrument() - populate Instrument\n";

    if (instrument == 0)
    {
        m_widgetStack->raiseWidget(m_noInstrumentParameters);
        emit instrumentPercussionSetChanged(instrument);
        return;
    }

    // ok
    m_selectedInstrument = instrument;

    // Hide or Show according to Instrument type
    //
    if (instrument->getType() == Instrument::Audio ||
	instrument->getType() == Instrument::SoftSynth)
    {
        m_audioInstrumentParameters->setupForInstrument(m_selectedInstrument);
        m_widgetStack->raiseWidget(m_audioInstrumentParameters);

    } else { // Midi

        m_midiInstrumentParameters->setupForInstrument(m_selectedInstrument);
        m_midiInstrumentParameters->showAdditionalControls(m_lastShowAdditionalControlsArg);
	m_widgetStack->raiseWidget(m_midiInstrumentParameters);
        emit instrumentPercussionSetChanged(instrument);
        
    }

}


void
InstrumentParameterBox::slotUpdateAllBoxes()
{
    emit instrumentPercussionSetChanged(m_selectedInstrument);

    std::vector<InstrumentParameterBox*>::iterator it =
        instrumentParamBoxes.begin();

    // To update all open IPBs
    //
    for (; it != instrumentParamBoxes.end(); it++)
    {
        if ((*it) != this && m_selectedInstrument &&
            (*it)->getSelectedInstrument() == m_selectedInstrument)
            (*it)->useInstrument(m_selectedInstrument);
    }
}

void
InstrumentParameterBox::slotInstrumentParametersChanged(Rosegarden::InstrumentId id)
{
    std::vector<InstrumentParameterBox*>::iterator it =
        instrumentParamBoxes.begin();

    blockSignals(true);

    for (; it != instrumentParamBoxes.end(); it++)
    {
	if ((*it)->getSelectedInstrument()) {
	    if ((*it)->getSelectedInstrument()->getId() == id) {
		(*it)->useInstrument((*it)->getSelectedInstrument()); // refresh
	    }
	}	    
    }

    blockSignals(false);
}

void 
InstrumentParameterBox::showAdditionalControls(bool showThem)
{
    m_midiInstrumentParameters->showAdditionalControls(showThem);
    m_lastShowAdditionalControlsArg = showThem;
}




void
AudioInstrumentParameterPanel::slotSelectAudioLevel(float dB)
{
    if (m_selectedInstrument == 0)
        return;

    if (m_selectedInstrument->getType() == Instrument::Audio ||
	m_selectedInstrument->getType() == Instrument::SoftSynth)
    {
	m_selectedInstrument->setLevel(dB);

	Rosegarden::StudioControl::setStudioObjectProperty
	    (Rosegarden::MappedObjectId(m_selectedInstrument->getMappedId()),
	     Rosegarden::MappedAudioFader::FaderLevel,
	     Rosegarden::MappedObjectValue(dB));
    }

    emit updateAllBoxes();
    emit instrumentParametersChanged(m_selectedInstrument->getId());
}

void
AudioInstrumentParameterPanel::slotSelectAudioRecordLevel(float dB)
{
    if (m_selectedInstrument == 0)
        return;

//    std::cerr << "AudioInstrumentParameterPanel::slotSelectAudioRecordLevel("
//	      << dB << ")" << std::endl;

    if (m_selectedInstrument->getType() == Instrument::Audio)
    {
	m_selectedInstrument->setRecordLevel(dB);

	Rosegarden::StudioControl::setStudioObjectProperty
	    (Rosegarden::MappedObjectId(m_selectedInstrument->getMappedId()),
	     Rosegarden::MappedAudioFader::FaderRecordLevel,
	     Rosegarden::MappedObjectValue(dB));

	emit updateAllBoxes();
	emit instrumentParametersChanged(m_selectedInstrument->getId());
    }
}


void
AudioInstrumentParameterPanel::slotPluginSelected(Rosegarden::InstrumentId instrumentId,
						  int index, int plugin)
{
    if (!m_selectedInstrument ||
	instrumentId != m_selectedInstrument->getId()) return;

    RG_DEBUG << "AudioInstrumentParameterPanel::slotPluginSelected - "
             << "instrument = " << instrumentId
             << ", index = " << index
             << ", plugin = " << plugin << endl;

    QColor pluginBackgroundColour = Qt::black;
    bool bypassed = false;

    QPushButton *button = 0;
    QString noneText;

    // updates synth gui button &c:
    m_audioFader->slotSetInstrument(&m_doc->getStudio(), m_selectedInstrument);

    if (index == (int)Rosegarden::Instrument::SYNTH_PLUGIN_POSITION) {
	button = m_audioFader->m_synthButton;
	noneText = i18n("<no synth>");
    } else {
	button = m_audioFader->m_plugins[index];
	noneText = i18n("<no plugin>");
    }

    if (!button) return;

    if (plugin == -1) {

	button->setText(noneText);
        QToolTip::add(button, noneText);

    } else {

	Rosegarden::AudioPlugin *pluginClass 
	    = m_doc->getPluginManager()->getPlugin(plugin);

	if (pluginClass)
        {
	    button->setText(pluginClass->getLabel());

            QToolTip::add(button, pluginClass->getLabel());

            pluginBackgroundColour = pluginClass->getColour();
        }
    }

    Rosegarden::AudioPluginInstance *inst = 
        m_selectedInstrument->getPlugin(index);

    if (inst)
        bypassed = inst->isBypassed();

    setButtonColour(index, bypassed, pluginBackgroundColour);
    
    if (index == (int)Rosegarden::Instrument::SYNTH_PLUGIN_POSITION) {
        emit changeInstrumentLabel(instrumentId, button->text());
    }
}

void
AudioInstrumentParameterPanel::slotPluginBypassed(Rosegarden::InstrumentId instrumentId,
						  int pluginIndex, bool bp)
{
    if (!m_selectedInstrument ||
	instrumentId != m_selectedInstrument->getId()) return;

    Rosegarden::AudioPluginInstance *inst = 
        m_selectedInstrument->getPlugin(pluginIndex);

    QColor backgroundColour = Qt::black; // default background colour

    if (inst && inst->isAssigned())
    {
        Rosegarden::AudioPlugin *pluginClass 
            = m_doc->getPluginManager()->getPlugin(
                    m_doc->getPluginManager()->
                        getPositionByIdentifier(inst->getIdentifier().c_str()));

        /// Set the colour on the button
        //
        if (pluginClass)
            backgroundColour = pluginClass->getColour();
    }

    setButtonColour(pluginIndex, bp, backgroundColour);
}

// Set the button colour
//
void
AudioInstrumentParameterPanel::setButtonColour(
        int pluginIndex, bool bypassState, const QColor &colour)
{
    RG_DEBUG << "AudioInstrumentParameterPanel::setButtonColour " 
        << "pluginIndex = " << pluginIndex
        << ", bypassState = " << bypassState
        << ", rgb = " << colour.name() << endl;

    QPushButton *button = 0;

    if (pluginIndex == Rosegarden::Instrument::SYNTH_PLUGIN_POSITION) {
	button = m_audioFader->m_synthButton;
    } else {
	button = m_audioFader->m_plugins[pluginIndex];
    }

    if (!button) return;

    // Set the bypass colour on the plugin button
    if (bypassState)
    {
	button->
            setPaletteForegroundColor(kapp->palette().
                    color(QPalette::Active, QColorGroup::Button));

	button->
            setPaletteBackgroundColor(kapp->palette().
                    color(QPalette::Active, QColorGroup::ButtonText));
    }
    else if (colour == Qt::black)
    {
	button->
            setPaletteForegroundColor(kapp->palette().
                    color(QPalette::Active, QColorGroup::ButtonText));

	button->
            setPaletteBackgroundColor(kapp->palette().
                    color(QPalette::Active, QColorGroup::Button));
    }
    else
    {
	button->
            setPaletteForegroundColor(Qt::white);

	button->
	    setPaletteBackgroundColor(colour);
    }
}


AudioInstrumentParameterPanel::AudioInstrumentParameterPanel(RosegardenGUIDoc* doc, QWidget* parent)
    : InstrumentParameterPanel(doc, parent),
      m_audioFader(new AudioFaderBox(this))
{
    QGridLayout *gridLayout = new QGridLayout(this, 3, 2, 5, 5);

    // Instrument label : first row, all cols
    gridLayout->addMultiCellWidget(m_instrumentLabel, 0, 0, 0, 1, AlignCenter);

    // fader and connect it
    gridLayout->addMultiCellWidget(m_audioFader, 1, 1, 0, 1);

    gridLayout->setRowStretch(2, 1);

    connect(m_audioFader, SIGNAL(audioChannelsChanged(int)),
            this, SLOT(slotAudioChannels(int)));

    connect(m_audioFader->m_signalMapper, SIGNAL(mapped(int)),
            this, SLOT(slotSelectPlugin(int)));

    connect(m_audioFader->m_fader, SIGNAL(faderChanged(float)),
            this, SLOT(slotSelectAudioLevel(float)));

    connect(m_audioFader->m_recordFader, SIGNAL(faderChanged(float)),
            this, SLOT(slotSelectAudioRecordLevel(float)));

    connect(m_audioFader->m_pan, SIGNAL(valueChanged(float)),
            this, SLOT(slotSetPan(float)));

    connect(m_audioFader->m_audioOutput, SIGNAL(changed()),
            this, SLOT(slotAudioRoutingChanged()));

    connect(m_audioFader->m_audioInput, SIGNAL(changed()),
            this, SLOT(slotAudioRoutingChanged()));

    connect(m_audioFader->m_synthButton, SIGNAL(clicked()),
	    this, SLOT(slotSynthButtonClicked()));

    connect(m_audioFader->m_synthGUIButton, SIGNAL(clicked()),
	    this, SLOT(slotSynthGUIButtonClicked()));
}

void
AudioInstrumentParameterPanel::slotSynthButtonClicked()
{
    slotSelectPlugin(Rosegarden::Instrument::SYNTH_PLUGIN_POSITION);
}

void
AudioInstrumentParameterPanel::slotSynthGUIButtonClicked()
{
    emit showPluginGUI(m_selectedInstrument->getId(),
			Rosegarden::Instrument::SYNTH_PLUGIN_POSITION);
}

void
AudioInstrumentParameterPanel::slotSetPan(float pan)
{
    RG_DEBUG << "AudioInstrumentParameterPanel::slotSetPan - "
             << "pan = " << pan << endl;

    Rosegarden::StudioControl::setStudioObjectProperty
        (Rosegarden::MappedObjectId(m_selectedInstrument->getMappedId()),
         Rosegarden::MappedAudioFader::Pan,
         Rosegarden::MappedObjectValue(pan));

    m_selectedInstrument->setPan(Rosegarden::MidiByte(pan + 100.0));
    emit instrumentParametersChanged(m_selectedInstrument->getId());
}

void
AudioInstrumentParameterPanel::setAudioMeter(float dBleft, float dBright,
					     float recDBleft, float recDBright)
{
//    RG_DEBUG << "AudioInstrumentParameterPanel::setAudioMeter: (" << dBleft
//	     << "," << dBright << ")" << endl;
    
    if (m_selectedInstrument) {
	// Always set stereo, because we have to reflect what's happening
	// with the pan setting even on mono tracks
	m_audioFader->m_vuMeter->setLevel(dBleft, dBright);
	m_audioFader->m_vuMeter->setRecordLevel(recDBleft, recDBright);
    }
}


void
AudioInstrumentParameterPanel::setupForInstrument(Instrument* instrument)
{
    blockSignals(true);

    m_selectedInstrument = instrument;

    m_instrumentLabel->setText(strtoqstr(instrument->getName()));

    m_audioFader->m_recordFader->setFader(instrument->getRecordLevel());
    m_audioFader->m_fader->setFader(instrument->getLevel());

    m_audioFader->slotSetInstrument(&m_doc->getStudio(), instrument);

    int start = 0;

    if (instrument->getType() == Rosegarden::Instrument::SoftSynth) start = -1;

    for (int i = start; i < int(m_audioFader->m_plugins.size()); i++)
    {
	int index;
	QPushButton *button;
	QString noneText;

	if (i == -1) {
	    index = Rosegarden::Instrument::SYNTH_PLUGIN_POSITION;
	    button = m_audioFader->m_synthButton;
	    noneText = i18n("<no synth>");
	} else {
	    index = i;
	    button = m_audioFader->m_plugins[i];
	    noneText = i18n("<no plugin>");
	}

	button->show();

        Rosegarden::AudioPluginInstance *inst = instrument->getPlugin(index);

        if (inst && inst->isAssigned())
        {
            Rosegarden::AudioPlugin *pluginClass 
                = m_doc->getPluginManager()->getPlugin(
                        m_doc->getPluginManager()->
                            getPositionByIdentifier(inst->getIdentifier().c_str()));

            if (pluginClass)
            {
                button->setText(pluginClass->getLabel());
                QToolTip::add(button, pluginClass->getLabel());
                setButtonColour(index, inst->isBypassed(), 
                        pluginClass->getColour());
            }
        }
        else
        {
            button->setText(noneText);
            QToolTip::add(button, noneText);
            setButtonColour(index, inst ? inst->isBypassed() : false, Qt::black);
        }
    }

    // Set the number of channels on the fader widget
    //
    m_audioFader->setAudioChannels(instrument->getAudioChannels());

    // Pan - adjusted backwards
    //
    m_audioFader->m_pan->setPosition(instrument->getPan() - 100);

    // Tell fader box whether to include e.g. audio input selection
    // 
    m_audioFader->setIsSynth(instrument->getType() == Rosegarden::Instrument::SoftSynth);

    blockSignals(false);
}

void
AudioInstrumentParameterPanel::slotAudioChannels(int channels)
{
    RG_DEBUG << "AudioInstrumentParameterPanel::slotAudioChannels - "
             << "channels = " << channels << endl;

    m_selectedInstrument->setAudioChannels(channels);

    Rosegarden::StudioControl::setStudioObjectProperty
        (Rosegarden::MappedObjectId(m_selectedInstrument->getMappedId()),
         Rosegarden::MappedAudioFader::Channels,
         Rosegarden::MappedObjectValue(channels));

    emit instrumentParametersChanged(m_selectedInstrument->getId());

}

void
AudioInstrumentParameterPanel::slotAudioRoutingChanged()
{
    if (m_selectedInstrument)
	emit instrumentParametersChanged(m_selectedInstrument->getId());
}

void
AudioInstrumentParameterPanel::slotSelectPlugin(int index)
{
    if (m_selectedInstrument) {
	emit selectPlugin(0, m_selectedInstrument->getId(), index);
    }
}



MIDIInstrumentParameterPanel::MIDIInstrumentParameterPanel(RosegardenGUIDoc *doc, QWidget* parent):
    InstrumentParameterPanel(doc, parent),
    m_rotaryFrame(0),
    m_rotaryMapper(new QSignalMapper(this))
{
    m_mainGrid = new QGridLayout(this, 10, 4, 2, 1);

    m_connectionLabel = new KSqueezedTextLabel(this);
    m_bankValue = new KComboBox(this);
    m_channelValue = new KComboBox(this);
    m_programValue = new KComboBox(this);
    m_variationValue = new KComboBox(this);
    m_bankCheckBox = new QCheckBox(this);
    m_programCheckBox = new QCheckBox(this);
    m_variationCheckBox = new QCheckBox(this);
    m_percussionCheckBox = new QCheckBox(this);

    m_bankValue->setSizeLimit(20);
    m_programValue->setSizeLimit(20);
    m_variationValue->setSizeLimit(20);

    m_bankLabel = new QLabel(i18n("Bank"), this);
    m_variationLabel = new QLabel(i18n("Variation"), this);
    m_programLabel = new QLabel(i18n("Program"), this);
    QLabel *channelLabel = new QLabel(i18n("Channel out"), this);
    QLabel *percussionLabel = new QLabel(i18n("Percussion"), this);

    // Ensure a reasonable amount of space in the program dropdowns even
    // if no instrument initially selected
    QFontMetrics metrics(m_programValue->font());
    int width22 = metrics.width("1234567890123456789012");
    int width25 = metrics.width("1234567890123456789012345");
    
    m_bankValue->setFixedWidth(width22);
    m_programValue->setFixedWidth(width22);
    m_variationValue->setFixedWidth(width22);
    
    m_connectionLabel->setFixedWidth(width25);
    m_connectionLabel->setAlignment(Qt::AlignCenter);

    // Configure the empty final row to accomodate any extra vertical space.

    m_mainGrid->setRowStretch(m_mainGrid->numRows()-1, 1);

    // Configure the empty final column to accomodate any extra horizontal
    // space.

    m_mainGrid->setColStretch(m_mainGrid->numCols()-1, 1);

    m_mainGrid->addMultiCellWidget(m_instrumentLabel, 0, 0, 0, 2, AlignCenter);
    m_mainGrid->addMultiCellWidget(m_connectionLabel, 1, 1, 0, 2, AlignCenter);

    m_mainGrid->addMultiCellWidget(channelLabel, 2, 2, 0, 1, AlignLeft);
    m_mainGrid->addWidget(m_channelValue,   2, 2, AlignRight);
    
    m_mainGrid->addMultiCellWidget(percussionLabel, 3, 3, 0, 1, AlignLeft);
    m_mainGrid->addWidget(m_percussionCheckBox, 3, 2, AlignRight);

    m_mainGrid->addWidget(m_bankLabel, 4, 0, AlignLeft);
    m_mainGrid->addWidget(m_bankCheckBox, 4, 1);
    m_mainGrid->addWidget(m_bankValue, 4, 2, AlignRight);

    m_mainGrid->addWidget(m_programLabel, 5, 0);
    m_mainGrid->addWidget(m_programCheckBox, 5, 1);
    m_mainGrid->addWidget(m_programValue, 5, 2, AlignRight);

    m_mainGrid->addWidget(m_variationLabel, 6, 0);
    m_mainGrid->addWidget(m_variationCheckBox, 6, 1);
    m_mainGrid->addWidget(m_variationValue, 6, 2, AlignRight);

    // Populate channel lists
    //
    for (int i = 0; i < 16; i++) {
        m_channelValue->insertItem(QString("%1").arg(i+1));
    }
        
    m_channelValue->setSizeLimit(16);

    // Disable these by default - they are activate by their
    // checkboxes
    //
    m_programValue->setDisabled(true);
    m_bankValue->setDisabled(true);
    m_variationValue->setDisabled(true);

    // Only active if we have an Instrument selected
    //
    m_percussionCheckBox->setDisabled(true);
    m_programCheckBox->setDisabled(true);
    m_bankCheckBox->setDisabled(true);
    m_variationCheckBox->setDisabled(true);

    // Connect up the toggle boxes
    //
    connect(m_percussionCheckBox, SIGNAL(toggled(bool)),
	    this, SLOT(slotTogglePercussion(bool)));

    connect(m_programCheckBox, SIGNAL(toggled(bool)),
            this, SLOT(slotToggleProgramChange(bool)));

    connect(m_bankCheckBox, SIGNAL(toggled(bool)),
            this, SLOT(slotToggleBank(bool)));

    connect(m_variationCheckBox, SIGNAL(toggled(bool)),
            this, SLOT(slotToggleVariation(bool)));


    // Connect activations
    //
    connect(m_bankValue, SIGNAL(activated(int)),
            this, SLOT(slotSelectBank(int)));

    connect(m_variationValue, SIGNAL(activated(int)),
            this, SLOT(slotSelectVariation(int)));

    connect(m_programValue, SIGNAL(activated(int)),
            this, SLOT(slotSelectProgram(int)));

    connect(m_channelValue, SIGNAL(activated(int)),
            this, SLOT(slotSelectChannel(int)));

    // don't select any of the options in any dropdown
    m_programValue->setCurrentItem(-1);
    m_bankValue->setCurrentItem(-1);
    m_channelValue->setCurrentItem(-1);
    m_variationValue->setCurrentItem(-1);

    connect(m_rotaryMapper, SIGNAL(mapped(int)),
            this, SLOT(slotControllerChanged(int)));
}

void
MIDIInstrumentParameterPanel::setupForInstrument(Instrument *instrument)
{
    RG_DEBUG << "MIDIInstrumentParameterPanel::setupForInstrument" << endl;
    MidiDevice *md = dynamic_cast<MidiDevice*>
	(instrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::setupForInstrument:"
                 << " No MidiDevice for Instrument "
	         << instrument->getId() << endl;
	return;
    }

    m_selectedInstrument = instrument;

    // Set instrument name
    //
    m_instrumentLabel->setText(strtoqstr(instrument->getPresentationName()));

    // Set Studio Device name
    //
    QString connection(strtoqstr(md->getConnection()));
    if (connection == "") {
	m_connectionLabel->setText(i18n("[ %1 ]").arg(i18n("No connection")));
    } else {
	
	// remove trailing "(duplex)", "(read only)", "(write only)" etc
	connection.replace(QRegExp("\\s*\\([^)0-9]+\\)\\s*$"), "");
	
	QString text = i18n("[ %1 ]").arg(connection);
	/*QString origText(text);
	
	QFontMetrics metrics(m_connectionLabel->fontMetrics());
	int maxwidth = metrics.width
	    ("Program: [X]   Acoustic Grand Piano 123");// kind of arbitrary!
	
	int hlen = text.length() / 2;
	while (metrics.width(text) > maxwidth && text.length() > 10) {
	    --hlen;
	    text = origText.left(hlen) + "..." + origText.right(hlen);
	}
	
	if (text.length() > origText.length() - 7) text = origText;*/
	m_connectionLabel->setText(text);
    }

    // Enable all check boxes
    //
    m_percussionCheckBox->setDisabled(false);
    m_programCheckBox->setDisabled(false);
    m_bankCheckBox->setDisabled(false);
    m_variationCheckBox->setDisabled(false);

    // Activate all checkboxes
    //
    m_percussionCheckBox->setChecked(instrument->isPercussion());
    m_programCheckBox->setChecked(instrument->sendsProgramChange());
    m_bankCheckBox->setChecked(instrument->sendsBankSelect());
    m_variationCheckBox->setChecked(instrument->sendsBankSelect());

    // Basic parameters
    //
    m_channelValue->setCurrentItem((int)instrument->getMidiChannel());

    // Check for program change
    //
    populateBankList();
    populateProgramList();
    populateVariationList();

    // Setup the ControlParameters
    //
    setupControllers(md);

    // Set all the positions by controller number
    //
    for (RotaryMap::iterator it = m_rotaries.begin() ;
            it != m_rotaries.end(); ++it)
    {
        Rosegarden::MidiByte value = 0;

        // Special cases
        //
        if (it->first == Rosegarden::MIDI_CONTROLLER_PAN)
            value = int(instrument->getPan());
        else if (it->first == Rosegarden::MIDI_CONTROLLER_VOLUME)
            value = int(instrument->getVolume());
        else
        {
            try
            {
                value = instrument->getControllerValue(
                        Rosegarden::MidiByte(it->first));
            }
            catch(...)
            {
                continue;
            }
        }

        setRotaryToValue(it->first, int(value));
    }
}

void
MIDIInstrumentParameterPanel::setupControllers(MidiDevice *md)
{
    if (!m_rotaryFrame) {
	m_rotaryFrame = new QFrame(this);
	m_mainGrid->addMultiCellWidget(m_rotaryFrame, 8, 8, 0, 2, Qt::AlignHCenter);
	m_rotaryGrid = new QGridLayout(m_rotaryFrame, 10, 3, 8, 1);
	m_rotaryGrid->addItem(new QSpacerItem(10, 4), 0, 1);
    }

    // To cut down on flicker, we avoid destroying and recreating
    // widgets as far as possible here.  If a label already exists,
    // we just set its text; if a rotary exists, we only replace it
    // if we actually need a different one.

    Rosegarden::Composition &comp = m_doc->getComposition();
    Rosegarden::ControlList list = md->getControlParameters();

    // sort by IPB position
    //
    std::sort(list.begin(), list.end(),
	      Rosegarden::ControlParameter::ControlPositionCmp());

    int count = 0;
    RotaryMap::iterator rmi = m_rotaries.begin();

    for (Rosegarden::ControlList::iterator it = list.begin();
	 it != list.end(); ++it)
    {
        if (it->getIPBPosition() == -1) continue;

	// Get the knob colour - only if the colour is non-default (>0)
	//
	QColor knobColour = Qt::black; // special case for RosegardenRotary
	if (it->getColourIndex() > 0) {
	    Rosegarden::Colour c =
		comp.getGeneralColourMap().getColourByIndex
		(it->getColourIndex());
	    knobColour = QColor(c.getRed(), c.getGreen(), c.getBlue());
	}

	RosegardenRotary *rotary = 0;

	if (rmi != m_rotaries.end()) {

	    // Update the controller number that is associated with the
	    // existing rotary widget.

  	    rmi->first = it->getControllerValue();

	    // Update the properties of the existing rotary widget.

	    rotary = rmi->second.first;
	    int redraw = 0; // 1 -> position, 2 -> all

	    if (rotary->getMinValue() != it->getMin()) {
		rotary->setMinValue(it->getMin());
		redraw = 1;
	    }
	    if (rotary->getMaxValue() != it->getMax()) {
		rotary->setMaxValue(it->getMax());
		redraw = 1;
	    }
	    if (rotary->getKnobColour() != knobColour) {
		rotary->setKnobColour(knobColour);
		redraw = 2;
	    }
	    if (redraw == 1 || rotary->getPosition() != it->getDefault()) {
		rotary->setPosition(it->getDefault());
		if (redraw == 1) redraw = 0;
	    }
	    if (redraw == 2) {
		rotary->repaint();
	    }

	    // Update the controller name that is associated with
	    // with the existing rotary widget.

	    QLabel *label = rmi->second.second;
	    label->setText(strtoqstr(it->getName()));

	    ++rmi;

	} else {

	    QHBox *hbox = new QHBox(m_rotaryFrame);
	    hbox->setSpacing(8);

	    float smallStep = 1.0;

	    float bigStep = 5.0;
	    if (it->getMax() - it->getMin() < 10) bigStep = 1.0;
	    else if (it->getMax() - it->getMin() < 20) bigStep = 2.0;

	    rotary = new RosegardenRotary
		(hbox,
		 it->getMin(),
		 it->getMax(),
		 smallStep,
		 bigStep,
		 it->getDefault(),
		 20,
		 RosegardenRotary::NoTicks,
		 false,
		 it->getDefault() == 64); //!!! hacky

	    rotary->setKnobColour(knobColour);

	    // Add a label
	    QLabel *label = new KSqueezedTextLabel(strtoqstr(it->getName()), hbox);

	    RG_DEBUG << "Adding new widget at " << (count/2) << "," << (count%2) << endl;

	    // Add the compound widget
	    //
	    m_rotaryGrid->addWidget(hbox, count/2, (count%2) * 2, AlignLeft);
	    hbox->show();

	    // Add to list
	    //
	    m_rotaries.push_back(std::pair<int, RotaryPair>
				 (it->getControllerValue(),
				  RotaryPair(rotary, label)));

	    // Connect
	    //
	    connect(rotary, SIGNAL(valueChanged(float)),
		    m_rotaryMapper, SLOT(map()));

	    rmi = m_rotaries.end();
	}

	// Add signal mapping
	//
	m_rotaryMapper->setMapping(rotary,
				   int(it->getControllerValue()));

        count++;
    }

    if (rmi != m_rotaries.end()) {
	for (RotaryMap::iterator rmj = rmi; rmj != m_rotaries.end(); ++rmj) {
	    delete rmj->second.first;
	    delete rmj->second.second;
	}
	m_rotaries = std::vector<std::pair<int, RotaryPair> >
	    (m_rotaries.begin(), rmi);
    }

    m_rotaryFrame->show();
}


void
MIDIInstrumentParameterPanel::setRotaryToValue(int controller, int value)
{
    /*
    RG_DEBUG << "MIDIInstrumentParameterPanel::setRotaryToValue - "
             << "controller = " << controller
             << ", value = " << value << std::endl;
             */

    for (RotaryMap::iterator it = m_rotaries.begin() ; it != m_rotaries.end(); ++it)
    {
        if (it->first == controller)
        {
	    it->second.first->setPosition(float(value));
            return;
        }
    }
}



void
MIDIInstrumentParameterPanel::slotSelectChannel(int index)
{
    if (m_selectedInstrument == 0)
        return;

    m_selectedInstrument->setMidiChannel(index);

    // don't use the emit - use this method instead
    Rosegarden::StudioControl::sendMappedInstrument(
            Rosegarden::MappedInstrument(m_selectedInstrument));
    emit updateAllBoxes();
}

void
MIDIInstrumentParameterPanel::populateBankList()
{
    if (m_selectedInstrument == 0)
	return;

    m_bankValue->clear();
    m_banks.clear();

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateBankList:"
                 << " No MidiDevice for Instrument "
                 << m_selectedInstrument->getId() << endl;
	return;
    }

    int currentBank = -1;
    Rosegarden::BankList banks;

    /*
    RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: "
             << "variation type is " << md->getVariationType() << endl;
             */

    if (md->getVariationType() == MidiDevice::NoVariations) {

	banks = md->getBanks(m_selectedInstrument->isPercussion());

	if (!banks.empty()) {
	    if (m_bankLabel->isHidden()) {
		m_bankLabel->show();
		m_bankCheckBox->show();
		m_bankValue->show();
	    }
	} else {
	    m_bankLabel->hide();
	    m_bankCheckBox->hide();
	    m_bankValue->hide();
	}

	for (unsigned int i = 0; i < banks.size(); ++i) {
	    if (m_selectedInstrument->getProgram().getBank() == banks[i]) {
		currentBank = i;
	    }
	}

    } else {

	Rosegarden::MidiByteList bytes;
	bool useMSB = (md->getVariationType() == MidiDevice::VariationFromLSB);

	if (useMSB) {
	    bytes = md->getDistinctMSBs(m_selectedInstrument->isPercussion());
	} else {
	    bytes = md->getDistinctLSBs(m_selectedInstrument->isPercussion());
	}
	
	if (bytes.size() < 2) {
	    if (!m_bankLabel->isHidden()) {
		m_bankLabel->hide();
		m_bankCheckBox->hide();
		m_bankValue->hide();
	    }
	} else {
	    if (m_bankLabel->isHidden()) {
		m_bankLabel->show();
		m_bankCheckBox->show();
		m_bankValue->show();
	    }
	}

	if (useMSB) {
	    for (unsigned int i = 0; i < bytes.size(); ++i) {
		Rosegarden::BankList bl = md->getBanksByMSB
		    (m_selectedInstrument->isPercussion(), bytes[i]);
		RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: have " << bl.size() << " variations for msb " << bytes[i] << endl;

		if (bl.size() == 0) continue;
		if (m_selectedInstrument->getMSB() == bytes[i]) {
		    currentBank = banks.size();
		}
		banks.push_back(bl[0]);
	    }
	} else {
	    for (unsigned int i = 0; i < bytes.size(); ++i) {
		Rosegarden::BankList bl = md->getBanksByLSB
		    (m_selectedInstrument->isPercussion(), bytes[i]);
		RG_DEBUG << "MIDIInstrumentParameterPanel::populateBankList: have " << bl.size() << " variations for lsb " << bytes[i] << endl;
		if (bl.size() == 0) continue;
		if (m_selectedInstrument->getLSB() == bytes[i]) {
		    currentBank = banks.size();
		}
		banks.push_back(bl[0]);
	    }
	}
    }

    for (Rosegarden::BankList::const_iterator i = banks.begin();
	 i != banks.end(); ++i) {
	m_banks.push_back(*i);
	m_bankValue->insertItem(strtoqstr(i->getName()));
    }

    m_bankValue->setEnabled(m_selectedInstrument->sendsBankSelect());
    
    if (currentBank < 0 && !banks.empty()) {
	m_bankValue->setCurrentItem(0);
	slotSelectBank(0);
    } else {
	m_bankValue->setCurrentItem(currentBank);
    }
}    
	
// Populate program list by bank context
//
void
MIDIInstrumentParameterPanel::populateProgramList()
{
    if (m_selectedInstrument == 0)
        return;

    m_programValue->clear();
    m_programs.clear();

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateProgramList: No MidiDevice for Instrument "
                 << m_selectedInstrument->getId() << endl;
	return;
    }

    /*
    RG_DEBUG << "MIDIInstrumentParameterPanel::populateProgramList:"
             << " variation type is " << md->getVariationType() << endl;
    */

    Rosegarden::MidiBank bank( m_selectedInstrument->isPercussion(), 
                               m_selectedInstrument->getMSB(), 
                               m_selectedInstrument->getLSB());

    if (m_selectedInstrument->sendsBankSelect()) {
	bank = m_selectedInstrument->getProgram().getBank();
    }

    int currentProgram = -1;

    Rosegarden::ProgramList programs = md->getPrograms(bank);

    if (!programs.empty()) {
	if (m_programLabel->isHidden()) {
	    m_programLabel->show();
	    m_programCheckBox->show();
	    m_programValue->show();
	}
    } else {
	m_programLabel->hide();
	m_programCheckBox->hide();
	m_programValue->hide();
    }

    for (unsigned int i = 0; i < programs.size(); ++i) {
	std::string programName = programs[i].getName();
	if (programName != "") {
	    m_programValue->insertItem(QString("%1. %2")
				       .arg(programs[i].getProgram() + 1)
				       .arg(strtoqstr(programName)));
	    if (m_selectedInstrument->getProgram() == programs[i]) {
		currentProgram = m_programs.size();
	    }
	    m_programs.push_back(programs[i]);
	}
    }

    m_programValue->setEnabled(m_selectedInstrument->sendsProgramChange());

    if (currentProgram < 0 && !m_programs.empty()) {
	m_programValue->setCurrentItem(0);
	slotSelectProgram(0);
    } else {
	m_programValue->setCurrentItem(currentProgram);

	// Ensure that stored program change value is same as the one
	// we're now showing (BUG 937371)
	//
	if (!m_programs.empty())
	{
	    m_selectedInstrument->setProgramChange
		((m_programs[m_programValue->currentItem()]).getProgram());
	}
    }
}

void
MIDIInstrumentParameterPanel::populateVariationList()
{
    if (m_selectedInstrument == 0)
	return;

    m_variationValue->clear();
    m_variations.clear();

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::populateVariationList: No MidiDevice for Instrument "
		  << m_selectedInstrument->getId() << endl;
	return;
    }

    /*
    RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList:"
             << " variation type is " << md->getVariationType() << endl;
    */

    if (md->getVariationType() == MidiDevice::NoVariations) {
	if (!m_variationLabel->isHidden()) {
	    m_variationLabel->hide();
	    m_variationCheckBox->hide();
	    m_variationValue->hide();
	}
	return;
    } 

    bool useMSB = (md->getVariationType() == MidiDevice::VariationFromMSB);
    Rosegarden::MidiByteList variations;

    if (useMSB) {
	Rosegarden::MidiByte lsb = m_selectedInstrument->getLSB();
	variations = md->getDistinctMSBs(m_selectedInstrument->isPercussion(),
					 lsb);
	RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList: have " << variations.size() << " variations for lsb " << lsb << endl;

    } else {
	Rosegarden::MidiByte msb = m_selectedInstrument->getMSB();
	variations = md->getDistinctLSBs(m_selectedInstrument->isPercussion(),
					 msb);
	RG_DEBUG << "MIDIInstrumentParameterPanel::populateVariationList: have " << variations.size() << " variations for msb " << msb << endl;
    }
    
    m_variationValue->setCurrentItem(-1);

    Rosegarden::MidiProgram defaultProgram;

    if (useMSB) {
	defaultProgram = Rosegarden::MidiProgram
	    (Rosegarden::MidiBank(m_selectedInstrument->isPercussion(),
				  0,
				  m_selectedInstrument->getLSB()),
	     m_selectedInstrument->getProgramChange());
    } else {
	defaultProgram = Rosegarden::MidiProgram
	    (Rosegarden::MidiBank(m_selectedInstrument->isPercussion(),
				  m_selectedInstrument->getMSB(),
				  0),
	     m_selectedInstrument->getProgramChange());
    }
    std::string defaultProgramName = md->getProgramName(defaultProgram);

    int currentVariation = -1;

    for (unsigned int i = 0; i < variations.size(); ++i) {

	Rosegarden::MidiProgram program;

	if (useMSB) {
	    program = Rosegarden::MidiProgram
		(Rosegarden::MidiBank(m_selectedInstrument->isPercussion(),
				      variations[i],
				      m_selectedInstrument->getLSB()),
		 m_selectedInstrument->getProgramChange());
	} else {
	    program = Rosegarden::MidiProgram
		(Rosegarden::MidiBank(m_selectedInstrument->isPercussion(),
				      m_selectedInstrument->getMSB(),
				      variations[i]),
		 m_selectedInstrument->getProgramChange());
	}

	std::string programName = md->getProgramName(program);

	if (programName != "") { // yes, that is how you know whether it exists
/*
	    m_variationValue->insertItem(programName == defaultProgramName ?
					 i18n("(default)") :
					 strtoqstr(programName));
*/
	    m_variationValue->insertItem(QString("%1. %2")
					 .arg(variations[i] + 1)
					 .arg(strtoqstr(programName)));
	    if (m_selectedInstrument->getProgram() == program) {
		currentVariation = m_variations.size();
	    }
	    m_variations.push_back(variations[i]);
	}
    }

    if (currentVariation < 0 && !m_variations.empty()) {
	m_variationValue->setCurrentItem(0);
	slotSelectVariation(0);
    } else {
	m_variationValue->setCurrentItem(currentVariation);
    }

    if (m_variations.size() < 2) {
	if (!m_variationLabel->isHidden()) {
	    m_variationLabel->hide();
	    m_variationCheckBox->hide();
	    m_variationValue->hide();
	}
	
    } else {
	//!!! seem to have problems here -- the grid layout doesn't
	//like us adding stuff in the middle so if we go from 1
	//visible row (say program) to 2 (program + variation) the
	//second one overlaps the control knobs

	if (m_variationLabel->isHidden()) {
	    m_variationLabel->show();
	    m_variationCheckBox->show();
	    m_variationValue->show();
	}

	if (m_programValue->width() > m_variationValue->width()) {
	    m_variationValue->setMinimumWidth(m_programValue->width());
	} else {
	    m_programValue->setMinimumWidth(m_variationValue->width());
	}
    }

    m_variationValue->setEnabled(m_selectedInstrument->sendsBankSelect());
}


void
MIDIInstrumentParameterPanel::slotTogglePercussion(bool value)
{
    if (m_selectedInstrument == 0)
    {
	m_percussionCheckBox->setChecked(false);
        emit updateAllBoxes();
        return;
    }

    m_selectedInstrument->setPercussion(value);

    populateBankList();
    populateProgramList();
    populateVariationList();

    sendBankAndProgram();

    emit changeInstrumentLabel(m_selectedInstrument->getId(),
			       strtoqstr(m_selectedInstrument->
					 getProgramName()));
    emit updateAllBoxes();
}


void
MIDIInstrumentParameterPanel::slotToggleBank(bool value)
{
    if (m_selectedInstrument == 0)
    {
        m_bankCheckBox->setChecked(false);
        emit updateAllBoxes();
        return;
    }

    m_variationCheckBox->setChecked(value);
    m_selectedInstrument->setSendBankSelect(value);

    m_bankValue->setDisabled(!value);
    populateBankList();
    populateProgramList();
    populateVariationList();

    sendBankAndProgram();

    emit changeInstrumentLabel(m_selectedInstrument->getId(),
			       strtoqstr(m_selectedInstrument->
					 getProgramName()));
    emit updateAllBoxes();
}

void
MIDIInstrumentParameterPanel::slotToggleProgramChange(bool value)
{
    if (m_selectedInstrument == 0)
    {
        m_programCheckBox->setChecked(false);
        emit updateAllBoxes();
        return;
    }

    m_selectedInstrument->setSendProgramChange(value);

    m_programValue->setDisabled(!value);
    populateProgramList();
    populateVariationList();

    if (value) sendBankAndProgram();

    emit changeInstrumentLabel(m_selectedInstrument->getId(),
			       strtoqstr(m_selectedInstrument->
					 getProgramName()));
    emit updateAllBoxes();
}

void
MIDIInstrumentParameterPanel::slotToggleVariation(bool value)
{
    if (m_selectedInstrument == 0)
    {
        m_variationCheckBox->setChecked(false);
        emit updateAllBoxes();
        return;
    }

    m_bankCheckBox->setChecked(value);
    m_selectedInstrument->setSendBankSelect(value);

    m_variationValue->setDisabled(!value);
    populateVariationList();

    sendBankAndProgram();

    emit changeInstrumentLabel(m_selectedInstrument->getId(),
			       strtoqstr(m_selectedInstrument->
					 getProgramName()));
    emit updateAllBoxes();
}

void
MIDIInstrumentParameterPanel::slotSelectBank(int index)
{
    if (m_selectedInstrument == 0)
        return;

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectBank: No MidiDevice for Instrument "
		  << m_selectedInstrument->getId() << endl;
	return;
    }

    const Rosegarden::MidiBank *bank = &m_banks[index];

    bool change = false;

    if (md->getVariationType() != MidiDevice::VariationFromLSB) {
	if (m_selectedInstrument->getLSB() != bank->getLSB()) {
	    m_selectedInstrument->setLSB(bank->getLSB());
	    change = true;
	}
    }
    if (md->getVariationType() != MidiDevice::VariationFromMSB) {
	if (m_selectedInstrument->getMSB() != bank->getMSB()) {
	    m_selectedInstrument->setMSB(bank->getMSB());
	    change = true;
	}
    }

    populateProgramList();

    if (change) {
	sendBankAndProgram();
	emit updateAllBoxes();
    }
}

void
MIDIInstrumentParameterPanel::slotSelectProgram(int index)
{
    const Rosegarden::MidiProgram *prg = &m_programs[index];
    if (prg == 0) {
        RG_DEBUG << "program change not found in bank" << endl;
        return;
    }

    bool change = false;
    if (m_selectedInstrument->getProgramChange() != prg->getProgram()) {
	m_selectedInstrument->setProgramChange(prg->getProgram());
	change = true;
    }

    populateVariationList();

    if (change) {
	sendBankAndProgram();
	emit changeInstrumentLabel(m_selectedInstrument->getId(),
				   strtoqstr(m_selectedInstrument->
					     getProgramName()));
	emit updateAllBoxes();
    }
}

void
MIDIInstrumentParameterPanel::slotSelectVariation(int index)
{
    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectVariation: No MidiDevice for Instrument "
		  << m_selectedInstrument->getId() << endl;
	return;
    }

    if (index < 0 || index > int(m_variations.size())) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::slotSelectVariation: index " << index << " out of range" << endl;
	return;
    }

    Rosegarden::MidiByte v = m_variations[index];
    
    bool change = false;

    if (md->getVariationType() == MidiDevice::VariationFromLSB) {
	if (m_selectedInstrument->getLSB() != v) {
	    m_selectedInstrument->setLSB(v);
	    change = true;
	}
    } else if (md->getVariationType() == MidiDevice::VariationFromMSB) {
	if (m_selectedInstrument->getMSB() != v) {
	    m_selectedInstrument->setMSB(v);
	    change = true;
	}
    }
    
    if (change) {
	sendBankAndProgram();
    }
}

void
MIDIInstrumentParameterPanel::sendBankAndProgram()
{
    if (m_selectedInstrument == 0)
        return;

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) {
	RG_DEBUG << "WARNING: MIDIInstrumentParameterPanel::sendBankAndProgram: No MidiDevice for Instrument "
		  << m_selectedInstrument->getId() << endl;
	return;
    }

    if (m_selectedInstrument->sendsBankSelect()) {

        // Send the bank select message before any PC message
        //
        Rosegarden::MappedEvent mEMSB(m_selectedInstrument->getId(), 
                                      Rosegarden::MappedEvent::MidiController,
                                      Rosegarden::MIDI_CONTROLLER_BANK_MSB,
                                      m_selectedInstrument->getMSB());

	RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
		 << "sending MSB = " 
		 << int(m_selectedInstrument->getMSB()) 
		 << endl;

        Rosegarden::StudioControl::sendMappedEvent(mEMSB);

        Rosegarden::MappedEvent mELSB(m_selectedInstrument->getId(), 
                                      Rosegarden::MappedEvent::MidiController,
                                      Rosegarden::MIDI_CONTROLLER_BANK_LSB,
                                      m_selectedInstrument->getLSB());

	RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
		 << "sending LSB = " 
		 << int(m_selectedInstrument->getLSB()) 
		 << endl;

        Rosegarden::StudioControl::sendMappedEvent(mELSB);
    }

    Rosegarden::MappedEvent mE(m_selectedInstrument->getId(), 
                               Rosegarden::MappedEvent::MidiProgramChange,
                               m_selectedInstrument->getProgramChange(),
                               (Rosegarden::MidiByte)0);

    RG_DEBUG << "MIDIInstrumentParameterPanel::sendBankAndProgram - "
             << "sending program change = " 
             << int(m_selectedInstrument->getProgramChange()) 
             << endl;


    // Send the controller change
    //
    Rosegarden::StudioControl::sendMappedEvent(mE);
}


void
MIDIInstrumentParameterPanel::slotControllerChanged(int controllerNumber)
{

    RG_DEBUG<< "MIDIInstrumentParameterPanel::slotControllerChanged - "
            << "controller = " << controllerNumber << "\n";


    if (m_selectedInstrument == 0)
        return;

    MidiDevice *md = dynamic_cast<MidiDevice*>
	(m_selectedInstrument->getDevice());
    if (!md) return;

    /*
    Rosegarden::ControlParameter *controller = 
	md->getControlParameter(Rosegarden::MidiByte(controllerNumber));
        */

    int value = getValueFromRotary(controllerNumber);

    if (value == -1)
    {
        RG_DEBUG << "MIDIInstrumentParameterPanel::slotControllerChanged - "
                 << "couldn't get value of rotary for controller " 
                 << controllerNumber << endl;
        return;
    }


    // two special cases
    if (controllerNumber == int(Rosegarden::MIDI_CONTROLLER_PAN))
    {
        float adjValue = value;
        if (m_selectedInstrument->getType() == Instrument::Audio ||
	    m_selectedInstrument->getType() == Instrument::SoftSynth)
            value += 100;

        m_selectedInstrument->setPan(Rosegarden::MidiByte(adjValue));
    }
    else if (controllerNumber == int(Rosegarden::MIDI_CONTROLLER_VOLUME))
    {
        m_selectedInstrument->setVolume(Rosegarden::MidiByte(value));
    }
    else // just set the controller (this will create it on the instrument if
         // it doesn't exist)
    {
        m_selectedInstrument->setControllerValue(Rosegarden::MidiByte(controllerNumber),
                                                 Rosegarden::MidiByte(value));

        RG_DEBUG << "SET CONTROLLER VALUE (" << controllerNumber << ") = " << value << endl;
    }
    /*
    else
    {
        RG_DEBUG << "MIDIInstrumentParameterPanel::slotControllerChanged - "
                 << "no controller retrieved\n";
        return;
    }
    */

    Rosegarden::MappedEvent mE(m_selectedInstrument->getId(), 
                               Rosegarden::MappedEvent::MidiController,
                               (Rosegarden::MidiByte)controllerNumber,
                               (Rosegarden::MidiByte)value);
    Rosegarden::StudioControl::sendMappedEvent(mE);

    emit updateAllBoxes();
    emit instrumentParametersChanged(m_selectedInstrument->getId());
    
}

int
MIDIInstrumentParameterPanel::getValueFromRotary(int rotary)
{
    for (RotaryMap::iterator it = m_rotaries.begin(); it != m_rotaries.end(); ++it)
    {
        if (it->first == rotary)
            return int(it->second.first->getPosition());
    }

    return -1;
}

void 
MIDIInstrumentParameterPanel::showAdditionalControls(bool showThem)
{
    m_instrumentLabel->setShown(showThem);
    int index = 0;
    for (RotaryMap::iterator it = m_rotaries.begin(); it != m_rotaries.end(); ++it)
    {
        it->second.first->parentWidget()->setShown(showThem || (index < 8));
        //it->second.first->setShown(showThem || (index < 8));
        //it->second.second->setShown(showThem || (index < 8));
        index++;
    }
}

#include "instrumentparameterbox.moc"
