Files
lmms/plugins/GigPlayer/PatchesDialog.cpp

409 lines
8.3 KiB
C++

/*
* PatchesDialog.cpp - display GIG patches (based on Sf2 patches_dialog.cpp)
*
* Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
*
* This file is part of LMMS - https://lmms.io
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "PatchesDialog.h"
#include <QHeaderView>
namespace lmms::gui
{
// Custom list-view item (as for numerical sort purposes...)
class PatchItem : public QTreeWidgetItem
{
public:
// Constructor.
PatchItem( QTreeWidget *pListView,
QTreeWidgetItem *pItemAfter )
: QTreeWidgetItem( pListView, pItemAfter ) {}
// Sort/compare overriden method.
bool operator< ( const QTreeWidgetItem& other ) const override
{
int iColumn = QTreeWidgetItem::treeWidget()->sortColumn();
const QString& s1 = text( iColumn );
const QString& s2 = other.text( iColumn );
if( iColumn == 0 || iColumn == 2 )
{
return s1.toInt() < s2.toInt();
}
else
{
return s1 < s2;
}
}
};
// Constructor.
PatchesDialog::PatchesDialog( QWidget * pParent, Qt::WindowFlags wflags )
: QDialog( pParent, wflags )
{
// Setup UI struct...
setupUi( this );
m_pSynth = nullptr;
m_iChan = 0;
m_iBank = 0;
m_iProg = 0;
// Soundfonts list view...
QHeaderView * pHeader = m_progListView->header();
pHeader->setDefaultAlignment( Qt::AlignLeft );
pHeader->setSectionsMovable( false );
pHeader->setStretchLastSection( true );
m_progListView->resizeColumnToContents( 0 ); // Prog.
// Initial sort order...
m_bankListView->sortItems( 0, Qt::AscendingOrder );
m_progListView->sortItems( 0, Qt::AscendingOrder );
// UI connections...
QObject::connect( m_bankListView,
SIGNAL( currentItemChanged( QTreeWidgetItem *,QTreeWidgetItem * ) ),
SLOT( bankChanged() ) );
QObject::connect( m_progListView,
SIGNAL( currentItemChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ),
SLOT( progChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ) );
QObject::connect( m_progListView,
SIGNAL( itemActivated( QTreeWidgetItem *, int ) ),
SLOT( accept() ) );
QObject::connect( m_okButton,
SIGNAL( clicked() ),
SLOT( accept() ) );
QObject::connect( m_cancelButton,
SIGNAL( clicked() ),
SLOT( reject() ) );
}
// Dialog setup loader.
void PatchesDialog::setup( GigInstance * pSynth, int iChan,
const QString & chanName,
LcdSpinBoxModel * bankModel,
LcdSpinBoxModel * progModel,
QLabel * patchLabel )
{
// We'll going to changes the whole thing...
m_dirty = 0;
m_bankModel = bankModel;
m_progModel = progModel;
m_patchLabel = patchLabel;
// Set the proper caption...
setWindowTitle( chanName + " - GIG patches" );
// set m_pSynth to NULL so we don't trigger any progChanged events
m_pSynth = nullptr;
// Load bank list from actual synth stack...
m_bankListView->setSortingEnabled( false );
m_bankListView->clear();
// now it should be safe to set internal stuff
m_pSynth = pSynth;
m_iChan = iChan;
//fluid_preset_t preset;
QTreeWidgetItem * pBankItem = nullptr;
// Currently just use zero as the only bank
int iBankDefault = -1;
int iProgDefault = -1;
gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
while( pInstrument )
{
int iBank = pInstrument->MIDIBank;
int iProg = pInstrument->MIDIProgram;
if ( !findBankItem( iBank ) )
{
pBankItem = new PatchItem( m_bankListView, pBankItem );
if( pBankItem )
{
pBankItem->setText( 0, QString::number( iBank ) );
if( iBankDefault == -1 )
{
iBankDefault = iBank;
iProgDefault = iProg;
}
}
}
pInstrument = m_pSynth->gig.GetNextInstrument();
}
m_bankListView->setSortingEnabled( true );
// Set the selected bank.
if( iBankDefault != -1 )
{
m_iBank = iBankDefault;
}
pBankItem = findBankItem( m_iBank );
m_bankListView->setCurrentItem( pBankItem );
m_bankListView->scrollToItem( pBankItem );
bankChanged();
// Set the selected program.
if( iProgDefault != -1 )
{
m_iProg = iProgDefault;
}
QTreeWidgetItem * pProgItem = findProgItem( m_iProg );
m_progListView->setCurrentItem( pProgItem );
m_progListView->scrollToItem( pProgItem );
}
// Stabilize current state form.
void PatchesDialog::stabilizeForm()
{
m_okButton->setEnabled( validateForm() );
}
// Validate form fields.
bool PatchesDialog::validateForm()
{
bool bValid = true;
bValid = bValid && ( m_bankListView->currentItem() != nullptr );
bValid = bValid && ( m_progListView->currentItem() != nullptr );
return bValid;
}
// Realize a bank-program selection preset.
void PatchesDialog::setBankProg( int iBank, int iProg )
{
if( m_pSynth == nullptr )
{
return;
}
}
// Validate form fields and accept it valid.
void PatchesDialog::accept()
{
if( validateForm() )
{
// Unload from current selected dialog items.
int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
int iProg = ( m_progListView->currentItem() )->text( 0 ).toInt();
// And set it right away...
setBankProg( iBank, iProg );
if( m_dirty > 0 )
{
m_bankModel->setValue( iBank );
m_progModel->setValue( iProg );
m_patchLabel->setText( m_progListView->
currentItem()->text( 1 ) );
}
// We got it.
QDialog::accept();
}
}
// Reject settings (Cancel button slot).
void PatchesDialog::reject()
{
// Reset selection to initial selection, if applicable...
if( m_dirty > 0 )
{
setBankProg( m_bankModel->value(), m_progModel->value() );
}
// Done (hopefully nothing).
QDialog::reject();
}
// Find the bank item of given bank number id.
QTreeWidgetItem * PatchesDialog::findBankItem( int iBank )
{
QList<QTreeWidgetItem *> banks
= m_bankListView->findItems(
QString::number( iBank ), Qt::MatchExactly, 0 );
QListIterator<QTreeWidgetItem *> iter( banks );
if( iter.hasNext() )
{
return iter.next();
}
else
{
return nullptr;
}
}
// Find the program item of given program number id.
QTreeWidgetItem *PatchesDialog::findProgItem( int iProg )
{
QList<QTreeWidgetItem *> progs
= m_progListView->findItems(
QString::number( iProg ), Qt::MatchExactly, 0 );
QListIterator<QTreeWidgetItem *> iter( progs );
if( iter.hasNext() )
{
return iter.next();
}
else
{
return nullptr;
}
}
// Bank change slot.
void PatchesDialog::bankChanged()
{
if( m_pSynth == nullptr )
{
return;
}
QTreeWidgetItem * pBankItem = m_bankListView->currentItem();
if( pBankItem == nullptr )
{
return;
}
int iBankSelected = pBankItem->text( 0 ).toInt();
// Clear up the program listview.
m_progListView->setSortingEnabled( false );
m_progListView->clear();
QTreeWidgetItem * pProgItem = nullptr;
gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
while( pInstrument )
{
QString name = QString::fromStdString( pInstrument->pInfo->Name );
if( name == "" )
{
name = "<no name>";
}
int iBank = pInstrument->MIDIBank;
int iProg = pInstrument->MIDIProgram;
if( iBank == iBankSelected && !findProgItem( iProg ) )
{
pProgItem = new PatchItem( m_progListView, pProgItem );
if( pProgItem )
{
pProgItem->setText( 0, QString::number( iProg ) );
pProgItem->setText( 1, name );
}
}
pInstrument = m_pSynth->gig.GetNextInstrument();
}
m_progListView->setSortingEnabled( true );
// Stabilize the form.
stabilizeForm();
}
// Program change slot.
void PatchesDialog::progChanged( QTreeWidgetItem * curr, QTreeWidgetItem * prev )
{
if( m_pSynth == nullptr || curr == nullptr )
{
return;
}
// Which preview state...
if( validateForm() )
{
// Set current selection.
int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
int iProg = curr->text( 0 ).toInt();
// And set it right away...
setBankProg( iBank, iProg );
// Now we're dirty nuff.
m_dirty++;
}
// Stabilize the form.
stabilizeForm();
}
} // namespace lmms::gui