/*
 *	(c) Copyright 1991 Sun Microsystems, Inc.  All rights reserved.
 *	See LEGAL_NOTICE file for terms and restrictions.
 */

#ifndef lint
#ifdef sccs
static char sccsid[] = "@(#)array.c 1.19 91/05/06";
#endif
#endif

#include <X11/Xlib.h>
#include <xview/xv_xrect.h>
#include "r_impl.h"
#include "array_impl.h"

Pkg_private int 	array_tile_init();
Pkg_private Xv_opaque	array_tile_set_avlist();
Pkg_private Xv_opaque	array_tile_get_attr();
Pkg_private int 	array_tile_destroy();

void	array_tile_paint_proc();
void	array_tile_del_child_proc();
void	array_tile_compact();
void	array_tile_add_child_proc();
void	array_tile_set_geometry_proc();
void	array_tile_manage_child_proc();
void	calc_array_rect_size();
void	calc_child_row_column();
void	calc_child_rect();
void	array_tile_reset_dimensions();
void	array_tile_unmanage_child();
Rectobj	*position_to_childp();
void	rebuild_arrayp();
int	position_child();
void	position_children();

#define get_layout_data_private( _rinfo_ )	\
		(Array_layout_data *) ( _rinfo_ )->layout_data
#define get_layout_data(_rectobj_) 		\
		(Array_layout_data*)(RECTOBJ_PRIVATE( _rectobj_ )->layout_data)
#define SET_ROW 1
#define SET_COLUMN 2

/*ARGSUSED*/
Pkg_private int
array_tile_init(parent, array_tile, avlist)
	Xv_opaque	parent;
	Array_tile		array_tile;
	Attr_avlist	avlist;
{
	Array_tile_info	*ainfo;
	Array_tile_struct	*array_tile_object;
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(array_tile);

	ainfo = xv_alloc(Array_tile_info);
	array_tile_object = (Array_tile_struct*) array_tile;
	array_tile_object->private_data = (Xv_opaque) ainfo;

	ainfo->column_gap 	= 15;
	ainfo->row_gap 		= 15;
	ainfo->column_width 	= 40;
	ainfo->row_height	= 20;
	ainfo->layout		= ARRAY_TILE_LAYOUT_ROW;
	ainfo->auto_layout	= TRUE;
	ainfo->align		= ARRAY_TILE_ALIGN_CENTER;
	array_tile_reset_dimensions(ainfo, 4, 1);
	calc_array_rect_size(ainfo, &rinfo->rect);

	rinfo->set_geometry_proc = array_tile_set_geometry_proc;
	rinfo->manage_child_proc = array_tile_manage_child_proc;
	rinfo->add_child_proc = array_tile_add_child_proc;
	rinfo->del_child_proc = array_tile_del_child_proc;
	rinfo->paint_proc = array_tile_paint_proc;

	return(XV_OK);
}


