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

#ifndef lint
#ifdef sccs
static char     sccsid[] = "@(#)rectobj.c 1.52 91/05/10";
#endif
#endif

#include <memory.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <xview/rect.h>
#include <xview/xv_xrect.h>
#include <xview/win_input.h>
#include <sspkg/rectobj.h>
#include "r_impl.h"

Pkg_private int 	rectobj_init();
Pkg_private Xv_opaque	rectobj_set_avlist();
Pkg_private Xv_opaque	rectobj_get_attr();
Pkg_private int 	rectobj_destroy();

void	rectobj_internal_init();
void	rectobj_paint_proc();
int	rectobj_event_proc();
Rectobj	rectobj_map_event_proc();
void	rectobj_manage_child_proc();
void	rectobj_set_geometry_proc();
void	rectobj_void_proc();
void	rectobj_add_to_parent_list();
void	*set_shared_info();

int	rectobj_global_invocation_level = 0;

/*ARGSUSED*/
Pkg_private int
rectobj_init(parent, rectobj, avlist)
	Xv_opaque	parent;
	Rectobj		rectobj;
	Attr_avlist	avlist;
{
	Rectobj_info	*rinfo;
	Rectobj_struct	*rectobj_object;
	Rectobj_info	*parent_rinfo;

	rinfo = xv_alloc(Rectobj_info);
	rectobj_object = (Rectobj_struct*) rectobj;
	rectobj_object->private_data = (Xv_opaque) rinfo;

	rectobj_internal_init(rectobj, rinfo);
	rinfo->parent = parent;
	if(parent) {
		parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
		rinfo->rect.r_left = rinfo->old_rect.r_left = 
			parent_rinfo->rect.r_left;
		rinfo->rect.r_top = rinfo->old_rect.r_top = 
			parent_rinfo->rect.r_top;
	}
	return(XV_OK);
}


void
rectobj_internal_init(rectobj, rinfo)
	Rectobj		rectobj;
	Rectobj_info	*rinfo;
{
	rinfo->listnode.handle = (void*)rectobj;
	rinfo->paint_proc = rectobj_paint_proc;
	rinfo->event_proc = rectobj_event_proc;
	rinfo->map_event_proc = rectobj_map_event_proc;
	rinfo->manage_child_proc = rectobj_manage_child_proc;
	rinfo->set_geometry_proc = rectobj_set_geometry_proc;
	rinfo->add_child_proc = rectobj_void_proc;
	rinfo->del_child_proc = rectobj_void_proc;

	rinfo->painted = TRUE;
	rinfo->selectable = FALSE;
	rinfo->fg_color = -1;
	rinfo->inherit_menu = TRUE;
	rinfo->invocation_level = 1000;	/* arbitrarily high number */
	rinfo->state = RECTOBJ_STATE_INIT;
}


