aboutsummaryrefslogblamecommitdiff
path: root/qmidiplayer-desktop/qmpsettingswindow.cpp
blob: 8d4a9648ff8834f12995e1056d5d73d1472c4d5b (plain) (tree)
1
2
3
4
5
6
7
8
9
                    
                      
                      
               
                      
                         


                      
                                
                                      


                                 
                                                                                


                                     
                                                                       
                                                                                                            


                                         
                                       
                                                   









                                                      
                     


                             





                                                    

                                               
                     
                                                    




                                               
                     

                             
 


                                                                





                                                                                            

                                               
                                                                                                    
                                               
         
 
 
                                  
 
                       
                 







                                                                                                  



                                                                                                     



                                                                                                                                       





                                                                              
         
                                              
                                          
                                                                                                        
                                                                                                                                       

                               
                                                                                                                    



                 





















































                                                                                                                                                                                                                                      
                 






























                                                                             
                 



                                                                                    
                 






                                                                              
                 









                                                                                                                                    
                 






















                                                                                                                        
                 




                                                                                         
                 















                                                                                          
                 











                                                                                                                                    
                 

                                                                                                                 

 
                                                  
 












                                                                                                                                                                                                                                                         

 
                                                   
 
                                                        
         


                                                    
                 



























































                                                                                            
                 






                                                                             
         
                                                       
         


                                                                             
         
                                   

 
                                                   
 
                                              
         

                                                    
                 
























































                                                                                                     
                 



                                                       

 
                                      
 
                                                  
         




                                                                                                                                  
                 
























































                                                                                                    
                 



                                                                                
                 

                                                            
                 

                    




                                                                                         
                 
         
                     

 
                                                           
 







                                                                        
 









                                                  
                                                                      




                                                          
                                                                                                                   

                                 
#include <QLineEdit>
#include <QToolButton>
#include <QFileDialog>
#include <QDir>
#include <QMessageBox>
#include <QStandardPaths>
#include <QHeaderView>
#include <QCheckBox>
#include <set>
#include "qmpsettingswindow.hpp"
#include "qmpdeviceprioritydialog.hpp"
#include "ui_qmpsettingswindow.h"
#include "qmpmainwindow.hpp"

qmpSettingsWindow::qmpSettingsWindow(qmpSettings *qmpsettings,QWidget *parent) :
	QDialog(parent),
	ui(new Ui::qmpSettingsWindow)
{
	ui->setupUi(this);customOptions.clear();customOptPages.clear();
	connect(this,&qmpSettingsWindow::dialogClosing,(qmpMainWindow*)parent,&qmpMainWindow::dialogClosed);
	settings=qmpsettings;
	cwt=new qmpCustomizeWindow(this);
	cwa=new qmpCustomizeWindow(this);
	dps=new qmpDevPropDialog(this);
	devpriod=new qmpDevicePriorityDialog(this);
}

qmpSettingsWindow::~qmpSettingsWindow()
{
	delete ui;
}

void qmpSettingsWindow::closeEvent(QCloseEvent *event)
{
	setVisible(false);
	loadOption();
	emit dialogClosing();
	event->accept();
}
void qmpSettingsWindow::hideEvent(QHideEvent *event)
{
	emit dialogClosing();
	event->accept();
}

void qmpSettingsWindow::on_buttonBox_accepted()
{
	saveOption();
	qmpMainWindow::getInstance()->setupWidget();
	emit dialogClosing();
}

void qmpSettingsWindow::on_buttonBox_rejected()
{
	loadOption();
	emit dialogClosing();
}

void qmpSettingsWindow::updatePluginList(qmpPluginManager *pmgr)
{
	std::vector<qmpPlugin> *plugins=pmgr->getPlugins();
	QVariant *data=static_cast<QVariant*>(settings->getOptionCustom("DisabledPlugins"));
	QList<QVariant> disabled_plugins_l=static_cast<QVariant*>(data)->toList();
	delete data;
	std::set<std::string> disabled_plugins_s;
	for(auto &i:disabled_plugins_l)
		disabled_plugins_s.insert(i.toString().toStdString());
	for(unsigned i=0;i<plugins->size();++i)
	{
		bool enabled=disabled_plugins_s.find(plugins->at(i).name)==disabled_plugins_s.end();
		plugins->at(i).enabled=enabled;
	}
}