Pkg_private Xv_opaque
array_tile_set_avlist(array_tile, avlist)
	Array_tile			array_tile;
	register Attr_avlist	avlist;
{
        register Array_tile_attr attr;
        register Array_tile_info *ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info		 *rinfo = RECTOBJ_PRIVATE(array_tile);
	Rectobj_info 		 *child_rinfo;
	int			 rows;
	int			 columns;

	if(*avlist != XV_END_CREATE) {
		Xv_opaque set_result;
		set_result =
		    xv_super_set_avlist(array_tile, ARRAY_TILE, avlist);
		if(set_result != XV_OK) {
			rectobj_reset_set_info(array_tile);
			return(set_result);
		}
	}

	while (attr = (Array_tile_attr) * avlist++)
	  switch (attr) {

		case ARRAY_TILE_N_COLUMNS:
			columns = (int)*avlist++;
			if(columns < 1)
				columns = 1;
			array_tile_reset_dimensions(ainfo, columns, 
					ainfo->n_rows);
			rebuild_arrayp(array_tile);
			calc_array_rect_size(ainfo, &rinfo->rect);
			ainfo->relayout = TRUE;
			break;

		case ARRAY_TILE_N_ROWS:
			rows = (int)*avlist++;
			if(rows < 1)
				rows = 1;
			array_tile_reset_dimensions(ainfo, ainfo->n_columns, 
				rows);
			rebuild_arrayp(array_tile);
			calc_array_rect_size(ainfo, &rinfo->rect);
			ainfo->relayout = TRUE;
			break;

		case ARRAY_TILE_COLUMN_GAP:
			ainfo->column_gap = (int)*avlist++;
			ainfo->relayout = TRUE;
			calc_array_rect_size(ainfo, &rinfo->rect);
			break;

		case ARRAY_TILE_ROW_GAP:
			ainfo->row_gap = (int)*avlist++;
			ainfo->relayout = TRUE;
			calc_array_rect_size(ainfo, &rinfo->rect);
			break;
 
		case ARRAY_TILE_COLUMN_WIDTH:
			ainfo->column_width = (int)*avlist++;
			ainfo->relayout = TRUE;
			calc_array_rect_size(ainfo, &rinfo->rect);
			break;

		case ARRAY_TILE_ROW_HEIGHT:
			ainfo->row_height = (int)*avlist++;
			ainfo->relayout = TRUE;
			calc_array_rect_size(ainfo, &rinfo->rect);
			break;

		case ARRAY_TILE_COLUMN:
			(void) position_child(array_tile, ainfo, 
				(Rectobj) *avlist, 
				(int) *(avlist+1), 0, 
				SET_COLUMN);
			avlist+=2;
			ainfo->relayout = TRUE;
			break;
			
		case ARRAY_TILE_ROW:
			(void) position_child(array_tile, ainfo, 
				(Rectobj) *avlist, 
				0, (int) *(avlist+1), 
				SET_ROW);
			avlist+=2;
			ainfo->relayout = TRUE;
			break;
			
		case ARRAY_TILE_POSITION:
			(void) position_child(array_tile, ainfo, 
				(Rectobj) *avlist, 
				(int) *(avlist+1), (int) *(avlist+2), 
				SET_ROW|SET_COLUMN);
				avlist+=3;
			ainfo->relayout = TRUE;
			break;

		case ARRAY_TILE_LAYOUT:
			ainfo->layout = (Array_tile_layout)*avlist++;
			ainfo->relayout = TRUE;
			break;

		case ARRAY_TILE_AUTO_LAYOUT:
			ainfo->relayout = 
			ainfo->auto_layout = (int) *avlist++;
			if(ainfo->auto_layout)
				array_tile_compact(ainfo);
			break;

		case ARRAY_TILE_ALIGN:
			ainfo->align = (Array_tile_align)*avlist++;
			ainfo->relayout = TRUE;
			break;

		case XV_END_CREATE:
			break;

	    default:
		avlist = attr_skip(attr, avlist);

	  }

	rectobj_set_delay_repaint(array_tile, TRUE);
	rectobj_finish_set(array_tile);
	/* The callbacks didn't do the work, make sure it gets done. */
	if(ainfo->relayout == TRUE) {
		position_children(ainfo);
		array_tile_set_geometry_proc(array_tile, 
				(Rect*)NULL, (Rect*)NULL);
	}
	rectobj_set_delay_repaint(array_tile, FALSE);
	return(XV_SET_DONE);
}


