#include "OptEdit.h"

#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "WimpLib:Choices.h"
#include "WimpLib:Desktop.h"
#include "WimpLib:Display.h"
#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "FileMenu.h"
#include "WimpLib:mem.h"
#include "WimpLib:Message.h"
#include "Setup.h"
#include "WimpLib:Slider.h"
#include "WimpLib:Task.h"
#include "WimpLib:Template.h"
#include "WimpLib:Utils.h"
#include "WimpLib:Window.h"

#include "cdswi.h"
#include "DigitalCD.h"
#include "Player.h"
#include "PlayList.h"
#include "SDswi.h"
#include "TreeList.h"
#include "UserEvents.h"

struct COptEdit
{
	HWind		m_wnd;
	HWind		m_wndSkins;
	HWind		m_wndPlayers;
	HWind		m_wndCDDB;
	HWind		m_wndCDFS;
	HWind		m_wndFiles;
	HWind		m_wndFileTypes;
	HWind		m_wndDrivers;
	HWind		m_wndAMPlayer;
	HWind		m_wndDiskSample;
	HWind		m_wndPlayIt;
	HWind		m_wndTimPlayer;
	HWind		m_wndMixing;
	HWind		m_wndStereo;
	HWind		m_wndEqualizer;
	HWind		m_wndIconbar;
	HWind		m_wndPlugIns;
	HWind		m_wndSamplers;
	HWind		m_wndInternet;

	HMenu		m_MenuSkin;
	HMenu		m_MenuTmp;
	HMenu		m_MenuLoadAction;
	HMenu		m_MenuPlayItFormat;
	HMenu		m_MenuPlayItFreq;
	HMenu		m_MenuPlayItChannels;
	HMenu		m_MenuSpTarget;
	HMenu		m_MenuFreqMultiply;
	HMenu		m_MenuDMASize;
	HMenu		m_MenuCDPlayMode;

	TreeList*	m_pSections;
	WListCore	m_Types;
	bool		m_bModified;
	COptions	m_Options;
	HWind		m_wndPane;
	int		m_Freqs;
	int		m_Freq[256];
	EqualizerParams	m_EqualizerStyle;
	CSpriteArea*	m_pPreviewSprites;
	int		m_CurrentSampler;
	int		m_CurrentCDDrive;
	int		m_CurrentDesktopPlugIn;
};

static COptEdit* App_pOptEdit = NULL;

#define Icon_Set				0
#define Icon_Cancel				1
#define Icon_Save				2
#define Icon_SectionsBorder		3

#define Icon_SkinName			2
#define Icon_SkinPopup			3
#define Icon_SkinDown			5
#define Icon_SkinUp				6
#define Icon_SkinHelp			7
#define Icon_SinglePanelMode    8
#define Icon_RemoveAsNeverPlay  9

#define Icon_AutoStartPlaylist	2
#define Icon_AutoStartNewCDs	3
#define Icon_IntroScanTime		5
#define Icon_IntroScanDown		6
#define Icon_IntroScanUp		7
#define Icon_StopPlayingOnError 9
#define Icon_ParentalLock		10
#define Icon_ReShuffleOnLoop	11
#define Icon_RememberPosition	12
#define Icon_GaplessPlayback    13

#define Icon_Frequency			3
#define Icon_FrequencyPopup		5
#define Icon_Interpol			6
#define Icon_VolumeRamping		7
#define Icon_DMASize            9
#define Icon_DMASizePopup       11
#define Icon_FreqMultiply       12
#define Icon_FreqMultiplyPopup  13
#define Icon_IgnoreSystemVolume 14

#define Icon_Balance			4
#define Icon_Balance2			5
#define Icon_BalanceLeft		7
#define Icon_BalanceCenter		9
#define Icon_BalanceRight		11
#define Icon_Separation			14
#define Icon_Separation2		15
#define Icon_SeparationM400		17
#define Icon_Separation0		21
#define Icon_Separation100		28
#define Icon_Separation400		19

#define Icon_Equalizer			1
#define Icon_BandSlider			2
#define Icon_BandMark			22
#define Icon_0dB				47
#define Icon_EqualizerStyle		51
#define Icon_EqualizerMenu		52
#define Icon_EqualizerDelete	53
#define Icon_EqualizerSave		54

#define Icon_SelectName			4
#define Icon_SelectPopup		5
#define Icon_AdjustName			13
#define Icon_AdjustPopup		14

#define Icon_DesktopPlugIn			2
#define Icon_DesktopPlugInName		3
#define Icon_DesktopPlugInEnable	4
#define Icon_DesktopPlugInInfo		5
#define Icon_DesktopPlugInSetup		6
#define Icon_PlugInAutoStart		7
#define Icon_FullPlugIn				8
#define Icon_FullPlugInName			9
#define Icon_FullPlugInPopup		10
#define Icon_FullPlugInInfo			11
#define Icon_FullPlugInSetup		12
#define Icon_RescaleWithTrackVolume	13
#define Icon_DesktopPlugInDown		14
#define Icon_DesktopPlugInUp		15

#define Icon_SpTarget				3
#define Icon_SpTargetMenu			4
#define Icon_SpDriver				6
#define Icon_SpDriverMenu			7
#define Icon_SpSourceLeft			9
#define Icon_SpSourceLeftMenu		10
#define Icon_SpSourceRight			12
#define Icon_SpSourceRightMenu		13
#define Icon_SpSampleType			15
#define Icon_SpSampleTypeMenu		16
#define Icon_SpSampleRate			18
#define Icon_SpSampleRateMenu		19
#define Icon_SpForceDriverConfig	22

#define Icon_FileShowNameAsTitle	2
#define Icon_FileUnloadModule		3
#define Icon_FileDoubleClickInfo	6
#define Icon_FileDoubleClickMenu	7
#define Icon_FileDropOnIconBarInfo	9
#define Icon_FileDropOnIconBarMenu	10
#define Icon_FileDropOnPlayerInfo	12
#define Icon_FileDropOnPlayerMenu	13
#define Icon_FileAutoOpenPlaylist	14
//#define Icon_FileAutoEnableProgram	15

#define Icon_Types					4
#define Icon_TypesHelp				5

#define Icon_AMPlayerBuffer			3
#define Icon_AMPlayerBufferDown		4
#define Icon_AMPlayerBufferUp		5

#define Icon_CDDB				1
#define Icon_CDDBAllowRemote	2
#define Icon_CDDBShowQuery		3
#define Icon_CDDBPath			5

#define Icon_CDDrive			3
#define Icon_CDDriveDown		4
#define Icon_CDDriveUp			5
#define Icon_CD150FramesBug		6
#define Icon_CDPlayMode			7
#define Icon_CDPlayModeMenu     8
#define Icon_CDNudgeSize		10
#define Icon_CDNudgeSizeDown	11
#define Icon_CDNudgeSizeUp		12
#define Icon_CDPollingDelay		16
#define Icon_CDPollingDelayDown	17
#define Icon_CDPollingDelayUp	18
#define Icon_CDHideDriveNumber  20

#define Icon_DiskInputBuffer		3
#define Icon_DiskInputBufferDown	4
#define Icon_DiskInputBufferUp		5
#define Icon_DiskOutputBuffer		8
#define Icon_DiskOutputBufferDown	9
#define Icon_DiskOutputBufferUp		10
#define Icon_DiskFreeVolume			12
#define Icon_DiskEnableInterrupts	13

#define Icon_PlayItBuffer			3
#define Icon_PlayItBufferDown		4
#define Icon_PlayItBufferUp			5
#define Icon_PlayItInterpretUnknown	7
#define Icon_PlayItFormat			9
#define Icon_PlayItFormatMenu		10
#define Icon_PlayItFreq				12
#define Icon_PlayItFreqMenu			13
#define Icon_PlayItChannels			15
#define Icon_PlayItChannelsMenu		16

#define Icon_TimFreeVolume      	2
#define Icon_TimEnableInterrupts	3
#define Icon_TimPolyphony           5
#define Icon_TimPolyphonyDown       6
#define Icon_TimPolyphonyUp         7


#define Icon_NetProxyUrl			3
#define Icon_NetIcyMetadata			4
#define Icon_NetAudiocastMetadata	5
#define Icon_NetUseProxy			6
#define Icon_NetAudiocastPort		8
#define Icon_NetReserve				10
#define Icon_NetReserveDown			11
#define Icon_NetReserveUp			12

static const char Section_Choices[]			= "Choices";

static const char Section_Startup[]			= "Startup";
static const char Var_AutoStartPlaylist[]	= "RestartPlay";
static const char Var_Skin[]				= "Skin";

static const char Section_Players[]				= "Players";
static const char Var_IntroScanTime[]			= "IntroScanTime";
static const char Var_PlayerMaxVisibleTracks[]	= "MaxVisibleTracks";
static const char Var_StopPlayingOnError[]		= "StopPlayingOnError";
static const char Var_FixTrackVolume[]			= "FixTrackVolume";
static const char Var_AutoStartNewCDs[]			= "AutoStartNewCDs";
static const char Var_ParentalLock[]			= "ParentalLock";
static const char Var_ReShuffleOnLoop[]			= "ReShuffleOnLoop";
static const char Var_SinglePlayerMode[]		= "SinglePlayerMode";
static const char Var_RememberPosition[]		= "RememberPosition";
static const char Var_RemoveAsNeverPlay[]		= "RemoveAsNeverPlay";
static const char Var_IgnoreSystemVolume[]		= "IgnoreSystemVolume";
static const char Var_GaplessPlayback[]			= "GaplessPlayback";
static const char Var_FadingTime[]				= "FadingTime";

static const char Section_PlayLists[]	= "PlayLists";
static const char Var_MaxPoints[]		= "MaxPoints";

static const char Section_Music_Files[]		= "Music_Files";
static const char Var_ShowFileAsTitle[]		= "ShowFileAsTitle";
static const char Var_UnloadModules[]		= "UnloadModules";
static const char Var_Frequency[]			= "Frequency";
static const char Var_FreqMultiply[]		= "FreqMultiply";
static const char Var_DMASize[]				= "DMASize";
static const char Var_Balance[]				= "Balance";
static const char Var_StereoSeparation[]	= "StereoSeparation";
static const char Var_Interpol[]			= "Interpol";
static const char Var_VolumeRamping[]		= "VolumeRamping";
static const char Var_DoubleClickAction[]	= "DoubleClickAction";
static const char Var_DropOnIconBarAction[]	= "DropOnIconBarAction";
static const char Var_DropOnPlayerAction[]	= "DropOnPlayerAction";
static const char Var_AutoOpenPlaylist[]	= "AutoOpenPlaylist";
static const char Var_AutoEnableProgram[]	= "AutoEnableProgram";

static const char Section_Equalizer[]	= "Equalizer";
static const char Var_Active[]			= "Active";
static const char Var_Name[]			= "Name";
static const char Var_Gain[]			= "Gain%d";
//static const char Var_Freq[]			= "Freq%d";

static const char Section_Iconbar[]		= "Iconbar";
static const char Var_Select[]			= "Select";
static const char Var_Adjust[]			= "Adjust";

static const char Section_PlugIns[]				= "PlugIns";
static const char Var_Mode[]					= "Mode";
static const char Var_Desktop[]					= "Desktop";
static const char Var_FullScreen[]				= "FullScreen";
static const char Var_AutoStart[]				= "AutoStart";
static const char Var_RescaleWithTrackVolume[]	= "RescaleWithTrackVolume";

static const char Section_Sampling[]		= "Sampling";
static const char Var_ForceDriverConfig[]	= "ForceDriverConfig";

static const char Section_Sampler[]			= "Samplers%d";
static const char Var_Driver[]				= "Driver";
static const char Var_SourceLeft[]			= "SourceLeft";
static const char Var_SourceRight[]			= "SourceRight";
static const char Var_SampleType[]			= "SampleType";
static const char Var_SampleRate[]			= "SampleRate";

static const char Var_None[]			= "None";
static const char Var_SoundDMA[]		= "SoundDMA";

static const char Dir_PlugIn_Desktop[] = "DigitalCDRes:Plugins.Desktop";
static const char Dir_PlugIn_FullScreen[] = "DigitalCDRes:Plugins.FullScreen";
static const char Dir_Skin[] = "DigitalCDRes:Skins";
static const char File_Help[] = "!Help";
static const char File_Setup[] = "!SvrSetup";
static const char File_ReadMe[] = "!ReadMe";
static const char File_Preview[] = "Preview";

static const char Section_AMPlayer[]	= "AMPlayer";
static const char Var_BufSize[]			= "AntiShockBufferSize";

static const char Section_DiskSample[]	= "DiskSample";
static const char Var_InBufSize[]		= "InputBufferSize";
static const char Var_OutBufSize[]		= "OutputBufferSize";

static const char Section_CDs[]				= "CDs";
static const char Var_150FramesBug[]		= "150FramesBug";
static const char Var_PlayTrackByTrack[]	= "PlayTrackByTrack";
static const char Var_PollingDelay[]		= "PollingDelay";
static const char Var_HideDriveNumber[]		= "HideDriveNumber";

static const char Section_CDDB[]		= "CDDB";
static const char Var_SendQuery[]		= "SendQuery";
static const char Var_AllowRemote[]		= "AllowRemote";
static const char Var_ShowQuery[]		= "ShowQuery";
static const char Var_Path[]			= "Path";

static const char Section_PlayIt[]			= "PlayIt";
static const char Var_BufferSize[]			= "BufferSize";
static const char Var_InterpretUnknown[]	= "InterpretUnkown";
static const char Var_Format[]				= "Format";
static const char Var_Channels[]			= "Channels";

static const char Section_TimPlayer[]		= "TimPlayer";
static const char Var_FreeVolume[]			= "FreeVolume";
static const char Var_EnableInterrupts[]	= "EnableInterrupts";
static const char Var_Polyphony[]			= "Polyphony";

static const char Section_Internet[]		= "Internet";
static const char Var_UseProxy[]			= "UseProxy";
static const char Var_ProxyUrl[]			= "ProxyUrl";
static const char Var_IcyMetadata[]			= "IcyMetadata";
static const char Var_AudiocastMetadata[]	= "AudiocastMetadata";
static const char Var_AudiocastPort[]		= "AudiocastPort";
static const char Var_Reserve[]				= "Reserve";
static const char Var_Band[]				= "Band";

#define MAX_DMASIZE 3
static const int DMASizes[MAX_DMASIZE + 1] = {0, 208, 512, 1024};

typedef struct
{
	int		Type;
	const void*	Data;
} CHint;

static EListenerAction OptEdit_EventHandler(void* handle, const Event* e);
static EListenerAction OptEdit_F1Handler(void* handle, const Event* e);
static EListenerAction OptOp_EventHandler(void* handle, const Event* e);

static EListenerAction OptSkins_EventHandler(void* handle, const Event* e);
static EListenerAction OptPlayers_EventHandler(void* handle, const Event* e);
static EListenerAction OptCDDB_EventHandler(void* handle, const Event* e);
static EListenerAction OptCDFS_EventHandler(void* handle, const Event* e);
static EListenerAction OptFiles_EventHandler(void* handle, const Event* e);
static EListenerAction OptFileTypes_EventHandler(void* handle, const Event* e);
static EListenerAction OptAMPlayer_EventHandler(void* handle, const Event* e);
static EListenerAction OptDiskSample_EventHandler(void* handle, const Event* e);
static EListenerAction OptPlayIt_EventHandler(void* handle, const Event* e);
static EListenerAction OptTimPlayer_EventHandler(void* handle, const Event* e);
static EListenerAction OptMixing_EventHandler(void* handle, const Event* e);
static EListenerAction OptStereo_EventHandler(void* handle, const Event* e);
static EListenerAction OptEqualizer_EventHandler(void* handle, const Event* e);
static EListenerAction OptIconbar_EventHandler(void* handle, const Event* e);
static EListenerAction OptPlugIns_EventHandler(void* handle, const Event* e);
static EListenerAction OptSamplers_EventHandler(void* handle, const Event* e);
static EListenerAction OptInternet_EventHandler(void* handle, const Event* e);

static void Balance_Set(COptEdit* This, int value);
static void Separation_Set(COptEdit* This, int value);
static void OptEdit_ReadEqualizerStyle(COptEdit* This, const char* style);
static void OptEdit_SelectSkin(COptEdit* This, const char* pname, int offset);
static void OptEdit_BuildSkinMenu(COptEdit* This);

static bool Filer_Find(const char* prefix, const char* path, const char* name)
{
	const char* pstr;

	pstr = SPrintf("%s.%s.%s", prefix, path, name);
	return (File_GetFileType(pstr) != -2);
}

static bool Filer_Run(const char* prefix, const char* path, const char* name)
{
	const char* pstr;
	HTask dummy;

	pstr = SPrintf("%s.%s.%s", prefix, path, name);
	if (File_GetFileType(pstr) != -2)
	{
		pstr = SPrintf("Filer_Run %s.%s.%s", prefix, path, name);
		StartTask(&dummy, pstr);
		return true;
	}

	return false;
}

const COptions* Options(void)
{
	return &DigitalCD.Options;
}

