code:
/*
 * SetupDialog.cpp - dialog for setting up LMMS
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QComboBox>
#include <QImageReader>
#include <QLabel>
#include <QLayout>
#include <QLineEdit>
#include <QMessageBox>
#include <QScrollArea>

#include "AudioDeviceSetupWidget.h"
#include "debug.h"
#include "embed.h"
#include "Engine.h"
#include "FileDialog.h"
#include "gui_templates.h"
#include "MainWindow.h"
#include "MidiSetupWidget.h"
#include "Mixer.h"
#include "ProjectJournal.h"
#include "SetupDialog.h"
#include "TabBar.h"
#include "TabButton.h"
#include "ToolTip.h"

// Platform-specific audio-interface classes.
#include "AudioAlsa.h"
#include "AudioAlsaSetupWidget.h"
#include "AudioDummy.h"
#include "AudioJack.h"
#include "AudioOss.h"
#include "AudioPortAudio.h"
#include "AudioPulseAudio.h"
#include "AudioSdl.h"
#include "AudioSndio.h"
#include "AudioSoundIo.h"

// Platform-specific midi-interface classes.
#include "MidiAlsaRaw.h"
#include "MidiAlsaSeq.h"
#include "MidiApple.h"
#include "MidiDummy.h"
#include "MidiJack.h"
#include "MidiOss.h"
#include "MidiSndio.h"
#include "MidiWinMM.h"

constexpr int BUFFERSIZE_RESOLUTION = 32;

inline void labelWidget(QWidget * w, const QString & txt)
{
	QLabel * title = new QLabel(txt, w);
	QFont f = title->font();
	f.setBold(true);
	title->setFont(pointSize<12>(f));

	assert(dynamic_cast<QBoxLayout *>(w->layout()) != NULL);

	dynamic_cast<QBoxLayout *>(w->layout())->addSpacing(5);
	dynamic_cast<QBoxLayout *>(w->layout())->addWidget(title);
}

SetupDialog::SetupDialog(ConfigTabs tab_to_open) :
	m_displaydBFS(ConfigManager::inst()->value(
			"app", "displaydbfs").toInt()),
	m_tooltips(!ConfigManager::inst()->value(
			"tooltips", "disabled").toInt()),
	m_displayWaveform(ConfigManager::inst()->value(
			"ui", "displaywaveform").toInt()),
	m_printNoteLabels(ConfigManager::inst()->value(
			"ui", "printnotelabels").toInt()),
	m_compactTrackButtons(ConfigManager::inst()->value(
			"ui", "compacttrackbuttons").toInt()),
	m_oneInstrumentTrackWindow(ConfigManager::inst()->value(
			"ui", "oneinstrumenttrackwindow").toInt()),
	m_sideBarOnRight(ConfigManager::inst()->value(
			"ui", "sidebaronright").toInt()),
	m_letPreviewsFinish(ConfigManager::inst()->value(
			"ui", "letpreviewsfinish").toInt()),
	m_soloLegacyBehavior(ConfigManager::inst()->value(
			"app", "sololegacybehavior", "0").toInt()),
	m_MMPZ(!ConfigManager::inst()->value(
			"app", "nommpz").toInt()),
	m_disableBackup(!ConfigManager::inst()->value(
			"app", "disablebackup").toInt()),
	m_openLastProject(ConfigManager::inst()->value(
			"app", "openlastproject").toInt()),
	m_lang(ConfigManager::inst()->value(
			"app", "language")),
	m_saveInterval(	ConfigManager::inst()->value(
			"ui", "saveinterval").toInt() < 1 ?
			MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES :
			ConfigManager::inst()->value(
			"ui", "saveinterval").toInt()),
	m_enableAutoSave(ConfigManager::inst()->value(
			"ui", "enableautosave", "1").toInt()),
	m_enableRunningAutoSave(ConfigManager::inst()->value(
			"ui", "enablerunningautosave", "0").toInt()),
	m_smoothScroll(ConfigManager::inst()->value(
			"ui", "smoothscroll").toInt()),
	m_animateAFP(ConfigManager::inst()->value(
			"ui", "animateafp", "1").toInt()),
	m_vstEmbedMethod(ConfigManager::inst()->vstEmbedMethod()),
	m_vstAlwaysOnTop(ConfigManager::inst()->value(
			"ui", "vstalwaysontop").toInt()),
	m_syncVSTPlugins(ConfigManager::inst()->value(
			"ui", "syncvstplugins", "1").toInt()),
	m_disableAutoQuit(ConfigManager::inst()->value(
			"ui", "disableautoquit", "1").toInt()),
	m_NaNHandler(ConfigManager::inst()->value(
			"app", "nanhandler", "1").toInt()),
	m_hqAudioDev(ConfigManager::inst()->value(
			"mixer", "hqaudio").toInt()),
	m_bufferSize(ConfigManager::inst()->value(
			"mixer", "framesperaudiobuffer").toInt()),
	m_workingDir(QDir::toNativeSeparators(ConfigManager::inst()->workingDir())),
	m_vstDir(QDir::toNativeSeparators(ConfigManager::inst()->vstDir())),
	m_ladspaDir(QDir::toNativeSeparators(ConfigManager::inst()->ladspaDir())),
	m_gigDir(QDir::toNativeSeparators(ConfigManager::inst()->gigDir())),
	m_sf2Dir(QDir::toNativeSeparators(ConfigManager::inst()->sf2Dir())),
#ifdef LMMS_HAVE_FLUIDSYNTH
	m_sf2File(QDir::toNativeSeparators(ConfigManager::inst()->sf2File())),
#endif
	m_themeDir(QDir::toNativeSeparators(ConfigManager::inst()->themeDir())),
	m_backgroundPicFile(QDir::toNativeSeparators(ConfigManager::inst()->backgroundPicFile()))
{
	setWindowIcon(embed::getIconPixmap("setup_general"));
	setWindowTitle(tr("Settings"));
	// TODO: Equivalent to the new setWindowFlag(Qt::WindowContextHelpButtonHint, false)
	setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
	setModal(true);
	setFixedSize(454, 400);

	Engine::projectJournal()->setJournalling(false);

	// Constants for positioning LED check boxes.
	const int XDelta = 10;
	const int YDelta = 18;

	// Main widget.
	QWidget * main_w = new QWidget(this);

	// Vertical layout.
	QVBoxLayout * vlayout = new QVBoxLayout(this);
	vlayout->setSpacing(0);
	vlayout->setMargin(0);

	// Horizontal layout.
	QHBoxLayout * hlayout = new QHBoxLayout(main_w);
	hlayout->setSpacing(0);
	hlayout->setMargin(0);

	// Tab bar for the main tabs.
	m_tabBar = new TabBar(main_w, QBoxLayout::TopToBottom);
	m_tabBar->setExclusive(true);
	m_tabBar->setFixedWidth(72);

	// Settings widget.
	QWidget * settings_w = new QWidget(main_w);
	settings_w->setFixedSize(360, 360);

	// General widget.
	QWidget * general_w = new QWidget(settings_w);
	QVBoxLayout * general_layout = new QVBoxLayout(general_w);
	general_layout->setSpacing(10);
	general_layout->setMargin(0);
	labelWidget(general_w, tr("General"));

	auto addLedCheckBox = [&XDelta, &YDelta, this](
		const QString &ledText,
		TabWidget* tw,
		int& counter,
		bool initialState,
		const char* toggledSlot,
		bool showRestartWarning
	){
		LedCheckBox * checkBox = new LedCheckBox(ledText, tw);
		counter++;
		checkBox->move(XDelta, YDelta * counter);
		checkBox->setChecked(initialState);
		connect(checkBox, SIGNAL(toggled(bool)), this, toggledSlot);
		if (showRestartWarning)
		{
			connect(checkBox, SIGNAL(toggled(bool)), this, SLOT(showRestartWarning()));
		}
	};

	int counter = 0;

	// GUI tab.
	TabWidget * gui_tw = new TabWidget(
			tr("Graphical user interface (GUI)"), general_w);

	addLedCheckBox(tr("Display volume as dBFS "), gui_tw, counter,
		m_displaydBFS, SLOT(toggleDisplaydBFS(bool)), true);
	addLedCheckBox(tr("Enable tooltips"), gui_tw, counter,
		m_tooltips, SLOT(toggleTooltips(bool)), true);
	addLedCheckBox(tr("Enable master oscilloscope by default"), gui_tw, counter,
		m_displayWaveform, SLOT(toggleDisplayWaveform(bool)), true);
	addLedCheckBox(tr("Enable all note labels in piano roll"), gui_tw, counter,
		m_printNoteLabels, SLOT(toggleNoteLabels(bool)), false);
	addLedCheckBox(tr("Enable compact track buttons"), gui_tw, counter,
		m_compactTrackButtons, SLOT(toggleCompactTrackButtons(bool)), true);
	addLedCheckBox(tr("Enable one instrument-track-window mode"), gui_tw, counter,
		m_oneInstrumentTrackWindow, SLOT(toggleOneInstrumentTrackWindow(bool)), true);
	addLedCheckBox(tr("Show sidebar on the right-hand side"), gui_tw, counter,
		m_sideBarOnRight, SLOT(toggleSideBarOnRight(bool)), true);
	addLedCheckBox(tr("Let sample previews continue when mouse is released"), gui_tw, counter,
		m_letPreviewsFinish, SLOT(toggleLetPreviewsFinish(bool)), false);
	addLedCheckBox(tr("Mute automation tracks during solo"), gui_tw, counter,
		m_soloLegacyBehavior, SLOT(toggleSoloLegacyBehavior(bool)), false);

	gui_tw->setFixedHeight(YDelta + YDelta * counter);

	counter = 0;

	// Projects tab.
	TabWidget * projects_tw = new TabWidget(
			tr("Projects"), general_w);

	addLedCheckBox(tr("Compress project files by default"), projects_tw, counter,
		m_MMPZ, SLOT(toggleMMPZ(bool)), true);
	addLedCheckBox(tr("Create a backup file when saving a project"), projects_tw, counter,
		m_disableBackup, SLOT(toggleDisableBackup(bool)), false);
	addLedCheckBox(tr("Reopen last project on startup"), projects_tw, counter,
		m_openLastProject, SLOT(toggleOpenLastProject(bool)), false);

	projects_tw->setFixedHeight(YDelta + YDelta * counter);

	// Language tab.
	TabWidget * lang_tw = new TabWidget(
			tr("Language"), general_w);
	lang_tw->setFixedHeight(48);
	QComboBox * changeLang = new QComboBox(lang_tw);
	changeLang->move(XDelta, 20);

	QDir dir(ConfigManager::inst()->localeDir());
	QStringList fileNames = dir.entryList(QStringList("*.qm"));
	for(int i = 0; i < fileNames.size(); ++i)
	{
		// Get locale extracted by filename.
		fileNames[i].truncate(fileNames[i].lastIndexOf('.'));
		m_languages.append(fileNames[i]);
		QString lang = QLocale(m_languages.last()).nativeLanguageName();
		changeLang->addItem(lang);
	}

	// If language unset, fallback to system language when available.
	if(m_lang == "")
	{
		QString tmp = QLocale::system().name().left(2);
		if(m_languages.contains(tmp))
		{
			m_lang = tmp;
		}
		else
		{
			m_lang = "en";
		}
	}

	for(int i = 0; i < changeLang->count(); ++i)
	{
		if(m_lang == m_languages.at(i))
		{
			changeLang->setCurrentIndex(i);
			break;
		}
	}

	connect(changeLang, SIGNAL(currentIndexChanged(int)),
			this, SLOT(setLanguage(int)));
	connect(changeLang, SIGNAL(currentIndexChanged(int)),
			this, SLOT(showRestartWarning()));

	// General layout ordering.
	general_layout->addWidget(gui_tw);
	general_layout->addWidget(projects_tw);
	general_layout->addWidget(lang_tw);
	general_layout->addStretch();

	// Performance widget.
	QWidget * performance_w = new QWidget(settings_w);
	QVBoxLayout * performance_layout = new QVBoxLayout(performance_w);
	performance_layout->setSpacing(10);
	performance_layout->setMargin(0);
	labelWidget(performance_w,
			tr("Performance"));

	// Autosave tab.
	TabWidget * auto_save_tw = new TabWidget(
			tr("Autosave"), performance_w);
	auto_save_tw->setFixedHeight(106);

	m_saveIntervalSlider = new QSlider(Qt::Horizontal, auto_save_tw);
	m_saveIntervalSlider->setValue(m_saveInterval);
	m_saveIntervalSlider->setRange(1, 20);
	m_saveIntervalSlider->setTickInterval(1);
	m_saveIntervalSlider->setPageStep(1);
	m_saveIntervalSlider->setGeometry(10, 18, 340, 18);
	m_saveIntervalSlider->setTickPosition(QSlider::TicksBelow);

	connect(m_saveIntervalSlider, SIGNAL(valueChanged(int)),
			this, SLOT(setAutoSaveInterval(int)));

	m_saveIntervalLbl = new QLabel(auto_save_tw);
	m_saveIntervalLbl->setGeometry(10, 40, 200, 24);
	setAutoSaveInterval(m_saveIntervalSlider->value());

	m_autoSave = new LedCheckBox(
			tr("Enable autosave"), auto_save_tw);
	m_autoSave->move(10, 70);
	m_autoSave->setChecked(m_enableAutoSave);
	connect(m_autoSave, SIGNAL(toggled(bool)),
			this, SLOT(toggleAutoSave(bool)));

	m_runningAutoSave = new LedCheckBox(
			tr("Allow autosave while playing"), auto_save_tw);
	m_runningAutoSave->move(20, 88);
	m_runningAutoSave->setChecked(m_enableRunningAutoSave);
	connect(m_runningAutoSave, SIGNAL(toggled(bool)),
			this, SLOT(toggleRunningAutoSave(bool)));

	QPushButton * autoSaveResetBtn = new QPushButton(
			embed::getIconPixmap("reload"), "", auto_save_tw);
	autoSaveResetBtn->setGeometry(320, 70, 28, 28);
	connect(autoSaveResetBtn, SIGNAL(clicked()),
			this, SLOT(resetAutoSave()));

	m_saveIntervalSlider->setEnabled(m_enableAutoSave);
	m_runningAutoSave->setVisible(m_enableAutoSave);

	counter = 0;

	// UI effect vs. performance tab.
	TabWidget * ui_fx_tw = new TabWidget(
			tr("User interface (UI) effects vs. performance"), performance_w);

	addLedCheckBox(tr("Smooth scroll in song editor"), ui_fx_tw, counter,
		m_smoothScroll, SLOT(toggleSmoothScroll(bool)), false);
	addLedCheckBox(tr("Display playback cursor in AudioFileProcessor"), ui_fx_tw, counter,
		m_animateAFP, SLOT(toggleAnimateAFP(bool)), false);

	ui_fx_tw->setFixedHeight(YDelta + YDelta * counter);

	counter = 0;

	// Plugins tab.
	TabWidget * plugins_tw = new TabWidget(
			tr("Plugins"), performance_w);

	m_vstEmbedLbl = new QLabel(plugins_tw);
	m_vstEmbedLbl->move(XDelta, YDelta * ++counter);
	m_vstEmbedLbl->setText(tr("VST plugins embedding:"));

	m_vstEmbedComboBox = new QComboBox(plugins_tw);
	m_vstEmbedComboBox->move(XDelta, YDelta * ++counter);

	QStringList embedMethods = ConfigManager::availableVstEmbedMethods();
	m_vstEmbedComboBox->addItem(tr("No embedding"), "none");
	if(embedMethods.contains("qt"))
	{
		m_vstEmbedComboBox->addItem(tr("Embed using Qt API"), "qt");
	}
	if(embedMethods.contains("win32"))
	{
		m_vstEmbedComboBox->addItem(tr("Embed using native Win32 API"), "win32");
	}
	if(embedMethods.contains("xembed"))
	{
		m_vstEmbedComboBox->addItem(tr("Embed using XEmbed protocol"), "xembed");
	}
	m_vstEmbedComboBox->setCurrentIndex(m_vstEmbedComboBox->findData(m_vstEmbedMethod));
	connect(m_vstEmbedComboBox, SIGNAL(currentIndexChanged(int)),
			this, SLOT(vstEmbedMethodChanged()));

	counter += 2;

	m_vstAlwaysOnTopCheckBox = new LedCheckBox(
			tr("Keep plugin windows on top when not embedded"), plugins_tw);
	m_vstAlwaysOnTopCheckBox->move(20, 66);
	m_vstAlwaysOnTopCheckBox->setChecked(m_vstAlwaysOnTop);
	m_vstAlwaysOnTopCheckBox->setVisible(m_vstEmbedMethod == "none");
	connect(m_vstAlwaysOnTopCheckBox, SIGNAL(toggled(bool)),
			this, SLOT(toggleVSTAlwaysOnTop(bool)));

	addLedCheckBox(tr("Sync VST plugins to host playback"), plugins_tw, counter,
		m_syncVSTPlugins, SLOT(toggleSyncVSTPlugins(bool)), false);

	addLedCheckBox(tr("Keep effects running even without input"), plugins_tw, counter,
		m_disableAutoQuit, SLOT(toggleDisableAutoQuit(bool)), false);

	plugins_tw->setFixedHeight(YDelta + YDelta * counter);

	// Performance layout ordering.
	performance_layout->addWidget(auto_save_tw);
	performance_layout->addWidget(ui_fx_tw);
	performance_layout->addWidget(plugins_tw);
	performance_layout->addStretch();

	// Audio widget.
	QWidget * audio_w = new QWidget(settings_w);
	QVBoxLayout * audio_layout = new QVBoxLayout(audio_w);
	audio_layout->setSpacing(10);
	audio_layout->setMargin(0);
	labelWidget(audio_w,
			tr("Audio"));

	// Audio interface tab.
	TabWidget * audioiface_tw = new TabWidget(
			tr("Audio interface"), audio_w);
	audioiface_tw->setFixedHeight(56);

	m_audioInterfaces = new QComboBox(audioiface_tw);
	m_audioInterfaces->setGeometry(10, 20, 240, 28);

	// Ifaces-settings-widget.
	QWidget * as_w = new QWidget(audio_w);
	as_w->setFixedHeight(60);

	QHBoxLayout * as_w_layout = new QHBoxLayout(as_w);
	as_w_layout->setSpacing(0);
	as_w_layout->setMargin(0);

#ifdef LMMS_HAVE_JACK
	m_audioIfaceSetupWidgets[AudioJack::name()] =
			new AudioJack::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_ALSA
	m_audioIfaceSetupWidgets[AudioAlsa::name()] =
			new AudioAlsaSetupWidget(as_w);
#endif

#ifdef LMMS_HAVE_PULSEAUDIO
	m_audioIfaceSetupWidgets[AudioPulseAudio::name()] =
			new AudioPulseAudio::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_PORTAUDIO
	m_audioIfaceSetupWidgets[AudioPortAudio::name()] =
			new AudioPortAudio::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_SOUNDIO
	m_audioIfaceSetupWidgets[AudioSoundIo::name()] =
			new AudioSoundIo::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_SDL
	m_audioIfaceSetupWidgets[AudioSdl::name()] =
			new AudioSdl::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_OSS
	m_audioIfaceSetupWidgets[AudioOss::name()] =
			new AudioOss::setupWidget(as_w);
#endif

#ifdef LMMS_HAVE_SNDIO
	m_audioIfaceSetupWidgets[AudioSndio::name()] =
			new AudioSndio::setupWidget(as_w);
#endif

	m_audioIfaceSetupWidgets[AudioDummy::name()] =
			new AudioDummy::setupWidget(as_w);

	for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin();
		it != m_audioIfaceSetupWidgets.end(); ++it)
	{
		m_audioIfaceNames[
			AudioDeviceSetupWidget::tr(it.key().toUtf8())] = it.key();
	}
	for(trMap::iterator it = m_audioIfaceNames.begin();
		it != m_audioIfaceNames.end(); ++it)
	{
		QWidget * audioWidget = m_audioIfaceSetupWidgets[it.value()];
		audioWidget->hide();
		as_w_layout->addWidget(audioWidget);
		m_audioInterfaces->addItem(it.key());
	}

	// If no preferred audio device is saved, save the current one.
	QString audioDevName = ConfigManager::inst()->value("mixer", "audiodev");
	if (m_audioInterfaces->findText(audioDevName) < 0)
	{
		audioDevName = Engine::mixer()->audioDevName();
		ConfigManager::inst()->setValue("mixer", "audiodev", audioDevName);
	}
	m_audioInterfaces->
		setCurrentIndex(m_audioInterfaces->findText(audioDevName));
	m_audioIfaceSetupWidgets[audioDevName]->show();

	connect(m_audioInterfaces, SIGNAL(activated(const QString &)),
			this, SLOT(audioInterfaceChanged(const QString &)));

	// Advanced setting, hidden for now
	if(false)
	{
		LedCheckBox * useNaNHandler = new LedCheckBox(
			tr("Use built-in NaN handler"), audio_w);
		useNaNHandler->setChecked(m_NaNHandler);
	}

	// HQ mode LED.
	LedCheckBox * hqaudio = new LedCheckBox(
			tr("HQ mode for output audio device"), audio_w);
	hqaudio->move(10, 0);
	hqaudio->setChecked(m_hqAudioDev);
	connect(hqaudio, SIGNAL(toggled(bool)),
			this, SLOT(toggleHQAudioDev(bool)));

	// Buffer size tab.
	TabWidget * bufferSize_tw = new TabWidget(
			tr("Buffer size"), audio_w);
	bufferSize_tw->setFixedHeight(76);

	m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSize_tw);
	m_bufferSizeSlider->setRange(1, 128);
	m_bufferSizeSlider->setTickInterval(8);
	m_bufferSizeSlider->setPageStep(8);
	m_bufferSizeSlider->setValue(m_bufferSize / BUFFERSIZE_RESOLUTION);
	m_bufferSizeSlider->setGeometry(10, 18, 340, 18);
	m_bufferSizeSlider->setTickPosition(QSlider::TicksBelow);

	connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)),
			this, SLOT(setBufferSize(int)));
	connect(m_bufferSizeSlider, SIGNAL(valueChanged(int)),
			this, SLOT(showRestartWarning()));

	m_bufferSizeLbl = new QLabel(bufferSize_tw);
	m_bufferSizeLbl->setGeometry(10, 40, 200, 24);
	setBufferSize(m_bufferSizeSlider->value());

	QPushButton * bufferSize_reset_btn = new QPushButton(
			embed::getIconPixmap("reload"), "", bufferSize_tw);
	bufferSize_reset_btn->setGeometry(320, 40, 28, 28);
	connect(bufferSize_reset_btn, SIGNAL(clicked()),
			this, SLOT(resetBufferSize()));
	ToolTip::add(bufferSize_reset_btn,
			tr("Reset to default value"));

	// Audio layout ordering.
	audio_layout->addWidget(audioiface_tw);
	audio_layout->addWidget(as_w);
	audio_layout->addWidget(hqaudio);
	audio_layout->addWidget(bufferSize_tw);
	audio_layout->addStretch();

	// MIDI widget.
	QWidget * midi_w = new QWidget(settings_w);
	QVBoxLayout * midi_layout = new QVBoxLayout(midi_w);
	midi_layout->setSpacing(10);
	midi_layout->setMargin(0);
	labelWidget(midi_w,
			tr("MIDI"));

	// MIDI interface tab.
	TabWidget * midiiface_tw = new TabWidget(
			tr("MIDI interface"), midi_w);
	midiiface_tw->setFixedHeight(56);

	m_midiInterfaces = new QComboBox(midiiface_tw);
	m_midiInterfaces->setGeometry(10, 20, 240, 28);

	// Ifaces-settings-widget.
	QWidget * ms_w = new QWidget(midi_w);
	ms_w->setFixedHeight(60);

	QHBoxLayout * ms_w_layout = new QHBoxLayout(ms_w);
	ms_w_layout->setSpacing(0);
	ms_w_layout->setMargin(0);

#ifdef LMMS_HAVE_ALSA
	m_midiIfaceSetupWidgets[MidiAlsaSeq::name()] =
			MidiSetupWidget::create<MidiAlsaSeq>(ms_w);
	m_midiIfaceSetupWidgets[MidiAlsaRaw::name()] =
			MidiSetupWidget::create<MidiAlsaRaw>(ms_w);
#endif

#ifdef LMMS_HAVE_JACK
	m_midiIfaceSetupWidgets[MidiJack::name()] =
			MidiSetupWidget::create<MidiJack>(ms_w);
#endif

#ifdef LMMS_HAVE_OSS
	m_midiIfaceSetupWidgets[MidiOss::name()] =
			MidiSetupWidget::create<MidiOss>(ms_w);
#endif

#ifdef LMMS_HAVE_SNDIO
	m_midiIfaceSetupWidgets[MidiSndio::name()] =
			MidiSetupWidget::create<MidiSndio>(ms_w);
#endif

#ifdef LMMS_BUILD_WIN32
	m_midiIfaceSetupWidgets[MidiWinMM::name()] =
			MidiSetupWidget::create<MidiWinMM>(ms_w);
#endif

#ifdef LMMS_BUILD_APPLE
    m_midiIfaceSetupWidgets[MidiApple::name()] =
			MidiSetupWidget::create<MidiApple>(ms_w);
#endif

	m_midiIfaceSetupWidgets[MidiDummy::name()] =
			MidiSetupWidget::create<MidiDummy>(ms_w);

	for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin();
		it != m_midiIfaceSetupWidgets.end(); ++it)
	{
		m_midiIfaceNames[
			MidiSetupWidget::tr(it.key().toUtf8())] = it.key();
	}
	for(trMap::iterator it = m_midiIfaceNames.begin();
		it != m_midiIfaceNames.end(); ++it)
	{
		QWidget * midiWidget = m_midiIfaceSetupWidgets[it.value()];
		midiWidget->hide();
		ms_w_layout->addWidget(midiWidget);
		m_midiInterfaces->addItem(it.key());
	}

	QString midiDevName = ConfigManager::inst()->value("mixer", "mididev");
	if (m_midiInterfaces->findText(midiDevName) < 0)
	{
		midiDevName = Engine::mixer()->midiClientName();
		ConfigManager::inst()->setValue("mixer", "mididev", midiDevName);
	}
	m_midiInterfaces->setCurrentIndex(m_midiInterfaces->findText(midiDevName));
	m_midiIfaceSetupWidgets[midiDevName]->show();

	connect(m_midiInterfaces, SIGNAL(activated(const QString &)),
			this, SLOT(midiInterfaceChanged(const QString &)));

	// MIDI autoassign tab.
	TabWidget * midiAutoAssign_tw = new TabWidget(
			tr("Automatically assign MIDI controller to selected track"), midi_w);
	midiAutoAssign_tw->setFixedHeight(56);

	m_assignableMidiDevices = new QComboBox(midiAutoAssign_tw);
	m_assignableMidiDevices->setGeometry(10, 20, 240, 28);
	m_assignableMidiDevices->addItem("none");
	if ( !Engine::mixer()->midiClient()->isRaw() )
	{
		m_assignableMidiDevices->addItems(Engine::mixer()->midiClient()->readablePorts());
	}
	else
	{
		m_assignableMidiDevices->addItem("all");
	}
	int current = m_assignableMidiDevices->findText(ConfigManager::inst()->value("midi", "midiautoassign"));
	if (current >= 0)
	{
		m_assignableMidiDevices->setCurrentIndex(current);
	}

	// MIDI layout ordering.
	midi_layout->addWidget(midiiface_tw);
	midi_layout->addWidget(ms_w);
	midi_layout->addWidget(midiAutoAssign_tw);
	midi_layout->addStretch();

	// Paths widget.
	QWidget * paths_w = new QWidget(settings_w);

	QVBoxLayout * paths_layout = new QVBoxLayout(paths_w);
	paths_layout->setSpacing(10);
	paths_layout->setMargin(0);

	labelWidget(paths_w, tr("Paths"));

	// Paths scroll area.
	QScrollArea * pathsScroll = new QScrollArea(paths_w);
	pathsScroll->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
	pathsScroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

	// Path selectors widget.
	QWidget * pathSelectors = new QWidget(paths_w);

	const int txtLength = 284;
	const int btnStart = 300;

	// Path selectors layout.
	QVBoxLayout * pathSelectorsLayout = new QVBoxLayout;
	pathSelectorsLayout->setSpacing(10);

	auto addPathEntry = [&](const QString &caption,
		const QString& content,
		const char* setSlot,
		const char* openSlot,
		QLineEdit*& lineEdit,
		const char* pixmap = "project_open")
	{
		TabWidget * newTw = new TabWidget(caption,
					pathSelectors);
		newTw->setFixedHeight(48);

		lineEdit = new QLineEdit(content, newTw);
		lineEdit->setGeometry(10, 20, txtLength, 16);
		connect(lineEdit, SIGNAL(textChanged(const QString &)),
			this, setSlot);

		QPushButton * selectBtn = new QPushButton(
			embed::getIconPixmap(pixmap, 16, 16),
			"", newTw);
		selectBtn->setFixedSize(24, 24);
		selectBtn->move(btnStart, 16);
		connect(selectBtn, SIGNAL(clicked()), this, openSlot);

		pathSelectorsLayout->addWidget(newTw);
		pathSelectorsLayout->addSpacing(10);
	};

	addPathEntry(tr("LMMS working directory"), m_workingDir,
		SLOT(setWorkingDir(const QString &)),
		SLOT(openWorkingDir()),
		m_workingDirLineEdit);
	addPathEntry(tr("VST plugins directory"), m_vstDir,
		SLOT(setVSTDir(const QString &)),
		SLOT(openVSTDir()),
		m_vstDirLineEdit);
	addPathEntry(tr("LADSPA plugins directories"), m_ladspaDir,
		SLOT(setLADSPADir(const QString &)),
		SLOT(openLADSPADir()),
		m_ladspaDirLineEdit, "add_folder");
	addPathEntry(tr("SF2 directory"), m_sf2Dir,
		SLOT(setSF2Dir(const QString &)),
		SLOT(openSF2Dir()),
		m_sf2DirLineEdit);
#ifdef LMMS_HAVE_FLUIDSYNTH
	addPathEntry(tr("Default SF2"), m_sf2File,
		SLOT(setSF2File(const QString &)),
		SLOT(openSF2File()),
		m_sf2FileLineEdit);
#endif
	addPathEntry(tr("GIG directory"), m_gigDir,
		SLOT(setGIGDir(const QString &)),
		SLOT(openGIGDir()),
		m_gigDirLineEdit);
	addPathEntry(tr("Theme directory"), m_themeDir,
		SLOT(setThemeDir(const QString &)),
		SLOT(openThemeDir()),
		m_themeDirLineEdit);
	addPathEntry(tr("Background artwork"), m_backgroundPicFile,
		SLOT(setBackgroundPicFile(const QString &)),
		SLOT(openBackgroundPicFile()),
		m_backgroundPicFileLineEdit);

	pathSelectorsLayout->addStretch();

	pathSelectors->setLayout(pathSelectorsLayout);

	pathsScroll->setWidget(pathSelectors);
	pathsScroll->setWidgetResizable(true);

	paths_layout->addWidget(pathsScroll);
	paths_layout->addStretch();

	// Major tabs ordering.
	m_tabBar->addTab(general_w,
			tr("General"), 0, false, true)->setIcon(
					embed::getIconPixmap("setup_general"));
	m_tabBar->addTab(performance_w,
			tr("Performance"), 1, false, true)->setIcon(
					embed::getIconPixmap("setup_performance"));
	m_tabBar->addTab(audio_w,
			tr("Audio"), 2, false, true)->setIcon(
					embed::getIconPixmap("setup_audio"));
	m_tabBar->addTab(midi_w,
			tr("MIDI"), 3, false, true)->setIcon(
					embed::getIconPixmap("setup_midi"));
	m_tabBar->addTab(paths_w,
			tr("Paths"), 4, true, true)->setIcon(
					embed::getIconPixmap("setup_directories"));

	m_tabBar->setActiveTab(tab_to_open);

	// Horizontal layout ordering.
	hlayout->addSpacing(2);
	hlayout->addWidget(m_tabBar);
	hlayout->addSpacing(10);
	hlayout->addWidget(settings_w);
	hlayout->addSpacing(10);

	// Extras widget and layout.
	QWidget * extras_w = new QWidget(this);
	QHBoxLayout * extras_layout = new QHBoxLayout(extras_w);
	extras_layout->setSpacing(0);
	extras_layout->setMargin(0);

	// Restart warning label.
	restartWarningLbl = new QLabel(
			tr("Some changes require restarting."), extras_w);
	restartWarningLbl->hide();

	// OK button.
	QPushButton * ok_btn = new QPushButton(
			embed::getIconPixmap("apply"),
			tr("OK"), extras_w);
	connect(ok_btn, SIGNAL(clicked()),
			this, SLOT(accept()));

	// Cancel button.
	QPushButton * cancel_btn = new QPushButton(
			embed::getIconPixmap("cancel"),
			tr("Cancel"), extras_w);
	connect(cancel_btn, SIGNAL(clicked()),
			this, SLOT(reject()));

	// Extras layout ordering.
	extras_layout->addSpacing(10);
	extras_layout->addWidget(restartWarningLbl);
	extras_layout->addStretch();
	extras_layout->addWidget(ok_btn);
	extras_layout->addSpacing(10);
	extras_layout->addWidget(cancel_btn);
	extras_layout->addSpacing(10);

	// Vertical layout ordering.
	vlayout->addWidget(main_w);
	vlayout->addSpacing(10);
	vlayout->addWidget(extras_w);
	vlayout->addSpacing(10);

	show();
}

SetupDialog::~SetupDialog()
{
	Engine::projectJournal()->setJournalling(true);
}

void SetupDialog::accept()
{
	/* Hide dialog before setting values. This prevents an obscure bug
	where non-embedded VST windows would steal focus and prevent LMMS
	from taking mouse input, rendering the application unusable. */
	QDialog::accept();

	ConfigManager::inst()->setValue("app", "displaydbfs",
					QString::number(m_displaydBFS));
	ConfigManager::inst()->setValue("tooltips", "disabled",
					QString::number(!m_tooltips));
	ConfigManager::inst()->setValue("ui", "displaywaveform",
					QString::number(m_displayWaveform));
	ConfigManager::inst()->setValue("ui", "printnotelabels",
					QString::number(m_printNoteLabels));
	ConfigManager::inst()->setValue("ui", "compacttrackbuttons",
					QString::number(m_compactTrackButtons));
	ConfigManager::inst()->setValue("ui", "oneinstrumenttrackwindow",
					QString::number(m_oneInstrumentTrackWindow));
	ConfigManager::inst()->setValue("ui", "sidebaronright",
					QString::number(m_sideBarOnRight));
	ConfigManager::inst()->setValue("ui", "letpreviewsfinish",
					QString::number(m_letPreviewsFinish));
	ConfigManager::inst()->setValue("app", "sololegacybehavior",
					QString::number(m_soloLegacyBehavior));
	ConfigManager::inst()->setValue("app", "nommpz",
					QString::number(!m_MMPZ));
	ConfigManager::inst()->setValue("app", "disablebackup",
					QString::number(!m_disableBackup));
	ConfigManager::inst()->setValue("app", "openlastproject",
					QString::number(m_openLastProject));
	ConfigManager::inst()->setValue("app", "language", m_lang);
	ConfigManager::inst()->setValue("ui", "saveinterval",
					QString::number(m_saveInterval));
	ConfigManager::inst()->setValue("ui", "enableautosave",
					QString::number(m_enableAutoSave));
	ConfigManager::inst()->setValue("ui", "enablerunningautosave",
					QString::number(m_enableRunningAutoSave));
	ConfigManager::inst()->setValue("ui", "smoothscroll",
					QString::number(m_smoothScroll));
	ConfigManager::inst()->setValue("ui", "animateafp",
					QString::number(m_animateAFP));
	ConfigManager::inst()->setValue("ui", "vstembedmethod",
					m_vstEmbedComboBox->currentData().toString());
	ConfigManager::inst()->setValue("ui", "vstalwaysontop",
					QString::number(m_vstAlwaysOnTop));
	ConfigManager::inst()->setValue("ui", "syncvstplugins",
					QString::number(m_syncVSTPlugins));
	ConfigManager::inst()->setValue("ui", "disableautoquit",
					QString::number(m_disableAutoQuit));
	ConfigManager::inst()->setValue("mixer", "audiodev",
					m_audioIfaceNames[m_audioInterfaces->currentText()]);
	ConfigManager::inst()->setValue("app", "nanhandler",
					QString::number(m_NaNHandler));
	ConfigManager::inst()->setValue("mixer", "hqaudio",
					QString::number(m_hqAudioDev));
	ConfigManager::inst()->setValue("mixer", "framesperaudiobuffer",
					QString::number(m_bufferSize));
	ConfigManager::inst()->setValue("mixer", "mididev",
					m_midiIfaceNames[m_midiInterfaces->currentText()]);
	ConfigManager::inst()->setValue("midi", "midiautoassign",
					m_assignableMidiDevices->currentText());

	ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir));
	ConfigManager::inst()->setVSTDir(QDir::fromNativeSeparators(m_vstDir));
	ConfigManager::inst()->setLADSPADir(QDir::fromNativeSeparators(m_ladspaDir));
	ConfigManager::inst()->setSF2Dir(QDir::fromNativeSeparators(m_sf2Dir));