Pkg_private Xv_opaque
rectobj_set_avlist(rectobj, avlist)
	Rectobj			rectobj;
	register Attr_avlist	avlist;
{
        register Rectobj_attr	attr;
        register Rectobj_info	*rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj_info 		*alt_rinfo;

	if(rinfo->invocation_level == 0)
		rectobj_reset_set_info(rectobj);

	rinfo->invocation_level++;
	rectobj_global_invocation_level++;

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

		case XV_X:
			if(!rinfo->parent)
			  rinfo->rect.r_left = (short)*avlist;
			else
			  rinfo->rect.r_left = 
				RECTOBJ_PRIVATE(rinfo->parent)->rect.r_left
					+ (short)*avlist;
			avlist++;
			break;

		case XV_Y:
			if(!rinfo->parent)
			  rinfo->rect.r_top = (short)*avlist;
			else
			  rinfo->rect.r_top = 
				RECTOBJ_PRIVATE(rinfo->parent)->rect.r_top
					+ (short)*avlist;
			avlist++;
			break;

		case RECTOBJ_X:
			rinfo->rect.r_left	=	(short)*avlist++;
			break;
		case RECTOBJ_Y:
			rinfo->rect.r_top 	=	(short)*avlist++;
			break;

		case XV_WIDTH:
			rinfo->rect.r_width	=	(short)*avlist++;
			break;
		case XV_HEIGHT:
			rinfo->rect.r_height	=	(short)*avlist++;
			break;

		case XV_RECT:
			rinfo->rect 		=	*(Rect*)(*avlist++);
			break;

		case RECTOBJ_PAINT_PROC:
			rinfo->paint_proc = (void (*) ()) *avlist++;
			rinfo->repaint = TRUE;
			break;

		case RECTOBJ_EVENT_PROC:
			rinfo->event_proc = (int (*) ())*avlist++;
			break;

		case RECTOBJ_MAP_EVENT_PROC:
			rinfo->map_event_proc = (Rectobj (*) ())*avlist++;
			break;

		case XV_SHOW:
			if(*avlist++) {
				rinfo->painted = TRUE;
				rectobj_repaint_rect(rectobj,
					&rinfo->rect, TRUE);
			} else {
				rectobj_repaint_rect(rectobj,
					&rinfo->rect, TRUE);
				rinfo->painted = FALSE;
			}
			/* if not painted, it can't be selected */
			/*rinfo->selectable = rinfo->painted;*/
			/* remove from selected list here */
			rinfo->repaint = FALSE;
			break;

		case RECTOBJ_PARENT:
		case XV_OWNER:
		  /* bug: need to handle when the object is selected. */

			if(rinfo->state != RECTOBJ_STATE_INIT) {
			  if(rinfo->parent)
				RECTOBJ_PRIVATE(rinfo->parent)->children = 
					(Rectobj_list*) list_first(
					    list_unlink_node(&rinfo->listnode));
				rinfo->previous_parent = rinfo->parent;
				rinfo->parent = (Rectobj)*avlist++;
				rectobj_add_to_parent_list(rinfo);
			} else
			  rinfo->parent = (Rectobj)*avlist++;
		  break;

		case RECTOBJ_SELECTED:
			/* if already set to right state, then ignore */
			if(rinfo->selected == *avlist) {
				avlist++;
				break;
			}

			/* otherwise, set and repaint */
			if(rinfo->selected = *avlist++)
				add_to_selected_list(rectobj);
			else
				del_from_selected_list(rectobj);
			rinfo->repaint = TRUE;
			break;

		case RECTOBJ_SELECTABLE:
			rinfo->selectable = (int) *avlist++;
			if(!rinfo->selectable && rinfo->selected) {
				/* remove from selected list */
				;
				/* repaint */
			}
			break;

		/* not sure the following should go here.... */
		case RECTOBJ_DRAGGABLE:
			rinfo->draggable = (int) *avlist++;
			break;

		case RECTOBJ_DRAG_CURSOR:
			rinfo->drag_cursor = (Xv_cursor)*avlist++;
			break;

		case RECTOBJ_DROP_PROC:
			rinfo->drop_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_CHILD_DROP_PROC:
			rinfo->child_drop_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_ACCEPTS_DROP:
			rinfo->accept_drop = (int) *avlist++;
			break;

		case RECTOBJ_ACCEPTS_CHILD_DROP:
			rinfo->accept_child_drop = (int) *avlist++;
			break;

		case RECTOBJ_DBL_CLICK_PROC:
			rinfo->dbl_click_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_LAYOUT_DATA:
			rinfo->layout_data = (void*) *avlist++;
			break;

		case RECTOBJ_FOREGROUND_COLOR:
			rinfo->fg_color = (int) *avlist++;
			if(!rinfo->shared_info)
				break;
			if((rinfo->fg_color < 0) ||
			   (rinfo->fg_color >= rinfo->shared_info->num_colors))
				rinfo->fg_color = 
					xv_get(rinfo->shared_info->canvas_shell,
						WIN_FOREGROUND_COLOR);
			rinfo->repaint = TRUE;
			break;

		case RECTOBJ_BACKGROUND_COLOR:
			rinfo->bg_color = (int) *avlist++;
			if(!rinfo->shared_info)
				break;
			if((rinfo->bg_color < 0) ||
			   (rinfo->bg_color >= rinfo->shared_info->num_colors))
				rinfo->bg_color = 0;
			rinfo->repaint = TRUE;
			break;

		case RECTOBJ_MENU:
			rinfo->menu = (Menu)*avlist++;
			break;

		case RECTOBJ_INHERIT_MENU:
			rinfo->inherit_menu = (Menu)*avlist++;
			break;

		case RECTOBJ_SET_GEOMETRY_PROC:
			rinfo->set_geometry_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_ADD_CHILD_PROC:
			rinfo->add_child_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_DEL_CHILD_PROC:
			rinfo->del_child_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_MANAGE_CHILD_PROC:
			rinfo->manage_child_proc = (void(*)()) *avlist++;
			break;

		case RECTOBJ_NO_MANAGE_CHILD:
			rinfo->no_manage_child = (int) *avlist++;
			break;

		case RECTOBJ_STACKING_POSITION: {
			Rectobj_list *alt_list;
			Rectobj_list *node;
			int	i;
			int	n = (int) *avlist++;

			if(!rinfo->parent)
				break;
			alt_rinfo = RECTOBJ_PRIVATE(rinfo->parent);

			node= list_find(alt_rinfo->children, rectobj);
			if(!node)	/* This will be TRUE will creating. */
				break;
			alt_rinfo->children = alt_list =
				list_first(list_unlink_node(node));
			i = 0;
			while((i < n) && (alt_list)) {
				alt_list = list_next(alt_list);
				i++;
			}

			if((i>0) && (!alt_list))
				(void)list_concat(alt_rinfo->children, node);
			else
				alt_rinfo->children = list_first(
					list_insert_before(alt_list, node));

			rectobj_repaint_rect(rectobj, &rinfo->rect, TRUE);
			}
			break;

		case XV_END_CREATE:
			rinfo->repaint = TRUE;
			rinfo->state = RECTOBJ_STATE_CREATED;
			rinfo->invocation_level = 1;
			rectobj_add_to_parent_list(rinfo);
			break;

		default:
			avlist = attr_skip(attr, avlist);

	  }

	if(((Xv_base*)rectobj)->pkg == RECTOBJ)
		rectobj_finish_set(rectobj);

	return(XV_OK);
}



