/*
** OpenURL - MUI preferences for openurl.library
** Written by Troels Walsted Hansen <troels@thule.no>
** Placed in the public domain.
**
** This module contains the code to the AppList Group.mui subclass. It
** manages a single list of applications with associated buttons.
*/

#include "prefs_common.h"
#include "prefs_main.h"
#include "prefs_app.h"
#include "prefs_prefswin.h"
#include "prefs_applist.h"

/**************************************************************************/

static ULONG mNew          (struct IClass *cl, Object *obj, struct opSet *msg);
static ULONG mGet          (struct IClass *cl, Object *obj, struct opGet *msg);
static ULONG mAdd          (struct IClass *cl, Object *obj, struct MUIP_AppList_Add *msg);
static ULONG mEdit         (struct IClass *cl, Object *obj, struct MUIP_AppList_Edit *msg);
static ULONG mClone        (struct IClass *cl, Object *obj, struct MUIP_AppList_Clone *msg);
static ULONG mDelete       (struct IClass *cl, Object *obj, struct MUIP_AppList_Delete *msg);
static ULONG mActiveChanged(struct IClass *cl, Object *obj, struct MUIP_AppList_ActiveChanged *msg);

static SAVEDS ASM APTR ConstructFunc(REG(a0) struct Hook *hook, REG(a2) APTR pool, REG(a1) UBYTE *node);
static SAVEDS ASM VOID DestructFunc (REG(a0) struct Hook *hook, REG(a2) APTR pool, REG(a1) UBYTE *node);
static SAVEDS ASM LONG DisplayFunc(REG(a0) struct Hook *hook, REG(a2) char **array, REG(a1) UBYTE *node);

/**************************************************************************/

static ULONG mNew(struct IClass *cl, Object *obj, struct opSet *msg)
{
	Object *appl, *addb, *editb, *cloneb, *deleteb;

	obj = (Object *)DoSuperNew(cl, obj,
		Child, appl = ListviewObject,
			MUIA_CycleChain,        TRUE,
			MUIA_Listview_DragType, MUIV_Listview_DragType_Immediate,
			MUIA_Listview_List,     ListObject,
				InputListFrame,
				MUIA_List_DragSortable, TRUE,
				MUIA_List_Title,        TRUE,
				MUIA_List_Format,       ",",
			End,
		End,
		Child, ColGroup(4),
			Child, addb    = SimpleButton("_Add..."),
			Child, editb   = SimpleButton("_Edit..."),
			Child, cloneb  = SimpleButton("C_lone..."),
			Child, deleteb = SimpleButton("_Delete"),
		End,
		TAG_MORE, msg->ops_AttrList);

	if(obj)
	{
		struct AppList_Data *data = INST_DATA(cl, obj);
		struct TagItem *tags, *tag;
		memset(data, '\0', sizeof(struct AppList_Data));

		/* init instance data */

		data->AppLstObj    = appl;
		data->AddButObj    = addb;
		data->EditButObj   = editb;
		data->CloneButObj  = cloneb;
		data->DeleteButObj = deleteb;

		for(tags = msg->ops_AttrList; tag = NextTagItem(&tags); )
		{
			switch(tag->ti_Tag)
			{
				case MUIA_AppList_NodeNameOffset:
					data->NodeNameOffset = tag->ti_Data;
					break;

				case MUIA_AppList_NodePathOffset:
					data->NodePathOffset = tag->ti_Data;
					break;

				case MUIA_AppList_NodeSize:
					data->NodeSize = tag->ti_Data;
					break;

				case MUIA_AppList_NodeEditWinClass:
					data->NodeEditWinClass = tag->ti_Data;
					break;

				case MUIA_AppList_NodeEditWinIDAttr:
					data->NodeEditWinIDAttr = tag->ti_Data;
					break;

				case MUIA_AppList_NodeEditWinLstAttr:
					data->NodeEditWinLstAttr = tag->ti_Data;
					break;

				case MUIA_AppList_NewNodeName:
					data->NewNodeName = (STRPTR)tag->ti_Data;
					break;
			}
		}

		/* add hooks */

		data->ConstructHook.h_Entry = (HOOKFUNC)ConstructFunc;
		data->ConstructHook.h_Data  = data;
		set(appl, MUIA_List_ConstructHook, &data->ConstructHook);

		data->DestructHook.h_Entry = (HOOKFUNC)DestructFunc;
		data->DestructHook.h_Data  = data;
		set(appl, MUIA_List_DestructHook, &data->DestructHook);

		data->DisplayHook.h_Entry = (HOOKFUNC)DisplayFunc;
		data->DisplayHook.h_Data  = data;
		set(appl, MUIA_List_DisplayHook, &data->DisplayHook);

		/* listview */

		DoMethod(appl, MUIM_Notify, MUIA_List_Active, MUIV_EveryTime, obj, 1, MUIM_AppList_ActiveChanged);
		DoMethod(appl, MUIM_Notify, MUIA_Listview_DoubleClick, TRUE, obj, 1, MUIM_AppList_Edit);

		/* buttons */

		DoMethod(obj, MUIM_MultiSet, MUIA_CycleChain, TRUE, addb, editb, 
		         cloneb, deleteb, NULL);

		DoMethod(obj, MUIM_MultiSet, MUIA_Disabled, TRUE, 
		        editb, cloneb, deleteb, NULL);

		/* list buttons */

		DoMethod(addb,    MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_AppList_Add);
		DoMethod(editb,   MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_AppList_Edit);
		DoMethod(cloneb,  MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_AppList_Clone);
		DoMethod(deleteb, MUIM_Notify, MUIA_Pressed, FALSE, obj, 1, MUIM_AppList_Delete);
	}

	return((ULONG)obj);
}