#ifdef LMMS_HAVE_FLUIDSYNTH
	ConfigManager::inst()->setSF2File(m_sf2File);
#endif
	ConfigManager::inst()->setGIGDir(QDir::fromNativeSeparators(m_gigDir));
	ConfigManager::inst()->setThemeDir(QDir::fromNativeSeparators(m_themeDir));
	ConfigManager::inst()->setBackgroundPicFile(m_backgroundPicFile);

	// Tell all audio-settings-widgets to save their settings.
	for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin();
		it != m_audioIfaceSetupWidgets.end(); ++it)
	{
		it.value()->saveSettings();
	}
	// Tell all MIDI-settings-widgets to save their settings.
	for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin();
		it != m_midiIfaceSetupWidgets.end(); ++it)
	{
		it.value()->saveSettings();
	}
	ConfigManager::inst()->saveConfigFile();
}

// General settings slots.

void SetupDialog::toggleDisplaydBFS(bool enabled)
{
	m_displaydBFS = enabled;
}

void SetupDialog::toggleTooltips(bool enabled)
{
	m_tooltips = enabled;
}

void SetupDialog::toggleDisplayWaveform(bool enabled)
{
	m_displayWaveform = enabled;
}

void SetupDialog::toggleNoteLabels(bool enabled)
{
	m_printNoteLabels = enabled;
}