void
rectobj_finish_set(rectobj)
	Rectobj rectobj;
{
	register Rectobj_info *rinfo = RECTOBJ_PRIVATE(rectobj);

	rinfo->invocation_level--;
	if((rinfo->invocation_level != 0) || 
	   (rinfo->state == RECTOBJ_STATE_INIT)) {
  		rectobj_global_invocation_level--;
		return;
	}

	if(rinfo->parent != rinfo->previous_parent) {
	  if(rinfo->previous_parent)
		(RECTOBJ_PRIVATE(rinfo->previous_parent)->del_child_proc)
			(rinfo->previous_parent, rectobj);
		
	  if(rinfo->parent)
		(RECTOBJ_PRIVATE(rinfo->parent)->add_child_proc)
			(rinfo->parent, rectobj);

	  rinfo->previous_parent = rinfo->parent;
	}

	if((!rinfo->no_manage_child) && 
	   (rinfo->state != RECTOBJ_STATE_DESTROYING))
		rectobj_geometry_manage(rectobj, NULL);

	if(rinfo->repaint)
		rectobj_repaint_rect(rectobj, &rinfo->rect, FALSE);

	rectobj_global_invocation_level--;
	if(!rectobj_global_invocation_level) 
	  	rectobj_flush_repaint();
	rectobj_reset_set_info(rectobj);
}


void
rectobj_reset_set_info(rectobj)
	Rectobj rectobj;
{
	register Rectobj_info *rinfo = RECTOBJ_PRIVATE(rectobj);

	/* reinitialize for next call to set function on this object */
	rinfo->repaint = FALSE;
	rinfo->invocation_level = 0;
	rinfo->old_rect = rinfo->rect;
	rinfo->no_manage_child = FALSE;
}


