PatchesDialog.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. /*
  2. * PatchesDialog.cpp - display GIG patches (based on Sf2 patches_dialog.cpp)
  3. *
  4. * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
  5. *
  6. * This file is part of LMMS - https://lmms.io
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public
  10. * License as published by the Free Software Foundation; either
  11. * version 2 of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public
  19. * License along with this program (see COPYING); if not, write to the
  20. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. * Boston, MA 02110-1301 USA.
  22. *
  23. */
  24. #include "PatchesDialog.h"
  25. #include <QHeaderView>
  26. // Custom list-view item (as for numerical sort purposes...)
  27. class PatchItem : public QTreeWidgetItem
  28. {
  29. public:
  30. // Constructor.
  31. PatchItem( QTreeWidget *pListView,
  32. QTreeWidgetItem *pItemAfter )
  33. : QTreeWidgetItem( pListView, pItemAfter ) {}
  34. // Sort/compare overriden method.
  35. bool operator< ( const QTreeWidgetItem& other ) const
  36. {
  37. int iColumn = QTreeWidgetItem::treeWidget()->sortColumn();
  38. const QString& s1 = text( iColumn );
  39. const QString& s2 = other.text( iColumn );
  40. if( iColumn == 0 || iColumn == 2 )
  41. {
  42. return s1.toInt() < s2.toInt();
  43. }
  44. else
  45. {
  46. return s1 < s2;
  47. }
  48. }
  49. };
  50. // Constructor.
  51. PatchesDialog::PatchesDialog( QWidget * pParent, Qt::WindowFlags wflags )
  52. : QDialog( pParent, wflags )
  53. {
  54. // Setup UI struct...
  55. setupUi( this );
  56. m_pSynth = NULL;
  57. m_iChan = 0;
  58. m_iBank = 0;
  59. m_iProg = 0;
  60. // Soundfonts list view...
  61. QHeaderView * pHeader = m_progListView->header();
  62. pHeader->setDefaultAlignment( Qt::AlignLeft );
  63. pHeader->setSectionsMovable( false );
  64. pHeader->setStretchLastSection( true );
  65. m_progListView->resizeColumnToContents( 0 ); // Prog.
  66. // Initial sort order...
  67. m_bankListView->sortItems( 0, Qt::AscendingOrder );
  68. m_progListView->sortItems( 0, Qt::AscendingOrder );
  69. // UI connections...
  70. QObject::connect( m_bankListView,
  71. SIGNAL( currentItemChanged( QTreeWidgetItem *,QTreeWidgetItem * ) ),
  72. SLOT( bankChanged() ) );
  73. QObject::connect( m_progListView,
  74. SIGNAL( currentItemChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ),
  75. SLOT( progChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ) );
  76. QObject::connect( m_progListView,
  77. SIGNAL( itemActivated( QTreeWidgetItem *, int ) ),
  78. SLOT( accept() ) );
  79. QObject::connect( m_okButton,
  80. SIGNAL( clicked() ),
  81. SLOT( accept() ) );
  82. QObject::connect( m_cancelButton,
  83. SIGNAL( clicked() ),
  84. SLOT( reject() ) );
  85. }
  86. // Destructor.
  87. PatchesDialog::~PatchesDialog()
  88. {
  89. }
  90. // Dialog setup loader.
  91. void PatchesDialog::setup( GigInstance * pSynth, int iChan,
  92. const QString & chanName,
  93. LcdSpinBoxModel * bankModel,
  94. LcdSpinBoxModel * progModel,
  95. QLabel * patchLabel )
  96. {
  97. // We'll going to changes the whole thing...
  98. m_dirty = 0;
  99. m_bankModel = bankModel;
  100. m_progModel = progModel;
  101. m_patchLabel = patchLabel;
  102. // Set the proper caption...
  103. setWindowTitle( chanName + " - GIG patches" );
  104. // set m_pSynth to NULL so we don't trigger any progChanged events
  105. m_pSynth = NULL;
  106. // Load bank list from actual synth stack...
  107. m_bankListView->setSortingEnabled( false );
  108. m_bankListView->clear();
  109. // now it should be safe to set internal stuff
  110. m_pSynth = pSynth;
  111. m_iChan = iChan;
  112. //fluid_preset_t preset;
  113. QTreeWidgetItem * pBankItem = NULL;
  114. // Currently just use zero as the only bank
  115. int iBankDefault = -1;
  116. int iProgDefault = -1;
  117. gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
  118. while( pInstrument )
  119. {
  120. int iBank = pInstrument->MIDIBank;
  121. int iProg = pInstrument->MIDIProgram;
  122. if ( !findBankItem( iBank ) )
  123. {
  124. pBankItem = new PatchItem( m_bankListView, pBankItem );
  125. if( pBankItem )
  126. {
  127. pBankItem->setText( 0, QString::number( iBank ) );
  128. if( iBankDefault == -1 )
  129. {
  130. iBankDefault = iBank;
  131. iProgDefault = iProg;
  132. }
  133. }
  134. }
  135. pInstrument = m_pSynth->gig.GetNextInstrument();
  136. }
  137. m_bankListView->setSortingEnabled( true );
  138. // Set the selected bank.
  139. if( iBankDefault != -1 )
  140. {
  141. m_iBank = iBankDefault;
  142. }
  143. pBankItem = findBankItem( m_iBank );
  144. m_bankListView->setCurrentItem( pBankItem );
  145. m_bankListView->scrollToItem( pBankItem );
  146. bankChanged();
  147. // Set the selected program.
  148. if( iProgDefault != -1 )
  149. {
  150. m_iProg = iProgDefault;
  151. }
  152. QTreeWidgetItem * pProgItem = findProgItem( m_iProg );
  153. m_progListView->setCurrentItem( pProgItem );
  154. m_progListView->scrollToItem( pProgItem );
  155. }
  156. // Stabilize current state form.
  157. void PatchesDialog::stabilizeForm()
  158. {
  159. m_okButton->setEnabled( validateForm() );
  160. }
  161. // Validate form fields.
  162. bool PatchesDialog::validateForm()
  163. {
  164. bool bValid = true;
  165. bValid = bValid && ( m_bankListView->currentItem() != NULL );
  166. bValid = bValid && ( m_progListView->currentItem() != NULL );
  167. return bValid;
  168. }
  169. // Realize a bank-program selection preset.
  170. void PatchesDialog::setBankProg( int iBank, int iProg )
  171. {
  172. if( m_pSynth == NULL )
  173. {
  174. return;
  175. }
  176. }
  177. // Validate form fields and accept it valid.
  178. void PatchesDialog::accept()
  179. {
  180. if( validateForm() )
  181. {
  182. // Unload from current selected dialog items.
  183. int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
  184. int iProg = ( m_progListView->currentItem() )->text( 0 ).toInt();
  185. // And set it right away...
  186. setBankProg( iBank, iProg );
  187. if( m_dirty > 0 )
  188. {
  189. m_bankModel->setValue( iBank );
  190. m_progModel->setValue( iProg );
  191. m_patchLabel->setText( m_progListView->
  192. currentItem()->text( 1 ) );
  193. }
  194. // We got it.
  195. QDialog::accept();
  196. }
  197. }
  198. // Reject settings (Cancel button slot).
  199. void PatchesDialog::reject()
  200. {
  201. // Reset selection to initial selection, if applicable...
  202. if( m_dirty > 0 )
  203. {
  204. setBankProg( m_bankModel->value(), m_progModel->value() );
  205. }
  206. // Done (hopefully nothing).
  207. QDialog::reject();
  208. }
  209. // Find the bank item of given bank number id.
  210. QTreeWidgetItem * PatchesDialog::findBankItem( int iBank )
  211. {
  212. QList<QTreeWidgetItem *> banks
  213. = m_bankListView->findItems(
  214. QString::number( iBank ), Qt::MatchExactly, 0 );
  215. QListIterator<QTreeWidgetItem *> iter( banks );
  216. if( iter.hasNext() )
  217. {
  218. return iter.next();
  219. }
  220. else
  221. {
  222. return NULL;
  223. }
  224. }
  225. // Find the program item of given program number id.
  226. QTreeWidgetItem *PatchesDialog::findProgItem( int iProg )
  227. {
  228. QList<QTreeWidgetItem *> progs
  229. = m_progListView->findItems(
  230. QString::number( iProg ), Qt::MatchExactly, 0 );
  231. QListIterator<QTreeWidgetItem *> iter( progs );
  232. if( iter.hasNext() )
  233. {
  234. return iter.next();
  235. }
  236. else
  237. {
  238. return NULL;
  239. }
  240. }
  241. // Bank change slot.
  242. void PatchesDialog::bankChanged()
  243. {
  244. if( m_pSynth == NULL )
  245. {
  246. return;
  247. }
  248. QTreeWidgetItem * pBankItem = m_bankListView->currentItem();
  249. if( pBankItem == NULL )
  250. {
  251. return;
  252. }
  253. int iBankSelected = pBankItem->text( 0 ).toInt();
  254. // Clear up the program listview.
  255. m_progListView->setSortingEnabled( false );
  256. m_progListView->clear();
  257. QTreeWidgetItem * pProgItem = NULL;
  258. gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
  259. while( pInstrument )
  260. {
  261. QString name = QString::fromStdString( pInstrument->pInfo->Name );
  262. if( name == "" )
  263. {
  264. name = "<no name>";
  265. }
  266. int iBank = pInstrument->MIDIBank;
  267. int iProg = pInstrument->MIDIProgram;
  268. if( iBank == iBankSelected && !findProgItem( iProg ) )
  269. {
  270. pProgItem = new PatchItem( m_progListView, pProgItem );
  271. if( pProgItem )
  272. {
  273. pProgItem->setText( 0, QString::number( iProg ) );
  274. pProgItem->setText( 1, name );
  275. }
  276. }
  277. pInstrument = m_pSynth->gig.GetNextInstrument();
  278. }
  279. m_progListView->setSortingEnabled( true );
  280. // Stabilize the form.
  281. stabilizeForm();
  282. }
  283. // Program change slot.
  284. void PatchesDialog::progChanged( QTreeWidgetItem * curr, QTreeWidgetItem * prev )
  285. {
  286. if( m_pSynth == NULL || curr == NULL )
  287. {
  288. return;
  289. }
  290. // Which preview state...
  291. if( validateForm() )
  292. {
  293. // Set current selection.
  294. int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
  295. int iProg = curr->text( 0 ).toInt();
  296. // And set it right away...
  297. setBankProg( iBank, iProg );
  298. // Now we're dirty nuff.
  299. m_dirty++;
  300. }
  301. // Stabilize the form.
  302. stabilizeForm();
  303. }