void SetupDialog::toggleCompactTrackButtons(bool enabled)
{
	m_compactTrackButtons = enabled;
}

void SetupDialog::toggleOneInstrumentTrackWindow(bool enabled)
{
	m_oneInstrumentTrackWindow = enabled;
}

void SetupDialog::toggleSideBarOnRight(bool enabled)
{
	m_sideBarOnRight = enabled;
}

void SetupDialog::toggleLetPreviewsFinish(bool enabled)
{
	m_letPreviewsFinish = enabled;
}

void SetupDialog::toggleMMPZ(bool enabled)
{
	m_MMPZ = enabled;
}

void SetupDialog::toggleDisableBackup(bool enabled)
{
	m_disableBackup = enabled;
}

void SetupDialog::toggleOpenLastProject(bool enabled)
{
	m_openLastProject = enabled;
}

void SetupDialog::setLanguage(int lang)
{
	m_lang = m_languages[lang];
}

void SetupDialog::toggleSoloLegacyBehavior(bool enabled)
{
	m_soloLegacyBehavior = enabled;
}

// Performance settings slots.

void SetupDialog::setAutoSaveInterval(int value)
{
	m_saveInterval = value;
	m_saveIntervalSlider->setValue(m_saveInterval);
	QString minutes = m_saveInterval > 1 ? tr("minutes") : tr("minute");
	minutes = QString("%1 %2").arg(QString::number(m_saveInterval), minutes);
	minutes = m_enableAutoSave ?  minutes : tr("Disabled");
	m_saveIntervalLbl->setText(
		tr("Autosave interval: %1").arg(minutes));
}

