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

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

#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <xview/rect.h>
#include <xview/win_input.h>
#include <string.h>
#include "grip_impl.h"
#include "r_impl.h"
#include "do_impl.h"

Pkg_private int 	grip_init();
Pkg_private Xv_opaque	grip_set_avlist();
Pkg_private Xv_opaque	grip_get_attr();
Pkg_private int 	grip_destroy();

int	grip_grab_proc();
int	grip_event_proc();

int	mouse_offset_x;
int	mouse_offset_y;

static Server_image grip_image = NULL;
#include "grip.xbm"

/*ARGSUSED*/
Pkg_private int
grip_init(parent, grip_public, avlist)
	Xv_opaque	parent;
	Grip		grip_public;
	Attr_avlist	avlist;
{
	Grip_info	*ginfo;
	Grip_struct	*grip_object;
	Rect		*rect;

	ginfo = xv_alloc(Grip_info);
	ginfo->public_self = grip_public;
	grip_object = (Grip_struct*)grip_public;
	grip_object->private_data = (Xv_opaque) ginfo;

	if(grip_image == NULL) {
		grip_image = xv_create(NULL, SERVER_IMAGE,
			XV_WIDTH, grip_image_width,
			XV_HEIGHT, grip_image_height,
			SERVER_IMAGE_X_BITS, grip_image_bits,
			XV_NULL);
	}

	xv_set(grip_public, 
		RECTOBJ_EVENT_PROC, grip_event_proc,
		DRAWIMAGE_SVRIMAGE, grip_image,
		DRAWIMAGE_HIGHLIGHT_IMAGE, grip_image,
#ifndef DBL_CLICK_GRIP
		RECTOBJ_SELECTABLE, FALSE,
#endif
		XV_NULL);

	ginfo->slide_x = TRUE;
	ginfo->slide_y = TRUE;
	ginfo->grip_move_proc = default_grip_move_proc;

	ginfo->max_x = 2000;	/* obviously brain dead... */
	ginfo->max_y = 2000;

	return(XV_OK);
}


Pkg_private Xv_opaque
grip_set_avlist(grip_public, avlist)
	Grip			grip_public;
	register Attr_avlist	avlist;
{
        register Grip_attr attr;
        register Grip_info *ginfo = GRIP_PRIVATE(grip_public);

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

		case GRIP_SLIDE_X:
			ginfo->slide_x = (int)*avlist++;
			break;

		case GRIP_SLIDE_Y:
			ginfo->slide_y = (int)*avlist++;
			break;

		case GRIP_MOVE_PROC:
			ginfo->grip_move_proc = (int (*) ())*avlist++;
			break;

		case GRIP_DONE_PROC:
			ginfo->grip_done_proc = (void (*) ())*avlist++;
			break;

		case GRIP_MAX_X:
			ginfo->max_x = (short)*avlist++;
			break;

		case GRIP_MAX_Y:
			ginfo->max_y = (short)*avlist++;
			break;

		case GRIP_MIN_X:
			ginfo->min_x = (short)*avlist++;
			break;

		case GRIP_MIN_Y:
			ginfo->min_y = (short)*avlist++;
			break;

		case XV_END_CREATE:
			break;

		default:
			avlist = attr_skip(attr, avlist);
	  }

	return(XV_OK);
}