/*ARGSUSED*/
Pkg_private Xv_opaque
array_tile_get_attr(array_tile, status, which_attr, avlist)
	Array_tile		array_tile;
	int		*status;
	register Attr_attribute which_attr;
	Attr_avlist	avlist;
{
	Array_tile_info *ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj		child;
	Rectobj_info	*child_rinfo;

	switch (which_attr) {
		case ARRAY_TILE_COLUMN_GAP:
			return (Xv_opaque) ainfo->column_gap;

		case ARRAY_TILE_ROW_GAP:
			return (Xv_opaque) ainfo->row_gap;

		case ARRAY_TILE_COLUMN_WIDTH:
			return (Xv_opaque) ainfo->column_width;

		case ARRAY_TILE_ROW_HEIGHT:
			return (Xv_opaque) ainfo->row_height;

		case ARRAY_TILE_N_COLUMNS:
			return (Xv_opaque) ainfo->n_columns;

		case ARRAY_TILE_N_ROWS:
			return (Xv_opaque) ainfo->n_rows;

		case ARRAY_TILE_COLUMN:
			child = (Rectobj)*avlist;
			child_rinfo = RECTOBJ_PRIVATE(child);
			return (Xv_opaque) 
				((Array_layout_data*)child_rinfo->layout_data)
					->column;

		case ARRAY_TILE_ROW:
			child = (Rectobj)*avlist;
			child_rinfo = RECTOBJ_PRIVATE(child);
			return (Xv_opaque) 
				((Array_layout_data*)child_rinfo->layout_data)
					->row;

		case ARRAY_TILE_POSITION: {
			Array_layout_data layout_data;
			Rectobj		*childp;

			layout_data.column = (int)*avlist;
			layout_data.row = (int)*(avlist+1);
			layout_data.flags = 0;
			if(childp = position_to_childp(ainfo, &layout_data))
				return (Xv_opaque) *childp;
			return (Xv_opaque) NULL;
			}

		case ARRAY_TILE_LAYOUT:
			return (Xv_opaque) ainfo->layout;

		case ARRAY_TILE_AUTO_LAYOUT:
			return (Xv_opaque) ainfo->auto_layout;

		case ARRAY_TILE_ALIGN:
			return (Xv_opaque) ainfo->align;

		default:
			*status = XV_ERROR;
			return (Xv_opaque) 0;
	}
}

/*ARGSUSED*/
Pkg_private int
array_tile_destroy(array_tile, status)
	Array_tile		array_tile;
	Destroy_status	status;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(array_tile);

	if ((status == DESTROY_CHECKING) || (status == DESTROY_SAVE_YOURSELF))
		return XV_OK;

	while(rinfo->children)
		xv_destroy( RECTOBJ_LIST_HANDLE(rinfo->children) );

	free(ainfo->arrayp);
	free(ainfo);

	return XV_OK;
}


void
array_tile_paint_proc(array_tile, dpy, win, xrects)
        Array_tile array_tile;
        Display *dpy;
        Window win;
        Xv_xrectlist *xrects;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	register int i;

	for(i=0; i < ainfo->array_size; i++)
	    if(*(ainfo->arrayp+i))
		rectobj_paint_child(*(ainfo->arrayp+i), dpy, win, xrects);
}


void
array_tile_del_child_proc(array_tile, del_child)
	Array_tile	array_tile;
	Rectobj		del_child;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*del_child_rinfo;
	Array_layout_data *layout_data;
	Rectobj		*childp;
	int		unmanaged;
	int		rows, columns;

	del_child_rinfo = RECTOBJ_PRIVATE(del_child);
	layout_data = get_layout_data_private(del_child_rinfo);
	unmanaged = layout_data->flags & UNMANAGED;

	if(!unmanaged) {
		if(childp = position_to_childp(ainfo, layout_data))
			*childp = (Rectobj) NULL;
	}

	free(layout_data);
	del_child_rinfo->layout_data = (void*)NULL;

	if(unmanaged)
		return;

	ainfo->n_managed--;

	if(!ainfo->auto_layout)
		return;

	array_tile_compact(ainfo);

	/* see if the array_tile should shrink */

	columns = ainfo->n_columns;
	rows = ainfo->n_rows;

	switch(ainfo->layout) {
	  case ARRAY_TILE_LAYOUT_COLUMN:
		columns = ((ainfo->n_managed-1)/ainfo->n_rows)+1;
		break;
	  case ARRAY_TILE_LAYOUT_ROW:
		rows = ((ainfo->n_managed-1)/ainfo->n_columns)+1;
		break;
	}

	if((columns != ainfo->n_columns) || (rows != ainfo->n_rows)) {
		array_tile_reset_dimensions(ainfo, columns, rows);
		rebuild_arrayp(array_tile);
		calc_array_rect_size(ainfo,
			 &(RECTOBJ_PRIVATE(array_tile)->rect));
	}
	position_children(ainfo);
	array_tile_set_geometry_proc(array_tile, (Rect*) NULL, (Rect*) NULL);
}