void SetupDialog::toggleAutoSave(bool enabled)
{
	m_enableAutoSave = enabled;
	m_saveIntervalSlider->setEnabled(enabled);
	m_runningAutoSave->setVisible(enabled);
	setAutoSaveInterval(m_saveIntervalSlider->value());
}

void SetupDialog::toggleRunningAutoSave(bool enabled)
{
	m_enableRunningAutoSave = enabled;
}

void SetupDialog::resetAutoSave()
{
	setAutoSaveInterval(MainWindow::DEFAULT_SAVE_INTERVAL_MINUTES);
	m_autoSave->setChecked(true);
	m_runningAutoSave->setChecked(false);
}

void SetupDialog::toggleSmoothScroll(bool enabled)
{
	m_smoothScroll = enabled;
}

void SetupDialog::toggleAnimateAFP(bool enabled)
{
	m_animateAFP = enabled;
}

void SetupDialog::toggleSyncVSTPlugins(bool enabled)
{
	m_syncVSTPlugins = enabled;
}

void SetupDialog::vstEmbedMethodChanged()
{
	m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString();
	m_vstAlwaysOnTopCheckBox->setVisible(m_vstEmbedMethod == "none");
}

void SetupDialog::toggleVSTAlwaysOnTop(bool enabled)
{
	m_vstAlwaysOnTop = enabled;
}

void SetupDialog::toggleDisableAutoQuit(bool enabled)
{
	m_disableAutoQuit = enabled;
}

// Audio settings slots.

void SetupDialog::toggleHQAudioDev(bool enabled)
{
	m_hqAudioDev = enabled;
}

void SetupDialog::audioInterfaceChanged(const QString & iface)
{
	for(AswMap::iterator it = m_audioIfaceSetupWidgets.begin();
		it != m_audioIfaceSetupWidgets.end(); ++it)
	{
		it.value()->hide();
	}

	m_audioIfaceSetupWidgets[m_audioIfaceNames[iface]]->show();
}

void SetupDialog::setBufferSize(int value)
{
	const int step = DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION;
	if(value > step && value % step)
	{
		int mod_value = value % step;
		if(mod_value < step / 2)
		{
			m_bufferSizeSlider->setValue(value - mod_value);
		}
		else
		{
			m_bufferSizeSlider->setValue(value + step - mod_value);
		}
		return;
	}

	if(m_bufferSizeSlider->value() != value)
	{
		m_bufferSizeSlider->setValue(value);
	}

	m_bufferSize = value * BUFFERSIZE_RESOLUTION;
	m_bufferSizeLbl->setText(tr("Frames: %1\nLatency: %2 ms").arg(m_bufferSize).arg(
		1000.0f * m_bufferSize / Engine::mixer()->processingSampleRate(), 0, 'f', 1));
}

void SetupDialog::resetBufferSize()
{
	setBufferSize(DEFAULT_BUFFER_SIZE / BUFFERSIZE_RESOLUTION);
}

// MIDI settings slots.

void SetupDialog::midiInterfaceChanged(const QString & iface)
{
	for(MswMap::iterator it = m_midiIfaceSetupWidgets.begin();
		it != m_midiIfaceSetupWidgets.end(); ++it)
	{
		it.value()->hide();
	}

	m_midiIfaceSetupWidgets[m_midiIfaceNames[iface]]->show();
}