void throw_Options_Options(COptions* This)
{
	int i;

	This->Player.pSkinName = mem_allocstring("Default");

	This->Player.bAutoStartPlaylist = false;
	This->Player.bRememberPosition = true;
	This->Player.bAutoStartNewCDs = false;
	This->Player.IntroScanTime = 1500;
	This->Player.bStopPlayingOnError = true;
	This->Player.bParentalLock = false;
	This->Player.bFixTrackVolume = false;
	This->Player.bReShuffleOnLoop = false;
	This->Player.bSinglePanelMode = false;
	This->Player.bRemoveAsNeverPlay = false;
	This->Player.bIgnoreSystemVolume = false;
	This->Player.MaxVisibleTracks = 10;
	This->Player.bGaplessPlayback = false;
	This->Player.FadingTime = 1500;

	This->Music_Files.bShowFileAsTitle = false;
	This->Music_Files.bUnloadModules = true;
	This->Music_Files.Frequency = 44100;
	This->Music_Files.FreqMultiply = 0;
	This->Music_Files.DMASize = 1024;
	This->Music_Files.Balance = 128;
	This->Music_Files.StereoSeparation = 256;
	This->Music_Files.bInterpol = true;
	This->Music_Files.bVolumeRamping = true;
	This->Music_Files.DoubleClickAction = ELoadAction_QueueAndPlay;
	This->Music_Files.DropOnIconBarAction = ELoadAction_QueueAndPlay;
	This->Music_Files.DropOnPlayerAction = ELoadAction_QueueAndPlay;
	This->Music_Files.bAutoOpenPlaylist = true;
	This->Music_Files.bAutoEnableProgram = false;

	This->Drivers.AMPlayer.BufferSize = 16;

	This->Drivers.CDDB.bSendQuery = false;
	This->Drivers.CDDB.bAllowRemote = true;
	This->Drivers.CDDB.bShowQuery = false;
	This->Drivers.CDDB.pPath = mem_allocstring(NULL);

	for (i = 0; i < 8; i++)
	{
		This->Drivers.CDFS.PlayMode[i] = ECDMode_ReadAudioSWI;
		This->Drivers.CDFS.NudgeSize[i] = 0;
	}
	This->Drivers.CDFS.PollingDelay = 5;
	This->Drivers.CDFS.bHideDriveNumber = false;

	This->Drivers.DiskSample.InputBufferSize = 128;
	This->Drivers.DiskSample.OutputBufferSize = 1024;
	This->Drivers.DiskSample.bFreeVolume = true;
	This->Drivers.DiskSample.bEnableInterrupts = false;

	This->Drivers.PlayIt.BufferSize = 64;
	This->Drivers.PlayIt.bInterpretUnknown = false;
	This->Drivers.PlayIt.Format = 0;
	This->Drivers.PlayIt.Frequency = 44100;
	This->Drivers.PlayIt.Channels = 1;

	This->Drivers.TimPlayer.bFreeVolume = true;
	This->Drivers.TimPlayer.bEnableInterrupts = false;
	This->Drivers.TimPlayer.Polyphony = 64;

	This->Equalizer.bActive = false;
	This->Equalizer.Name = mem_allocstring(NULL);

	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		This->Equalizer.Params.Gain[i] = 1<<16;
	This->Equalizer.Params.Freq[0] = 300;
	This->Equalizer.Params.Freq[1] = 750;
	This->Equalizer.Params.Freq[2] = 2500;
	This->Equalizer.Params.Freq[3] = 6000;
	This->Equalizer.Params.Freq[4] = 1000000;

	This->Iconbar.Select[0] = 0;
	This->Iconbar.Select[1] = Cmd_OpenPlayList;
	This->Iconbar.Select[2] = Cmd_OpenChoices;
	This->Iconbar.Adjust[0] = 1;
	This->Iconbar.Adjust[1] = Cmd_OpenCDList;
	This->Iconbar.Adjust[2] = Cmd_OpenChoices;

	This->PlugIns.bAutoStart = false;
	This->PlugIns.bRescaleWithTrackVolume = false;
	This->PlugIns.bDesktop = true;
	This->PlugIns.pDesktop = mem_allocstring(NULL);
	This->PlugIns.pFullScreen = mem_allocstring(NULL);

	This->Samplers.Count = DigitalCD.Hardware.NrOfCDDrives + 3;
	This->Samplers.bForceDriverConfig = false;

	This->Samplers.Params[0].Target = mem_allocstring(Msg_Lookup("SpTarget0"));
	This->Samplers.Params[0].Driver = mem_allocstring(Var_SoundDMA);
	This->Samplers.Params[0].SourceLeft = mem_allocstring(NULL);
	This->Samplers.Params[0].SourceRight = mem_allocstring(NULL);
	This->Samplers.Params[0].SampleType = mem_allocstring(NULL);
	This->Samplers.Params[0].SampleRate = 0;

	This->Samplers.Params[1].Target = mem_allocstring(Msg_Lookup("SpTarget1"));
	This->Samplers.Params[1].Driver = mem_allocstring(Var_SoundDMA);
	This->Samplers.Params[1].SourceLeft = mem_allocstring(NULL);
	This->Samplers.Params[1].SourceRight = mem_allocstring(NULL);
	This->Samplers.Params[1].SampleType = mem_allocstring(NULL);
	This->Samplers.Params[1].SampleRate = 0;

	This->Samplers.Params[2].Target = mem_allocstring(Msg_Lookup("SpTarget2"));
	This->Samplers.Params[2].Driver = mem_allocstring(Var_SoundDMA);
	This->Samplers.Params[2].SourceLeft = mem_allocstring(NULL);
	This->Samplers.Params[2].SourceRight = mem_allocstring(NULL);
	This->Samplers.Params[2].SampleType = mem_allocstring(NULL);
	This->Samplers.Params[2].SampleRate = 0;

	for (i = 3; i < This->Samplers.Count; i++)
	{
		This->Samplers.Params[i].Target = mem_allocstring(SPrintf(Msg_Lookup("SpTargetN"), i - 3));
		This->Samplers.Params[i].Driver = mem_allocstring(Var_SoundDMA);
		This->Samplers.Params[i].SourceLeft = mem_allocstring(NULL);
		This->Samplers.Params[i].SourceRight = mem_allocstring(NULL);
		This->Samplers.Params[i].SampleType = mem_allocstring(NULL);
		This->Samplers.Params[i].SampleRate = 44100;
	}

	This->PlayLists.MaxPoints = 100;

	This->Internet.bUseProxy = false;
	This->Internet.ProxyUrl = mem_allocstring(NULL);
	This->Internet.bIcyMetadata = true;
	This->Internet.bAudiocastMetadata = true;
	This->Internet.AudiocastPort = 7777;
	This->Internet.Reserve = 1;
	This->Internet.BandRates[0] = 1;
	This->Internet.BandRates[1] = 64;
	This->Internet.BandRates[2] = 128;
	This->Internet.BandRates[3] = 256;
}

void throw_Options_CopyOptions(COptions* This, const COptions* pcopy)
{
	int i;

	mem_free(This->Player.pSkinName);
	mem_free(This->Drivers.CDDB.pPath);
	mem_free(This->Equalizer.Name);
	mem_free(This->PlugIns.pDesktop);
	mem_free(This->PlugIns.pFullScreen);

	for (i = 0; i < This->Samplers.Count; i++)
	{
		mem_free(This->Samplers.Params[i].Target);
		mem_free(This->Samplers.Params[i].Driver);
		mem_free(This->Samplers.Params[i].SourceLeft);
		mem_free(This->Samplers.Params[i].SourceRight);
		mem_free(This->Samplers.Params[i].SampleType);
	}
	mem_free(This->Internet.ProxyUrl);

	*This = *pcopy;

	This->Player.pSkinName = throw_mem_allocstring(pcopy->Player.pSkinName);
	This->Drivers.CDDB.pPath = throw_mem_allocstring(pcopy->Drivers.CDDB.pPath);
	This->Equalizer.Name = throw_mem_allocstring(pcopy->Equalizer.Name);
	This->PlugIns.pDesktop = throw_mem_allocstring(pcopy->PlugIns.pDesktop);
	This->PlugIns.pFullScreen = throw_mem_allocstring(pcopy->PlugIns.pFullScreen);

	for (i = 0; i < This->Samplers.Count; i++)
	{
		This->Samplers.Params[i].Target = throw_mem_allocstring(pcopy->Samplers.Params[i].Target);
		This->Samplers.Params[i].Driver = throw_mem_allocstring(pcopy->Samplers.Params[i].Driver);
		This->Samplers.Params[i].SourceLeft = throw_mem_allocstring(pcopy->Samplers.Params[i].SourceLeft);
		This->Samplers.Params[i].SourceRight = throw_mem_allocstring(pcopy->Samplers.Params[i].SourceRight);
		This->Samplers.Params[i].SampleType = throw_mem_allocstring(pcopy->Samplers.Params[i].SampleType);
	}
	This->Internet.ProxyUrl = throw_mem_allocstring(pcopy->Internet.ProxyUrl);
}

void Options_NotOptions(COptions* This)
{
	int i;

	mem_free(This->Player.pSkinName);
	mem_free(This->Drivers.CDDB.pPath);
	mem_free(This->Equalizer.Name);
	mem_free(This->PlugIns.pDesktop);
	mem_free(This->PlugIns.pFullScreen);

	for (i = 0; i < This->Samplers.Count; i++)
	{
		mem_free(This->Samplers.Params[i].Target);
		mem_free(This->Samplers.Params[i].Driver);
		mem_free(This->Samplers.Params[i].SourceLeft);
		mem_free(This->Samplers.Params[i].SourceRight);
		mem_free(This->Samplers.Params[i].SampleType);
	}
	mem_free(This->Internet.ProxyUrl);

	memset(This, 0, sizeof(*This));
}

void Options_Load(COptions* This)
{
	int i, mode;

	Choices_Update(Section_Startup, Var_Skin, &This->Player.pSkinName);

	Choices_UpdateBool(Section_Startup, Var_AutoStartPlaylist, &This->Player.bAutoStartPlaylist);
	Choices_UpdateBool(Section_Players, Var_RememberPosition, &This->Player.bRememberPosition);
	Choices_UpdateBool(Section_Players, Var_AutoStartNewCDs, &This->Player.bAutoStartNewCDs);
	Choices_UpdateInt (Section_Players, Var_IntroScanTime, &This->Player.IntroScanTime, 500, 6000);
	Choices_UpdateBool(Section_Players, Var_StopPlayingOnError, &This->Player.bStopPlayingOnError);
	Choices_UpdateBool(Section_Players, Var_ParentalLock, &This->Player.bParentalLock);
	Choices_UpdateBool(Section_Players, Var_FixTrackVolume, &This->Player.bFixTrackVolume);
	Choices_UpdateBool(Section_Players, Var_ReShuffleOnLoop, &This->Player.bReShuffleOnLoop);
	Choices_UpdateBool(Section_Players, Var_SinglePlayerMode, &This->Player.bSinglePanelMode);
	Choices_UpdateBool(Section_Players, Var_RemoveAsNeverPlay, &This->Player.bRemoveAsNeverPlay);
	Choices_UpdateBool(Section_Players, Var_IgnoreSystemVolume, &This->Player.bIgnoreSystemVolume);
	Choices_UpdateInt (Section_Players, Var_PlayerMaxVisibleTracks, &This->Player.MaxVisibleTracks, 10, 99);
	Choices_UpdateBool(Section_Players, Var_GaplessPlayback, &This->Player.bGaplessPlayback);
	Choices_UpdateInt (Section_Players, Var_FadingTime, &This->Player.FadingTime, 500, 3000);

	Choices_UpdateBool(Section_Music_Files, Var_ShowFileAsTitle, &This->Music_Files.bShowFileAsTitle);
	Choices_UpdateBool(Section_Music_Files, Var_UnloadModules, &This->Music_Files.bUnloadModules);
	Choices_UpdateInt (Section_Music_Files, Var_Frequency, &This->Music_Files.Frequency, 5000, 200000);
	Choices_UpdateInt (Section_Music_Files, Var_FreqMultiply, &This->Music_Files.FreqMultiply, 0, 2);
	Choices_UpdateInt (Section_Music_Files, Var_DMASize, (int*) &This->Music_Files.DMASize, 0, 1024);
	for (i = 0; i <= MAX_DMASIZE; i++)
	{
		if (This->Music_Files.DMASize <= DMASizes[i])
		{
			This->Music_Files.DMASize = DMASizes[i];
			break;
		}
	}
	Choices_UpdateInt (Section_Music_Files, Var_Balance, (int*) &This->Music_Files.Balance, 0, 255);
	Choices_UpdateInt (Section_Music_Files, Var_StereoSeparation, &This->Music_Files.StereoSeparation, -1024, 1024);
	Choices_UpdateBool(Section_Music_Files, Var_Interpol, &This->Music_Files.bInterpol);
	Choices_UpdateBool(Section_Music_Files, Var_VolumeRamping, &This->Music_Files.bVolumeRamping);
	This->Music_Files.DoubleClickAction = (ELoadAction) FitInRange(Choices_ReadInt(Section_Music_Files, Var_DoubleClickAction
	                                                               , This->Music_Files.DoubleClickAction), ELoadAction_Ignore, ELoadAction_Max);
	This->Music_Files.DropOnIconBarAction = (ELoadAction) FitInRange(Choices_ReadInt(Section_Music_Files, Var_DropOnIconBarAction
	                                                               , This->Music_Files.DropOnIconBarAction), ELoadAction_Ignore, ELoadAction_Max);
	This->Music_Files.DropOnPlayerAction = (ELoadAction) FitInRange(Choices_ReadInt(Section_Music_Files, Var_DropOnPlayerAction
	                                                               , This->Music_Files.DropOnPlayerAction), ELoadAction_Ignore, ELoadAction_Max);
	Choices_UpdateBool(Section_Music_Files, Var_AutoOpenPlaylist, &This->Music_Files.bAutoOpenPlaylist);
//	Choices_UpdateBool(Section_Music_Files, Var_AutoEnableProgram, &This->Music_Files.bAutoEnableProgram);

	Choices_UpdateInt(Section_AMPlayer, Var_BufSize, &This->Drivers.AMPlayer.BufferSize, 4, 512);

	Choices_UpdateBool(Section_CDDB, Var_SendQuery, &This->Drivers.CDDB.bSendQuery);
	Choices_UpdateBool(Section_CDDB, Var_AllowRemote, &This->Drivers.CDDB.bAllowRemote);
	Choices_UpdateBool(Section_CDDB, Var_ShowQuery, &This->Drivers.CDDB.bShowQuery);
	Choices_Update    (Section_CDDB, Var_Path, (const char**) &This->Drivers.CDDB.pPath);

	// Read old vars
	mode = 0;
	if (Choices_ReadBool(Section_CDs, Var_150FramesBug, false))
		mode |= ECDMode_150FramesBug;
	if (Choices_ReadBool(Section_CDs, Var_PlayTrackByTrack, false))
		mode |= ECDMode_TrackByTrack;

	for (i = 0; i < 8; i++)
	{
		This->Drivers.CDFS.PlayMode[i] = Choices_ReadInt(Section_CDs, SPrintf("PlayMode%d", i), This->Drivers.CDFS.PlayMode[i]);
		This->Drivers.CDFS.NudgeSize[i] = Choices_ReadInt(Section_CDs, SPrintf("NudgeSize%d", i), This->Drivers.CDFS.NudgeSize[i]);
	}

	Choices_UpdateInt (Section_CDs, Var_PollingDelay, &This->Drivers.CDFS.PollingDelay, 5, 99);
	Choices_UpdateBool(Section_CDs, Var_HideDriveNumber, &This->Drivers.CDFS.bHideDriveNumber);

	Choices_UpdateInt (Section_DiskSample, Var_InBufSize, &This->Drivers.DiskSample.InputBufferSize, 4, 2048);
	Choices_UpdateInt (Section_DiskSample, Var_OutBufSize, &This->Drivers.DiskSample.OutputBufferSize, 64, 4096);
	Choices_UpdateBool(Section_DiskSample, Var_FreeVolume, &This->Drivers.DiskSample.bFreeVolume);
	Choices_UpdateBool(Section_DiskSample, Var_EnableInterrupts, &This->Drivers.DiskSample.bEnableInterrupts);

	Choices_UpdateInt (Section_PlayIt, Var_BufferSize, &This->Drivers.PlayIt.BufferSize, 4, 512);
	Choices_UpdateBool(Section_PlayIt, Var_InterpretUnknown, &This->Drivers.PlayIt.bInterpretUnknown);
	This->Drivers.PlayIt.Format = Choices_ReadInt(Section_PlayIt, Var_Format, This->Drivers.PlayIt.Format);
	Choices_UpdateInt (Section_PlayIt, Var_Frequency, &This->Drivers.PlayIt.Frequency, 5000, 200000);
	Choices_UpdateInt (Section_PlayIt, Var_Channels, &This->Drivers.PlayIt.Channels, 1, 2);

	Choices_UpdateBool(Section_TimPlayer, Var_FreeVolume, &This->Drivers.TimPlayer.bFreeVolume);
	Choices_UpdateBool(Section_TimPlayer, Var_EnableInterrupts, &This->Drivers.TimPlayer.bEnableInterrupts);
	Choices_UpdateInt (Section_TimPlayer, Var_Polyphony, &This->Drivers.TimPlayer.Polyphony, 4, 128);

	Choices_UpdateBool(Section_Equalizer, Var_Active, &This->Equalizer.bActive);
	Choices_Update(Section_Equalizer, Var_Name, &This->Equalizer.Name);
	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		Choices_UpdateInt(Section_Equalizer, SPrintf(Var_Gain, i), &This->Equalizer.Params.Gain[i], 1<<14, 1<< 18);

	// Read old configuration variables
	This->Iconbar.Select[0] = Choices_ReadInt(Section_Iconbar, Var_Select, This->Iconbar.Select[0]);
	This->Iconbar.Adjust[0] = Choices_ReadInt(Section_Iconbar, Var_Adjust, This->Iconbar.Adjust[0]);
	// Read new configuration variables
	for (i = 0; i < 3; i++)
	{
		This->Iconbar.Select[i] = Choices_ReadInt(Section_Iconbar, SPrintf("Select%d", i), This->Iconbar.Select[i]);
		if (This->Iconbar.Select[i] < Cmd_OpenChoices)
			This->Iconbar.Select[i] = FitInRange(This->Iconbar.Select[i], 0, PlayLists_Count() - 1);
		else
			This->Iconbar.Select[i] = FitInRange(This->Iconbar.Select[i], Cmd_OpenChoices, Cmd_OpenCDList);

		This->Iconbar.Adjust[i] = Choices_ReadInt(Section_Iconbar, SPrintf("Adjust%d", i), This->Iconbar.Adjust[i]);
		if (This->Iconbar.Adjust[i] < Cmd_OpenChoices)
			This->Iconbar.Adjust[i] = FitInRange(This->Iconbar.Adjust[i], 0, PlayLists_Count() - 1);
		else
			This->Iconbar.Adjust[i] = FitInRange(This->Iconbar.Adjust[i], Cmd_OpenChoices, Cmd_OpenCDList);
	}

	Choices_UpdateBool(Section_PlugIns, Var_AutoStart, &This->PlugIns.bAutoStart);
	Choices_UpdateBool(Section_PlugIns, Var_RescaleWithTrackVolume, &This->PlugIns.bRescaleWithTrackVolume);
	Choices_UpdateBool(Section_PlugIns, Var_Mode, &This->PlugIns.bDesktop);
	Choices_Update    (Section_PlugIns, Var_Desktop, &This->PlugIns.pDesktop);
	Choices_Update    (Section_PlugIns, Var_FullScreen, &This->PlugIns.pFullScreen);

	Choices_UpdateBool(Section_Sampling, Var_ForceDriverConfig, &This->Samplers.bForceDriverConfig);
	for (i = 0; i < This->Samplers.Count; i++)
	{
		Choices_Update(SPrintf(Section_Sampler, i), Var_Driver, &This->Samplers.Params[i].Driver);
		Choices_Update(SPrintf(Section_Sampler, i), Var_SourceLeft, &This->Samplers.Params[i].SourceLeft);
		Choices_Update(SPrintf(Section_Sampler, i), Var_SourceRight, &This->Samplers.Params[i].SourceRight);
		Choices_Update(SPrintf(Section_Sampler, i), Var_SampleType, &This->Samplers.Params[i].SampleType);
		Choices_UpdateInt(SPrintf(Section_Sampler, i), Var_SampleRate, &This->Samplers.Params[i].SampleRate, 0, 99999);
	}

	Choices_UpdateInt(Section_PlayLists, Var_MaxPoints, &This->PlayLists.MaxPoints, 5, 1000);

	Choices_UpdateBool(Section_Internet, Var_UseProxy, &This->Internet.bUseProxy);
	Choices_Update(Section_Internet, Var_ProxyUrl, &This->Internet.ProxyUrl);
	Choices_UpdateBool(Section_Internet, Var_IcyMetadata, &This->Internet.bIcyMetadata);
	Choices_UpdateBool(Section_Internet, Var_AudiocastMetadata, &This->Internet.bAudiocastMetadata);
	Choices_UpdateInt(Section_Internet, Var_AudiocastPort, &This->Internet.AudiocastPort, 1024, 0x3FFF);
	Choices_UpdateInt(Section_Internet, Var_Reserve, &This->Internet.Reserve, 1, 60);
	for (i = 1; i < 4; i++)
	{
		This->Internet.BandRates[i] = Choices_ReadInt(Section_Internet, SPrintf("%s%d", Var_Band, i), This->Internet.BandRates[i]);
		if (This->Internet.BandRates[i] <= This->Internet.BandRates[i-1])
			This->Internet.BandRates[i] = This->Internet.BandRates[i-1] + 1;
	}
}

