PatchesDialog.cpp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  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. #if QT_VERSION >= 0x050000
  64. pHeader->setSectionsMovable( false );
  65. #else
  66. pHeader->setMovable( false );
  67. #endif
  68. pHeader->setStretchLastSection( true );
  69. m_progListView->resizeColumnToContents( 0 ); // Prog.
  70. // Initial sort order...
  71. m_bankListView->sortItems( 0, Qt::AscendingOrder );
  72. m_progListView->sortItems( 0, Qt::AscendingOrder );
  73. // UI connections...
  74. QObject::connect( m_bankListView,
  75. SIGNAL( currentItemChanged( QTreeWidgetItem *,QTreeWidgetItem * ) ),
  76. SLOT( bankChanged() ) );
  77. QObject::connect( m_progListView,
  78. SIGNAL( currentItemChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ),
  79. SLOT( progChanged( QTreeWidgetItem *, QTreeWidgetItem * ) ) );
  80. QObject::connect( m_progListView,
  81. SIGNAL( itemActivated( QTreeWidgetItem *, int ) ),
  82. SLOT( accept() ) );
  83. QObject::connect( m_okButton,
  84. SIGNAL( clicked() ),
  85. SLOT( accept() ) );
  86. QObject::connect( m_cancelButton,
  87. SIGNAL( clicked() ),
  88. SLOT( reject() ) );
  89. }
  90. // Destructor.
  91. PatchesDialog::~PatchesDialog()
  92. {
  93. }
  94. // Dialog setup loader.
  95. void PatchesDialog::setup( GigInstance * pSynth, int iChan,
  96. const QString & chanName,
  97. LcdSpinBoxModel * bankModel,
  98. LcdSpinBoxModel * progModel,
  99. QLabel * patchLabel )
  100. {
  101. // We'll going to changes the whole thing...
  102. m_dirty = 0;
  103. m_bankModel = bankModel;
  104. m_progModel = progModel;
  105. m_patchLabel = patchLabel;
  106. // Set the proper caption...
  107. setWindowTitle( chanName + " - GIG patches" );
  108. // set m_pSynth to NULL so we don't trigger any progChanged events
  109. m_pSynth = NULL;
  110. // Load bank list from actual synth stack...
  111. m_bankListView->setSortingEnabled( false );
  112. m_bankListView->clear();
  113. // now it should be safe to set internal stuff
  114. m_pSynth = pSynth;
  115. m_iChan = iChan;
  116. //fluid_preset_t preset;
  117. QTreeWidgetItem * pBankItem = NULL;
  118. // Currently just use zero as the only bank
  119. int iBankDefault = -1;
  120. int iProgDefault = -1;
  121. gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
  122. while( pInstrument )
  123. {
  124. int iBank = pInstrument->MIDIBank;
  125. int iProg = pInstrument->MIDIProgram;
  126. if ( !findBankItem( iBank ) )
  127. {
  128. pBankItem = new PatchItem( m_bankListView, pBankItem );
  129. if( pBankItem )
  130. {
  131. pBankItem->setText( 0, QString::number( iBank ) );
  132. if( iBankDefault == -1 )
  133. {
  134. iBankDefault = iBank;
  135. iProgDefault = iProg;
  136. }
  137. }
  138. }
  139. pInstrument = m_pSynth->gig.GetNextInstrument();
  140. }
  141. m_bankListView->setSortingEnabled( true );
  142. // Set the selected bank.
  143. if( iBankDefault != -1 )
  144. {
  145. m_iBank = iBankDefault;
  146. }
  147. pBankItem = findBankItem( m_iBank );
  148. m_bankListView->setCurrentItem( pBankItem );
  149. m_bankListView->scrollToItem( pBankItem );
  150. bankChanged();
  151. // Set the selected program.
  152. if( iProgDefault != -1 )
  153. {
  154. m_iProg = iProgDefault;
  155. }
  156. QTreeWidgetItem * pProgItem = findProgItem( m_iProg );
  157. m_progListView->setCurrentItem( pProgItem );
  158. m_progListView->scrollToItem( pProgItem );
  159. }
  160. // Stabilize current state form.
  161. void PatchesDialog::stabilizeForm()
  162. {
  163. m_okButton->setEnabled( validateForm() );
  164. }
  165. // Validate form fields.
  166. bool PatchesDialog::validateForm()
  167. {
  168. bool bValid = true;
  169. bValid = bValid && ( m_bankListView->currentItem() != NULL );
  170. bValid = bValid && ( m_progListView->currentItem() != NULL );
  171. return bValid;
  172. }
  173. // Realize a bank-program selection preset.
  174. void PatchesDialog::setBankProg( int iBank, int iProg )
  175. {
  176. if( m_pSynth == NULL )
  177. {
  178. return;
  179. }
  180. }
  181. // Validate form fields and accept it valid.
  182. void PatchesDialog::accept()
  183. {
  184. if( validateForm() )
  185. {
  186. // Unload from current selected dialog items.
  187. int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
  188. int iProg = ( m_progListView->currentItem() )->text( 0 ).toInt();
  189. // And set it right away...
  190. setBankProg( iBank, iProg );
  191. if( m_dirty > 0 )
  192. {
  193. m_bankModel->setValue( iBank );
  194. m_progModel->setValue( iProg );
  195. m_patchLabel->setText( m_progListView->
  196. currentItem()->text( 1 ) );
  197. }
  198. // We got it.
  199. QDialog::accept();
  200. }
  201. }
  202. // Reject settings (Cancel button slot).
  203. void PatchesDialog::reject()
  204. {
  205. // Reset selection to initial selection, if applicable...
  206. if( m_dirty > 0 )
  207. {
  208. setBankProg( m_bankModel->value(), m_progModel->value() );
  209. }
  210. // Done (hopefully nothing).
  211. QDialog::reject();
  212. }
  213. // Find the bank item of given bank number id.
  214. QTreeWidgetItem * PatchesDialog::findBankItem( int iBank )
  215. {
  216. QList<QTreeWidgetItem *> banks
  217. = m_bankListView->findItems(
  218. QString::number( iBank ), Qt::MatchExactly, 0 );
  219. QListIterator<QTreeWidgetItem *> iter( banks );
  220. if( iter.hasNext() )
  221. {
  222. return iter.next();
  223. }
  224. else
  225. {
  226. return NULL;
  227. }
  228. }
  229. // Find the program item of given program number id.
  230. QTreeWidgetItem *PatchesDialog::findProgItem( int iProg )
  231. {
  232. QList<QTreeWidgetItem *> progs
  233. = m_progListView->findItems(
  234. QString::number( iProg ), Qt::MatchExactly, 0 );
  235. QListIterator<QTreeWidgetItem *> iter( progs );
  236. if( iter.hasNext() )
  237. {
  238. return iter.next();
  239. }
  240. else
  241. {
  242. return NULL;
  243. }
  244. }
  245. // Bank change slot.
  246. void PatchesDialog::bankChanged()
  247. {
  248. if( m_pSynth == NULL )
  249. {
  250. return;
  251. }
  252. QTreeWidgetItem * pBankItem = m_bankListView->currentItem();
  253. if( pBankItem == NULL )
  254. {
  255. return;
  256. }
  257. int iBankSelected = pBankItem->text( 0 ).toInt();
  258. // Clear up the program listview.
  259. m_progListView->setSortingEnabled( false );
  260. m_progListView->clear();
  261. QTreeWidgetItem * pProgItem = NULL;
  262. gig::Instrument * pInstrument = m_pSynth->gig.GetFirstInstrument();
  263. while( pInstrument )
  264. {
  265. QString name = QString::fromStdString( pInstrument->pInfo->Name );
  266. if( name == "" )
  267. {
  268. name = "<no name>";
  269. }
  270. int iBank = pInstrument->MIDIBank;
  271. int iProg = pInstrument->MIDIProgram;
  272. if( iBank == iBankSelected && !findProgItem( iProg ) )
  273. {
  274. pProgItem = new PatchItem( m_progListView, pProgItem );
  275. if( pProgItem )
  276. {
  277. pProgItem->setText( 0, QString::number( iProg ) );
  278. pProgItem->setText( 1, name );
  279. }
  280. }
  281. pInstrument = m_pSynth->gig.GetNextInstrument();
  282. }
  283. m_progListView->setSortingEnabled( true );
  284. // Stabilize the form.
  285. stabilizeForm();
  286. }
  287. // Program change slot.
  288. void PatchesDialog::progChanged( QTreeWidgetItem * curr, QTreeWidgetItem * prev )
  289. {
  290. if( m_pSynth == NULL || curr == NULL )
  291. {
  292. return;
  293. }
  294. // Which preview state...
  295. if( validateForm() )
  296. {
  297. // Set current selection.
  298. int iBank = ( m_bankListView->currentItem() )->text( 0 ).toInt();
  299. int iProg = curr->text( 0 ).toInt();
  300. // And set it right away...
  301. setBankProg( iBank, iProg );
  302. // Now we're dirty nuff.
  303. m_dirty++;
  304. }
  305. // Stabilize the form.
  306. stabilizeForm();
  307. }