/*  
 * generic_list.c
 *
 * Copyright (c) 1992 Sun Microsystems, Inc.  All rights reserved.
 */

#include <xview/xview.h>
#include <xview/panel.h>

typedef	struct	list_props
{
	Xv_opaque	(*get_item)();
	Xv_opaque	(*put_item)();
	Xv_opaque	(*label)();
	int		cur_select;
	Xv_opaque	list;
	Xv_opaque	add;
	Xv_opaque	delete;
	Xv_opaque	change;
	Xv_opaque	find;
} Lst;

static int
lst_select(Panel          item,
	   char          *string,
	   Xv_opaque      client_data,
	   Panel_list_op  op,
	   Event         *event,
	   int            row)
{
	Lst	*lst = (Lst *)xv_get(item, PANEL_CLIENT_DATA);

	if(lst->cur_select != -1)
	{
		xv_set(item, PANEL_LIST_SELECTED, lst->cur_select, FALSE, NULL);
		lst->cur_select = -1;
	}
        switch(op)
	{
	case PANEL_LIST_OP_DESELECT:
		(*lst->put_item)(NULL);
		xv_set(lst->delete, PANEL_INACTIVE, TRUE, NULL);
		xv_set(lst->change, PANEL_INACTIVE, TRUE, NULL);
		break;

	case PANEL_LIST_OP_SELECT:
		(*lst->put_item)(client_data);
		xv_set(lst->delete, PANEL_INACTIVE, FALSE, NULL);
		xv_set(lst->change, PANEL_INACTIVE, FALSE, NULL);
		lst->cur_select = row;
		break;

	case PANEL_LIST_OP_VALIDATE:
		break;

	case PANEL_LIST_OP_DELETE:
		lst_delete(lst, row);
		break;
	}

	return XV_OK;
}

static int
lst_add(Panel_item item, Event *event)
{
	Xv_opaque	new;
	Lst		*lst = (Lst *)xv_get(item, PANEL_CLIENT_DATA);
	int		nrows = xv_get(lst->list, PANEL_LIST_NROWS);
	char		*label;

	new = (*lst->get_item)(NULL);

	if(lst->cur_select != -1)
	{
		xv_set(lst->list,
			PANEL_LIST_SELECTED, lst->cur_select, FALSE,
			NULL);
		lst->cur_select = -1;
	}

 	label = (char *)(*lst->label)(new);
	if (!label || !strlen(label)) label = "No description";
	xv_set(lst->list,
		PANEL_LIST_INSERT,	nrows,
		PANEL_LIST_STRING,	nrows, label,
		PANEL_LIST_CLIENT_DATA,	nrows, new,
		NULL);
	(*lst->put_item)(NULL);

	xv_set(lst->delete, PANEL_INACTIVE, TRUE, NULL);
	xv_set(lst->change, PANEL_INACTIVE, TRUE, NULL);
}

static int
lst_change(Panel_item item, Event *event)
{
	Lst		*lst = (Lst *)xv_get(item, PANEL_CLIENT_DATA);
	Xv_opaque	cur;

	if(lst->cur_select == -1)
	{
		abort();
	}
	cur = (Xv_opaque)xv_get(lst->list,
			PANEL_LIST_CLIENT_DATA, lst->cur_select);
	cur = (*lst->get_item)(cur);

	xv_set(lst->list,
		PANEL_LIST_STRING,	lst->cur_select ,(*lst->label)(cur),
		NULL);
}

static int
lst_delete(Panel_item item, Event *event)
{
	Lst	*lst = (Lst *)xv_get(item, PANEL_CLIENT_DATA);
	Xv_opaque	data;

	if(lst->cur_select == -1)
	{
		abort();
	}
	data = xv_get(lst->list, PANEL_LIST_CLIENT_DATA, lst->cur_select);
	free((void *)data);

	(*lst->put_item)(NULL);

	xv_set(lst->list,
		PANEL_LIST_SELECT,	lst->cur_select, FALSE,
		PANEL_LIST_DELETE,	lst->cur_select,
		NULL);
	lst->cur_select = -1;
	xv_set(lst->delete, PANEL_INACTIVE, TRUE, NULL);
	xv_set(lst->change, PANEL_INACTIVE, TRUE, NULL);
}