void Options_Save(const COptions* This)
{
	int i;

	Choices_Write(Section_Startup, Var_Skin, "%s", This->Player.pSkinName);

	Choices_Write(Section_Startup, Var_AutoStartPlaylist, "%d", This->Player.bAutoStartPlaylist);
	Choices_Write(Section_Players, Var_RememberPosition, "%d", This->Player.bRememberPosition);
	Choices_Write(Section_Players, Var_AutoStartNewCDs, "%d", This->Player.bAutoStartNewCDs);
	Choices_Write(Section_Players, Var_IntroScanTime, "%d", This->Player.IntroScanTime);
	Choices_Write(Section_Players, Var_StopPlayingOnError, "%d", This->Player.bStopPlayingOnError);
	Choices_Write(Section_Players, Var_ParentalLock, "%d", This->Player.bParentalLock);
	Choices_Write(Section_Players, Var_FixTrackVolume, "%d", This->Player.bFixTrackVolume);
	Choices_Write(Section_Players, Var_ReShuffleOnLoop, "%d", This->Player.bReShuffleOnLoop);
	Choices_Write(Section_Players, Var_SinglePlayerMode, "%d", This->Player.bSinglePanelMode);
	Choices_Write(Section_Players, Var_RemoveAsNeverPlay, "%d", This->Player.bRemoveAsNeverPlay);
	Choices_Write(Section_Players, Var_IgnoreSystemVolume, "%d", This->Player.bIgnoreSystemVolume);
	Choices_Write(Section_Players, Var_PlayerMaxVisibleTracks, "%d", This->Player.MaxVisibleTracks);
	Choices_Write(Section_Players, Var_GaplessPlayback, "%d", This->Player.bGaplessPlayback);
	Choices_Write(Section_Players, Var_FadingTime, "%d", This->Player.FadingTime);

	Choices_Write(Section_Music_Files, Var_ShowFileAsTitle, "%d", This->Music_Files.bShowFileAsTitle);
	Choices_Write(Section_Music_Files, Var_UnloadModules, "%d", This->Music_Files.bUnloadModules);
	Choices_Write(Section_Music_Files, Var_Frequency, "%d", This->Music_Files.Frequency);
	Choices_Write(Section_Music_Files, Var_FreqMultiply, "%d", This->Music_Files.FreqMultiply);
	Choices_Write(Section_Music_Files, Var_DMASize, "%d", This->Music_Files.DMASize);
	Choices_Write(Section_Music_Files, Var_Balance, "%d", This->Music_Files.Balance);
	Choices_Write(Section_Music_Files, Var_StereoSeparation, "%d", This->Music_Files.StereoSeparation);
	Choices_Write(Section_Music_Files, Var_Interpol, "%d", This->Music_Files.bInterpol);
	Choices_Write(Section_Music_Files, Var_VolumeRamping, "%d", This->Music_Files.bVolumeRamping);
	Choices_Write(Section_Music_Files, Var_DoubleClickAction, "%d", This->Music_Files.DoubleClickAction);
	Choices_Write(Section_Music_Files, Var_DropOnIconBarAction, "%d", This->Music_Files.DropOnIconBarAction);
	Choices_Write(Section_Music_Files, Var_DropOnPlayerAction, "%d", This->Music_Files.DropOnPlayerAction);
	Choices_Write(Section_Music_Files, Var_AutoOpenPlaylist, "%d", This->Music_Files.bAutoOpenPlaylist);
	Choices_Write(Section_Music_Files, Var_AutoEnableProgram, "%d", This->Music_Files.bAutoEnableProgram);

	Choices_Write(Section_AMPlayer, Var_BufSize, "%d", This->Drivers.AMPlayer.BufferSize);

	Choices_Write(Section_CDDB, Var_SendQuery, "%d", This->Drivers.CDDB.bSendQuery);
	Choices_Write(Section_CDDB, Var_AllowRemote, "%d", This->Drivers.CDDB.bAllowRemote);
	Choices_Write(Section_CDDB, Var_ShowQuery, "%d", This->Drivers.CDDB.bShowQuery);
	Choices_Write(Section_CDDB, Var_Path, "%s", This->Drivers.CDDB.pPath);

	for (i = 0; i < 8; i++)
	{
		Choices_Write(Section_CDs, SPrintf("PlayMode%d", i), "%d", This->Drivers.CDFS.PlayMode[i]);
		Choices_Write(Section_CDs, SPrintf("NudgeSize%d", i), "%d", This->Drivers.CDFS.NudgeSize[i]);
	}
	Choices_Write(Section_CDs, Var_PollingDelay, "%d", This->Drivers.CDFS.PollingDelay);
	Choices_Write(Section_CDs, Var_HideDriveNumber, "%d", This->Drivers.CDFS.bHideDriveNumber);

	Choices_Write(Section_DiskSample, Var_InBufSize, "%d", This->Drivers.DiskSample.InputBufferSize);
	Choices_Write(Section_DiskSample, Var_OutBufSize, "%d", This->Drivers.DiskSample.OutputBufferSize);
	Choices_Write(Section_DiskSample, Var_FreeVolume, "%d", This->Drivers.DiskSample.bFreeVolume);
	Choices_Write(Section_DiskSample, Var_EnableInterrupts, "%d", This->Drivers.DiskSample.bEnableInterrupts);

	Choices_Write(Section_PlayIt, Var_BufferSize, "%d", This->Drivers.PlayIt.BufferSize);
	Choices_Write(Section_PlayIt, Var_InterpretUnknown, "%d", This->Drivers.PlayIt.bInterpretUnknown);
	Choices_Write(Section_PlayIt, Var_Format, "%d", This->Drivers.PlayIt.Format);
	Choices_Write(Section_PlayIt, Var_Frequency, "%d", This->Drivers.PlayIt.Frequency);
	Choices_Write(Section_PlayIt, Var_Channels, "%d", This->Drivers.PlayIt.Channels);

	Choices_Write(Section_TimPlayer, Var_FreeVolume, "%d", This->Drivers.TimPlayer.bFreeVolume);
	Choices_Write(Section_TimPlayer, Var_EnableInterrupts, "%d", This->Drivers.TimPlayer.bEnableInterrupts);
	Choices_Write(Section_TimPlayer, Var_Polyphony, "%d", This->Drivers.TimPlayer.Polyphony);

	Choices_Write(Section_Equalizer, Var_Active, "%d", This->Equalizer.bActive);
	Choices_Write(Section_Equalizer, Var_Name, "%s", This->Equalizer.Name);
	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		Choices_Write(Section_Equalizer, SPrintf(Var_Gain, i), "%d", This->Equalizer.Params.Gain[i]);

	for (i = 0; i < 3; i++)
	{
		Choices_Write(Section_Iconbar, SPrintf("Select%d", i), "%d", This->Iconbar.Select[i]);
		Choices_Write(Section_Iconbar, SPrintf("Adjust%d", i), "%d", This->Iconbar.Adjust[i]);
	}

	Choices_Write(Section_PlugIns, Var_AutoStart, "%d", This->PlugIns.bAutoStart);
	Choices_Write(Section_PlugIns, Var_RescaleWithTrackVolume, "%d", This->PlugIns.bRescaleWithTrackVolume);
	Choices_Write(Section_PlugIns, Var_Mode, "%d", This->PlugIns.bDesktop);
	Choices_Write(Section_PlugIns, Var_Desktop, "%s", This->PlugIns.pDesktop);
	Choices_Write(Section_PlugIns, Var_FullScreen, "%s", This->PlugIns.pFullScreen);

	Choices_Write(Section_Sampling, Var_ForceDriverConfig, "%d", This->Samplers.bForceDriverConfig);
	for (i = 0; i < This->Samplers.Count; i++)
	{
		Choices_Write(SPrintf(Section_Sampler, i), Var_Driver, "%s", This->Samplers.Params[i].Driver);
		Choices_Write(SPrintf(Section_Sampler, i), Var_SourceLeft, "%s", This->Samplers.Params[i].SourceLeft);
		Choices_Write(SPrintf(Section_Sampler, i), Var_SourceRight, "%s", This->Samplers.Params[i].SourceRight);
		Choices_Write(SPrintf(Section_Sampler, i), Var_SampleType, "%s", This->Samplers.Params[i].SampleType);
		Choices_Write(SPrintf(Section_Sampler, i), Var_SampleRate, "%d", This->Samplers.Params[i].SampleRate);
	}

	Choices_Write(Section_PlayLists, Var_MaxPoints, "%d", This->PlayLists.MaxPoints);

	Choices_Write(Section_Internet, Var_UseProxy, "%d", This->Internet.bUseProxy);
	Choices_Write(Section_Internet, Var_ProxyUrl, "%s", This->Internet.ProxyUrl);
	Choices_Write(Section_Internet, Var_IcyMetadata, "%d", This->Internet.bIcyMetadata);
	Choices_Write(Section_Internet, Var_AudiocastMetadata, "%d", This->Internet.bAudiocastMetadata);
	Choices_Write(Section_Internet, Var_AudiocastPort, "%d", This->Internet.AudiocastPort);
	Choices_Write(Section_Internet, Var_Reserve, "%d", This->Internet.Reserve);
	for (i = 1; i < 4; i++)
	{
		Choices_Write(Section_Internet, SPrintf("%s%d", Var_Band, i), "%d", This->Internet.BandRates[i]);
	}
}

static HWind throw_OptEdit_Register(COptEdit* This, int level, const char* name, const char* helpid, Listener_FOnEvent pEventHandler)
{
	HWind	volatile w = HWind_None;
	TreeNode* pParent = TreeList_GetRootNode(This->m_pSections);
	TreeNode* pBrother = NULL;
	int     lev = 0;

	try
	{
		if (pEventHandler)
		{
			w = throw_Window_CreateFrom(name, helpid);
			throw_Window_RegisterEventHandler(w, pEventHandler, This, false);
		}
		else
		{
			w = throw_Window_CreateFrom("Op", helpid);
		}
		throw_Window_RegisterEventHandler(w, OptOp_EventHandler, This, false);

		while(lev < level)
		{
			lev++;
			pParent = TreeNode_GetLastChild(pParent);
		}
		pBrother = TreeNode_GetLastChild(pParent);
		throw_TreeList_InsertChild(This->m_pSections, pParent, pBrother, NULL, Msg_Lookup(name), (void*) w);
	}
	catch
	{
		Window_Delete(w);
		throw_current();
	}
	catch_end

	return w;
}

/*-------------------------------------------------------------------------*
 *--- List Redraw handling ------------------------------------------------*
 *-------------------------------------------------------------------------*/

/* Set size of every item in the list according to the maximal sizes
   of file type width and driver name width */

#define Types_Max 224 - 36

// Plot an item at a given position

static void Types_PlotItem(const void* pObject, const void* pOwner, const WPlotItem* pItem)
{
	const CLoadFileType* pNode = pObject;
	CRect			box = pItem->m_box;
	CRect			box2;
	int			bcol, fcol;
	static char		text[5];
	int			max = (pItem->m_pmaxsizes[0] > Types_Max) ? pItem->m_pmaxsizes[0] : Types_Max;

	IGNORE(pOwner);

	if (pItem->m_flags & EWItem_HasFocus)
	{
		bcol = 7;
		fcol = 0;
	}
	else
	{
		bcol = 0;
		fcol = 7;
	}

	box = RectToScreen(&pItem->m_box, &pItem->m_cvtinfo);
	RoundRectForPlot(&box);

	// Plot filled rectangle
	Desktop_SetStdColour(bcol);
	Display_Plot(4, box.x0, box.y0);
	Display_Plot(101, box.x1, box.y1);

	// Plot type
	snprintf(text, sizeof(text), "%03X", pNode->type);
	Desktop_SetStdColour(fcol);
	Desktop_SetStdColour(0x80 + bcol);
	box2 = box;
	box2.x0 += 8;
	box2.x1 = box2.x0 + max - 8;
	Desktop_PlotText(text, &box2);

	// Plot driver
	box2 = box;
	box2.x0 += max + 8;
	box2.x1 -= 8;
	Desktop_PlotText(pNode->pdriver->pDriverName, &box2);

	// Plot focus
	if (pItem->m_flags & EWItem_HasFocus)
	{
		Desktop_SetStdColour(fcol);

		// Set dash-line pattern
		Display_SetDashPattern(24, 0xdb, 0x6d, 0xb6, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc);
		// plot dashed rectangle
		Display_Plot(4, box.x0, box.y0);
		Display_Plot(21, box.x0, box.y1);
		Display_Plot(53, box.x1, box.y1);
		Display_Plot(53, box.x1, box.y0);
		Display_Plot(53, box.x0, box.y0);
	}
}

/* Extract size parameters of one item to compare it to the existing
   maximal values */

static void Types_GetItemSize(const void* pObject, const void* pOwner, int* psizes)
{
	const CLoadFileType* pNode = pObject;
	static char text[5];

	// Stop warnings
	IGNORE(pOwner);

	snprintf(text, sizeof(text), "%03X", pNode->type);
	psizes[0] = Desktop_GetTextWidth(text) + 16;
	psizes[1] = Desktop_GetTextWidth(pNode->pdriver->pDriverName) + 16;
}

static CSize Types_GetItemBox(const void* pObject, const void* pOwner, const int* pSizes, const int* pMaxSizes)
{
	CSize size;

	// Stop warnings
	IGNORE(pObject);
	IGNORE(pOwner);
	IGNORE(pSizes);

	size.cx = (pMaxSizes[0] > Types_Max) ? pMaxSizes[0] : Types_Max;
	size.cx += pMaxSizes[1];
	size.cy = 40;

	return size;
}

static WListCore_FWList Types_FWList =
{
	  Types_PlotItem
	, Types_GetItemSize
	, Types_GetItemBox
	, NULL
	, NULL
	, WOwner_PlotBackGround_White
};

static void Delete_OptEdit(COptEdit* This)
{
	if (This)
	{
		Delete_Menu(This->m_MenuSkin, true);
		Delete_Menu(This->m_MenuTmp, false);
		Delete_Menu(This->m_MenuLoadAction, false);
		Delete_Menu(This->m_MenuPlayItFormat, false);
		Delete_Menu(This->m_MenuPlayItFreq, false);
		Delete_Menu(This->m_MenuPlayItChannels, false);
		Delete_Menu(This->m_MenuSpTarget, false);
		Delete_Menu(This->m_MenuDMASize, false);
		Delete_Menu(This->m_MenuFreqMultiply, false);
		Delete_Menu(This->m_MenuCDPlayMode, false);
		if (This->m_pSections != NULL)
		{
			Window_DeRegisterEventHandler(TreeList_GetWindow(This->m_pSections), OptEdit_F1Handler, This);
			Delete_TreeList(This->m_pSections);
		}
		Window_DeRegisterEventHandler(WListCore_GetWindow(&This->m_Types), WListCore_ListEventHandler, &This->m_Types);
		WListCore_NotWListCore(&This->m_Types);
		mem_free(This->m_pPreviewSprites);
		Window_Delete(This->m_wndSkins);
		Window_Delete(This->m_wndPlayers);
		Window_Delete(This->m_wndCDDB);
		Window_Delete(This->m_wndCDFS);
		Window_Delete(This->m_wndFiles);
		Window_Delete(This->m_wndFileTypes);
		Window_Delete(This->m_wndDrivers);
		Window_Delete(This->m_wndAMPlayer);
		Window_Delete(This->m_wndDiskSample);
		Window_Delete(This->m_wndPlayIt);
		Window_Delete(This->m_wndTimPlayer);
		Window_Delete(This->m_wndMixing);
		Window_Delete(This->m_wndStereo);
		Window_Delete(This->m_wndEqualizer);
		Window_Delete(This->m_wndIconbar);
		Window_Delete(This->m_wndPlugIns);
		Window_Delete(This->m_wndSamplers);
		Window_Delete(This->m_wndInternet);
		if (This->m_wnd != HWind_None)
		{
			CWindCvt Info = Window_GetPosInfo(This->m_wnd);
			Choices_Write(Section_Choices, "PosX", "%d", Info.box.x0);
			Choices_Write(Section_Choices, "PosY", "%d", Info.box.y0);
			Window_Delete(This->m_wnd);
		}

		Options_NotOptions(&DigitalCD.Options);
		Options_NotOptions(&This->m_Options);

		mem_free(This);
	}
}