/*
 * packs all children as tightly as possible into upper left corner.
 */
static void
array_tile_compact(ainfo)
	Array_tile_info	*ainfo;
{
	int		hole;	/* index to holes */
	int		index;	/* index to next non-hole */
	Rectobj		child;

	/* make a compact array out of a sparse one */

	if(!ainfo->auto_layout)
		return;

	for( index =0, hole=0; hole < ainfo->array_size; hole++) {

		/* skip until a hole is found */
		if(*(ainfo->arrayp+hole) != (Rectobj) NULL)
			continue;

		/* get next non-hole */
		index = hole;
		while(*(ainfo->arrayp+index) == (Rectobj) NULL) {
			index++;
			if(index >= ainfo->array_size)
				return; /* all done */
		}

		/* move the rectobj at index to hole */
		child = *(ainfo->arrayp+index);
		*(ainfo->arrayp+index) = (Rectobj) NULL;
		*(ainfo->arrayp+hole) = child;

		calc_child_row_column(ainfo, get_layout_data(child), hole);
	}
}


void
array_tile_add_child_proc(array_tile, child)
	Array_tile	array_tile;
	Rectobj		child;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*child_rinfo = RECTOBJ_PRIVATE(child);
	Array_layout_data* layout_data;

	layout_data =
	child_rinfo->layout_data = (void*) xv_alloc(Array_layout_data);

	layout_data->flags = UNMANAGED;
	layout_data->column = -1;
	layout_data->row = -1;

	if(ainfo->auto_layout)
		layout_data->flags |= NEW_CHILD;
}


void
array_tile_set_geometry_proc(array_tile, newrect, oldrect)
	Array_tile	array_tile;
	Rect		*newrect;
	Rect		*oldrect;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(array_tile);
	Rectobj		child;
	Rectobj_info	*child_rinfo;
	int		i;

	for(i=0; i<ainfo->array_size; i++)
		if(child = *(ainfo->arrayp+i)) {
			child_rinfo = RECTOBJ_PRIVATE(child);
			calc_child_rect(array_tile, child, &child_rinfo->rect);
			rectobj_set_geometry( child, &child_rinfo->rect);
		}
	ainfo->relayout = FALSE;
}


void
array_tile_manage_child_proc(array_tile, child, child_newrect, child_oldrect)
	Array_tile	array_tile;
	Rectobj		child;
	Rect		*child_newrect;
	Rect		*child_oldrect;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(array_tile);
	Array_layout_data *layout_data = get_layout_data(child);
	int	geo_manage = FALSE;


	if(layout_data->flags & NEW_CHILD) {
		/* 
		 * Position a new child.
		 */
		geo_manage = position_child(array_tile, ainfo, child, 0, 0, 0);
		layout_data->flags ^= NEW_CHILD;
	} else {
		if(layout_data->flags & UNMANAGED) {
			/* 
			 * The child isn't managed, give it want it wants and
			 * return.  It will not affect others until it 
			 * is managed.
			 */
			rectobj_set_geometry(child, child_newrect);
			return;
		}
		/*
		 * A old child has been resized, see if it effects 
		 * layout of other children.
		 */
		if(child_newrect->r_width > ainfo->column_width) {
			ainfo->column_width = child_newrect->r_width;
			geo_manage = TRUE;
		}

		if(child_newrect->r_height > ainfo->row_height) {
			ainfo->row_height = child_newrect->r_height;
			geo_manage = TRUE;
		} 
	}

	if(geo_manage) {
		Rect	rect;

		rect = rinfo->rect;
		calc_array_rect_size(ainfo, &rect);
		rectobj_geometry_manage(array_tile, &rect);
	} else {
		if(ainfo->relayout)
			array_tile_set_geometry_proc(array_tile, 
					(Rect*) NULL, (Rect*) NULL);
		else {
			calc_child_rect(array_tile, child, child_newrect);
			rectobj_set_geometry(child, child_newrect);
		}
	}
}