void qmpSettingsWindow::postInit()
{
	setupWidgets();
	int sf=0;
	QVariant *data=static_cast<QVariant*>(settings->getOptionCustom("FluidSynth/SoundFonts"));
	for(auto i:data->toList())
		if(!i.toString().startsWith('#'))
		{
			sf=1;
			break;
		}
	delete data;
	std::string selecteddev;
	std::vector<std::string> devs=qmpMainWindow::getInstance()->getPlayer()->getMidiOutDevices();
	std::set<std::string> devset;
	for(auto dev:devs)devset.insert(dev);
	QVariant *devpriov=static_cast<QVariant*>(qmpMainWindow::getInstance()->getSettings()->getOptionCustom("Midi/DevicePriority"));
	QList<QVariant> devprio=devpriov->toList();
	delete devpriov;
	for(auto &setdev:devprio)
		if(devset.find(setdev.toString().toStdString())!=devset.end())
		{
			selecteddev=setdev.toString().toStdString();
			break;
		}
	if(selecteddev=="Internal FluidSynth"&&!sf)
	{
		if(QMessageBox::question(this,
		tr("No soundfont loaded"),
		tr("Internal fluidsynth is the only available MIDI output but it has no soundfont set. "
		   "Would you like to setup soundfonts now? You may have to reload the internal synth afterwards."))==QMessageBox::Yes)
		{
			show();
			ui->tabWidget->setCurrentWidget(qobject_cast<QWidget*>(pageForTab("SoundFonts")->parent()));
		}
	}
}

void qmpSettingsWindow::registerCustomizeWidgetOptions()
{
	QPushButton *pbCustomizeToolbar=new QPushButton(tr("Customize..."));
	QPushButton *pbCustomizeActions=new QPushButton(tr("Customize..."));
	QVariant toolbar_def_val=QList<QVariant>({"Channel","Playlist","Effects","Visualization"});
	QVariant actions_def_val=QList<QVariant>({"FileInfo","Render","Panic","ReloadSynth"});
	settings->registerOptionCustom("Behavior","Customize toolbar","Behavior/Toolbar",pbCustomizeToolbar,&toolbar_def_val,std::bind(&qmpCustomizeWindow::save,cwt),std::bind(&qmpCustomizeWindow::load,cwt,std::placeholders::_1));
	settings->registerOptionCustom("Behavior","Customize actions","Behavior/Actions",pbCustomizeActions,&actions_def_val,std::bind(&qmpCustomizeWindow::save,cwa),std::bind(&qmpCustomizeWindow::load,cwa,std::placeholders::_1));
	connect(pbCustomizeToolbar,&QPushButton::clicked,[this]{loadOption("Behavior/Toolbar");cwt->show();});
	connect(pbCustomizeActions,&QPushButton::clicked,[this]{loadOption("Behavior/Actions");cwa->show();});
	connect(cwt,&QDialog::accepted,[this]{saveOption("Behavior/Toolbar");qmpMainWindow::getInstance()->setupWidget();});
	connect(cwa,&QDialog::accepted,[this]{saveOption("Behavior/Actions");qmpMainWindow::getInstance()->setupWidget();});
	connect(cwt,&QDialog::rejected,[this]{loadOption("Behavior/Toolbar");});
	connect(cwa,&QDialog::rejected,[this]{loadOption("Behavior/Actions");});
	qmpMainWindow::getInstance()->setupWidget();
}