static COptEdit* throw_New_OptEdit(void)
{
	COptEdit* This = throw_mem_calloc(1, sizeof(*This));
	CTemplate* tc = Templates_Find("Choices");
	CTemplate* volatile t = NULL;
	CTemplate* volatile t2 = NULL;
	CWind* info;
	CIcon icon;
	CRect box;
	const CLoadFileType* pType = NULL;
	int i;

	This->m_wnd = This->m_wndSkins = This->m_wndPlayers = This->m_wndCDDB
	            = This->m_wndCDFS = This->m_wndFiles = This->m_wndFileTypes
	            = This->m_wndDrivers = This->m_wndAMPlayer = This->m_wndDiskSample
	            = This->m_wndPlayIt = This->m_wndTimPlayer = This->m_wndMixing
	            = This->m_wndStereo = This->m_wndEqualizer = This->m_wndIconbar
	            = This->m_wndPlugIns = This->m_wndSamplers = This->m_wndInternet
	            = This->m_wndPane = HWind_None;

	try
	{
		if (tc == NULL) throw_string("Template 'Choices' not found");
		t = throw_Templates_Blank(0, 0xd0000022, 0, 0);
		t2 = throw_Templates_Blank(0, 0x90000022, 0, 0);
		This->m_MenuLoadAction = throw_New_Menu(Msg_Lookup("Action:"),Msg_Lookup("FiAction:"));
		This->m_MenuPlayItFormat = throw_New_Menu(Msg_Lookup("Format:"),Msg_Lookup("PlItFormat:"));
		This->m_MenuPlayItFreq = throw_New_Menu(Msg_Lookup("Frequency:"),Msg_Lookup("PlItFreq:"));;
		This->m_MenuPlayItChannels = throw_New_Menu(Msg_Lookup("Channels:"),Msg_Lookup("PlItChannels:"));
		This->m_MenuDMASize = throw_New_Menu(Msg_Lookup("DMASize:"),Msg_Lookup("DMANoSize:"));
		for (i = 1; i <= MAX_DMASIZE; i++)
			throw_Menu_InsertItem(This->m_MenuDMASize, SPrintf("%d", DMASizes[i]), -1);
		This->m_MenuFreqMultiply = throw_New_Menu(Msg_Lookup("FreqMultiply:"),Msg_Lookup("FreqMulAction:"));
		This->m_MenuCDPlayMode = throw_New_Menu(Msg_Lookup("PlayMode:"),Msg_Lookup("CDPlayModes:"));
		// Disable non-working ReadAudio mode
		// Menu_ItemSetProperty(This->m_MenuCDPlayMode, 0, EMenu_Item_Fade, true);


		info = Template_GetWindow(tc);
		box = info->o.o.cvt.box;
		info->o.o.cvt.box.x0 = Choices_ReadInt(Section_Choices, "PosX", info->o.o.cvt.box.x0);
		info->o.o.cvt.box.y0 = Choices_ReadInt(Section_Choices, "PosY", info->o.o.cvt.box.y0);
		info->o.o.cvt.box.x1 = info->o.o.cvt.box.x0 + box.x1 - box.x0;
		info->o.o.cvt.box.y1 = info->o.o.cvt.box.y0 + box.y1 - box.y0;
		This->m_wnd = throw_Window_Create(tc, "OpEdH");
		throw_Window_RegisterEventHandler(This->m_wnd, OptEdit_EventHandler, This, false);

		This->m_pSections = throw_New_TreeList(0, t2, NULL);
		throw_Window_RegisterEventHandler(TreeList_GetWindow(This->m_pSections), OptEdit_F1Handler, This, true);

		Icon_GetInfo(This->m_wnd, Icon_SectionsBorder, &icon);
		icon.box.x0 += 4;
		icon.box.y0 += 4;
		icon.box.x1 -= 4;
		icon.box.y1 -= 4;
		throw_TreeList_SetPane(This->m_pSections, This->m_wnd, &icon.box);
		TreeList_SetParent(This->m_pSections, This->m_wnd);

		This->m_wndSkins     = throw_OptEdit_Register(This, 0, "OpSkins"    , "OpSkH", OptSkins_EventHandler);
		This->m_wndPlayers   = throw_OptEdit_Register(This, 0, "OpPlayers"  , "OpPlH", OptPlayers_EventHandler);
		This->m_wndMixing    = throw_OptEdit_Register(This, 0, "OpMixing"   , "OpMiH", OptMixing_EventHandler);
		This->m_wndStereo    = throw_OptEdit_Register(This, 1, "OpStereo"   , "OpStH", OptStereo_EventHandler);
		if (DigitalCD.Hardware.Capabilities & Hardware_Mul64bit)
			This->m_wndEqualizer = throw_OptEdit_Register(This, 1, "OpEqual"    , "OpEqH", OptEqualizer_EventHandler);
		This->m_wndIconbar   = throw_OptEdit_Register(This, 0, "OpIconbar"  , "OpIcH", OptIconbar_EventHandler);
		This->m_wndPlugIns   = throw_OptEdit_Register(This, 0, "OpPlugIns"  , "OpPuH", OptPlugIns_EventHandler);
		This->m_wndSamplers  = throw_OptEdit_Register(This, 1, "OpSamplers" , "OpSaH", OptSamplers_EventHandler);
		This->m_wndFiles     = throw_OptEdit_Register(This, 0, "OpFiles"    , "OpFiH", OptFiles_EventHandler);
		This->m_wndFileTypes = throw_OptEdit_Register(This, 1, "OpFileTypes", "OpFTH", OptFileTypes_EventHandler);
		This->m_wndDrivers   = throw_OptEdit_Register(This, 0, "OpDrivers"  , "OpDrH", NULL);
		This->m_wndAMPlayer  = throw_OptEdit_Register(This, 1, "OpAMPlayer" , "OpAMH", OptAMPlayer_EventHandler);
		This->m_wndCDFS      = throw_OptEdit_Register(This, 1, "OpCDFS"     , "OpCFH", OptCDFS_EventHandler);
		This->m_wndCDDB      = throw_OptEdit_Register(This, 2, "OpAcornCD"  , "OpCDH", OptCDDB_EventHandler);
		This->m_wndDiskSample= throw_OptEdit_Register(This, 1, "OpDisk"     , "OpMPH", OptDiskSample_EventHandler);
		This->m_wndPlayIt    = throw_OptEdit_Register(This, 1, "OpPlayIt"   , "OpPaH", OptPlayIt_EventHandler);
		This->m_wndTimPlayer = throw_OptEdit_Register(This, 1, "OpTimPlayer", "OpTiH", OptTimPlayer_EventHandler);
		This->m_wndInternet  = throw_OptEdit_Register(This, 0, "OpInternet" , "OpInH", OptInternet_EventHandler);

		TreeList_ExpandParents(This->m_pSections, NULL);
		TreeList_ExpandParents(This->m_pSections, TreeNode_GetFirstChild(TreeList_GetWListItem(This->m_pSections, 2)));

		Icon_GetInfo(This->m_wndFileTypes, Icon_Types, &icon);
		icon.box.x0 += 4;
		icon.box.y0 += 4;
		icon.box.x1 -= 4;
		icon.box.y1 -= 4;
		info = Template_GetWindow(t);
		info->o.o.cvt.box = icon.box;

		throw_WListCore_WListCore(&This->m_Types, EWList_SelModeSingle, t, This, 2, &Types_FWList);
		throw_WListCore_SetPane(&This->m_Types, This->m_wndFileTypes, &icon.box);
		throw_Window_RegisterEventHandler(WListCore_GetWindow(&This->m_Types), WListCore_ListEventHandler, &This->m_Types, false);

		This->m_CurrentSampler = 0;
		This->m_CurrentCDDrive = 0;
		This->m_CurrentDesktopPlugIn = 0;

		throw_Options_Options(&DigitalCD.Options);
		Options_Load(&DigitalCD.Options);
		throw_Options_Options(&This->m_Options);
		This->m_EqualizerStyle = This->m_Options.Equalizer.Params;

		while((pType = LoadTypes_GetNext(pType)) != NULL)
		{
			throw_WListCore_Insert(&This->m_Types, -1, (void*) pType);
		}

		This->m_MenuSpTarget = throw_New_Menu(Msg_Lookup("SpTTarget"), DigitalCD.Options.Samplers.Params[0].Target);
		for (i = 2/* not 1 to hide a target */; i < DigitalCD.Options.Samplers.Count; i++)
		{
			throw_Menu_InsertItem(This->m_MenuSpTarget, DigitalCD.Options.Samplers.Params[i].Target, -1);
		}

		This->m_bModified = true;
		OptEdit_SetModified(This, false);
		OptEdit_SetPane(This, 0);
		OptEdit_BuildSkinMenu(This);
	}
	catch
	{
		Delete_OptEdit(This);
		throw_current();
	}
	catch_end

	return This;
}

COptEdit* OptEdit_Get(void)
{
	if (!App_pOptEdit) App_pOptEdit = throw_New_OptEdit();

	return App_pOptEdit;
}

void OptEdit_Release(void)
{
	if (App_pOptEdit)
	{
		Delete_OptEdit(App_pOptEdit);
		App_pOptEdit = NULL;
	}
}

static void OptEdit_Refresh(COptEdit* This, const void* data)
{
	Event		e;
	CHint		hint;

	hint.Type = 0;
	hint.Data = data;

	e.Type = EEvent_OnUpdate;
	e.pData = &hint;

	Window_ProcessEvent(This->m_wndPane, &e);

	OptEdit_SetModified(This, !OptEdit_IsCurrent(This));
}

void OptEdit_SetPane(COptEdit* This, int index)
{
	TreeNode*	pNode = TreeList_GetWListItem(This->m_pSections, index);
	HWind		w = (pNode) ? (HWind) TreeNode_GetData(pNode) : HWind_None;

	if (This->m_wndPane == w)
		return;

	if (This->m_wndPane != HWind_None)
	{
		Window_RemovePane(This->m_wnd, This->m_wndPane);
		Window_Close(This->m_wndPane);
	}

	This->m_wndPane = w;
	if (This->m_wndPane != HWind_None)
	{
		throw_Window_AddPane(This->m_wnd, This->m_wndPane);

		OptEdit_Refresh(This, NULL);
	}
}

void OptEdit_Show(COptEdit* This)
{
	if (!Window_IsOpen(This->m_wnd)) OptEdit_ShowCurrent(This);

	Window_Open(This->m_wnd);
	Icon_SetFocus(This->m_wnd, HIcon_None, 0);
}

bool OptEdit_IsModified(COptEdit* This)
{
	return This->m_bModified;
}

void OptEdit_SetModified(COptEdit* This, bool bModified)
{
	if (This->m_bModified == bModified) return;

	This->m_bModified = bModified;

	Icon_SetDimmed(This->m_wnd, Icon_Set, !This->m_bModified);
}

void OptEdit_ShowCurrent(COptEdit* This)
{
	COptions*	opt = &This->m_Options;
	throw_Options_CopyOptions(opt, &DigitalCD.Options);

	// Update items for which values will be used directly instead of the value in This->m_Options
	OptEdit_SelectSkin(This, opt->Player.pSkinName, 0);
	Icon_SetData(This->m_wndCDDB, Icon_CDDBPath, opt->Drivers.CDDB.pPath);
	OptEdit_ReadEqualizerStyle(This, opt->Equalizer.Name);
	Icon_SetData(This->m_wndEqualizer, Icon_EqualizerStyle, opt->Equalizer.Name);
	Icon_SetData(This->m_wndInternet, Icon_NetProxyUrl, opt->Internet.ProxyUrl);
	Icon_Printf(This->m_wndInternet, Icon_NetAudiocastPort, "%d", opt->Internet.AudiocastPort);

	// Update the content of current property page
	OptEdit_Refresh(This, NULL);
}

bool OptEdit_IsCurrent(COptEdit* This)
{
	COptions*	opt = &This->m_Options;
	COptions*	ref = &DigitalCD.Options;
	int i;

	if (strcmp(Icon_GetData(This->m_wndSkins, Icon_SkinName), ref->Player.pSkinName)) return false;

	if (opt->Player.bAutoStartPlaylist != ref->Player.bAutoStartPlaylist) return false;
	if (opt->Player.bRememberPosition != opt->Player.bRememberPosition) return false;
	if (opt->Player.bAutoStartNewCDs != ref->Player.bAutoStartNewCDs) return false;
	if (opt->Player.IntroScanTime != ref->Player.IntroScanTime) return false;
	if (opt->Player.bStopPlayingOnError != ref->Player.bStopPlayingOnError) return false;
	if (opt->Player.bParentalLock != ref->Player.bParentalLock) return false;
	if (opt->Player.bFixTrackVolume != ref->Player.bFixTrackVolume) return false;
	if (opt->Player.bReShuffleOnLoop != ref->Player.bReShuffleOnLoop) return false;
	if (opt->Player.bSinglePanelMode != ref->Player.bSinglePanelMode) return false;
	if (opt->Player.bRemoveAsNeverPlay != ref->Player.bRemoveAsNeverPlay) return false;
	if (opt->Player.bIgnoreSystemVolume != ref->Player.bIgnoreSystemVolume) return false;
	if (opt->Player.MaxVisibleTracks != ref->Player.MaxVisibleTracks) return false;
	if (opt->Player.bGaplessPlayback != ref->Player.bGaplessPlayback) return false;
	if (opt->Player.FadingTime != ref->Player.FadingTime) return false;

	if (memcmp(&opt->Music_Files, &ref->Music_Files, sizeof(opt->Music_Files))) return false;

	if (memcmp(&opt->Drivers.AMPlayer, &ref->Drivers.AMPlayer, sizeof(ref->Drivers.AMPlayer))) return false;

	if (opt->Drivers.CDDB.bSendQuery != ref->Drivers.CDDB.bSendQuery) return false;
	if (opt->Drivers.CDDB.bAllowRemote != ref->Drivers.CDDB.bAllowRemote) return false;
	if (opt->Drivers.CDDB.bShowQuery != ref->Drivers.CDDB.bShowQuery) return false;
	if (strcmp(Icon_GetData(This->m_wndCDDB, Icon_CDDBPath), ref->Drivers.CDDB.pPath)) return false;

	if (memcmp(&opt->Drivers.CDFS, &ref->Drivers.CDFS, sizeof(opt->Drivers.CDFS))) return false;

	if (memcmp(&opt->Drivers.DiskSample, &ref->Drivers.DiskSample, sizeof(ref->Drivers.DiskSample))) return false;

	if (memcmp(&opt->Drivers.PlayIt, &ref->Drivers.PlayIt, sizeof(ref->Drivers.PlayIt))) return false;

	if (memcmp(&opt->Drivers.TimPlayer, &ref->Drivers.TimPlayer, sizeof(ref->Drivers.TimPlayer))) return false;

	if (opt->Equalizer.bActive != ref->Equalizer.bActive) return false;
	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
	{
		if (opt->Equalizer.Params.Gain[i] != ref->Equalizer.Params.Gain[i]) return false;
	}

	for (i = 0; i < 3; i++)
	{
		if (opt->Iconbar.Select[i] != ref->Iconbar.Select[i]) return false;
		if (opt->Iconbar.Adjust[i] != ref->Iconbar.Adjust[i]) return false;
	}

	if (opt->PlugIns.bAutoStart != ref->PlugIns.bAutoStart) return false;
	if (opt->PlugIns.bDesktop != ref->PlugIns.bDesktop) return false;
	if (opt->PlugIns.bRescaleWithTrackVolume != ref->PlugIns.bRescaleWithTrackVolume) return false;
	if (strcmp(opt->PlugIns.pDesktop, ref->PlugIns.pDesktop)) return false;
	if (strcmp(opt->PlugIns.pFullScreen, ref->PlugIns.pFullScreen)) return false;

	if (opt->Samplers.bForceDriverConfig != ref->Samplers.bForceDriverConfig) return false;
	for (i = 0; i < ref->Samplers.Count; i++)
	{
		if (strcmp(opt->Samplers.Params[i].Target, ref->Samplers.Params[i].Target)) return false;
		if (strcmp(opt->Samplers.Params[i].Driver, ref->Samplers.Params[i].Driver)) return false;
		if (strcmp(opt->Samplers.Params[i].SourceLeft, ref->Samplers.Params[i].SourceLeft)) return false;
		if (strcmp(opt->Samplers.Params[i].SourceRight, ref->Samplers.Params[i].SourceRight)) return false;
		if (strcmp(opt->Samplers.Params[i].SampleType, ref->Samplers.Params[i].SampleType)) return false;
		if (opt->Samplers.Params[i].SampleRate !=  ref->Samplers.Params[i].SampleRate) return false;
	}

	if (opt->Internet.bUseProxy != ref->Internet.bUseProxy) return false;
	if (strcmp(Icon_GetData(This->m_wndInternet, Icon_NetProxyUrl), ref->Internet.ProxyUrl)) return false;
	if (opt->Internet.bIcyMetadata != ref->Internet.bIcyMetadata) return false;
	if (opt->Internet.bAudiocastMetadata != ref->Internet.bAudiocastMetadata) return false;
	if (atoi(Icon_GetData(This->m_wndInternet, Icon_NetAudiocastPort)) != ref->Internet.AudiocastPort) return false;
	if (opt->Internet.Reserve != ref->Internet.Reserve) return false;

	return true;
}

static void OptEdit_Cancel(COptEdit* This)
{
	Event e = {NULL, EEvent_OptionMixingParameters, NULL};

	// Stop warnings
	IGNORE(This);

	// Reset previewed parameters
	Task_ProcessEvent(&e);
}

void OptEdit_Set(COptEdit* This)
{
	COptions*		opt = &This->m_Options;
	COptions*		ref = &DigitalCD.Options;
	COptions* volatile	cpy = NULL;
	bool			bskin = false;
	bool			bequ = false;
	Event			e;
	int			i;

	try
	{
		// Keep a copy of old settings
		cpy = throw_mem_alloc(sizeof(*cpy));
		throw_Options_Options(cpy);
		// Copy old options
		throw_Options_CopyOptions(cpy, ref);

		// Copy string from icons
		if (strcmp(opt->Player.pSkinName, Icon_GetData(This->m_wndSkins, Icon_SkinName)))
		{
			throw_mem_setstring(&opt->Player.pSkinName, Icon_GetData(This->m_wndSkins, Icon_SkinName));
			bskin = true;
		}
		throw_mem_setstring((const char**) &opt->Drivers.CDDB.pPath, Icon_GetData(This->m_wndCDDB, Icon_CDDBPath));
		for (char* pc = opt->Drivers.CDDB.pPath; *pc; pc++)
		{
			if (*pc == ' ') *pc = 0xa0;
		}
		throw_mem_setstring(&opt->Equalizer.Name, Icon_GetData(This->m_wndEqualizer, Icon_EqualizerStyle));
		throw_mem_setstring(&opt->Internet.ProxyUrl, Icon_GetData(This->m_wndInternet, Icon_NetProxyUrl));
		opt->Internet.AudiocastPort = atoi(Icon_GetData(This->m_wndInternet, Icon_NetAudiocastPort));

		// Copy options
		throw_Options_CopyOptions(ref, opt);
	}
	catch
	{
		App_ReportException();
		mem_free(cpy);
		cpy = NULL;
	}
	catch_end

	OptEdit_SetModified(This, false);
	if (!cpy) return;

	// Broadcast changes
	if (cpy->Player.bSinglePanelMode != ref->Player.bSinglePanelMode)
	{
		// Ensure we keep only 1 player in single player mode
		if (ref->Player.bSinglePanelMode)
			throw_PlayList_EnsurePlayer(NULL, true);
	}
	if (cpy->Player.bIgnoreSystemVolume != ref->Player.bIgnoreSystemVolume)
	{
		e.Type = EEvent_OptionRescaleVolume;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}
	if (cpy->Drivers.CDFS.bHideDriveNumber != ref->Drivers.CDFS.bHideDriveNumber)
	{
		e.Type = EEvent_OptionHideCDDriveNumber;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.bShowFileAsTitle != ref->Music_Files.bShowFileAsTitle)
	{
		e.Type = EEvent_OptionShowFileAsTitle;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}
	if ((cpy->Music_Files.Frequency != ref->Music_Files.Frequency)
	||  (cpy->Music_Files.FreqMultiply != ref->Music_Files.FreqMultiply))
	{
		e.Type = EEvent_OptionMixingFrequency;
		e.pData = &ref->Music_Files.Frequency;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.DMASize != ref->Music_Files.DMASize)
	{
		e.Type = EEvent_OptionMixingDMASize;
		e.pData = &ref->Music_Files.DMASize;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.bInterpol != ref->Music_Files.bInterpol)
	{
		e.Type = EEvent_OptionMixingInterpol;
		e.pData = &ref->Music_Files.bInterpol;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.bVolumeRamping != ref->Music_Files.bVolumeRamping)
	{
		e.Type = EEvent_OptionMixingVolRamping;
		e.pData = &ref->Music_Files.bVolumeRamping;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.Balance != ref->Music_Files.Balance)
	{
		e.Type = EEvent_OptionMixingBalance;
		e.pData = &ref->Music_Files.Balance;
		Task_ProcessEvent(&e);
	}
	if (cpy->Music_Files.StereoSeparation != ref->Music_Files.StereoSeparation)
	{
		e.Type = EEvent_OptionMixingStereo;
		e.pData = &ref->Music_Files.StereoSeparation;
		Task_ProcessEvent(&e);
	}

	if (cpy->Equalizer.bActive != ref->Equalizer.bActive)
		bequ = true;
	if (!bequ && ref->Equalizer.bActive)
	{
		for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		{
			if (cpy->Equalizer.Params.Gain[i] != ref->Equalizer.Params.Gain[i])
				bequ = true;
		}
	}
	if (bequ)
	{
		e.Type = EEvent_OptionMixingEqualizer;
		e.pData = ref;
		Task_ProcessEvent(&e);
	}
	if (cpy->Iconbar.Select[0] != ref->Iconbar.Select[0])
	{
		e.Type = EEvent_OptionIconbarActions;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}
	if (cpy->PlugIns.bRescaleWithTrackVolume != ref->PlugIns.bRescaleWithTrackVolume)
	{
		e.Type = EEvent_OptionRescaleVolume;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}

	if (bskin) throw_Players_ReloadSkins();

	if (cpy->Player.bParentalLock != ref->Player.bParentalLock)
	{
		e.Type = EEvent_OptionParentalLock;
		e.pData = NULL;
		Task_ProcessEvent(&e);
	}

	Options_NotOptions(cpy);
	mem_free(cpy);
}

