#include "EditFile.h"

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

#include "WimpLib:Caret.h"
#include "WimpLib:Desktop.h"
#include "WimpLib:Display.h"
#include "WimpLib:Exception.h"
#include "WimpLib:File.h"
#include "WimpLib:Hourglass.h"
#include "WimpLib:mem.h"
#include "WimpLib:Menu.h"
#include "WimpLib:Message.h"
#include "WimpLib:Slider.h"
#include "WimpLib:Task.h"
#include "WimpLib:Template.h"
#include "WimpLib:Window.h"
#include "WimpLib:WList.h"
#include "WimpLib:WStringList.h"

#include "DigitalCD.h"
#include "Options.h"
#include "PListFiles.h"
#include "Setup.h"
#include "DocEvents.h"
#include "UserEvents.h"

#define Prop_DirOpen     0x00001
#define Prop_Filename    0x00002
#define Prop_Title       0x00004
#define Prop_Album       0x00008
#define Prop_Compilation 0x00010
#define Prop_Box         0x00020
#define Prop_Artist      0x00040
#define Prop_Collective  0x00080
#define Prop_Info        0x00100
#define Prop_Section     0x00200
#define Prop_Clue        0x00400
#define Prop_Points      0x00800
#define Prop_Volume      0x01000
#define Prop_NeverPlay   0x02000
#define Prop_TrackNr     0x04000
#define Prop_Date        0x08000
#define Prop_PlayAll     0x10000
#define Prop_TimIgnoreLoops         0x020000
#define Prop_TimIgnoreRestartPos    0x040000
#define Prop_TimIgnoreEndMarker     0x080000
#define Prop_TimIgnoreMuteFlags     0x100000
#define Prop_TimVBlankMode          0x200000
#define Prop_TimNoInstSwap          0x400000
#define Prop_TimUltimateSTK         0x800000
#define Prop_AllProps        0x00ffffff
#define Prop_SingleObject    0x80000000
#define Prop_RefreshContents 0x40000000
#define Prop_RefreshState    0x20000000

typedef unsigned int Props;

#define EIcon_ColorMask   0xf0000000
#define EIcon_ColorNormal 0x00000000
#define EIcon_ColorDiffer 0x20000000

#define EIcon_FillColorMask   0xf0000020
#define EIcon_FillColorNormal 0x10000000
#define EIcon_FillColorDiffer 0x20000020

#define EditFlags_Create   0x01
#define EditFlags_Updating 0x02 // We are fiddling with the tracks, ignore DocEvents

typedef struct
{
	HWind             m_wnd;
	HWind             m_wndMain;
	List*             m_pTracks;
	const FLTrack*    m_pRefTrack;
	void*             m_pView;
	Props             m_AllowedProps;
	Props             m_DifferingProps;
	Props             m_ModifiedProps;
	unsigned int      m_flags;
	struct
	{
		const char*   filename;
		const char*   title;
		const char*   album;
		const char*   box;
		const char*   artist;
		const char*   collective;
		const char*   info;
		const char*   date;
		unsigned int  section;
		const CSpriteHdr* pclue;
		int           points;
		unsigned int  volume;
		unsigned int  tracknr;
		bool          neverplay;
		bool          compilation;
		bool          playall;
		unsigned int  timflags;
	} m_ref;
	struct
	{
		unsigned int  section;
		const CSpriteHdr* pclue;
		int           points;
		unsigned int  volume;
		unsigned int  tracknr;
		bool          neverplay;
		bool          compilation;
		bool          playall;
		unsigned int  timflags;
	} m_new;
	WStringList*      m_pWCollectiveList;
	WStringList*      m_pWArtistList;
	WStringList*      m_pWBoxList;
	WStringList*      m_pWAlbumList;
} EditFile;

static EditFile TheEditFile = {HWind_None, HWind_None};

#define Icon_Set          0
#define Icon_Cancel       1
#define Icon_Pane         2

#define Icon_DirOpen      2
#define Icon_Filename     3
#define Icon_TrackNr      5
#define Icon_Title        7
#define Icon_Album        9
#define Icon_Compilation 10
#define Icon_Date        12
#define Icon_Box         14
#define Icon_Artist      16
#define Icon_Collective  18
#define Icon_Section     45
#define Icon_Info        22
#define Icon_Points      24
#define Icon_Stars       25
#define Icon_Clue        31
#define Icon_ClueMenu    32
#define Icon_VolumeStart 33
#define Icon_VolumeEnd   40
#define Icon_Volume      35
#define Icon_Volume2     36
#define Icon_100         38
#define Icon_500         40
#define Icon_NeverPlay   42
#define Icon_PlayAll     43
#define Icon_TimIgnoreLoops         48
#define Icon_TimIgnoreRestartPos    49
#define Icon_TimIgnoreEndMarker     50
#define Icon_TimIgnoreMuteFlags     51
#define Icon_TimVBlankMode          52
#define Icon_TimNoInstSwap          53
#define Icon_TimUltimateSTK         54

/**
 * Sets the reference track to use for building the properties.
 * If the reference track is not contained in the list of edited tracks
 * the first one from the list is used as reference.
 *
 * @param  pRef  Pointer to the reference track to use.
 */
static void EditFile_SetRefTrack(EditFile* This, const FLTrack* pRef)
{
	This->m_pRefTrack = NULL;

	if (pRef)
	{
		if (List_Find(This->m_pTracks, 0, pRef) != -1)
			This->m_pRefTrack = pRef;
	}

	if (!This->m_pRefTrack && (List_Count(This->m_pTracks) > 0))
		This->m_pRefTrack = List_Get(This->m_pTracks, 0);
}

/**
 * Refreshes the state of buttons
 */
static void EditFile_RefreshButtons(EditFile* This)
{
	bool b;

	b = !This->m_ModifiedProps;
	if (This->m_flags & EditFlags_Create) b = false;

	Icon_SetDimmed(This->m_wndMain, Icon_Set, b);
}

/**
 * Refreshes the window contents for given properties
 *
 * @param  props  bitlist of properties to refresh
 *                + Prop_RefreshContents is the properties values are to be updated
 *                + Prop_RefreshState is the properties common/allowed status are to be updated.
 */