/*
 * calculate the dimensions of the array_tile object.
 */
static void
calc_array_rect_size(ainfo, rect)
	Array_tile_info	*ainfo;
	Rect		*rect;
{
	rect->r_width = ainfo->column_gap +
		ainfo->n_columns * (ainfo->column_gap + ainfo->column_width);

	rect->r_height = ainfo->row_gap +
		ainfo->n_rows * (ainfo->row_gap + ainfo->row_height);
}


/*
 * calculate the row-column position of a child, given its position.
 */
static void
calc_child_row_column(ainfo, layout_data, n)
	Array_tile_info		*ainfo;
	Array_layout_data 	*layout_data;
	int			n;
{
	/* given an index into the array, calculate the row/column position */

	switch(ainfo->layout) {
	  case ARRAY_TILE_LAYOUT_COLUMN:
		layout_data->column	= n / ainfo->n_rows;
		layout_data->row	= n % ainfo->n_rows;
		break;

	  case ARRAY_TILE_LAYOUT_ROW:
		layout_data->column	= n % ainfo->n_columns;
		layout_data->row	= n / ainfo->n_columns;
		break;
	}
}


/*
 * calculate a child's position
 */
static void
calc_child_rect(array_tile, child, rect)
	Array_tile	array_tile;
	Rectobj		child;
	Rect		*rect;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(array_tile);
	Rectobj_info	*child_rinfo = RECTOBJ_PRIVATE(child);
	Array_layout_data *layout_data = get_layout_data_private(child_rinfo);

	rect->r_left = 
	  rinfo->rect.r_left +
	  ainfo->column_gap + 
	  layout_data->column * (ainfo->column_gap + ainfo->column_width);

	switch(ainfo->align) {
		case ARRAY_TILE_ALIGN_NORTHWEST:
		case ARRAY_TILE_ALIGN_SOUTHWEST:
		case ARRAY_TILE_ALIGN_WEST:
			break;

		case ARRAY_TILE_ALIGN_NORTH:
		case ARRAY_TILE_ALIGN_SOUTH:
		case ARRAY_TILE_ALIGN_CENTER:
	 		rect->r_left += 
			  (ainfo->column_width/2 - child_rinfo->rect.r_width/2);
			break;

		case ARRAY_TILE_ALIGN_NORTHEAST:
		case ARRAY_TILE_ALIGN_EAST:
		case ARRAY_TILE_ALIGN_SOUTHEAST:
			rect->r_left += 
			  (ainfo->column_width - child_rinfo->rect.r_width);
	}

	rect->r_top = 
		rinfo->rect.r_top +
		ainfo->row_gap + 
		layout_data->row * (ainfo->row_gap + ainfo->row_height);

	switch(ainfo->align) {
		case ARRAY_TILE_ALIGN_NORTH:
		case ARRAY_TILE_ALIGN_NORTHEAST:
		case ARRAY_TILE_ALIGN_NORTHWEST:
			break;

		case ARRAY_TILE_ALIGN_EAST:
		case ARRAY_TILE_ALIGN_WEST:
		case ARRAY_TILE_ALIGN_CENTER:
	 		rect->r_top += 
			  (ainfo->row_height/2 - child_rinfo->rect.r_height/2);
			break;

		case ARRAY_TILE_ALIGN_SOUTH:
		case ARRAY_TILE_ALIGN_SOUTHWEST:
		case ARRAY_TILE_ALIGN_SOUTHEAST:
	 		rect->r_top += 
			  (ainfo->row_height - child_rinfo->rect.r_height);
	}

}


/*
 * change the number of rows or the number of columns of the array_tile.
 * call rebuild_arrayp after this to restore the children's position.
 */