void OptEdit_Load(COptEdit* This)
{
	// Stop warnings
	IGNORE(This);

	Options_Load(&DigitalCD.Options);
}

void OptEdit_Save(COptEdit* This)
{
	OptEdit_Set(This);
	Options_Save(&DigitalCD.Options);
}

static EListenerAction OptEdit_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_WindowClose:
		{
			OptEdit_SetModified(This, false);
			return EListenerAction_ContinueEvent;
		}
		break;
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_Set:
				{
					OptEdit_Set(This);
					if (m->but & EBut_Select)
						Window_Close(This->m_wnd);
				}
				break;
				case Icon_Cancel:
				{
					OptEdit_ShowCurrent(This);
					OptEdit_Cancel(This);
					if (m->but & EBut_Select)
						Window_Close(This->m_wnd);
				}
				break;
				case Icon_Save:
				{
					OptEdit_Save(This);
					if (m->but & EBut_Select)
						Window_Close(This->m_wnd);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			switch(((Event_Key*) e->pData)->code)
			{
				case 0x1b: // Esc
					OptEdit_Cancel(This);
					Window_Close(This->m_wnd);
					return EListenerAction_StopEvent;
				break;
				case 0x0d: // Return
					OptEdit_Set(This);
					Window_Close(This->m_wnd);
					return EListenerAction_StopEvent;
				break;
				case 0x181: // F1
				{
					App_StrongHelp("Choices_Choices");
					return EListenerAction_StopEvent;
				}
				break;
				default:
					OptEdit_SetModified(This, !OptEdit_IsCurrent(This));
			}
		}
		break;
		case EEvent_SelectionChange:
		{
			const Event_SelectionChange* ev = e->pData;

			OptEdit_SetPane(This, ev->Index);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptEdit_F1Handler(void* handle, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Key:
		{
			if (((Event_Key*) e->pData)->code == 0x181) // F1
			{
				Window_ProcessEvent
					( (This->m_wndPane == HWind_None)
					  ? This->m_wnd
					  : This->m_wndPane
					, e);
				return EListenerAction_StopEvent;
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptOp_EventHandler(void* handle, const Event* e)
{
	IGNORE(handle);

	switch(e->Type)
	{
		case EEvent_WindowOpen:
		{
			CWindOpen*	po = (CWindOpen*) e->pData;
			CWindOpen	o = Window_GetOwnerInfo(po->w);
			CRect		rect;

			rect.x0 = 0;
			rect.y1 = 0;
			rect.x1 = po->cvt.box.x1 - po->cvt.box.x0;
			rect.y0 = po->cvt.box.y0 - po->cvt.box.y1;

			Window_OpenPane(po, &o, &rect, pane_fit_hgadgets | pane_fit_vgadgets);

			return EListenerAction_ContinueEvent;
		}
		break;
		case EEvent_Key:
		{
			return OptEdit_EventHandler(handle, e);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static const CRect PreviewArea = {28, -412, 688, -124};

static bool Skin_Accept(const char* pnewfile)
{
	// Must be a directory
	if (File_GetFileType(pnewfile) < 0x01000)
		return false;

	// Should contain at least a template file
	if (File_GetFileType(SPrintf("%s.Templates", pnewfile)) != 0xfec)
		return false;

	// Should contain at least a sprite file
	if (File_GetFileType(SPrintf("%s.Sprites", pnewfile)) != 0xff9)
		return false;

	return true;
}

static void OptEdit_BuildSkinMenu(COptEdit* This)
{
	Delete_Menu(This->m_MenuSkin, true);
	This->m_MenuSkin = FileMenu_Build(Msg_Lookup("Skins"), Dir_Skin, Skin_Accept, true);
}

void OptEdit_SelectSkin(COptEdit* This, const char* pname, int offset)
{
	int hit[10];
	bool bFound = FileMenu_IsValidText(This->m_MenuSkin, pname, hit);

	if (!bFound || (offset > 0))
		FileMenu_NextHit(This->m_MenuSkin, hit);
	if (bFound && offset < 0)
		FileMenu_PreviousHit(This->m_MenuSkin, hit);

	pname = FileMenu_Decode(This->m_MenuSkin, hit);

	if (strcmp(pname, Icon_GetData(This->m_wndSkins, Icon_SkinName)))
	{
		Icon_SetData(This->m_wndSkins, Icon_SkinName, pname);
		OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

		bFound = (Filer_Find(Dir_Skin, pname, File_Help)
		     ||   Filer_Find(Dir_Skin, pname, File_ReadMe));
		Icon_SetDimmed(This->m_wndSkins, Icon_SkinHelp, !bFound);

		mem_free(This->m_pPreviewSprites);
		try
		{
			This->m_pPreviewSprites = throw_Sprites_LoadFile(SPrintf("%s.%s.%s", Dir_Skin, pname, File_Preview));
		}
		catch
		{
			This->m_pPreviewSprites = NULL;
		}
		catch_end
		Window_Invalidate(This->m_wndSkins, &PreviewArea);
	}
}

static void OptEdit_PlayerDrawer(void* handle, const CWindRedraw* pdraw)
{
	COptEdit*	This = handle;
	CSpriteHdr*	psprite;

	if ((This->m_pPreviewSprites)
	&&  ((psprite = Sprites_SelectSprite(This->m_pPreviewSprites, "preview")) != NULL))
	{
		CPoint		pt;
		CSize		size = Desktop_GetSpriteSize(psprite);
		CSize		asize;
		CSpriteFactors	scale = {1, 1, 1, 1};

		asize.cx = PreviewArea.x1 - PreviewArea.x0;
		asize.cy = PreviewArea.y1 - PreviewArea.y0;

		if ((asize.cx < size.cx)
		||  (asize.cy < size.cy))
		{
			if (asize.cx < size.cx)
			{
				scale.xmag = asize.cx;
				scale.xdiv = size.cx;
			}
			if (asize.cy < size.cy)
			{
				scale.ymag = asize.cy;
				scale.ydiv = size.cy;
			}
/*			if (asize.cx * size.cy > asize.cy * size.cx)
			{
				scale.xmag = scale.ymag = asize.cy;
				scale.xdiv = scale.ydiv = size.cy;
			}
			else
			{
				scale.xmag = scale.ymag = asize.cx;
				scale.xdiv = scale.ydiv = size.cx;
			}
*/			size.cx *= scale.xmag;
			size.cx /= scale.xdiv;
			size.cy *= scale.ymag;
			size.cy /= scale.ydiv;
		}
		pt.x = PreviewArea.x0 + (asize.cx - size.cx)/2;
		pt.y = PreviewArea.y0 + (asize.cy - size.cy)/2;
		pt = PointToScreen(&pt, &pdraw->cvt);
		Desktop_PlotSprite(psprite, pt, &scale);
	}
}

static EListenerAction OptSkins_SkinMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// Skin Menu
			if (FileMenu_IsValidHit(This->m_MenuSkin, hit))
				OptEdit_SelectSkin(This, FileMenu_Decode(This->m_MenuSkin, hit), 0);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSkins_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndSkins;

	switch(e->Type)
	{
		case EEvent_WindowRedraw:
		{
			Window_Redraw(wnd, OptEdit_PlayerDrawer, This);

			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_SkinPopup:
				{
					if (m->but & EBut_Adjust) OptEdit_BuildSkinMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuSkin, OptSkins_SkinMenuHandler, handle);
				}
				break;
				case Icon_SkinUp:
				case Icon_SkinDown:
				{
					int dir = 1;
					if (m->i == Icon_SkinUp) dir = -dir;
					if (m->but & EBut_Adjust) dir = -dir;
					// OptEdit_BuildSkinMenu(This);
					OptEdit_SelectSkin(This, Icon_GetData(wnd, Icon_SkinName), dir);
				}
				break;
				case Icon_SkinHelp:
				{
					const char* pname = Icon_GetData(wnd, Icon_SkinName);
					if (!Filer_Run(Dir_Skin, pname, File_Help))
						Filer_Run(Dir_Skin, pname, File_ReadMe);
				}
				break;
				case Icon_SinglePanelMode:
				{
					opt->Player.bSinglePanelMode ^= 1;
					OptEdit_Refresh(This, &opt->Player.bSinglePanelMode);
				}
				break;
				case Icon_RemoveAsNeverPlay:
				{
					opt->Player.bRemoveAsNeverPlay ^= 1;
					OptEdit_Refresh(This, &opt->Player.bRemoveAsNeverPlay);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Skins");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Player.bSinglePanelMode))
				Icon_SetHighlight(wnd, Icon_SinglePanelMode, opt->Player.bSinglePanelMode);
			if (!phint->Data || (phint->Data == &opt->Player.bRemoveAsNeverPlay))
				Icon_SetHighlight(wnd, Icon_RemoveAsNeverPlay, opt->Player.bRemoveAsNeverPlay);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptPlayers_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndPlayers;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_AutoStartPlaylist:
				{
					opt->Player.bAutoStartPlaylist ^= true;
					OptEdit_Refresh(This, &opt->Player.bAutoStartPlaylist);
				}
				break;
				case Icon_RememberPosition:
				{
					opt->Player.bRememberPosition ^= true;
					OptEdit_Refresh(This, &opt->Player.bRememberPosition);
				}
				break;
				case Icon_AutoStartNewCDs:
				{
					opt->Player.bAutoStartNewCDs ^= true;
					OptEdit_Refresh(This, &opt->Player.bAutoStartNewCDs);
				}
				break;
				case Icon_IntroScanUp:
				{
					opt->Player.IntroScanTime += 500;
					if (opt->Player.IntroScanTime > 6000)
						opt->Player.IntroScanTime = 6000;
					OptEdit_Refresh(This, &opt->Player.IntroScanTime);
				}
				break;
				case Icon_IntroScanDown:
				{
					opt->Player.IntroScanTime -= 500;
					if (opt->Player.IntroScanTime < 500)
						opt->Player.IntroScanTime = 500;
					OptEdit_Refresh(This, &opt->Player.IntroScanTime);
				}
				break;
				case Icon_StopPlayingOnError:
				{
					opt->Player.bStopPlayingOnError ^= 1;
					OptEdit_Refresh(This, &opt->Player.bStopPlayingOnError);
				}
				break;
				case Icon_ParentalLock:
				{
					opt->Player.bParentalLock ^= 1;
					OptEdit_Refresh(This, &opt->Player.bParentalLock);
				}
				break;
				case Icon_ReShuffleOnLoop:
				{
					opt->Player.bReShuffleOnLoop ^= 1;
					OptEdit_Refresh(This, &opt->Player.bReShuffleOnLoop);
				}
				break;
				case Icon_GaplessPlayback:
				{
					opt->Player.bGaplessPlayback ^= 1;
					OptEdit_Refresh(This, &opt->Player.bGaplessPlayback);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Players");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Player.bAutoStartPlaylist))
				Icon_SetHighlight(wnd, Icon_AutoStartPlaylist, opt->Player.bAutoStartPlaylist);
			if (!phint->Data || (phint->Data == &opt->Player.bRememberPosition))
				Icon_SetHighlight(wnd, Icon_RememberPosition, opt->Player.bRememberPosition);
			if (!phint->Data || (phint->Data == &opt->Player.bAutoStartNewCDs))
				Icon_SetHighlight(wnd, Icon_AutoStartNewCDs, opt->Player.bAutoStartNewCDs);
			if (!phint->Data || (phint->Data == &opt->Player.IntroScanTime))
				Icon_Printf(wnd, Icon_IntroScanTime, "%d", opt->Player.IntroScanTime / 100);
			if (!phint->Data || (phint->Data == &opt->Player.bStopPlayingOnError))
				Icon_SetHighlight(wnd, Icon_StopPlayingOnError, opt->Player.bStopPlayingOnError);
			if (!phint->Data || (phint->Data == &opt->Player.bParentalLock))
				Icon_SetHighlight(wnd, Icon_ParentalLock, opt->Player.bParentalLock);
			if (!phint->Data || (phint->Data == &opt->Player.bReShuffleOnLoop))
				Icon_SetHighlight(wnd, Icon_ReShuffleOnLoop, opt->Player.bReShuffleOnLoop);
			if (!phint->Data || (phint->Data == &opt->Player.bGaplessPlayback))
				Icon_SetHighlight(wnd, Icon_GaplessPlayback, opt->Player.bGaplessPlayback);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptCDDB_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndCDDB;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_CDDB:
				{
					opt->Drivers.CDDB.bSendQuery ^= true;
					OptEdit_Refresh(This, &opt->Drivers.CDDB.bSendQuery);
				}
				break;
				case Icon_CDDBAllowRemote:
				{
					opt->Drivers.CDDB.bAllowRemote ^= true;
					OptEdit_Refresh(This, &opt->Drivers.CDDB.bAllowRemote);
				}
				break;
				case Icon_CDDBShowQuery:
				{
					opt->Drivers.CDDB.bShowQuery ^= true;
					OptEdit_Refresh(This, &opt->Drivers.CDDB.bShowQuery);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_AcornCD");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_Message:
		case EEvent_MessageWantAck:
		{
			switch(((Msg*) e->pData)->hdr.action)
			{
				case EMsg_DataLoad:
				{
					const Msg_FileData* msg = e->pData;

					if ((msg->pos.i == Icon_CDDBPath)
					&&  (msg->type >= 0x1000))
					{
						Icon_SetData(wnd, Icon_CDDBPath, msg->name);
						OptEdit_SetModified(This, !OptEdit_IsCurrent(This));
					}
                    return EListenerAction_StopEvent; // any other drag is ignored
				}
				break;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.CDDB.bSendQuery))
				Icon_SetHighlight(wnd, Icon_CDDB, opt->Drivers.CDDB.bSendQuery);
			if (!phint->Data || (phint->Data == &opt->Drivers.CDDB.bAllowRemote))
				Icon_SetHighlight(wnd, Icon_CDDBAllowRemote, opt->Drivers.CDDB.bAllowRemote);
			if (!phint->Data || (phint->Data == &opt->Drivers.CDDB.bShowQuery))
				Icon_SetHighlight(wnd, Icon_CDDBShowQuery, opt->Drivers.CDDB.bShowQuery);
			if (!phint->Data || (phint->Data == &opt->Drivers.CDDB.pPath))
				Icon_SetData(wnd, Icon_CDDBPath, opt->Drivers.CDDB.pPath);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static void OptEdit_SelectCDPlayMode(COptEdit* This, const int index)
{
	COptions* opt = &This->m_Options;

	switch(index)
	{
		case 0:
		{
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] |= ECDMode_ReadAudioSWI;
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] &= ~(ECDMode_PlayTrackSWI|ECDMode_TrackByTrack);
		}
		break;
		case 1:
		{
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] &= ~(ECDMode_ReadAudioSWI|ECDMode_PlayTrackSWI|ECDMode_TrackByTrack);
		}
		break;
		case 2:
		{
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] |= ECDMode_TrackByTrack;
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] &= ~(ECDMode_ReadAudioSWI|ECDMode_PlayTrackSWI);
		}
		break;
		case 3:
		{
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] |= ECDMode_PlayTrackSWI;
			opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] &= ~(ECDMode_ReadAudioSWI|ECDMode_TrackByTrack);
		}
		break;
	}
	OptEdit_Refresh(This, &opt->Drivers.CDFS.PlayMode);
}

static EListenerAction OptMixing_CDPlayModeMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// CDPlayMode Menu
			OptEdit_SelectCDPlayMode(This, hit[0]);
			OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Message:
		case EEvent_MessageWantAck:
		{
			switch(((Msg*) e->pData)->hdr.action)
			{
				case EMsg_HelpRequest:
				{
					const Msg_HelpRequest* pmsg = e->pData;
					char msg1[12];

					if (hit[0] >= 0)
					{
						sprintf(msg1, "CDPlayMH%03d", hit[0]);
						Task_HelpReply_FromMenu(pmsg, msg1, NULL);
					}

					return EListenerAction_StopEvent;
				}
				break;
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptCDFS_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndCDFS;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_CDDriveDown:
				{
					if (This->m_CurrentCDDrive > 0)
					{
						This->m_CurrentCDDrive--;
						OptEdit_Refresh(This, NULL);
					}
				}
				break;
				case Icon_CDDriveUp:
				{
					if (This->m_CurrentCDDrive < (DigitalCD.Hardware.NrOfCDDrives - 1))
					{
						This->m_CurrentCDDrive++;
						OptEdit_Refresh(This, NULL);
					}
				}
				break;
				case Icon_CD150FramesBug:
				{
					opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] ^= ECDMode_150FramesBug;
					OptEdit_Refresh(This, &opt->Drivers.CDFS.PlayMode);
				}
				break;
				case Icon_CDPlayModeMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuCDPlayMode, OptMixing_CDPlayModeMenuHandler, handle);
				}
				break;
				case Icon_CDNudgeSizeUp:
				{
					if (opt->Drivers.CDFS.NudgeSize[This->m_CurrentCDDrive] < 99)
					{
						opt->Drivers.CDFS.NudgeSize[This->m_CurrentCDDrive] += 1;
						OptEdit_Refresh(This, &opt->Drivers.CDFS.NudgeSize);
					}
				}
				break;
				case Icon_CDNudgeSizeDown:
				{
					if (opt->Drivers.CDFS.NudgeSize[This->m_CurrentCDDrive] > -99)
					{
						opt->Drivers.CDFS.NudgeSize[This->m_CurrentCDDrive] -= 1;
						OptEdit_Refresh(This, &opt->Drivers.CDFS.NudgeSize);
					}
				}
				break;
				case Icon_CDPollingDelayUp:
				{
					opt->Drivers.CDFS.PollingDelay += 1;
					if (opt->Drivers.CDFS.PollingDelay > 99)
						opt->Drivers.CDFS.PollingDelay = 99;
					OptEdit_Refresh(This, &opt->Drivers.CDFS.PollingDelay);
				}
				break;
				case Icon_CDPollingDelayDown:
				{
					opt->Drivers.CDFS.PollingDelay -= 1;
					if (opt->Drivers.CDFS.PollingDelay < 5)
						opt->Drivers.CDFS.PollingDelay = 5;
					OptEdit_Refresh(This, &opt->Drivers.CDFS.PollingDelay);
				}
				break;
				case Icon_CDHideDriveNumber:
				{
					opt->Drivers.CDFS.bHideDriveNumber ^= true;
					OptEdit_Refresh(This, &opt->Drivers.CDFS.bHideDriveNumber);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_CDFS");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.CDFS.PlayMode))
			{
				Icon_Printf(wnd, Icon_CDDrive, "%d", This->m_CurrentCDDrive);
				Icon_SetHighlight(wnd, Icon_CD150FramesBug, opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] & ECDMode_150FramesBug);
				int index = 1;
				if (opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] & ECDMode_PlayTrackSWI)
					index = 3;
				else if (opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] & ECDMode_TrackByTrack)
					index = 2;
				else if (opt->Drivers.CDFS.PlayMode[This->m_CurrentCDDrive] & ECDMode_ReadAudioSWI)
					index = 0;
				Icon_SetData(wnd, Icon_CDPlayMode, Menu_ItemGetText(This->m_MenuCDPlayMode, index));
			}
			if (!phint->Data || (phint->Data == &opt->Drivers.CDFS.NudgeSize))
				Icon_Printf(wnd, Icon_CDNudgeSize, "%d", opt->Drivers.CDFS.NudgeSize[This->m_CurrentCDDrive]);
			if (!phint->Data || (phint->Data == &opt->Drivers.CDFS.PollingDelay))
				Icon_Printf(wnd, Icon_CDPollingDelay, "%d", opt->Drivers.CDFS.PollingDelay);
			if (!phint->Data || (phint->Data == &opt->Drivers.CDFS.bHideDriveNumber))
				Icon_SetHighlight(wnd, Icon_CDHideDriveNumber, opt->Drivers.CDFS.bHideDriveNumber);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptFiles_MenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = OptEdit_Get();
	ELoadAction* pvar = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			if (hit[0] >= 0)
			{
				*pvar = (ELoadAction) hit[0];
				OptEdit_Refresh(This, pvar);
			}

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptFiles_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndFiles;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_FileShowNameAsTitle:
				{
					opt->Music_Files.bShowFileAsTitle ^= true;
					OptEdit_Refresh(This, &opt->Music_Files.bShowFileAsTitle);
				}
				break;
				case Icon_FileUnloadModule:
				{
					opt->Music_Files.bUnloadModules ^= true;
					OptEdit_Refresh(This, &opt->Music_Files.bUnloadModules);
				}
				break;
				case Icon_FileDoubleClickMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuLoadAction, OptFiles_MenuHandler, &opt->Music_Files.DoubleClickAction);
				}
				break;
				case Icon_FileDropOnIconBarMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuLoadAction, OptFiles_MenuHandler, &opt->Music_Files.DropOnIconBarAction);
				}
				break;
				case Icon_FileDropOnPlayerMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuLoadAction, OptFiles_MenuHandler, &opt->Music_Files.DropOnPlayerAction);
				}
				break;
				case Icon_FileAutoOpenPlaylist:
				{
					opt->Music_Files.bAutoOpenPlaylist ^= true;
					OptEdit_Refresh(This, &opt->Music_Files.bAutoOpenPlaylist);
				}
				break;