// Paths settings slots.

void SetupDialog::openWorkingDir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose the LMMS working directory"), m_workingDir);
	if (!new_dir.isEmpty())
	{
		m_workingDirLineEdit->setText(new_dir);
	}
}

void SetupDialog::setWorkingDir(const QString & workingDir)
{
	m_workingDir = workingDir;
}

void SetupDialog::openVSTDir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose your VST plugins directory"), m_vstDir);
	if (!new_dir.isEmpty())
	{
		m_vstDirLineEdit->setText(new_dir);
	}
}

void SetupDialog::setVSTDir(const QString & vstDir)
{
	m_vstDir = vstDir;
}

void SetupDialog::openLADSPADir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose your LADSPA plugins directory"), m_ladspaDir);
	if (!new_dir.isEmpty())
	{
		if(m_ladspaDirLineEdit->text() == "")
		{
			m_ladspaDirLineEdit->setText(new_dir);
		}
		else
		{
			m_ladspaDirLineEdit->setText(m_ladspaDirLineEdit->text() + "," +
								new_dir);
		}
	}
}

void SetupDialog::setLADSPADir(const QString & ladspaDir)
{
	m_ladspaDir = ladspaDir;
}

void SetupDialog::openSF2Dir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose your SF2 directory"), m_sf2Dir);
	if (!new_dir.isEmpty())
	{
		m_sf2DirLineEdit->setText(new_dir);
	}
}

void SetupDialog::setSF2Dir(const QString & sf2Dir)
{
	m_sf2Dir = sf2Dir;
}

void SetupDialog::openSF2File()
{
#ifdef LMMS_HAVE_FLUIDSYNTH
	QString new_file = FileDialog::getOpenFileName(this,
		tr("Choose your default SF2"), m_sf2File, "SoundFont 2 files (*.sf2)");

	if (!new_file.isEmpty())
	{
		m_sf2FileLineEdit->setText(new_file);
	}
#endif
}

void SetupDialog::setSF2File(const QString & sf2File)
{
#ifdef LMMS_HAVE_FLUIDSYNTH
	m_sf2File = sf2File;
#endif
}

void SetupDialog::openGIGDir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose your GIG directory"), m_gigDir);
	if(!new_dir.isEmpty())
	{
		m_gigDirLineEdit->setText(new_dir);
	}
}

void SetupDialog::setGIGDir(const QString & gigDir)
{
	m_gigDir = gigDir;
}

void SetupDialog::openThemeDir()
{
	QString new_dir = FileDialog::getExistingDirectory(this,
		tr("Choose your theme directory"), m_themeDir);
	if(!new_dir.isEmpty())
	{
		m_themeDirLineEdit->setText(new_dir);
	}
}

void SetupDialog::setThemeDir(const QString & themeDir)
{
	m_themeDir = themeDir;
}

void SetupDialog::openBackgroundPicFile()
{
	QList<QByteArray> fileTypesList = QImageReader::supportedImageFormats();
	QString fileTypes;
	for(int i = 0; i < fileTypesList.count(); i++)
	{
		if(fileTypesList[i] != fileTypesList[i].toUpper())
		{
			if(!fileTypes.isEmpty())
			{
				fileTypes += " ";
			}
			fileTypes += "*." + QString(fileTypesList[i]);
		}
	}

	QString dir = (m_backgroundPicFile.isEmpty()) ?
		m_themeDir :
		m_backgroundPicFile;
	QString new_file = FileDialog::getOpenFileName(this,
		tr("Choose your background picture"), dir, "Picture files (" + fileTypes + ")");

	if(!new_file.isEmpty())
	{
		m_backgroundPicFileLineEdit->setText(new_file);
	}
}

void SetupDialog::setBackgroundPicFile(const QString & backgroundPicFile)
{
	m_backgroundPicFile = backgroundPicFile;
}

void SetupDialog::showRestartWarning()
{
	restartWarningLbl->show();
}


code:
/*
 * StringPairDrag.cpp - class StringPairDrag which provides general support
 *                        for drag'n'drop of string-pairs and which is the base
 *                        for all drag'n'drop-actions within LMMS
 *
 * Copyright (c) 2005-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QMimeData>
#include <QDragEnterEvent>

#include "StringPairDrag.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "Clipboard.h"

StringPairDrag::StringPairDrag( const QString & _key, const QString & _value,
					const QPixmap & _icon, QWidget * _w ) :
	QDrag( _w )
{
	// For mimeType() and MimeType enum class
	using namespace Clipboard;

	if( _icon.isNull() && _w )
	{
		setPixmap( _w->grab().scaled(
						64, 64,
						Qt::KeepAspectRatio,
						Qt::SmoothTransformation ) );
	}
	else
	{
		setPixmap( _icon );
	}
	QString txt = _key + ":" + _value;
	QMimeData * m = new QMimeData();
	m->setData( mimeType( MimeType::StringPair ), txt.toUtf8() );
	setMimeData( m );
	exec( Qt::LinkAction, Qt::LinkAction );
}

StringPairDrag::~StringPairDrag()
{
	// during a drag, we might have lost key-press-events, so reset
	// modifiers of main-win
	if( gui->mainWindow() )
	{
		gui->mainWindow()->clearKeyModifiers();
	}
}

bool StringPairDrag::processDragEnterEvent( QDragEnterEvent * _dee,
						const QString & _allowed_keys )
{
	// For mimeType() and MimeType enum class
	using namespace Clipboard;

	if( !_dee->mimeData()->hasFormat( mimeType( MimeType::StringPair ) ) )
	{
		return( false );
	}
	QString txt = _dee->mimeData()->data( mimeType( MimeType::StringPair ) );
	if( _allowed_keys.split( ',' ).contains( txt.section( ':', 0, 0 ) ) )
	{
		_dee->acceptProposedAction();
		return( true );
	}
	_dee->ignore();
	return( false );
}

QString StringPairDrag::decodeKey( QDropEvent * _de )
{
	return Clipboard::decodeKey( _de->mimeData() );
}

QString StringPairDrag::decodeValue( QDropEvent * _de )
{
	return Clipboard::decodeValue( _de->mimeData() );
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * SubWindow.cpp - Implementation of QMdiSubWindow that correctly tracks
 *   the geometry that windows should be restored to.
 *   Workaround for https://bugreports.qt.io/browse/QTBUG-256
 *   This implementation adds a custom themed title bar to
 *   the subwindow.
 *
 * Copyright (c) 2015 Colin Wallace <wallace.colin.a@gmail.com>
 * Copyright (c) 2016 Steffen Baranowsky <baramgb@freenet.de>
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "SubWindow.h"

#include <QMdiArea>
#include <QMoveEvent>
#include <QPainter>
#include <QScrollBar>

#include "embed.h"

SubWindow::SubWindow( QWidget *parent, Qt::WindowFlags windowFlags ) :
	QMdiSubWindow( parent, windowFlags ),
	m_buttonSize( 17, 17 ),
	m_titleBarHeight( 24 ),
	m_hasFocus( false )
{
	// initialize the tracked geometry to whatever Qt thinks the normal geometry currently is.
	// this should always work, since QMdiSubWindows will not start as maximized
	m_trackedNormalGeom = normalGeometry();

	// inits the colors
	m_activeColor = Qt::SolidPattern;
	m_textShadowColor = Qt::black;
	m_borderColor = Qt::black;

	// close, maximize and restore (after maximizing) buttons
	m_closeBtn = new QPushButton( embed::getIconPixmap( "close" ), QString(), this );
	m_closeBtn->resize( m_buttonSize );
	m_closeBtn->setFocusPolicy( Qt::NoFocus );
	m_closeBtn->setCursor( Qt::ArrowCursor );
	m_closeBtn->setAttribute( Qt::WA_NoMousePropagation );
	m_closeBtn->setToolTip( tr( "Close" ) );
	connect( m_closeBtn, SIGNAL( clicked( bool ) ), this, SLOT( close() ) );

	m_maximizeBtn = new QPushButton( embed::getIconPixmap( "maximize" ), QString(), this );
	m_maximizeBtn->resize( m_buttonSize );
	m_maximizeBtn->setFocusPolicy( Qt::NoFocus );
	m_maximizeBtn->setCursor( Qt::ArrowCursor );
	m_maximizeBtn->setAttribute( Qt::WA_NoMousePropagation );
	m_maximizeBtn->setToolTip( tr( "Maximize" ) );
	connect( m_maximizeBtn, SIGNAL( clicked( bool ) ), this, SLOT( showMaximized() ) );

	m_restoreBtn = new QPushButton( embed::getIconPixmap( "restore" ), QString(), this );
	m_restoreBtn->resize( m_buttonSize );
	m_restoreBtn->setFocusPolicy( Qt::NoFocus );
	m_restoreBtn->setCursor( Qt::ArrowCursor );
	m_restoreBtn->setAttribute( Qt::WA_NoMousePropagation );
	m_restoreBtn->setToolTip( tr( "Restore" ) );
	connect( m_restoreBtn, SIGNAL( clicked( bool ) ), this, SLOT( showNormal() ) );

	// QLabel for the window title and the shadow effect
	m_shadow = new QGraphicsDropShadowEffect();
	m_shadow->setColor( m_textShadowColor );
	m_shadow->setXOffset( 1 );
	m_shadow->setYOffset( 1 );

	m_windowTitle = new QLabel( this );
	m_windowTitle->setFocusPolicy( Qt::NoFocus );
	m_windowTitle->setAttribute( Qt::WA_TransparentForMouseEvents, true );
	m_windowTitle->setGraphicsEffect( m_shadow );

	// disable the minimize button
	setWindowFlags( Qt::SubWindow | Qt::WindowMaximizeButtonHint |
		Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint |
		Qt::CustomizeWindowHint );
	connect( mdiArea(), SIGNAL( subWindowActivated( QMdiSubWindow* ) ), this, SLOT( focusChanged( QMdiSubWindow* ) ) );
}

/**
 * @brief SubWindow::paintEvent
 *
 *  This draws our new title bar with custom colors
 *  and draws a window icon on the left upper corner.
 */
void SubWindow::paintEvent( QPaintEvent * )
{
	QPainter p( this );
	QRect rect( 0, 0, width(), m_titleBarHeight );

	bool isActive = mdiArea()
			? mdiArea()->activeSubWindow() == this
			: false;

	p.fillRect( rect, isActive ? activeColor() : p.pen().brush() );

	// window border
	p.setPen( borderColor() );

	// bottom, left, and right lines
	p.drawLine( 0, height() - 1, width(), height() - 1 );
	p.drawLine( 0, m_titleBarHeight, 0, height() - 1 );
	p.drawLine( width() - 1, m_titleBarHeight, width() - 1, height() - 1 );

	// window icon
	if( widget() )
	{
		QPixmap winicon( widget()->windowIcon().pixmap( m_buttonSize ) );
		p.drawPixmap( 3, 3, m_buttonSize.width(), m_buttonSize.height(), winicon );
	}
}