/*ARGSUSED*/
Pkg_private Xv_opaque
rectobj_get_attr(rectobj, status, which_attr, avlist)
	Rectobj		rectobj;
	int		*status;
	register Attr_attribute which_attr;
	Attr_avlist	avlist;
{
	Rectobj_info  *rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj_info *parent_rinfo;

	switch (which_attr) {

		case XV_X: {
			int parent_x;

			if(!rinfo->parent)
				parent_x = 0;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				parent_x = parent_rinfo->rect.r_left;
			}
			return (Xv_opaque) rinfo->rect.r_left - parent_x;
		}

		case XV_Y: {
			int parent_y;

			if(!rinfo->parent)
				parent_y = 0;
			else {
				parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
				parent_y = parent_rinfo->rect.r_top;
			}
			return (Xv_opaque) rinfo->rect.r_top - parent_y;
		}

		case RECTOBJ_X:
			return (Xv_opaque) rinfo->rect.r_left;
		case RECTOBJ_Y:
			return (Xv_opaque) rinfo->rect.r_top;
		case XV_WIDTH:
			return (Xv_opaque) rinfo->rect.r_width;
		case XV_HEIGHT:
			return (Xv_opaque) rinfo->rect.r_height;

		case XV_RECT:
			return (Xv_opaque) &rinfo->rect;

		case RECTOBJ_PAINT_PROC:
			return (Xv_opaque) rinfo->paint_proc;

		case RECTOBJ_EVENT_PROC:
			return (Xv_opaque) rinfo->event_proc;

		case RECTOBJ_MAP_EVENT_PROC:
			return (Xv_opaque) rinfo->map_event_proc;

		case RECTOBJ_PARENT:
		case XV_OWNER:
			return (Xv_opaque) rinfo->parent;

		case RECTOBJ_CANVAS:
			if(rinfo->shared_info)
			  return (Xv_opaque) rinfo->shared_info->canvas_shell;
			return (Xv_opaque) 0;

		case RECTOBJ_CHILDREN:
			return (Xv_opaque) rinfo->children;

		case RECTOBJ_NTH_CHILD: {
			int i, n;
			Rectobj_list *node;

			n = (int)*avlist;
			node = list_first(rinfo->children);

			for(i=0; ((i < n) && (node != NULL)); i++)
				node = list_next(node);
			return (Xv_opaque) RECTOBJ_LIST_HANDLE(node);
			}

		case RECTOBJ_STACKING_POSITION: {
			Rectobj_list *list;
			int i;

			if(!rinfo->parent)
				return (Xv_opaque) 0;
			list = RECTOBJ_PRIVATE(rinfo->parent)->children;

			i = 0;
			while(list) {
				if(RECTOBJ_LIST_HANDLE(list) == rectobj)
					break;
				list = list_next(list);
				  i++;
			}
			return (Xv_opaque) i;
			}


		case RECTOBJ_STATE:
			return (Xv_opaque) rinfo->state;

		case RECTOBJ_SHARED_INFO:
			return (Xv_opaque) rinfo->shared_info;
			break;

		case RECTOBJ_SELECTED:
			return (Xv_opaque) rinfo->selected;

		case RECTOBJ_SELECTABLE:
			return (Xv_opaque) rinfo->selectable;

		case RECTOBJ_DRAGGABLE:
			return (Xv_opaque) rinfo->draggable;

		case RECTOBJ_DRAG_CURSOR:
			return (Xv_opaque) rinfo->drag_cursor;

		case RECTOBJ_DROP_PROC:
			return (Xv_opaque) rinfo->drop_proc;

		case RECTOBJ_CHILD_DROP_PROC:
			return (Xv_opaque) rinfo->child_drop_proc;

		case RECTOBJ_ACCEPTS_DROP:
			return (Xv_opaque) rinfo->accept_drop;

		case RECTOBJ_ACCEPTS_CHILD_DROP:
			return (Xv_opaque) rinfo->accept_child_drop;

		case RECTOBJ_DBL_CLICK_PROC:
			return (Xv_opaque) rinfo->dbl_click_proc;

		case RECTOBJ_LAYOUT_DATA:
			return (Xv_opaque) rinfo->layout_data;

		case RECTOBJ_FOREGROUND_COLOR:
			return (Xv_opaque) rinfo->fg_color;

		case RECTOBJ_BACKGROUND_COLOR:
			return (Xv_opaque) rinfo->bg_color;

		case RECTOBJ_MENU:
			return (Xv_opaque) rinfo->menu;

		case RECTOBJ_INHERIT_MENU:
			if(rinfo->menu)
				return (Xv_opaque) FALSE;
			return (Xv_opaque) rinfo->inherit_menu;

		case RECTOBJ_SET_GEOMETRY_PROC:
			return (Xv_opaque) rinfo->set_geometry_proc;

		case RECTOBJ_ADD_CHILD_PROC:
			return (Xv_opaque) rinfo->add_child_proc;

		case RECTOBJ_DEL_CHILD_PROC:
			return (Xv_opaque) rinfo->del_child_proc;

		case RECTOBJ_MANAGE_CHILD_PROC:
			return (Xv_opaque) rinfo->manage_child_proc;

		case RECTOBJ_NO_MANAGE_CHILD:
			return (Xv_opaque) rinfo->no_manage_child;

		case XV_SHOW:
			return (Xv_opaque) rinfo->painted;

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


/*ARGSUSED*/
Pkg_private int
rectobj_destroy(rectobj, status)
	Rectobj		rectobj;
	Destroy_status	status;
{
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj		parent;

	/* bug alert:  this is wrong... each child's destroy function
	 * should be called to see if it is okay to destroy first.
	 */
	if ((status == DESTROY_CHECKING) || (status == DESTROY_SAVE_YOURSELF))
		return XV_OK;

	rectobj_set_delay_repaint(rectobj, TRUE);
	if(rinfo->shared_info)
	  rectobj_repaint_rect(rectobj, &rinfo->rect, TRUE);
	rinfo->state = RECTOBJ_STATE_DESTROYING;
	/* destroy all children */
	while(rinfo->children)
		xv_destroy( RECTOBJ_LIST_HANDLE(rinfo->children) );

	/* remove from parent's list, if attached */
	parent = rinfo->parent;
	if(parent) {
		xv_set(rectobj, RECTOBJ_PARENT, XV_NULL, NULL);
		rectobj_set_delay_repaint(parent, FALSE);
	}

	if(rinfo->selected)
		del_from_selected_list(rectobj);


	free(rinfo);
	return XV_OK;
}


static void
rectobj_add_to_parent_list(rinfo) 
	Rectobj_info *rinfo;
{
	Rectobj_info *parent_rinfo;

	/* add adjustment for stacking order here */
	if(rinfo->parent) {
		parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
		parent_rinfo->children = (Rectobj_list*) list_concat(
			parent_rinfo->children, &rinfo->listnode);
	}
	(void) traverse_rectobj_tree( RECTOBJ_PUBLIC(rinfo), set_shared_info, 
		(rinfo->parent ? parent_rinfo->shared_info: NULL));
}


void
rectobj_paint_proc(rectobj, dpy, win, xrects)
	Rectobj	rectobj;
	Display *dpy;
	Window 	win;
	Xv_xrectlist *xrects;
{
	rectobj_paint_children(rectobj, dpy, win, xrects, TRUE);
}


int
rectobj_event_proc(paint_window, event, canvas, rectobj)
	Xv_window	paint_window;
	Event		*event;
	Canvas		canvas;
	Rectobj		rectobj;	
{
	return background_event_proc(paint_window, event, canvas, rectobj);
}


Rectobj
rectobj_map_event_proc(rectobj, event)
	Rectobj		rectobj;
	Event		*event;
{
	Rectobj_list	*node;
	Rectobj		child;
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj		return_val;

	if(rinfo->painted == FALSE)
		return 0;

	node = rinfo->children;

	list_rof(node) {
	  child = RECTOBJ_LIST_HANDLE(node);
	  rinfo = RECTOBJ_PRIVATE(child);
	  if(!rect_includespoint(&rinfo->rect, event_x(event), event_y(event)))
		continue;

	  if(rinfo->map_event_proc)
		if( return_val = (*rinfo->map_event_proc)(child, event) )
			return return_val;
	}
	return rectobj;
}


/*
 * make a depth first recursive traversal of the rectobj tree,
 * call a function on each node.  If the function ever returns
 * non-zero,  then stop the traversal, returning result to caller.
 */
void *
traverse_rectobj_tree(rectobj, function, arg)
	Rectobj rectobj;
	void	*(*function)();
	void	*arg;
{
	Rectobj_list *node;
	Rectobj	child;
	void 	*result;

	node = (Rectobj_list*)xv_get(rectobj, RECTOBJ_CHILDREN);

	list_for(node) {
		child = RECTOBJ_LIST_HANDLE(node);
		result = traverse_rectobj_tree(child, function, arg);
		if(result)
			return result;
	}
	return((function)(rectobj, arg));
}


static void *
set_shared_info(rectobj, shared_info)
	Rectobj		rectobj;
	Shared_info	*shared_info;
{
	Rectobj_info    *rinfo = RECTOBJ_PRIVATE(rectobj);

	rinfo->shared_info = shared_info;

	if(shared_info) {

		if((rinfo->fg_color < 0) ||
		   (rinfo->fg_color >= shared_info->num_colors))
			rinfo->fg_color = xv_get(shared_info->canvas_shell,
				WIN_FOREGROUND_COLOR);

		if((rinfo->bg_color < 0) ||
		   (rinfo->bg_color >= shared_info->num_colors))
			rinfo->bg_color = 0;
	}
	return (void*) NULL;
}


void
rectobj_geometry_manage(rectobj, newrect)
	Rectobj rectobj;
	Rect	*newrect;
{
	register Rectobj_info *rinfo = RECTOBJ_PRIVATE(rectobj);

	if(newrect == NULL)
		newrect = &rinfo->rect;
	else
		rinfo->rect = *newrect;

	if((rectcmp(newrect, &rinfo->old_rect)) && (rinfo->parent)) {
		/* rect has changed, inform the parent */
		(RECTOBJ_PRIVATE(rinfo->parent)->manage_child_proc) 
			(rinfo->parent, rectobj, newrect, &rinfo->old_rect);
	} 
}


void
rectobj_manage_child_proc(parent, child, child_new_rect, child_old_rect)
	Rectobj			parent;
	Rectobj			child;
	Rect			*child_new_rect;
	Rect			*child_old_rect;
{
	register Rectobj_info *rinfo = RECTOBJ_PRIVATE(parent);
	int w, h;

	if(rect_includesrect(&rinfo->rect, child_new_rect)) {
		/* check for shrink to fit? */
		rectobj_set_geometry(child, child_new_rect);
	} else {
		/* expand to fit child */
		w = rect_right(child_new_rect) - rect_right(&rinfo->rect);
		if(w > 0)
			rinfo->rect.r_width += w;
		h = rect_bottom(child_new_rect) - rect_bottom(&rinfo->rect);
		if(h > 0)
			rinfo->rect.r_height += h;

		if(w > 0 || h > 0)
			rectobj_geometry_manage(parent, NULL);
	}
}


int rectobj_hack_no_old_rect_repaint = FALSE;/* until I sort this out... */

void
rectobj_set_geometry(child, newrect)
	Rectobj	child;
	Rect	*newrect;
{
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(child);

	if(newrect == NULL)
		newrect = &rinfo->rect;
	else
		rinfo->rect = *newrect;
	
	if(!rectcmp(newrect, &rinfo->old_rect))
		return;

	if(!rectobj_hack_no_old_rect_repaint)
		rectobj_repaint_rect(child, &rinfo->old_rect, TRUE);

	rectobj_repaint_rect(child, newrect, FALSE);

	if(rinfo->set_geometry_proc)
		(rinfo->set_geometry_proc)(child, newrect, rinfo->old_rect);
	rinfo->old_rect = rinfo->rect;

	if(rectobj_hack_no_old_rect_repaint)
		rectobj_hack_no_old_rect_repaint = FALSE;
}


void
rectobj_set_geometry_proc(rectobj, newrect, oldrect)
	Rectobj	rectobj;
	Rect	*newrect;
	Rect	*oldrect;
{
	rectobj_move_children(rectobj);
}


/*
 * An object that has moved can use this utility function to calculate
 * the new positions of its children.  
 */
void
rectobj_move_adjust_rect(parent, child, rect)
	Rectobj	parent;
	Rectobj	child;
	Rect	*rect;
{
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(parent);
	Rectobj_info	*child_rinfo = RECTOBJ_PRIVATE(child);
	int		delta_x;
	int		delta_y;

	delta_x = rinfo->rect.r_left - rinfo->old_rect.r_left;
	delta_y = rinfo->rect.r_top - rinfo->old_rect.r_top;

	*rect = child_rinfo->rect;
	rect->r_left += delta_x;
	rect->r_top += delta_y;
}

/*
 * Blanket move of all children, relative to how the parent has moved.
 */
void
rectobj_move_children(parent)
	Rectobj	parent;
{
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(parent);
	Rectobj_list	*node;
	Rectobj		child;
	Rectobj_info	*child_rinfo;
	Rect		rect;
	int		delta_x;
	int		delta_y;

	if(!rectcmp(&rinfo->rect, &rinfo->old_rect))
		return;

	delta_x = rinfo->rect.r_left - rinfo->old_rect.r_left;
	delta_y = rinfo->rect.r_top - rinfo->old_rect.r_top;

	node = rinfo->children;
	list_for(node) {
		child = RECTOBJ_LIST_HANDLE(node);
		child_rinfo = RECTOBJ_PRIVATE(child);

		rect = child_rinfo->rect;
		rect.r_left += delta_x;
		rect.r_top  += delta_y;

		rectobj_set_geometry(child, &rect);
	}
}


/*
 * This returns FALSE when any of rectobj's siblings, uncles, grand 
 * uncles, etc. overlap the rect.  If check_children is TRUE, it also checks
 * the children for overlaps.
 *
 * This returns TRUE when the rectobj has complete impunity to paint whatever
 * and whenever it will within the rect.
 *
 * This should only be used when the object calling this function is
 * not transparent.
 */
int
rectobj_own_rect(rectobj, rect, check_children)
	Rectobj rectobj;
	Rect 	*rect;
	int	check_children;
{
	Rectobj_info 	*rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj_info 	*parent_rinfo;
	Rectobj_info 	*alt_rinfo;
	Rectobj_list	*list;

	if(rect == NULL)
		rect = &rinfo->rect;

	while(rinfo->parent) {
	  parent_rinfo = RECTOBJ_PRIVATE(rinfo->parent);
	  list = (Rectobj_list*)list_find(parent_rinfo->children, rectobj);

	  list = list_next(list);

	  while(list) {
		alt_rinfo = RECTOBJ_PRIVATE(RECTOBJ_LIST_HANDLE(list));
		if(alt_rinfo->painted == FALSE)
			continue;
		if(rect_intersectsrect(&alt_rinfo->rect, rect))
			return FALSE;
		list = list_next(list);
	  }

	  rectobj = rinfo->parent;
	  rinfo = RECTOBJ_PRIVATE(rectobj);
	}

	/*
	 * Only the first level of children need to be checked because 
	 * further decendants will be within their parent's rect.
	 */
	if(check_children) {
		rinfo = RECTOBJ_PRIVATE(rectobj);
		list = (Rectobj_list*)rinfo->children;
		list_for(list) {
			alt_rinfo= RECTOBJ_PRIVATE(RECTOBJ_LIST_HANDLE(list));
			if(alt_rinfo->painted == FALSE)
				continue;
			if(rect_intersectsrect(&alt_rinfo->rect, rect))
				return FALSE;
		}
	}
	return TRUE;
}


void
rectobj_paint_children(rectobj, dpy, win, xrects)
	Rectobj	rectobj;
	Display *dpy;
	Window 	win;
	Xv_xrectlist *xrects;
{
	Rectobj_list	*node;
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(rectobj);
	Rectobj		child;
	Rectobj_info	*child_rinfo;
	register int	i;

	node = rinfo->children;

	list_for(node) {
	  child = RECTOBJ_LIST_HANDLE(node);
	  child_rinfo = RECTOBJ_PRIVATE(child);

	  if(!child_rinfo->painted)
		continue;

	  for(i=0; i<xrects->count; i++)
		if(rect_intersectsrect( &child_rinfo->rect, 
				(Rect*)(&xrects->rect_array[i]))) {
	  		if(child_rinfo->paint_proc)
			  (child_rinfo->paint_proc) (child, dpy, win, xrects);
			break;
		}

	}
}


int
rectobj_paint_child(rectobj, dpy, win, xrects)
	Rectobj		rectobj;
	Display		*dpy;
	Window		win;
	Xv_xrectlist	*xrects;
{
	Rectobj_info	*rinfo = RECTOBJ_PRIVATE(rectobj);
	register int i;

	if(!rinfo->painted)
		return;
	for(i=0; i<xrects->count; i++)
	  if(rect_intersectsrect(&rinfo->rect, (Rect*)(&xrects->rect_array[i])))
		if(rinfo->paint_proc)
			(rinfo->paint_proc) (rectobj, dpy, win, xrects);
}


void
rectobj_void_proc()
{
	/* do nothing */
}