/*				case Icon_FileAutoEnableProgram:
				{
					opt->Music_Files.bAutoEnableProgram ^= true;
					OptEdit_Refresh(This, &opt->Music_Files.bAutoEnableProgram);
				}
				break;
*/			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Files");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Music_Files.bShowFileAsTitle))
				Icon_SetHighlight(wnd, Icon_FileShowNameAsTitle, opt->Music_Files.bShowFileAsTitle);
			if (!phint->Data || (phint->Data == &opt->Music_Files.bUnloadModules))
				Icon_SetHighlight(wnd, Icon_FileUnloadModule, opt->Music_Files.bUnloadModules);
			if (!phint->Data || (phint->Data == &opt->Music_Files.DoubleClickAction))
				Icon_SetData(wnd, Icon_FileDoubleClickInfo, Menu_ItemGetText(This->m_MenuLoadAction, opt->Music_Files.DoubleClickAction));
			if (!phint->Data || (phint->Data == &opt->Music_Files.DropOnIconBarAction))
				Icon_SetData(wnd, Icon_FileDropOnIconBarInfo, Menu_ItemGetText(This->m_MenuLoadAction, opt->Music_Files.DropOnIconBarAction));
			if (!phint->Data || (phint->Data == &opt->Music_Files.DropOnPlayerAction))
				Icon_SetData(wnd, Icon_FileDropOnPlayerInfo, Menu_ItemGetText(This->m_MenuLoadAction, opt->Music_Files.DropOnPlayerAction));
			if (!phint->Data || (phint->Data == &opt->Music_Files.bAutoOpenPlaylist))
				Icon_SetHighlight(wnd, Icon_FileAutoOpenPlaylist, opt->Music_Files.bAutoOpenPlaylist);