void qmpSettingsWindow::registerSoundFontOption()
{
	QWidget *sfpanel=new QWidget();
	sfpanel->setLayout(new QVBoxLayout);
	sfpanel->layout()->setMargin(0);
	QTableWidget *twsf=new QTableWidget();
	twsf->setColumnCount(2);
	twsf->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
	twsf->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
	twsf->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
	twsf->setHorizontalHeaderLabels({tr("E"),tr("Path")});
	twsf->setHorizontalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
	twsf->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
	sfpanel->layout()->addWidget(twsf);
	QWidget *controls=new QWidget();
	controls->setLayout(new QHBoxLayout);
	controls->layout()->setMargin(0);
	QPushButton *pbsfadd=new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_DialogOpenButton),QString());
	QPushButton *pbsfrem=new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_DialogDiscardButton),QString());
	QPushButton *pbsfmup=new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_ArrowUp),QString());
	QPushButton *pbsfmdn=new QPushButton(style()->standardIcon(QStyle::StandardPixmap::SP_ArrowDown),QString());
	controls->layout()->addWidget(pbsfadd);
	controls->layout()->addWidget(pbsfrem);
	controls->layout()->addWidget(pbsfmup);
	controls->layout()->addWidget(pbsfmdn);
	sfpanel->layout()->addWidget(controls);

	connect(pbsfadd,&QPushButton::clicked,[twsf,this]{
		QStringList sl=QFileDialog::getOpenFileNames(this,"Add File","","SoundFont files (*.sf2)");
		for(int i=0;i<sl.size();++i){
			twsf->insertRow(twsf->rowCount());
			QTableWidgetItem *sfn,*sfe;
			twsf->setItem(twsf->rowCount()-1,1,sfn=new QTableWidgetItem(sl[i]));
			twsf->setItem(twsf->rowCount()-1,0,sfe=new QTableWidgetItem());
			sfe->setCheckState(Qt::CheckState::Unchecked);
			sfn->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable);
			sfe->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable|Qt::ItemFlag::ItemIsUserCheckable);
		}
	});
	connect(pbsfrem,&QPushButton::clicked,[twsf]{
		QList<QTableWidgetItem*> sl=twsf->selectedItems();
		for(int i=0;i<sl.size();++i)
			twsf->removeRow(twsf->row(sl[i]));
	});
	connect(pbsfmup,&QPushButton::clicked,[twsf]{
		int cid=twsf->currentRow();if(!cid)return;
		QTableWidgetItem *ci=twsf->takeItem(cid,1);
		QTableWidgetItem *ce=twsf->takeItem(cid,0);
		twsf->removeRow(cid);
		twsf->insertRow(cid-1);
		twsf->setItem(cid-1,0,ce);
		twsf->setItem(cid-1,1,ci);
		twsf->setCurrentCell(cid-1,1);
	});
	connect(pbsfmdn,&QPushButton::clicked,[twsf]{
		int cid=twsf->currentRow();if(cid==twsf->rowCount()-1)return;
		QTableWidgetItem *ci=twsf->takeItem(cid,1);
		QTableWidgetItem *ce=twsf->takeItem(cid,0);
		twsf->removeRow(cid);
		twsf->insertRow(cid+1);
		twsf->setItem(cid+1,0,ce);
		twsf->setItem(cid+1,1,ci);
		twsf->setCurrentCell(cid+1,1);
	});

	QVariant sf_def_val=QList<QVariant>();
	auto save_func=[twsf]()->void*{
		QList<QVariant> sflist;
		for(int i=0;i<twsf->rowCount();++i)
		{
			QString sfs=twsf->item(i,1)->text();
			if(twsf->item(i,0)->checkState()==Qt::CheckState::Unchecked)
				sfs="#"+sfs;
			sflist.push_back(sfs);
		}
		return new QVariant(sflist);
	};
	auto load_func=[twsf](void* data){
		QList<QVariant> sflist=static_cast<QVariant*>(data)->toList();
		twsf->clearContents();
		twsf->setRowCount(0);
		for(int i=0;i<sflist.size();++i)
		{
			twsf->insertRow(i);
			QTableWidgetItem *sfn,*sfe;
			QString sf=sflist[i].toString();
			bool enabled=!sf.startsWith('#');
			if(!enabled)sf=sf.mid(1);
			twsf->setItem(i,1,sfn=new QTableWidgetItem(sf));
			twsf->setItem(i,0,sfe=new QTableWidgetItem());
			sfn->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable);
			sfe->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable|Qt::ItemFlag::ItemIsUserCheckable);
			sfe->setCheckState(enabled?Qt::CheckState::Checked:Qt::CheckState::Unchecked);
		}
	};
	settings->registerOptionCustom("SoundFonts","","FluidSynth/SoundFonts",sfpanel,&sf_def_val,save_func,load_func);
}

