/*
**	SequenceWin.c
**
**	Copyright (C) 1993,94,95,96 Bernardo Innocenti
**
**	Sequence editor handling functions.
*/

#include <intuition/intuition.h>
#include <intuition/gadgetclass.h>
#include <libraries/gadtools.h>

#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/gadtools.h>
#include <proto/xmodule.h>


#include "XModulePriv.h"
#include "Gui.h"



/* Gadget IDs */

enum
{
	GD_SequenceGroup0,
		GD_SequenceGroup1,
			GD_SequenceGroup2,
				GD_PattList,
				GD_PattName,
			GD_SequenceGroup3,
				GD_PattAdd,
				GD_PattDel,
				GD_PattUp,
				GD_PattDown,
		GD_SequenceGroup4,
			GD_SeqList,
		GD_SequenceGroup5,
			GD_SeqInsert,
			GD_SeqDel,
			GD_SeqUp,
			GD_SeqDown,

	Sequence_CNT
};



/* Local function prototypes */

static void SequencePostClose	(void);

static void SeqListClicked		(struct WinUserData *wud);
static void PattAddClicked		(struct WinUserData *wud);
static void PattDelClicked		(struct WinUserData *wud);
static void PattUpClicked		(struct WinUserData *wud);
static void PattDownClicked		(struct WinUserData *wud);
static void PattNameClicked		(struct WinUserData *wud);
static void PattListClicked		(struct WinUserData *wud);
static void SeqDelClicked		(struct WinUserData *wud);
static void SeqUpClicked		(struct WinUserData *wud);
static void SeqDownClicked		(struct WinUserData *wud);
static void SeqInsertClicked	(struct WinUserData *wud);


struct Gadget	*SequenceGadgets[Sequence_CNT];
struct List		 SequenceList;
struct List		 PatternsList;
static ULONG	 SequenceSecs = 0, SequenceMicros = 0;
static ULONG	 PatternSecs = 0, PatternMicros = 0;



static ULONG SequenceArgs[] =
{
	HGROUP_KIND, BBFT_RIDGE,
		VGROUP_KIND, 0,
			LISTVIEW_KIND,	(ULONG)PattListClicked,	MSG_PATTERNS_GAD,	(ULONG)&PatternsList,	TAG_DONE,
			STRING_KIND,	(ULONG)PattNameClicked,	MSG_UNDERSCORE_NAME_GAD,	128, TAG_DONE,
			ENDGROUP_KIND,
		VGROUP_KIND, 0,
			BUTTON_KIND,	(ULONG)PattAddClicked,	MSG_UNDERSCORE_ADD_GAD,	TAG_DONE,
			BUTTON_KIND,	(ULONG)PattDelClicked,	MSG_DEL_GAD,			TAG_DONE,
			BUTTON_KIND,	(ULONG)PattUpClicked,	MSG_UP_GAD,				TAG_DONE,
			BUTTON_KIND,	(ULONG)PattDownClicked,	MSG_DOWN_GAD,			TAG_DONE,
			ENDGROUP_KIND,
		ENDGROUP_KIND,
	HGROUP_KIND, BBFT_RIDGE,
		LISTVIEW_KIND,	(ULONG)SeqListClicked,	MSG_SEQUENCE_GAD,	(ULONG)&SequenceList,	TAG_DONE,
		VGROUP_KIND, 0,
			BUTTON_KIND,	(ULONG)SeqInsertClicked,	MSG_UNDERSCORE_INS_GAD,		TAG_DONE,
			BUTTON_KIND,	(ULONG)SeqDelClicked,		MSG_DEL_GAD,				TAG_DONE,
			BUTTON_KIND,	(ULONG)SeqUpClicked,		MSG_UNDERSCORE_UP_GAD,		TAG_DONE,
			BUTTON_KIND,	(ULONG)SeqDownClicked,		MSG_UNDERSCORE_DOWN_GAD,	TAG_DONE,
			ENDGROUP_KIND,
		ENDGROUP_KIND,
	ENDGROUP_KIND
};



XDEF LONG SequenceWinTags[] =
{
	XMWIN_LayoutArgs,	(LONG)SequenceArgs,
	XMWIN_GCount,		Sequence_CNT,
	XMWIN_Title,		MSG_SEQUENCE_TITLE,
	XMWIN_WindowFlags,	WFLG_CLOSEGADGET,
	XMWIN_IDCMPFlags,	BUTTONIDCMP|LISTVIEWIDCMP|IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW,
	XMWIN_PostOpenFunc,	(LONG)UpdatePatternList,
	XMWIN_PostCloseFunc,(LONG)SequencePostClose,
	XMWIN_HelpNode,		(LONG)"Sequence",
	TAG_DONE
};