static void EditFile_Refresh(EditFile* This, Props props)
{
	bool b;

	if (props & Prop_DirOpen)
	{
		b = (This->m_AllowedProps & Prop_DirOpen) == 0;

		if (props & Prop_RefreshState)
			Icon_SetDimmed(This->m_wnd, Icon_DirOpen, b);
	}

	if (props & Prop_Filename)
	{
		b = (This->m_AllowedProps & Prop_Filename) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetData(This->m_wnd, Icon_Filename, This->m_ref.filename);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Filename, b);
			Icon_SetState(This->m_wnd, Icon_Filename
			             , (This->m_DifferingProps & Prop_Filename)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Title)
	{
		b = (This->m_AllowedProps & Prop_Title) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetData(This->m_wnd, Icon_Title, This->m_ref.title);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Title - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Title, b);
			Icon_SetState(This->m_wnd, Icon_Title
			             , (This->m_DifferingProps & Prop_Title)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Album)
	{
		b = (This->m_AllowedProps & Prop_Album) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWAlbumList, This->m_ref.album);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Album - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Album, b);
			Icon_SetState(This->m_wnd, Icon_Album
			             , (This->m_DifferingProps & Prop_Album)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Compilation)
	{
		b = (This->m_AllowedProps & Prop_Compilation) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_Compilation, This->m_new.compilation);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Compilation, b);
			Icon_SetState(This->m_wnd, Icon_Compilation
			             , (This->m_DifferingProps & Prop_Compilation)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_Box)
	{
		b = (This->m_AllowedProps & Prop_Box) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWBoxList, This->m_ref.box);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Box - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Box, b);
			Icon_SetState(This->m_wnd, Icon_Box
			             , (This->m_DifferingProps & Prop_Box)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Artist)
	{
		b = (This->m_AllowedProps & Prop_Artist) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWArtistList, This->m_ref.artist);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Artist - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Artist, b);
			Icon_SetState(This->m_wnd, Icon_Artist
			             , (This->m_DifferingProps & Prop_Artist)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Collective)
	{
		b = (This->m_AllowedProps & Prop_Collective) == 0;

		if (props & Prop_RefreshContents)
			WStringList_SetIconData(This->m_pWCollectiveList, This->m_ref.collective);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Collective - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Collective, b);
			Icon_SetState(This->m_wnd, Icon_Collective
			             , (This->m_DifferingProps & Prop_Collective)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Info)
	{
		b = (This->m_AllowedProps & Prop_Info) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetData(This->m_wnd, Icon_Info, This->m_ref.info);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd,Icon_Info - 1, b);
			Icon_SetDimmed(This->m_wnd,Icon_Info, b);
			Icon_SetState(This->m_wnd, Icon_Info
			             , (This->m_DifferingProps & Prop_Info)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Section)
	{
		b = (This->m_AllowedProps & Prop_Section) == 0;

		if (props & Prop_RefreshContents)
		{
			if (b || !This->m_new.section)
				Icon_SetData(This->m_wnd, Icon_Section, NULL);
			else
				Icon_Printf(This->m_wnd, Icon_Section, "%d", This->m_new.section);
		}

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Section - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Section, b);
			Icon_SetState(This->m_wnd, Icon_Section
			             , (This->m_DifferingProps & Prop_Section)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Clue)
	{
		Icon_Redraw(This->m_wnd, Icon_Clue);
	}

	if (props & Prop_Points)
	{
		b = (This->m_AllowedProps & Prop_Points) == 0;

		if (props & Prop_RefreshContents)
		{
			int points = This->m_new.points;

			if (points != -1)
			{
				Icon_Printf(This->m_wnd, Icon_Points, "%d", points);
			}
			else
				Icon_SetData(This->m_wnd, Icon_Points, NULL);

			for (int i = 0; i < 5; i++)
			{
				if (points < (((i<<1) + 1) * Options()->PlayLists.MaxPoints / 10))
					Icon_SetData(This->m_wnd, Icon_Stars + i, "rate_0");
				else if (points < (((i<<1) + 2) * Options()->PlayLists.MaxPoints / 10))
					Icon_SetData(This->m_wnd, Icon_Stars + i, "rate_5");
				else
					Icon_SetData(This->m_wnd, Icon_Stars + i, "rate_10");
			}
		}

		if (props & Prop_RefreshState)
		{
			for (int i = Icon_Points - 1; i <= Icon_Stars + 4; i++)
				Icon_SetDimmed(This->m_wnd, i, b);
			Icon_SetState(This->m_wnd, Icon_Points
			             , (This->m_DifferingProps & Prop_Points)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Volume)
	{
		b = (This->m_AllowedProps & Prop_Volume) == 0;

		if (props & Prop_RefreshContents)
			Slider_Set(This->m_wnd, Icon_Volume, Icon_Volume2, 0, (float)This->m_new.volume/500);

		if (props & Prop_RefreshState)
		{
			for (int i = Icon_VolumeStart; i <= Icon_VolumeEnd; i++)
				Icon_SetDimmed(This->m_wnd, i, b);
			Icon_SetState(This->m_wnd, Icon_Volume
			             , (This->m_DifferingProps & Prop_Volume)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_NeverPlay)
	{
		b = (This->m_AllowedProps & Prop_NeverPlay) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_NeverPlay, This->m_new.neverplay);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_NeverPlay, b);
			Icon_SetState(This->m_wnd, Icon_NeverPlay
			             , (This->m_DifferingProps & Prop_NeverPlay)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_PlayAll)
	{
		b = (This->m_AllowedProps & Prop_PlayAll) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_PlayAll, This->m_new.playall);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_PlayAll, b);
			Icon_SetState(This->m_wnd, Icon_PlayAll
			             , (This->m_DifferingProps & Prop_PlayAll)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TrackNr)
	{
		b = (This->m_AllowedProps & Prop_TrackNr) == 0;

		if (props & Prop_RefreshContents)
		{
			if (b || !This->m_new.tracknr)
				Icon_SetData(This->m_wnd, Icon_TrackNr, NULL);
			else
				Icon_Printf(This->m_wnd, Icon_TrackNr, "%d", This->m_new.tracknr);
		}

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TrackNr - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_TrackNr, b);
			Icon_SetState(This->m_wnd, Icon_TrackNr
			             , (This->m_DifferingProps & Prop_TrackNr)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_Date)
	{
		b = (This->m_AllowedProps & Prop_Date) == 0;

		if (props & Prop_RefreshContents)
		{
			if (b)
				Icon_SetData(This->m_wnd, Icon_Date, NULL);
			else
				Icon_SetData(This->m_wnd, Icon_Date, This->m_ref.date);
		}

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_Date - 1, b);
			Icon_SetDimmed(This->m_wnd, Icon_Date, b);
			Icon_SetState(This->m_wnd, Icon_Date
			             , (This->m_DifferingProps & Prop_Date)
			             ? EIcon_ColorDiffer: EIcon_ColorNormal, EIcon_ColorMask);
		}
	}

	if (props & Prop_TimIgnoreLoops)
	{
		b = (This->m_AllowedProps & Prop_TimIgnoreLoops) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimIgnoreLoops, (This->m_new.timflags & Flag_TimIgnoreLastPatternLoops) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimIgnoreLoops, b);
			Icon_SetState(This->m_wnd, Icon_TimIgnoreLoops
			             , (This->m_DifferingProps & Prop_TimIgnoreLoops)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimIgnoreRestartPos)
	{
		b = (This->m_AllowedProps & Prop_TimIgnoreRestartPos) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimIgnoreRestartPos, (This->m_new.timflags & Flag_TimIgnoreRestartPos) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimIgnoreRestartPos, b);
			Icon_SetState(This->m_wnd, Icon_TimIgnoreRestartPos
			             , (This->m_DifferingProps & Prop_TimIgnoreRestartPos)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimIgnoreEndMarker)
	{
		b = (This->m_AllowedProps & Prop_TimIgnoreEndMarker) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimIgnoreEndMarker, (This->m_new.timflags & Flag_TimIgnoreSequenceEndMarkers) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimIgnoreEndMarker, b);
			Icon_SetState(This->m_wnd, Icon_TimIgnoreEndMarker
			             , (This->m_DifferingProps & Prop_TimIgnoreEndMarker)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimIgnoreMuteFlags)
	{
		b = (This->m_AllowedProps & Prop_TimIgnoreMuteFlags) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimIgnoreMuteFlags, (This->m_new.timflags & Flag_TimDoNotPlayMutedChannels) == 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimIgnoreMuteFlags, b);
			Icon_SetState(This->m_wnd, Icon_TimIgnoreMuteFlags
			             , (This->m_DifferingProps & Prop_TimIgnoreMuteFlags)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimVBlankMode)
	{
		b = (This->m_AllowedProps & Prop_TimVBlankMode) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimVBlankMode, (This->m_new.timflags & Flag_TimVBlankMode) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimVBlankMode, b);
			Icon_SetState(This->m_wnd, Icon_TimVBlankMode
			             , (This->m_DifferingProps & Prop_TimVBlankMode)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimNoInstSwap)
	{
		b = (This->m_AllowedProps & Prop_TimNoInstSwap) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimNoInstSwap, (This->m_new.timflags & Flag_TimNoInstSwap) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimNoInstSwap, b);
			Icon_SetState(This->m_wnd, Icon_TimNoInstSwap
			             , (This->m_DifferingProps & Prop_TimNoInstSwap)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}

	if (props & Prop_TimUltimateSTK)
	{
		b = (This->m_AllowedProps & Prop_TimUltimateSTK) == 0;

		if (props & Prop_RefreshContents)
			Icon_SetHighlight(This->m_wnd, Icon_TimUltimateSTK, (This->m_new.timflags & Flag_TimUltimateSTK) != 0);

		if (props & Prop_RefreshState)
		{
			Icon_SetDimmed(This->m_wnd, Icon_TimUltimateSTK, b);
			Icon_SetState(This->m_wnd, Icon_TimUltimateSTK
			             , (This->m_DifferingProps & Prop_TimUltimateSTK)
			             ? EIcon_FillColorDiffer: EIcon_FillColorNormal, EIcon_FillColorMask);
		}
	}
}

