1 // qsynthSetupForm.cpp
2 //
3 /****************************************************************************
4    Copyright (C) 2003-2021, rncbc aka Rui Nuno Capela. All rights reserved.
5 
6    This program is free software; you can redistribute it and/or
7    modify it under the terms of the GNU General Public License
8    as published by the Free Software Foundation; either version 2
9    of the License, or (at your option) any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License along
17    with this program; if not, write to the Free Software Foundation, Inc.,
18    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 
20 *****************************************************************************/
21 
22 #include "qsynthAbout.h"
23 #include "qsynthSetupForm.h"
24 
25 #include "qsynthEngine.h"
26 
27 #include <QValidator>
28 #include <QHeaderView>
29 #include <QMessageBox>
30 #include <QFileDialog>
31 #include <QFontDialog>
32 #include <QFileInfo>
33 #include <QPixmap>
34 #include <QMenu>
35 
36 
37 // Our local parameter data struct.
38 struct qsynth_settings_data
39 {
40 	qsynthSetup     *pSetup;
41 	QTreeWidget     *pListView;
42 	QTreeWidgetItem *pListItem;
43 	QStringList      options;
44 };
45 
qsynth_settings_foreach_option(void * pvData,const char *,const char * pszOption)46 static void qsynth_settings_foreach_option (
47 #ifdef CONFIG_FLUID_SETTINGS_FOREACH_OPTION
48 	void *pvData, const char *, const char *pszOption )
49 #else
50 	void *pvData, char *, char *pszOption )
51 #endif
52 {
53 	qsynth_settings_data *pData = (qsynth_settings_data *) pvData;
54 
55 	pData->options.append(pszOption);
56 }
57 
qsynth_settings_foreach(void * pvData,const char * pszName,int iType)58 static void qsynth_settings_foreach (
59 #ifdef CONFIG_FLUID_SETTINGS_FOREACH
60 	void *pvData, const char *pszName, int iType )
61 #else
62 	void *pvData, char *pszName, int iType )
63 #endif
64 {
65 	qsynth_settings_data *pData = (qsynth_settings_data *) pvData;
66 	fluid_settings_t *pFluidSettings = (pData->pSetup)->fluid_settings();
67 
68 	// Add the new list item.
69 	int iCol = 0;
70 	pData->pListItem = new QTreeWidgetItem(pData->pListView, pData->pListItem);
71 	(pData->pListItem)->setText(iCol++, pszName);
72 
73 	// Check for type...
74 	QString qsType = "?";
75 	switch (iType) {
76 	case FLUID_NUM_TYPE: qsType = "num"; break;
77 	case FLUID_INT_TYPE: qsType = "int"; break;
78 	case FLUID_STR_TYPE: qsType = "str"; break;
79 	case FLUID_SET_TYPE: qsType = "set"; break;
80 	}
81 	(pData->pListItem)->setText(iCol++, qsType);
82 /*
83 	// Check for hints...
84 	int iHints = ::fluid_settings_get_hints(pFluidSettings, pszName);
85 	QString sHints = "";
86 	if (iHints & FLUID_HINT_BOUNDED_BELOW)
87 		sHints += " BOUNDED_BELOW ";
88 	if (iHints & FLUID_HINT_BOUNDED_ABOVE)
89 		sHints += " BOUNDED_ABOVE ";
90 	if (iHints & FLUID_HINT_TOGGLED)
91 		sHints += " TOGGLED ";
92 	if (iHints & FLUID_HINT_SAMPLE_RATE)
93 		sHints += " SAMPLE_RATE ";
94 	if (iHints & FLUID_HINT_LOGARITHMIC)
95 		sHints += " LOGARITHMIC ";
96 	if (iHints & FLUID_HINT_LOGARITHMIC)
97 		sHints += " INTEGER ";
98 	if (iHints & FLUID_HINT_FILENAME)
99 		sHints += " FILENAME ";
100 	if (iHints & FLUID_HINT_OPTIONLIST)
101 		sHints += " OPTIONLIST ";
102 */
103 	bool bRealtime = (bool) ::fluid_settings_is_realtime(pFluidSettings, pszName);
104 	(pData->pListItem)->setText(iCol++, (bRealtime ? "yes" : "no"));
105 
106 	switch (iType) {
107 
108 	case FLUID_NUM_TYPE:
109 	{
110 	#ifdef CONFIG_FLUID_SETTINGS_GETNUM_DEFAULT
111 		double fDefault = 0.0f;
112 		::fluid_settings_getnum_default(pFluidSettings, pszName, &fDefault);
113 	#else
114 		const double fDefault  = ::fluid_settings_getnum_default(pFluidSettings, pszName);
115 	#endif
116 		double fCurrent  = 0.0;
117 		double fRangeMin = 0.0;
118 		double fRangeMax = 0.0;
119 		::fluid_settings_getnum(pFluidSettings, pszName, &fCurrent);
120 		::fluid_settings_getnum_range(pFluidSettings, pszName, &fRangeMin, &fRangeMax);
121 		(pData->pListItem)->setText(iCol++, QString::number(fCurrent));
122 		(pData->pListItem)->setText(iCol++, QString::number(fDefault));
123 		(pData->pListItem)->setText(iCol++, QString::number(fRangeMin));
124 		(pData->pListItem)->setText(iCol++, QString::number(fRangeMax));
125 		break;
126 	}
127 
128 	case FLUID_INT_TYPE:
129 	{
130 	#ifdef CONFIG_FLUID_SETTINGS_GETINT_DEFAULT
131 		int iDefault = 0;
132 		::fluid_settings_getint_default(pFluidSettings, pszName, &iDefault);
133 	#else
134 		const int iDefault  = ::fluid_settings_getint_default(pFluidSettings, pszName);
135 	#endif
136 		int iCurrent  = 0;
137 		int iRangeMin = 0;
138 		int iRangeMax = 0;
139 		::fluid_settings_getint(pFluidSettings, pszName, &iCurrent);
140 		::fluid_settings_getint_range(pFluidSettings, pszName, &iRangeMin, &iRangeMax);
141 		if (iRangeMin + iRangeMax < 2) {
142 			iRangeMin = 0;
143 			iRangeMax = 1;
144 		}
145 		(pData->pListItem)->setText(iCol++, QString::number(iCurrent));
146 		(pData->pListItem)->setText(iCol++, QString::number(iDefault));
147 		(pData->pListItem)->setText(iCol++, QString::number(iRangeMin));
148 		(pData->pListItem)->setText(iCol++, QString::number(iRangeMax));
149 		break;
150 	}
151 
152 	case FLUID_STR_TYPE:
153 	{
154 	#ifdef CONFIG_FLUID_SETTINGS_GETSTR_DEFAULT
155 		char *pszDefault = nullptr;
156 		::fluid_settings_getstr_default(pFluidSettings, pszName, &pszDefault);
157 	#else
158 		const char *pszDefault = ::fluid_settings_getstr_default(pFluidSettings, pszName);
159 	#endif
160 		char *pszCurrent = nullptr;
161 	#ifdef CONFIG_FLUID_SETTINGS_DUPSTR
162 		::fluid_settings_dupstr(pFluidSettings, pszName, &pszCurrent);
163 	#else
164 		::fluid_settings_getstr(pFluidSettings, pszName, &pszCurrent);
165 	#endif
166 		(pData->pListItem)->setText(iCol++, pszCurrent);
167 		(pData->pListItem)->setText(iCol++, pszDefault);
168 		(pData->pListItem)->setText(iCol++, QString());
169 		(pData->pListItem)->setText(iCol++, QString());
170 	#ifdef CONFIG_FLUID_SETTINGS_DUPSTR
171 		::free(pszCurrent);
172 	#endif
173 		break;
174 	}}
175 
176 	// Check for options.
177 	pData->options.clear();
178 	::fluid_settings_foreach_option(pFluidSettings, pszName, pvData, qsynth_settings_foreach_option);
179 	(pData->pListItem)->setText(iCol++, pData->options.join(" "));
180 }
181 
182 
183 //----------------------------------------------------------------------------
184 // qsynthSetupForm -- UI wrapper form.
185 
186 // Constructor.
qsynthSetupForm(QWidget * pParent)187 qsynthSetupForm::qsynthSetupForm ( QWidget *pParent )
188 	: QDialog(pParent)
189 {
190 	// Setup UI struct...
191 	m_ui.setupUi(this);
192 
193 	// No settings descriptor initially (the caller will set it).
194 	m_pSetup = nullptr;
195 	m_pOptions = nullptr;
196 
197 	// Initialize dirty control state.
198 	m_iDirtySetup = 0;
199 	m_iDirtyCount = 0;
200 
201 	// Check for pixmaps.
202 	m_pXpmSoundFont = new QPixmap(":/images/sfont1.png");
203 
204 	// Set dialog validators...
205 	QRegularExpression rx("[\\w-]+");
206 	m_ui.DisplayNameLineEdit->setValidator(new QRegularExpressionValidator(rx, m_ui.DisplayNameLineEdit));
207 	m_ui.SampleRateComboBox->setValidator(new QIntValidator(m_ui.SampleRateComboBox));
208 	m_ui.AudioBufSizeComboBox->setValidator(new QIntValidator(m_ui.AudioBufSizeComboBox));
209 	m_ui.AudioBufCountComboBox->setValidator(new QIntValidator(m_ui.AudioBufCountComboBox));
210 	m_ui.JackNameComboBox->setValidator(new QRegularExpressionValidator(rx, m_ui.JackNameComboBox));
211 	m_ui.MidiNameComboBox->setValidator(new QRegularExpressionValidator(rx, m_ui.MidiNameComboBox));
212 
213 	// No sorting on soundfont stack list.
214 	//m_ui.SoundFontListView->setSorting(-1);
215 
216 	// Soundfonts list view...
217 	QHeaderView *pHeader = m_ui.SoundFontListView->header();
218 	pHeader->setDefaultAlignment(Qt::AlignLeft);
219 //	pHeader->setDefaultSectionSize(300);
220 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
221 //	pHeader->setSectionResizeMode(QHeaderView::Custom);
222 	pHeader->setSectionsMovable(false);
223 #else
224 //	pHeader->setResizeMode(QHeaderView::Custom);
225 	pHeader->setMovable(false);
226 #endif
227 	pHeader->setStretchLastSection(true);
228 
229 	m_ui.SoundFontListView->resizeColumnToContents(0);	// SFID.
230 	pHeader->resizeSection(1, 300);						// Name.
231 	m_ui.SoundFontListView->resizeColumnToContents(2);	// Offset.
232 
233 	// Settings list view...
234 	pHeader = m_ui.SettingsListView->header();
235 	pHeader->setDefaultAlignment(Qt::AlignLeft);
236 //	pHeader->setDefaultSectionSize(160);
237 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
238 //	pHeader->setSectionResizeMode(QHeaderView::Custom);
239 	pHeader->setSectionsMovable(false);
240 #else
241 //	pHeader->setResizeMode(QHeaderView::Custom);
242 	pHeader->setMovable(false);
243 #endif
244 	pHeader->setStretchLastSection(true);
245 
246 	pHeader->resizeSection(0, 160);						// Name.
247 	m_ui.SettingsListView->resizeColumnToContents(1);	// Type.
248 	m_ui.SettingsListView->resizeColumnToContents(2);	// Realtime.
249 //	m_ui.SettingsListView->resizeColumnToContents(3);	// Current.
250 //	m_ui.SettingsListView->resizeColumnToContents(4);	// Default.
251 	m_ui.SettingsListView->resizeColumnToContents(5);	// Min.
252 	m_ui.SettingsListView->resizeColumnToContents(6);	// Max.
253 	m_ui.SettingsListView->resizeColumnToContents(7);	// Options.
254 
255 	// Try to restore old window positioning.
256 	adjustSize();
257 
258 	// UI connections...
259 	QObject::connect(m_ui.DisplayNameLineEdit,
260 		SIGNAL(textChanged(const QString&)),
261 		SLOT(nameChanged(const QString&)));
262 	QObject::connect(m_ui.MidiInCheckBox,
263 		SIGNAL(stateChanged(int)),
264 		SLOT(settingsChanged()));
265 	QObject::connect(m_ui.MidiDriverComboBox,
266 		SIGNAL(activated(const QString&)),
267 		SLOT(midiDriverChanged(const QString&)));
268 	QObject::connect(m_ui.MidiDeviceComboBox,
269 		SIGNAL(editTextChanged(const QString&)),
270 		SLOT(settingsChanged()));
271 	QObject::connect(m_ui.MidiChannelsSpinBox,
272 		SIGNAL(valueChanged(int)),
273 		SLOT(settingsChanged()));
274 	QObject::connect(m_ui.VerboseCheckBox,
275 		SIGNAL(stateChanged(int)),
276 		SLOT(settingsChanged()));
277 	QObject::connect(m_ui.MidiDumpCheckBox,
278 		SIGNAL(stateChanged(int)),
279 		SLOT(settingsChanged()));
280 	QObject::connect(m_ui.MidiNameComboBox,
281 		SIGNAL(editTextChanged(const QString&)),
282 		SLOT(settingsChanged()));
283 	QObject::connect(m_ui.MidiBankSelectComboBox,
284 		SIGNAL(activated(const QString&)),
285 		SLOT(settingsChanged()));
286 	QObject::connect(m_ui.AudioDriverComboBox,
287 		SIGNAL(activated(const QString&)),
288 		SLOT(audioDriverChanged(const QString&)));
289 	QObject::connect(m_ui.AudioDeviceComboBox,
290 		SIGNAL(editTextChanged(const QString&)),
291 		SLOT(settingsChanged()));
292 	QObject::connect(m_ui.SampleFormatComboBox,
293 		SIGNAL(activated(int)),
294 		SLOT(settingsChanged()));
295 	QObject::connect(m_ui.SampleRateComboBox,
296 		SIGNAL(editTextChanged(const QString&)),
297 		SLOT(settingsChanged()));
298 	QObject::connect(m_ui.AudioBufSizeComboBox,
299 		SIGNAL(editTextChanged(const QString&)),
300 		SLOT(settingsChanged()));
301 	QObject::connect(m_ui.AudioBufCountComboBox,
302 		SIGNAL(editTextChanged(const QString&)),
303 		SLOT(settingsChanged()));
304 	QObject::connect(m_ui.AudioChannelsSpinBox,
305 		SIGNAL(valueChanged(int)),
306 		SLOT(settingsChanged()));
307 	QObject::connect(m_ui.AudioGroupsSpinBox,
308 		SIGNAL(valueChanged(int)),
309 		SLOT(settingsChanged()));
310 	QObject::connect(m_ui.PolyphonySpinBox,
311 		SIGNAL(valueChanged(int)),
312 		SLOT(settingsChanged()));
313 	QObject::connect(m_ui.JackAutoConnectCheckBox,
314 		SIGNAL(stateChanged(int)),
315 		SLOT(settingsChanged()));
316 	QObject::connect(m_ui.JackMultiCheckBox,
317 		SIGNAL(stateChanged(int)),
318 		SLOT(settingsChanged()));
319 	QObject::connect(m_ui.JackNameComboBox,
320 		SIGNAL(editTextChanged(const QString&)),
321 		SLOT(settingsChanged()));
322 	QObject::connect(m_ui.SoundFontListView,
323 		SIGNAL(customContextMenuRequested(const QPoint&)),
324 		SLOT(contextMenuRequested(const QPoint&)));
325 	QObject::connect(m_ui.SoundFontListView,
326 		SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
327 		SLOT(stabilizeForm()));
328 	QObject::connect(m_ui.SoundFontOpenPushButton,
329 		SIGNAL(clicked()),
330 		SLOT(openSoundFont()));
331 	QObject::connect(m_ui.SoundFontRemovePushButton,
332 		SIGNAL(clicked()),
333 		SLOT(removeSoundFont()));
334 	QObject::connect(m_ui.SoundFontEditPushButton,
335 		SIGNAL(clicked()),
336 		SLOT(editSoundFont()));
337 	QObject::connect(m_ui.SoundFontMoveUpPushButton,
338 		SIGNAL(clicked()),
339 		SLOT(moveUpSoundFont()));
340 	QObject::connect(m_ui.SoundFontMoveDownPushButton,
341 		SIGNAL(clicked()),
342 		SLOT(moveDownSoundFont()));
343 	QObject::connect(m_ui.SoundFontListView->itemDelegate(),
344 		SIGNAL(commitData(QWidget*)),
345 		SLOT(itemRenamed()));
346 	QObject::connect(m_ui.DialogButtonBox,
347 		SIGNAL(accepted()),
348 		SLOT(accept()));
349 	QObject::connect(m_ui.DialogButtonBox,
350 		SIGNAL(rejected()),
351 		SLOT(reject()));
352 }
353 
354 
355 // Destructor.
~qsynthSetupForm(void)356 qsynthSetupForm::~qsynthSetupForm (void)
357 {
358 	// Delete pixmaps.
359 	delete m_pXpmSoundFont;
360 }
361 
362 
363 // A combo-box text item setter helper.
setComboBoxCurrentText(QComboBox * pComboBox,const QString & sText) const364 void qsynthSetupForm::setComboBoxCurrentText (
365         QComboBox *pComboBox, const QString& sText ) const
366 {
367 	if (pComboBox->isEditable()) {
368 		pComboBox->setEditText(sText);
369 	} else {
370 		int iIndex = pComboBox->findText(sText);
371 		if (iIndex < 0) {
372 			iIndex = 0;
373 			if (!sText.isEmpty())
374 				pComboBox->insertItem(0, sText);
375 		}
376 		pComboBox->setCurrentIndex(iIndex);
377 	}
378 }
379 
380 
381 // Populate (setup) dialog controls from settings descriptors.
setup(qsynthOptions * pOptions,qsynthEngine * pEngine,bool bNew)382 void qsynthSetupForm::setup ( qsynthOptions *pOptions, qsynthEngine *pEngine, bool bNew )
383 {
384 	// Check this first.
385 	if (pOptions == nullptr || pEngine == nullptr)
386 		return;
387 
388 	// Set reference descriptors.
389 	m_pOptions = pOptions;
390 	m_pSetup = pEngine->setup();
391 
392 	// Update caption.
393 	setWindowTitle(pEngine->name() + " - " + tr("Setup"));
394 
395 	// Start clean?
396 	m_iDirtyCount = 0;
397 	if (bNew) {
398 		m_pSetup->realize();
399 		++m_iDirtyCount;
400 	}
401 	// Avoid nested changes.
402 	++m_iDirtySetup;
403 
404 	// Display name.
405 	m_ui.DisplayNameLineEdit->setText(m_pSetup->sDisplayName);
406 
407 	// Load Setttings view...
408 	qsynth_settings_data data;
409 	// Set data context.
410 	data.pSetup    = m_pSetup;
411 	data.pListView = m_ui.SettingsListView;
412 	data.pListItem = nullptr;
413 	// And start filling it in...
414 	::fluid_settings_foreach(m_pSetup->fluid_settings(), &data, qsynth_settings_foreach);
415 
416 	// Midi Driver combobox options;
417 	// check if intended MIDI driver is available.
418 	data.options.clear();
419 	char midi_driver[] = "midi.driver";
420 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
421 		midi_driver, &data, qsynth_settings_foreach_option);
422 	m_ui.MidiDriverComboBox->clear();
423 	if (!data.options.contains(m_pSetup->sMidiDriver))
424 		data.options.append(m_pSetup->sMidiDriver);
425 	m_ui.MidiDriverComboBox->addItems(data.options);
426 
427 	// Audio Driver combobox options.
428 	// check if intended Audio driver is available.
429 	data.options.clear();
430 	char audio_driver[] = "audio.driver";
431 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
432 		audio_driver, &data, qsynth_settings_foreach_option);
433 	m_ui.AudioDriverComboBox->clear();
434 	if (!data.options.contains(m_pSetup->sAudioDriver))
435 		data.options.append(m_pSetup->sAudioDriver);
436 	m_ui.AudioDriverComboBox->addItems(data.options);
437 
438 	// Sample Format combobox options.
439 	data.options.clear();
440 	char audio_sample_fmt[] = "audio.sample-format";
441 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
442 		audio_sample_fmt, &data, qsynth_settings_foreach_option);
443 	m_ui.SampleFormatComboBox->clear();
444 	m_ui.SampleFormatComboBox->addItems(data.options);
445 
446 	// Midi bank select combobox options
447 	data.options.clear();
448 	char midi_bank_select[] = "synth.midi-bank-select";
449 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
450 		midi_bank_select, &data, qsynth_settings_foreach_option);
451 	m_ui.MidiBankSelectComboBox->clear();
452 	m_ui.MidiBankSelectComboBox->addItems(data.options);
453 
454 	// Midi settings...
455 	m_ui.MidiInCheckBox->setChecked(m_pSetup->bMidiIn);
456 	setComboBoxCurrentText(m_ui.MidiDriverComboBox,
457 		m_pSetup->sMidiDriver);
458 	updateMidiDevices(m_pSetup->sMidiDriver);
459 
460 	setComboBoxCurrentText(m_ui.MidiDeviceComboBox,
461 		m_pSetup->sMidiDevice);
462 	setComboBoxCurrentText(m_ui.MidiBankSelectComboBox,
463 		m_pSetup->sMidiBankSelect);
464 	m_ui.MidiChannelsSpinBox->setValue(m_pSetup->iMidiChannels);
465 	m_ui.MidiDumpCheckBox->setChecked(m_pSetup->bMidiDump);
466 	m_ui.VerboseCheckBox->setChecked(m_pSetup->bVerbose);
467 	// ALSA client identifier.
468 	m_ui.MidiNameComboBox->addItem(m_pSetup->sDisplayName);
469 	setComboBoxCurrentText(m_ui.MidiNameComboBox,
470 		bNew ? m_pSetup->sDisplayName : m_pSetup->sMidiName);
471 
472 	// Audio settings...
473 	setComboBoxCurrentText(m_ui.AudioDriverComboBox,
474 		m_pSetup->sAudioDriver);
475 	updateAudioDevices(m_pSetup->sAudioDriver);
476 
477 	setComboBoxCurrentText(m_ui.AudioDeviceComboBox,
478 		m_pSetup->sAudioDevice);
479 	setComboBoxCurrentText(m_ui.SampleFormatComboBox,
480 		m_pSetup->sSampleFormat);
481 	setComboBoxCurrentText(m_ui.SampleRateComboBox,
482 		QString::number(m_pSetup->fSampleRate));
483 	setComboBoxCurrentText(m_ui.AudioBufSizeComboBox,
484 		QString::number(m_pSetup->iAudioBufSize));
485 	setComboBoxCurrentText(m_ui.AudioBufCountComboBox,
486 		QString::number(m_pSetup->iAudioBufCount));
487 	m_ui.AudioChannelsSpinBox->setValue(m_pSetup->iAudioChannels);
488 	m_ui.AudioGroupsSpinBox->setValue(m_pSetup->iAudioGroups);
489 	m_ui.PolyphonySpinBox->setValue(m_pSetup->iPolyphony);
490 	m_ui.JackMultiCheckBox->setChecked(m_pSetup->bJackMulti);
491 	m_ui.JackAutoConnectCheckBox->setChecked(m_pSetup->bJackAutoConnect);
492 	// JACK client name...
493 	QString sJackName;
494 	if (!m_pSetup->sDisplayName.contains(QSYNTH_TITLE))
495 		sJackName += QSYNTH_TITLE "_";
496 	sJackName += m_pSetup->sDisplayName;
497 	m_ui.JackNameComboBox->addItem(sJackName);
498 	setComboBoxCurrentText(m_ui.JackNameComboBox,
499 		bNew ? sJackName : m_pSetup->sJackName);
500 
501 	// Load the soundfonts view.
502 	m_ui.SoundFontListView->clear();
503 	m_ui.SoundFontListView->setUpdatesEnabled(false);
504 	QTreeWidgetItem *pItem = nullptr;
505 	if (pEngine->pSynth) {
506 		// Load soundfont view from actual synth stack...
507 		int cSoundFonts = ::fluid_synth_sfcount(pEngine->pSynth);
508 		for (int i = cSoundFonts - 1; i >= 0; i--) {
509 			fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(pEngine->pSynth, i);
510 			if (pSoundFont) {
511 				pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
512 				if (pItem) {
513 				#ifdef CONFIG_FLUID_SFONT_GET_ID
514 					const int iSFID = ::fluid_sfont_get_id(pSoundFont);
515 				#else
516 					const int iSFID = pSoundFont->id;
517 				#endif
518 				#ifdef CONFIG_FLUID_SFONT_GET_NAME
519 					const QString sSFName = ::fluid_sfont_get_name(pSoundFont);
520 				#else
521 					const QString sSFName = pSoundFont->get_name(pSoundFont);
522 				#endif
523 					pItem->setIcon(0, *m_pXpmSoundFont);
524 					pItem->setText(0, QString::number(iSFID));
525 					pItem->setText(1, sSFName);
526 				#ifdef CONFIG_FLUID_BANK_OFFSET
527 					pItem->setText(2, QString::number(
528 						::fluid_synth_get_bank_offset(pEngine->pSynth, iSFID)));
529 					pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
530 				#endif
531 				}
532 			}
533 		}
534 	} else {
535 		// Load soundfont view from configuration setup list...
536 		int i = 0;
537 		QStringListIterator iter(m_pSetup->soundfonts);
538 		while (iter.hasNext()) {
539 			pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
540 			if (pItem) {
541 				pItem->setIcon(0, *m_pXpmSoundFont);
542 				pItem->setText(0, QString::number(i));
543 				pItem->setText(1, iter.next());
544 			#ifdef CONFIG_FLUID_BANK_OFFSET
545 				pItem->setText(2, m_pSetup->bankoffsets[i]);
546 				pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
547 			#endif
548 			}
549 			++i;
550 		}
551 	}
552 	m_ui.SoundFontListView->setUpdatesEnabled(true);
553 	m_ui.SoundFontListView->update();
554 
555 	// Done.
556 	m_iDirtySetup--;
557 	stabilizeForm();
558 }
559 
560 
561 // Accept settings (OK button slot).
accept(void)562 void qsynthSetupForm::accept (void)
563 {
564 	if (m_iDirtyCount > 0) {
565 		// Save the soundfont view.
566 		m_pSetup->soundfonts.clear();
567 		m_pSetup->bankoffsets.clear();
568 		const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
569 		for (int i = 0; i < iItemCount; ++i) {
570 			QTreeWidgetItem *pItem = m_ui.SoundFontListView->topLevelItem(i);
571 			m_pSetup->soundfonts.append(pItem->text(1));
572 			m_pSetup->bankoffsets.append(pItem->text(2));
573 		}
574 		// Will we have a setup renaming?
575 		m_pSetup->sDisplayName     = m_ui.DisplayNameLineEdit->text();
576 		// Midi settings...
577 		m_pSetup->bMidiIn          = m_ui.MidiInCheckBox->isChecked();
578 		m_pSetup->sMidiDriver      = m_ui.MidiDriverComboBox->currentText();
579 		m_pSetup->sMidiDevice      = m_ui.MidiDeviceComboBox->currentText();
580 		m_pSetup->iMidiChannels    = m_ui.MidiChannelsSpinBox->value();
581 		m_pSetup->sMidiBankSelect  = m_ui.MidiBankSelectComboBox->currentText();
582 		m_pSetup->bMidiDump        = m_ui.MidiDumpCheckBox->isChecked();
583 		m_pSetup->bVerbose         = m_ui.VerboseCheckBox->isChecked();
584 		m_pSetup->sMidiName        = m_ui.MidiNameComboBox->currentText();
585 		// Audio settings...
586 		m_pSetup->sAudioDriver     = m_ui.AudioDriverComboBox->currentText();
587 		m_pSetup->sAudioDevice     = m_ui.AudioDeviceComboBox->currentText();
588 		m_pSetup->sSampleFormat    = m_ui.SampleFormatComboBox->currentText();
589 		m_pSetup->fSampleRate      = m_ui.SampleRateComboBox->currentText().toDouble();
590 		m_pSetup->iAudioBufSize    = m_ui.AudioBufSizeComboBox->currentText().toInt();
591 		m_pSetup->iAudioBufCount   = m_ui.AudioBufCountComboBox->currentText().toInt();
592 		m_pSetup->iAudioChannels   = m_ui.AudioChannelsSpinBox->value();
593 		m_pSetup->iAudioGroups     = m_ui.AudioGroupsSpinBox->value();
594 		m_pSetup->iPolyphony       = m_ui.PolyphonySpinBox->value();
595 		m_pSetup->bJackMulti       = m_ui.JackMultiCheckBox->isChecked();
596 		m_pSetup->bJackAutoConnect = m_ui.JackAutoConnectCheckBox->isChecked();
597 		m_pSetup->sJackName        = m_ui.JackNameComboBox->currentText();
598 		// Reset dirty flag.
599 		m_iDirtyCount = 0;
600 	}
601 
602 	// Just go with dialog acceptance.
603 	QDialog::accept();
604 }
605 
606 
607 // Reject settings (Cancel button slot).
reject(void)608 void qsynthSetupForm::reject (void)
609 {
610 	bool bReject = true;
611 
612 	// Check if there's any pending changes...
613 	if (m_iDirtyCount > 0) {
614 		switch (QMessageBox::warning(this,
615 			tr("Warning"),
616 			tr("Some settings have been changed.") + "\n\n" +
617 			tr("Do you want to apply the changes?"),
618 			QMessageBox::Apply | QMessageBox::Discard | QMessageBox::Cancel)) {
619 		case QMessageBox::Apply:
620 			accept();
621 			return;
622 		case QMessageBox::Discard:
623 			break;
624 		default:    // Cancel.
625 			bReject = false;
626 		}
627 	}
628 
629 	if (bReject)
630 		QDialog::reject();
631 }
632 
633 
634 // Dirty up engine display name.
nameChanged(const QString &)635 void qsynthSetupForm::nameChanged ( const QString& )
636 {
637 	if (m_iDirtySetup > 0)
638 		return;
639 
640 	int iItem;
641 	const QString& sDisplayName = m_ui.DisplayNameLineEdit->text();
642 
643 	iItem = m_ui.MidiNameComboBox->findText(sDisplayName);
644 	if (iItem >= 0) {
645 		m_ui.MidiNameComboBox->removeItem(iItem);
646 		m_ui.MidiNameComboBox->insertItem(0, sDisplayName);
647 	}
648 
649 	iItem = m_ui.JackNameComboBox->findText(sDisplayName);
650 	if (iItem >= 0) {
651 		m_ui.JackNameComboBox->removeItem(iItem);
652 		m_ui.JackNameComboBox->insertItem(0, sDisplayName);
653 	}
654 
655 	settingsChanged();
656 }
657 
658 
659 // Fill MIDI device options.
updateMidiDevices(const QString & sMidiDriver)660 void qsynthSetupForm::updateMidiDevices ( const QString& sMidiDriver )
661 {
662 	qsynth_settings_data data;
663 	data.pSetup    = m_pSetup;
664 	data.pListView = nullptr;
665 	data.pListItem = nullptr;
666 
667 	// MIDI Device combobox options;
668 	QString sOldText = m_ui.MidiDeviceComboBox->currentText();
669 	m_ui.MidiDeviceComboBox->clear();
670 	QString sName = "midi." + sMidiDriver + ".device";
671 	data.options.clear();
672 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
673 		sName.toLocal8Bit().data(), &data, qsynth_settings_foreach_option);
674 
675 	m_ui.MidiDeviceComboBox->addItems(data.options);
676 }
677 
678 
midiDriverChanged(const QString & sMidiDriver)679 void qsynthSetupForm::midiDriverChanged ( const QString& sMidiDriver )
680 {
681 	if (m_iDirtySetup > 0)
682 		return;
683 
684 	QString sMidiDevice = m_ui.MidiDeviceComboBox->currentText();
685 	updateMidiDevices(sMidiDriver);
686 	setComboBoxCurrentText(m_ui.MidiDeviceComboBox, sMidiDevice);
687 
688 	settingsChanged();
689 }
690 
691 
692 // Fill audio device options.
updateAudioDevices(const QString & sAudioDriver)693 void qsynthSetupForm::updateAudioDevices ( const QString& sAudioDriver )
694 {
695 	qsynth_settings_data data;
696 	data.pSetup    = m_pSetup;
697 	data.pListView = nullptr;
698 	data.pListItem = nullptr;
699 
700 	// Audio Device combobox options;
701 	QString sOldText = m_ui.AudioDeviceComboBox->currentText();
702 	m_ui.AudioDeviceComboBox->clear();
703 	QString sName = "audio." + sAudioDriver + ".device";
704 	data.options.clear();
705 	::fluid_settings_foreach_option(m_pSetup->fluid_settings(),
706 		sName.toLocal8Bit().data(), &data, qsynth_settings_foreach_option);
707 
708 	m_ui.AudioDeviceComboBox->addItems(data.options);
709 }
710 
audioDriverChanged(const QString & sAudioDriver)711 void qsynthSetupForm::audioDriverChanged ( const QString& sAudioDriver )
712 {
713 	if (m_iDirtySetup > 0)
714 		return;
715 
716 	const QString& sAudioDevice = m_ui.AudioDeviceComboBox->currentText();
717 	updateAudioDevices(sAudioDriver);
718 	setComboBoxCurrentText(m_ui.AudioDeviceComboBox, sAudioDevice);
719 
720 	settingsChanged();
721 }
722 
723 
724 // Dirty up settings.
settingsChanged(void)725 void qsynthSetupForm::settingsChanged (void)
726 {
727 	if (m_iDirtySetup > 0)
728 		return;
729 
730 	m_iDirtyCount++;
731 	stabilizeForm();
732 }
733 
734 
735 // Stabilize current form state.
stabilizeForm(void)736 void qsynthSetupForm::stabilizeForm (void)
737 {
738 	bool bEnabled = m_ui.MidiInCheckBox->isChecked();
739 
740 	const bool bAlsaEnabled = (m_ui.MidiDriverComboBox->currentText() == "alsa_seq");
741 	const bool bCoreMidiEnabled = (m_ui.MidiDriverComboBox->currentText() == "coremidi");
742 	m_ui.MidiDriverTextLabel->setEnabled(bEnabled);
743 	m_ui.MidiDriverComboBox->setEnabled(bEnabled);
744 	m_ui.MidiDeviceTextLabel->setEnabled(bEnabled && !bAlsaEnabled);
745 	m_ui.MidiDeviceComboBox->setEnabled(bEnabled && !bAlsaEnabled);
746 	m_ui.MidiChannelsTextLabel->setEnabled(bEnabled);
747 	m_ui.MidiChannelsSpinBox->setEnabled(bEnabled);
748 	m_ui.MidiDumpCheckBox->setEnabled(bEnabled);
749 	m_ui.VerboseCheckBox->setEnabled(bEnabled);
750 	m_ui.MidiBankSelectTextLabel->setEnabled(bEnabled);
751 	m_ui.MidiBankSelectComboBox->setEnabled(bEnabled);
752 	m_ui.MidiNameTextLabel->setEnabled(bEnabled && (bAlsaEnabled | bCoreMidiEnabled));
753 	m_ui.MidiNameComboBox->setEnabled(bEnabled && (bAlsaEnabled | bCoreMidiEnabled));
754 
755 	const bool bJackEnabled = (m_ui.AudioDriverComboBox->currentText() == "jack");
756 	const bool bJackMultiEnabled = m_ui.JackMultiCheckBox->isChecked();
757 	m_ui.AudioDeviceTextLabel->setEnabled(!bJackEnabled);
758 	m_ui.AudioDeviceComboBox->setEnabled(!bJackEnabled);
759 	m_ui.JackMultiCheckBox->setEnabled(bJackEnabled);
760 	m_ui.JackAutoConnectCheckBox->setEnabled(bJackEnabled);
761 	m_ui.JackNameTextLabel->setEnabled(bJackEnabled);
762 	m_ui.JackNameComboBox->setEnabled(bJackEnabled);
763 	if (bJackEnabled) {
764 		m_ui.AudioChannelsTextLabel->setEnabled(bJackMultiEnabled);
765 		m_ui.AudioChannelsSpinBox->setEnabled(bJackMultiEnabled);
766 		m_ui.AudioChannelsSpinBox->setSingleStep(2);
767 		m_ui.AudioChannelsSpinBox->setMinimum(2);
768 		m_ui.AudioGroupsTextLabel->setEnabled(bJackMultiEnabled);
769 		m_ui.AudioGroupsSpinBox->setEnabled(bJackMultiEnabled);
770 	//	m_ui.AudioGroupsSpinBox->setSingleStep(2);
771 	//	m_ui.AudioGroupsSpinBox->setMinimum(2);
772 	}
773 
774 	m_ui.SoundFontOpenPushButton->setEnabled(true);
775 	QTreeWidgetItem *pSelectedItem = m_ui.SoundFontListView->currentItem();
776 	if (pSelectedItem) {
777 		const int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pSelectedItem);
778 		const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
779 		m_ui.SoundFontEditPushButton->setEnabled(
780 				pSelectedItem->flags() & Qt::ItemIsEditable);
781 		m_ui.SoundFontRemovePushButton->setEnabled(true);
782 		m_ui.SoundFontMoveUpPushButton->setEnabled(iItem > 0);
783 		m_ui.SoundFontMoveDownPushButton->setEnabled(iItem < iItemCount - 1);
784 	} else {
785 		m_ui.SoundFontRemovePushButton->setEnabled(false);
786 		m_ui.SoundFontEditPushButton->setEnabled(false);
787 		m_ui.SoundFontMoveUpPushButton->setEnabled(false);
788 		m_ui.SoundFontMoveDownPushButton->setEnabled(false);
789 	}
790 
791 	bEnabled = (m_iDirtyCount > 0);
792 	if (bEnabled && m_pSetup) {
793 		const QString& sDisplayName = m_ui.DisplayNameLineEdit->text();
794 		if (sDisplayName != m_pSetup->sDisplayName) {
795 			bEnabled = (!m_pOptions->engines.contains(sDisplayName));
796 			if (bEnabled)
797 				bEnabled = (sDisplayName != (m_pOptions->defaultSetup())->sDisplayName);
798 		}
799 	}
800 	m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bEnabled);
801 }
802 
803 
804 // Soundfont view context menu handler.
contextMenuRequested(const QPoint & pos)805 void qsynthSetupForm::contextMenuRequested ( const QPoint& pos )
806 {
807 	int iItem = 0;
808 	int iItemCount = 0;
809 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->itemAt(pos);
810 	if (pItem == nullptr)
811 		pItem = m_ui.SoundFontListView->currentItem();
812 	if (pItem) {
813 		iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
814 		iItemCount = m_ui.SoundFontListView->topLevelItemCount();
815 	}
816 
817 	// Build the soundfont context menu...
818 	QMenu menu(this);
819 	QAction *pAction;
820 
821 	pAction = menu.addAction(
822 		QIcon(":/images/open1.png"),
823 		tr("Open..."), this, SLOT(openSoundFont()));
824 	menu.addSeparator();
825 	bool bEnabled = (pItem != nullptr);
826 	pAction = menu.addAction(
827 		QIcon(":/images/edit1.png"),
828 		tr("Edit"), this, SLOT(editSoundFont()));
829 	pAction->setEnabled(
830 			(bEnabled && (pItem->flags() & Qt::ItemIsEditable)));
831 	pAction = menu.addAction(
832 		QIcon(":/images/remove1.png"),
833 		tr("Remove"), this, SLOT(removeSoundFont()));
834 	pAction->setEnabled(bEnabled);
835 	menu.addSeparator();
836 	pAction = menu.addAction(
837 		QIcon(":/images/up1.png"),
838 		tr("Move Up"), this, SLOT(moveUpSoundFont()));
839 	pAction->setEnabled(bEnabled && iItem > 0);
840 	pAction = menu.addAction(
841 		QIcon(":/images/down1.png"),
842 		tr("Move Down"), this, SLOT(moveDownSoundFont()));
843 	pAction->setEnabled(bEnabled && iItem < iItemCount - 1);
844 
845 	menu.exec((m_ui.SoundFontListView->viewport())->mapToGlobal(pos));
846 }
847 
848 
849 // Refresh the soundfont view ids.
refreshSoundFonts(void)850 void qsynthSetupForm::refreshSoundFonts (void)
851 {
852 	m_ui.SoundFontListView->setUpdatesEnabled(false);
853 	const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
854 	for (int i = 0; i < iItemCount; ++i) {
855 		QTreeWidgetItem *pItem = m_ui.SoundFontListView->topLevelItem(i);
856 		pItem->setText(0, QString::number(i + 1));
857 	}
858 	m_ui.SoundFontListView->setUpdatesEnabled(true);
859 	m_ui.SoundFontListView->update();
860 }
861 
862 
863 // Browse for a soundfont file on the filesystem.
openSoundFont(void)864 void qsynthSetupForm::openSoundFont (void)
865 {
866 	QStringList soundfonts = QFileDialog::getOpenFileNames(this,
867 		tr("Soundfont files"), m_pOptions->sSoundFontDir,
868 		tr("Soundfont files") + " (*.sf2 *.SF2 *.sf3 *.SF3)");
869 
870 	QTreeWidgetItem *pItem = nullptr;
871 
872 	// For avery selected soundfont to load...
873 	QStringListIterator iter(soundfonts);
874 	while (iter.hasNext()) {
875 		const QString& sSoundFont = iter.next();
876 		// Is it a soundfont file...
877 		if (::fluid_is_soundfont(sSoundFont.toLocal8Bit().data())) {
878 			// Check if not already there...
879 			if (!m_ui.SoundFontListView->findItems(
880 					sSoundFont, Qt::MatchExactly, 1).isEmpty() &&
881 				QMessageBox::warning(this,
882 					tr("Warning"),
883 					tr("Soundfont file already on list") + ":\n\n" +
884 					"\"" + sSoundFont + "\"\n\n" +
885 					tr("Add anyway?"),
886 					QMessageBox::Ok | QMessageBox::Cancel)
887 				== QMessageBox::Cancel) {
888 				continue;
889 			}
890 			// Start inserting in the current selected or last item...
891 			if (pItem == nullptr) {
892 				pItem = m_ui.SoundFontListView->currentItem();
893 				if (pItem)
894 					pItem->setSelected(false);
895 			}
896 			// New item on the block :-)
897 			pItem = new QTreeWidgetItem(m_ui.SoundFontListView, pItem);
898 			if (pItem) {
899 				pItem->setIcon(0, *m_pXpmSoundFont);
900 				pItem->setText(1, sSoundFont);
901 			#ifdef CONFIG_FLUID_BANK_OFFSET
902 				pItem->setText(2, "0");
903 				pItem->setFlags(pItem->flags() | Qt::ItemIsEditable);
904 			#endif
905 				pItem->setSelected(true);
906 				m_ui.SoundFontListView->setCurrentItem(pItem);
907 				m_pOptions->sSoundFontDir = QFileInfo(sSoundFont).dir().absolutePath();
908 				++m_iDirtyCount;
909 			}
910 		} else {
911 			QMessageBox::critical(this,
912 				tr("Error"),
913 				tr("Failed to add soundfont file") + ":\n\n" +
914 				"\"" + sSoundFont + "\"\n\n" +
915 				tr("Please, check for a valid soundfont file."),
916 				QMessageBox::Cancel);
917 		}
918 	}
919 
920 	refreshSoundFonts();
921 	stabilizeForm();
922 }
923 
924 
925 // Edit current selected soundfont.
editSoundFont(void)926 void qsynthSetupForm::editSoundFont (void)
927 {
928 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
929 	if (pItem)
930 		m_ui.SoundFontListView->editItem(pItem, 2);
931 
932 	stabilizeForm();
933 }
934 
935 
936 // Remove current selected soundfont.
removeSoundFont(void)937 void qsynthSetupForm::removeSoundFont (void)
938 {
939 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
940 	if (pItem) {
941 		++m_iDirtyCount;
942 		delete pItem;
943 	}
944 
945 	refreshSoundFonts();
946 	stabilizeForm();
947 }
948 
949 
950 // Move current selected soundfont one position up.
moveUpSoundFont(void)951 void qsynthSetupForm::moveUpSoundFont (void)
952 {
953 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
954 	if (pItem) {
955 		int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
956 		if (iItem > 0) {
957 			pItem->setSelected(false);
958 			pItem = m_ui.SoundFontListView->takeTopLevelItem(iItem);
959 			m_ui.SoundFontListView->insertTopLevelItem(iItem - 1, pItem);
960 			pItem->setSelected(true);
961 			m_ui.SoundFontListView->setCurrentItem(pItem);
962 			++m_iDirtyCount;
963 		}
964 	}
965 
966 	refreshSoundFonts();
967 	stabilizeForm();
968 }
969 
970 
971 // Move current selected soundfont one position down.
moveDownSoundFont(void)972 void qsynthSetupForm::moveDownSoundFont (void)
973 {
974 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
975 	if (pItem) {
976 		const int iItemCount = m_ui.SoundFontListView->topLevelItemCount();
977 		int iItem = m_ui.SoundFontListView->indexOfTopLevelItem(pItem);
978 		if (iItem < iItemCount - 1) {
979 			pItem->setSelected(false);
980 			pItem = m_ui.SoundFontListView->takeTopLevelItem(iItem);
981 			m_ui.SoundFontListView->insertTopLevelItem(iItem + 1, pItem);
982 			pItem->setSelected(true);
983 			m_ui.SoundFontListView->setCurrentItem(pItem);
984 			m_iDirtyCount++;
985 		}
986 	}
987 
988 	refreshSoundFonts();
989 	stabilizeForm();
990 }
991 
992 
993 // Check soundfont bank offset edit.
itemRenamed(void)994 void qsynthSetupForm::itemRenamed (void)
995 {
996 	QTreeWidgetItem *pItem = m_ui.SoundFontListView->currentItem();
997 	if (pItem) {
998 		const int iBankOffset = pItem->text(2).toInt();
999 		if (iBankOffset < 0 || iBankOffset > 16384)
1000 			pItem->setText(2, QString::number(0));
1001 		m_iDirtyCount++;
1002 	}
1003 
1004 	stabilizeForm();
1005 }
1006 
1007 
1008 // end of qsynthSetupForm.cpp
1009