static void SequencePostClose (void)
{
	while (!IsListEmpty (&SequenceList))
		RemListViewNode (SequenceList.lh_Head);

	while (!IsListEmpty (&PatternsList))
		RemListViewNode (PatternsList.lh_Head);
}



/**********************/
/* Sequence Functions */
/**********************/

GLOBALCALL void UpdateSequenceList (void)
{
	struct WinUserData *wud = WDescr[WID_SEQUENCE].Wud;
	struct SongInfo *si;
	ULONG i;
	UWORD pos;

	if (wud && wud->Win)
	{
		GT_SetGadgetAttrs (wud->Gadgets[GD_SeqList], wud->Win, NULL,
			GTLV_Labels, ~0,
			TAG_DONE);

		while (!IsListEmpty (&SequenceList))
			RemListViewNode (SequenceList.lh_Head);

		if (si = xmLockActiveSong (SM_SHARED))
		{
			for (i = 0 ; i < si->Length; i++)
			{
				pos = si->Sequence[i];
				AddListViewNode (&SequenceList, "%03lu - %03ld %s", i, pos,
					(pos >= si->NumPatterns) ? (STRPTR)"--none--" : si->Patt[pos]->Name);
			}

			GT_SetGadgetAttrs (wud->Gadgets[GD_SeqList], wud->Win, NULL,
				GTLV_Labels, &SequenceList,
				GTLV_Selected, si->CurrentPos,
				GTLV_MakeVisible, si->CurrentPos,
				TAG_DONE);

			ReleaseSemaphore (&si->Lock);
		}
	}
}



GLOBALCALL void UpdatePatternList (void)
{
	struct WinUserData *wud = WDescr[WID_SEQUENCE].Wud;
	struct SongInfo *si;
	struct Pattern *patt;
	ULONG i;

	if (wud && wud->Win)
	{
		GT_SetGadgetAttrs (wud->Gadgets[GD_PattList], wud->Win, NULL,
			GTLV_Labels, ~0,
			TAG_DONE);

		while (!IsListEmpty (&PatternsList))
			RemListViewNode (PatternsList.lh_Head);


		if (si = xmLockActiveSong (SM_SHARED))
		{
			for (i = 0 ; i < si->NumPatterns; i++)
			{
				if (patt = si->Patt[i])
				{
					AddListViewNode (&PatternsList, "%03lu (%lu,%lu) %s",
						i, patt->Tracks, patt->Lines,
						patt->Name ? patt->Name : (STRPTR) STR(MSG_UNNAMED));
				}
				else AddListViewNode (&PatternsList, STR(MSG_EMPTY));
			}

			GT_SetGadgetAttrs (wud->Gadgets[GD_PattName], wud->Win, NULL,
				GTST_String, si->NumPatterns ? si->Patt[si->CurrentPatt]->Name : NULL,
				TAG_DONE);

			GT_SetGadgetAttrs (wud->Gadgets[GD_PattList], wud->Win, NULL,
				GTLV_Labels,		&PatternsList,
				GTLV_Selected,		si->CurrentPatt,
				GTLV_MakeVisible,	si->CurrentPatt,
				TAG_DONE);

			ReleaseSemaphore (&si->Lock);
		}

		UpdateSequenceList();
	}

	UpdatePattern();
}



/********************/
/* Sequence Gadgets */
/********************/

static void SeqListClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (IntuiMsg.Code == si->CurrentPos)
		{
			/* Check Double Click */
			if (DoubleClick (SequenceSecs, SequenceMicros, IntuiMsg.Seconds, IntuiMsg.Micros))
			{
				si->Sequence[si->CurrentPos] = si->CurrentPatt;
				UpdateSequenceList();
			}
		}

		si->CurrentPos = IntuiMsg.Code;

		ReleaseSemaphore (&si->Lock);
	}

	SequenceSecs	= IntuiMsg.Seconds;
	SequenceMicros	= IntuiMsg.Micros;
}



static void PattAddClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (si->NumPatterns >= MAXPATTERNS)
			DisplayBeep (Scr);
		else
		{
			xmAddPatternA (si, NULL);
			UpdateSongInfo();	/* this also calls UpdatePatternList() */
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void PattDelClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (si->NumPatterns <= 1)
			DisplayBeep (Scr);
		else
			xmRemPattern (si, si->CurrentPatt, 0);

		ReleaseSemaphore (&si->Lock);
	}

	UpdateSongInfo();
}