/**
 * @brief SubWindow::changeEvent
 *
 * Triggers if the window title changes and calls adjustTitleBar().
 * @param event
 */
void SubWindow::changeEvent( QEvent *event )
{
	QMdiSubWindow::changeEvent( event );

	if( event->type() == QEvent::WindowTitleChange )
	{
		adjustTitleBar();
	}

}

/**
 * @brief SubWindow::elideText
 *
 *  Stores the given text into the given label.
 *  Shorts the text if it's too big for the labels width
 *  ans adds three dots (...)
 *
 * @param label - holds a pointer to the QLabel
 * @param text  - the text which will be stored (and if needed breaked down) into the QLabel.
 */
void SubWindow::elideText( QLabel *label, QString text )
{
	QFontMetrics metrix( label->font() );
	int width = label->width() - 2;
	QString clippedText = metrix.elidedText( text, Qt::ElideRight, width );
	label->setText( clippedText );
}

/**
 * @brief SubWindow::getTrueNormalGeometry
 *
 *  same as QWidet::normalGeometry, but works properly under X11
 *  see https://bugreports.qt.io/browse/QTBUG-256
 */
QRect SubWindow::getTrueNormalGeometry() const
{
	return m_trackedNormalGeom;
}

QBrush SubWindow::activeColor() const
{
	return m_activeColor;
}

QColor SubWindow::textShadowColor() const
{
	return m_textShadowColor;
}

QColor SubWindow::borderColor() const
{
	return m_borderColor;
}

void SubWindow::setActiveColor( const QBrush & b )
{
	m_activeColor = b;
}

void SubWindow::setTextShadowColor( const QColor & c )
{
	m_textShadowColor = c;
}

void SubWindow::setBorderColor( const QColor &c )
{
	m_borderColor = c;
}

/**
 * @brief SubWindow::moveEvent
 *
 *  overides the QMdiSubWindow::moveEvent() for saving the position
 *  of the subwindow into m_trackedNormalGeom. This position
 *  will be saved with the project because of an Qt bug wich doesn't
 *  save the right position. look at: https://bugreports.qt.io/browse/QTBUG-256
 * @param event
 */
void SubWindow::moveEvent( QMoveEvent * event )
{
	QMdiSubWindow::moveEvent( event );
	// if the window was moved and ISN'T minimized/maximized/fullscreen,
	// then save the current position
	if( !isMaximized() && !isMinimized() && !isFullScreen() )
	{
		m_trackedNormalGeom.moveTopLeft( event->pos() );
	}
}

/**
 * @brief SubWindow::adjustTitleBar
 *
 *  Our title bar needs buttons for maximize/restore and close in the right upper corner.
 *  We check if the subwindow is maximizable and put the buttons on the right positions.
 *  At next we calculate the width of the title label and call elideText() for adding
 *  the window title to m_windowTitle (which is a QLabel)
 */
void SubWindow::adjustTitleBar()
{
	// button adjustments
	m_maximizeBtn->hide();
	m_restoreBtn->hide();

	const int rightSpace = 3;
	const int buttonGap = 1;
	const int menuButtonSpace = 24;

	QPoint rightButtonPos( width() - rightSpace - m_buttonSize.width(), 3 );
	QPoint middleButtonPos( width() - rightSpace - ( 2 * m_buttonSize.width() ) - buttonGap, 3 );
	QPoint leftButtonPos( width() - rightSpace - ( 3 * m_buttonSize.width() ) - ( 2 * buttonGap ), 3 );

	// the buttonBarWidth depends on the number of buttons.
	// we need it to calculate the width of window title label
	int buttonBarWidth = rightSpace + m_buttonSize.width();

	// set the buttons on their positions.
	// the close button is always needed and on the rightButtonPos
	m_closeBtn->move( rightButtonPos );

	// here we ask: is the Subwindow maximizable and
	// then we set the buttons and show them if needed
	if( windowFlags() & Qt::WindowMaximizeButtonHint )
	{
		buttonBarWidth = buttonBarWidth + m_buttonSize.width() + buttonGap;
		m_maximizeBtn->move( middleButtonPos );
		m_restoreBtn->move( middleButtonPos );
		m_maximizeBtn->setHidden( isMaximized() );
	}

	// we're keeping the restore button around if we open projects
	// from older versions that have saved minimized windows
	m_restoreBtn->setVisible( isMaximized() || isMinimized() );
	if( isMinimized() )
	{
		m_restoreBtn->move( m_maximizeBtn->isHidden() ?  middleButtonPos : leftButtonPos );
	}

	if( widget() )
	{
		// title QLabel adjustments
		m_windowTitle->setAlignment( Qt::AlignHCenter );
		m_windowTitle->setFixedWidth( widget()->width() - ( menuButtonSpace + buttonBarWidth ) );
		m_windowTitle->move( menuButtonSpace,
			( m_titleBarHeight / 2 ) - ( m_windowTitle->sizeHint().height() / 2 ) - 1 );

		// if minimized we can't use widget()->width(). We have to hard code the width,
		// as the width of all minimized windows is the same.
		if( isMinimized() )
		{
			m_windowTitle->setFixedWidth( 120 );
		}

		// truncate the label string if the window is to small. Adds "..."
		elideText( m_windowTitle, widget()->windowTitle() );
		m_windowTitle->setTextInteractionFlags( Qt::NoTextInteraction );
		m_windowTitle->adjustSize();
	}
}

void SubWindow::focusChanged( QMdiSubWindow *subWindow )
{
	if( m_hasFocus && subWindow != this )
	{
		m_hasFocus = false;
		emit focusLost();
	}
	else if( subWindow == this )
	{
		m_hasFocus = true;
	}
}

/**
 * @brief SubWindow::resizeEvent
 *
 *  At first we give the event to QMdiSubWindow::resizeEvent() which handles
 *  the event on its behavior.
 *
 *  On every resize event we have to adjust our title label.
 *
 *  At last we store the current size into m_trackedNormalGeom. This size
 *  will be saved with the project because of an Qt bug wich doesn't
 *  save the right size. look at: https://bugreports.qt.io/browse/QTBUG-256
 *
 * @param event
 */
void SubWindow::resizeEvent( QResizeEvent * event )
{
	// When the parent QMdiArea gets resized, maximized subwindows also gets resized, if any.
	// In that case, we should call QMdiSubWindow::resizeEvent first
	// to ensure we get the correct window state.
	QMdiSubWindow::resizeEvent( event );
	adjustTitleBar();

	// if the window was resized and ISN'T minimized/maximized/fullscreen,
	// then save the current size
	if( !isMaximized() && !isMinimized() && !isFullScreen() )
	{
		m_trackedNormalGeom.setSize( event->size() );
	}
}


