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

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


#include <xview/win_input.h>
#include <xview/openmenu.h>
#include <sspkg/canshell.h>
#include <sspkg/rectobj.h>
#include <sspkg/drawobj.h> /* temporary until subpackages get cleaned up */
#include "r_impl.h"

int	btn_down_drag_x, btn_down_drag_y;

is_dbl_click(then, now)
	struct timeval *then;
	struct timeval *now;
{
	struct timeval delta;
	static int multiclicktimeout = 0;
 
	delta.tv_sec = now->tv_sec - then->tv_sec;
	if ((delta.tv_usec = now->tv_usec - then->tv_usec) < 0) {
		delta.tv_usec += 1000000;
		delta.tv_sec -= 1;
	}

	if(multiclicktimeout == 0) {
		multiclicktimeout = defaults_get_integer(
				"openwindows.multiclicktimeout",
				"OpenWindows.MultiClickTimeout", 5);
	}
	/* compare the delta against the multi-click timeout */
	/* (a value between 2 and 10 tenths of second) */
	if((delta.tv_sec*10 + delta.tv_usec/100000) <= multiclicktimeout)
		return TRUE;
	return FALSE;
}
 

#define ABS(a)		(((a) < 0) ? -(a) : (a))

/*
 * Wait for a button release or a drag beyond the threshold.
 */
static int
wait_for_next_event(paint_window, event, canvas_shell, rectobj)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Rectobj		rectobj;
{
	static	int	clickmovethreshold = 0;
	int		distance_x;
	int		distance_y;

	if(clickmovethreshold == 0)
		clickmovethreshold = defaults_get_integer(
			"openwindows.clickmovethreshold",
			"OpenWindows.ClickMoveThreshold", 
			5);

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

		if(event_is_up(event) && count_buttons_down(event) < 2) {
			if(rectobj == event_to_rectobj(canvas_shell, event))
				return DO_DBL_CLICK;
			return DO_DBL_NOTHING;
		}

		if(event_action(event) == LOC_DRAG) {
			distance_x = ABS(event_x(event) - btn_down_drag_x);
			distance_y = ABS(event_y(event) - btn_down_drag_y);

			/* 
			 * need to add a feature here...  if the cmd button
			 * is down, then ignore this test and return DO_DBL_DRAG
			 */

			if((distance_x > clickmovethreshold) ||
			   (distance_y > clickmovethreshold))
				return DO_DBL_DRAG;
		}
	}
}


/*ARGSUSED*/
int
do_dbl_click_drag(paint_window, event, canvas_shell, rectobj, event2)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Rectobj		rectobj;
	Event		*event2;
{
	int			pre_selected;
	int			selectable;
	int			return_code;
	static 	Rectobj		rectobj_first_click;
	static	struct timeval	click_time;
 
	if((!event_is_down(event)) || (!event_is_button(event)))
		return DO_DBL_NOTHING;

	if(!xv_get(rectobj, XV_SHOW))
		return DO_DBL_NOTHING;

	btn_down_drag_x = event_x(event);
	btn_down_drag_y = event_y(event);

	if(event_id(event) == MS_RIGHT) {
		Menu	rectobj_get_menu();
		Menu	menu;

		if(menu = rectobj_get_menu(rectobj))
			menu_show(menu, paint_window, event, 0);
		return DO_DBL_NOTHING;
	}

	pre_selected = xv_get(rectobj, RECTOBJ_SELECTED);
	selectable = xv_get(rectobj, RECTOBJ_SELECTABLE);

	if(event_id(event) == MS_MIDDLE) {
		if(selectable)
			xv_set(rectobj, RECTOBJ_SELECTED, pre_selected ^ TRUE, 
					XV_NULL);
		rectobj_first_click = (Rectobj) 0;
		return DO_DBL_NOTHING;
	} 

	if((pre_selected == FALSE) && (selectable == TRUE)) {
		clear_selection(rectobj);
		xv_set(rectobj, RECTOBJ_SELECTED, TRUE, 
				RECTOBJ_STACKING_POSITION, 0x7FFFFFFF,/*INT_MAX*/
				XV_NULL);
	}

	return_code = wait_for_next_event(paint_window, event2, 
				canvas_shell, rectobj);

	if(return_code == DO_DBL_CLICK) {
		if((rectobj_first_click == rectobj)  &&
		   (is_dbl_click(&click_time, &event_time(event2))))
			return DO_DBL_CLICK;
		else {
			/* set up click time for next click */
			click_time = event_time(event2);
			rectobj_first_click = rectobj;
			return DO_DBL_NOTHING;
		}
	}
	click_time.tv_sec = 0;
	click_time.tv_usec = 0;
	rectobj_first_click = (Rectobj) 0;
	return return_code;
}


int
start_dbl_click(paint_window, event, canvas_shell, rectobj)
	Xv_window	paint_window;
	Event		*event;
	Canvas_shell	canvas_shell;
	Rectobj		rectobj;
{
	Event	event2;
	void	(*dbl_proc)();
	void	start_drag_drop();

	switch( do_dbl_click_drag(paint_window, event, 
				canvas_shell, rectobj, &event2) ) {
	  case DO_DBL_CLICK:
		dbl_proc = (void(*)()) xv_get(rectobj, RECTOBJ_DBL_CLICK_PROC);

		if(dbl_proc)
			(*dbl_proc)(paint_window, &event2, canvas_shell, 
					rectobj);
		break;

	  case DO_DBL_DRAG:
		if(xv_get(rectobj, RECTOBJ_DRAGGABLE))
			start_drag_drop(canvas_shell, rectobj);
		break;

	  case DO_DBL_NOTHING:
		break;
	}
	return TRUE;
}