/**
 * Builds the properties initial values and allowed status.
 */

static void EditFile_BuildProperties(EditFile* This)
{
	const FLTrack* pTrack;
	bool b;

	This->m_AllowedProps = Prop_AllProps;
	This->m_ModifiedProps = 0;

	if (List_Count(This->m_pTracks) > 1)
	{
		This->m_AllowedProps &= ~( Prop_DirOpen
		                         | Prop_Filename
		                         | Prop_Volume
		                         | Prop_TimIgnoreLoops
		                         | Prop_TimIgnoreRestartPos
		                         | Prop_TimIgnoreEndMarker
		                         | Prop_TimIgnoreMuteFlags
		                         | Prop_TimVBlankMode
		                         | Prop_TimNoInstSwap
		                         | Prop_TimUltimateSTK
		                         );
	}
	else This->m_AllowedProps |= Prop_SingleObject;

	pTrack = This->m_pRefTrack;

	b = (This->m_AllowedProps & Prop_Filename) == 0;
	throw_mem_setstring(&This->m_ref.filename, b ? NULL : FLTrack_GetFilename(pTrack));

	b = (This->m_AllowedProps & Prop_Title) == 0;
	throw_mem_setstring(&This->m_ref.title, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamTitle));

	b = (This->m_AllowedProps & Prop_Album) == 0;
	throw_mem_setstring(&This->m_ref.album, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamAlbum));

	b = (This->m_AllowedProps & Prop_Compilation) == 0;
	This->m_new.compilation = This->m_ref.compilation
				= b ? false : ((FLTrack_GetFlags(pTrack) & FLTrack_Compilation) != 0);

	b = (This->m_AllowedProps & Prop_Box) == 0;
	throw_mem_setstring(&This->m_ref.box, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamBox));

	b = (This->m_AllowedProps & Prop_Artist) == 0;
	throw_mem_setstring(&This->m_ref.artist, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamArtist));

	b = (This->m_AllowedProps & Prop_Collective) == 0;
	throw_mem_setstring(&This->m_ref.collective, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamCollective));

	b = (This->m_AllowedProps & Prop_Info) == 0;
	throw_mem_setstring(&This->m_ref.info, b ? NULL : FLTrack_GetInfo(pTrack));

	b = (This->m_AllowedProps & Prop_Section) == 0;
	This->m_new.section = This->m_ref.section = b ? 0 : FLTrack_GetSection(pTrack);

	b = (This->m_AllowedProps & Prop_Clue) == 0;
	This->m_new.pclue = This->m_ref.pclue = b ? NULL : FLTrack_GetClue(pTrack);

	b = (This->m_AllowedProps & Prop_Points) == 0;
	This->m_new.points = This->m_ref.points = b ? -1 : FLTrack_GetPoints(pTrack);

	b = (This->m_AllowedProps & Prop_Volume) == 0;
	This->m_new.volume = This->m_ref.volume = b ? 100 : FLTrack_GetVolume(pTrack);

	b = (This->m_AllowedProps & Prop_NeverPlay) == 0;
	This->m_new.neverplay = This->m_ref.neverplay
				= b ? false : ((FLTrack_GetFlags(pTrack) & FLTrack_NeverPlay) != 0);

	b = (This->m_AllowedProps & Prop_PlayAll) == 0;
	This->m_new.playall = This->m_ref.playall
				= b ? false : ((FLTrack_GetFlags(pTrack) & FLTrack_PlayAllSections) != 0);

	b = (This->m_AllowedProps & Prop_TrackNr) == 0;
	This->m_new.tracknr = This->m_ref.tracknr = b ? 0 : FLTrack_GetOrder(pTrack);

	b = (This->m_AllowedProps & Prop_Date) == 0;
	throw_mem_setstring(&This->m_ref.date, b ? NULL : FLTrack_GetMetaString(pTrack, EMetaId_StreamDate));

	b = (This->m_AllowedProps & Prop_TimIgnoreLoops) == 0;
	This->m_new.timflags = This->m_ref.timflags
				= b ? false : FLTrack_GetTimFlags(pTrack);

	EditFile_Refresh(This, Prop_RefreshContents | Prop_AllProps);

	EditFile_RefreshButtons(This);
}

/**
 * Takes note of the properties for which the values are not identical in all tracks.
 */