void qmpSettingsWindow::registerPluginOption(qmpPluginManager *pmgr)
{
	QTableWidget *twplugins=new QTableWidget();
	twplugins->setColumnCount(4);
	twplugins->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::ResizeToContents);
	twplugins->setSelectionMode(QAbstractItemView::SelectionMode::SingleSelection);
	twplugins->setSelectionBehavior(QAbstractItemView::SelectionBehavior::SelectRows);
	twplugins->setHorizontalHeaderLabels({tr("E"),tr("Plugin Name"),tr("Version"),tr("Path")});
	twplugins->setHorizontalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
	twplugins->setVerticalScrollMode(QAbstractItemView::ScrollMode::ScrollPerPixel);
	QVariant ep_def_val=QList<QVariant>();
	auto save_func=[twplugins,this]()->void*{
		QVariant *data=static_cast<QVariant*>(settings->getOptionCustom("DisabledPlugins"));
		QList<QVariant> disabled_plugins_ol=static_cast<QVariant*>(data)->toList();
		delete data;
		std::set<std::string> disabled_plugins_s;
		for(auto &i:disabled_plugins_ol)
			disabled_plugins_s.insert(i.toString().toStdString());
		for(int i=0;i<twplugins->rowCount();++i)
		{
			QString pn=twplugins->item(i,1)->text();
			if(twplugins->item(i,0)->checkState()==Qt::CheckState::Unchecked)
				disabled_plugins_s.insert(pn.toStdString());
			else
				disabled_plugins_s.erase(pn.toStdString());
		}
		QList<QVariant> disabled_plugins;
		for(auto &i:disabled_plugins_s)
			disabled_plugins.push_back(QString(i.c_str()));
		return new QVariant(disabled_plugins);
	};
	auto load_func=[twplugins,pmgr](void* data){
		QList<QVariant> disabled_plugins_l=static_cast<QVariant*>(data)->toList();
		std::set<std::string> disabled_plugins;
		for(auto i:disabled_plugins_l)
			disabled_plugins.insert(i.toString().toStdString());

		twplugins->clearContents();
		twplugins->setRowCount(0);

		std::vector<qmpPlugin> *plugins=pmgr->getPlugins();
		for(int i=0;static_cast<size_t>(i)<plugins->size();++i)
		{
			twplugins->insertRow(i);
			qmpPlugin &p=plugins->at(static_cast<size_t>(i));
			QTableWidgetItem *icb;
			twplugins->setItem(i,0,icb=new QTableWidgetItem());
			bool enabled=disabled_plugins.find(p.name)==disabled_plugins.end();
			icb->setCheckState(enabled?Qt::CheckState::Checked:Qt::CheckState::Unchecked);
			icb->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable|Qt::ItemFlag::ItemIsUserCheckable);
			twplugins->setItem(i,1,new QTableWidgetItem(p.name.c_str()));
			twplugins->setItem(i,2,new QTableWidgetItem(p.version.c_str()));
			twplugins->setItem(i,3,new QTableWidgetItem(p.path.c_str()));
			for(int j=1;j<=3;++j)
			twplugins->item(i,j)->setFlags(Qt::ItemFlag::ItemIsEnabled|Qt::ItemFlag::ItemIsSelectable);
		}
	};
	settings->registerOptionCustom("Plugins","","DisabledPlugins",twplugins,&ep_def_val,save_func,load_func);
}

void qmpSettingsWindow::registerExtraMidiOptions()
{
	QPushButton *pbDevPrio=new QPushButton("...");
	connect(pbDevPrio,&QPushButton::clicked,[this]{loadOption("Midi/DevicePriority");devpriod->show();});
	connect(devpriod,&QDialog::accepted,[this]{saveOption("Midi/DevicePriority");});
	connect(devpriod,&QDialog::rejected,[this]{loadOption("Midi/DevicePriority");});
	QVariant devprio_def_val=QList<QVariant>({"Internal FluidSynth"});
	settings->registerOptionCustom("MIDI","Select MIDI output devices","Midi/DevicePriority",pbDevPrio,&devprio_def_val,std::bind(&qmpDevicePriorityDialog::save,devpriod),std::bind(&qmpDevicePriorityDialog::load,devpriod,std::placeholders::_1));

	QPushButton *pbDevProp=new QPushButton("...");
	connect(pbDevProp,&QPushButton::clicked,[this]{loadOption("Midi/DeviceInitializationFiles");dps->show();});
	connect(dps,&QDialog::accepted,[this]{saveOption("Midi/DeviceInitializationFiles");});
	connect(dps,&QDialog::rejected,[this]{loadOption("Midi/DeviceInitializationFiles");});
	QVariant devprop_def_val=QList<QVariant>({});
	settings->registerOptionCustom("MIDI","External MIDI device setup","Midi/DeviceInitializationFiles",pbDevProp,&devprop_def_val,std::bind(&qmpDevPropDialog::save,dps),std::bind(&qmpDevPropDialog::load,dps,std::placeholders::_1));
}

