1 // samplv1widget_config.cpp
2 //
3 /****************************************************************************
4    Copyright (C) 2012-2020, 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 "samplv1widget_config.h"
23 #include "samplv1widget_param.h"
24 
25 #include "samplv1widget_palette.h"
26 
27 #include "samplv1_ui.h"
28 
29 #include "samplv1_pshifter.h"
30 
31 #include "samplv1_controls.h"
32 #include "samplv1_programs.h"
33 
34 #include "ui_samplv1widget_config.h"
35 
36 #include <QPushButton>
37 #include <QComboBox>
38 
39 #include <QMessageBox>
40 #include <QFileDialog>
41 #include <QFileInfo>
42 #include <QUrl>
43 
44 #include <QMenu>
45 
46 #include <QStyleFactory>
47 
48 
49 // Default (empty/blank) name.
50 static const char *g_pszDefName = QT_TRANSLATE_NOOP("samplv1widget_config", "(default)");
51 
52 
53 //----------------------------------------------------------------------------
54 // samplv1widget_config -- UI wrapper form.
55 
56 // ctor.
samplv1widget_config(samplv1_ui * pSamplUi,QWidget * pParent)57 samplv1widget_config::samplv1widget_config (
58 	samplv1_ui *pSamplUi, QWidget *pParent )
59 	: QDialog(pParent), p_ui(new Ui::samplv1widget_config), m_ui(*p_ui),
60 		m_pSamplUi(pSamplUi)
61 {
62 	// Setup UI struct...
63 	m_ui.setupUi(this);
64 
65 	// Pitch-shift types.
66 	m_ui.PitchShiftTypeComboBox->addItem(tr("(default)"));
67 	m_ui.PitchShiftTypeComboBox->addItem(tr("S.M.Bernsee"));
68 #ifdef CONFIG_LIBRUBBERBAND
69 	m_ui.PitchShiftTypeComboBox->addItem(tr("RubberBand"));
70 #endif
71 
72 	// Note names.
73 	QStringList notes;
74 	for (int note = 0; note < 128; ++note)
75 		notes << samplv1_ui::noteName(note);
76 	m_ui.TuningRefNoteComboBox->insertItems(0, notes);
77 
78 	// Tuning specifics setup...
79 	m_ui.TuningTabBar->addTab(tr("&Global"));
80 	m_ui.TuningTabBar->addTab(tr("&Instance"));
81 
82 	// Dialog dirty flags.
83 	m_iDirtyTuning   = 0;
84 	m_iDirtyControls = 0;
85 	m_iDirtyPrograms = 0;
86 	m_iDirtyOptions  = 0;
87 
88 	// Setup options...
89 	samplv1_config *pConfig = samplv1_config::getInstance();
90 	if (pConfig && m_pSamplUi) {
91 		const bool bPlugin = m_pSamplUi->isPlugin();
92 		m_ui.ProgramsPreviewCheckBox->setChecked(pConfig->bProgramsPreview);
93 		m_ui.UseNativeDialogsCheckBox->setChecked(pConfig->bUseNativeDialogs);
94 		m_ui.KnobDialModeComboBox->setCurrentIndex(pConfig->iKnobDialMode);
95 		m_ui.KnobEditModeComboBox->setCurrentIndex(pConfig->iKnobEditMode);
96 		m_ui.FrameTimeFormatComboBox->setCurrentIndex(pConfig->iFrameTimeFormat);
97 		m_ui.RandomizePercentSpinBox->setValue(pConfig->fRandomizePercent);
98 		m_ui.PitchShiftTypeComboBox->setCurrentIndex(pConfig->iPitchShiftType);
99 		// Custom display options (only for no-plugin forms)...
100 		resetCustomColorThemes(pConfig->sCustomColorTheme);
101 		resetCustomStyleThemes(pConfig->sCustomStyleTheme);
102 		// Load controllers database...
103 		samplv1_controls *pControls = m_pSamplUi->controls();
104 		if (pControls) {
105 			m_ui.ControlsTreeWidget->loadControls(pControls);
106 			m_ui.ControlsEnabledCheckBox->setEnabled(bPlugin);
107 			m_ui.ControlsEnabledCheckBox->setChecked(pControls->enabled());
108 		}
109 		// Load programs database...
110 		samplv1_programs *pPrograms = m_pSamplUi->programs();
111 		if (pPrograms) {
112 			m_ui.ProgramsTreeWidget->loadPrograms(pPrograms);
113 			m_ui.ProgramsEnabledCheckBox->setEnabled(bPlugin);
114 			m_ui.ProgramsPreviewCheckBox->setEnabled(!bPlugin);
115 			m_ui.ProgramsEnabledCheckBox->setChecked(pPrograms->enabled());
116 		}
117 		// Initialize conveniency options...
118 		loadComboBoxHistory(m_ui.TuningScaleFileComboBox);
119 		loadComboBoxHistory(m_ui.TuningKeyMapFileComboBox);
120 		// Micro-tonal tuning settings...
121 		const int iTuningTab = (m_pSamplUi->isTuningEnabled() ? 1 : 0);
122 		m_ui.TuningTabBar->setCurrentIndex(iTuningTab);
123 		tuningTabChanged(iTuningTab);
124 	}
125 
126 	// Signal/slots connections...
127 	QObject::connect(m_ui.ControlsAddItemToolButton,
128 		SIGNAL(clicked()),
129 		SLOT(controlsAddItem()));
130 	QObject::connect(m_ui.ControlsEditToolButton,
131 		SIGNAL(clicked()),
132 		SLOT(controlsEditItem()));
133 	QObject::connect(m_ui.ControlsDeleteToolButton,
134 		SIGNAL(clicked()),
135 		SLOT(controlsDeleteItem()));
136 
137 	QObject::connect(m_ui.ControlsTreeWidget,
138 		SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
139 		SLOT(controlsCurrentChanged()));
140 	QObject::connect(m_ui.ControlsTreeWidget,
141 		SIGNAL(itemChanged(QTreeWidgetItem *, int)),
142 		SLOT(controlsChanged()));
143 
144 	QObject::connect(m_ui.ControlsEnabledCheckBox,
145 		SIGNAL(toggled(bool)),
146 		SLOT(controlsEnabled(bool)));
147 
148 	QObject::connect(m_ui.ProgramsAddBankToolButton,
149 		SIGNAL(clicked()),
150 		SLOT(programsAddBankItem()));
151 	QObject::connect(m_ui.ProgramsAddItemToolButton,
152 		SIGNAL(clicked()),
153 		SLOT(programsAddItem()));
154 	QObject::connect(m_ui.ProgramsEditToolButton,
155 		SIGNAL(clicked()),
156 		SLOT(programsEditItem()));
157 	QObject::connect(m_ui.ProgramsDeleteToolButton,
158 		SIGNAL(clicked()),
159 		SLOT(programsDeleteItem()));
160 
161 	QObject::connect(m_ui.ProgramsTreeWidget,
162 		SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
163 		SLOT(programsCurrentChanged()));
164 	QObject::connect(m_ui.ProgramsTreeWidget,
165 		SIGNAL(itemChanged(QTreeWidgetItem *, int)),
166 		SLOT(programsChanged()));
167 	QObject::connect(m_ui.ProgramsTreeWidget,
168 		SIGNAL(itemActivated(QTreeWidgetItem *, int)),
169 		SLOT(programsActivated()));
170 
171 	QObject::connect(m_ui.ProgramsEnabledCheckBox,
172 		SIGNAL(toggled(bool)),
173 		SLOT(programsEnabled(bool)));
174 
175 	// Custom context menu...
176 	m_ui.ControlsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
177 	m_ui.ProgramsTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
178 
179 	QObject::connect(m_ui.ControlsTreeWidget,
180 		SIGNAL(customContextMenuRequested(const QPoint&)),
181 		SLOT(controlsContextMenuRequested(const QPoint&)));
182 	QObject::connect(m_ui.ProgramsTreeWidget,
183 		SIGNAL(customContextMenuRequested(const QPoint&)),
184 		SLOT(programsContextMenuRequested(const QPoint&)));
185 
186 	// Tuning slots...
187 	QObject::connect(m_ui.TuningTabBar,
188 		SIGNAL(currentChanged(int)),
189 		SLOT(tuningTabChanged(int)));
190 	QObject::connect(m_ui.TuningEnabledCheckBox,
191 		SIGNAL(toggled(bool)),
192 		SLOT(tuningChanged()));
193 	QObject::connect(m_ui.TuningRefNoteComboBox,
194 		SIGNAL(activated(int)),
195 		SLOT(tuningChanged()));
196 	QObject::connect(m_ui.TuningRefPitchSpinBox,
197 		SIGNAL(valueChanged(double)),
198 		SLOT(tuningChanged()));
199 	QObject::connect(m_ui.TuningRefNotePushButton,
200 		SIGNAL(clicked()),
201 		SLOT(tuningRefNoteClicked()));
202 	QObject::connect(m_ui.TuningScaleFileComboBox,
203 		SIGNAL(activated(int)),
204 		SLOT(tuningChanged()));
205 	QObject::connect(m_ui.TuningScaleFileToolButton,
206 		SIGNAL(clicked()),
207 		SLOT(tuningScaleFileClicked()));
208 	QObject::connect(m_ui.TuningKeyMapFileToolButton,
209 		SIGNAL(clicked()),
210 		SLOT(tuningKeyMapFileClicked()));
211 	QObject::connect(m_ui.TuningKeyMapFileComboBox,
212 		SIGNAL(activated(int)),
213 		SLOT(tuningChanged()));
214 
215 	// Options slots...
216 	QObject::connect(m_ui.ProgramsPreviewCheckBox,
217 		SIGNAL(toggled(bool)),
218 		SLOT(optionsChanged()));
219 	QObject::connect(m_ui.UseNativeDialogsCheckBox,
220 		SIGNAL(toggled(bool)),
221 		SLOT(optionsChanged()));
222 	QObject::connect(m_ui.KnobDialModeComboBox,
223 		SIGNAL(activated(int)),
224 		SLOT(optionsChanged()));
225 	QObject::connect(m_ui.KnobEditModeComboBox,
226 		SIGNAL(activated(int)),
227 		SLOT(optionsChanged()));
228 	QObject::connect(m_ui.CustomColorThemeComboBox,
229 		SIGNAL(activated(int)),
230 		SLOT(optionsChanged()));
231 	QObject::connect(m_ui.CustomColorThemeToolButton,
232 		SIGNAL(clicked()),
233 		SLOT(editCustomColorThemes()));
234 	QObject::connect(m_ui.CustomStyleThemeComboBox,
235 		SIGNAL(activated(int)),
236 		SLOT(optionsChanged()));
237 	QObject::connect(m_ui.FrameTimeFormatComboBox,
238 		SIGNAL(activated(int)),
239 		SLOT(optionsChanged()));
240 	QObject::connect(m_ui.RandomizePercentSpinBox,
241 		SIGNAL(valueChanged(double)),
242 		SLOT(optionsChanged()));
243 	QObject::connect(m_ui.PitchShiftTypeComboBox,
244 		SIGNAL(activated(int)),
245 		SLOT(optionsChanged()));
246 
247 	// Dialog commands...
248 	QObject::connect(m_ui.DialogButtonBox,
249 		SIGNAL(accepted()),
250 		SLOT(accept()));
251 	QObject::connect(m_ui.DialogButtonBox,
252 		SIGNAL(rejected()),
253 		SLOT(reject()));
254 
255 	// Done.
256 	stabilize();
257 }
258 
259 
260 // dtor.
~samplv1widget_config(void)261 samplv1widget_config::~samplv1widget_config (void)
262 {
263 	delete p_ui;
264 }
265 
266 
ui_instance(void) const267 samplv1_ui *samplv1widget_config::ui_instance (void) const
268 {
269 	return m_pSamplUi;
270 }
271 
272 
273 // controllers command slots.
controlsAddItem(void)274 void samplv1widget_config::controlsAddItem (void)
275 {
276 	m_ui.ControlsTreeWidget->addControlItem();
277 
278 	controlsChanged();
279 }
280 
281 
controlsEditItem(void)282 void samplv1widget_config::controlsEditItem (void)
283 {
284 	QTreeWidgetItem *pItem = m_ui.ControlsTreeWidget->currentItem();
285 	if (pItem)
286 		m_ui.ControlsTreeWidget->editItem(pItem, 0);
287 
288 	controlsChanged();
289 }
290 
291 
controlsDeleteItem(void)292 void samplv1widget_config::controlsDeleteItem (void)
293 {
294 	QTreeWidgetItem *pItem = m_ui.ControlsTreeWidget->currentItem();
295 	if (pItem)
296 		delete pItem;
297 
298 	controlsChanged();
299 }
300 
301 
302 // controllers janitorial slots.
controlsCurrentChanged(void)303 void samplv1widget_config::controlsCurrentChanged (void)
304 {
305 	stabilize();
306 }
307 
308 
controlsContextMenuRequested(const QPoint & pos)309 void samplv1widget_config::controlsContextMenuRequested ( const QPoint& pos )
310 {
311 	QTreeWidgetItem *pItem = m_ui.ControlsTreeWidget->currentItem();
312 
313 	QMenu menu(this);
314 	QAction *pAction;
315 
316 	bool bEnabled = (m_pSamplUi && m_pSamplUi->controls() != nullptr);
317 
318 	pAction = menu.addAction(QIcon(":/images/samplv1_preset.png"),
319 		tr("&Add Controller"), this, SLOT(controlsAddItem()));
320 	pAction->setEnabled(bEnabled);
321 
322 	menu.addSeparator();
323 
324 	bEnabled = bEnabled && (pItem != nullptr);
325 
326 	pAction = menu.addAction(QIcon(":/images/presetEdit.png"),
327 		tr("&Edit"), this, SLOT(controlsEditItem()));
328 	pAction->setEnabled(bEnabled);
329 
330 	menu.addSeparator();
331 
332 	pAction = menu.addAction(QIcon(":/images/presetDelete.png"),
333 		tr("&Delete"), this, SLOT(controlsDeleteItem()));
334 	pAction->setEnabled(bEnabled);
335 
336 	menu.exec(m_ui.ControlsTreeWidget->mapToGlobal(pos));
337 }
338 
339 
controlsEnabled(bool bOn)340 void samplv1widget_config::controlsEnabled ( bool bOn )
341 {
342 	if (m_pSamplUi) {
343 		samplv1_controls *pControls = m_pSamplUi->controls();
344 		if (pControls && m_pSamplUi->isPlugin())
345 			pControls->enabled(bOn);
346 	}
347 
348 	controlsChanged();
349 }
350 
351 
controlsChanged(void)352 void samplv1widget_config::controlsChanged (void)
353 {
354 	++m_iDirtyControls;
355 
356 	stabilize();
357 }
358 
359 
360 // programs command slots.
programsAddBankItem(void)361 void samplv1widget_config::programsAddBankItem (void)
362 {
363 	m_ui.ProgramsTreeWidget->addBankItem();
364 
365 	programsChanged();
366 }
367 
368 
programsAddItem(void)369 void samplv1widget_config::programsAddItem (void)
370 {
371 	m_ui.ProgramsTreeWidget->addProgramItem();
372 
373 	programsChanged();
374 }
375 
376 
programsEditItem(void)377 void samplv1widget_config::programsEditItem (void)
378 {
379 	QTreeWidgetItem *pItem = m_ui.ProgramsTreeWidget->currentItem();
380 	if (pItem)
381 		m_ui.ProgramsTreeWidget->editItem(pItem, 1);
382 
383 	programsChanged();
384 }
385 
386 
programsDeleteItem(void)387 void samplv1widget_config::programsDeleteItem (void)
388 {
389 	QTreeWidgetItem *pItem = m_ui.ProgramsTreeWidget->currentItem();
390 	if (pItem)
391 		delete pItem;
392 
393 	programsChanged();
394 }
395 
396 
397 // programs janitor slots.
programsCurrentChanged(void)398 void samplv1widget_config::programsCurrentChanged (void)
399 {
400 	stabilize();
401 }
402 
403 
programsContextMenuRequested(const QPoint & pos)404 void samplv1widget_config::programsContextMenuRequested ( const QPoint& pos )
405 {
406 	QTreeWidgetItem *pItem = m_ui.ProgramsTreeWidget->currentItem();
407 
408 	QMenu menu(this);
409 	QAction *pAction;
410 
411 	bool bEnabled = (m_pSamplUi && m_pSamplUi->programs() != nullptr);
412 
413 	pAction = menu.addAction(QIcon(":/images/presetBank.png"),
414 		tr("Add &Bank"), this, SLOT(programsAddBankItem()));
415 	pAction->setEnabled(bEnabled);
416 
417 	pAction = menu.addAction(QIcon(":/images/samplv1_preset.png"),
418 		tr("&Add Program"), this, SLOT(programsAddItem()));
419 	pAction->setEnabled(bEnabled);
420 
421 	menu.addSeparator();
422 
423 	bEnabled = bEnabled && (pItem != nullptr);
424 
425 	pAction = menu.addAction(QIcon(":/images/presetEdit.png"),
426 		tr("&Edit"), this, SLOT(programsEditItem()));
427 	pAction->setEnabled(bEnabled);
428 
429 	menu.addSeparator();
430 
431 	pAction = menu.addAction(QIcon(":/images/presetDelete.png"),
432 		tr("&Delete"), this, SLOT(programsDeleteItem()));
433 	pAction->setEnabled(bEnabled);
434 
435 	menu.exec(m_ui.ProgramsTreeWidget->mapToGlobal(pos));
436 }
437 
438 
programsEnabled(bool bOn)439 void samplv1widget_config::programsEnabled ( bool bOn )
440 {
441 	if (m_pSamplUi) {
442 		samplv1_programs *pPrograms = m_pSamplUi->programs();
443 		if (pPrograms && m_pSamplUi->isPlugin())
444 			pPrograms->enabled(bOn);
445 	}
446 
447 	programsChanged();
448 }
449 
450 
programsChanged(void)451 void samplv1widget_config::programsChanged (void)
452 {
453 	++m_iDirtyPrograms;
454 
455 	stabilize();
456 }
457 
458 
programsActivated(void)459 void samplv1widget_config::programsActivated (void)
460 {
461 	if (m_pSamplUi) {
462 		samplv1_programs *pPrograms = m_pSamplUi->programs();
463 		if (m_ui.ProgramsPreviewCheckBox->isChecked() && pPrograms)
464 			m_ui.ProgramsTreeWidget->selectProgram(pPrograms);
465 	}
466 
467 	stabilize();
468 }
469 
470 
471 // tuning command slots
tuningTabChanged(int iTuningTab)472 void samplv1widget_config::tuningTabChanged ( int iTuningTab )
473 {
474 	// Prevent loss of some tuning changes here...
475 	if (m_iDirtyTuning > 0 &&
476 		QMessageBox::warning(this,
477 			tr("Warning"),
478 			tr("%1 tuning settings have been changed.\n\n"
479 			"Do you want to discard the changes?")
480 			.arg(m_ui.TuningTabBar->tabText(1 - iTuningTab).remove('&')),
481 			QMessageBox::Discard | QMessageBox::Cancel)
482 			== QMessageBox::Cancel) {
483 		const bool bBlockSignals = m_ui.TuningTabBar->blockSignals(true);
484 		m_ui.TuningTabBar->setCurrentIndex(1 - iTuningTab);
485 		m_ui.TuningTabBar->blockSignals(bBlockSignals);
486 		return;
487 	}
488 
489 	if (iTuningTab == 0) {
490 		// Global (default) scope...
491 		samplv1_config *pConfig = samplv1_config::getInstance();
492 		if (pConfig) {
493 			m_ui.TuningEnabledCheckBox->setChecked(pConfig->bTuningEnabled);
494 			m_ui.TuningRefNoteComboBox->setCurrentIndex(pConfig->iTuningRefNote);
495 			m_ui.TuningRefPitchSpinBox->setValue(double(pConfig->fTuningRefPitch));
496 			setComboBoxCurrentItem(
497 				m_ui.TuningScaleFileComboBox,
498 				QFileInfo(pConfig->sTuningScaleFile));
499 			setComboBoxCurrentItem(
500 				m_ui.TuningKeyMapFileComboBox,
501 				QFileInfo(pConfig->sTuningKeyMapFile));
502 		}
503 	}
504 	else
505 	if (m_pSamplUi) {
506 		// Instance scope...
507 		m_ui.TuningEnabledCheckBox->setChecked(m_pSamplUi->isTuningEnabled());
508 		m_ui.TuningRefNoteComboBox->setCurrentIndex(m_pSamplUi->tuningRefNote());
509 		m_ui.TuningRefPitchSpinBox->setValue(double(m_pSamplUi->tuningRefPitch()));
510 		setComboBoxCurrentItem(
511 			m_ui.TuningScaleFileComboBox,
512 			QFileInfo(QString::fromUtf8(m_pSamplUi->tuningScaleFile())));
513 		setComboBoxCurrentItem(
514 			m_ui.TuningKeyMapFileComboBox,
515 			QFileInfo(QString::fromUtf8(m_pSamplUi->tuningKeyMapFile())));
516 	}
517 
518 	// Reset tuning dirty flag...
519 	m_iDirtyTuning = 0;
520 }
521 
522 
tuningRefNoteClicked(void)523 void samplv1widget_config::tuningRefNoteClicked (void)
524 {
525 	m_ui.TuningRefNoteComboBox->setCurrentIndex(69);
526 	m_ui.TuningRefPitchSpinBox->setValue(double(440.0f));
527 
528 	tuningChanged();
529 }
530 
531 
tuningScaleFileClicked(void)532 void samplv1widget_config::tuningScaleFileClicked (void)
533 {
534 	samplv1_config *pConfig = samplv1_config::getInstance();
535 	if (pConfig == nullptr)
536 		return;
537 
538 	QString sTuningScaleFile = m_ui.TuningScaleFileComboBox->currentText();
539 
540 	const QString  sExt("scl");
541 	const QString& sTitle  = tr("Open Scale File");
542 
543 	QStringList filters;
544 	filters.append(tr("Scale files (*.%1)").arg(sExt));
545 	filters.append(tr("All files (*.*)"));
546 	const QString& sFilter = filters.join(";;");
547 
548 	QWidget *pParentWidget = nullptr;
549 	QFileDialog::Options options;
550 	if (pConfig->bDontUseNativeDialogs) {
551 		options |= QFileDialog::DontUseNativeDialog;
552 		pParentWidget = QWidget::window();
553 	}
554 #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0)
555 	sTuningScaleFile = QFileDialog::getOpenFileName(pParentWidget,
556 		sTitle, pConfig->sTuningScaleDir, sFilter, nullptr, options);
557 #else
558 	QFileDialog fileDialog(pParentWidget,
559 		sTitle, sTuningScaleFile, sFilter);
560 	fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
561 	fileDialog.setFileMode(QFileDialog::ExistingFiles);
562 	fileDialog.setDefaultSuffix(sExt);
563 	QList<QUrl> urls(fileDialog.sidebarUrls());
564 	urls.append(QUrl::fromLocalFile(pConfig->sTuningScaleDir));
565 	fileDialog.setSidebarUrls(urls);
566 	fileDialog.setOptions(options);
567 	if (fileDialog.exec())
568 		sTuningScaleFile = fileDialog.selectedFiles().first();
569 #endif
570 
571 	if (!sTuningScaleFile.isEmpty()) {
572 		const QFileInfo info(sTuningScaleFile);
573 		if (setComboBoxCurrentItem(m_ui.TuningScaleFileComboBox, info)) {
574 			pConfig->sTuningScaleDir = info.absolutePath();
575 			tuningChanged();
576 		}
577 	}
578 }
579 
580 
tuningKeyMapFileClicked(void)581 void samplv1widget_config::tuningKeyMapFileClicked (void)
582 {
583 	samplv1_config *pConfig = samplv1_config::getInstance();
584 	if (pConfig == nullptr)
585 		return;
586 
587 	QString sTuningKeyMapFile = m_ui.TuningKeyMapFileComboBox->currentText();
588 
589 	const QString  sExt("kbm");
590 	const QString& sTitle  = tr("Open Key Map File");
591 
592 	QStringList filters;
593 	filters.append(tr("Key Map files (*.%1)").arg(sExt));
594 	filters.append(tr("All files (*.*)"));
595 	const QString& sFilter = filters.join(";;");
596 
597 	QWidget *pParentWidget = nullptr;
598 	QFileDialog::Options options;
599 	if (pConfig->bDontUseNativeDialogs) {
600 		options |= QFileDialog::DontUseNativeDialog;
601 		pParentWidget = QWidget::window();
602 	}
603 #if 1//QT_VERSION < QT_VERSION_CHECK(4, 4, 0)
604 	sTuningKeyMapFile = QFileDialog::getOpenFileName(pParentWidget,
605 		sTitle, pConfig->sTuningKeyMapDir, sFilter, nullptr, options);
606 #else
607 	QFileDialog fileDialog(pParentWidget,
608 		sTitle, sTuningScaleFile, sFilter);
609 	fileDialog.setAcceptMode(QFileDialog::AcceptOpen);
610 	fileDialog.setFileMode(QFileDialog::ExistingFiles);
611 	fileDialog.setDefaultSuffix(sExt);
612 	QList<QUrl> urls(fileDialog.sidebarUrls());
613 	urls.append(QUrl::fromLocalFile(pConfig->sTuningKeyMapDir));
614 	fileDialog.setSidebarUrls(urls);
615 	fileDialog.setOptions(options);
616 	if (fileDialog.exec())
617 		sTuningKeyMapFile = fileDialog.selectedFiles().first();
618 #endif
619 
620 	if (!sTuningKeyMapFile.isEmpty()) {
621 		const QFileInfo info(sTuningKeyMapFile);
622 		if (setComboBoxCurrentItem(m_ui.TuningKeyMapFileComboBox, info)) {
623 			pConfig->sTuningKeyMapDir = info.absolutePath();
624 			tuningChanged();
625 		}
626 	}
627 }
628 
629 
tuningChanged(void)630 void samplv1widget_config::tuningChanged (void)
631 {
632 	++m_iDirtyTuning;
633 
634 	stabilize();
635 }
636 
637 
638 // options slot.
optionsChanged(void)639 void samplv1widget_config::optionsChanged (void)
640 {
641 	++m_iDirtyOptions;
642 
643 	stabilize();
644 }
645 
646 
647 // stabilizer.
stabilize(void)648 void samplv1widget_config::stabilize (void)
649 {
650 	QTreeWidgetItem *pItem = m_ui.ControlsTreeWidget->currentItem();
651 	bool bEnabled = (m_pSamplUi && m_pSamplUi->controls() != nullptr);
652 	m_ui.ControlsAddItemToolButton->setEnabled(bEnabled);
653 	bEnabled = bEnabled && (pItem != nullptr);
654 	m_ui.ControlsEditToolButton->setEnabled(bEnabled);
655 	m_ui.ControlsDeleteToolButton->setEnabled(bEnabled);
656 
657 	pItem = m_ui.ProgramsTreeWidget->currentItem();
658 	bEnabled = (m_pSamplUi && m_pSamplUi->programs() != nullptr);
659 	m_ui.ProgramsPreviewCheckBox->setEnabled(
660 		bEnabled && m_ui.ProgramsEnabledCheckBox->isChecked());
661 	m_ui.ProgramsAddBankToolButton->setEnabled(bEnabled);
662 	m_ui.ProgramsAddItemToolButton->setEnabled(bEnabled);
663 	bEnabled = bEnabled && (pItem != nullptr);
664 	m_ui.ProgramsEditToolButton->setEnabled(bEnabled);
665 	m_ui.ProgramsDeleteToolButton->setEnabled(bEnabled);
666 
667 	bEnabled = m_ui.TuningEnabledCheckBox->isChecked();
668 	const bool bTuningRefEnabled = bEnabled
669 		&& comboBoxCurrentItem(m_ui.TuningKeyMapFileComboBox).isEmpty();
670 	m_ui.TuningRefNoteTextLabel->setEnabled(bTuningRefEnabled);
671 	m_ui.TuningRefNoteComboBox->setEnabled(bTuningRefEnabled);
672 	m_ui.TuningRefPitchSpinBox->setEnabled(bTuningRefEnabled);
673 	m_ui.TuningRefNotePushButton->setEnabled(bTuningRefEnabled);
674 	m_ui.TuningScaleFileTextLabel->setEnabled(bEnabled);
675 	m_ui.TuningScaleFileComboBox->setEnabled(bEnabled);
676 	m_ui.TuningScaleFileToolButton->setEnabled(bEnabled);
677 	m_ui.TuningKeyMapFileTextLabel->setEnabled(bEnabled);
678 	m_ui.TuningKeyMapFileComboBox->setEnabled(bEnabled);
679 	m_ui.TuningKeyMapFileToolButton->setEnabled(bEnabled);
680 
681 	const bool bValid
682 		= (m_iDirtyTuning   > 0
683 		|| m_iDirtyControls > 0
684 		|| m_iDirtyPrograms > 0
685 		|| m_iDirtyOptions  > 0);
686 	m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(bValid);
687 }
688 
689 
690 // dialog slots.
accept(void)691 void samplv1widget_config::accept (void)
692 {
693 	samplv1_config *pConfig = samplv1_config::getInstance();
694 
695 	if (m_iDirtyTuning > 0 && pConfig && m_pSamplUi) {
696 		// Micro-tonal tuning settings...
697 		if (m_ui.TuningTabBar->currentIndex() == 0) {
698 			// Global (default) scope...
699 			pConfig->bTuningEnabled = m_ui.TuningEnabledCheckBox->isChecked();
700 			pConfig->iTuningRefNote = m_ui.TuningRefNoteComboBox->currentIndex();
701 			pConfig->fTuningRefPitch = float(m_ui.TuningRefPitchSpinBox->value());
702 			pConfig->sTuningScaleFile = comboBoxCurrentItem(m_ui.TuningScaleFileComboBox);
703 			pConfig->sTuningKeyMapFile = comboBoxCurrentItem(m_ui.TuningKeyMapFileComboBox);
704 		} else {
705 			m_pSamplUi->setTuningEnabled(
706 				m_ui.TuningEnabledCheckBox->isChecked());
707 			m_pSamplUi->setTuningRefNote(
708 				m_ui.TuningRefNoteComboBox->currentIndex());
709 			m_pSamplUi->setTuningRefPitch(
710 				float(m_ui.TuningRefPitchSpinBox->value()));
711 			m_pSamplUi->setTuningScaleFile(comboBoxCurrentItem(
712 				m_ui.TuningScaleFileComboBox).toUtf8().constData());
713 			m_pSamplUi->setTuningKeyMapFile(comboBoxCurrentItem(
714 				m_ui.TuningKeyMapFileComboBox).toUtf8().constData());
715 		}
716 		// Reset/update micro-tonal tuning...
717 		m_pSamplUi->resetTuning();
718 		// Save other conveniency options...
719 		saveComboBoxHistory(m_ui.TuningScaleFileComboBox);
720 		saveComboBoxHistory(m_ui.TuningKeyMapFileComboBox);
721 		// Reset dirty flag.
722 		m_iDirtyTuning = 0;
723 	}
724 
725 	if (m_iDirtyControls > 0 && pConfig && m_pSamplUi) {
726 		// Save controls...
727 		samplv1_controls *pControls = m_pSamplUi->controls();
728 		if (pControls) {
729 			m_ui.ControlsTreeWidget->saveControls(pControls);
730 			pConfig->saveControls(pControls);
731 			// Reset dirty flag.
732 			m_iDirtyControls = 0;
733 		}
734 	}
735 
736 	if (m_iDirtyPrograms > 0 && pConfig && m_pSamplUi) {
737 		// Save programs...
738 		samplv1_programs *pPrograms = m_pSamplUi->programs();
739 		if (pPrograms) {
740 			m_ui.ProgramsTreeWidget->savePrograms(pPrograms);
741 			pConfig->savePrograms(pPrograms);
742 			// Reset dirty flag.
743 			m_iDirtyPrograms = 0;
744 		}
745 	}
746 
747 	if (m_iDirtyOptions > 0 && pConfig) {
748 		// Save options...
749 		pConfig->bProgramsPreview = m_ui.ProgramsPreviewCheckBox->isChecked();
750 		pConfig->bUseNativeDialogs = m_ui.UseNativeDialogsCheckBox->isChecked();
751 		pConfig->bDontUseNativeDialogs = !pConfig->bUseNativeDialogs;
752 		pConfig->iKnobDialMode = m_ui.KnobDialModeComboBox->currentIndex();
753 		samplv1widget_dial::setDialMode(
754 			samplv1widget_dial::DialMode(pConfig->iKnobDialMode));
755 		pConfig->iKnobEditMode = m_ui.KnobEditModeComboBox->currentIndex();
756 		samplv1widget_edit::setEditMode(
757 			samplv1widget_edit::EditMode(pConfig->iKnobEditMode));
758 		const QString sOldCustomColorTheme = pConfig->sCustomColorTheme;
759 		if (m_ui.CustomColorThemeComboBox->currentIndex() > 0)
760 			pConfig->sCustomColorTheme = m_ui.CustomColorThemeComboBox->currentText();
761 		else
762 			pConfig->sCustomColorTheme.clear();
763 		const QString sOldCustomStyleTheme = pConfig->sCustomStyleTheme;
764 		if (m_ui.CustomStyleThemeComboBox->currentIndex() > 0)
765 			pConfig->sCustomStyleTheme = m_ui.CustomStyleThemeComboBox->currentText();
766 		else
767 			pConfig->sCustomStyleTheme.clear();
768 		const int iOldFrameTimeFormat = pConfig->iFrameTimeFormat;
769 		const int iOldPitchShiftType  = pConfig->iPitchShiftType;
770 		pConfig->iFrameTimeFormat  = m_ui.FrameTimeFormatComboBox->currentIndex();
771 		pConfig->fRandomizePercent = float(m_ui.RandomizePercentSpinBox->value());
772 		pConfig->iPitchShiftType   = m_ui.PitchShiftTypeComboBox->currentIndex();
773 		int iNeedRestart = 0;
774 		if (pConfig->sCustomStyleTheme != sOldCustomStyleTheme) {
775 			if (pConfig->sCustomStyleTheme.isEmpty()) {
776 				++iNeedRestart;
777 			} else {
778 				QApplication::setStyle(
779 					QStyleFactory::create(pConfig->sCustomStyleTheme));
780 			}
781  		}
782  		if (pConfig->sCustomColorTheme != sOldCustomColorTheme) {
783 			if (pConfig->sCustomColorTheme.isEmpty()) {
784 				++iNeedRestart;
785 			} else {
786 				QPalette pal;
787 				if (samplv1widget_palette::namedPalette(
788 						pConfig, pConfig->sCustomColorTheme, pal))
789 					QApplication::setPalette(pal);
790 			}
791 		}
792 		if (pConfig->iFrameTimeFormat != iOldFrameTimeFormat)
793 			++iNeedRestart;
794 		if (pConfig->iPitchShiftType != iOldPitchShiftType) {
795 			++iNeedRestart;
796 			samplv1_pshifter::setDefaultType(
797 				samplv1_pshifter::Type(pConfig->iPitchShiftType));
798 		}
799 		// Show restart message if needed...
800  		if (iNeedRestart > 0) {
801 			QMessageBox::information(this,
802 				tr("Information"),
803 				tr("Some settings may be only effective\n"
804 				"next time you start this application."));
805 		}
806 		// Reset dirty flag.
807 		m_iDirtyOptions = 0;
808 	}
809 
810 	// Just go with dialog acceptance.
811 	QDialog::accept();
812 }
813 
814 
reject(void)815 void samplv1widget_config::reject (void)
816 {
817 	bool bReject = true;
818 
819 	// Check if there's any pending changes...
820 	if (m_iDirtyTuning   > 0 ||
821 		m_iDirtyControls > 0 ||
822 		m_iDirtyPrograms > 0 ||
823 		m_iDirtyOptions  > 0) {
824 		QMessageBox::StandardButtons buttons
825 			= QMessageBox::Discard | QMessageBox::Cancel;
826 		if (m_ui.DialogButtonBox->button(QDialogButtonBox::Ok)->isEnabled())
827 			buttons |= QMessageBox::Apply;
828 		switch (QMessageBox::warning(this,
829 			tr("Warning"),
830 			tr("Some settings have been changed.\n\n"
831 			"Do you want to apply the changes?"),
832 			buttons)) {
833 		case QMessageBox::Apply:
834 			accept();
835 			return;
836 		case QMessageBox::Discard:
837 			break;
838 		default:    // Cancel.
839 			bReject = false;
840 		}
841 	}
842 
843 	if (bReject)
844 		QDialog::reject();
845 }
846 
847 
848 // Custom color palette theme manager.
editCustomColorThemes(void)849 void samplv1widget_config::editCustomColorThemes (void)
850 {
851 	samplv1_config *pConfig = samplv1_config::getInstance();
852 	if (pConfig == nullptr)
853 		return;
854 
855 	samplv1widget_palette form(this);
856 	form.setSettings(pConfig);
857 
858 	QString sCustomColorTheme;
859 	int iDirtyCustomColorTheme = 0;
860 
861 	const int iCustomColorTheme
862 		= m_ui.CustomColorThemeComboBox->currentIndex();
863 	if (iCustomColorTheme > 0) {
864 		sCustomColorTheme = m_ui.CustomColorThemeComboBox->itemText(iCustomColorTheme);
865 		form.setPaletteName(sCustomColorTheme);
866 	}
867 
868 	if (form.exec() == QDialog::Accepted) {
869 		sCustomColorTheme = form.paletteName();
870 		++iDirtyCustomColorTheme;
871 	}
872 
873 	if (iDirtyCustomColorTheme > 0 || form.isDirty()) {
874 		resetCustomColorThemes(sCustomColorTheme);
875 		optionsChanged();
876 	}
877 }
878 
879 
880 // Custom color palette themes settler.
resetCustomColorThemes(const QString & sCustomColorTheme)881 void samplv1widget_config::resetCustomColorThemes (
882 	const QString& sCustomColorTheme )
883 {
884 	m_ui.CustomColorThemeComboBox->clear();
885 	m_ui.CustomColorThemeComboBox->addItem(
886 		tr(g_pszDefName));
887 	samplv1_config *pConfig = samplv1_config::getInstance();
888 	if (pConfig) m_ui.CustomColorThemeComboBox->addItems(
889 		samplv1widget_palette::namedPaletteList(pConfig));
890 
891 	int iCustomColorTheme = 0;
892 	if (!sCustomColorTheme.isEmpty()) {
893 		iCustomColorTheme = m_ui.CustomColorThemeComboBox->findText(
894 			sCustomColorTheme);
895 		if (iCustomColorTheme < 0)
896 			iCustomColorTheme = 0;
897 	}
898 	m_ui.CustomColorThemeComboBox->setCurrentIndex(iCustomColorTheme);
899 }
900 
901 
902 // Custom widget style themes settler.
resetCustomStyleThemes(const QString & sCustomStyleTheme)903 void samplv1widget_config::resetCustomStyleThemes (
904 	const QString& sCustomStyleTheme )
905 {
906 	m_ui.CustomStyleThemeComboBox->clear();
907 	m_ui.CustomStyleThemeComboBox->addItem(
908 		tr(g_pszDefName));
909 	m_ui.CustomStyleThemeComboBox->addItems(
910 		QStyleFactory::keys());
911 
912 	int iCustomStyleTheme = 0;
913 	if (!sCustomStyleTheme.isEmpty()) {
914 		iCustomStyleTheme = m_ui.CustomStyleThemeComboBox->findText(
915 			sCustomStyleTheme);
916 		if (iCustomStyleTheme < 0)
917 			iCustomStyleTheme = 0;
918 	}
919 	m_ui.CustomStyleThemeComboBox->setCurrentIndex(iCustomStyleTheme);
920 }
921 
922 
923 // Combo box history persistence helper implementation.
loadComboBoxHistory(QComboBox * pComboBox)924 void samplv1widget_config::loadComboBoxHistory ( QComboBox *pComboBox )
925 {
926 	samplv1_config *pConfig = samplv1_config::getInstance();
927 	if (pConfig == nullptr)
928 		return;
929 
930 	// Load combobox list from configuration settings file...
931 	const bool bBlockSignals = pComboBox->blockSignals(true);
932 	pConfig->beginGroup("/History");
933 	const QStringList& history
934 		= pConfig->value('/' + pComboBox->objectName()).toStringList();
935 	QStringListIterator iter(history);
936 	while (iter.hasNext()) {
937 		const QFileInfo info(iter.next());
938 		if (info.exists() && info.isReadable()) {
939 			const QString& sPath = info.canonicalFilePath();
940 			pComboBox->insertItem(0, info.fileName(), sPath);
941 		}
942 	}
943 	pConfig->endGroup();
944 	pComboBox->blockSignals(bBlockSignals);
945 }
946 
947 
saveComboBoxHistory(QComboBox * pComboBox)948 void samplv1widget_config::saveComboBoxHistory ( QComboBox *pComboBox )
949 {
950 	samplv1_config *pConfig = samplv1_config::getInstance();
951 	if (pConfig == nullptr)
952 		return;
953 
954 	// Save combobox list to configuration settings file...
955 	const bool bBlockSignals = pComboBox->blockSignals(true);
956 	pConfig->beginGroup("/History");
957 	QStringList history;
958 	const int iCount = pComboBox->count();
959 	for (int i = 0; i < iCount; ++i) {
960 		const QString& sData = pComboBox->itemData(i).toString();
961 		if (!sData.isEmpty())
962 			history.prepend(sData);
963 	}
964 	pConfig->setValue('/' + pComboBox->objectName(), history);
965 	pConfig->endGroup();
966 	pComboBox->blockSignals(bBlockSignals);
967 }
968 
969 
970 // Combo box settter/gettter helper prototypes.
setComboBoxCurrentItem(QComboBox * pComboBox,const QFileInfo & info)971 bool samplv1widget_config::setComboBoxCurrentItem (
972 	QComboBox *pComboBox, const QFileInfo& info )
973 {
974 	const bool bBlockSignals = pComboBox->blockSignals(true);
975 	const bool bResult = info.exists() && info.isReadable();
976 	if (bResult) {
977 		const QString& sData = info.canonicalFilePath();
978 		int iIndex = pComboBox->findData(sData);
979 		if (iIndex < 0) {
980 			pComboBox->insertItem(0, info.fileName(), sData);
981 			iIndex = 0;
982 		}
983 		pComboBox->setCurrentIndex(iIndex);
984 		pComboBox->setToolTip(sData);
985 	} else {
986 		pComboBox->setCurrentIndex(pComboBox->count() - 1);
987 		pComboBox->setToolTip(pComboBox->currentText());
988 	}
989 	pComboBox->blockSignals(bBlockSignals);
990 
991 	return bResult;
992 }
993 
994 
comboBoxCurrentItem(QComboBox * pComboBox)995 QString samplv1widget_config::comboBoxCurrentItem ( QComboBox *pComboBox )
996 {
997 	QString sData;
998 
999 	const int iIndex = pComboBox->currentIndex();
1000 	if (iIndex >= 0)
1001 		sData = pComboBox->itemData(iIndex).toString();
1002 
1003 	return sData;
1004 }
1005 
1006 
1007 // end of samplv1widget_config.cpp
1008