code:
/*
 * TimeLineWidget.cpp - class timeLine, representing a time-line with position marker
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include <QDomElement>
#include <QTimer>
#include <QApplication>
#include <QLayout>
#include <QMouseEvent>
#include <QPainter>
#include <QToolBar>

#include "TimeLineWidget.h"
#include "embed.h"
#include "NStateButton.h"
#include "GuiApplication.h"
#include "TextFloat.h"
#include "SongEditor.h"

QPixmap * TimeLineWidget::s_posMarkerPixmap = NULL;

TimeLineWidget::TimeLineWidget( const int xoff, const int yoff, const float ppb,
			Song::PlayPos & pos, const TimePos & begin, Song::PlayModes mode,
							QWidget * parent ) :
	QWidget( parent ),
	m_inactiveLoopColor( 52, 63, 53, 64 ),
	m_inactiveLoopBrush( QColor( 255, 255, 255, 32 ) ),
	m_inactiveLoopInnerColor( 255, 255, 255, 32 ),
	m_activeLoopColor( 52, 63, 53, 255 ),
	m_activeLoopBrush( QColor( 55, 141, 89 ) ),
	m_activeLoopInnerColor( 74, 155, 100, 255 ),
	m_loopRectangleVerticalPadding( 1 ),
	m_barLineColor( 192, 192, 192 ),
	m_barNumberColor( m_barLineColor.darker( 120 ) ),
	m_autoScroll( AutoScrollEnabled ),
	m_loopPoints( LoopPointsDisabled ),
	m_behaviourAtStop( BackToZero ),
	m_changedPosition( true ),
	m_xOffset( xoff ),
	m_posMarkerX( 0 ),
	m_ppb( ppb ),
	m_pos( pos ),
	m_begin( begin ),
	m_mode( mode ),
	m_savedPos( -1 ),
	m_hint( NULL ),
	m_action( NoAction ),
	m_moveXOff( 0 )
{
	m_loopPos[0] = 0;
	m_loopPos[1] = DefaultTicksPerBar;

	if( s_posMarkerPixmap == NULL )
	{
		s_posMarkerPixmap = new QPixmap( embed::getIconPixmap(
							"playpos_marker" ) );
	}

	setAttribute( Qt::WA_OpaquePaintEvent, true );
	move( 0, yoff );

	m_xOffset -= s_posMarkerPixmap->width() / 2;

	setMouseTracking(true);
	m_pos.m_timeLine = this;

	QTimer * updateTimer = new QTimer( this );
	connect( updateTimer, SIGNAL( timeout() ),
					this, SLOT( updatePosition() ) );
	updateTimer->start( 1000 / 60 );  // 60 fps
	connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int,int ) ),
					this, SLOT( update() ) );
}

TimeLineWidget::~TimeLineWidget()
{
	if( gui->songEditor() )
	{
		m_pos.m_timeLine = NULL;
	}
	delete m_hint;
}

void TimeLineWidget::setXOffset(const int x)
{
	m_xOffset = x;
	if (s_posMarkerPixmap != nullptr) { m_xOffset -= s_posMarkerPixmap->width() / 2; }
}

void TimeLineWidget::addToolButtons( QToolBar * _tool_bar )
{
	NStateButton * autoScroll = new NStateButton( _tool_bar );
	autoScroll->setGeneralToolTip( tr( "Auto scrolling" ) );
	autoScroll->addState( embed::getIconPixmap( "autoscroll_on" ) );
	autoScroll->addState( embed::getIconPixmap( "autoscroll_off" ) );
	connect( autoScroll, SIGNAL( changedState( int ) ), this,
					SLOT( toggleAutoScroll( int ) ) );

	NStateButton * loopPoints = new NStateButton( _tool_bar );
	loopPoints->setGeneralToolTip( tr( "Loop points" ) );
	loopPoints->addState( embed::getIconPixmap( "loop_points_off" ) );
	loopPoints->addState( embed::getIconPixmap( "loop_points_on" ) );
	connect( loopPoints, SIGNAL( changedState( int ) ), this,
					SLOT( toggleLoopPoints( int ) ) );
	connect( this, SIGNAL( loopPointStateLoaded( int ) ), loopPoints,
					SLOT( changeState( int ) ) );

	NStateButton * behaviourAtStop = new NStateButton( _tool_bar );
	behaviourAtStop->addState( embed::getIconPixmap( "back_to_zero" ),
					tr( "After stopping go back to beginning" )
									);
	behaviourAtStop->addState( embed::getIconPixmap( "back_to_start" ),
					tr( "After stopping go back to "
						"position at which playing was "
						"started" ) );
	behaviourAtStop->addState( embed::getIconPixmap( "keep_stop_position" ),
					tr( "After stopping keep position" ) );
	connect( behaviourAtStop, SIGNAL( changedState( int ) ), this,
					SLOT( toggleBehaviourAtStop( int ) ) );
	connect( this, SIGNAL( loadBehaviourAtStop( int ) ), behaviourAtStop,
					SLOT( changeState( int ) ) );
	behaviourAtStop->changeState( BackToStart );

	_tool_bar->addWidget( autoScroll );
	_tool_bar->addWidget( loopPoints );
	_tool_bar->addWidget( behaviourAtStop );
}

void TimeLineWidget::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setAttribute( "lp0pos", (int) loopBegin() );
	_this.setAttribute( "lp1pos", (int) loopEnd() );
	_this.setAttribute( "lpstate", m_loopPoints );
	_this.setAttribute( "stopbehaviour", m_behaviourAtStop );
}

void TimeLineWidget::loadSettings( const QDomElement & _this )
{
	m_loopPos[0] = _this.attribute( "lp0pos" ).toInt();
	m_loopPos[1] = _this.attribute( "lp1pos" ).toInt();
	m_loopPoints = static_cast<LoopPointStates>(
					_this.attribute( "lpstate" ).toInt() );
	update();
	emit loopPointStateLoaded( m_loopPoints );

	if( _this.hasAttribute( "stopbehaviour" ) )
	{
		emit loadBehaviourAtStop( _this.attribute( "stopbehaviour" ).toInt() );
	}
}

void TimeLineWidget::updatePosition( const TimePos & )
{
	const int new_x = markerX( m_pos );

	if( new_x != m_posMarkerX )
	{
		m_posMarkerX = new_x;
		m_changedPosition = true;
		emit positionChanged( m_pos );
		update();
	}
}

void TimeLineWidget::toggleAutoScroll( int _n )
{
	m_autoScroll = static_cast<AutoScrollStates>( _n );
}

void TimeLineWidget::toggleLoopPoints( int _n )
{
	m_loopPoints = static_cast<LoopPointStates>( _n );
	update();
}

void TimeLineWidget::toggleBehaviourAtStop( int _n )
{
	m_behaviourAtStop = static_cast<BehaviourAtStopStates>( _n );
}

void TimeLineWidget::paintEvent( QPaintEvent * )
{
	QPainter p( this );

	// Draw background
	p.fillRect( 0, 0, width(), height(), p.background() );

	// Clip so that we only draw everything starting from the offset
	p.setClipRect( m_xOffset, 0, width() - m_xOffset, height() );

	// Draw the loop rectangle
	int const & loopRectMargin = getLoopRectangleVerticalPadding();
	int const loopRectHeight = this->height() - 2 * loopRectMargin;
	int const loopStart = markerX( loopBegin() ) + 8;
	int const loopEndR = markerX( loopEnd() ) + 9;
	int const loopRectWidth = loopEndR - loopStart;

	bool const loopPointsActive = loopPointsEnabled();

	// Draw the main rectangle (inner fill only)
	QRect outerRectangle( loopStart, loopRectMargin, loopRectWidth - 1, loopRectHeight - 1 );
	p.fillRect( outerRectangle, loopPointsActive ? getActiveLoopBrush() : getInactiveLoopBrush());

	// Draw the bar lines and numbers
	// Activate hinting on the font
	QFont font = p.font();
	font.setHintingPreference( QFont::PreferFullHinting );
	p.setFont(font);
	int const fontAscent = p.fontMetrics().ascent();
	int const fontHeight = p.fontMetrics().height();

	QColor const & barLineColor = getBarLineColor();
	QColor const & barNumberColor = getBarNumberColor();

	bar_t barNumber = m_begin.getBar();
	int const x = m_xOffset + s_posMarkerPixmap->width() / 2 -
			( ( static_cast<int>( m_begin * m_ppb ) / TimePos::ticksPerBar() ) % static_cast<int>( m_ppb ) );

	for( int i = 0; x + i * m_ppb < width(); ++i )
	{
		++barNumber;
		if( ( barNumber - 1 ) %
			qMax( 1, qRound( 1.0f / 3.0f *
				TimePos::ticksPerBar() / m_ppb ) ) == 0 )
		{
			const int cx = x + qRound( i * m_ppb );
			p.setPen( barLineColor );
			p.drawLine( cx, 5, cx, height() - 6 );

			const QString s = QString::number( barNumber );
			p.setPen( barNumberColor );
			p.drawText( cx + 5, ((height() - fontHeight) / 2) + fontAscent, s );
		}
	}

	// Draw the main rectangle (outer border)
	p.setPen( loopPointsActive ? getActiveLoopColor() : getInactiveLoopColor() );
	p.setBrush( Qt::NoBrush );
	p.drawRect( outerRectangle );

	// Draw the inner border outline (no fill)
	QRect innerRectangle = outerRectangle.adjusted( 1, 1, -1, -1 );
	p.setPen( loopPointsActive ? getActiveLoopInnerColor() : getInactiveLoopInnerColor() );
	p.setBrush( Qt::NoBrush );
	p.drawRect( innerRectangle );

	// Draw the position marker
	p.setOpacity( 0.6 );
	p.drawPixmap( m_posMarkerX, height() - s_posMarkerPixmap->height(), *s_posMarkerPixmap );
}

void TimeLineWidget::mousePressEvent( QMouseEvent* event )
{
	if( event->x() < m_xOffset )
	{
		return;
	}
	if( event->button() == Qt::LeftButton  && !(event->modifiers() & Qt::ShiftModifier) )
	{
		m_action = MovePositionMarker;
		if( event->x() - m_xOffset < s_posMarkerPixmap->width() )
		{
			m_moveXOff = event->x() - m_xOffset;
		}
		else
		{
			m_moveXOff = s_posMarkerPixmap->width() / 2;
		}
	}
	else if( event->button() == Qt::LeftButton  && (event->modifiers() & Qt::ShiftModifier) )
	{
		m_action = SelectSongTCO;
		m_initalXSelect = event->x();
	}
	else if( event->button() == Qt::RightButton )
	{
		m_moveXOff = s_posMarkerPixmap->width() / 2;
		const TimePos t = m_begin + static_cast<int>( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb );
		const TimePos loopMid = ( m_loopPos[0] + m_loopPos[1] ) / 2;

		if( t < loopMid )
		{
			m_action = MoveLoopBegin;
		}
		else if( t > loopMid )
		{
			m_action = MoveLoopEnd;
		}

		if( m_loopPos[0] > m_loopPos[1]  )
		{
			qSwap( m_loopPos[0], m_loopPos[1] );
		}

		m_loopPos[( m_action == MoveLoopBegin ) ? 0 : 1] = t;
	}

	if( m_action == MoveLoopBegin || m_action == MoveLoopEnd )
	{
		delete m_hint;
		m_hint = TextFloat::displayMessage( tr( "Hint" ),
					tr( "Press <%1> to disable magnetic loop points." ).arg(UI_CTRL_KEY),
					embed::getIconPixmap( "hint" ), 0 );
	}
	mouseMoveEvent( event );
}

void TimeLineWidget::mouseMoveEvent( QMouseEvent* event )
{
	parentWidget()->update(); // essential for widgets that this timeline had taken their mouse move event from.
	const TimePos t = m_begin + static_cast<int>( qMax( event->x() - m_xOffset - m_moveXOff, 0 ) * TimePos::ticksPerBar() / m_ppb );

	switch( m_action )
	{
		case MovePositionMarker:
			m_pos.setTicks(t.getTicks());
			Engine::getSong()->setToTime(t, m_mode);
			if (!( Engine::getSong()->isPlaying()))
			{
				//Song::Mode_None is used when nothing is being played.
				Engine::getSong()->setToTime(t, Song::Mode_None);
			}
			m_pos.setCurrentFrame( 0 );
			m_pos.setJumped( true );
			updatePosition();
			positionMarkerMoved();
			break;

		case MoveLoopBegin:
		case MoveLoopEnd:
		{
			const int i = m_action - MoveLoopBegin; // i == 0 || i == 1
			if( event->modifiers() & Qt::ControlModifier )
			{
				// no ctrl-press-hint when having ctrl pressed
				delete m_hint;
				m_hint = NULL;
				m_loopPos[i] = t;
			}
			else
			{
				m_loopPos[i] = t.quantize(1.0);
			}
			// Catch begin == end
			if( m_loopPos[0] == m_loopPos[1] )
			{
				// Note, swap 1 and 0 below and the behavior "skips" the other
				// marking instead of pushing it.
				if( m_action == MoveLoopBegin )
				{
					m_loopPos[0] -= TimePos::ticksPerBar();
				}
				else
				{
					m_loopPos[1] += TimePos::ticksPerBar();
				}
			}
			update();
			break;
		}
	case SelectSongTCO:
			emit regionSelectedFromPixels( m_initalXSelect , event->x() );
		break;

		default:
			break;
	}
}

void TimeLineWidget::mouseReleaseEvent( QMouseEvent* event )
{
	delete m_hint;
	m_hint = NULL;
	if ( m_action == SelectSongTCO ) { emit selectionFinished(); }
	m_action = NoAction;
}


code:
/*
 * ToolPluginView.cpp - implementation of ToolPluginView
 *
 * Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
 * Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "ToolPlugin.h"
#include "ToolPluginView.h"

#include <QIcon>

#include "embed.h"
#include "GuiApplication.h"
#include "MainWindow.h"

ToolPluginView::ToolPluginView( ToolPlugin * _toolPlugin ) :
	PluginView( _toolPlugin, NULL )
{
	gui->mainWindow()->addWindowedWidget( this );
	parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );

	setWindowTitle( _toolPlugin->displayName() );
	setWindowIcon( _toolPlugin->descriptor()->logo->pixmap() );
}


code:
/*
 * TrackContainerView.cpp - view-component for TrackContainer
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public
 * License along with this program (see COPYING); if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
 *
 */

#include "TrackContainerView.h"

#include <cmath>

#include <QApplication>
#include <QLayout>
#include <QMdiArea>
#include <QScrollBar>
#include <QWheelEvent>

#include "TrackContainer.h"
#include "BBTrack.h"
#include "DataFile.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "FileBrowser.h"
#include "ImportFilter.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "Song.h"
#include "StringPairDrag.h"
#include "GuiApplication.h"
#include "PluginFactory.h"

using namespace std;