static void EditFile_BuildCommonStatus(EditFile* This)
{
	if (This->m_AllowedProps & Prop_SingleObject)
	{
		This->m_DifferingProps = 0;
	}
	else
	{
		ListNode* pNode = NULL;
		const FLTrack* pTrack;
		Props commons = This->m_AllowedProps;

		// Check all allowed/non-modified properties to see
		// if their values correspond to the initial values.
		pNode = NULL;

		while (commons && ((pNode = List_GetSuccessor(This->m_pTracks, pNode)) != NULL))
		{
			pTrack = List_GetNodeData(This->m_pTracks, pNode);

			if (commons & Prop_Filename)
			{
				if (strcmp(nvl(This->m_ref.filename), FLTrack_GetFilename(pTrack)))
					commons &= ~Prop_Filename;
			}

			if (commons & Prop_Title)
			{
				if (strcmp(nvl(This->m_ref.title), FLTrack_GetMetaString(pTrack, EMetaId_StreamTitle)))
					commons &= ~Prop_Title;
			}

			if (commons & Prop_Album)
			{
				if (strcmp(nvl(This->m_ref.album), FLTrack_GetMetaString(pTrack, EMetaId_StreamAlbum)))
					commons &= ~Prop_Album;
			}

			if (commons & Prop_Compilation)
			{
				bool b = ((FLTrack_GetFlags(pTrack) & FLTrack_Compilation) != 0);
				if (This->m_ref.compilation != b)
					commons &= ~Prop_Compilation;
			}

			if (commons & Prop_Box)
			{
				if (strcmp(nvl(This->m_ref.box), FLTrack_GetMetaString(pTrack, EMetaId_StreamBox)))
					commons &= ~Prop_Box;
			}

			if (commons & Prop_Artist)
			{
				if (strcmp(nvl(This->m_ref.artist), FLTrack_GetMetaString(pTrack, EMetaId_StreamArtist)))
					commons &= ~Prop_Artist;
			}

			if (commons & Prop_Collective)
			{
				if (strcmp(nvl(This->m_ref.collective), FLTrack_GetMetaString(pTrack, EMetaId_StreamCollective)))
					commons &= ~Prop_Collective;
			}

			if (commons & Prop_Info)
			{
				if (strcmp(nvl(This->m_ref.info), FLTrack_GetInfo(pTrack)))
					commons &= ~Prop_Info;
			}

			if (commons & Prop_Section)
			{
				if (This->m_ref.section != FLTrack_GetSection(pTrack))
					commons &= ~Prop_Section;
			}

			if (commons & Prop_Clue)
			{
				if (This->m_ref.pclue != FLTrack_GetClue(pTrack))
					commons &= ~Prop_Clue;
			}

			if (commons & Prop_Points)
			{
				if (This->m_ref.points != FLTrack_GetPoints(pTrack))
					commons &= ~Prop_Points;
			}

			if (commons & Prop_Volume)
			{
				if (This->m_ref.volume != FLTrack_GetVolume(pTrack))
					commons &= ~Prop_Volume;
			}

			if (commons & Prop_NeverPlay)
			{
				bool b = ((FLTrack_GetFlags(pTrack) & FLTrack_NeverPlay) != 0);
				if (This->m_ref.neverplay != b)
					commons &= ~Prop_NeverPlay;
			}

			if (commons & Prop_PlayAll)
			{
				bool b = ((FLTrack_GetFlags(pTrack) & FLTrack_PlayAllSections) != 0);
				if (This->m_ref.playall != b)
					commons &= ~Prop_PlayAll;
			}

			if (commons & Prop_TrackNr)
			{
				if (This->m_ref.tracknr != FLTrack_GetOrder(pTrack))
					commons &= ~Prop_TrackNr;
			}

			if (commons & Prop_Date)
			{
				if (strcmp(nvl(This->m_ref.date), FLTrack_GetMetaString(pTrack, EMetaId_StreamDate)))
					commons &= ~Prop_Date;
			}
		}

		This->m_DifferingProps = This->m_AllowedProps & ~commons;
	}

	EditFile_Refresh(This, Prop_RefreshState | Prop_AllProps);
}

/**
 * Takes note of the properties which were modified.
 */

static void EditFile_CheckChanges(EditFile* This, Props props)
{
	Props modifs = 0;
	Props updated;

	props &= This->m_AllowedProps;

	// Check values with references
	if (props & Prop_Filename)
	{
		if (strcmp(nvl(This->m_ref.filename), Icon_GetData(This->m_wnd, Icon_Filename)))
			modifs |= Prop_Filename;
	}

	if (props & Prop_Title)
	{
		if (strcmp(nvl(This->m_ref.title), Icon_GetData(This->m_wnd, Icon_Title)))
			modifs |= Prop_Title;
	}

	if (props & Prop_Album)
	{
		if (strcmp(nvl(This->m_ref.album), Icon_GetData(This->m_wnd, Icon_Album)))
			modifs |= Prop_Album;
	}

	if (props & Prop_Compilation)
	{
		This->m_new.compilation = (Icon_GetState(This->m_wnd, Icon_Compilation, EIcon_Selected) != 0);
		if (This->m_ref.compilation != This->m_new.compilation)
			modifs |= Prop_Compilation;
	}

	if (props & Prop_Box)
	{
		if (strcmp(nvl(This->m_ref.box), Icon_GetData(This->m_wnd, Icon_Box)))
			modifs |= Prop_Box;
	}

	if (props & Prop_Artist)
	{
		if (strcmp(nvl(This->m_ref.artist), Icon_GetData(This->m_wnd, Icon_Artist)))
			modifs |= Prop_Artist;
	}

	if (props & Prop_Collective)
	{
		if (strcmp(nvl(This->m_ref.collective), Icon_GetData(This->m_wnd, Icon_Collective)))
			modifs |= Prop_Collective;
	}

	if (props & Prop_Info)
	{
		if (strcmp(nvl(This->m_ref.info), Icon_GetData(This->m_wnd, Icon_Info)))
			modifs |= Prop_Info;
	}

	if (props & Prop_Section)
	{
		This->m_new.section = atoi(Icon_GetData(This->m_wnd, Icon_Section));
		if (This->m_ref.section != This->m_new.section)
			modifs |= Prop_Section;
	}

	if (props & Prop_Clue)
	{
		if (This->m_ref.pclue != This->m_new.pclue)
			modifs |= Prop_Clue;
	}

	if (props & Prop_Points)
	{
		if (This->m_ref.points != This->m_new.points)
			modifs |= Prop_Points;
	}

	if (props & Prop_Volume)
	{
		if (This->m_ref.volume != This->m_new.volume)
			modifs |= Prop_Volume;
	}

	if (props & Prop_NeverPlay)
	{
		This->m_new.neverplay = (Icon_GetState(This->m_wnd, Icon_NeverPlay, EIcon_Selected) != 0);
		if (This->m_ref.neverplay != This->m_new.neverplay)
			modifs |= Prop_NeverPlay;
	}

	if (props & Prop_PlayAll)
	{
		This->m_new.playall = (Icon_GetState(This->m_wnd, Icon_PlayAll, EIcon_Selected) != 0);
		if (This->m_ref.playall != This->m_new.playall)
			modifs |= Prop_PlayAll;
	}

	if (props & Prop_TrackNr)
	{
		This->m_new.tracknr = atoi(Icon_GetData(This->m_wnd, Icon_TrackNr));
		if (This->m_ref.tracknr != This->m_new.tracknr)
			modifs |= Prop_TrackNr;
	}

	if (props & Prop_Date)
	{
		if (strcmp(nvl(This->m_ref.date), Icon_GetData(This->m_wnd, Icon_Date)))
			modifs |= Prop_Date;
	}

	if (props & Prop_TimIgnoreLoops)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimIgnoreLoops, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimIgnoreLastPatternLoops;
		else
			This->m_new.timflags &= ~Flag_TimIgnoreLastPatternLoops;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimIgnoreLoops;
	}

	if (props & Prop_TimIgnoreRestartPos)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimIgnoreRestartPos, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimIgnoreRestartPos;
		else
			This->m_new.timflags &= ~Flag_TimIgnoreRestartPos;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimIgnoreRestartPos;
	}

	if (props & Prop_TimIgnoreEndMarker)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimIgnoreEndMarker, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimIgnoreSequenceEndMarkers;
		else
			This->m_new.timflags &= ~Flag_TimIgnoreSequenceEndMarkers;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimIgnoreEndMarker;
	}

	if (props & Prop_TimIgnoreMuteFlags)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimIgnoreMuteFlags, EIcon_Selected) != 0)
			This->m_new.timflags &= ~Flag_TimDoNotPlayMutedChannels;
		else
			This->m_new.timflags |= Flag_TimDoNotPlayMutedChannels;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimIgnoreMuteFlags;
	}

	if (props & Prop_TimVBlankMode)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimVBlankMode, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimVBlankMode;
		else
			This->m_new.timflags &= ~Flag_TimVBlankMode;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimVBlankMode;
	}

	if (props & Prop_TimNoInstSwap)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimNoInstSwap, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimNoInstSwap;
		else
			This->m_new.timflags &= ~Flag_TimNoInstSwap;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimNoInstSwap;
	}

	if (props & Prop_TimUltimateSTK)
	{
		if (Icon_GetState(This->m_wnd, Icon_TimUltimateSTK, EIcon_Selected) != 0)
			This->m_new.timflags |= Flag_TimUltimateSTK;
		else
			This->m_new.timflags &= ~Flag_TimUltimateSTK;
		if (This->m_ref.timflags != This->m_new.timflags)
			modifs |= Prop_TimUltimateSTK;
	}

	// Any change compared to old status?
	updated = (modifs ^ This->m_ModifiedProps) & props;

	if (This->m_AllowedProps & Prop_SingleObject)
	{
		// reflect even reversion to original values
		if (updated)
		{
			This->m_ModifiedProps ^= updated;

			EditFile_RefreshButtons(This);
		}
	}
	else
	{
		// reflect new changes but there is no reversion
		// to original value since any change affected old values
		updated &= modifs;

		if (updated)
		{
			This->m_ModifiedProps |= updated;
			This->m_DifferingProps &= ~updated;
			EditFile_Refresh(This, Prop_RefreshState | updated);

			EditFile_RefreshButtons(This);
		}
	}
}