init_lst(Xv_opaque   list,
	 Xv_opaque   add,
	 Xv_opaque   delete,
	 Xv_opaque   change,
	 Xv_opaque   find,
	 Xv_opaque (*get_item)(),
	 Xv_opaque (*put_item)(),
	 Xv_opaque (*label)())
{
	Lst	*nl;

	nl = (Lst *)malloc(sizeof(Lst));
	nl->cur_select = -1;
	nl->get_item = get_item;
	nl->put_item = put_item;
	nl->label = label;
	nl->list = list;
	nl->add = add;
	nl->delete = delete;
	nl->change = change;

	xv_set(add,
		PANEL_NOTIFY_PROC, lst_add,
		PANEL_CLIENT_DATA, nl,
		NULL);
	xv_set(delete,
		PANEL_NOTIFY_PROC, lst_delete,
		PANEL_CLIENT_DATA, nl,
		NULL);
	xv_set(change,
		PANEL_NOTIFY_PROC, lst_change,
		PANEL_CLIENT_DATA, nl,
		NULL);
	xv_set(list,
		PANEL_NOTIFY_PROC, lst_select,
		PANEL_CLIENT_DATA, nl,
		NULL);
}

Xv_opaque *
get_list(Xv_opaque list)
{
	Xv_opaque	*item;
	Lst		*lst = (Lst *)xv_get(list, PANEL_CLIENT_DATA);
	int		count, i;

	count = xv_get(list, PANEL_LIST_NROWS);
	item = (Xv_opaque *)malloc((count+1) * sizeof(Xv_opaque *));

	for(i = 0; i < count; i++)
	{
		item[i] = xv_get(list, PANEL_LIST_CLIENT_DATA, i);
	}
	item[i] = 0;
	return(item);
}

set_list(Xv_opaque list, Xv_opaque *item)
{
	Lst		*lst = (Lst *)xv_get(list, PANEL_CLIENT_DATA);
	int		nrows = xv_get(list, PANEL_LIST_NROWS);
	int		i, j;
	Xv_opaque	data;

	for(i = 0, j = 0; i < nrows; i++)
	{
		data = xv_get(list, PANEL_LIST_CLIENT_DATA, j);
		free((void *)data);
		if(item && item[j])
		{
			xv_set(list,
				PANEL_LIST_STRING, j, (*lst->label)(item[j]),
				PANEL_LIST_CLIENT_DATA, j, item[j],
				NULL);
			j++;
		}
		else
		{
			xv_set(list, PANEL_LIST_DELETE, j, NULL);
		}
	}

	if(item == NULL)
	{
		(*lst->put_item)(NULL);
		return;
	}

	for(; item[j]; j++)
	{
		xv_set(list,
			PANEL_LIST_INSERT,	j,
			PANEL_LIST_STRING,	j, (*lst->label)(item[j]),
			PANEL_LIST_CLIENT_DATA,	j, item[j],
			NULL);
	}
}

char **
get_labels(Xv_opaque list)
{
	Lst		*lst = (Lst *)xv_get(list, PANEL_CLIENT_DATA);
	int		nrows = xv_get(lst->list, PANEL_LIST_NROWS);
	int		i;
	char		**lbls;

	lbls = (char **)malloc((nrows+1)*sizeof(char *));
	for(i = 0; i < nrows; i++)
	{
		lbls[i] = strdup((char *)xv_get(lst->list, PANEL_LIST_STRING, i));
	}
	lbls[i] = 0;
	return(lbls);
}

set_row(Xv_opaque list, int row)
{
	Lst	*lst = (Lst *)xv_get(list, PANEL_CLIENT_DATA);
	Xv_opaque client_data = xv_get(lst->list, PANEL_LIST_CLIENT_DATA, row);

	if(lst->cur_select != -1)
	{
		xv_set(lst->list, PANEL_LIST_SELECTED, lst->cur_select, FALSE, 
				NULL);
		lst->cur_select = -1;
	}

	if(row > xv_get(lst->list, PANEL_LIST_NROWS))
	{
		return(FALSE);
	}

	(*lst->put_item)(client_data);
	xv_set(lst->delete, PANEL_INACTIVE, FALSE, NULL);
	xv_set(lst->change, PANEL_INACTIVE, FALSE, NULL);
	lst->cur_select = row;
	return(TRUE);
}