TrackContainerView::TrackContainerView( TrackContainer * _tc ) :
	QWidget(),
	ModelView( NULL, this ),
	JournallingObject(),
	SerializingObjectHook(),
	m_currentPosition( 0, 0 ),
	m_tc( _tc ),
	m_trackViews(),
	m_scrollArea( new scrollArea( this ) ),
	m_ppb( DEFAULT_PIXELS_PER_BAR ),
	m_rubberBand( new RubberBand( m_scrollArea ) )
{
	m_tc->setHook( this );
	//keeps the direction of the widget, undepended on the locale
	setLayoutDirection( Qt::LeftToRight );
	QVBoxLayout * layout = new QVBoxLayout( this );
	layout->setMargin( 0 );
	layout->setSpacing( 0 );
	layout->addWidget( m_scrollArea );

	QWidget * scrollContent = new QWidget;
	m_scrollLayout = new QVBoxLayout( scrollContent );
	m_scrollLayout->setMargin( 0 );
	m_scrollLayout->setSpacing( 0 );
	m_scrollLayout->setSizeConstraint( QLayout::SetMinAndMaxSize );

	m_scrollArea->setWidget( scrollContent );

	m_scrollArea->show();
	m_rubberBand->hide();
	m_rubberBand->setEnabled( false );

	setAcceptDrops( true );

	connect( Engine::getSong(), SIGNAL( timeSignatureChanged( int, int ) ),
						this, SLOT( realignTracks() ) );
	connect( m_tc, SIGNAL( trackAdded( Track * ) ),
			this, SLOT( createTrackView( Track * ) ),
			Qt::QueuedConnection );
}

TrackContainerView::~TrackContainerView()
{
	while( !m_trackViews.empty() )
	{
		delete m_trackViews.takeLast();
	}
}

void TrackContainerView::saveSettings( QDomDocument & _doc,
							QDomElement & _this )
{
	MainWindow::saveWidgetState( this, _this );
}

void TrackContainerView::loadSettings( const QDomElement & _this )
{
	MainWindow::restoreWidgetState( this, _this );
}

TrackView * TrackContainerView::addTrackView( TrackView * _tv )
{
	m_trackViews.push_back( _tv );
	m_scrollLayout->addWidget( _tv );
	connect( this, SIGNAL( positionChanged( const TimePos & ) ),
				_tv->getTrackContentWidget(),
				SLOT( changePosition( const TimePos & ) ) );
	realignTracks();
	return( _tv );
}

void TrackContainerView::removeTrackView( TrackView * _tv )
{
	int index = m_trackViews.indexOf( _tv );
	if( index != -1 )
	{
		m_trackViews.removeAt( index );

		disconnect( _tv );
		m_scrollLayout->removeWidget( _tv );

		realignTracks();
		if( Engine::getSong() )
		{
			Engine::getSong()->setModified();
		}
	}
}

void TrackContainerView::moveTrackView( TrackView * trackView, int indexTo )
{
	// Can't move out of bounds
	if ( indexTo >= m_trackViews.size() || indexTo < 0 ) { return; }

	// Does not need to move to itself
	int indexFrom = m_trackViews.indexOf( trackView );
	if ( indexFrom == indexTo ) { return; }

	BBTrack::swapBBTracks( trackView->getTrack(),
			m_trackViews[indexTo]->getTrack() );

	m_scrollLayout->removeWidget( trackView );
	m_scrollLayout->insertWidget( indexTo, trackView );

	Track * track = m_tc->m_tracks[indexFrom];

	m_tc->m_tracks.remove( indexFrom );
	m_tc->m_tracks.insert( indexTo, track );
	m_trackViews.move( indexFrom, indexTo );

	realignTracks();
}

void TrackContainerView::moveTrackViewUp( TrackView * trackView )
{
	int index = m_trackViews.indexOf( trackView );

	moveTrackView( trackView, index - 1 );
}

void TrackContainerView::moveTrackViewDown( TrackView * trackView )
{
	int index = m_trackViews.indexOf( trackView );

	moveTrackView( trackView, index + 1 );
}

void TrackContainerView::scrollToTrackView( TrackView * _tv )
{
	if (!m_trackViews.contains(_tv))
	{
		qWarning("TrackContainerView::scrollToTrackView: TrackView is not owned by this");
	}
	else
	{
		int currentScrollTop = m_scrollArea->verticalScrollBar()->value();
		int scrollAreaHeight = m_scrollArea->size().height();
		int trackViewTop = _tv->pos().y();
		int trackViewBottom = trackViewTop + _tv->size().height();

		// displayed_location = widget_location - currentScrollTop
		// want to make sure that the widget top has displayed location > 0,
		// and widget bottom < scrollAreaHeight
		// trackViewTop - scrollY > 0 && trackViewBottom - scrollY < scrollAreaHeight
		// therefore scrollY < trackViewTop && scrollY > trackViewBottom - scrollAreaHeight
		int newScroll = std::max( trackViewBottom-scrollAreaHeight, std::min(currentScrollTop, trackViewTop) );
		m_scrollArea->verticalScrollBar()->setValue(newScroll);
	}
}

void TrackContainerView::realignTracks()
{
	m_scrollArea->widget()->setFixedWidth(width());
	m_scrollArea->widget()->setFixedHeight(
				m_scrollArea->widget()->minimumSizeHint().height());

	for( trackViewList::iterator it = m_trackViews.begin();
						it != m_trackViews.end(); ++it )
	{
		( *it )->show();
		( *it )->update();
	}
}

TrackView * TrackContainerView::createTrackView( Track * _t )
{
	//m_tc->addJournalCheckPoint();

	// Avoid duplicating track views
	for( trackViewList::iterator it = m_trackViews.begin();
						it != m_trackViews.end(); ++it )
	{
		if ( ( *it )->getTrack() == _t ) { return ( *it ); }
	}

	return _t->createView( this );
}

void TrackContainerView::deleteTrackView( TrackView * _tv )
{
	//m_tc->addJournalCheckPoint();

	Track * t = _tv->getTrack();
	removeTrackView( _tv );
	delete _tv;

	Engine::mixer()->requestChangeInModel();
	delete t;
	Engine::mixer()->doneChangeInModel();
}

const TrackView * TrackContainerView::trackViewAt( const int _y ) const
{
	const int abs_y = _y + m_scrollArea->verticalScrollBar()->value();
	int y_cnt = 0;

//	debug code
//	qDebug( "abs_y %d", abs_y );

	for( trackViewList::const_iterator it = m_trackViews.begin();
						it != m_trackViews.end(); ++it )
	{
		const int y_cnt1 = y_cnt;
		y_cnt += ( *it )->height();
		if( abs_y >= y_cnt1 && abs_y < y_cnt )
		{
			return( *it );
		}
	}
	return( NULL );
}

bool TrackContainerView::allowRubberband() const
{
	return( false );
}

void TrackContainerView::setPixelsPerBar( int ppb )
{
	m_ppb = ppb;

	// tell all TrackContentWidgets to update their background tile pixmap
	for( trackViewList::Iterator it = m_trackViews.begin();
						it != m_trackViews.end(); ++it )
	{
		( *it )->getTrackContentWidget()->updateBackground();
	}
}

void TrackContainerView::clearAllTracks()
{
	while( !m_trackViews.empty() )
	{
		TrackView * tv = m_trackViews.takeLast();
		Track * t = tv->getTrack();
		delete tv;
		delete t;
	}
}

void TrackContainerView::dragEnterEvent( QDragEnterEvent * _dee )
{
	StringPairDrag::processDragEnterEvent( _dee,
		QString( "presetfile,pluginpresetfile,samplefile,instrument,"
				"importedproject,soundfontfile,patchfile,vstpluginfile,projectfile,"
				"track_%1,track_%2" ).
						arg( Track::InstrumentTrack ).
						arg( Track::SampleTrack ) );
}

void TrackContainerView::stopRubberBand()
{
	m_rubberBand->hide();
	m_rubberBand->setEnabled( false );
}

void TrackContainerView::dropEvent( QDropEvent * _de )
{
	QString type = StringPairDrag::decodeKey( _de );
	QString value = StringPairDrag::decodeValue( _de );
	if( type == "instrument" )
	{
		InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
				Track::create( Track::InstrumentTrack,
								m_tc ) );
		InstrumentLoaderThread *ilt = new InstrumentLoaderThread(
					this, it, value );
		ilt->start();
		//it->toggledInstrumentTrackButton( true );
		_de->accept();
	}
	else if( type == "samplefile" || type == "pluginpresetfile"
		|| type == "soundfontfile" || type == "vstpluginfile"
		|| type == "patchfile" )
	{
		InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
				Track::create( Track::InstrumentTrack,
								m_tc ) );
		PluginFactory::PluginInfoAndKey piakn =
			pluginFactory->pluginSupportingExtension(FileItem::extension(value));
		Instrument * i = it->loadInstrument(piakn.info.name(), &piakn.key);
		i->loadFile( value );
		//it->toggledInstrumentTrackButton( true );
		_de->accept();
	}
	else if( type == "presetfile" )
	{
		DataFile dataFile( value );
		InstrumentTrack * it = dynamic_cast<InstrumentTrack *>(
				Track::create( Track::InstrumentTrack,
								m_tc ) );
		it->setSimpleSerializing();
		it->loadSettings( dataFile.content().toElement() );
		//it->toggledInstrumentTrackButton( true );
		_de->accept();
	}
	else if( type == "importedproject" )
	{
		ImportFilter::import( value, m_tc );
		_de->accept();
	}

	else if( type == "projectfile")
	{
		if( gui->mainWindow()->mayChangeProject(true) )
		{
			Engine::getSong()->loadProject( value );
		}
		_de->accept();
	}

	else if( type.left( 6 ) == "track_" )
	{
		DataFile dataFile( value.toUtf8() );
		Track::create( dataFile.content().firstChild().toElement(), m_tc );
		_de->accept();
	}
}

void TrackContainerView::resizeEvent( QResizeEvent * _re )
{
	realignTracks();
	QWidget::resizeEvent( _re );
}

RubberBand *TrackContainerView::rubberBand() const
{
	return m_rubberBand;
}

TrackContainerView::scrollArea::scrollArea( TrackContainerView * _parent ) :
	QScrollArea( _parent ),
	m_trackContainerView( _parent )
{
	setFrameStyle( QFrame::NoFrame );
	setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff );
}

TrackContainerView::scrollArea::~scrollArea()
{
}

void TrackContainerView::scrollArea::wheelEvent( QWheelEvent * _we )
{
	// always pass wheel-event to parent-widget (song-editor
	// bb-editor etc.) because they might want to use it for zooming
	// or scrolling left/right if a modifier-key is pressed, otherwise
	// they do not accept it and we pass it up to QScrollArea
	m_trackContainerView->wheelEvent( _we );
	if( !_we->isAccepted() )
	{
		QScrollArea::wheelEvent( _we );
	}
}

InstrumentLoaderThread::InstrumentLoaderThread( QObject *parent, InstrumentTrack *it, QString name ) :
	QThread( parent ),
	m_it( it ),
	m_name( name )
{
	m_containerThread = thread();
}

void InstrumentLoaderThread::run()
{
	Instrument *i = m_it->loadInstrument(m_name, nullptr,
				true /*always DnD*/);
	QObject *parent = i->parent();
	i->setParent( 0 );
	i->moveToThread( m_containerThread );
	i->setParent( parent );
}