/* * PatchesDialog.cpp - display GIG patches (based on Sf2 patches_dialog.cpp) * * Copyright (c) 2008 Paul Giblock * * 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 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 banks = m_bankListView->findItems( QString::number( iBank ), Qt::MatchExactly, 0 ); QListIterator 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 progs = m_progListView->findItems( QString::number( iProg ), Qt::MatchExactly, 0 ); QListIterator 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 = ""; } 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