static void
array_tile_reset_dimensions(ainfo, columns, rows)
	Array_tile_info	*ainfo;
	int		columns;
	int		rows;
{
	Array_tile_info	tmp_ainfo;
	int i;

	tmp_ainfo = *ainfo;
	tmp_ainfo.n_columns	= MAX(1, columns);
	tmp_ainfo.n_rows 	= MAX(1, rows);

	tmp_ainfo.array_size = rows * columns;

	if((ainfo->auto_layout) && 
	   ((tmp_ainfo.n_rows < ainfo->n_rows) || 
	    (tmp_ainfo.n_columns < ainfo->n_columns)))
		position_children(&tmp_ainfo);

	if(tmp_ainfo.array_size < ainfo->array_size)
		for(i=tmp_ainfo.array_size; i<ainfo->array_size; i++)
			array_tile_unmanage_child(ainfo, *(ainfo->arrayp+i));

	if(ainfo->arrayp) {

	  ainfo->arrayp = (Rectobj*) realloc(
			ainfo->arrayp, tmp_ainfo.array_size * sizeof(Rectobj));

	  if(ainfo->array_size < tmp_ainfo.array_size)
		  bzero((char*)(ainfo->arrayp + ainfo->array_size), 
		    (tmp_ainfo.array_size - ainfo->array_size)*sizeof(Rectobj));

	} else
	  ainfo->arrayp = (Rectobj*) calloc(
				tmp_ainfo.array_size, sizeof(Rectobj));

	ainfo->n_columns= tmp_ainfo.n_columns;
	ainfo->n_rows 	= tmp_ainfo.n_rows;
	ainfo->array_size = tmp_ainfo.array_size;
}


/*
 * remove a child from the set of managed children.
 */
static void
array_tile_unmanage_child(ainfo, child)
	Array_tile_info	*ainfo;
	Rectobj		child;
{
	Array_layout_data *layout_data;
	Rectobj	*childp;

	if(!child)
		return;
	layout_data = get_layout_data(child);

	if(layout_data->flags & UNMANAGED)
		return;
	if(childp = position_to_childp(ainfo, layout_data))
		*childp = (Rectobj) NULL;
	layout_data->flags |= UNMANAGED;
	layout_data->row = layout_data->column = -1;
	ainfo->n_managed--;
}

/*
 * return a pointer to the child at a row column position
 */
Rectobj *
position_to_childp(ainfo, layout_data)
	Array_tile_info	*ainfo;
	Array_layout_data *layout_data;
{
	int i;

	if(	(layout_data->flags & UNMANAGED) 	||
		(layout_data->row < 0) 			||
		(layout_data->column < 0) 		||
		(layout_data->row >= ainfo->n_rows) 	||
		(layout_data->column >= ainfo->n_columns))
			return (Rectobj*) NULL;

	switch(ainfo->layout) {
	  case ARRAY_TILE_LAYOUT_ROW:
		i = ainfo->n_columns*layout_data->row + layout_data->column;
		break;

	  case ARRAY_TILE_LAYOUT_COLUMN:
		i = ainfo->n_rows*layout_data->column + layout_data->row;
		break;
	}
	return ainfo->arrayp+i;
}


/*
 * restore ainfo -> arrayp from the layout data of the array_tile's children.
 */
static void
rebuild_arrayp(array_tile)
	Array_tile	array_tile;
{
	Array_tile_info	*ainfo = ARRAY_TILE_PRIVATE(array_tile);
	Rectobj_list	*list;
	Array_layout_data *layout_data;
	Rectobj		child;
	Rectobj		*childp;

	/* zero arrayp out */
	bzero( (char*) ainfo->arrayp, ainfo->array_size * sizeof(Rectobj) );
	list = RECTOBJ_PRIVATE(array_tile)->children;

	/* rebuild arrayp from layout_data attached to children */
	list_for(list) {
		child = RECTOBJ_LIST_HANDLE(list);
		layout_data = get_layout_data(child);
		if(layout_data->flags & UNMANAGED) 
			continue;
		if((layout_data->row < 0) 		||
		   (layout_data->row >= ainfo->n_rows)	||
		   (layout_data->column < 0)		||
		   (layout_data->column >= ainfo->n_columns)) {
			array_tile_unmanage_child(ainfo, child);
			continue;
		}
		if(childp = position_to_childp(ainfo, layout_data))
			*childp = child;
	}
	array_tile_compact(ainfo);
}


