You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
630 lines
17 KiB
630 lines
17 KiB
/*
|
|
Rosegarden
|
|
A MIDI and audio sequencer and musical notation editor.
|
|
|
|
This program is Copyright 2000-2008
|
|
Guillaume Laurent <glaurent@telegraph-road.org>,
|
|
Chris Cannam <cannam@all-day-breakfast.com>,
|
|
Richard Bown <richard.bown@ferventsoftware.com>
|
|
|
|
The moral rights of Guillaume Laurent, Chris Cannam, and Richard
|
|
Bown to claim authorship of this work have been asserted.
|
|
|
|
Other copyrights also apply to some parts of this work. Please
|
|
see the AUTHORS file and individual file headers for details.
|
|
|
|
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 "MidiProgramsEditor.h"
|
|
#include "MidiBankListViewItem.h"
|
|
#include "NameSetEditor.h"
|
|
#include "misc/Debug.h"
|
|
#include "misc/Strings.h"
|
|
#include "BankEditorDialog.h"
|
|
#include "base/Device.h"
|
|
#include "base/MidiDevice.h"
|
|
#include "base/MidiProgram.h"
|
|
#include "gui/widgets/RosegardenPopupMenu.h"
|
|
#include <kcompletion.h>
|
|
#include <tdeglobal.h>
|
|
#include <klineedit.h>
|
|
#include <tdelocale.h>
|
|
#include <kstddirs.h>
|
|
#include <tqcheckbox.h>
|
|
#include <tqcursor.h>
|
|
#include <tqfile.h>
|
|
#include <tqframe.h>
|
|
#include <tqlabel.h>
|
|
#include <tqlayout.h>
|
|
#include <tqobjectlist.h>
|
|
#include <tqpixmap.h>
|
|
#include <tqpoint.h>
|
|
#include <tqpopupmenu.h>
|
|
#include <tqpushbutton.h>
|
|
#include <tqspinbox.h>
|
|
#include <tqstring.h>
|
|
#include <tqtooltip.h>
|
|
#include <tqvgroupbox.h>
|
|
#include <tqwidget.h>
|
|
#include <algorithm>
|
|
|
|
namespace Rosegarden
|
|
{
|
|
|
|
MidiProgramsEditor::MidiProgramsEditor(BankEditorDialog* bankEditor,
|
|
TQWidget* parent,
|
|
const char* name)
|
|
: NameSetEditor(bankEditor,
|
|
i18n("Bank and Program details"),
|
|
parent, name, i18n("Programs"), true),
|
|
m_device(0),
|
|
m_bankList(bankEditor->getBankList()),
|
|
m_programList(bankEditor->getProgramList()),
|
|
m_oldBank(false, 0, 0)
|
|
{
|
|
TQWidget *additionalWidget = makeAdditionalWidget(m_mainFrame);
|
|
if (additionalWidget) {
|
|
m_mainLayout->addMultiCellWidget(additionalWidget, 0, 2, 0, 2);
|
|
}
|
|
}
|
|
|
|
TQWidget *
|
|
MidiProgramsEditor::makeAdditionalWidget(TQWidget *parent)
|
|
{
|
|
TQFrame *frame = new TQFrame(parent);
|
|
|
|
m_percussion = new TQCheckBox(frame);
|
|
m_msb = new TQSpinBox(frame);
|
|
m_lsb = new TQSpinBox(frame);
|
|
|
|
TQGridLayout *gridLayout = new TQGridLayout(frame,
|
|
3, // rows
|
|
2, // cols
|
|
2); // margin
|
|
|
|
gridLayout->addWidget(new TQLabel(i18n("Percussion"), frame),
|
|
0, 0, AlignLeft);
|
|
gridLayout->addWidget(m_percussion, 0, 1, AlignLeft);
|
|
connect(m_percussion, TQT_SIGNAL(clicked()),
|
|
this, TQT_SLOT(slotNewPercussion()));
|
|
|
|
gridLayout->addWidget(new TQLabel(i18n("MSB Value"), frame),
|
|
1, 0, AlignLeft);
|
|
m_msb->setMinValue(0);
|
|
m_msb->setMaxValue(127);
|
|
gridLayout->addWidget(m_msb, 1, 1, AlignLeft);
|
|
|
|
TQToolTip::add
|
|
(m_msb,
|
|
i18n("Selects a MSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
|
|
|
|
TQToolTip::add
|
|
(m_lsb,
|
|
i18n("Selects a LSB controller Bank number (MSB/LSB pairs are always unique for any Device)"));
|
|
|
|
connect(m_msb, TQT_SIGNAL(valueChanged(int)),
|
|
this, TQT_SLOT(slotNewMSB(int)));
|
|
|
|
gridLayout->addWidget(new TQLabel(i18n("LSB Value"), frame),
|
|
2, 0, AlignLeft);
|
|
m_lsb->setMinValue(0);
|
|
m_lsb->setMaxValue(127);
|
|
gridLayout->addWidget(m_lsb, 2, 1, AlignLeft);
|
|
|
|
connect(m_lsb, TQT_SIGNAL(valueChanged(int)),
|
|
this, TQT_SLOT(slotNewLSB(int)));
|
|
|
|
return frame;
|
|
}
|
|
|
|
ProgramList
|
|
MidiProgramsEditor::getBankSubset(const MidiBank &bank)
|
|
{
|
|
ProgramList program;
|
|
ProgramList::iterator it;
|
|
|
|
for (it = m_programList.begin(); it != m_programList.end(); it++) {
|
|
if (it->getBank() == bank)
|
|
program.push_back(*it);
|
|
}
|
|
|
|
return program;
|
|
}
|
|
|
|
MidiBank*
|
|
MidiProgramsEditor::getCurrentBank()
|
|
{
|
|
return m_currentBank;
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::modifyCurrentPrograms(const MidiBank &oldBank,
|
|
const MidiBank &newBank)
|
|
{
|
|
ProgramList::iterator it;
|
|
|
|
for (it = m_programList.begin(); it != m_programList.end(); it++) {
|
|
if (it->getBank() == oldBank) {
|
|
*it = MidiProgram(newBank, it->getProgram(), it->getName());
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::clearAll()
|
|
{
|
|
blockAllSignals(true);
|
|
|
|
for (unsigned int i = 0; i < m_names.size(); ++i)
|
|
m_names[i]->clear();
|
|
|
|
setTitle(i18n("Bank and Program details"));
|
|
|
|
m_percussion->setChecked(false);
|
|
m_msb->setValue(0);
|
|
m_lsb->setValue(0);
|
|
m_librarian->clear();
|
|
m_librarianEmail->clear();
|
|
m_currentBank = 0;
|
|
setEnabled(false);
|
|
|
|
blockAllSignals(false);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::populate(TQListViewItem* item)
|
|
{
|
|
RG_DEBUG << "MidiProgramsEditor::populate\n";
|
|
|
|
MidiBankListViewItem* bankItem = dynamic_cast<MidiBankListViewItem*>(item);
|
|
if (!bankItem) {
|
|
RG_DEBUG << "MidiProgramsEditor::populate : not a bank item - returning\n";
|
|
return ;
|
|
}
|
|
|
|
DeviceId deviceId = bankItem->getDeviceId();
|
|
m_device = m_bankEditor->getMidiDevice(deviceId);
|
|
if (!m_device)
|
|
return ;
|
|
|
|
setEnabled(true);
|
|
|
|
setBankName(item->text(0));
|
|
|
|
RG_DEBUG << "MidiProgramsEditor::populate : bankItem->getBank = "
|
|
<< bankItem->getBank() << endl;
|
|
|
|
m_currentBank = &(m_bankList[bankItem->getBank()]); // m_device->getBankByIndex(bankItem->getBank());
|
|
|
|
blockAllSignals(true);
|
|
|
|
// set the bank values
|
|
m_percussion->setChecked(m_currentBank->isPercussion());
|
|
m_msb->setValue(m_currentBank->getMSB());
|
|
m_lsb->setValue(m_currentBank->getLSB());
|
|
|
|
m_oldBank = *m_currentBank;
|
|
|
|
// Librarian details
|
|
//
|
|
m_librarian->setText(strtoqstr(m_device->getLibrarianName()));
|
|
m_librarianEmail->setText(strtoqstr(m_device->getLibrarianEmail()));
|
|
|
|
ProgramList programSubset = getBankSubset(*m_currentBank);
|
|
ProgramList::iterator it;
|
|
|
|
TQPixmap noKeyPixmap, keyPixmap;
|
|
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
|
|
TQString file = pixmapDir + "/toolbar/key-white.png";
|
|
if (TQFile(file).exists())
|
|
noKeyPixmap = TQPixmap(file);
|
|
file = pixmapDir + "/toolbar/key-green.png";
|
|
if (TQFile(file).exists())
|
|
keyPixmap = TQPixmap(file);
|
|
|
|
bool haveKeyMappings = m_currentBank->isPercussion()
|
|
&& (m_device->getKeyMappings().size() > 0);
|
|
|
|
for (unsigned int i = 0; i < m_names.size(); i++) {
|
|
m_names[i]->clear();
|
|
getEntryButton(i)->setEnabled(haveKeyMappings);
|
|
getEntryButton(i)->setPixmap(noKeyPixmap);
|
|
TQToolTip::remove
|
|
( getEntryButton(i) );
|
|
|
|
for (it = programSubset.begin(); it != programSubset.end(); it++) {
|
|
if (it->getProgram() == i) {
|
|
|
|
TQString programName = strtoqstr(it->getName());
|
|
m_completion.addItem(programName);
|
|
m_names[i]->setText(programName);
|
|
|
|
if (m_device->getKeyMappingForProgram(*it)) {
|
|
getEntryButton(i)->setPixmap(keyPixmap);
|
|
TQToolTip::add
|
|
(getEntryButton(i),
|
|
i18n("Key Mapping: %1").arg(
|
|
strtoqstr(m_device->getKeyMappingForProgram(*it)->getName())));
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// show start of label
|
|
m_names[i]->setCursorPosition(0);
|
|
}
|
|
|
|
blockAllSignals(false);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::reset()
|
|
{
|
|
m_percussion->blockSignals(true);
|
|
m_msb->blockSignals(true);
|
|
m_lsb->blockSignals(true);
|
|
|
|
m_percussion->setChecked(m_oldBank.isPercussion());
|
|
m_msb->setValue(m_oldBank.getMSB());
|
|
m_lsb->setValue(m_oldBank.getLSB());
|
|
|
|
if (m_currentBank) {
|
|
modifyCurrentPrograms(*m_currentBank, m_oldBank);
|
|
*m_currentBank = m_oldBank;
|
|
}
|
|
|
|
m_percussion->blockSignals(false);
|
|
m_msb->blockSignals(false);
|
|
m_lsb->blockSignals(false);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::slotNewPercussion()
|
|
{
|
|
RG_DEBUG << "MidiProgramsEditor::slotNewPercussion" << endl;
|
|
bool percussion = m_percussion->isChecked();
|
|
m_percussion->blockSignals(true);
|
|
if (banklistContains(MidiBank(percussion, m_msb->value(), m_lsb->value()))) {
|
|
RG_DEBUG << "MidiProgramsEditor::slotNewPercussion: calling setChecked(" << !percussion << ")" << endl;
|
|
m_percussion->setChecked(!percussion);
|
|
} else {
|
|
MidiBank newBank(percussion,
|
|
m_msb->value(),
|
|
m_lsb->value());
|
|
modifyCurrentPrograms(*getCurrentBank(), newBank);
|
|
*getCurrentBank() = newBank;
|
|
}
|
|
m_percussion->blockSignals(false);
|
|
m_bankEditor->setModified(true);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::slotNewMSB(int value)
|
|
{
|
|
RG_DEBUG << "MidiProgramsEditor::slotNewMSB(" << value << ")\n";
|
|
|
|
m_msb->blockSignals(true);
|
|
|
|
int msb;
|
|
|
|
try {
|
|
msb = ensureUniqueMSB(value, value > getCurrentBank()->getMSB());
|
|
} catch (bool) {
|
|
msb = getCurrentBank()->getMSB();
|
|
}
|
|
|
|
MidiBank newBank(m_percussion->isChecked(),
|
|
msb,
|
|
m_lsb->value());
|
|
|
|
modifyCurrentPrograms(*getCurrentBank(), newBank);
|
|
|
|
m_msb->setValue(msb);
|
|
*getCurrentBank() = newBank;
|
|
|
|
m_msb->blockSignals(false);
|
|
|
|
m_bankEditor->setModified(true);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::slotNewLSB(int value)
|
|
{
|
|
RG_DEBUG << "MidiProgramsEditor::slotNewLSB(" << value << ")\n";
|
|
|
|
m_lsb->blockSignals(true);
|
|
|
|
int lsb;
|
|
|
|
try {
|
|
lsb = ensureUniqueLSB(value, value > getCurrentBank()->getLSB());
|
|
} catch (bool) {
|
|
lsb = getCurrentBank()->getLSB();
|
|
}
|
|
|
|
MidiBank newBank(m_percussion->isChecked(),
|
|
m_msb->value(),
|
|
lsb);
|
|
|
|
modifyCurrentPrograms(*getCurrentBank(), newBank);
|
|
|
|
m_lsb->setValue(lsb);
|
|
*getCurrentBank() = newBank;
|
|
|
|
m_lsb->blockSignals(false);
|
|
|
|
m_bankEditor->setModified(true);
|
|
}
|
|
|
|
struct ProgramCmp
|
|
{
|
|
bool operator()(const Rosegarden::MidiProgram &p1,
|
|
const Rosegarden::MidiProgram &p2)
|
|
{
|
|
if (p1.getProgram() == p2.getProgram()) {
|
|
const Rosegarden::MidiBank &b1(p1.getBank());
|
|
const Rosegarden::MidiBank &b2(p2.getBank());
|
|
if (b1.getMSB() == b2.getMSB())
|
|
if (b1.getLSB() == b2.getLSB())
|
|
return ((b1.isPercussion() ? 1 : 0) < (b2.isPercussion() ? 1 : 0));
|
|
else return (b1.getLSB() < b2.getLSB());
|
|
else return (b1.getMSB() < b2.getMSB());
|
|
} else return (p1.getProgram() < p2.getProgram());
|
|
}
|
|
};
|
|
|
|
void
|
|
MidiProgramsEditor::slotNameChanged(const TQString& programName)
|
|
{
|
|
const KLineEdit* lineEdit = dynamic_cast<const KLineEdit*>(sender());
|
|
if (!lineEdit) {
|
|
RG_DEBUG << "MidiProgramsEditor::slotProgramChanged() : %%% ERROR - signal sender is not a KLineEdit\n";
|
|
return ;
|
|
}
|
|
|
|
TQString senderName = sender()->name();
|
|
|
|
// Adjust value back to zero rated
|
|
//
|
|
unsigned int id = senderName.toUInt() - 1;
|
|
|
|
RG_DEBUG << "MidiProgramsEditor::slotNameChanged("
|
|
<< programName << ") : id = " << id << endl;
|
|
|
|
MidiProgram *program = getProgram(*getCurrentBank(), id);
|
|
|
|
if (program == 0) {
|
|
// Do nothing if program name is empty
|
|
if (programName.isEmpty())
|
|
return ;
|
|
|
|
program = new MidiProgram(*getCurrentBank(), id);
|
|
m_programList.push_back(*program);
|
|
|
|
// Sort the program list by id
|
|
std::sort(m_programList.begin(), m_programList.end(), ProgramCmp());
|
|
|
|
// Now, get with the program
|
|
//
|
|
program = getProgram(*getCurrentBank(), id);
|
|
} else {
|
|
// If we've found a program and the label is now empty
|
|
// then remove it from the program list.
|
|
//
|
|
if (programName.isEmpty()) {
|
|
ProgramList::iterator it = m_programList.begin();
|
|
ProgramList tmpProg;
|
|
|
|
for (; it != m_programList.end(); it++) {
|
|
if (((unsigned int)it->getProgram()) == id) {
|
|
m_programList.erase(it);
|
|
m_bankEditor->setModified(true);
|
|
RG_DEBUG << "deleting empty program (" << id << ")" << endl;
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (qstrtostr(programName) != program->getName()) {
|
|
program->setName(qstrtostr(programName));
|
|
m_bankEditor->setModified(true);
|
|
}
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::slotEntryButtonPressed()
|
|
{
|
|
TQPushButton* button = dynamic_cast<TQPushButton*>(TQT_TQWIDGET(const_cast<TQObject*>(sender())));
|
|
if (!button) {
|
|
RG_DEBUG << "MidiProgramsEditor::slotEntryButtonPressed() : %%% ERROR - signal sender is not a TQPushButton\n";
|
|
return ;
|
|
}
|
|
|
|
TQString senderName = button->name();
|
|
|
|
if (!m_device)
|
|
return ;
|
|
|
|
const KeyMappingList &kml = m_device->getKeyMappings();
|
|
if (kml.empty())
|
|
return ;
|
|
|
|
// Adjust value back to zero rated
|
|
//
|
|
unsigned int id = senderName.toUInt() - 1;
|
|
MidiProgram *program = getProgram(*getCurrentBank(), id);
|
|
if (!program)
|
|
return ;
|
|
m_currentMenuProgram = id;
|
|
|
|
RosegardenPopupMenu *menu = new RosegardenPopupMenu(button);
|
|
|
|
const MidiKeyMapping *currentMapping =
|
|
m_device->getKeyMappingForProgram(*program);
|
|
int currentEntry = 0;
|
|
|
|
menu->insertItem(i18n("<no key mapping>"), this,
|
|
TQT_SLOT(slotEntryMenuItemSelected(int)), 0, 0);
|
|
menu->setItemParameter(0, 0);
|
|
|
|
for (int i = 0; i < kml.size(); ++i) {
|
|
menu->insertItem(strtoqstr(kml[i].getName()),
|
|
this, TQT_SLOT(slotEntryMenuItemSelected(int)),
|
|
0, i + 1);
|
|
menu->setItemParameter(i + 1, i + 1);
|
|
if (currentMapping && (kml[i] == *currentMapping))
|
|
currentEntry = i + 1;
|
|
}
|
|
|
|
int itemHeight = menu->itemHeight(0) + 2;
|
|
TQPoint pos = TQCursor::pos();
|
|
|
|
pos.rx() -= 10;
|
|
pos.ry() -= (itemHeight / 2 + currentEntry * itemHeight);
|
|
|
|
menu->popup(pos);
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::slotEntryMenuItemSelected(int i)
|
|
{
|
|
if (!m_device)
|
|
return ;
|
|
|
|
const KeyMappingList &kml = m_device->getKeyMappings();
|
|
if (kml.empty())
|
|
return ;
|
|
|
|
MidiProgram *program = getProgram(*getCurrentBank(), m_currentMenuProgram);
|
|
if (!program)
|
|
return ;
|
|
|
|
std::string newMapping;
|
|
|
|
if (i == 0) { // no key mapping
|
|
newMapping = "";
|
|
} else {
|
|
--i;
|
|
if (i < kml.size()) {
|
|
newMapping = kml[i].getName();
|
|
}
|
|
}
|
|
|
|
m_device->setKeyMappingForProgram(*program, newMapping);
|
|
TQString pixmapDir = TDEGlobal::dirs()->findResource("appdata", "pixmaps/");
|
|
bool haveKeyMappings = (m_device->getKeyMappings().size() > 0);
|
|
TQPushButton *btn = getEntryButton(m_currentMenuProgram);
|
|
|
|
if (newMapping.empty()) {
|
|
TQString file = pixmapDir + "/toolbar/key-white.png";
|
|
if (TQFile(file).exists()) {
|
|
btn->setPixmap(TQPixmap(file));
|
|
}
|
|
TQToolTip::remove
|
|
(btn);
|
|
} else {
|
|
TQString file = pixmapDir + "/toolbar/key-green.png";
|
|
if (TQFile(file).exists()) {
|
|
btn->setPixmap(TQPixmap(file));
|
|
}
|
|
TQToolTip::add
|
|
(btn, i18n("Key Mapping: %1").arg(strtoqstr(newMapping)));
|
|
}
|
|
btn->setEnabled(haveKeyMappings);
|
|
}
|
|
|
|
int
|
|
MidiProgramsEditor::ensureUniqueMSB(int msb, bool ascending)
|
|
{
|
|
int newMSB = msb;
|
|
while (banklistContains(MidiBank(m_percussion->isChecked(),
|
|
newMSB, m_lsb->value()))
|
|
&& newMSB < 128
|
|
&& newMSB > -1)
|
|
if (ascending)
|
|
newMSB++;
|
|
else
|
|
newMSB--;
|
|
|
|
if (newMSB == -1 || newMSB == 128)
|
|
throw false;
|
|
|
|
return newMSB;
|
|
}
|
|
|
|
int
|
|
MidiProgramsEditor::ensureUniqueLSB(int lsb, bool ascending)
|
|
{
|
|
int newLSB = lsb;
|
|
while (banklistContains(MidiBank(m_percussion->isChecked(),
|
|
m_msb->value(), newLSB))
|
|
&& newLSB < 128
|
|
&& newLSB > -1)
|
|
if (ascending)
|
|
newLSB++;
|
|
else
|
|
newLSB--;
|
|
|
|
if (newLSB == -1 || newLSB == 128)
|
|
throw false;
|
|
|
|
return newLSB;
|
|
}
|
|
|
|
bool
|
|
MidiProgramsEditor::banklistContains(const MidiBank &bank)
|
|
{
|
|
BankList::iterator it;
|
|
|
|
for (it = m_bankList.begin(); it != m_bankList.end(); it++)
|
|
if (*it == bank)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
MidiProgram*
|
|
MidiProgramsEditor::getProgram(const MidiBank &bank, int programNo)
|
|
{
|
|
ProgramList::iterator it = m_programList.begin();
|
|
|
|
for (; it != m_programList.end(); it++) {
|
|
if (it->getBank() == bank && it->getProgram() == programNo)
|
|
return &(*it);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
MidiProgramsEditor::setBankName(const TQString& s)
|
|
{
|
|
setTitle(s);
|
|
}
|
|
|
|
void MidiProgramsEditor::blockAllSignals(bool block)
|
|
{
|
|
const TQObjectList* allChildren = queryList("KLineEdit", "[0-9]+");
|
|
TQObjectListIt it(*allChildren);
|
|
TQObject *obj;
|
|
|
|
while ( (obj = it.current()) != 0 ) {
|
|
obj->blockSignals(block);
|
|
++it;
|
|
}
|
|
|
|
m_msb->blockSignals(block);
|
|
m_lsb->blockSignals(block);
|
|
}
|
|
|
|
}
|
|
#include "MidiProgramsEditor.moc"
|