static void PattUpClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (si->CurrentPatt == 0)
			DisplayBeep (Scr);
		else
		{
			struct Pattern *tmp_patt;

			/* Swap pattern with its predecessor */
			tmp_patt = si->Patt[si->CurrentPatt];
			si->Patt[si->CurrentPatt] = si->Patt[si->CurrentPatt - 1];
			si->Patt[si->CurrentPatt - 1] = tmp_patt;

			SetAttrs (si,
				SNGA_CurrentPatt, si->CurrentPatt - 1,
				TAG_DONE);

			UpdatePatternList();
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void PattDownClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (si->CurrentPatt >= si->NumPatterns - 1)
			DisplayBeep (Scr);
		else
		{
			struct Pattern *tmp_patt;

			/* Swap pattern with its successor */
			tmp_patt = si->Patt[si->CurrentPatt];
			si->Patt[si->CurrentPatt] = si->Patt[si->CurrentPatt + 1];
			si->Patt[si->CurrentPatt + 1] = tmp_patt;

			SetAttrs (si,
				SNGA_CurrentPatt, si->CurrentPatt + 1,
				TAG_DONE);

			UpdatePatternList();
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void PattNameClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		xmSetPattern (si, si->CurrentPatt,
			PATTA_Name,	GetString (wud->Gadgets[GD_PattName]),
			TAG_DONE);

		UpdatePatternList();
		ReleaseSemaphore (&si->Lock);
	}
}



static void PattListClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_SHARED))
	{
		if (IntuiMsg.Code == si->CurrentPatt)
		{
			/* Check Double Click */
			if (DoubleClick (PatternSecs, PatternMicros, IntuiMsg.Seconds, IntuiMsg.Micros))
				NewWindow(WID_PATTERN);
		}
		else
		{
			si->CurrentPatt = IntuiMsg.Code;

			GT_SetGadgetAttrs (wud->Gadgets[GD_PattName], wud->Win, NULL,
				GTST_String, si->Patt[si->CurrentPatt]->Name,
				TAG_DONE);

			UpdatePattern();
		}

		ReleaseSemaphore (&si->Lock);
	}

	PatternSecs = IntuiMsg.Seconds;
	PatternMicros = IntuiMsg.Micros;
}



static void SeqDelClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		ULONG i;

		/* You can't have an empty sequence */
		if (si->Length == 1)
			DisplayBeep (Scr);
		else
		{
			/* Shift positions back */
			for (i = si->CurrentPos ; i < si->Length; i++)
				si->Sequence[i] = si->Sequence[i+1];

			xmSetSongLen (si, si->Length - 1);

			UpdateSongInfo();	/* Will call also UpdateSequenceList() */
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void SeqUpClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		UWORD temp;

		if (si->CurrentPos < 1)
			DisplayBeep (Scr);
		else
		{
			temp = si->Sequence[si->CurrentPos];
			si->Sequence[si->CurrentPos] = si->Sequence[si->CurrentPos - 1];
			si->Sequence[si->CurrentPos - 1] = temp;

			si->CurrentPos--;
			UpdateSequenceList();
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void SeqDownClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		UWORD temp;

		if (si->CurrentPos >= si->Length - 1)
			DisplayBeep (Scr);
		else
		{
			temp = si->Sequence[si->CurrentPos];
			si->Sequence[si->CurrentPos] = si->Sequence[si->CurrentPos + 1];
			si->Sequence[si->CurrentPos + 1] = temp;

			si->CurrentPos++;
			UpdateSequenceList();
		}

		ReleaseSemaphore (&si->Lock);
	}
}



static void SeqInsertClicked (struct WinUserData *wud)
{
	struct SongInfo *si;

	if (si = xmLockActiveSong (SM_EXCLUSIVE))
	{
		if (!(xmSetSongLen (si, si->Length + 1)))
			DisplayBeep (Scr);
		else
		{
			ULONG i;

			for (i = si->Length - 1; i > si->CurrentPos; i--)
				si->Sequence[i] = si->Sequence[i-1];

			si->Sequence[si->CurrentPos] = si->CurrentPatt;
		}

		ReleaseSemaphore (&si->Lock);

		UpdateSongInfo();	/* This will also update the sequence list */
	}
}