static void EditFile_SetVolume(EditFile* This, unsigned int volume)
{
	ListNode* pNode;
	FLTrack* pTrack;

	if (!(This->m_AllowedProps & Prop_Volume))
		return;

	if (volume < 10) volume = 10;
	if (volume > 500) volume = 500;
	This->m_new.volume = volume;

	EditFile_CheckChanges(This, Prop_Volume);
	EditFile_Refresh(This, Prop_RefreshContents | Prop_Volume);

	pNode = NULL;

	while ((pNode = List_GetSuccessor(This->m_pTracks, pNode)) != NULL)
	{
		pTrack = List_GetNodeData(This->m_pTracks, pNode);

		FLTrack_SetVolume(pTrack, This->m_new.volume);
	}

	PListFiles_RefreshPlayer(PListFiles_Get(), player_refresh_volume);
}

static void EditFile_UpdateVolume(void* pData, int value, int maxvalue, ESlider_UpdateMode mode)
{
	IGNORE(maxvalue);

	if (mode == ESlider_UpdateMode_Cancel) return;

	EditFile_SetVolume(pData, value);
}

static void EditFile_SetPoints(EditFile* This, int points)
{
	if (points < -1) points = -1;
	if (points > Options()->PlayLists.MaxPoints) points = Options()->PlayLists.MaxPoints;
	This->m_new.points = points;

	EditFile_CheckChanges(This, Prop_Points);
	EditFile_Refresh(This, Prop_RefreshContents | Prop_Points);
}

static void EditFile_ClearTracks(EditFile* This)
{
	This->m_flags |= EditFlags_Updating;

	try
	{
		if (This->m_flags & EditFlags_Create)
		{
			ListNode* pNode;
			FLTrack* pTrack;

			pNode = NULL;

			while ((pNode = List_GetSuccessor(This->m_pTracks, pNode)) != NULL)
			{
				pTrack = List_GetNodeData(This->m_pTracks, pNode);
				Delete_FLTrack(pTrack);
			}
		}
	}
	catch
	{
		App_ReportException();
	}
	catch_end

	List_Clear(This->m_pTracks);

	This->m_flags &= ~EditFlags_Updating;
}

