/*
 *  Copyright (C) 2006-2019  Ronald Blankendaal
 *
 *  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; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
package org.dbgl.gui.abstractdialog;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.dbgl.exception.DrivelettersExhaustedException;
import org.dbgl.gui.GeneralPurposeDialogs;
import org.dbgl.gui.controls.DaControl;
import org.dbgl.gui.controls.DaControlConvertorAdapter;
import org.dbgl.gui.controls.MetaControl;
import org.dbgl.gui.dialog.EditMixerDialog;
import org.dbgl.gui.dialog.EditMountDialog;
import org.dbgl.gui.dialog.EditNativeCommandDialog;
import org.dbgl.gui.interfaces.DaControlConvertor;
import org.dbgl.model.NativeCommand;
import org.dbgl.model.aggregate.DosboxVersion;
import org.dbgl.model.conf.Autoexec;
import org.dbgl.model.conf.Configuration;
import org.dbgl.model.entity.TemplateProfileBase;
import org.dbgl.model.helper.DriveLetterHelper;
import org.dbgl.model.repository.DosboxVersionRepository;
import org.dbgl.service.FileLocationService;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ExpandAdapter;
import org.eclipse.swt.events.ExpandEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.ExpandBar;
import org.eclipse.swt.widgets.ExpandItem;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.Text;


public abstract class EditConfigurableDialog<T> extends SizeControlledTabbedDialog<T> {

	protected enum DosboxConfAction {
		SET, SWITCH, RELOAD, RELOAD_TEMPLATE
	}

	protected final List<DaControl> daControls_ = new ArrayList<>();
	protected final List<MetaControl> metaControls_ = new ArrayList<>();
	protected final List<MetaControl> autoexecControls_ = new ArrayList<>();

	protected List<DosboxVersion> dbversionsList_;
	protected int dbversionIndex_;
	protected Combo dbversionCombo_;
	protected Button setButton_;
	protected ExpandItem booterExpandItem_, dosExpandItem_;
	protected org.eclipse.swt.widgets.List mountingpointsList_;

	private Button switchButton_, ipx_;
	private Text ipxNet_;
	private Combo output_, scaler_, machine_, cpuType_, midiDevice_, sbType_, oplMode_;
	private org.eclipse.swt.widgets.List nativeCommandsList_;

	public EditConfigurableDialog(Shell parent, String dialogName) {
		super(parent, dialogName);
	}

	abstract protected void doPerformDosboxConfAction(DosboxConfAction action, DosboxVersion newDosboxVersion);

	@Override
	protected boolean prepare() {
		try {
			dbversionsList_ = new DosboxVersionRepository().listAll();
			dbversionIndex_ = DosboxVersionRepository.indexOfDefault(dbversionsList_);
			return true;
		} catch (SQLException e) {
			GeneralPurposeDialogs.warningMessage(getParent(), e);
			return false;
		}
	}

	protected void updateControlsByConfigurable(TemplateProfileBase configurable, boolean multiEdit) {
		Configuration overrideConf = configurable.getConfiguration();
		Autoexec overrideAuto = overrideConf.getAutoexec();
		Configuration combinedConf = configurable.getCombinedConfiguration();
		Autoexec combinedAuto = combinedConf.getAutoexec();
		DosboxVersion dosbox = configurable.getDosboxVersion();
		Configuration dosboxConf = dosbox.getConfiguration();

		// enable or disable controls
		daControls_.forEach(x -> x.enableOrDisable(dosbox));

		// set possible values for certain dropdowns
		String[] machineValues = dosbox.isUsingNewMachineConfig() ? settings_.getValues("profile", "machine073"): settings_.getValues("profile", "machine");
		if (!Arrays.equals(machine_.getItems(), machineValues)) {
			machine_.setItems(machineValues);
		}

		updateComboItems(output_, dosbox.likelyDirect3DSupport(), new String[] {"openglhq", "direct3d"});
		updateComboItems(scaler_, dosbox.likelyHardwareScalerSupport(), new String[] {"hardware2x", "hardware3x"});
		updateComboItems(machine_, dosbox.likelyAmstradSupport(), new String[] {"cga_mono", "svga_s3_full", "amstrad"});
		updateComboItems(cpuType_, dosbox.likelyAdditionalCPUTypesSupport(), new String[] {"486", "pentium", "pentium_mmx"});
		updateComboItems(midiDevice_, dosbox.hasMT32Support(), new String[] {"mt32", "synth", "timidity"});
		updateComboItems(sbType_, dosbox.likelySoundBlaster16VibraSupport(), new String[] {"sb16vibra"});
		updateComboItems(oplMode_, dosbox.hasHardwareOPLSupport(), new String[] {"hardware", "hardwaregb"});

		// set control values
		daControls_.forEach(x -> x.setControlByConf(dosboxConf, overrideConf, combinedConf, multiEdit));

		DaControl.setFieldIfEnabled(mountingpointsList_, configurable.getMountStringsForUI());
		DaControl.setFieldIfEnabled(dosExpandItem_, !combinedAuto.isBooter());
		DaControl.setFieldIfEnabled(booterExpandItem_, combinedAuto.isBooter());

		autoexecControls_.forEach(x -> x.setControlByAutoexec(overrideAuto, combinedAuto, multiEdit));

		setButton_.setEnabled(false);
		switchButton_.setEnabled(false);

		ipxNet_.setEnabled(ipx_.getSelection());

		updateNativeCommands(-1, configurable);
	}

	protected void updateConfigurableByControls(TemplateProfileBase configurable) {
		daControls_.forEach(x -> x.updateConfigurationByControl(configurable));
		autoexecControls_.forEach(x -> x.updateAutoexecByControl(configurable.getConfiguration().getAutoexec()));

		configurable.setBooter(booterExpandItem_.getExpanded());
	}

	protected void updateComboItems(Combo combo, boolean available, String[] items) {
		List<String> comboItems = new ArrayList<>(Arrays.asList(combo.getItems()));
		boolean changes = false;
		if (available) {
			for (String s: items) {
				if (!comboItems.contains(s)) {
					comboItems.add(s);
					changes = true;
				}
			}
		} else {
			for (String s: items) {
				if (comboItems.contains(s)) {
					comboItems.remove(s);
					changes = true;
				}
			}
		}
		if (changes) {
			combo.setItems(comboItems.toArray(new String[comboItems.size()]));
		}
	}

	protected Group createGeneralTab(String capturesText, String configFileText) {
		Composite composite = createTabWithComposite(text_.get("dialog.template.tab.general"), new GridLayout());

		Group associationGroup = createGroup(composite, text_.get("dialog.template.association"), 5);
		associationGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		dbversionCombo_ = createLabelAndWideCombo(associationGroup, text_.get("dialog.template.dosboxversion"));
		dbversionCombo_.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		dbversionsList_.forEach(x -> dbversionCombo_.add(x.getTitle()));
		dbversionCombo_.select(dbversionIndex_);
		setButton_ = createButton(associationGroup, null, text_.get("dialog.template.set"), text_.get("dialog.template.set.tooltip"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doPerformDosboxConfAction(DosboxConfAction.SET, dbversionsList_.get(dbversionCombo_.getSelectionIndex()));
			}
		});
		switchButton_ = createButton(associationGroup, null, text_.get("dialog.template.switch"), text_.get("dialog.template.switch.tooltip"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doPerformDosboxConfAction(DosboxConfAction.SWITCH, dbversionsList_.get(dbversionCombo_.getSelectionIndex()));
			}
		});
		createButton(associationGroup, null, text_.get("dialog.template.reloadsettings"), text_.get("dialog.template.reloadsettings.tooltip"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doPerformDosboxConfAction(DosboxConfAction.RELOAD, dbversionsList_.get(dbversionCombo_.getSelectionIndex()));
			}
		});
		dbversionCombo_.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				setButton_.setEnabled(true);
				switchButton_.setEnabled(true);
			}
		});

		Group miscGroup = createGroup(composite, text_.get("dialog.template.miscellaneous"), 3);
		miscGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		createLabel(miscGroup);
		createLabel(miscGroup, text_.get("dialog.template.active"));
		createLabel(miscGroup, text_.get("dialog.template.inactive"));
		Label priorityLabel = createLabel(miscGroup, text_.get("dialog.template.priority"));
		Combo priorityActive = createCombo(miscGroup, settings_.getValues("profile", "priority_active"), text_.get("dialog.template.priority.tooltip"));
		Combo priorityInactive = createCombo(miscGroup, settings_.getValues("profile", "priority_inactive"), text_.get("dialog.template.priority.tooltip"));
		daControls_.add(new DaControl(priorityLabel, new Control[] {priorityActive, priorityInactive}, "sdl", "priority"));
		createLabelAndCheckButtonDaControl(miscGroup, text_.get("dialog.template.waitonerror"), "sdl", "waitonerror");
		createLabel(miscGroup);
		createLabelAndCheckButtonAutoexecControl(miscGroup, text_.get("dialog.template.exitafterwards"), Autoexec::getExit, Autoexec::setExit);
		createLabel(miscGroup);
		Text language = createLabelAndTooltipTextDaControl(miscGroup, text_.get("dialog.template.languagefile"), text_.get("dialog.template.languagefile.tooltip"), "dosbox", "language");
		language.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		Text captures = createLabelAndText(miscGroup, text_.get("dialog.template.captures"), 2, capturesText, text_.get("dialog.template.captures.tooltip"));
		captures.setEditable(false);
		Text configFile_ = createLabelAndText(miscGroup, text_.get("dialog.profile.configfile"), 2, configFileText);
		configFile_.setEditable(false);

		return associationGroup;
	}

	protected void createDisplayTab() {
		TabFolder subTabFolder = createSubTabs("dialog.template.tab.display", 1, 2);
		Composite releaseComposite = (Composite)subTabFolder.getChildren()[0];
		Composite experimentalComposite = (Composite)subTabFolder.getChildren()[1];

		Group groupRelease = createGroup(releaseComposite, text_.get("dialog.template.general"), 4);
		groupRelease.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false));

		output_ = createLabelAndTooltipComboDaControl(groupRelease, text_.get("dialog.template.output"), settings_.getValues("profile", "output"), 10, text_.get("dialog.template.output.tooltip"),
			"sdl", "output");
		createLabel(groupRelease, 2, 2);
		createLabelAndTooltipComboDaControl(groupRelease, text_.get("dialog.template.frameskip"), settings_.getValues("profile", "frameskip"), 15, text_.get("dialog.template.frameskip.tooltip"),
			"render", "frameskip");
		Label scalerLabel = createLabel(groupRelease, text_.get("dialog.template.scaler"));
		scaler_ = createCombo(groupRelease, settings_.getValues("profile", "scaler"), 15, text_.get("dialog.template.scaler.tooltip"));
		Button scaler_forced = createLabelAndTooltipCheckButton(groupRelease, text_.get("dialog.template.scalerforced"), text_.get("dialog.template.scalerforced.tooltip"), false);

		daControls_.add(new DaControl(scalerLabel, new Control[] {scaler_, scaler_forced}, "render", "scaler", new DaControlConvertorAdapter() {
			public String toConfValue(String[] values) {
				String result = values[0];
				if (Boolean.valueOf(values[1]))
					result += " forced";
				return result;
			}

			public String[] toControlValues(String value) {
				if (value == null)
					return new String[0];
				String[] results = new String[2];
				if (value.endsWith("forced")) {
					results[0] = value.substring(0, value.length() - 7);
					results[1] = "true";
				} else {
					results[0] = value;
					results[1] = "false";
				}
				return results;
			}
		}));

		createLabelAndTooltipComboDaControl(groupRelease, text_.get("dialog.template.fullscreenresolution"), settings_.getValues("profile", "fullresolution"), 10,
			text_.get("dialog.template.fullscreenresolution.tooltip"), "sdl", "fullresolution");
		createLabel(groupRelease, 2, 5);
		createLabelAndTooltipComboDaControl(groupRelease, text_.get("dialog.template.windowresolution"), settings_.getValues("profile", "windowresolution"), 10,
			text_.get("dialog.template.windowresolution.tooltip"), "sdl", "windowresolution");
		createLabelAndTooltipCheckButtonDaControl(groupRelease, text_.get("dialog.template.fullscreen"), text_.get("dialog.template.fullscreen.tooltip"), "sdl", "fullscreen");
		createLabelAndTooltipCheckButtonDaControl(groupRelease, text_.get("dialog.template.doublebuffering"), text_.get("dialog.template.doublebuffering.tooltip"), "sdl", "fulldouble");
		createLabelAndTooltipCheckButtonDaControl(groupRelease, text_.get("dialog.template.aspectcorrection"), text_.get("dialog.template.aspectcorrection.tooltip"), "render", "aspect");

		Group groupExpGeneral = createGroup(experimentalComposite, text_.get("dialog.template.general"), 2);
		groupExpGeneral.setLayoutData(new GridData(SWT.BEGINNING, SWT.BEGINNING, false, false, 1, 3));
		createLabelAndCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.autofit"), "render", "autofit");
		Combo pixelshader = createLabelAndComboDaControl(groupExpGeneral, text_.get("dialog.template.pixelshader"), 20, "sdl", "pixelshader");
		String[] shaders = FileLocationService.getInstance().listShaderFilenames();
		if (shaders != null && shaders.length > 0) {
			pixelshader.setItems(shaders);
			pixelshader.add("none", 0);
		} else {
			pixelshader.setItems(settings_.getValues("profile", "pixelshader"));
		}
		createLabelAndCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.linewise"), "render", "linewise");
		createLabelAndCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.char9"), "render", "char9");
		createLabelAndCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.multiscan"), "render", "multiscan");
		createLabelAndCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.cgasnow"), "cpu", "cgasnow");
		createLabelAndEditableComboDaControl(groupExpGeneral, text_.get("dialog.template.overscan"), settings_.getValues("profile", "overscan"), 10, "sdl", "overscan");
		createLabelAndComboDaControl(groupExpGeneral, text_.get("dialog.template.vsyncmode"), settings_.getValues("profile", "vsyncmode"), 10, "vsync", "vsyncmode");
		createLabelAndTextDaControl(groupExpGeneral, text_.get("dialog.template.vsyncrate"), "vsync", "vsyncrate");
		createLabelAndTextDaControl(groupExpGeneral, text_.get("dialog.template.forcerate"), "cpu", "forcerate");
		createLabelAndComboDaControl(groupExpGeneral, text_.get("dialog.template.videoram"), settings_.getValues("profile", "vmemsize"), 10, "dosbox", "vmemsize");
		createLabelAndTooltipCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.fullborderless"), text_.get("dialog.template.fullborderless.tooltip"), "sdl", "fullborderless");
		createLabelAndTooltipCheckButtonDaControl(groupExpGeneral, text_.get("dialog.template.glfullvsync"), text_.get("dialog.template.glfullvsync.tooltip"), "sdl", "glfullvsync");

		Group groupExpGlide = createGroup(experimentalComposite, text_.get("dialog.template.glide"), 2);
		createLabelAndComboDaControl(groupExpGlide, text_.get("dialog.template.glide"), settings_.getValues("profile", "glide"), 10, "glide", "glide");
		createLabelAndTextDaControl(groupExpGlide, text_.get("dialog.template.glideport"), "glide", "port", "grport");
		createLabelAndComboDaControl(groupExpGlide, text_.get("dialog.template.lfbglide"), settings_.getValues("profile", "lfbglide"), 10, "glide", "lfb");
		createLabelAndCheckButtonDaControl(groupExpGlide, text_.get("dialog.template.splash3dfx"), "glide", "splash");

		Group groupExpVoodoo = createGroup(experimentalComposite, text_.get("dialog.template.voodoo"), 2);
		createLabelAndComboDaControl(groupExpVoodoo, text_.get("dialog.template.voodoo"), settings_.getValues("profile", "voodoo"), 10, "pci", "voodoo");
		createLabelAndComboDaControl(groupExpVoodoo, text_.get("dialog.template.voodoomem"), settings_.getValues("profile", "voodoomem"), 10, "pci", "voodoomem");

		Group groupExpPPScaling = createGroup(experimentalComposite, text_.get("dialog.template.pixelperfectscaling"), 2);
		createLabelAndSpinnerDaControl(groupExpPPScaling, text_.get("dialog.template.surfacenpsharpness"), 0, 100, "sdl", "surfacenp-sharpness");
	}

	protected Group createMachineTab() {
		TabFolder subTabFolder = createSubTabs("dialog.template.tab.machine", 1, 1);
		Composite releaseComposite = (Composite)subTabFolder.getChildren()[0];
		Composite experimentalComposite = (Composite)subTabFolder.getChildren()[1];

		Group cpuGroup = createGroup(releaseComposite, text_.get("dialog.template.cpu"), 6);
		machine_ = createLabelAndTooltipComboDaControl(cpuGroup, text_.get("dialog.template.machine"), new String[] {}, 20, text_.get("dialog.template.machine.tooltip"), "dosbox", "machine");
		cpuType_ = createLabelAndTooltipComboDaControl(cpuGroup, text_.get("dialog.template.cputype"), settings_.getValues("profile", "cputype"), 10, text_.get("dialog.template.cputype.tooltip"),
			"cpu", "cputype");
		createLabel(cpuGroup, 2, 1);
		createLabelAndTooltipComboDaControl(cpuGroup, text_.get("dialog.template.core"), settings_.getValues("profile", "core"), 10, text_.get("dialog.template.core.tooltip"), "cpu", "core");
		createLabel(cpuGroup, 4, 1);
		createLabelAndEditableComboDaControl(cpuGroup, text_.get("dialog.template.cycles"), text_.get("dialog.template.cycles.tooltip"), settings_.getValues("profile", "cycles"), 15, "cpu", "cycles");
		createLabelAndEditableComboDaControl(cpuGroup, text_.get("dialog.template.up"), text_.get("dialog.template.up.tooltip"), settings_.getValues("profile", "cycles_up"), 10, "cpu", "cycleup");
		createLabelAndEditableComboDaControl(cpuGroup, text_.get("dialog.template.down"), text_.get("dialog.template.down.tooltip"), settings_.getValues("profile", "cycles_down"), 10, "cpu",
			"cycledown");

		Group memoryGroup = createGroup(releaseComposite, text_.get("dialog.template.memory"), 4);
		createLabelAndTooltipComboDaControl(memoryGroup, text_.get("dialog.template.memorysize"), settings_.getValues("profile", "memsize"), 10, text_.get("dialog.template.memorysize.tooltip"),
			"dosbox", "memsize");
		createLabel(memoryGroup, 2, 4);
		createLabelAndTooltipCheckButtonDaControl(memoryGroup, text_.get("dialog.template.xms"), text_.get("dialog.template.xms.tooltip"), "dos", "xms");
		createLabelAndTooltipComboDaControl(memoryGroup, text_.get("dialog.template.ems"), settings_.getValues("profile", "ems"), 10, text_.get("dialog.template.ems.tooltip"), "dos", "ems");
		createLabelAndTooltipComboDaControl(memoryGroup, text_.get("dialog.template.umb"), settings_.getValues("profile", "umb"), 10, text_.get("dialog.template.umb.tooltip"), "dos", "umb");

		Group expMemoryGroup = createGroup(experimentalComposite, text_.get("dialog.template.memory"), 2);
		createLabelAndTextDaControl(expMemoryGroup, text_.get("dialog.template.memorysizekb"), "dosbox", "memsizekb");
		createLabelAndEditableComboDaControl(expMemoryGroup, text_.get("dialog.template.memalias"), settings_.getValues("profile", "memalias"), 10, "dosbox", "memalias");

		return memoryGroup;
	}

	protected void createAudioTab() {
		TabFolder subTabFolder = createSubTabs("dialog.template.tab.audio", 3, 3);
		Composite releaseComposite = (Composite)subTabFolder.getChildren()[0];
		Composite experimentalComposite = (Composite)subTabFolder.getChildren()[1];

		Group generalGroup = createGroup(releaseComposite, text_.get("dialog.template.general"), 2);
		generalGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipCheckButtonDaControl(generalGroup, text_.get("dialog.template.silentmode"), text_.get("dialog.template.silentmode.tooltip"), "mixer", "nosound");
		createLabelAndTooltipComboDaControl(generalGroup, text_.get("dialog.template.samplerate"), settings_.getValues("profile", "rate"), 10, text_.get("dialog.template.samplerate.tooltip"), "mixer",
			"rate");
		createLabelAndTooltipComboDaControl(generalGroup, text_.get("dialog.template.blocksize"), settings_.getValues("profile", "blocksize"), 10, text_.get("dialog.template.blocksize.tooltip"),
			"mixer", "blocksize");
		createLabelAndEditableComboDaControl(generalGroup, text_.get("dialog.template.prebuffer"), text_.get("dialog.template.prebuffer.tooltip"), settings_.getValues("profile", "prebuffer"), 10,
			"mixer", "prebuffer");
		Label mpu401Label = createLabel(generalGroup, text_.get("dialog.template.mpu401"));
		Combo mpu401 = createCombo(generalGroup, settings_.getValues("profile", "mpu401"), text_.get("dialog.template.mpu401.tooltip"));
		daControls_.add(new DaControl(mpu401Label, mpu401, "midi", "intelligent", "midi", "mpu401", new DaControlConvertorAdapter() {
			public String toConfValue(String[] values) {
				return values[0];
			}

			public String[] toControlValues(String value) {
				if (value == null)
					return new String[0];
				return new String[] {value};
			}

			public String[] toConfValues(String[] values) {
				String[] result = new String[2];
				result[0] = String.valueOf(!values[0].equalsIgnoreCase("none"));
				result[1] = String.valueOf(!values[0].equalsIgnoreCase("uart"));
				return result;
			}

			public String[] toControlValues(String[] values) {
				boolean intelligent = Boolean.valueOf(values[0]);
				boolean mpu = Boolean.valueOf(values[1]);
				return new String[] {mpu ? intelligent ? "intelligent": "uart": "none"};
			}
		}));
		midiDevice_ = createLabelAndTooltipComboDaControl(generalGroup, text_.get("dialog.template.mididevice"), settings_.getValues("profile", "device"), 10,
			text_.get("dialog.template.mididevice.tooltip"), "midi", "device", "mididevice");
		createLabelAndTooltipTextDaControl(generalGroup, text_.get("dialog.template.midiconfig"), text_.get("dialog.template.midiconfig.tooltip"), "midi", "config", "midiconfig");

		Label mixerConfigLabel = createLabel(generalGroup, text_.get("dialog.template.mixercommand"));
		Composite mixerSettings = createInnerComposite(generalGroup, new GridData(SWT.FILL, SWT.FILL, true, false), 2);
		Text mixer_config = createText(mixerSettings, new GridData(SWT.FILL, SWT.CENTER, true, false));
		autoexecControls_.add(new MetaControl(mixerConfigLabel, mixer_config).connectAutoexec(Autoexec::getMixer, Autoexec::setMixer));
		createThreeDotButton(mixerSettings, new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				EditMixerDialog dialog = new EditMixerDialog(shell_, mixer_config.getText());
				String command = dialog.open();
				if (command != null) {
					mixer_config.setText(command);
				}
			}
		});

		Group soundblasterGroup = createGroup(releaseComposite, text_.get("dialog.template.soundblaster"), 2);
		sbType_ = createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sbtype"), settings_.getValues("profile", "sbtype"), 10, text_.get("dialog.template.sbtype.tooltip"),
			"sblaster", "type", "sbtype");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sboplrate"), settings_.getValues("profile", "oplrate"), 10, text_.get("dialog.template.sboplrate.tooltip"),
			"sblaster", "oplrate");
		oplMode_ = createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sboplmode"), settings_.getValues("profile", "oplmode"), 10,
			text_.get("dialog.template.sboplmode.tooltip"), "sblaster", "oplmode");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sboplemu"), settings_.getValues("profile", "oplemu"), 10, text_.get("dialog.template.sboplemu.tooltip"),
			"sblaster", "oplemu");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sbaddress"), settings_.getValues("profile", "sbbase"), 10, text_.get("dialog.template.sbaddress.tooltip"),
			"sblaster", "base", "sbbase");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sbirq"), settings_.getValues("profile", "irq"), 10, text_.get("dialog.template.sbirq.tooltip"), "sblaster",
			"irq");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sbdma"), settings_.getValues("profile", "dma"), 10, text_.get("dialog.template.sbdma.tooltip"), "sblaster",
			"dma");
		createLabelAndTooltipComboDaControl(soundblasterGroup, text_.get("dialog.template.sbhdma"), settings_.getValues("profile", "hdma"), 10, text_.get("dialog.template.sbhdma.tooltip"), "sblaster",
			"hdma");
		createLabelAndTooltipCheckButtonDaControl(soundblasterGroup, text_.get("dialog.template.mixer"), text_.get("dialog.template.mixer.tooltip"), "sblaster", "mixer", "sbmixer");

		Group gusGroup = createGroup(releaseComposite, text_.get("dialog.template.gravisultrasound"), 2);
		gusGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipCheckButtonDaControl(gusGroup, text_.get("dialog.template.enablegus"), text_.get("dialog.template.enablegus.tooltip"), "gus", "gus");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusrate"), settings_.getValues("profile", "gusrate"), 10, text_.get("dialog.template.gusrate.tooltip"), "gus", "rate",
			"gusrate");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusaddress"), settings_.getValues("profile", "gusbase"), 10, text_.get("dialog.template.gusaddress.tooltip"), "gus",
			"base", "gusbase");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusirq1"), settings_.getValues("profile", "irq1"), 10, text_.get("dialog.template.gusirq1.tooltip"), "gus", "irq1",
			"gusirq");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusirq2"), settings_.getValues("profile", "irq2"), 10, text_.get("dialog.template.gusirq1.tooltip"), "gus", "irq2");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusdma1"), settings_.getValues("profile", "dma1"), 10, text_.get("dialog.template.gusdma1.tooltip"), "gus", "dma1",
			"gusdma");
		createLabelAndTooltipComboDaControl(gusGroup, text_.get("dialog.template.gusdma2"), settings_.getValues("profile", "dma2"), 10, text_.get("dialog.template.gusdma1.tooltip"), "gus", "dma2");
		createLabelAndTooltipTextDaControl(gusGroup, text_.get("dialog.template.ultradir"), text_.get("dialog.template.ultradir.tooltip"), "gus", "ultradir");

		Group speakerGroup = createGroup(releaseComposite, text_.get("dialog.template.pcspeaker"), 2);
		speakerGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipCheckButtonDaControl(speakerGroup, text_.get("dialog.template.enablepcspeaker"), text_.get("dialog.template.enablepcspeaker.tooltip"), "speaker", "pcspeaker");
		createLabelAndTooltipComboDaControl(speakerGroup, text_.get("dialog.template.pcrate"), settings_.getValues("profile", "pcrate"), 10, text_.get("dialog.template.pcrate.tooltip"), "speaker",
			"pcrate");

		Group tandyGroup = createGroup(releaseComposite, text_.get("dialog.template.tandy"), 2);
		tandyGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndTooltipComboDaControl(tandyGroup, text_.get("dialog.template.enabletandy"), settings_.getValues("profile", "tandy"), 10, text_.get("dialog.template.enabletandy.tooltip"),
			"speaker", "tandy");
		createLabelAndTooltipComboDaControl(tandyGroup, text_.get("dialog.template.tandyrate"), settings_.getValues("profile", "tandyrate"), 10, text_.get("dialog.template.tandyrate.tooltip"),
			"speaker", "tandyrate");

		Group disneyGroup = createGroup(releaseComposite, text_.get("dialog.template.miscellaneous"), 2);
		disneyGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipCheckButtonDaControl(disneyGroup, text_.get("dialog.template.enablesoundsource"), text_.get("dialog.template.enablesoundsource.tooltip"), "speaker", "disney");

		Group generalExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.general"), 2);
		generalExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndCheckButtonDaControl(generalExpGroup, text_.get("dialog.template.swapstereo"), "mixer", "swapstereo");

		Group soundblasterExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.soundblaster"), 2);
		soundblasterExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndComboDaControl(soundblasterExpGroup, text_.get("dialog.template.hardwaresbaddress"), settings_.getValues("profile", "hardwaresbbase"), 10, "sblaster", "hardwarebase");
		createLabelAndCheckButtonDaControl(soundblasterExpGroup, text_.get("dialog.template.goldplay"), "sblaster", "goldplay");
		createLabelAndSpinnerDaControl(soundblasterExpGroup, text_.get("dialog.template.fmstrength"), 1, 1000, "sblaster", "fmstrength");

		Group mt32ExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.mt32"), 2);
		mt32ExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 3));
		createLabelAndTextDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.romdir"), "midi", "mt32.romdir");
		createLabelAndCheckButtonDaControl(mt32ExpGroup, text_.get("dialog.template.swapstereo"), "midi", "mt32.reverse.stereo", false);
		createLabelAndCheckButtonDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.verboselogging"), "midi", "mt32.verbose", false);
		createLabelAndCheckButtonDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.multithread"), "midi", "mt32.thread", false);
		createLabelAndSpinnerDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.chunk"), 2, 100, "midi", "mt32.chunk");
		createLabelAndSpinnerDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.prebuffer"), 3, 200, "midi", "mt32.prebuffer");
		createLabelAndSpinnerDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.partials"), 0, 256, "midi", "mt32.partials");
		createLabelAndComboDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.dac"), settings_.getValues("profile", "mt32dac"), 10, "midi", "mt32.dac");
		createLabelAndComboDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.analog"), settings_.getValues("profile", "mt32analog"), 10, "midi", "mt32.analog");
		createLabelAndComboDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.reverbmode"), settings_.getValues("profile", "mt32reverbmode"), 10, "midi", "mt32.reverb.mode");
		createLabelAndComboDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.reverbtime"), settings_.getValues("profile", "mt32reverbtime"), 10, "midi", "mt32.reverb.time");
		createLabelAndComboDaControl(mt32ExpGroup, text_.get("dialog.template.mt32.reverblevel"), settings_.getValues("profile", "mt32reverblevel"), 10, "midi", "mt32.reverb.level");

		Group fluidExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.fluidsynth"), 2);
		fluidExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 2));
		createLabelAndEditableComboDaControl(fluidExpGroup, text_.get("dialog.template.fluidsynth.driver"), settings_.getValues("profile", "fluidsynthdriver"), 10, "midi", "fluid.driver");
		createLabelAndTextDaControl(fluidExpGroup, text_.get("dialog.template.fluidsynth.soundfont"), "midi", "fluid.soundfont");
		createLabelAndComboDaControl(fluidExpGroup, text_.get("dialog.template.fluidsynth.samplerate"), settings_.getValues("profile", "fluidsynthsamplerate"), 10, "midi", "fluid.samplerate");
		createLabelAndTextDaControl(fluidExpGroup, text_.get("dialog.template.fluidsynth.gain"), "midi", "fluid.gain");

		Group innovaExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.innova"), 2);
		innovaExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndCheckButtonDaControl(innovaExpGroup, text_.get("dialog.template.innovaenable"), "innova", "innova");
		createLabelAndComboDaControl(innovaExpGroup, text_.get("dialog.template.innovarate"), settings_.getValues("profile", "innovarate"), 10, "innova", "samplerate");
		createLabelAndComboDaControl(innovaExpGroup, text_.get("dialog.template.innovaaddress"), settings_.getValues("profile", "innovabase"), 10, "innova", "sidbase");
		createLabelAndComboDaControl(innovaExpGroup, text_.get("dialog.template.innovaquality"), settings_.getValues("profile", "innovaquality"), 10, "innova", "quality");

		Group ps1ExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.ps1"), 2);
		ps1ExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndCheckButtonDaControl(ps1ExpGroup, text_.get("dialog.template.ps1enable"), "speaker", "ps1audio", true);
		createLabelAndComboDaControl(ps1ExpGroup, text_.get("dialog.template.ps1rate"), settings_.getValues("profile", "ps1rate"), 10, "speaker", "ps1audiorate");
	}

	protected void createIOTab() {
		TabFolder subTabFolder = createSubTabs("dialog.template.tab.io", 3, 4);
		Composite releaseComposite = (Composite)subTabFolder.getChildren()[0];
		Composite experimentalComposite = (Composite)subTabFolder.getChildren()[1];

		Group mouseGroup = createGroup(releaseComposite, text_.get("dialog.template.mouse"), 2);
		mouseGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndTooltipCheckButtonDaControl(mouseGroup, text_.get("dialog.template.autolock"), text_.get("dialog.template.autolock.tooltip"), "sdl", "autolock");
		Label sensitivityLabel = createLabel(mouseGroup, text_.get("dialog.template.sensitivity"));
		Combo sensitivity = createEditableCombo(mouseGroup, settings_.getValues("profile", "sensitivity"), 20, text_.get("dialog.template.sensitivity.tooltip"));
		daControls_.add(new DaControl(sensitivityLabel, sensitivity, "sdl", "sensitivity", new DaControlConvertor() {
			public String toConfValue(String[] values) {
				return String.join(",", values);
			}

			public String[] toControlValues(String value) {
				return value == null ? new String[0]: new String[] {value};
			}

			public String[] toConfValues(String[] values) {
				return null;
			}

			public String[] toControlValues(String[] values) {
				return null;
			}
		}));

		Group keyboardGroup = createGroup(releaseComposite, text_.get("dialog.template.keyboard"), 2);
		keyboardGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipCheckButtonDaControl(keyboardGroup, text_.get("dialog.template.usescancodes"), text_.get("dialog.template.usescancodes.tooltip"), "sdl", "usescancodes");
		Label mapperFileLabel = createLabel(keyboardGroup, text_.get("dialog.template.mapperfile"));
		Composite mapperfileSettings = createInnerComposite(keyboardGroup, new GridData(SWT.FILL, SWT.CENTER, true, false), 2);
		Text mapperfile = createText(mapperfileSettings, SWT.BORDER, new GridData(SWT.FILL, SWT.CENTER, true, false), null, text_.get("dialog.template.mapperfile.tooltip"));
		daControls_.add(new DaControl(mapperFileLabel, mapperfile, "sdl", "mapperfile"));
		createButton(mapperfileSettings, "*", new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				mapperfile.setText(settings_.getValue("profile", "uniquemapperfile"));
			}
		});
		createLabelAndWideEditableComboDaControl(keyboardGroup, text_.get("dialog.template.keyboardlayout"), settings_.getValues("profile", "keyboardlayout"), 15,
			text_.get("dialog.template.keyboardlayout.tooltip"), "dos", "keyboardlayout");
		createLabelAndTextAutoexecControl(keyboardGroup, text_.get("dialog.template.keybcommand"), Autoexec::getKeyb, Autoexec::setKeyb);

		Group joystickGroup = createGroup(releaseComposite, text_.get("dialog.template.joystick"), 2);
		joystickGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		createLabelAndTooltipComboDaControl(joystickGroup, text_.get("dialog.template.joysticktype"), settings_.getValues("profile", "joysticktype"), 10,
			text_.get("dialog.template.joysticktype.tooltip"), "bios", "joysticktype", "joystick", "joysticktype");
		createLabelAndTooltipCheckButtonDaControl(joystickGroup, text_.get("dialog.template.timedemulation"), text_.get("dialog.template.timedemulation.tooltip"), "joystick", "timed");
		createLabelAndTooltipCheckButtonDaControl(joystickGroup, text_.get("dialog.template.autofire"), text_.get("dialog.template.autofire.tooltip"), "joystick", "autofire");
		createLabelAndTooltipCheckButtonDaControl(joystickGroup, text_.get("dialog.template.swap34"), text_.get("dialog.template.swap34.tooltip"), "joystick", "swap34");
		createLabelAndTooltipCheckButtonDaControl(joystickGroup, text_.get("dialog.template.buttonwrapping"), text_.get("dialog.template.buttonwrapping.tooltip"), "joystick", "buttonwrap");

		Group modemGroup = createGroup(releaseComposite, text_.get("dialog.template.modem"), 2);
		modemGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		createLabelAndTooltipTextDaControl(modemGroup, text_.get("dialog.template.serial1"), text_.get("dialog.template.serial.tooltip"), "serial", "serial1");
		createLabelAndTooltipTextDaControl(modemGroup, text_.get("dialog.template.serial2"), text_.get("dialog.template.serial.tooltip"), "serial", "serial2");
		createLabelAndTooltipTextDaControl(modemGroup, text_.get("dialog.template.serial3"), text_.get("dialog.template.serial.tooltip"), "serial", "serial3");
		createLabelAndTooltipTextDaControl(modemGroup, text_.get("dialog.template.serial4"), text_.get("dialog.template.serial.tooltip"), "serial", "serial4");

		Group networkGroup = createGroup(releaseComposite, text_.get("dialog.template.network"), 2);
		networkGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		ipx_ = createLabelAndTooltipCheckButtonDaControl(networkGroup, text_.get("dialog.template.enableipx"), text_.get("dialog.template.enableipx.tooltip"), "ipx", "ipx");
		Label ipxnetCommandLabel = createLabel(networkGroup, text_.get("dialog.template.ipxnetcommand"));
		ipxnetCommandLabel.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false, 2, 1));
		ipxNet_ = createText(networkGroup, new GridData(SWT.FILL, SWT.CENTER, true, false, 2, 1));
		autoexecControls_.add(new MetaControl(ipxnetCommandLabel, ipxNet_).connectAutoexec(Autoexec::getIpxnet, Autoexec::setIpxnet));

		ipx_.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				ipxNet_.setEnabled(ipx_.getSelection());
			}
		});

		Group mouseExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.mouse"), 2);
		mouseExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndCheckButtonDaControl(mouseExpGroup, text_.get("dialog.template.int33"), "dos", "int33");
		createLabelAndCheckButtonDaControl(mouseExpGroup, text_.get("dialog.template.biosps2"), "dos", "biosps2");
		createLabelAndCheckButtonDaControl(mouseExpGroup, text_.get("dialog.template.aux"), "keyboard", "aux");
		createLabelAndComboDaControl(mouseExpGroup, text_.get("dialog.template.auxdevice"), settings_.getValues("profile", "auxdevice"), 10, "keyboard", "auxdevice");

		Group miscExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.miscellaneous"), 2);
		miscExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndSpinnerDaControl(miscExpGroup, text_.get("dialog.template.files"), 8, 255, "dos", "files");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.isapnpbios"), "cpu", "isapnpbios");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.ide1"), "ide, primary", "enable");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.ide2"), "ide, secondary", "enable");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.ide3"), "ide, tertiary", "enable");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.ide4"), "ide, quaternary", "enable");
		createLabelAndCheckButtonDaControl(miscExpGroup, text_.get("dialog.template.automount"), "dos", "automount");

		Group printerExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.printer"), 2);
		printerExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
		createLabelAndCheckButtonDaControl(printerExpGroup, text_.get("dialog.template.printerenable"), "printer", "printer");
		createLabelAndSpinnerDaControl(printerExpGroup, text_.get("dialog.template.printerdpi"), 0, Short.MAX_VALUE, "printer", "dpi");
		createLabelAndSpinnerDaControl(printerExpGroup, text_.get("dialog.template.printerwidth"), 0, Short.MAX_VALUE, "printer", "width");
		createLabelAndSpinnerDaControl(printerExpGroup, text_.get("dialog.template.printerheight"), 0, Short.MAX_VALUE, "printer", "height");
		createLabelAndComboDaControl(printerExpGroup, text_.get("dialog.template.printeroutput"), settings_.getValues("profile", "printeroutput"), 10, "printer", "printoutput");
		createLabelAndCheckButtonDaControl(printerExpGroup, text_.get("dialog.template.printermultipage"), "printer", "multipage");
		createLabelAndTextDaControl(printerExpGroup, text_.get("dialog.template.printerdocpath"), "printer", "docpath");
		createLabelAndSpinnerDaControl(printerExpGroup, text_.get("dialog.template.printertimeout"), 0, Short.MAX_VALUE, "printer", "timeout");

		Group joystickExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.joystick"), 2);
		createLabelAndTooltipCheckButtonDaControl(joystickExpGroup, text_.get("dialog.template.circularinput"), text_.get("dialog.template.circularinput.tooltip"), "joystick", "circularinput");
		createLabelAndSpinnerDaControl(joystickExpGroup, text_.get("dialog.template.deadzone"), 1, 100, "joystick", "deadzone");
		joystickExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));

		Group parallelExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.parallel"), 2);
		parallelExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 2, 1));
		createLabelAndTextDaControl(parallelExpGroup, text_.get("dialog.template.parallel1"), "parallel", "parallel1");
		createLabelAndTextDaControl(parallelExpGroup, text_.get("dialog.template.parallel2"), "parallel", "parallel2");
		createLabelAndTextDaControl(parallelExpGroup, text_.get("dialog.template.parallel3"), "parallel", "parallel3");
		createLabelAndCheckButtonDaControl(parallelExpGroup, text_.get("dialog.template.dongle"), "parallel", "dongle");

		Group ne2000ExpGroup = createGroup(experimentalComposite, text_.get("dialog.template.ne2000"), 2);
		ne2000ExpGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 2, 1));
		createLabelAndCheckButtonDaControl(ne2000ExpGroup, text_.get("dialog.template.ne2000enable"), "ne2000", "ne2000");
		createLabelAndTextDaControl(ne2000ExpGroup, text_.get("dialog.template.ne2000base"), "ne2000", "nicbase");
		createLabelAndTextDaControl(ne2000ExpGroup, text_.get("dialog.template.ne2000irq"), "ne2000", "nicirq");
		createLabelAndTextDaControl(ne2000ExpGroup, text_.get("dialog.template.ne2000macaddress"), "ne2000", "macaddr");
		createLabelAndTextDaControl(ne2000ExpGroup, text_.get("dialog.template.ne2000realnic"), "ne2000", "realnic");
	}

	protected void createCustomCommandsTab(TemplateProfileBase configurable, boolean multiEdit) {
		TabFolder subTabFolder = createSubTabs("dialog.template.tab.customcommands", "dialog.template.tab.dosboxautoexec", 2, "dialog.template.tab.native", 2);
		Composite dosboxComposite = (Composite)subTabFolder.getChildren()[0];
		Composite nativeComposite = (Composite)subTabFolder.getChildren()[1];

		Text[] customCommands = new Text[Autoexec.SECTIONS];
		for (int i = 0; i < Autoexec.SECTIONS; i++) {
			Label filterLabel = createLabel(dosboxComposite, text_.get("dialog.template.customcommand" + (i + 1)));
			customCommands[i] = createTextarea(dosboxComposite, false);
			autoexecControls_.add(new MetaControl(filterLabel, customCommands[i]).connectAutoexec(i, Autoexec::getCustomSection, Autoexec::setCustomSection));
		}
		nativeCommandsList_ = createList(nativeComposite, 1, 6);
		nativeCommandsList_.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent event) {
				if (nativeCommandsList_.getSelectionIndex() == -1) {
					doAddNativeCommand(configurable);
				} else {
					doEditNativeCommand(configurable);
				}
			}
		});
		createButton(nativeComposite, text_.get("dialog.template.mount.add"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doAddNativeCommand(configurable);
			}
		});
		createButton(nativeComposite, text_.get("dialog.template.mount.edit"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doEditNativeCommand(configurable);
			}
		});
		createButton(nativeComposite, text_.get("dialog.template.mount.remove"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doRemoveNativeCommand(configurable);
			}
		});
		createUpButton(nativeComposite, new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				int sel = nativeCommandsList_.getSelectionIndex();
				if (sel > 0) {
					Collections.swap(configurable.getNativeCommands(), sel, sel - 1);
					updateNativeCommands(sel - 1, configurable);
				}
			}
		});
		createDownButton(nativeComposite, new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				int sel = nativeCommandsList_.getSelectionIndex();
				if (sel >= 0 && sel < nativeCommandsList_.getItemCount() - 1) {
					Collections.swap(configurable.getNativeCommands(), sel, sel + 1);
					updateNativeCommands(sel + 1, configurable);
				}
			}
		});
		createLabel(nativeComposite);

		if (multiEdit)
			Arrays.asList(nativeComposite.getChildren()).forEach(x -> x.setEnabled(false));
	}

	protected void updateNativeCommands(int sel, TemplateProfileBase configurable) {
		nativeCommandsList_.removeAll();
		for (NativeCommand cmd: configurable.getNativeCommands())
			nativeCommandsList_.add(cmd.toString());
		nativeCommandsList_.select(sel);
	}

	protected void doAddNativeCommand(TemplateProfileBase configurable) {
		EditNativeCommandDialog cmdDialog = new EditNativeCommandDialog(shell_, null);
		NativeCommand cmd = cmdDialog.open();
		if (cmd != null) {
			int nr = nativeCommandsList_.getSelectionIndex() + 1;
			configurable.getNativeCommands().add(nr, cmd);
			updateNativeCommands(nr, configurable);
		}
	}

	protected void doEditNativeCommand(TemplateProfileBase configurable) {
		int sel = nativeCommandsList_.getSelectionIndex();
		if (sel != -1) {
			NativeCommand cmd = configurable.getNativeCommands().get(sel);
			if (!cmd.isDosboxCommand()) {
				EditNativeCommandDialog cmdDialog = new EditNativeCommandDialog(shell_, cmd);
				cmd = cmdDialog.open();
				if (cmd != null) {
					configurable.getNativeCommands().set(sel, cmd);
					updateNativeCommands(sel, configurable);
				}
			}
		}
	}

	protected void doRemoveNativeCommand(TemplateProfileBase configurable) {
		int sel = nativeCommandsList_.getSelectionIndex();
		if (sel != -1) {
			NativeCommand cmd = configurable.getNativeCommands().get(sel);
			if (!cmd.isDosboxCommand()) {
				configurable.getNativeCommands().remove(sel);
				updateNativeCommands(Math.min(sel, nativeCommandsList_.getItemCount() - 1), configurable);
			}
		}
	}

	protected void createMountingTab(TemplateProfileBase configurable, boolean multiEdit) {
		Composite composite = createTabWithComposite(text_.get("dialog.template.tab.mounting"), new GridLayout());

		Group mountGroup = createGroup(composite, text_.get("dialog.template.mountingoverview"), 2);
		mountGroup.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
		mountingpointsList_ = createList(mountGroup, 1, 3);

		Group executeGroup = createGroup(composite, text_.get("dialog.template.execute"), new FillLayout());
		executeGroup.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

		ExpandBar dosBooterExpandBar = new ExpandBar(executeGroup, SWT.V_SCROLL);

		Composite booterComposite = createComposite(dosBooterExpandBar);
		Composite dosComposite = createComposite(dosBooterExpandBar);

		booterExpandItem_ = createExpandItem(dosBooterExpandBar, text_.get("dialog.template.booter"), false, booterComposite);
		dosExpandItem_ = createExpandItem(dosBooterExpandBar, text_.get("dialog.template.dos"), false, dosComposite);

		dosBooterExpandBar.addExpandListener(new ExpandAdapter() {
			public void itemCollapsed(ExpandEvent e) {
				dosBooterExpandBar.getItem((((ExpandItem)e.item).getText().equals(text_.get("dialog.template.dos"))) ? 0: 1).setExpanded(true);
				Display.getCurrent().asyncExec(new Runnable() {
					public void run() {
						composite.layout();
					}
				});
			}

			public void itemExpanded(ExpandEvent e) {
				dosBooterExpandBar.getItem((((ExpandItem)e.item).getText().equals(text_.get("dialog.template.dos"))) ? 0: 1).setExpanded(false);
				Display.getCurrent().asyncExec(new Runnable() {
					public void run() {
						composite.layout();
					}
				});
			}
		});

		mountingpointsList_.addMouseListener(new MouseAdapter() {
			public void mouseDoubleClick(MouseEvent event) {
				if (mountingpointsList_.getSelectionIndex() == -1) {
					doAddMount(shell_, booterExpandItem_.getExpanded(), mountingpointsList_, configurable);
				} else {
					doEditMount(shell_, mountingpointsList_, configurable);
				}
			}
		});
		createButton(mountGroup, text_.get("dialog.template.mount.add"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doAddMount(shell_, booterExpandItem_.getExpanded(), mountingpointsList_, configurable);
			}
		});
		createButton(mountGroup, text_.get("dialog.template.mount.edit"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doEditMount(shell_, mountingpointsList_, configurable);
			}
		});
		createButton(mountGroup, text_.get("dialog.template.mount.remove"), new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				doRemoveMount(configurable);
			}
		});

		if (multiEdit)
			Arrays.asList(mountGroup.getChildren()).forEach(x -> x.setEnabled(false));
	}

	public static void doAddMount(Shell shell, boolean floppy, org.eclipse.swt.widgets.List mountingpoints, TemplateProfileBase configurable) {
		char drive;
		try {
			drive = DriveLetterHelper.getFirstAvailable(floppy, configurable.getNettoMountedDrives());
		} catch (DrivelettersExhaustedException e) {
			// nothing we can do, just take a sensible default
			drive = 'C';
		}
		EditMountDialog addMountDialog = new EditMountDialog(shell, null, drive);
		String mount = addMountDialog.open();
		if (mount != null) {
			configurable.addMount(mount);
			mountingpoints.setItems(configurable.getMountStringsForUI());
			mountingpoints.select(mountingpoints.getItemCount() - 1);
		}
	}

	public static void doEditMount(Shell shell, org.eclipse.swt.widgets.List mountingpoints, TemplateProfileBase configurable) {
		int mounts = mountingpoints.getItemCount();
		int sel = mountingpoints.getSelectionIndex();
		if (sel == -1 && mounts == 1) {
			sel = 0;
			mountingpoints.select(sel);
		}
		if (sel != -1) {
			if (!configurable.getMountingPointsForUI().get(sel).isUnmounted()) {
				EditMountDialog editMountDialog = new EditMountDialog(shell, mountingpoints.getItem(sel), 'C');
				String mount = editMountDialog.open();
				if (mount != null) {
					configurable.editMountBasedOnIndexUI(sel, mount);
					mountingpoints.setItems(configurable.getMountStringsForUI());
					if (mountingpoints.getItemCount() == mounts) {
						mountingpoints.select(sel);
					} else {
						mountingpoints.select(mountingpoints.getItemCount() - 1);
					}
				}
			}
		}
	}

	protected void doRemoveMount(TemplateProfileBase configurable) {
		doRemoveMount(mountingpointsList_, configurable);
	}

	public static void doRemoveMount(org.eclipse.swt.widgets.List mountingpoints, TemplateProfileBase configurable) {
		int mounts = mountingpoints.getItemCount();
		int sel = mountingpoints.getSelectionIndex();
		if (sel == -1 && mounts == 1) {
			sel = 0;
			mountingpoints.select(sel);
		}
		if (sel != -1) {
			configurable.removeMountBasedOnIndexUI(sel);
			mountingpoints.setItems(configurable.getMountStringsForUI());
			if (mountingpoints.getItemCount() == mounts) {
				mountingpoints.select(sel);
			} else {
				if (mountingpoints.getItemCount() > 0) {
					mountingpoints.select(mountingpoints.getItemCount() - 1);
				}
			}
		}
	}

	protected Text createLabelAndTextDaControl(Composite composite, String labelText, String section, String item) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite);
		daControls_.add(new DaControl(label, text, section, item));
		return text;
	}

	protected Text createLabelAndTextDaControl(Composite composite, String labelText, String section, String itemOld, String itemNew) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite);
		daControls_.add(new DaControl(label, text, section, itemOld, itemNew));
		return text;
	}

	protected Text createLabelAndTooltipTextDaControl(Composite composite, String labelText, String tooltip, String section, String item) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, SWT.BORDER, new GridData(SWT.FILL, SWT.CENTER, true, false), null, tooltip);
		daControls_.add(new DaControl(label, text, section, item));
		return text;
	}

	protected Text createLabelAndTooltipTextDaControl(Composite composite, String labelText, String tooltip, String section, String itemOld, String itemNew) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, SWT.BORDER, null, null, tooltip);
		daControls_.add(new DaControl(label, text, section, itemOld, itemNew));
		return text;
	}

	protected Text createLabelAndTextAutoexecControl(Composite composite, String labelText, Function<Autoexec, String> getMethod, BiConsumer<Autoexec, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite);
		autoexecControls_.add(new MetaControl(label, text).connectAutoexec(getMethod, updateMethod));
		return text;
	}

	protected Text createLabelAndTextAutoexecControl(Composite composite, String labelText, int horizontalSpan, Function<Autoexec, String> getMethod, BiConsumer<Autoexec, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Text text = createText(composite, horizontalSpan);
		autoexecControls_.add(new MetaControl(label, text).connectAutoexec(getMethod, updateMethod));
		return text;
	}

	protected Button createLabelAndCheckButtonDaControl(Composite composite, String labeltext, String section, String item) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, false);
		daControls_.add(new DaControl(label, checkButton, section, item));
		return checkButton;
	}

	protected Button createLabelAndCheckButtonDaControl(Composite composite, String labeltext, String section, String itemOld, String itemNew) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, false);
		daControls_.add(new DaControl(label, checkButton, section, itemOld, itemNew));
		return checkButton;
	}

	protected Button createLabelAndCheckButtonDaControl(Composite composite, String labeltext, String section, String item, boolean isOnOff) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, false);
		daControls_.add(new DaControl(label, checkButton, section, item, isOnOff));
		return checkButton;
	}

	protected Button createLabelAndTooltipCheckButtonDaControl(Composite composite, String labeltext, String tooltip, String section, String item) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, tooltip);
		daControls_.add(new DaControl(label, checkButton, section, item));
		return checkButton;
	}

	protected Button createLabelAndTooltipCheckButtonDaControl(Composite composite, String labeltext, String tooltip, String section, String itemOld, String itemNew) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, tooltip);
		daControls_.add(new DaControl(label, checkButton, section, itemOld, itemNew));
		return checkButton;
	}

	protected Button createLabelAndCheckButtonAutoexecControl(Composite composite, String labeltext, Function<Autoexec, String> getMethod, BiConsumer<Autoexec, String> updateMethod) {
		Label label = createLabel(composite, labeltext);
		Button checkButton = createCheckButton(composite, false);
		autoexecControls_.add(new MetaControl(label, checkButton).connectAutoexec(getMethod, updateMethod));
		return checkButton;
	}

	protected Combo createLabelAndComboDaControl(Composite composite, String labelText, int visibleItemCount, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, null, visibleItemCount);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Combo createLabelAndComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, items, visibleItemCount);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Combo createLabelAndComboAutoexecControl(Composite composite, String labelText, String[] items, int visibleItemCount, Function<Autoexec, String> getMethod,
			BiConsumer<Autoexec, String> updateMethod) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, items, visibleItemCount);
		autoexecControls_.add(new MetaControl(label, combo).connectAutoexec(getMethod, updateMethod));
		return combo;
	}

	protected Combo createLabelAndTooltipComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String tooltip, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, items, visibleItemCount, tooltip);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Combo createLabelAndTooltipComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String tooltip, String section, String itemOld, String itemNew) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, items, visibleItemCount, tooltip);
		daControls_.add(new DaControl(label, combo, section, itemOld, itemNew));
		return combo;
	}

	protected Combo createLabelAndTooltipComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String tooltip, String sectionOld, String itemOld,
			String sectionNew, String itemNew) {
		Label label = createLabel(composite, labelText);
		Combo combo = createCombo(composite, items, visibleItemCount, tooltip);
		daControls_.add(new DaControl(label, combo, sectionOld, itemOld, sectionNew, itemNew));
		return combo;
	}

	protected Combo createLabelAndEditableComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createEditableCombo(composite, items, visibleItemCount);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Combo createLabelAndWideEditableComboDaControl(Composite composite, String labelText, String[] items, int visibleItemCount, String tooltip, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createWideEditableCombo(composite, items, visibleItemCount, tooltip);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Combo createLabelAndEditableComboDaControl(Composite composite, String labelText, String tooltip, String[] items, int visibleItemCount, String section, String item) {
		Label label = createLabel(composite, labelText);
		Combo combo = createEditableCombo(composite, items, visibleItemCount, tooltip);
		daControls_.add(new DaControl(label, combo, section, item));
		return combo;
	}

	protected Spinner createLabelAndSpinnerDaControl(Composite composite, String labelText, int min, int max, String section, String item) {
		Label label = createLabel(composite, labelText);
		Spinner spinner = createSpinner(composite, min, max);
		daControls_.add(new DaControl(label, spinner, section, item));
		return spinner;
	}

	protected TabFolder createSubTabs(String titleKey, String key1, int numColumns1, String key2, int numColumns2) {
		Composite compositeHoldingSubTabs = createTabWithComposite(text_.get(titleKey), new FillLayout());
		TabFolder subTabFolder = new TabFolder(compositeHoldingSubTabs, SWT.NONE);
		createTabWithComposite(subTabFolder, text_.get(key1), numColumns1);
		createTabWithComposite(subTabFolder, text_.get(key2), numColumns2);
		return subTabFolder;
	}

	protected TabFolder createSubTabs(String titleKey, int numColumns1, int numColumns2) {
		return createSubTabs(titleKey, "dialog.template.tab.releaseoptions", numColumns1, "dialog.template.tab.experimentaloptions", numColumns2);
	}
}
