--- qsampler/trunk/src/qsamplerInstrumentList.cpp 2006/12/07 10:36:26 971 +++ qsampler/trunk/src/qsamplerInstrumentList.cpp 2007/11/01 17:14:21 1464 @@ -1,7 +1,8 @@ // qsamplerInstrumentList.cpp // /**************************************************************************** - Copyright (C) 2003-2005, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2003-2007, rncbc aka Rui Nuno Capela. All rights reserved. + Copyright (C) 2007, Christian Schoenebeck This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -25,35 +26,38 @@ #include "qsamplerInstrument.h" #include "qsamplerInstrumentForm.h" +#include "qsamplerOptions.h" #include "qsamplerMainForm.h" +#include +#include +#include #include +#include #include -#include + +#include // Needed for lroundf() #include -#ifdef __BORLANDC__ -static long lroundf ( float fval ) +#ifndef CONFIG_ROUND +static inline long lroundf ( float x ) { - double fint = 0.0; - float frac = float(::modf(fval, &fint)); - long lint = long(fint); - if (frac >= +0.5f) - lint++; - else - if (frac <= -0.5f) - lint--; - return lint; + if (x >= 0.0f) + return long(x + 0.5f); + else + return long(x - 0.5f); } #endif +using namespace QSampler; //---------------------------------------------------------------------- // class qsamplerInstrumentGroup -- custom group list view item. // +#if 0 // Constructors. qsamplerInstrumentGroup::qsamplerInstrumentGroup ( qsamplerInstrumentList *pListView, const QString& sName, @@ -62,7 +66,7 @@ { QListViewItem::setRenameEnabled(0, true); - QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemGroup.png")); + QListViewItem::setPixmap(0, QPixmap(":/icons/itemGroup.png")); QListViewItem::setText(0, sName); } @@ -73,7 +77,7 @@ { QListViewItem::setRenameEnabled(0, true); - QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemGroup.png")); + QListViewItem::setPixmap(0, QPixmap(":/icons/itemGroup.png")); } @@ -116,8 +120,8 @@ { // Set the proper pixmap of this... if (rtti() == qsamplerInstrumentList::Group) { - QListViewItem::setPixmap(0, QPixmap::fromMimeSource( - bOpen ? "itemGroupOpen.png" : "itemGroup.png")); + QListViewItem::setPixmap(0, QPixmap(bOpen ? + ":/icons/itemGroupOpen.png" : ":/icons/itemGroup.png")); } // Open it up... QListViewItem::setOpen(bOpen); @@ -187,17 +191,18 @@ // Item refreshment. void qsamplerInstrumentItem::update (void) { - QListViewItem::setPixmap(0, QPixmap::fromMimeSource("itemFile.png")); + QListViewItem::setPixmap(0, QPixmap(":/icons/itemFile.png")); const QString s = "-"; if (m_pInstrument) { setText(0, m_pInstrument->name()); - setText(1, QString::number(m_pInstrument->bank())); - setText(2, QString::number(m_pInstrument->program() + 1)); - setText(3, m_pInstrument->engineName()); - setText(4, QFileInfo(m_pInstrument->instrumentFile()).fileName()); - setText(5, QString::number(m_pInstrument->instrumentNr())); - setText(6, QString::number(::lroundf(100.0f * m_pInstrument->volume()))); + setText(1, QString::number(m_pInstrument->map())); + setText(2, QString::number(m_pInstrument->bank())); + setText(3, QString::number(m_pInstrument->prog() + 1)); + setText(4, m_pInstrument->engineName()); + setText(5, QFileInfo(m_pInstrument->instrumentFile()).fileName()); + setText(6, QString::number(m_pInstrument->instrumentNr())); + setText(7, QString::number(::lroundf(100.0f * m_pInstrument->volume()))); QString sLoadMode = s; switch (m_pInstrument->loadMode()) { case 3: @@ -210,7 +215,7 @@ sLoadMode = QObject::tr("On Demand"); break; } - setText(7, sLoadMode); + setText(8, sLoadMode); } else { for (int i = 0; i < listView()->columns(); i++) setText(i, s); @@ -227,7 +232,10 @@ QWidget *pParent, const char *pszName ) : QListView(pParent, pszName) { + m_iMidiMap = LSCP_MIDI_MAP_ALL; + // QListView::setRootIsDecorated(true); + QListView::setAllColumnsShowFocus(true); QListView::setResizeMode(QListView::NoColumn); // QListView::setAcceptDrops(true); QListView::setDragAutoScroll(true); @@ -237,6 +245,7 @@ QListView::setSortColumn(-1); QListView::addColumn(tr("Name")); + QListView::addColumn(tr("Map")); QListView::addColumn(tr("Bank")); QListView::addColumn(tr("Prog")); QListView::addColumn(tr("Engine")); @@ -245,20 +254,38 @@ QListView::addColumn(tr("Vol")); QListView::addColumn(tr("Mode")); - QListView::setColumnAlignment(1, Qt::AlignHCenter); // Bank - QListView::setColumnAlignment(2, Qt::AlignHCenter); // Prog - QListView::setColumnAlignment(5, Qt::AlignHCenter); // Nr - QListView::setColumnAlignment(6, Qt::AlignHCenter); // Vol - - QListView::setColumnWidth(0, 60); // Name - QListView::setColumnWidth(0, 120); // File - - m_pNewGroupAction = new QAction(tr("New &Group"), tr("Ctrl+G"), this); - m_pNewItemAction = new QAction(tr("New &Instrument..."), tr("Ctrl+I"), this); - m_pEditItemAction = new QAction(tr("&Edit..."), tr("Ctrl+E"), this); - m_pRenameAction = new QAction(tr("&Rename"), tr("Ctrl+R"), this); - m_pDeleteAction = new QAction(tr("&Delete"), tr("Ctrl+D"), this); - m_pRefreshAction = new QAction(tr("Re&fresh"), tr("Ctrl+F"), this); + QListView::setColumnAlignment(1, Qt::AlignHCenter); // Map + QListView::setColumnAlignment(2, Qt::AlignHCenter); // Bank + QListView::setColumnAlignment(3, Qt::AlignHCenter); // Prog + QListView::setColumnAlignment(6, Qt::AlignHCenter); // Nr + QListView::setColumnAlignment(7, Qt::AlignHCenter); // Vol + + QListView::setColumnWidth(0, 120); // Name + QListView::setColumnWidth(5, 240); // File + + m_pNewGroupAction = new QAction( + QIcon(":/icons/itemGroupNew.png"), + tr("New &Group"), tr("Ctrl+G"), this); + m_pNewItemAction = new QAction( + QIcon(":/icons/itemNew.png"), + tr("New &Instrument..."), tr("Ins"), this); + m_pEditItemAction = new QAction( + QIcon(":/icons/formEdit.png"), + tr("&Edit..."), tr("Enter"), this); + m_pRenameAction = new QAction(tr("&Rename"), tr("F2"), this); + m_pDeleteAction = new QAction( + QIcon(":/icons/formRemove.png"), + tr("&Delete"), tr("Del"), this); + m_pRefreshAction = new QAction( + QIcon(":/icons/formRefresh.png"), + tr("Re&fresh"), tr("F5"), this); + + m_pNewGroupAction->setToolTip(tr("New Group")); + m_pNewItemAction->setToolTip(tr("New Instrument")); + m_pEditItemAction->setToolTip(tr("Edit")); + m_pRenameAction->setToolTip(tr("Rename")); + m_pDeleteAction->setToolTip(tr("Delete")); + m_pRefreshAction->setToolTip(tr("Refresh")); QObject::connect(m_pNewGroupAction, SIGNAL(activated()), @@ -312,14 +339,31 @@ qsamplerInstrument *pInstrument, qsamplerInstrumentGroup *pParentGroup ) { + // Check it there's already one instrument item + // with the very same key (bank, program); + // if yes, just remove it without prejudice... qsamplerInstrumentItem *pItem = findItem(pInstrument); - if (pItem == NULL) { - if (pParentGroup) - pItem = new qsamplerInstrumentItem(pParentGroup, pInstrument); - else - pItem = new qsamplerInstrumentItem(this, pInstrument); + if (pItem) { + // If exactly the same, just update view and bail out... + if (pItem->instrument() == pInstrument) { + pItem->update(); + return pItem; + } + // Remove it, as instrument keys must be unique. + delete pItem; } + + // Add the new item under proper group one, if any... + if (pParentGroup) { + pParentGroup->setOpen(true); + pItem = new qsamplerInstrumentItem(pParentGroup, pInstrument); + } else { + pItem = new qsamplerInstrumentItem(this, pInstrument); + } + + // Set it as current selection... QListView::setSelected(pItem, true); + return pItem; } @@ -330,10 +374,12 @@ { qsamplerInstrumentGroup *pGroup = findGroup(sName); if (pGroup == NULL) { - if (pParentGroup) + if (pParentGroup) { + pParentGroup->setOpen(true); pGroup = new qsamplerInstrumentGroup(pParentGroup, sName); - else + } else { pGroup = new qsamplerInstrumentGroup(this, sName); + } } QListView::setSelected(pGroup, true); return pGroup; @@ -372,8 +418,9 @@ qsamplerInstrumentItem *pItem = static_cast (pListItem); if (pItem && pItem->instrument() + && pItem->instrument()->map() == pInstrument->map() && pItem->instrument()->bank() == pInstrument->bank() - && pItem->instrument()->program() == pInstrument->program()) + && pItem->instrument()->prog() == pInstrument->prog()) return pItem; } ++iter; @@ -396,12 +443,8 @@ // Add a new group item below the current one. void qsamplerInstrumentList::newGroupSlot (void) { - qsamplerInstrumentGroup *pParentGroup - = groupItem(QListView::selectedItem()); qsamplerInstrumentGroup *pNewGroup - = addGroup(tr("New Group"), pParentGroup); - if (pParentGroup) - pParentGroup->setOpen(true); + = addGroup(tr("New Group"), groupItem(QListView::selectedItem())); if (pNewGroup) pNewGroup->startRename(0); @@ -409,6 +452,53 @@ } +// Map selector. +void qsamplerInstrumentList::setMidiMap ( int iMidiMap ) +{ + if (iMidiMap < 0) + iMidiMap = LSCP_MIDI_MAP_ALL; + + m_iMidiMap = iMidiMap; +} + +int qsamplerInstrumentList::midiMap (void) const +{ + return m_iMidiMap; +} + + +// List actions accessors. +QAction *qsamplerInstrumentList::newGroupAction (void) const +{ + return m_pNewGroupAction; +} + +QAction *qsamplerInstrumentList::newItemAction (void) const +{ + return m_pNewItemAction; +} + +QAction *qsamplerInstrumentList::editItemAction (void) const +{ + return m_pEditItemAction; +} + +QAction *qsamplerInstrumentList::renameAction (void) const +{ + return m_pRenameAction; +} + +QAction *qsamplerInstrumentList::deleteAction (void) const +{ + return m_pDeleteAction; +} + +QAction *qsamplerInstrumentList::refreshAction (void) const +{ + return m_pRefreshAction; +} + + // Add a new instrument item below the current one. void qsamplerInstrumentList::newItemSlot (void) { @@ -421,22 +511,13 @@ return; } - // Check it there's already one instrument item - // with the very same key (bank, program); - // if yes, just remove it without prejudice... - qsamplerInstrumentItem *pItem = findItem(pInstrument); - if (pItem) - delete pItem; - - pInstrument->map(); + // Commit... + pInstrument->mapInstrument(); + // add new item to the tree... + addItem(pInstrument, groupItem(QListView::selectedItem())); + // Notify we've changes... emit instrumentsChanged(); - qsamplerInstrumentGroup *pParentGroup - = groupItem(QListView::selectedItem()); - addItem(pInstrument, pParentGroup); - if (pParentGroup) - pParentGroup->setOpen(true); - selectionChangedSlot(); } @@ -448,15 +529,40 @@ if (pListItem == NULL) return; if (pListItem->rtti() == Item) { + qsamplerInstrument *pInstrument = NULL; qsamplerInstrumentItem *pItem = static_cast (pListItem); - if (pItem && pItem->instrument()) { + if (pItem) + pInstrument = pItem->instrument(); + if (pInstrument) { + // Save current key values... + qsamplerInstrument oldInstrument(*pInstrument); + // Do the edit dance... qsamplerInstrumentForm form(this); - form.setup(pItem->instrument()); + form.setup(pInstrument); if (form.exec()) { - pItem->instrument()->map(); + // Commit... + pInstrument->mapInstrument(); + // Check whether we changed instrument key... + if (oldInstrument.map() == pInstrument->map() && + oldInstrument.bank() == pInstrument->bank() && + oldInstrument.prog() == pInstrument->prog()) { + // just update tree item... + pItem->update(); + } else { + // Unmap old instance... + oldInstrument.unmapInstrument(); + // Change item tree, whether applicable... + if (m_iMidiMap < 0 || m_iMidiMap == pInstrument->map()) { + // Add new brand item into view... + addItem(pInstrument, groupItem(pListItem)); + } else { + // Just remove/hide old one. + delete pItem; + } + } + // Notify we've changes... emit instrumentsChanged(); - pItem->update(); } } } @@ -480,18 +586,40 @@ void qsamplerInstrumentList::deleteSlot (void) { QListViewItem *pListItem = QListView::selectedItem(); - if (pListItem) { - if (pListItem->rtti() == Item) { - qsamplerInstrumentItem *pItem - = static_cast (pListItem); - if (pItem && pItem->instrument()) { - pItem->instrument()->unmap(); - emit instrumentsChanged(); - } + if (pListItem == NULL) + return; + + qsamplerMainForm *pMainForm = qsamplerMainForm::getInstance(); + if (pMainForm == NULL) + return; + + // Prompt user if this is for real... + qsamplerOptions *pOptions = pMainForm->options(); + if (pOptions && pOptions->bConfirmRemove) { + if (QMessageBox::warning(this, + QSAMPLER_TITLE ": " + tr("Warning"), + tr("Delete %1:\n\n" + "%2\n\n" + "Are you sure?") + .arg(pListItem->rtti() == Item ? tr("instrument") : tr("group")) + .arg(pListItem->text(0)), + tr("OK"), tr("Cancel")) > 0) + return; + } + + // Unmap instrument entry... + if (pListItem->rtti() == Item) { + qsamplerInstrumentItem *pItem + = static_cast (pListItem); + if (pItem && pItem->instrument()) { + pItem->instrument()->unmapInstrument(); + emit instrumentsChanged(); } - delete pListItem; } + // Do it for real... + delete pListItem; + selectionChangedSlot(); } @@ -514,7 +642,7 @@ void qsamplerInstrumentList::activatedSlot ( QListViewItem *pListItem ) { // FIXME: Hope the list view item is the one selected. - if (pListItem->rtti() == Item) + if (pListItem && pListItem->rtti() == Item) editItemSlot(); } @@ -527,7 +655,7 @@ = static_cast (pListItem); if (pItem && pItem->instrument()) { pItem->instrument()->setName(pListItem->text(0)); - pItem->instrument()->map(); + pItem->instrument()->mapInstrument(); emit instrumentsChanged(); pItem->update(); } @@ -569,18 +697,26 @@ if (pMainForm->client() == NULL) return; + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + // Load the whole bunch of instrument items... qsamplerInstrumentItem *pItem = NULL; lscp_midi_instrument_t *pInstrs - = ::lscp_list_midi_instruments(pMainForm->client()); - for (int iInstr = 0; pInstrs && pInstrs[iInstr].program >= 0; ++iInstr) { - int iBank = (pInstrs[iInstr].bank_msb << 7) | pInstrs[iInstr].bank_lsb; - int iProgram = pInstrs[iInstr].program; + = ::lscp_list_midi_instruments(pMainForm->client(), m_iMidiMap); + for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; ++iInstr) { + int iMap = pInstrs[iInstr].map; + int iBank = pInstrs[iInstr].bank; + int iProg = pInstrs[iInstr].prog; qsamplerInstrument *pInstrument - = new qsamplerInstrument(iBank, iProgram); - if (pInstrument->get()) + = new qsamplerInstrument(iMap, iBank, iProg); + if (pInstrument->getInstrument()) pItem = new qsamplerInstrumentItem(this, pInstrument, pItem); + // Try to keep it snappy :) + QApplication::eventLoop()->processEvents(QEventLoop::ExcludeUserInput); } + QApplication::restoreOverrideCursor(); + if (pInstrs == NULL && ::lscp_client_get_errno(pMainForm->client())) { pMainForm->appendMessagesClient("lscp_list_midi_instruments"); pMainForm->appendMessagesError(tr("Could not get current list of MIDI instrument mappings.\n\nSorry.")); @@ -588,7 +724,161 @@ selectionChangedSlot(); } +#endif + +MidiInstrumentsModel::MidiInstrumentsModel(QObject* parent) : QAbstractTableModel(parent) { + m_iMidiMap = LSCP_MIDI_MAP_ALL; +} +int MidiInstrumentsModel::rowCount(const QModelIndex& /*parent*/) const { + if (m_iMidiMap == LSCP_MIDI_MAP_ALL) { + int n = 0; + for (InstrumentsMap::const_iterator itMap = instruments.begin(); itMap != instruments.end(); ++itMap) + n += (*itMap).size(); + return n; + } + InstrumentsMap::const_iterator itMap = instruments.find(m_iMidiMap); + if (itMap == instruments.end()) return 0; + return (*itMap).size(); +} + +int MidiInstrumentsModel::columnCount(const QModelIndex& /*parent*/) const { + return 9; +} + +QVariant MidiInstrumentsModel::data(const QModelIndex &index, int role) const { + if (!index.isValid() || role != Qt::DisplayRole) return QVariant(); + + if (m_iMidiMap == LSCP_MIDI_MAP_ALL) { + int n = 0; + for (InstrumentsMap::const_iterator itMap = instruments.begin(); itMap != instruments.end(); ++itMap) { + n += (*itMap).size(); + if (index.row() < n) + return QVariant::fromValue( + (*itMap)[index.row() + (*itMap).size() - n] + ); + } + } else { + // resolve MIDI instrument map + InstrumentsMap::const_iterator itMap = instruments.find(m_iMidiMap); + if (itMap == instruments.end()) return QVariant(); + // resolve instrument in that map + if (index.row() >= (*itMap).size()) return QVariant(); + return QVariant::fromValue( + (*itMap)[index.row()] + ); + } + + return QVariant(); +} + +QVariant MidiInstrumentsModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (orientation != Qt::Horizontal || role != Qt::DisplayRole) + return QVariant(); + + switch (section) { + case 0: return tr("Name"); + case 1: return tr("Map"); + case 2: return tr("Bank"); + case 3: return tr("Prog"); + case 4: return tr("Engine"); + case 5: return tr("File"); + case 6: return tr("Nr"); + case 7: return tr("Vol"); + case 8: return tr("Mode"); + default: return QVariant(); + } +} + +qsamplerInstrument* MidiInstrumentsModel::addInstrument(int iMap, int iBank, int iProg) { + // Check it there's already one instrument item + // with the very same key (bank, program); + // if yes, just remove it without prejudice... + for (int i = 0; i < instruments[iMap].size(); i++) { + if ( + instruments[iMap][i].bank() == iBank && + instruments[iMap][i].prog() == iProg + ) { + instruments[iMap].removeAt(i); + break; + } + } + + // resolve the appropriate place, we keep the list sorted that way ... + int i = 0; + for (; i < instruments[iMap].size(); i++) + if ( + iBank > instruments[iMap][i].bank() || + ( iBank == instruments[iMap][i].bank() && + iProg > instruments[iMap][i].prog() ) + ) break; + + qsamplerInstrument& instr = instruments[iMap][i] = qsamplerInstrument(iMap, iBank, iProg); + + return &instr; +} + +void MidiInstrumentsModel::setMidiMap(int iMidiMap) { + if (iMidiMap < 0) + iMidiMap = LSCP_MIDI_MAP_ALL; + + m_iMidiMap = iMidiMap; +} -// end of qsamplerInstrumentList.cpp +int MidiInstrumentsModel::midiMap() const { + return m_iMidiMap; +} +void MidiInstrumentsModel::refresh() { + instruments.clear(); + + MainForm* pMainForm = MainForm::getInstance(); + if (pMainForm == NULL) + return; + if (pMainForm->client() == NULL) + return; + + QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); + + // Load the whole bunch of instrument items... + lscp_midi_instrument_t* pInstrs + = ::lscp_list_midi_instruments(pMainForm->client(), m_iMidiMap); + for (int iInstr = 0; pInstrs && pInstrs[iInstr].map >= 0; ++iInstr) { + const int iMap = pInstrs[iInstr].map; + const int iBank = pInstrs[iInstr].bank; + const int iProg = pInstrs[iInstr].prog; + addInstrument(iMap, iBank, iProg); + // Try to keep it snappy :) + QApplication::processEvents(QEventLoop::ExcludeUserInput); + } + + QApplication::restoreOverrideCursor(); + + if (pInstrs == NULL && ::lscp_client_get_errno(pMainForm->client())) { + pMainForm->appendMessagesClient("lscp_list_midi_instruments"); + pMainForm->appendMessagesError(tr("Could not get current list of MIDI instrument mappings.\n\nSorry.")); + } + + //selectionChangedSlot(); +} + + +MidiInstrumentsDelegate::MidiInstrumentsDelegate(QObject* parent) : QItemDelegate(parent) { +} + +QWidget* MidiInstrumentsDelegate::createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const { + return new QLabel(index.model()->data(index, Qt::DisplayRole).toString(), parent); +} + +void MidiInstrumentsDelegate::setEditorData(QWidget* editor, const QModelIndex& index) const { +} + +void MidiInstrumentsDelegate::setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const { +} + +void MidiInstrumentsDelegate::updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& index) const { + if (editor) editor->setGeometry(option.rect); +} + + +// end of qsamplerInstrumentList.cpp