static bool EditFile_OnOk(EditFile* This)
{
	ListNode* pNode;
	FileList* pOwner = NULL;
	const FLTrack* pTrack;
	int i = 0;
	int max = List_Count(This->m_pTracks);
	CCaret caret;

	// Checks
	pTrack = This->m_pRefTrack;
	caret.pos.w = This->m_wnd;
	caret.height = -1;
	caret.index = 0;

	if ((This->m_ModifiedProps & Prop_Filename)
	||  (This->m_flags & EditFlags_Create))
	{
		// Convert spaces to hardspaces
		const char* filename = Icon_GetData(This->m_wnd, Icon_Filename);

		caret.pos.i = Icon_Filename;
		caret.index = strlen(filename);

		for (char* pc = (char*) filename; *pc; pc++)
		{
			if (*pc == ' ') *pc = 0xa0;
		}

		if (filename[0])
		{
			if (strcmp(FLTrack_GetFilename(pTrack), filename))
			{
				file_type type = File_GetFileType(filename);

				if (type == -2)
				{
					Caret_Set(&caret);
					App_ReportError("DSErr3:%s", filename);
					return false;
				}
				else if (LoadTypes_FindNext(type, NULL) == NULL)
				{
					Caret_Set(&caret);
					App_ReportError("DSErr5:%d", type);
					return false;
				}
			}
		}
		else
		{
			Caret_Set(&caret);
			App_ReportError("DSErr3:%s", filename);
			return false;
		}
	}

	if (This->m_AllowedProps & Prop_Date)
	{
		const char* date = Icon_GetData(This->m_wnd, Icon_Date);
		bool checked = false;

		caret.pos.i = Icon_Date;
		caret.index = strlen(date);

		if (date[0])
		{
			if ((date[0] >= '0') && (date[0] <= '9')
			&&  (date[1] >= '0') && (date[1] <= '9')
			&&  (date[2] >= '0') && (date[2] <= '9')
			&&  (date[3] >= '0') && (date[3] <= '9'))
			{
				if (date[4])
				{
					if ((date[4] == '-')
					&&  (date[5] >= '0') && (date[5] <= '1')
					&&  (date[6] >= '0') && (date[6] <= '9')
					&&  ((date[5] < '1') || (date[6] <= '2')))
					{
						int month = (date[5] - '0') * 10 + (date[6] - '0');

						if ((month >= 1) && (month <= 12))
						{
							if (date[7])
							{
								if ((date[7] == '-')
								&&  (date[8] >= '0') && (date[8] <= '3')
								&&  (date[9] >= '0') && (date[9] <= '9')
								&&  ((date[8] < '3') || (date[9] <= '1')))
								{
									int day = (date[8] - '0') * 10 + (date[9] - '0');

									if ((day >= 1)
									&&  (day <= 31)
									&&  ((month != 2) || (day <= 29))
									&&  (   (day < 31) || (month == 1) || (month == 3) || (month == 5)
									     || (month == 7) || (month == 8) || (month == 10) || (month == 12)))
									{
										checked = true;
									}
								}
							}
							else
							{
								checked = true;
							}
						}
					}
				}
				else
				{
					checked = true;
				}
			}
		}
		else
		{
			checked = true;
		}

		if (checked == false)
		{
			Caret_Set(&caret);
			App_ReportError("PrErr0");
			return false;
		}
	}

	// Set
	bool off = !WLib_Hourglass_IsOn();
	if (off) WLib_Hourglass_On();
	pNode = NULL;

	This->m_flags |= EditFlags_Updating;

	try
	{
		while ((pNode = List_GetSuccessor(This->m_pTracks, pNode)) != NULL)
		{
			FLTrack* pTrack = List_GetNodeData(This->m_pTracks, pNode);

			WLib_Hourglass_Percentage((100 * i++)/max);

			if (pOwner != FLTrack_GetOwner(pTrack))
			{
				if (pOwner != NULL) FileList_EndUpdate(pOwner, false, false);

				pOwner = FLTrack_GetOwner(pTrack);
				if (pOwner != NULL) FileList_StartUpdate(pOwner);
			}

			bool bModified = false;
			bool bAutoModified = false;
			bool bPlayModified = false;

			if (This->m_ModifiedProps & Prop_Filename)
				bModified |= FLTrack_SetFilename(pTrack, Icon_GetData(This->m_wnd, Icon_Filename), false);

			if (This->m_ModifiedProps & Prop_Title)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamTitle, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Title));

			if (This->m_ModifiedProps & Prop_Album)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamAlbum, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Album));

			if (This->m_ModifiedProps & Prop_Compilation)
				bAutoModified = FLTrack_SetFlags(pTrack, This->m_new.compilation ? FLTrack_Compilation : 0, FLTrack_Compilation);

			if (This->m_ModifiedProps & Prop_Box)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamBox, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Box));

			if (This->m_ModifiedProps & Prop_Artist)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamArtist, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Artist));

			if (This->m_ModifiedProps & Prop_Collective)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamCollective, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Collective));

			if (This->m_ModifiedProps & Prop_Info)
				bAutoModified |= FLTrack_SetInfo(pTrack, Icon_GetData(This->m_wnd, Icon_Info));

			if (This->m_ModifiedProps & Prop_Section)
				bAutoModified |= FLTrack_SetSection(pTrack, This->m_new.section);

			if (This->m_ModifiedProps & Prop_Clue)
				bAutoModified |= FLTrack_SetClue(pTrack, This->m_new.pclue);

			if (This->m_ModifiedProps & Prop_Points)
				bAutoModified |= FLTrack_SetPoints(pTrack, This->m_new.points);

			if (This->m_ModifiedProps & Prop_NeverPlay)
				bPlayModified |= FLTrack_SetFlags(pTrack, This->m_new.neverplay ? FLTrack_NeverPlay : 0, FLTrack_NeverPlay);

			if (This->m_ModifiedProps & Prop_PlayAll)
				bPlayModified |= FLTrack_SetFlags(pTrack, This->m_new.playall ? FLTrack_PlayAllSections : 0, FLTrack_PlayAllSections);

			if (This->m_ModifiedProps & Prop_TrackNr)
				bAutoModified |= FLTrack_SetOrder(pTrack, EMetaOrigin_User, This->m_new.tracknr);

			if (This->m_ModifiedProps & Prop_Date)
				bAutoModified |= FLTrack_SetMetaText(pTrack, EMetaId_StreamDate, EMetaOrigin_User, Icon_GetData(This->m_wnd, Icon_Date));

			if (This->m_ModifiedProps & ( Prop_TimIgnoreLoops
		    	                        | Prop_TimIgnoreRestartPos
		        	                    | Prop_TimIgnoreEndMarker
		            	                | Prop_TimIgnoreMuteFlags
		                	            | Prop_TimVBlankMode
		                    	        | Prop_TimNoInstSwap
		                        	    | Prop_TimUltimateSTK))
			{
				bAutoModified |= FLTrack_SetTimFlags(pTrack, This->m_new.timflags, 0xffffffff);
				PListFiles_RefreshPlayer(PListFiles_Get(), player_refresh_params);
			}

			if (bPlayModified)
			{
				bAutoModified |= bPlayModified;
				// Just don't do the following to avoid the list get randomised
				// after you disable a track while something's played.
				// PListFiles_RefreshTrackList(PListFiles_Get(), pOwner);
			}

			if (This->m_flags & EditFlags_Create)
			{
				try
				{
					pOwner = (FileList*) View_GetDocument(This->m_pView);
					int pos = throw_FileList_InsertTrackAt(pOwner, -1, pTrack);
					FileList_Show(pOwner, pos);
					bModified = true;
				}
				catch
				{
					App_ReportException();
				}
				catch_end
			}
			else if ((This->m_new.volume != This->m_ref.volume)
			||  bModified
			||  bAutoModified)
			{
				int refresh = player_refresh_track | player_refresh_volume;
				if (This->m_new.playall != This->m_ref.playall)
					refresh |= player_refresh_loopmode;
				FLTrack_RefreshViews(pTrack, This, bAutoModified, bModified);
				PListFiles_RefreshPlayer(PListFiles_Get(), refresh);
			}
		}

		if (pOwner != NULL) FileList_EndUpdate(pOwner, false, false);
	}
	catch
	{
		App_ReportException();
	}
	catch_end

	This->m_flags &= ~EditFlags_Updating;

	if (off) WLib_Hourglass_Off();

	// Once here new modications are updates
	This->m_flags &= ~EditFlags_Create;

	// Reset settings
	EditFile_BuildProperties(This);
	EditFile_BuildCommonStatus(This);

	return true;
}

