/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (c) 1995-1999.  The Regents of the University of California.  All     */
/*   rights reserved.                                                      */
/*                                                                         */
/*   This work was produced at the University of California, Lawrence      */
/*   Livermore National Laboratory (UC LLNL) under contract no.            */
/*   W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy     */
/*   (DOE) and The Regents of the University of California (University)    */
/*   for the operation of UC LLNL.  Copyright is reserved to the           */
/*   University for purposes of controlled dissemination,                  */
/*   commercialization through formal licensing, or other disposition      */
/*   under terms of Contract 48; DOE policies, regulations and orders;     */
/*   and U.S. statutes.  The rights of the Federal Government are          */
/*   reserved under Contract 48 subject to the restrictions agreed upon    */
/*   by the DOE and University.                                            */
/*                                                                         */
/*                                                                         */
/*                              DISCLAIMER                                 */
/*                                                                         */
/*   This software was prepared as an account of work sponsored by an      */
/*   agency of the United States Government.  Neither the United States    */
/*   Government nor the University of California nor any of their          */
/*   employees, makes any warranty, express or implied, or assumes any     */
/*   liability or responsibility for the accuracy, completeness, or        */
/*   usefulness of any information, apparatus, product, or process         */
/*   disclosed, or represents that its specific commercial products,       */
/*   process, or service by trade name, trademark, manufacturer, or        */
/*   otherwise, does not necessarily constitute or imply its               */
/*   endorsement, recommendation, or favoring by the United States         */
/*   Government or the University of California. The views and opinions    */
/*   of the authors expressed herein do not necessarily state or reflect   */
/*   those of the United States Government or the University of            */
/*   California, and shall not be used for advertising or product          */
/*   endorsement purposes.                                                 */
/*                                                                         */
/*   Permission to use, copy, modify and distribute this software and its  */
/*   documentation for any non-commercial purpose, without fee, is         */
/*   hereby granted, provided that the above copyright notice and this     */
/*   permission notice appear in all copies of the software and            */
/*   supporting documentation, and that all UC LLNL identification in      */
/*   the user interface remain unchanged.  The title to copyright LLNL     */
/*   XDIR shall at all times remain with The Regents of the University     */
/*   of California and users agree to preserve same. Users seeking the     */
/*   right to make derivative works with LLNL XDIR for commercial          */
/*   purposes may obtain a license from the Lawrence Livermore National    */
/*   Laboratory's Technology Transfer Office, P.O. Box 808, L-795,         */
/*   Livermore, CA 94550.                                                  */
/*                                                                         */
/***************************************************************************/
/***************************************************************************/

#include <Xm/RowColumn.h>
#include "xdir.h"
#include "list.h"

#define CONTROL     0
#define ICON_NAME   1

#define MIN_MOTION  5

static struct dirwin_st *src_dirwin;

extern Display *display;
extern Window root_window;
extern int dir_area_enabled;
extern int multiclick_interval;

struct entry_info *xy_to_entry();
char *entry_to_full_path();
char *entry_to_rel_path();
struct sl_struct *create_null_array_list();


/*
 * HandleMouseEvents - Handle mouse events in drawing area of directory
 *                     window.
 */