void qmpSettingsWindow::saveOption(std::string key)
{
	auto save_opt=[this](std::string& key)->QVariant
	{
		qmpOption &o=settings->options[key];
		QVariant ret;
		switch(o.type)
		{
			case qmpOption::ParameterType::parameter_int:
			{
				QSpinBox *sb=qobject_cast<QSpinBox*>(o.widget);
				if(sb)
					ret=sb->value();
			}
			break;
			case qmpOption::ParameterType::parameter_uint:
			{
				QHexSpinBox *sb=qobject_cast<QHexSpinBox*>(o.widget);
				if(sb)
				{
					int val=sb->value();
					ret=reinterpret_cast<unsigned&>(val);
				}
			}
			break;
			case qmpOption::ParameterType::parameter_bool:
			{
				QCheckBox *cb=qobject_cast<QCheckBox*>(o.widget);
				if(cb)
					ret=cb->isChecked();
			}
			break;
			case qmpOption::ParameterType::parameter_double:
			{
				QDoubleSpinBox *sb=qobject_cast<QDoubleSpinBox*>(o.widget);
				if(sb)
					ret=sb->value();
			}
			break;
			case qmpOption::ParameterType::parameter_str:
			{
				QLineEdit *le=qobject_cast<QLineEdit*>(o.widget);
				if(le)
					ret=le->text();
			}
			break;
			case qmpOption::ParameterType::parameter_enum:
			{
				QComboBox *cb=qobject_cast<QComboBox*>(o.widget);
				if(cb)
					ret=cb->currentText();
			}
			break;
			case qmpOption::ParameterType::parameter_url:
			{
				QFileEdit *fe=qobject_cast<QFileEdit*>(o.widget);
				if(fe)
					ret=fe->text();
			}
			break;
			default:
				if(o.save_func)
				{
					QVariant* var=static_cast<QVariant*>(o.save_func());
					ret=QVariant(*var);
					delete var;
				}
			break;
		}
		return ret;
	};
	if(key.length())
	{
		QVariant r=save_opt(key);
		if(r.isValid())
			settings->settings->setValue(QString(key.c_str()),r);
	}
	else for(std::string& key:settings->optionlist)
	{
		QVariant r=save_opt(key);
		if(r.isValid())
			settings->settings->setValue(QString(key.c_str()),r);
	}
	settings->settings->sync();
}

void qmpSettingsWindow::loadOption(std::string key)
{
	auto load_opt=[this](std::string& key)
	{
		qmpOption &o=settings->options[key];
		switch(o.type)
		{
			case qmpOption::ParameterType::parameter_int:
			{
				QSpinBox *sb=qobject_cast<QSpinBox*>(o.widget);
				if(sb)
					sb->setValue(settings->getOptionInt(key));
			}
			break;
			case qmpOption::ParameterType::parameter_uint:
			{
				QHexSpinBox *sb=qobject_cast<QHexSpinBox*>(o.widget);
				if(sb)
					sb->setValue(settings->getOptionUint(key));
			}
			break;
			case qmpOption::ParameterType::parameter_bool:
			{
				QCheckBox *cb=qobject_cast<QCheckBox*>(o.widget);
				if(cb)
					cb->setChecked(settings->getOptionBool(key));
			}
			break;
			case qmpOption::ParameterType::parameter_double:
			{
				QDoubleSpinBox *sb=qobject_cast<QDoubleSpinBox*>(o.widget);
				if(sb)
					sb->setValue(settings->getOptionDouble(key));
			}
			break;
			case qmpOption::ParameterType::parameter_str:
			{
				QLineEdit *le=qobject_cast<QLineEdit*>(o.widget);
				if(le)
					le->setText(QString(settings->getOptionString(key).c_str()));
			}
			break;
			case qmpOption::ParameterType::parameter_enum:
			{
				QComboBox *cb=qobject_cast<QComboBox*>(o.widget);
				if(cb)
					cb->setCurrentIndex(settings->getOptionEnumInt(key));
			}
			break;
			case qmpOption::ParameterType::parameter_url:
			{
				QFileEdit *fe=qobject_cast<QFileEdit*>(o.widget);
				if(fe)
					fe->setText(QString(settings->getOptionString(key).c_str()));
			}
			break;
			default:
				if(o.load_func)
				{
					void *var=settings->getOptionCustom(key);
					o.load_func(var);
					delete static_cast<QVariant*>(var);
				}
			break;
		}
	};
	if(key.length())load_opt(key);
	else for(std::string& key:settings->optionlist)
		load_opt(key);
}