static void EditFile_OnCancel(EditFile* This)
{
	EditFile_SetVolume(This, This->m_ref.volume);

	// Reset settings
	EditFile_BuildProperties(This);
	EditFile_BuildCommonStatus(This);
}

static void EditFile_Drawer(void* handle, const CWindRedraw* pdraw)
{
	EditFile* This = handle;
	CIcon icon;
	CPoint pt;
	CRect box;

	Icon_GetInfo(This->m_wnd, Icon_Clue, &icon);

	Desktop_SetStdColour((This->m_DifferingProps & Prop_Clue) ? 2: 0);
	box = RectToScreen(&icon.box, &pdraw->cvt);
	RoundRectForPlot(&box);
	Display_RectangleFill(&box);

	pt.x = box.x0 + 10;
	pt.y = box.y0 + 10;
	Desktop_PlotSprite(This->m_new.pclue, pt, NULL);
}

static EListenerAction EditFile_Listener(void* handle, const Event* e)
{
	EditFile* This = handle;

	switch(e->Type)
	{
		case EEvent_OptionParentalLock:
		{
			if (Options()->Player.bParentalLock)
			{
				EditFile_OnCancel(This);
				Window_Close(This->m_wndMain);
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction EditMain_EventHandler(void* handle, const Event* e)
{
	EditFile* This = handle;

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

			if (m->but & EBut_Menu)
				return EListenerAction_StopEvent;

			switch(m->i)
			{
				case Icon_Set:
				{
					if (EditFile_OnOk(This)
					&&  (m->but & EBut_Select))
					{
						Window_Close(This->m_wndMain);
					}
				}
				break;
				case Icon_Cancel:
				{
					EditFile_OnCancel(This);
					if (m->but & EBut_Select)
					{
						Window_Close(This->m_wndMain);
					}
				}
				break;
			}
			return EListenerAction_StopEvent;
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction EditFile_EventHandler(void* handle, const Event* e)
{
	EditFile* This = handle;

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

			Icon_GetInfo(This->m_wndMain, Icon_Pane, &icon);
			icon.box.x0 += 4;
			icon.box.y0 += 4;
			icon.box.x1 -= 4;
			icon.box.y1 -= 4;

			Window_OpenPane(po, &o, &icon.box, pane_fit_hgadgets | pane_fit_vgadgets);

			return EListenerAction_ContinueEvent;
		}
		break;
		case EEvent_WindowRedraw:
		{
			Window_Redraw(This->m_wnd, EditFile_Drawer, handle);

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

			if (m->but & EBut_Menu)
				return EListenerAction_StopEvent;

			switch(m->i)
			{
				case Icon_DirOpen:
				{
					FLTrack_OpenFileDir(This->m_pRefTrack);
				}
				break;
				case Icon_Compilation:
				{
					EditFile_CheckChanges(This, Prop_Compilation);
				}
				break;
				case Icon_ClueMenu:
				{
					// Open menu
					WList_SetParent(&DigitalCD.m_ClueList, This->m_wnd);
					WList_SetItemsState(&DigitalCD.m_ClueList, 0, EWItem_Selected, EWItem_Selected, EWItem_Selected);
					WList_Popup(&DigitalCD.m_ClueList, This->m_wnd, Icon_ClueMenu);
				}
				break;
				case Icon_100:
				{
					EditFile_SetVolume(This, 100);
				}
				break;
				case Icon_500:
				{
					EditFile_SetVolume(This, 500);
				}
				break;
				case Icon_Volume:
				case Icon_Volume2:
				{
					Slider_Update(This->m_wnd, Icon_Volume, Icon_Volume2, 0, m, EditFile_UpdateVolume, This);
				}
				break;
				case Icon_NeverPlay:
				{
					EditFile_CheckChanges(This, Prop_NeverPlay);
				}
				break;
				case Icon_PlayAll:
				{
					EditFile_CheckChanges(This, Prop_PlayAll);
				}
				break;
				case Icon_TimIgnoreLoops:
				{
					EditFile_CheckChanges(This, Prop_TimIgnoreLoops);
				}
				break;
				case Icon_TimIgnoreRestartPos:
				{
					EditFile_CheckChanges(This, Prop_TimIgnoreRestartPos);
				}
				break;
				case Icon_TimIgnoreEndMarker:
				{
					EditFile_CheckChanges(This, Prop_TimIgnoreEndMarker);
				}
				break;
				case Icon_TimIgnoreMuteFlags:
				{
					EditFile_CheckChanges(This, Prop_TimIgnoreMuteFlags);
				}
				break;
				case Icon_TimVBlankMode:
				{
					EditFile_CheckChanges(This, Prop_TimVBlankMode);
				}
				break;
				case Icon_TimNoInstSwap:
				{
					EditFile_CheckChanges(This, Prop_TimNoInstSwap);
				}
				break;
				case Icon_TimUltimateSTK:
				{
					EditFile_CheckChanges(This, Prop_TimUltimateSTK);
				}
				break;
			}

			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_Key:
		{
			const Event_Key* pkey = e->pData;

			switch(pkey->code)
			{
				case 0x1b: // Esc
				{
					EditFile_OnCancel(This);
					Window_Close(This->m_wndMain);
					return EListenerAction_StopEvent;
				}
				break;
				case 0x0d: // Return
				{
					if (pkey->caret.pos.i == Icon_Points)
					{
						if (EditFile_OnOk(This))
						{
							Window_Close(This->m_wndMain);
						}
					}
					return EListenerAction_StopEvent;
				}
				break;
				case 0x181: // F1
				{
					App_StrongHelp("Files_Properties");
					return EListenerAction_StopEvent;
				}
				break;
				default:
				{
					switch (pkey->caret.pos.i)
					{
						case Icon_Filename:
						{
							EditFile_CheckChanges(This, Prop_Filename);
						}
						break;
						case Icon_Title:
						{
							EditFile_CheckChanges(This, Prop_Title);
						}
						break;
						case Icon_Album:
						{
							EditFile_CheckChanges(This, Prop_Album);
						}
						break;
						case Icon_Box:
						{
							EditFile_CheckChanges(This, Prop_Box);
						}
						break;
						case Icon_Artist:
						{
							EditFile_CheckChanges(This, Prop_Artist);
						}
						break;
						case Icon_Collective:
						{
							EditFile_CheckChanges(This, Prop_Collective);
						}
						break;
						case Icon_Info:
						{
							EditFile_CheckChanges(This, Prop_Info);
						}
						break;
						case Icon_Section:
						{
							EditFile_CheckChanges(This, Prop_Section);
						}
						break;
						case Icon_Points:
						{
							const char* pdata = Icon_GetData(This->m_wnd, Icon_Points);

							if (*pdata)
							{
								EditFile_SetPoints(This, atoi(pdata));
							}
							else
								EditFile_SetPoints(This, -1);
						}
						break;
						case Icon_TrackNr:
						{
							EditFile_CheckChanges(This, Prop_TrackNr);
						}
						break;
						case Icon_Date:
						{
							EditFile_CheckChanges(This, Prop_Date);
						}
						break;
					}
					break;
				}
			}
		}
		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_Volume-1:
					case Icon_Volume:
					case Icon_Volume2:
					{
						EditFile_SetVolume(This, This->m_new.volume + 5*val);
					}
					break;
				}
			}
			return EListenerAction_StopEvent;
		}
		break;
		case EEvent_SelectionChange:
		{
			const Event_SelectionChange* ev = e->pData;
			const Mouse* m = Mouse_Get();

			if (ev->pWList == &DigitalCD.m_ClueList)
			{
				This->m_new.pclue = WList_Get(&DigitalCD.m_ClueList, ev->Index);
				EditFile_CheckChanges(This, Prop_Clue);
				EditFile_Refresh(This, Prop_RefreshContents | Prop_Clue);

				if (m->but == EBut_Select)
					Menu_Close();

				return EListenerAction_StopEvent;
			}
		}
		break;
		case EEvent_Message:
		case EEvent_MessageWantAck:
		{
			const Msg* msg = e->pData;

			switch(msg->hdr.action)
			{
				case EMsg_DataLoad:
				{
					const Msg_FileData* msg = e->pData;

					if ((msg->pos.i == Icon_Filename)
					&&  (This->m_AllowedProps & Prop_Filename)
					&&  (LoadTypes_FindNext(File_GetFileType(msg->name), NULL) != NULL))
					{
						Icon_SetData(This->m_wnd, Icon_Filename, msg->name);
						EditFile_CheckChanges(This, Prop_Filename);
					}

                    return EListenerAction_StopEvent; // any other drag is ignored
				}
				break;
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

static EListenerAction EditFile_OnDocEvent(void* pListener, const Event* pEvent)
{
	EditFile* This = pListener;
	const DocEvent* pe = pEvent->pData;

	if ((This->m_wnd == HWind_None)
	||  (This->m_flags & EditFlags_Updating))
		return EListenerAction_ContinueEvent;

	switch(pe->event)
	{
		case EDocEvent_RemoveElement:
		case EDocEvent_UpdateElement:
		{
			int pos = List_Find(This->m_pTracks, 0, pe->pElement);

			if (pos != -1)
			{
				EditFile_OnCancel(This);
				EditFile_ClearTracks(This);
				Window_Close(This->m_wndMain);
			}
		}
		break;
	}

	return EListenerAction_ContinueEvent;
}

void EditFile_EditNew(void* pView)
{
	EditFile_Edit(pView, NULL, NULL);
}

void EditFile_Edit(void* pView, List* pList, const FLTrack* pRef)
{
	EditFile* This = &TheEditFile;

	if (Options()->Player.bParentalLock)
		return;

	if (This->m_wnd == HWind_None)
	{
		memset(This, 0, sizeof(*This));
		This->m_wnd = HWind_None;
		This->m_wndMain = HWind_None;

		try
		{
			This->m_pTracks = New_List();

			This->m_wndMain = throw_Window_CreateFrom("PropFile", "TPrMnH");
			throw_Window_RegisterEventHandler(This->m_wndMain, EditMain_EventHandler, This, false);
			throw_Task_AddListener(EEvent_OptionParentalLock, EditFile_Listener, This, false);

			This->m_wnd = throw_Window_CreateFrom("PrFile", "TPropH");
			throw_Window_RegisterEventHandler(This->m_wnd, EditFile_EventHandler, This, false);
			throw_Window_AddPane(This->m_wndMain, This->m_wnd);

			This->m_pWCollectiveList = throw_New_WStringList(DigitalCD.pCollectivesCol, This->m_wnd, Icon_Collective);
			This->m_pWArtistList = throw_New_WStringList(DigitalCD.pArtistsCol, This->m_wnd, Icon_Artist);
			This->m_pWBoxList = throw_New_WStringList(DigitalCD.pBoxesCol, This->m_wnd, Icon_Box);
			This->m_pWAlbumList = throw_New_WStringList(DigitalCD.pAlbumsCol, This->m_wnd, Icon_Album);
			throw_DocEvents_AddListener(NULL, This, EditFile_OnDocEvent);
		}
		catch
		{
			EditFile_NotEdit();
			App_ReportException();
			return;
		}
		catch_end
	}

	This->m_pView = pView;

	EditFile_ClearTracks(This);

	try
	{
		if (pList && List_Count(pList))
		{
			ListNode* pNode = NULL;
			const FLTrack* pTrack;

			pNode = NULL;

			while ((pNode = List_GetSuccessor(pList, pNode)) != NULL)
			{
				pTrack = List_GetNodeData(pList, pNode);

				List_InsertBefore(This->m_pTracks, NULL, pTrack);
			}

			This->m_flags &= ~EditFlags_Create;
		}
		else if (!pList && pView)
		{
			// Creation mode
			FileList* pOwner = (FileList*) View_GetDocument(This->m_pView);
			FLTrack* pTrack = throw_New_FLTrack(pOwner, 0, &nil);

			List_InsertBefore(This->m_pTracks, NULL, pTrack);

			This->m_flags |= EditFlags_Create;
		}
	}
	catch
	{
		App_ReportException();
		EditFile_ClearTracks(This);
	}
	catch_end

	EditFile_SetRefTrack(This, pRef);

	if (!List_Count(This->m_pTracks))
	{
		Window_Close(This->m_wndMain);
	}
	else
	{
		EditFile_BuildProperties(This);
		EditFile_BuildCommonStatus(This);

		Window_Open(This->m_wndMain);
		Icon_SetFocus(This->m_wnd, Icon_Title, -1);
	}
}

void EditFile_NotEdit(void)
{
	EditFile* This = &TheEditFile;

	if (This->m_pTracks)
	{
		EditFile_ClearTracks(This);
		Delete_List(This->m_pTracks);
	}

	Delete_WStringList(This->m_pWCollectiveList);
	Delete_WStringList(This->m_pWArtistList);
	Delete_WStringList(This->m_pWBoxList);
	Delete_WStringList(This->m_pWAlbumList);

	if (This->m_wnd != HWind_None)
	{
		DocEvents_RemoveListener(NULL, This, EditFile_OnDocEvent);
		Task_RemoveListener(EEvent_OptionParentalLock, EditFile_Listener, This);
		Window_DeRegisterEventHandler(This->m_wnd, EditFile_EventHandler, This);
		Window_Delete(This->m_wnd);
	}
	if (This->m_wndMain != HWind_None)
	{
		Window_DeRegisterEventHandler(This->m_wnd, EditMain_EventHandler, This);
		Window_Delete(This->m_wndMain);
	}

	mem_free(This->m_ref.filename);
	mem_free(This->m_ref.title);
	mem_free(This->m_ref.album);
	mem_free(This->m_ref.box);
	mem_free(This->m_ref.artist);
	mem_free(This->m_ref.collective);
	mem_free(This->m_ref.info);
	mem_free(This->m_ref.date);

	memset(This, 0, sizeof(*This));
	This->m_wnd = HWind_None;
}