//			if (!phint->Data || (phint->Data == &opt->Music_Files.bAutoEnableProgram))
//				Icon_SetHighlight(wnd, Icon_FileAutoEnableProgram, opt->Music_Files.bAutoEnableProgram);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptFileTypes_EventHandler(void* handle, const Event* e)
{
	IGNORE(handle);

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_TypesHelp:
				{
					HTask dummy;

					StartTask(&dummy, SPrintf("Filer_Run %s", throw_File_FromRes("ModInfo")));
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_FileTypes");
				return EListenerAction_StopEvent;
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptAMPlayer_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndAMPlayer;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_AMPlayerBufferDown:
				{
					opt->Drivers.AMPlayer.BufferSize >>= 1;
					if (opt->Drivers.AMPlayer.BufferSize < 4)
						opt->Drivers.AMPlayer.BufferSize = 4;
					OptEdit_Refresh(This, &opt->Drivers.AMPlayer.BufferSize);
				}
				break;
				case Icon_AMPlayerBufferUp:
				{
					opt->Drivers.AMPlayer.BufferSize <<= 1;
					if (opt->Drivers.AMPlayer.BufferSize > 512)
						opt->Drivers.AMPlayer.BufferSize = 512;
					OptEdit_Refresh(This, &opt->Drivers.AMPlayer.BufferSize);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_AMPlayer");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.AMPlayer.BufferSize))
				Icon_Printf(wnd, Icon_AMPlayerBuffer, "%d", opt->Drivers.AMPlayer.BufferSize);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptDiskSample_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndDiskSample;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_DiskInputBufferDown:
				{
					opt->Drivers.DiskSample.InputBufferSize >>= 1;
					if (opt->Drivers.DiskSample.InputBufferSize < 4)
						opt->Drivers.DiskSample.InputBufferSize = 4;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.InputBufferSize);
				}
				break;
				case Icon_DiskInputBufferUp:
				{
					opt->Drivers.DiskSample.InputBufferSize <<= 1;
					if (opt->Drivers.DiskSample.InputBufferSize > 2048)
						opt->Drivers.DiskSample.InputBufferSize = 2048;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.InputBufferSize);
				}
				break;
				case Icon_DiskOutputBufferDown:
				{
					opt->Drivers.DiskSample.OutputBufferSize >>= 1;
					if (opt->Drivers.DiskSample.OutputBufferSize < 64)
						opt->Drivers.DiskSample.OutputBufferSize = 64;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.OutputBufferSize);
				}
				break;
				case Icon_DiskOutputBufferUp:
				{
					opt->Drivers.DiskSample.OutputBufferSize <<= 1;
					if (opt->Drivers.DiskSample.OutputBufferSize > 4096)
						opt->Drivers.DiskSample.OutputBufferSize = 4096;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.OutputBufferSize);
				}
				break;
				case Icon_DiskFreeVolume:
				{
					opt->Drivers.DiskSample.bFreeVolume ^= true;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.bFreeVolume);
				}
				break;
				case Icon_DiskEnableInterrupts:
				{
					opt->Drivers.DiskSample.bEnableInterrupts ^= true;
					OptEdit_Refresh(This, &opt->Drivers.DiskSample.bEnableInterrupts);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_DiskSample");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.DiskSample.InputBufferSize))
				Icon_Printf(wnd, Icon_DiskInputBuffer, "%d", opt->Drivers.DiskSample.InputBufferSize);
			if (!phint->Data || (phint->Data == &opt->Drivers.DiskSample.OutputBufferSize))
				Icon_Printf(wnd, Icon_DiskOutputBuffer, "%d", opt->Drivers.DiskSample.OutputBufferSize);
			if (!phint->Data || (phint->Data == &opt->Drivers.DiskSample.bFreeVolume))
				Icon_SetHighlight(wnd, Icon_DiskFreeVolume, opt->Drivers.DiskSample.bFreeVolume);
			if (!phint->Data || (phint->Data == &opt->Drivers.DiskSample.bEnableInterrupts))
				Icon_SetHighlight(wnd, Icon_DiskEnableInterrupts, opt->Drivers.DiskSample.bEnableInterrupts);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptPlayIt_FormatMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// PlayIt Menus
			opt->Drivers.PlayIt.Format = hit[0];
			if (opt->Drivers.PlayIt.Format > 4) opt->Drivers.PlayIt.Format += 3;
			OptEdit_Refresh(This, &opt->Drivers.PlayIt.Format);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptPlayIt_FreqMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// PlayIt Menus
			sscanf(Menu_ItemGetText(This->m_MenuPlayItFreq, hit[0]), "%d Hz", &opt->Drivers.PlayIt.Frequency);
			OptEdit_Refresh(This, &opt->Drivers.PlayIt.Frequency);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptPlayIt_ChannelsMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// PlayIt Menus
			opt->Drivers.PlayIt.Channels = hit[0] + 1;
			OptEdit_Refresh(This, &opt->Drivers.PlayIt.Channels);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptPlayIt_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndPlayIt;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_PlayItInterpretUnknown:
				{
					opt->Drivers.PlayIt.bInterpretUnknown ^= true;
					OptEdit_Refresh(This, &opt->Drivers.PlayIt.bInterpretUnknown);
				}
				break;
				case Icon_PlayItBufferDown:
				{
					opt->Drivers.PlayIt.BufferSize -= 4;
					if (opt->Drivers.PlayIt.BufferSize < 4)
						opt->Drivers.PlayIt.BufferSize = 4;
					OptEdit_Refresh(This, &opt->Drivers.PlayIt.BufferSize);
				}
				break;
				case Icon_PlayItBufferUp:
				{
					opt->Drivers.PlayIt.BufferSize += 4;
					if (opt->Drivers.PlayIt.BufferSize > 512)
						opt->Drivers.PlayIt.BufferSize = 512;
					OptEdit_Refresh(This, &opt->Drivers.PlayIt.BufferSize);
				}
				break;
				case Icon_PlayItFormatMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuPlayItFormat, OptPlayIt_FormatMenuHandler, handle);
				}
				break;
				case Icon_PlayItFreqMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuPlayItFreq, OptPlayIt_FreqMenuHandler, handle);
				}
				break;
				case Icon_PlayItChannelsMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuPlayItChannels, OptPlayIt_ChannelsMenuHandler, handle);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_PlayIt");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.PlayIt.BufferSize))
				Icon_Printf(wnd, Icon_PlayItBuffer, "%d", opt->Drivers.PlayIt.BufferSize);
			if (!phint->Data || (phint->Data == &opt->Drivers.PlayIt.bInterpretUnknown))
				Icon_SetHighlight(wnd, Icon_PlayItInterpretUnknown, opt->Drivers.PlayIt.bInterpretUnknown);
			if (!phint->Data || (phint->Data == &opt->Drivers.PlayIt.Format))
			{
				int val = opt->Drivers.PlayIt.Format;
				if (val > 4) val -= 3;
				Icon_SetData(wnd, Icon_PlayItFormat, Menu_ItemGetText(This->m_MenuPlayItFormat, val));
			}
			if (!phint->Data || (phint->Data == &opt->Drivers.PlayIt.Frequency))
				Icon_Printf(wnd, Icon_PlayItFreq, "%d Hz", opt->Drivers.PlayIt.Frequency);
			if (!phint->Data || (phint->Data == &opt->Drivers.PlayIt.Channels))
				Icon_SetData(wnd, Icon_PlayItChannels, Menu_ItemGetText(This->m_MenuPlayItChannels, opt->Drivers.PlayIt.Channels - 1));
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptTimPlayer_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndTimPlayer;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_TimFreeVolume:
				{
					opt->Drivers.TimPlayer.bFreeVolume ^= true;
					OptEdit_Refresh(This, &opt->Drivers.TimPlayer.bFreeVolume);
				}
				break;
				case Icon_TimEnableInterrupts:
				{
					opt->Drivers.TimPlayer.bEnableInterrupts ^= true;
					OptEdit_Refresh(This, &opt->Drivers.TimPlayer.bEnableInterrupts);
				}
				break;
				case Icon_TimPolyphonyDown:
				{
					opt->Drivers.TimPlayer.Polyphony--;
					if (opt->Drivers.TimPlayer.Polyphony < 4)
						opt->Drivers.TimPlayer.Polyphony = 4;
					OptEdit_Refresh(This, &opt->Drivers.TimPlayer.Polyphony);
				}
				break;
				case Icon_TimPolyphonyUp:
				{
					opt->Drivers.TimPlayer.Polyphony++;
					if (opt->Drivers.TimPlayer.Polyphony > 128)
						opt->Drivers.TimPlayer.Polyphony = 128;
					OptEdit_Refresh(This, &opt->Drivers.TimPlayer.Polyphony);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_TimPlayer");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Drivers.TimPlayer.bFreeVolume))
				Icon_SetHighlight(wnd, Icon_TimFreeVolume, opt->Drivers.TimPlayer.bFreeVolume);
			if (!phint->Data || (phint->Data == &opt->Drivers.TimPlayer.bEnableInterrupts))
				Icon_SetHighlight(wnd, Icon_TimEnableInterrupts, opt->Drivers.TimPlayer.bEnableInterrupts);
			if (!phint->Data || (phint->Data == &opt->Drivers.TimPlayer.Polyphony))
				Icon_Printf(wnd, Icon_TimPolyphony, "%d", opt->Drivers.TimPlayer.Polyphony);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static void OptEdit_BuildFreqMenu(COptEdit* This)
{
	_kernel_swi_regs regs;
	int i;
	char pbuffer[12];

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	// Sound_Mode, 0 read mode
	regs.r[0] = 0;
	if (_kernel_swi(0x60144, &regs, &regs) != NULL)
		This->m_Freqs = 0;
	else if (regs.r[0] == 0)
		This->m_Freqs = 0;
	else
	{
		// Sound_SampleRate, 0 read number of freqs
		regs.r[0] = 0;
		if (_kernel_swi(0x60146, &regs, &regs) != NULL)
			This->m_Freqs = 0;
		else
			This->m_Freqs = regs.r[1];
	}

	if (This->m_Freqs <= 0)
	{
		// 8-bit mode
		This->m_Freq[0] = 1000000/72;
		This->m_Freq[1] = 1000000/64;
		This->m_Freq[2] = 1000000/56;
		This->m_Freq[3] = 1000000/48;
		This->m_Freq[4] = 1000000/40;
		This->m_Freq[5] = 1000000/32;
		This->m_Freq[6] = 1000000/24;
		This->m_Freqs = 7;
	}
	else
	{
		// 16-bit mode
		for (i = 0; i < This->m_Freqs; i++)
		{
			// Sound_SampleRate, 2 read freq index
			regs.r[0] = 2;
			regs.r[1] = i + 1;
			_kernel_swi(0x60146, &regs, &regs);
			This->m_Freq[i] = regs.r[2]/1024;
		}
	}

	try
	{
		for (i = 0; i < This->m_Freqs; i++)
		{
			snprintf(pbuffer, sizeof(pbuffer), "%d Hz", This->m_Freq[i]);

			if (This->m_MenuTmp)
				throw_Menu_InsertItem(This->m_MenuTmp, pbuffer, -1);
			else
				This->m_MenuTmp = throw_New_Menu(Msg_Lookup("Frequency:"), pbuffer);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end
}

static void OptEdit_SelectFreq(COptEdit* This, const int index)
{
	Event e;

	e.Type = EEvent_OptionMixingFrequency;
	e.pData = &This->m_Options.Music_Files.Frequency;

	This->m_Options.Music_Files.Frequency = This->m_Freq[index];
	Icon_Printf(This->m_wndMixing, Icon_Frequency, "%d", This->m_Options.Music_Files.Frequency);

	Task_ProcessEvent(&e);
}

static void OptEdit_SelectDMASize(COptEdit* This, const int index)
{
	Event e;

	e.Type = EEvent_OptionMixingDMASize;
	e.pData = &This->m_Options.Music_Files.DMASize;

	This->m_Options.Music_Files.DMASize = DMASizes[index];
	if (This->m_Options.Music_Files.DMASize)
		Icon_Printf(This->m_wndMixing, Icon_DMASize, "%d", This->m_Options.Music_Files.DMASize);
	else
		Icon_Printf(This->m_wndMixing, Icon_DMASize, "-");

	Task_ProcessEvent(&e);
}

static void OptEdit_SelectFreqMultiply(COptEdit* This, const int index)
{
	Event e;

	e.Type = EEvent_OptionMixingFrequency;
	e.pData = &This->m_Options.Music_Files.Frequency;

	This->m_Options.Music_Files.FreqMultiply = index;

	Task_ProcessEvent(&e);
}

static EListenerAction OptMixing_FreqMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// Freq Menu
			OptEdit_SelectFreq(This, hit[0]);
			OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptMixing_DMASizeMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// DMASize Menu
			OptEdit_SelectDMASize(This, hit[0]);
			OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptMixing_FreqMultiplyMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// Freq Menu
			OptEdit_SelectFreqMultiply(This, hit[0]);
			OptEdit_Refresh(This, &This->m_Options.Music_Files.FreqMultiply);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptMixing_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndMixing;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_FrequencyPopup:
				{
					OptEdit_BuildFreqMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptMixing_FreqMenuHandler, handle);
				}
				break;
				case Icon_FreqMultiplyPopup:
				{
					Menu_Popup(m->w, m->i, This->m_MenuFreqMultiply, OptMixing_FreqMultiplyMenuHandler, handle);
				}
				break;
				case Icon_DMASizePopup:
				{
					Menu_Popup(m->w, m->i, This->m_MenuDMASize, OptMixing_DMASizeMenuHandler, handle);
				}
				break;
				case Icon_Interpol:
				{
					Event e;

					e.Type = EEvent_OptionMixingInterpol;
					e.pData = &opt->Music_Files.bInterpol;

					opt->Music_Files.bInterpol ^= true;

					OptEdit_Refresh(This, &opt->Music_Files.bInterpol);

					Task_ProcessEvent(&e);
				}
				break;
				case Icon_VolumeRamping:
				{
					Event e;

					e.Type = EEvent_OptionMixingVolRamping;
					e.pData = &opt->Music_Files.bVolumeRamping;

					opt->Music_Files.bVolumeRamping ^= true;

					OptEdit_Refresh(This, &opt->Music_Files.bVolumeRamping);

					Task_ProcessEvent(&e);
				}
				break;
				case Icon_IgnoreSystemVolume:
				{
					opt->Player.bIgnoreSystemVolume ^= 1;
					OptEdit_Refresh(This, &opt->Player.bIgnoreSystemVolume);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Mixing");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Music_Files.Frequency))
				Icon_Printf(wnd, Icon_Frequency, "%d", opt->Music_Files.Frequency);
			if (!phint->Data || (phint->Data == &opt->Music_Files.FreqMultiply))
				Icon_SetData(wnd, Icon_FreqMultiply, Menu_ItemGetText(This->m_MenuFreqMultiply, opt->Music_Files.FreqMultiply));
			if (!phint->Data || (phint->Data == &opt->Music_Files.DMASize))
			{
				if (This->m_Options.Music_Files.DMASize)
					Icon_Printf(This->m_wndMixing, Icon_DMASize, "%d", This->m_Options.Music_Files.DMASize);
				else
					Icon_Printf(This->m_wndMixing, Icon_DMASize, "-");
			}
			if (!phint->Data || (phint->Data == &opt->Music_Files.bInterpol))
				Icon_SetHighlight(wnd, Icon_Interpol, opt->Music_Files.bInterpol);
			if (!phint->Data || (phint->Data == &opt->Music_Files.bVolumeRamping))
				Icon_SetHighlight(wnd, Icon_VolumeRamping, opt->Music_Files.bVolumeRamping);
			if (!phint->Data || (phint->Data == &opt->Player.bIgnoreSystemVolume))
				Icon_SetHighlight(wnd, Icon_IgnoreSystemVolume, opt->Player.bIgnoreSystemVolume);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

void Balance_Set(COptEdit* This, int value)
{
	Event e;

	e.Type = EEvent_OptionMixingBalance;
	e.pData = &This->m_Options.Music_Files.Balance;

	if (value < 0) value = 0;
	if (value > 255) value = 255;
	This->m_Options.Music_Files.Balance = value;

	Slider_Set(This->m_wndStereo, Icon_Balance, Icon_Balance2, 0, (float)value/255);

	OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

	Task_ProcessEvent(&e);
}

static void Balance_Update(void* pData, int value, int maxvalue, ESlider_UpdateMode mode)
{
	if (mode == ESlider_UpdateMode_Cancel) return;

	Balance_Set(pData, (255 * value)/maxvalue);
}

void Separation_Set(COptEdit* This, int value)
{
	Event e;

	e.Type = EEvent_OptionMixingStereo;
	e.pData = &This->m_Options.Music_Files.StereoSeparation;

	if (value < -1024) value = -1204;
	if (value > 1024) value = 1024;
	This->m_Options.Music_Files.StereoSeparation = value;

	Slider_Set(This->m_wndStereo, Icon_Separation, Icon_Separation2, 0, (float)((int)(value+1024))/2048);

	OptEdit_SetModified(This, !OptEdit_IsCurrent(This));

	Task_ProcessEvent(&e);
}

static void Separation_Update(void* pData, int value, int maxvalue, ESlider_UpdateMode mode)
{
	if (mode == ESlider_UpdateMode_Cancel) return;

	Separation_Set(pData, (2048 * value)/maxvalue - 1024);
}

static EListenerAction OptStereo_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_Balance:
				case Icon_Balance2:
				{
					Slider_Update(m->w, Icon_Balance, Icon_Balance2, 0, m, Balance_Update, This);
				}
				break;
				case Icon_BalanceLeft:
				{
					Balance_Set(This, 0);
				}
				break;
				case Icon_BalanceCenter:
				{
					Balance_Set(This, 128);
				}
				break;
				case Icon_BalanceRight:
				{
					Balance_Set(This, 255);
				}
				break;
				case Icon_Separation:
				case Icon_Separation2:
				{
					Slider_Update(m->w, Icon_Separation, Icon_Separation2, 0, m, Separation_Update, This);
				}
				break;
				case Icon_SeparationM400:
				{
					Separation_Set(This, -1024);
				}
				break;
				case Icon_Separation0:
				{
					Separation_Set(This, 0);
				}
				break;
				case Icon_Separation100:
				{
					Separation_Set(This, 256);
				}
				break;
				case Icon_Separation400:
				{
					Separation_Set(This, 1024);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Stereo");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_WindowScroll:
		{
			const Event_WindowScroll* sc = e->pData;
			int val = (sc->dx & 3) ? 0: (sc->dx >> 2);
			val += (sc->dy & 3) ? 0: (sc->dy >> 2);
			if (val)
			{
				switch(sc->icon)
				{
					case Icon_Balance-1:
					case Icon_Balance:
					case Icon_Balance2:
					{
						Balance_Set(This, This->m_Options.Music_Files.Balance + 8*val);
					}
					break;
					case Icon_Separation-1:
					case Icon_Separation:
					case Icon_Separation2:
					{
						Separation_Set(This, This->m_Options.Music_Files.StereoSeparation + 8*val);
					}
					break;
				}
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Music_Files.Balance))
				Balance_Set(This, opt->Music_Files.Balance);
			if (!phint->Data || (phint->Data == &opt->Music_Files.StereoSeparation))
				Separation_Set(This, opt->Music_Files.StereoSeparation);
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static void EqualizerBand_Set(COptEdit* This, int band, int value)
{
	int icon = Icon_BandSlider + 3 * band;
	double lvalue;

	if (value < 1<<14) value = 1<<14;
	if (value > 1<<18) value = 1<<18;
	This->m_Options.Equalizer.Params.Gain[band] = value;
	lvalue = value/(double)(1<<14);
	lvalue = log(lvalue)/log(2)/4;

	Slider_Set(This->m_wndEqualizer, icon + 1, icon + 2, ESliderType_Vertical, lvalue);
}

typedef struct
{
	COptEdit*	pEdit;
	int		band;
} CEqUpdate;

static void EqualizerBand_Update(void* pData, int value, int maxval, ESlider_UpdateMode mode)
{
	Event e;
	CEqUpdate* pEq = pData;
	double lvalue;
	int gain;

	if (mode == ESlider_UpdateMode_Cancel) return;

	value = (value<<2) - (maxval<<1);
	lvalue = 16 + value/(double)maxval;
	gain = (int) pow(2, lvalue);

	if (pEq->pEdit->m_Options.Equalizer.Params.Gain[pEq->band] != gain)
	{
		EqualizerBand_Set(pEq->pEdit, pEq->band, gain);

		e.Type = EEvent_OptionMixingEqualizer;
		e.pData = &pEq->pEdit->m_Options;

		Task_ProcessEvent(&e);
	}

	OptEdit_Refresh(pEq->pEdit, NULL);
}

static const char* EquReadStyles = "Choices:DigitalCD.Equalizer";
const char* EquWriteStyles = "<Choices$Write>.DigitalCD.Equalizer";

void OptEdit_ReadEqualizerStyle(COptEdit* This, const char* style)
{
	int i;

	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		This->m_EqualizerStyle.Gain[i] = FitInRange(Choices_ReadIntFrom(EquReadStyles, style, SPrintf(Var_Gain, i), 1<<16), 1<<14, 1<< 18);
}

static void OptEdit_WriteEqualizerStyle(COptEdit* This, const char* style)
{
	int i;

	This->m_EqualizerStyle = This->m_Options.Equalizer.Params;

	for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
		Choices_WriteTo(EquWriteStyles, style, SPrintf(Var_Gain, i), "%d", This->m_EqualizerStyle.Gain[i]);
}

static void OptEdit_DeleteEqualizerStyle(COptEdit* This, const char* style)
{
	// Stop warnings
	IGNORE(This);

	Choices_RemoveSectionFrom(EquWriteStyles, style);
}

static void OptEdit_BuildEqualizerMenu(COptEdit* This)
{
	FILE* file;
	char* pId;
	static char string[256];

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	// scan the CDs file for the corresponding ids
	if ((file = fopen(EquReadStyles, "rb")) == NULL)
		return;

	try
	{
		while(true)
		{
			File_FindEndOfSection(file);

			// extract a line
			if (fgets(string, 255, file) == NULL) break;

			// find ']' in string
			pId = strchr(string + 1, ']');

			// loop if not found
			if ((pId == NULL) || (pId == string + 1)) continue;

			*pId = 0;

			if (This->m_MenuTmp)
				throw_Menu_InsertItem(This->m_MenuTmp, string+1, -1);
			else
				This->m_MenuTmp = throw_New_Menu(Msg_Lookup("Style:"), string+1);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end

	fclose(file);
}

static void OptEdit_SelectEqualizer(COptEdit* This, const char* style)
{
	COptions* opt = &This->m_Options;
	Event e;

	e.Type = EEvent_OptionMixingEqualizer;
	e.pData = &This->m_Options;

	mem_setstring(&opt->Equalizer.Name, style);
	OptEdit_ReadEqualizerStyle(This, style);
	opt->Equalizer.Params = This->m_EqualizerStyle;
	opt->Equalizer.bActive = true;
 	OptEdit_Refresh(This, NULL);

	Task_ProcessEvent(&e);
}

static int Equ_Mark[5] = {1<<16,1<<14,1<<18,1<<15,1<<17};

static EListenerAction OptEqualizer_MenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			// Equalizer Menu
			OptEdit_SelectEqualizer(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptEqualizer_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndEqualizer;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_EqualizerMenu:
				{
					OptEdit_BuildEqualizerMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptEqualizer_MenuHandler, handle);
				}
				break;
				case Icon_EqualizerSave:
				{
					char* name = mem_allocstring(Icon_GetData(m->w, Icon_EqualizerStyle));
					if (name)
					{
						String_StripBlanks(name);
						if (name[0]) OptEdit_WriteEqualizerStyle(This, name);
						mem_free(name);
					}
				}
				break;
				case Icon_EqualizerDelete:
				{
					char* name = mem_allocstring(Icon_GetData(m->w, Icon_EqualizerStyle));
					if (name)
					{
						String_StripBlanks(name);
						if (name[0]) OptEdit_DeleteEqualizerStyle(This, name);
						mem_free(name);
						Icon_SetData(m->w, Icon_EqualizerStyle, NULL);
					}
				}
				break;
				case Icon_Equalizer:
				{
					Event e;

					e.Type = EEvent_OptionMixingEqualizer;
					e.pData = opt;

					opt->Equalizer.bActive ^= true;
					OptEdit_Refresh(This, &opt->Equalizer.bActive);

					Task_ProcessEvent(&e);
				}
				break;
				case Icon_0dB:
				{
					Event e;
					int i;

					for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
						EqualizerBand_Set(This, i, Equ_Mark[0]);
					OptEdit_Refresh(This, NULL);

					e.Type = EEvent_OptionMixingEqualizer;
					e.pData = &This->m_Options;

					Task_ProcessEvent(&e);
				}
				break;
				default:
				{
					int band = (m->i - Icon_BandSlider)/3;
					int icon = (m->i - Icon_BandSlider)%3;

					if ((band >= 0)
					&&  (band < Opt_MAX_EQ_BANDS)
					&&  (m->i - 3*band))
					{
						CEqUpdate Eq;

						Eq.pEdit =This;
						Eq.band = band;
						icon = Icon_BandSlider + 3 * band;
						Slider_Update(m->w, icon + 1, icon + 2, ESliderType_Vertical, m, EqualizerBand_Update, &Eq);
					}

					band = (m->i - Icon_BandMark)/5;
					icon = (m->i - Icon_BandMark)%5;

					if ((band >= 0)
					&&  (band < Opt_MAX_EQ_BANDS))
					{
						Event e;

						EqualizerBand_Set(This, band, Equ_Mark[icon]);

						e.Type = EEvent_OptionMixingEqualizer;
						e.pData = &This->m_Options;

						Task_ProcessEvent(&e);
					}
					OptEdit_Refresh(This, NULL);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Equalizer");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_WindowScroll:
		{
			const Event_WindowScroll* sc = e->pData;
			int val = (sc->dx & 3) ? 0: (sc->dx >> 2);
			val += (sc->dy & 3) ? 0: (sc->dy >> 2);
			if (val)
			{
				int band = (sc->icon - Icon_BandSlider)/3;

				if ((band >= 0)
				&&  (band < Opt_MAX_EQ_BANDS))
				{
					Event e;
					double value = pow(2, ((double) val) / 12.);
					value*= (double) This->m_Options.Equalizer.Params.Gain[band];

					EqualizerBand_Set(This, band, (int) value);

					e.Type = EEvent_OptionMixingEqualizer;
					e.pData = &This->m_Options;

					Task_ProcessEvent(&e);
				}
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;
			int i;

			if (!phint->Data || (phint->Data == &opt->Equalizer.Name))
			{
				Icon_SetData(wnd, Icon_EqualizerStyle, opt->Equalizer.Name);
				if (memcmp(&opt->Equalizer.Params, &This->m_EqualizerStyle, sizeof(This->m_EqualizerStyle)))
					Icon_SetState(wnd, Icon_EqualizerStyle, 0x0B000000, 0xFF000000);
				else
					Icon_SetState(wnd, Icon_EqualizerStyle, 0x07000000, 0xFF000000);
			}
			if (!phint->Data || (phint->Data == &opt->Equalizer.bActive))
				Icon_SetHighlight(wnd, Icon_Equalizer, opt->Equalizer.bActive);

			for (i = 0; i < Opt_MAX_EQ_BANDS; i++)
			{
				if (!phint->Data || (phint->Data == &opt->Equalizer.Params.Gain[i]))
					EqualizerBand_Set(This, i, opt->Equalizer.Params.Gain[i]);
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static EListenerAction OptIconbar_MenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = OptEdit_Get();
	int* pvar = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			if (hit[0] > 0)
			{
				*pvar = Cmd_OpenChoices + hit[0] - 1;
				OptEdit_Refresh(This, pvar);
			}
			else if (hit[1] >= 0)
			{
				*pvar = hit[1];
				OptEdit_Refresh(This, pvar);
			}

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptIconbar_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndIconbar;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_SelectPopup:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Select[0]);
				}
				break;
				case Icon_SelectPopup+3:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Select[1]);
				}
				break;
				case Icon_SelectPopup+6:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Select[2]);
				}
				break;
				case Icon_AdjustPopup:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Adjust[0]);
				}
				break;
				case Icon_AdjustPopup+3:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Adjust[1]);
				}
				break;
				case Icon_AdjustPopup+6:
				{
					Menu_Popup(m->w, m->i, DigitalCD.MenuOpen, OptIconbar_MenuHandler, &opt->Iconbar.Adjust[2]);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_IconBar");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Iconbar.Select[0]))
			{
				if (opt->Iconbar.Select[0] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_SelectName, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Select[0]));
				else
					Icon_SetData(wnd, Icon_SelectName, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Select[0] - Cmd_OpenChoices + 1));
			}
			if (!phint->Data || (phint->Data == &opt->Iconbar.Select[1]))
			{
				if (opt->Iconbar.Select[1] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_SelectName+3, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Select[1]));
				else
					Icon_SetData(wnd, Icon_SelectName+3, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Select[1] - Cmd_OpenChoices + 1));
			}
			if (!phint->Data || (phint->Data == &opt->Iconbar.Select[2]))
			{
				if (opt->Iconbar.Select[2] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_SelectName+6, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Select[2]));
				else
					Icon_SetData(wnd, Icon_SelectName+6, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Select[2] - Cmd_OpenChoices + 1));
			}
			if (!phint->Data || (phint->Data == &opt->Iconbar.Adjust[0]))
			{
				if (opt->Iconbar.Adjust[0] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_AdjustName, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Adjust[0]));
				else
					Icon_SetData(wnd, Icon_AdjustName, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Adjust[0] - Cmd_OpenChoices + 1));
			}
			if (!phint->Data || (phint->Data == &opt->Iconbar.Adjust[1]))
			{
				if (opt->Iconbar.Adjust[1] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_AdjustName+3, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Adjust[1]));
				else
					Icon_SetData(wnd, Icon_AdjustName+3, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Adjust[1] - Cmd_OpenChoices + 1));
			}
			if (!phint->Data || (phint->Data == &opt->Iconbar.Adjust[2]))
			{
				if (opt->Iconbar.Adjust[2] < Cmd_OpenChoices)
					Icon_SetData(wnd, Icon_AdjustName+6, Menu_ItemGetText(DigitalCD.MenuPlayers, opt->Iconbar.Adjust[2]));
				else
					Icon_SetData(wnd, Icon_AdjustName+6, Menu_ItemGetText(DigitalCD.MenuOpen, opt->Iconbar.Adjust[2] - Cmd_OpenChoices + 1));
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static bool PlugIn_Accept(const char* pnewfile)
{
	// Must be a directory
	if (File_GetFileType(pnewfile) < 0x01000)
		return false;

	// Should contain at least a !Run file
	if (File_GetFileType(SPrintf("%s.!Run", pnewfile)) == -2)
		return false;

	return true;
}

static void OptEdit_CurrentDesktopPlugIn(COptEdit* This, int index, int offset)
{
	int hit[10];
	const char* pname;

	// Build list a menu as I've got plenty of API for that
	Delete_Menu(This->m_MenuTmp, true);
	This->m_MenuTmp = FileMenu_Build(Msg_Lookup("PlugIns"), Dir_PlugIn_Desktop, PlugIn_Accept, false);

	hit[0] = index;
	hit[1] = -1;

	if (offset > 0)
		FileMenu_NextHit(This->m_MenuTmp, hit);
	else if (offset < 0)
		FileMenu_PreviousHit(This->m_MenuTmp, hit);

	pname = FileMenu_Decode(This->m_MenuTmp, hit);

	if (strcmp(pname, Icon_GetData(This->m_wndPlugIns, Icon_DesktopPlugInName)))
	{
		This->m_CurrentDesktopPlugIn = hit[0];
		Icon_SetData(This->m_wndPlugIns, Icon_DesktopPlugInName, pname);
		OptEdit_Refresh(This, &This->m_CurrentDesktopPlugIn);
	}
}

static const char* OptEdit_LocateDesktopPlugIn(COptEdit* This, const char* pname)
{
	const char* psubname = This->m_Options.PlugIns.pDesktop;
	int len = strlen(pname);

	if (!len) return NULL;

	while ((psubname = strstr(psubname, pname)) != NULL)
	{
		// not just a substring ?
		if ((psubname[len] == '*')
		||  (psubname[len] == 0))
			return psubname;
		psubname += len + 1;
	}

	return NULL;
}

static void OptEdit_EnableDesktopPlug(COptEdit* This, const char* pname, bool b)
{
	COptions* opt = &This->m_Options;
	char* psubname = (char*) OptEdit_LocateDesktopPlugIn(This, pname);

	if (b && !psubname)
	{
		// Insert
		if (This->m_Options.PlugIns.pDesktop && opt->PlugIns.pDesktop[0])
			psubname = SPrintf("%s*%s", opt->PlugIns.pDesktop, pname);
		else
			psubname = (char*) pname;
		psubname = mem_allocstring(psubname);
		if (psubname)
		{
			mem_free(opt->PlugIns.pDesktop);
			opt->PlugIns.pDesktop = psubname;
		}
		OptEdit_Refresh(This, &opt->PlugIns.pDesktop);
	}
	else if (!b && psubname)
	{
		// Remove
		int len = strlen(pname);
		if (opt->PlugIns.pDesktop == psubname)
		{
			psubname += len;
			if (psubname[0] == '*') psubname++;
			memmove((char*) opt->PlugIns.pDesktop, psubname, strlen(psubname) + 1);
		}
		else
		{
			// There is '*' just before to remove too
			memmove(psubname - 1, psubname + len, strlen(psubname + len) + 1);
		}
		OptEdit_Refresh(This, &opt->PlugIns.pDesktop);
	}
}

static void OptEdit_BuildFullPlugInMenu(COptEdit* This)
{
	Delete_Menu(This->m_MenuTmp, true);
	This->m_MenuTmp = FileMenu_Build(Msg_Lookup("PlugIns"), Dir_PlugIn_FullScreen, PlugIn_Accept, false);
}

static void OptEdit_SelectFullPlugIn(COptEdit* This, const char* name)
{
	COptions* opt = &This->m_Options;

	mem_setstring(&opt->PlugIns.pFullScreen, name);
	OptEdit_Refresh(This, &opt->PlugIns.pFullScreen);
}

static EListenerAction OptPlugIns_FullScreenMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectFullPlugIn(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptPlugIns_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndPlugIns;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_PlugInAutoStart:
				{
					opt->PlugIns.bAutoStart ^= true;
					OptEdit_Refresh(This, &opt->PlugIns.bAutoStart);
				}
				break;
				case Icon_RescaleWithTrackVolume:
				{
					opt->PlugIns.bRescaleWithTrackVolume ^= true;
					OptEdit_Refresh(This, &opt->PlugIns.bRescaleWithTrackVolume);
				}
				break;
				case Icon_DesktopPlugIn:
				{
					opt->PlugIns.bDesktop = true;
					OptEdit_Refresh(This, &opt->PlugIns.bDesktop);
				}
				break;
				case Icon_DesktopPlugInDown:
				case Icon_DesktopPlugInUp:
				{
					int dir = 1;
					if (m->i == Icon_DesktopPlugInUp) dir = -dir;
					if (m->but & EBut_Adjust) dir = -dir;
					OptEdit_CurrentDesktopPlugIn(This, This->m_CurrentDesktopPlugIn, dir);
				}
				break;
				case Icon_DesktopPlugInEnable:
				{
					bool b = (Icon_GetState(m->w, m->i, EIcon_Selected) == EIcon_Selected);
					OptEdit_EnableDesktopPlug(This, Icon_GetData(wnd, Icon_DesktopPlugInName), b);
				}
				break;
				case Icon_DesktopPlugInSetup:
				{
					const char* pname = Icon_GetData(wnd, Icon_DesktopPlugInName);
					if (pname[0])
						Filer_Run(Dir_PlugIn_Desktop, pname, File_Setup);
				}
				break;
				case Icon_DesktopPlugInInfo:
				{
					const char* pname = Icon_GetData(wnd, Icon_DesktopPlugInName);
					if (pname[0])
						Filer_Run(Dir_PlugIn_Desktop, pname, File_Help);
				}
				break;
				case Icon_FullPlugIn:
				{
					opt->PlugIns.bDesktop = false;
					OptEdit_Refresh(This, &opt->PlugIns.bDesktop);
				}
				break;
				case Icon_FullPlugInPopup:
				{
					OptEdit_BuildFullPlugInMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptPlugIns_FullScreenMenuHandler, handle);
				}
				break;
				case Icon_FullPlugInSetup:
				{
					if (opt->PlugIns.pFullScreen[0])
						Filer_Run(Dir_PlugIn_FullScreen, opt->PlugIns.pFullScreen, File_Setup);
				}
				break;
				case Icon_FullPlugInInfo:
				{
					if (opt->PlugIns.pFullScreen[0])
						Filer_Run(Dir_PlugIn_FullScreen, opt->PlugIns.pFullScreen, File_Help);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_PlugIns");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->PlugIns.bAutoStart))
				Icon_SetHighlight(wnd, Icon_PlugInAutoStart, opt->PlugIns.bAutoStart);
			if (!phint->Data || (phint->Data == &opt->PlugIns.bRescaleWithTrackVolume))
				Icon_SetHighlight(wnd, Icon_RescaleWithTrackVolume, opt->PlugIns.bRescaleWithTrackVolume);
			if (!phint->Data || (phint->Data == &opt->PlugIns.bDesktop))
			{
				Icon_SetHighlight(wnd, Icon_DesktopPlugIn, opt->PlugIns.bDesktop);
				Icon_SetHighlight(wnd, Icon_FullPlugIn, !opt->PlugIns.bDesktop);
			}
			if (!phint->Data || (phint->Data == &This->m_CurrentDesktopPlugIn))
			{
				const char* pname;

				OptEdit_CurrentDesktopPlugIn(This, This->m_CurrentDesktopPlugIn, 0);
				pname = Icon_GetData(wnd, Icon_DesktopPlugInName);

				if (pname[0])
				{
					Icon_SetDimmed(wnd, Icon_DesktopPlugInInfo, !Filer_Find(Dir_PlugIn_Desktop, pname, File_Help));
					Icon_SetDimmed(wnd, Icon_DesktopPlugInSetup, !Filer_Find(Dir_PlugIn_Desktop, pname, File_Setup));
					Icon_SetHighlight(wnd, Icon_DesktopPlugInEnable, OptEdit_LocateDesktopPlugIn(This, pname) != NULL);
					Icon_SetDimmed(wnd, Icon_DesktopPlugInEnable, false);
				}
				else
				{
					Icon_SetDimmed(wnd, Icon_DesktopPlugInInfo, true);
					Icon_SetDimmed(wnd, Icon_DesktopPlugInSetup, true);
					Icon_SetHighlight(wnd, Icon_DesktopPlugInEnable, false);
					Icon_SetDimmed(wnd, Icon_DesktopPlugInEnable, true);
				}
			}
			if (!phint->Data || (phint->Data == &opt->PlugIns.pFullScreen))
			{
				Icon_SetData(wnd, Icon_FullPlugInName, opt->PlugIns.pFullScreen);

				if (opt->PlugIns.pFullScreen && opt->PlugIns.pFullScreen[0])
				{
					Icon_SetDimmed(wnd, Icon_FullPlugInInfo, !Filer_Find(Dir_PlugIn_FullScreen, opt->PlugIns.pFullScreen, File_Help));
					Icon_SetDimmed(wnd, Icon_FullPlugInSetup, !Filer_Find(Dir_PlugIn_FullScreen, opt->PlugIns.pFullScreen, File_Setup));
				}
				else
				{
					Icon_SetDimmed(wnd, Icon_FullPlugInInfo, true);
					Icon_SetDimmed(wnd, Icon_FullPlugInSetup, true);
				}
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

static void OptEdit_BuildSamplerDriverMenu(COptEdit* This)
{
	const char* name = NULL;
	int i;

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	try
	{
		This->m_MenuTmp = throw_New_Menu(Msg_Lookup("SpTDriver"), Var_None);
		throw_Menu_InsertItem(This->m_MenuTmp, Var_SoundDMA, -1);

		for(i = 1; i <= SoundDriver_GetDriversCount(); i++)
		{
			name = SoundDriver_GetDriver(i);
			throw_Menu_InsertItem(This->m_MenuTmp, name, -1);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end
}

static void OptEdit_InitSamplerDriver(COptEdit* This)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];
	int driver;

	driver = SoundDriver_FindDriver(pParams->Driver);
	if (driver > 0)
	{
		mem_setstring(&pParams->SourceLeft, SoundDriver_GetLeftSource(driver, 0));
		mem_setstring(&pParams->SourceRight, SoundDriver_GetRightSource(driver, 0));
		mem_setstring(&pParams->SampleType, SoundDriver_GetSampleType(driver, 0));
	}
	else
	{
		mem_setstring(&pParams->SourceLeft, Msg_Lookup("SpAuto"));
		mem_setstring(&pParams->SourceRight, Msg_Lookup("SpAuto"));
		mem_setstring(&pParams->SampleType, Msg_Lookup("SpAuto"));
	}
}

static void OptEdit_SelectSamplerDriver(COptEdit* This, const char* value)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

	if(!strcmp(pParams->Driver, value)) return;

	mem_setstring(&pParams->Driver, value);

	OptEdit_InitSamplerDriver(This);

	OptEdit_Refresh(This, NULL);
}

static void OptEdit_BuildSamplerSourceLeftMenu(COptEdit* This)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];
	const char* name = NULL;
	int driver;
	int i;

	driver = SoundDriver_FindDriver(pParams->Driver);
	if (driver == 0) return;

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	try
	{
		This->m_MenuTmp = throw_New_Menu(Msg_Lookup("SpTSourceLeft"), SoundDriver_GetLeftSource(driver, 0));

		for(i = 1; (name = SoundDriver_GetLeftSource(driver, i)) != NULL; i++)
		{
			throw_Menu_InsertItem(This->m_MenuTmp, name, -1);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end
}

static void OptEdit_SelectSamplerSourceLeft(COptEdit* This, const char* value)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

	if(!strcmp(pParams->SourceLeft, value)) return;

	mem_setstring(&pParams->SourceLeft, value);

	OptEdit_Refresh(This, &pParams->SourceLeft);
}

static void OptEdit_BuildSamplerSourceRightMenu(COptEdit* This)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];
	const char* name = NULL;
	int driver;
	int i;

	driver = SoundDriver_FindDriver(pParams->Driver);
	if (driver == 0) return;

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	try
	{
		This->m_MenuTmp = throw_New_Menu(Msg_Lookup("SpTSourceRight"), SoundDriver_GetRightSource(driver, 0));

		for(i = 1; (name = SoundDriver_GetRightSource(driver, i)) != NULL; i++)
		{
			throw_Menu_InsertItem(This->m_MenuTmp, name, -1);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end
}

static void OptEdit_SelectSamplerSourceRight(COptEdit* This, const char* value)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

	if(!strcmp(pParams->SourceRight, value)) return;

	mem_setstring(&pParams->SourceRight, value);

	OptEdit_Refresh(This, &pParams->SourceRight);
}