/**************************************************************************/

static ULONG mGet(struct IClass *cl, Object *obj, struct opGet *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);

	switch(msg->opg_AttrID)
	{
		case MUIA_AppList_ListObj:
			*(msg->opg_Storage) = (ULONG)data->AppLstObj;
			return(TRUE);
	}

	return(DoSuperMethodA(cl, obj, (Msg)msg));
}

/**************************************************************************/

static ULONG mAdd(struct IClass *cl, Object *obj, struct MUIP_AppList_Add *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);
	UBYTE *node = AllocMem(data->NodeSize, MEMF_CLEAR);

	if(!node) return(FALSE);
	strcpy(node + data->NodeNameOffset, data->NewNodeName);
	DoMethod(data->AppLstObj, MUIM_List_InsertSingle, node, MUIV_List_Insert_Bottom);
	FreeMem(node, data->NodeSize);
	set(data->AppLstObj, MUIA_List_Active, xget(data->AppLstObj, MUIA_List_InsertPosition));
	DoMethod(obj, MUIM_AppList_Edit);

	return(TRUE);
}

/**************************************************************************/

static ULONG mEdit(struct IClass *cl, Object *obj, struct MUIP_AppList_Edit *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);
	UBYTE *node;

	DoMethod(data->AppLstObj, MUIM_List_GetEntry, MUIV_List_GetEntry_Active, &node);

	if(node)
	{
		DoMethod(_app(obj), MUIM_App_OpenWin, data->NodeEditWinClass, 
		         data->NodeEditWinIDAttr, node, data->NodeEditWinLstAttr, 
		         data->AppLstObj, TAG_END);
	}

	return(TRUE);
}

/**************************************************************************/

static ULONG mClone(struct IClass *cl, Object *obj, struct MUIP_AppList_Clone *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);
	ULONG active;
	UBYTE *node;

	get(data->AppLstObj, MUIA_List_Active, &active);
	DoMethod(data->AppLstObj, MUIM_List_GetEntry, active, &node);

	if(node)
	{
		DoMethod(data->AppLstObj, MUIM_List_InsertSingle, node, ++active);
		set(data->AppLstObj, MUIA_List_Active, active);
		DoMethod(obj, MUIM_AppList_Edit);
	}

	return(TRUE);
}

/**************************************************************************/

static ULONG mDelete(struct IClass *cl, Object *obj, struct MUIP_AppList_Delete *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);
	ULONG active;
	UBYTE *node;

	get(data->AppLstObj, MUIA_List_Active, &active);
	DoMethod(data->AppLstObj, MUIM_List_GetEntry, active, &node);

	if(node)
	{
		DoMethod(_app(obj), MUIM_App_CloseWin, data->NodeEditWinIDAttr, node);
		DoMethod(data->AppLstObj, MUIM_List_Remove, active);
	}

	return(TRUE);
}

/**************************************************************************/

static ULONG mActiveChanged(struct IClass *cl, Object *obj, struct MUIP_AppList_ActiveChanged *msg)
{
	struct AppList_Data *data = INST_DATA(cl,obj);
	APTR node;
	DoMethod(data->AppLstObj, MUIM_List_GetEntry, MUIV_List_GetEntry_Active, &node);
	DoMethod(obj, MUIM_MultiSet, MUIA_Disabled, !node, data->EditButObj,
	         data->CloneButObj, data->DeleteButObj, NULL);
	return(TRUE);
}

/**************************************************************************/

SAVEDS ASM ULONG AppList_Dispatcher(REG(a0) struct IClass *cl, REG(a2) Object *obj, REG(a1) Msg msg)
{
	switch(msg->MethodID)
	{
		case OM_NEW                    : return(mNew          (cl,obj,(APTR)msg));
		case OM_GET                    : return(mGet          (cl,obj,(APTR)msg));
		case MUIM_AppList_Add          : return(mAdd          (cl,obj,(APTR)msg));
		case MUIM_AppList_Edit         : return(mEdit         (cl,obj,(APTR)msg));
		case MUIM_AppList_Clone        : return(mClone        (cl,obj,(APTR)msg));
		case MUIM_AppList_Delete       : return(mDelete       (cl,obj,(APTR)msg));
		case MUIM_AppList_ActiveChanged: return(mActiveChanged(cl,obj,(APTR)msg));
	}

	return(DoSuperMethodA(cl,obj,msg));
}

/**************************************************************************/

static SAVEDS ASM APTR ConstructFunc(REG(a0) struct Hook *hook, REG(a2) APTR pool, REG(a1) UBYTE *node)
{
	struct AppList_Data *data = (struct AppList_Data *)hook->h_Data;
	UBYTE *new;
	if((new = AsmAllocPooled(pool, data->NodeSize, SysBase)))
		memcpy(new, node, data->NodeSize);
	return(new);
}

/**************************************************************************/

static SAVEDS ASM VOID DestructFunc (REG(a0) struct Hook *hook, REG(a2) APTR pool, REG(a1) UBYTE *node)
{
	struct AppList_Data *data = (struct AppList_Data *)hook->h_Data;
	AsmFreePooled(pool, node, data->NodeSize, SysBase);
}

/**************************************************************************/

static SAVEDS ASM LONG DisplayFunc(REG(a0) struct Hook *hook, REG(a2) char **array, REG(a1) UBYTE *node)
{
	if(node)
	{
		struct AppList_Data *data = (struct AppList_Data *)hook->h_Data;
		*array++ = (node + data->NodeNameOffset);
		*array   = (node + data->NodePathOffset);
	}
	else
	{
		*array++ = MUIX_B "Name";
		*array   = MUIX_B "Path";
	}

	return(0);
}
