bool
ImportDeviceDialog::importFromRG(QString fileName)
{
    bool skipAutoload = true, clearCommandHistory = false;
    RosegardenDocument fileDoc(RosegardenMainWindow::self(), 0, 
                               skipAutoload, clearCommandHistory);

    if (!fileDoc.openDocument(fileName, false)) {
        return false;
    }

    for (int i = 0; i < (int)m_devices.size(); ++i) delete m_devices[i];
    m_devices.clear();

    DeviceList *list = fileDoc.getStudio().getDevices();
    if (list->size() == 0) {
        return true; // true because we successfully read the document
    }

    for (DeviceListIterator it = list->begin(); it != list->end(); ++it) {

        MidiDevice *device = dynamic_cast<MidiDevice*>(*it);

        if (device) {
            std::vector<MidiBank> banks = device->getBanks();

            // DMM - check for controllers too, because some users have
            // created .rgd files that contain only controllers
            // see bug #1183522
            //
            std::vector<ControlParameter> controllers =
                device->getControlParameters();

            // We've got a bank on a Device fom this file
            // (or a device that contains controllers or key mappings)
            //
            if (banks.size() ||
                controllers.size() ||
                device->getKeyMappings().size()) {
                m_devices.push_back(new MidiDevice(*device));
            }
        }
    }

    return true;
}
void
ModifyDeviceCommand::execute()
{
    Device *device = m_studio->getDevice(m_device);
    if (!device) {
        std::cerr << "ERROR: ModifyDeviceCommand::execute(): no such device as " << m_device << std::endl;
        return;
    }

    MidiDevice *midiDevice = dynamic_cast<MidiDevice *>(device);
    if (!midiDevice) {
        std::cerr << "ERROR: ModifyDeviceCommand::execute(): device " << m_device << " is not a MIDI device" << std::endl;
        return;
    }

    // Save Original Values for Undo

    // ??? Really wish we could just m_oldDevice = *(midiDevice).  See below.
    m_oldName = midiDevice->getName();
    m_oldBankList = midiDevice->getBanks();
    m_oldProgramList = midiDevice->getPrograms();
    m_oldControlList = midiDevice->getControlParameters();
    m_oldKeyMappingList = midiDevice->getKeyMappings();
    m_oldLibrarianName = midiDevice->getLibrarianName();
    m_oldLibrarianEmail = midiDevice->getLibrarianEmail();
    m_oldVariationType = midiDevice->getVariationType();
    InstrumentList instruments = midiDevice->getAllInstruments();
    for (size_t i = 0; i < instruments.size(); ++i) {
        // ??? Preserving just the programs isn't enough.  We need
        //     to preserve the rest of the Instrument as well.  However,
        //     the auto/fixed channel feature has made it impossible
        //     to safely make copies of Instrument objects.  Also, Instrument
        //     has an ID.  How should that be handled for undo?  ISTM
        //     that we either need to introduce some sort of copyForUndo()
        //     hack to each object, or develop a set of standards for coding
        //     objects that are undo-safe.  Sounds like a pretty big project.
        m_oldInstrumentPrograms.push_back(instruments[i]->getProgram());
    }

    // Make the Changes

    if (m_changeVariation)
        midiDevice->setVariationType(m_variationType);

    if (m_overwrite) {
        if (m_clearBankAndProgramList) {
            midiDevice->clearBankList();
            midiDevice->clearProgramList();
            midiDevice->clearKeyMappingList();
        } else {
            if (m_changeBanks)
                midiDevice->replaceBankList(m_bankList);
            if (m_changePrograms)
                midiDevice->replaceProgramList(m_programList);
            if (m_changeBanks || m_changePrograms) {
                // Make sure the instruments make sense.
                for (size_t i = 0; i < instruments.size(); ++i) {
                    instruments[i]->pickFirstProgram(
                            midiDevice->isPercussionNumber(i));
                }
            }
        }

        if (m_changeKeyMappings) {
            midiDevice->replaceKeyMappingList(m_keyMappingList);
        }

        if (m_rename)
            midiDevice->setName(m_name);
        midiDevice->setLibrarian(m_librarianName, m_librarianEmail);
    } else {
        if (m_clearBankAndProgramList) {
            midiDevice->clearBankList();
            midiDevice->clearProgramList();
        } else {
            if (m_changeBanks)
                midiDevice->mergeBankList(m_bankList);
            if (m_changePrograms)
                midiDevice->mergeProgramList(m_programList);
        }

        if (m_changeKeyMappings) {
            midiDevice->mergeKeyMappingList(m_keyMappingList);
        }

        if (m_rename) {
            std::string mergeName = midiDevice->getName() +
                                    std::string("/") + m_name;
            midiDevice->setName(mergeName);
        }
    }

    //!!! merge option?
    if (m_changeControls) {
        midiDevice->replaceControlParameters(m_controlList);
    }

    // ??? Instead of this kludge, we should be calling a Studio::hasChanged()
    //     which would then notify all observers (e.g. MIPP) who, in turn,
    //     would update themselves.
    RosegardenMainWindow::self()->uiUpdateKludge();
}
void
MIDIInstrumentParameterPanel::updateBankComboBox()
{
    RG_DEBUG << "updateBankComboBox()";

    if (!getSelectedInstrument())
        return;

    MidiDevice *md =
            dynamic_cast<MidiDevice *>(getSelectedInstrument()->getDevice());
    if (!md) {
        std::cerr << "WARNING: MIDIInstrumentParameterPanel::updateBankComboBox(): No MidiDevice for Instrument " << getSelectedInstrument()->getId() << '\n';
        return;
    }

    int currentBank = -1;
    BankList banks;

    RG_DEBUG << "updateBankComboBox(): Variation type is " << md->getVariationType();

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

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

        // If there are banks to display, show the bank widgets.
        // Why not showBank(banks.size()>1)?  Because that would hide the
        // bank checkbox which would take away the user's ability to
        // enable/disable bank selects.  If we do away with the checkbox
        // in the future, we should re-evaluate this decision.
        showBank(!banks.empty());

        // Find the selected bank in the MIDI Device's bank list.
        for (unsigned int i = 0; i < banks.size(); ++i) {
            if (getSelectedInstrument()->getProgram().getBank().partialCompare(banks[i])) {
                currentBank = i;
                break;
            }
        }

    } else {

        // Usually in variation mode, the bank widgets will be hidden.
        // E.g. in GM2, the MSB for all banks is 121 with the variations
        // in the LSB numbered 0-9.  If, however, there were another
        // MSB, say 122, with some variations in the LSB, this code would
        // display the Bank combobox to allow selection of the MSB.

        // If the variations are in the LSB, then the banks are in the MSB
        // and vice versa.
        bool useMSB = (md->getVariationType() == MidiDevice::VariationFromLSB);

        MidiByteList bytes;

        if (useMSB) {
            bytes = md->getDistinctMSBs(getSelectedInstrument()->isPercussion());
        } else {
            bytes = md->getDistinctLSBs(getSelectedInstrument()->isPercussion());
        }

        // If more than one bank value is found, show the bank widgets.
        showBank(bytes.size() > 1);

        // Load "banks" with the banks and figure out currentBank.

        if (useMSB) {
            for (unsigned int i = 0; i < bytes.size(); ++i) {
                BankList bl = md->getBanksByMSB
                              (getSelectedInstrument()->isPercussion(), bytes[i]);
                RG_DEBUG << "updateBankComboBox(): Have " << bl.size() << " variations for MSB " << bytes[i];

                if (bl.size() == 0)
                    continue;
                if (getSelectedInstrument()->getMSB() == bytes[i]) {
                    currentBank = banks.size();
                }
                banks.push_back(bl[0]);
            }
        } else {
            for (unsigned int i = 0; i < bytes.size(); ++i) {
                BankList bl = md->getBanksByLSB
                              (getSelectedInstrument()->isPercussion(), bytes[i]);

                RG_DEBUG << "updateBankComboBox(): Have " << bl.size() << " variations for LSB " << bytes[i];

                if (bl.size() == 0)
                    continue;
                if (getSelectedInstrument()->getLSB() == bytes[i]) {
                    currentBank = banks.size();
                }
                banks.push_back(bl[0]);
            }
        }
    }

    // Populate the combobox with bank names.

    // If we need to repopulate m_bankComboBox
    if (banks != m_banks)
    {
        // Update the cache.
        m_banks = banks;

        // Copy from m_banks to m_bankComboBox.
        m_bankComboBox->clear();
        for (BankList::const_iterator i = m_banks.begin();
                i != m_banks.end(); ++i) {
            m_bankComboBox->addItem(QObject::tr(i->getName().c_str()));
        }
    }

    m_bankComboBox->setEnabled(getSelectedInstrument()->sendsBankSelect());

#if 0
// ??? This is a pretty nifty idea, but unfortunately, it requires
//     that we maintain a bogus combobox entry.  For now, we'll go
//     with the simpler "unselected" approach.

    // If the current bank was not found...
    if (currentBank < 0  &&  !banks.empty()) {
        // Format bank MSB:LSB and add to combobox.
        MidiBank bank = getSelectedInstrument()->getProgram().getBank();
        QString bankString = QString("%1:%2").arg(bank.getMSB()).arg(bank.getLSB());
        m_bankComboBox.addItem(bankString);
        currentBank = banks.size();
    }
#endif

    // If the bank wasn't in the Device, show the bank widgets so
    // the user can fix it if they want.
    if (currentBank == -1  &&  !banks.empty())
        showBank(true);

    // Display the current bank.
    m_bankComboBox->setCurrentIndex(currentBank);
}