void qmpSettingsWindow::setupWidgets()
{
	for(std::string& key:settings->optionlist)
	{
		if(!settings->options[key].desc.length()&&settings->options[key].type!=qmpOption::ParameterType::parameter_custom)
			continue;
		QWidget *optw=nullptr;
		qmpOption &o=settings->options[key];
		switch(o.type)
		{
			case qmpOption::ParameterType::parameter_int:
			{
				QSpinBox *sb=new QSpinBox;
				sb->setMinimum(o.minv.toInt());
				sb->setMaximum(o.maxv.toInt());
				sb->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=sb;
			}
			break;
			case qmpOption::ParameterType::parameter_uint:
			{
				QHexSpinBox *sb=new QHexSpinBox;
				sb->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=sb;
			}
			break;
			case qmpOption::ParameterType::parameter_bool:
			{
				QCheckBox *cb=new QCheckBox(QString(o.desc.c_str()));
				cb->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
				optw=cb;
			}
			break;
			case qmpOption::ParameterType::parameter_double:
			{
				QDoubleSpinBox *sb=new QDoubleSpinBox;
				sb->setMinimum(o.minv.toDouble());
				sb->setMaximum(o.maxv.toDouble());
				sb->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=sb;
			}
			break;
			case qmpOption::ParameterType::parameter_str:
			{
				QLineEdit* te=new QLineEdit();
				te->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=te;
			}
			break;
			case qmpOption::ParameterType::parameter_enum:
			{
				QComboBox* cb=new QComboBox();
				for(std::string& item:o.enumlist)cb->addItem(QString(item.c_str()));
				cb->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=cb;
			}
			break;
			case qmpOption::ParameterType::parameter_url:
			{
				QFileEdit* fe=new QFileEdit();
				fe->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed);
				optw=fe;
			}
			break;
			default:
				optw=o.widget;
			break;
		}
		o.widget=optw;
		QGridLayout* page=pageForTab(o.tab);
		if(o.type==qmpOption::ParameterType::parameter_bool||
			(o.type==qmpOption::parameter_custom&&!o.desc.length()))
		{
			int row=page->rowCount();
			page->addWidget(o.widget,row,0,1,2);
		}
		else
		{
			QLabel* lb=new QLabel(o.desc.c_str(),page->parentWidget());
			lb->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
			int row=page->rowCount();
			page->addWidget(lb,row,0);
			page->addWidget(o.widget,row,1);
		}
	}
	loadOption();
}

QGridLayout* qmpSettingsWindow::pageForTab(std::string tab)
{
	if(customOptPages.find(tab)!=customOptPages.end())
		return customOptPages[tab];
	QWidget* w=new QWidget;
	QGridLayout* page=new QGridLayout(w);
	w->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding);
	ui->tabWidget->addTab(w,QString(tab.c_str()));
	customOptPages[tab]=page;
	return page;
}

QFileEdit::QFileEdit(QWidget *par):QWidget(par)
{
	QHBoxLayout *layout=new QHBoxLayout(this);
	layout->setMargin(0);
	le=new QLineEdit(this);
	layout->addWidget(le);
	tb=new QToolButton(this);
	tb->setText("...");
	layout->addWidget(tb);
	connect(tb,&QToolButton::clicked,this,&QFileEdit::chooseFile);
}
QString QFileEdit::text(){return le->text();}
void QFileEdit::setText(const QString& s){le->setText(s);}
void QFileEdit::chooseFile()
{
	QString s=QFileDialog::getOpenFileName(nullptr,tr("Select a file"),QFileInfo(text()).dir().absolutePath());
	if(s.length())setText(s);
}