static void OptEdit_BuildSamplerSampleTypeMenu(COptEdit* This)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];
	const char* name = NULL;
	int driver;
	int i;

	driver = SoundDriver_FindDriver(pParams->Driver);
	if (driver == 0) return;

	Delete_Menu(This->m_MenuTmp, false);
	This->m_MenuTmp = NULL;

	try
	{
		This->m_MenuTmp = throw_New_Menu(Msg_Lookup("SpTSampleType"), SoundDriver_GetSampleType(driver, 0));

		for(i = 1; (name = SoundDriver_GetSampleType(driver, i)) != NULL; i++)
		{
			throw_Menu_InsertItem(This->m_MenuTmp, name, -1);
		}
	}
	catch
	{
		Delete_Menu(This->m_MenuTmp, false);
		This->m_MenuTmp = NULL;
		App_ReportException();
	}
	catch_end
}

static void OptEdit_SelectSamplerSampleType(COptEdit* This, const char* value)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

	if(!strcmp(pParams->SampleType, value)) return;

	mem_setstring(&pParams->SampleType, value);

	OptEdit_Refresh(This, &pParams->SampleType);
}

static void OptEdit_SelectSamplerSampleRate(COptEdit* This, const int index)
{
	COptions* opt = &This->m_Options;
	SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

	pParams->SampleRate = This->m_Freq[index];

	OptEdit_Refresh(This, &pParams->SampleRate);
}

static EListenerAction OptSamplers_SpTargetMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			if (hit[0] < 1)
				This->m_CurrentSampler = hit[0];
			else
				This->m_CurrentSampler = hit[0] + 1;
			OptEdit_Refresh(This, NULL);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_SpDriverMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectSamplerDriver(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_SpSourceLeftMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectSamplerSourceLeft(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_SpSourceRightMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectSamplerSourceRight(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_SpSampleTypeMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectSamplerSampleType(This, Menu_ItemGetText(This->m_MenuTmp, hit[0]));

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_SpSampleRateMenuHandler(void* handle, const int* hit, const Event* e)
{
	COptEdit* This = handle;

	switch(e->Type)
	{
		case EEvent_Menu:
		{
			OptEdit_SelectSamplerSampleRate(This, hit[0]);

			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction OptSamplers_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndSamplers;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_SpForceDriverConfig:
				{
					opt->Samplers.bForceDriverConfig ^= true;
					OptEdit_Refresh(This, &opt->Samplers.bForceDriverConfig);
				}
				break;
				case Icon_SpTargetMenu:
				{
					Menu_Popup(m->w, m->i, This->m_MenuSpTarget, OptSamplers_SpTargetMenuHandler, handle);
				}
				break;
				case Icon_SpDriverMenu:
				{
					OptEdit_BuildSamplerDriverMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptSamplers_SpDriverMenuHandler, handle);
				}
				break;
				case Icon_SpSourceLeftMenu:
				{
					OptEdit_BuildSamplerSourceLeftMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptSamplers_SpSourceLeftMenuHandler, handle);
				}
				break;
				case Icon_SpSourceRightMenu:
				{
					OptEdit_BuildSamplerSourceRightMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptSamplers_SpSourceRightMenuHandler, handle);
				}
				break;
				case Icon_SpSampleTypeMenu:
				{
					OptEdit_BuildSamplerSampleTypeMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptSamplers_SpSampleTypeMenuHandler, handle);
				}
				break;
				case Icon_SpSampleRateMenu:
				{
					OptEdit_BuildFreqMenu(This);
					Menu_Popup(m->w, m->i, This->m_MenuTmp, OptSamplers_SpSampleRateMenuHandler, handle);
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Samplers");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;
			SamplerParams* pParams = &opt->Samplers.Params[This->m_CurrentSampler];

			if (!phint->Data)
				OptEdit_InitSamplerDriver(This);

			if (!phint->Data || (phint->Data == &opt->Samplers.bForceDriverConfig))
				Icon_SetHighlight(wnd, Icon_SpForceDriverConfig, opt->Samplers.bForceDriverConfig);
			if (!phint->Data || (phint->Data == &pParams->Target))
				Icon_SetData(wnd, Icon_SpTarget, pParams->Target);
			if (!phint->Data || (phint->Data == &pParams->Driver))
			{
				bool dim = (!strcmp(pParams->Driver, Var_None) || !strcmp(pParams->Driver, Var_SoundDMA));

				Icon_SetData(wnd, Icon_SpDriver, pParams->Driver);
				Icon_SetDimmed(wnd, Icon_SpSourceLeft, dim);
				Icon_SetDimmed(wnd, Icon_SpSourceLeftMenu, dim);
				Icon_SetDimmed(wnd, Icon_SpSourceRight, dim);
				Icon_SetDimmed(wnd, Icon_SpSourceRightMenu, dim);
				Icon_SetDimmed(wnd, Icon_SpSampleRate, dim);
				Icon_SetDimmed(wnd, Icon_SpSampleRateMenu, dim);
				Icon_SetDimmed(wnd, Icon_SpSampleType, dim);
				Icon_SetDimmed(wnd, Icon_SpSampleTypeMenu, dim);
			}
			if (!phint->Data || (phint->Data == &pParams->SourceLeft))
				Icon_SetData(wnd, Icon_SpSourceLeft, pParams->SourceLeft);
			if (!phint->Data || (phint->Data == &pParams->SourceRight))
				Icon_SetData(wnd, Icon_SpSourceRight, pParams->SourceRight);
			if (!phint->Data || (phint->Data == &pParams->SampleType))
				Icon_SetData(wnd, Icon_SpSampleType, pParams->SampleType);
			if (!phint->Data || (phint->Data == &pParams->SampleRate))
			{
				if (Icon_GetState(wnd, Icon_SpSampleRate, EIcon_Dimmed))
					Icon_SetData(wnd, Icon_SpSampleRate, Msg_Lookup("SpAuto"));
				else
					Icon_Printf(wnd, Icon_SpSampleRate, "%d Hz", pParams->SampleRate);
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

//-------------------------------------------------------------------------

int Quality_FromFrequency(int freq)
{
	int val = 1000000/freq;
	int res = val & 7;

	val = val - res;
	if (res > 3) val += 8;
	if (val <= 24) return 24;
	if (val >= 72) return 72;

	return val;
}

int Options_MixFrequency(const COptions* This, int input_freq)
{
	int freq = 0;

	if (This->Music_Files.FreqMultiply == 0)
	{
		freq = This->Music_Files.Frequency;
	}
	else
	{
		int freqmul = This->Music_Files.FreqMultiply;
		COptEdit* pEdit = OptEdit_Get();

		if (pEdit->m_Freqs <= 0) OptEdit_BuildFreqMenu(pEdit);

		while (!freq && (freqmul > 0))
		{
			int target = input_freq * freqmul;

			for (int i = 0; i < pEdit->m_Freqs; i++)
			{
				if (target <= pEdit->m_Freq[i])
				{
					freq = pEdit->m_Freq[i];
					break;
				}
			}

			freqmul--;
		}

		if (!freq) freq = This->Music_Files.Frequency;
	}

//	Log("Freq: %d (x %d) => %d", input_freq, This->Music_Files.FreqMultiply, freq);
	return freq;
}

//-------------------------------------------------------------------------

static EListenerAction OptInternet_EventHandler(void* handle, const Event* e)
{
	COptEdit* This = handle;
	COptions* opt = &This->m_Options;
	HWind     wnd = This->m_wndInternet;

	switch(e->Type)
	{
		case EEvent_Mouse:
		{
			const Mouse* m = e->pData;

			switch (m->i)
			{
				case Icon_NetUseProxy:
				{
					opt->Internet.bUseProxy ^= true;
					OptEdit_Refresh(This, &opt->Internet.bUseProxy);
				}
				break;
				case Icon_NetIcyMetadata:
				{
					opt->Internet.bIcyMetadata ^= true;
					OptEdit_Refresh(This, &opt->Internet.bIcyMetadata);
				}
				break;
				case Icon_NetAudiocastMetadata:
				{
					opt->Internet.bAudiocastMetadata ^= true;
					OptEdit_Refresh(This, &opt->Internet.bAudiocastMetadata);
				}
				break;
				case Icon_NetReserveDown:
				{
					if (opt->Internet.Reserve > 1)
					{
						opt->Internet.Reserve--;
						OptEdit_Refresh(This, &opt->Internet.Reserve);
					}
				}
				break;
				case Icon_NetReserveUp:
				{
					if (opt->Internet.Reserve < 60)
					{
						opt->Internet.Reserve++;
						OptEdit_Refresh(This, &opt->Internet.Reserve);
					}
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* key = e->pData;

			if (key->code == 0x181) // F1
			{
				App_StrongHelp("Choices_Internet");
				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_OnUpdate:
		{
			const CHint* phint = e->pData;

			if (!phint->Data || (phint->Data == &opt->Internet.bUseProxy))
				Icon_SetHighlight(wnd, Icon_NetUseProxy, opt->Internet.bUseProxy);
			if (!phint->Data || (phint->Data == &opt->Internet.ProxyUrl))
				Icon_SetData(wnd, Icon_NetProxyUrl, opt->Internet.ProxyUrl);
			if (!phint->Data || (phint->Data == &opt->Internet.bIcyMetadata))
				Icon_SetHighlight(wnd, Icon_NetIcyMetadata, opt->Internet.bIcyMetadata);
			if (!phint->Data || (phint->Data == &opt->Internet.bAudiocastMetadata))
				Icon_SetHighlight(wnd, Icon_NetAudiocastMetadata, opt->Internet.bAudiocastMetadata);
			if (!phint->Data || (phint->Data == &opt->Internet.AudiocastPort))
				Icon_Printf(wnd, Icon_NetAudiocastPort, "%d", opt->Internet.AudiocastPort);
			if (!phint->Data || (phint->Data == &opt->Internet.Reserve))
				Icon_Printf(wnd, Icon_NetReserve, "%2.1f", (float) (0.5f * (float) opt->Internet.Reserve));
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}