/*ARGSUSED*/
Pkg_private Xv_opaque
grip_get_attr(grip_public, status, which_attr, avlist)
	Grip		grip_public;
	int		*status;
	register Attr_attribute which_attr;
	Attr_avlist	avlist;
{
	Grip_info  *ginfo = GRIP_PRIVATE(grip_public);

	switch (which_attr) {

		case GRIP_SLIDE_X:
			return (Xv_opaque) ginfo->slide_x;

		case GRIP_SLIDE_Y:
			return (Xv_opaque) ginfo->slide_y;

		case GRIP_MOVE_PROC:
			return (Xv_opaque) ginfo->grip_move_proc;

		case GRIP_DONE_PROC:
			return (Xv_opaque) ginfo->grip_done_proc;

		case GRIP_MAX_X:
			return (Xv_opaque) ginfo->max_x;

		case GRIP_MAX_Y:
			return (Xv_opaque) ginfo->max_y;

		case GRIP_MIN_X:
			return (Xv_opaque) ginfo->min_x;

		case GRIP_MIN_Y:
			return (Xv_opaque) ginfo->min_y;

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

/*ARGSUSED*/
Pkg_private int
grip_destroy(grip_public, status)
	Grip		grip_public;
	Destroy_status	status;
{
	Grip_info	*ginfo = GRIP_PRIVATE(grip_public);

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

	free(ginfo);
	return XV_OK;
}

int
default_grip_move_proc(paint_window, event, canvas_shell, grip, new_x, new_y)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Grip grip;
	short *new_x;
	short *new_y;
{
	register Grip_info *ginfo;
	register Rectobj_info *rinfo;
	register short x, y;

	ginfo = GRIP_PRIVATE(grip);
	rinfo = RECTOBJ_PRIVATE(grip);

	/*
	 * if slideable, then check to see if it is within min/max range
	 */
	if(ginfo->slide_x) {
		x = MAX(ginfo->min_x, *new_x);
		x = MIN(ginfo->max_x - rinfo->rect.r_width, x);
	} else
		x = rinfo->rect.r_left;
		
	if(ginfo->slide_y) {
		y = MAX(ginfo->min_y, *new_y);
		y = MIN(ginfo->max_y - rinfo->rect.r_height, y);
	} else
		y = rinfo->rect.r_top;

	*new_x = x;
	*new_y = y;

	return TRUE;
}

void
grip_do_drag(paint_window, event, canvas_shell, grip)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Grip		grip;
{
	Grip_info	*ginfo;
	Rectobj_info	*rinfo;
	Rect 		*rect;
	short		new_x;
	short		new_y;

	ginfo = GRIP_PRIVATE(grip);

	while(1) {
		(void) xv_input_readevent(paint_window, event, 
					TRUE, FALSE, NULL);

		if(event_is_up(event) && count_buttons_down(event) < 2) {
			if(ginfo->grip_done_proc)
				(ginfo->grip_done_proc)(paint_window, event, 
					canvas_shell, grip);
			return;
		}
		if(event_id(event) != LOC_DRAG)
			continue;

		new_x = event_x(event) - mouse_offset_x;
		new_y = event_y(event) - mouse_offset_y;
		/*
		 * Call the grip_move_proc.  The client can disapprove of the
		 * new position by returning FALSE, or it may override the
		 * by scribbling into the new_x and new_y arguments.
		 */
		if(ginfo->grip_move_proc)
		  if((*ginfo->grip_move_proc)(paint_window, event, canvas_shell,
				grip, &new_x, &new_y) == FALSE)
			continue;

		rinfo = RECTOBJ_PRIVATE(grip);
		if( (new_x != rinfo->rect.r_left) || 
		    (new_y != rinfo->rect.r_top))
			xv_set(grip, 
				XV_X, new_x,
				XV_Y, new_y, 
				XV_NULL);
	}
}


int
grip_event_proc(paint_window, event, canvas_shell, grip)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Grip		grip;
{
	Rect	*start_rect;
	short	start_drag_x;
	short	start_drag_y;
        void    (*dbl_proc)();
        void    start_drag_drop();
	Event	event2;
 
	switch( do_dbl_click_drag(paint_window, event,
				canvas_shell, grip, &event2) ) {
	  case DO_DBL_CLICK:
		dbl_proc = (void (*)()) xv_get(grip, RECTOBJ_DBL_CLICK_PROC);
 
		if(dbl_proc)
			(dbl_proc)(paint_window, &event2, canvas_shell, grip);
		break;
 
	  case DO_DBL_DRAG:
		start_drag_x	= event_x(event);
		start_drag_y	= event_y(event);
		start_rect	= (Rect*)xv_get(grip, XV_RECT);
		mouse_offset_x 	= start_drag_x - start_rect->r_left;
		mouse_offset_y 	= start_drag_y - start_rect->r_top;
		grip_do_drag(paint_window, &event2, canvas_shell, grip);
	 	break;

	  case DO_DBL_NOTHING:
		break;
        }
	return TRUE;
}