void
HandleMouseEvents(widget, client_data, event, continue_to_dispatch_return)
Widget widget;
XtPointer client_data;
XEvent *event;
Boolean *continue_to_dispatch_return;
{
	static struct entry_info *prev_einfo = NULL;
	static struct entry_info *hilited_einfo;
	static Time prev_time;
	static int state = 0;
	static int button_press_x;
	static int button_press_y;
	struct dirwin_st *dirwin = (struct dirwin_st *)client_data;
	struct dirwin_st *temp_dirwin;
	struct entry_info *temp_einfo;
	char *path;
	char *rel_path;
	char *true_path;
	struct entry_info *einfo;
	int part;
	int x;
	int y;
	int delta_x;
	int delta_y;
	struct sl_struct *list;
	int retval;

	/* Is some important operation in progress? */
	if (op_in_progress())
		return;

	/* Process event */
	switch (event->type) {
	case ButtonPress:
		if (event->xbutton.button == Button3) {
			XmMenuPosition(dirwin->w_popupLaunchMenu,
				(XButtonPressedEvent *)event);
			XtManageChild(dirwin->w_popupLaunchMenu);
		}
		if (event->xbutton.button != Button1)
			return;
		src_dirwin = dirwin;
		x = virtual_x(dirwin, event->xbutton.x);
		y = virtual_y(dirwin, event->xbutton.y);
		einfo = xy_to_entry(dirwin, x, y, &part);
		if (einfo == NULL) {
			init_toggle_rect(src_dirwin, x, y);
			state = 1;
			return;
		}
		if (part == CONTROL) {
			raise_okflag();
			if (einfo->expanded)
				collapse_subtree(&einfo, True);
			else {
				if (!start_op(False))
					return;
				if (dirwin->host != LOCAL) {
					show_stop_button(dirwin);
					retval = check_connection(dirwin->host, dirwin);
					if (retval < 0) {
						switch (retval) {
						case -6:
							record_abort("Expand Directory");
							break;
						case -1:
							record_and_alert("Unable to expand directory.",
								dirwin->w_shell);
						}
						hide_stop_button();
						end_op();
						state = 0;
						return;
					}
				}
				path = entry_to_full_path(einfo);
				retval = determine_true_path(dirwin->host, path, True,
					dirwin->cache_mode, dirwin->cache_mode, &true_path);
				XtFree(path);
				if (retval < 0) {
					switch (retval) {
					case -6:
						record_abort("Expand Directory");
						break;
					case -1:
						record_and_alert("Unable to go expand directory.",
							dirwin->w_shell);
						break;
					case -3:
						restore_lost_connection(dirwin->host, dirwin);
					}
					if (dirwin->host != LOCAL)
						hide_stop_button();
					end_op();
					state = 0;
					return;
				}
				if (is_dir_displayed(dirwin->host, true_path, &temp_dirwin,
						&temp_einfo)) {
					if (dirwin == temp_dirwin) {
						if (dirwin->host != LOCAL)
							hide_stop_button();
						XtFree(true_path);
						end_op();
						state = 0;
						return;
					} else {
						if (temp_einfo)   /* Sanity check */
							fatal_error("Bug in HandleMouseEvents()");
						close_directory_window(temp_dirwin);
					}
				}
				XtFree(true_path);
				switch (expand_subtree(&einfo, dirwin->cache_mode,
					dirwin->cache_mode)) {
				case -6:
					record_abort("Expand Directory");
					break;
				case -1:
					record_and_alert("Unable to go expand directory.",
						dirwin->w_shell);
					break;
				case -3:
					restore_lost_connection(dirwin->host, dirwin);
					break;
				case 1:
					report_cache_use(dirwin, True);
				}
				if (dirwin->host != LOCAL)
					hide_stop_button();
				end_op();
			}
			state = 0;
			return;
		} else if (einfo == prev_einfo &&
				event->xbutton.time-prev_time < multiclick_interval) {
			clear_selected_entries();
			update_dir_controls();
			if ((einfo->type == FILE_TYPE)
					|| (einfo->type == EXECUTABLE_TYPE)) {
				list = create_null_array_list();
				rel_path = entry_to_rel_path(einfo);
				add_to_array_list(&list, rel_path);
				XtFree(rel_path);
				if (!start_op(True))
					return;
				retval = init_view(dirwin, list);
				release_array_list(list);
			} else {
				if (!start_op(False))
					return;
				if (dirwin->host != LOCAL) {
					show_stop_button(dirwin);
					retval = check_connection(dirwin->host, dirwin);
					if (retval < 0) {
						switch (retval) {
						case -6:
							record_abort("Go to Directory");
							break;
						case -1:
							record_and_alert("Unable to go to directory.",
								dirwin->w_shell);
						}
						hide_stop_button();
						end_op();
						state = 0;
						return;
					}
				}
				path = entry_to_full_path(einfo);
			    retval = display_dir(dirwin->host, dirwin, path, False, True,
					dirwin->cache_mode, dirwin->cache_mode);
			    XtFree(path);
				switch (retval) {
				case -6:
					record_abort("Go to Directory");
					break;
				case -1:
					record_and_alert("Unable to go to directory.",
						dirwin->w_shell);
					break;
				case -3:
					restore_lost_connection(dirwin->host, dirwin);
				}
				if (dirwin->host != LOCAL)
					hide_stop_button();
				end_op();
			}
			state = 0;
		} else {
			XtGrabPointer(dirwin->w_drawingArea, True,
				ButtonReleaseMask|Button1MotionMask, GrabModeAsync,
				GrabModeAsync, None, None, CurrentTime);
			prev_einfo = einfo;
			prev_time = event->xbutton.time;
			button_press_x = x;
			button_press_y = y;
			if (einfo->state == UNSELECTED) {
				toggle_entry(einfo);
				state = 5;
				hilited_einfo = NULL;
			} else
				state = 3;
		}
		break;
	case ButtonRelease:
		if (event->xbutton.button != Button1)
			return;
		if (state > 0)
			XtUngrabPointer(src_dirwin->w_drawingArea, CurrentTime);
		x = virtual_x(dirwin, event->xbutton.x);
		y = virtual_y(dirwin, event->xbutton.y);
		einfo = xy_to_entry(dirwin, x, y, &part);
		switch (state) {
		case 1:
			move_toggle_rect(x, y);
			remove_toggle_rect();
			break;
		case 2:
			if (hilited_einfo) {
				unhilite_entry(hilited_einfo);
				hilited_einfo = NULL;
			}
			restore_prev_cursor();
			remove_move_outlines();
			if (einfo && (einfo->type == DIRECTORY_TYPE) &&
					(einfo->state == UNSELECTED)) {
				if (event->xbutton.state&ControlMask)
					init_move(src_dirwin, dirwin, einfo);
				else
					init_copy(src_dirwin, dirwin, einfo);
			} else if (dirwin != src_dirwin) {
				if (event->xbutton.state&ControlMask)
					init_move(src_dirwin, dirwin, NULL);
				else
					init_copy(src_dirwin, dirwin, NULL);
			}
			break;
		case 3:
			if ((einfo == prev_einfo) && (einfo->state == SELECTED))
				toggle_entry(einfo);
			break;
		case 4:
			unset_toggle_rect_timer();
			move_toggle_rect(x, y);
			remove_toggle_rect();
			break;
		case 5:
			break;
		}
		state = 0;
		break;
	case MotionNotify:
		if (event->xbutton.root != root_window)
			break;
		x = virtual_x(dirwin, event->xbutton.x);
		y = virtual_y(dirwin, event->xbutton.y);
		einfo = xy_to_entry(dirwin, x, y, &part);
		switch (state) {
		case 1:
		case 4:
			move_toggle_rect(x, y);
			break;
		case 3:
		case 5:
			delta_x = x-button_press_x;
			delta_y = y-button_press_y;
			if (delta_x*delta_x+delta_y*delta_y < MIN_MOTION*MIN_MOTION)
				break;
			state = 2;
			init_outlines(src_dirwin, button_press_x, button_press_y);
			use_outlines_cursor();
		case 2:
			move_outlines(dirwin->w_drawingArea, event->xbutton.x,
				event->xbutton.y);
			if (hilited_einfo && hilited_einfo != einfo) {
				unhilite_entry(hilited_einfo);
				hilited_einfo = NULL;
			}
			if (einfo && part == ICON_NAME && einfo->state == UNSELECTED
				   && einfo->type == DIRECTORY_TYPE && einfo != hilited_einfo) {
				hilite_entry(einfo);
				hilited_einfo = einfo;
			}
			break;
		}
		break;
	case EnterNotify:
		if (state == 4) {
			state = 1;
			unset_toggle_rect_timer();
		}
		break;
	case LeaveNotify:
		if (state == 1) {
			state = 4;
			set_toggle_rect_timer();
		}
	}
}