/*
 * explicitly position a child.
 */
static int
position_child(array_tile, ainfo, child, column, row, which)
	Array_tile	array_tile;
	Array_tile_info	*ainfo;
	Rectobj		child;
	int		column;
	int		row;
	int		which;
{
	Array_layout_data *layout_data;
	Rectobj		*childp;
	Rectobj		*newposp;
	int		geometry_change = FALSE;
	int		previously_unmanaged;
	
	if(!child)
		return;
	layout_data = get_layout_data(child);
	if(!layout_data)
		return;

	previously_unmanaged = layout_data->flags & UNMANAGED;

	if(!previously_unmanaged)
		/* clear out the last position that the child was at */
		if(childp = position_to_childp(ainfo, layout_data))
			*childp = (Rectobj) NULL;

	if(which & SET_COLUMN) {
		if((column < 0) || (column >= ainfo->n_columns)) {
			array_tile_unmanage_child(ainfo, child);
			return;
		}
		layout_data->column = column;
	}

	if(which & SET_ROW) {
		if((row < 0) || (row >= ainfo->n_rows)) {
			array_tile_unmanage_child(ainfo, child);
			return;
		}
		layout_data->row = row;
	}

	if(layout_data->flags & NEW_CHILD)
		calc_child_row_column(ainfo, layout_data, ainfo->n_managed);

	if(previously_unmanaged) {
		ainfo->n_managed++;
		layout_data->flags ^= UNMANAGED;
	}

	if((ainfo->auto_layout) && (ainfo->n_managed > ainfo->array_size)) {
		switch(ainfo->layout) {
		  case ARRAY_TILE_LAYOUT_ROW:
			array_tile_reset_dimensions(ainfo, 
					ainfo->n_columns, ainfo->n_rows+1);
			break;
		  case ARRAY_TILE_LAYOUT_COLUMN:
			array_tile_reset_dimensions(ainfo, 
					ainfo->n_columns+1, ainfo->n_rows);
			break;
		}
		rebuild_arrayp(array_tile);
		calc_array_rect_size(ainfo,
			 &(RECTOBJ_PRIVATE(array_tile)->rect));
		geometry_change = TRUE;
	}

	/* 
	 * If there is an object at the destination spot, remove it.
	 */
	if(	(newposp = position_to_childp(ainfo, layout_data)) && 
		(*newposp != child))
		array_tile_unmanage_child(ainfo, *newposp);

	*newposp = child;

	if(ainfo->auto_layout && !(layout_data->flags & NEW_CHILD))
		array_tile_compact(ainfo);

	if(previously_unmanaged) {
		Rect *child_rect = &(RECTOBJ_PRIVATE(child)->rect);
		if(child_rect->r_width > ainfo->column_width) {
			ainfo->column_width = child_rect->r_width;
			geometry_change = TRUE;
		}

		if(child_rect->r_height > ainfo->row_height) {
			ainfo->row_height = child_rect->r_height;
			geometry_change = TRUE;
		} 
	}

	if(geometry_change) {
		ainfo->relayout = TRUE;
		calc_array_rect_size(ainfo,
			&(RECTOBJ_PRIVATE(array_tile)->rect));
	}

	return geometry_change;
}



/*
 * set the row-column of all children according to their position in the array
 */
static void
position_children(ainfo)
	Array_tile_info	*ainfo;
{
	int 	i;
	Rectobj	child;

	for(i=0; i<ainfo->array_size; i++) {
		if(child = *(ainfo->arrayp+i))
			calc_child_row_column(ainfo, get_layout_data(child), i);
	}
}