/*
 * xy_to_entry - Determine the entry at coordinates (x, y).  Returns NULL
 *               if no entry at this position.  If entry is found, part
 *               is set to "CONTROL" if (x, y) falls in a tree view
 *               control, or to "ICON_NAME" if (x, y) falls on the entry
 *               icon or name.
 */
struct entry_info *
xy_to_entry(dirwin, x, y, part)
struct dirwin_st *dirwin;
int x;
int y;
int *part;
{
	int indx;
	int row;
	int col;
	struct entry_info *einfo;
	int x_offset;
	int y_offset;

	if (dirwin->nentries == 0)
		return NULL;

	if (x < HMARGIN || y < 0)
		return NULL;

	switch (dirwin->layout) {
	case TABULAR:
	case ICONIC:
		col = (x-HMARGIN)/(dirwin->max_entry_width+HSPACING);
		if (col >= dirwin->ncols)
			return NULL;
		row = y/(dirwin->entry_height+VSPACING);
		if (row >= dirwin->nrows
				|| row >= dirwin->first_visible_row+dirwin->nrows_visible)
			return NULL;
		indx = col*dirwin->nrows+row;
		if (indx >= dirwin->nentries)
			return NULL;
		einfo = &dirwin->entries[indx];
		x_offset = x-HMARGIN-col*(dirwin->max_entry_width+HSPACING);
		if (x_offset >= einfo->width)
			return NULL;
		y_offset = y-row*(dirwin->entry_height+VSPACING);
		if (y_offset >=dirwin->entry_height)
			return NULL;
		*part = ICON_NAME;
		return einfo;
	case TREE:
		row = y/(dirwin->entry_height+VSPACING);
		if (row >= dirwin->nentries
				|| row >= dirwin->first_visible_row+dirwin->nrows_visible)
			return NULL;
		einfo = &dirwin->entries[row];
		y_offset = y-row*(dirwin->entry_height+VSPACING);
		if (y_offset >=dirwin->entry_height)
			return NULL;
		x_offset = x-HMARGIN-einfo->level*INDENT;
		if (x_offset < 0 || x_offset >= CWIDTH+CMARGIN+einfo->width)
			return NULL;
		else if (x_offset >= CWIDTH+CMARGIN) {
			*part = ICON_NAME;
			return einfo;
		} else if (einfo->type == DIRECTORY_TYPE) {
			*part = CONTROL;
			return einfo;
		} else
			return NULL;
	case FULL_INFO:
		row = y/(dirwin->entry_height+VSPACING);
		if (row >= dirwin->nrows
				|| row >= dirwin->first_visible_row+dirwin->nrows_visible)
			return NULL;
		einfo = &dirwin->entries[row];
		if (x >= HMARGIN+einfo->width)
			return NULL;
		else {
			*part = ICON_NAME;
			return einfo;
		}
	}

	return NULL;   /* Necessary to keep gcc compiler happy */
}



syntax highlighted by Code2HTML, v. 0.9.1