/***************************************************************************/
/***************************************************************************/
/*                                                                         */
/*   (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/Xm.h>
#include <string.h>
#include "xdir.h"
#include "list.h"

static struct {
	int host;
	struct dirwin_st *src_dirwin;
	struct dirwin_st *snk_dirwin;
	char *snk_dir;
	struct entry_link *head;
	struct entry_link *head_src_dirs;
	int error;
} mc;

static char *not_implemented_msg = "Moving entry(s) from a host to another host not allowed.\nConsider copying entry(s) instead.";

static char *msg1 = "Unable to redisplay source directory.\n\nSource directory window might be out of date.";

static char *msg2 = "Unable to redisplay sink directory.\n\nSink directory window might be out of date.";

extern int inquire_on_move;
extern XtAppContext app;
extern int beep_when_ops_done;
extern int diagnostics;
extern struct st_host_info hinfo[];

int cb_move_entries();
char *entry_to_rel_path();
struct entry_info *rel_path_to_entry();
struct ref_st *create_reference_subtree();
char *entry_to_full_path();
char *merge_paths();
char *equivalent_file_path();
char **path_to_links();
void update_move_displays();


/*
 * init_move - Initialize the move of selected entries in directory
 *             window "src_dirwin" to directory window "snk_dirwin".  If
 *             "einfo" is non-NULL then move the entries into the
 *             directory represented by "einfo".
 */
void
init_move(src_dirwin, snk_dirwin, einfo)
struct dirwin_st *src_dirwin;
struct dirwin_st *snk_dirwin;
struct entry_info *einfo;
{
	struct entry_link *head;
	struct entry_link *tail;
	struct entry_link *ptr;
	char *temp_snk_dir;
	char *snk_dir;
	int retval;
	int i;
	int j;
	int level;
	int need_to_update_entire_source;

	/* Start operation */
	if (!start_op(True))
		return;

	/* Sanity check */
	if (src_dirwin == snk_dirwin && einfo == NULL)
		fatal_error("Bug in init_move()");

	/* Clear error flag */
	raise_okflag();

	/* Is operation implemented */
	if (src_dirwin->host != snk_dirwin->host) {
		warn(not_implemented_msg, snk_dirwin->w_shell);
		end_op();
		return;
	}

	/* Ask the user if okay to move */
	if (inquire_on_move) {
		if (!verify_selection(src_dirwin, snk_dirwin,
				"Do you really want to move these items?")) {
			end_op();
			return;
		}
	}

	/* Make operation interruptable */
	show_stop_button(snk_dirwin);

    /* This might take some time */
    use_busy_cursor();

	/* Make sure connection is still good */
	if (snk_dirwin->host != LOCAL) {
		retval = check_connection(snk_dirwin->host, snk_dirwin);
		if (retval < 0) {
			restore_prev_cursor();
			switch (retval) {
			case -6:
				record_abort("Move Entries");
				break;
			case -1:
				record_and_alert("Unable to move entry(s).",
					snk_dirwin->w_shell);
			}
			hide_stop_button();
			end_op();
			return;
		}
	}

	/* Determine true sink directory path */
	if (einfo) {
		temp_snk_dir = entry_to_full_path(einfo);
	} else 
		temp_snk_dir = XtNewString(snk_dirwin->dirname);
    retval = determine_true_path(snk_dirwin->host, temp_snk_dir, True, False,
		False, &snk_dir);
	XtFree(temp_snk_dir);
	if (retval < 0) {
		switch (retval) {
		case -1:
			record_and_alert("Unable to move entry(s).", snk_dirwin->w_shell);
			break;
		case -3:
			restore_lost_connection(snk_dirwin->host, snk_dirwin);
			break;
		case -6:
			record_abort("Move Entries");
		}
		restore_prev_cursor();
		hide_stop_button();
		end_op();
		return;
	}

    /* Form list of selected items to move */
    head = NULL;
    tail = NULL;
	for (i=0; i<src_dirwin->nentries; i++)
		if (src_dirwin->entries[i].level == 0)
		    postorder_selected_items(&src_dirwin->entries[i], &head, &tail);

	/* Set up move control block */
	mc.host = src_dirwin->host;
	mc.src_dirwin = src_dirwin;
	mc.snk_dirwin = snk_dirwin;
	mc.snk_dir = snk_dir;
	mc.head = head;
	mc.head_src_dirs = NULL;
	mc.error = False;

	/* Make list of src dirs to update after move. NULL path means update all */
	if (src_dirwin->layout == TREE) {
		need_to_update_entire_source = False;
		for (i=0; i<src_dirwin->nentries; i++)
			if (src_dirwin->entries[i].level == 0 
					&& src_dirwin->entries[i].state == SELECTED) {
				need_to_update_entire_source = True;
				break;
			}
		if (snk_dirwin == src_dirwin && einfo == NULL)
			need_to_update_entire_source = True;
		if (need_to_update_entire_source == False) {
			i = 0;
			while (i<src_dirwin->nentries) {
				if (src_dirwin->entries[i].type == DIRECTORY_TYPE) {
					level = src_dirwin->entries[i].level;
					if (einfo == &src_dirwin->entries[i] && einfo->expanded) {
						ptr = XtNew(struct entry_link);
						ptr->entry = entry_to_rel_path(einfo);
						ptr->next = mc.head_src_dirs;
						mc.head_src_dirs = ptr;
						for (i++; i<src_dirwin->nentries; i++)
							if (src_dirwin->entries[i].level <= level)
								break;
						continue;
					}
					for (j=i+1; j<src_dirwin->nentries; j++) {
						if (src_dirwin->entries[j].level <= level)
							break;
						if (src_dirwin->entries[j].level == level+1
								&& src_dirwin->entries[j].state == SELECTED) {
							ptr = XtNew(struct entry_link);
							ptr->entry =
								entry_to_rel_path(&src_dirwin->entries[i]);
							ptr->next = mc.head_src_dirs;
							mc.head_src_dirs = ptr;
							for (i=j+1; i<src_dirwin->nentries; i++)
								if (src_dirwin->entries[i].level <= level) {
									i--;
									break;
								}
							break;
						}
					}
				}
				i++;
			}
		}
	}

	/* Perform move */
	show_mp_monitor(snk_dirwin, "Now Moving:");
	XtAppAddWorkProc(app, (XtWorkProc)cb_move_entries, NULL);
}


/*
 * cb_move_entries - Work proc to actually move directory entries.
 */
cb_move_entries()
{
    struct entry_link *ptr;
	char *src_path;
	char *snk_path;
	int retval;
	char msg[MAXPATHLEN+40];
	char *temp_path;
	char **links;
	char **link_ptr;
	int nlinks;
	char *dir;

	/* Did user push stop button? */
	if (stop())
		goto abort;

    /* Are we done? */
    if (mc.head == NULL) {
		if (mc.error)
			record_and_alert("At least one move failed.",
				mc.snk_dirwin->w_shell);
        goto done;
	}

	/* Let user know where we are */
	update_mp_monitor(mc.head->entry);

	/* Create source path */
	temp_path = merge_paths(hinfo[mc.src_dirwin->host].system,
		mc.src_dirwin->dirname, mc.head->entry);
	if (mc.host != LOCAL) {
		flush_cache_directory(mc.src_dirwin->host, temp_path);
		parse_path(hinfo[mc.src_dirwin->host].system, temp_path, &dir, NULL);
		flush_cache_directory(mc.src_dirwin->host, dir);
		XtFree(dir);
	}
	src_path = equivalent_file_path(hinfo[mc.src_dirwin->host].system,
		temp_path);
	XtFree(temp_path);

	/* Create sink path */
	links = path_to_links(hinfo[mc.snk_dirwin->host].system, mc.head->entry);
	nlinks = 0;
	link_ptr = links;
	while (*link_ptr++)
		nlinks++;
	temp_path = merge_paths(hinfo[mc.snk_dirwin->host].system, mc.snk_dir,
		links[nlinks-1]);
	release_path_links(links);
	if (mc.host != LOCAL) {
		flush_cache_directory(mc.snk_dirwin->host, temp_path);
		parse_path(hinfo[mc.snk_dirwin->host].system, temp_path, &dir, NULL);
		flush_cache_directory(mc.snk_dirwin->host, dir);
		XtFree(dir);
	}
	snk_path = equivalent_file_path(hinfo[mc.snk_dirwin->host].system,
		temp_path);
	XtFree(temp_path);

	/* Move entry */
	if (mc.host == LOCAL)
		retval = local_rename(src_path, snk_path);
	else
		retval = remote_rename(mc.host, src_path, snk_path);
	XtFree(src_path);
	XtFree(snk_path);
	switch (retval) {
	case -6:
		goto abort;
	case -3:
		goto lost;
	case -1:
		sprintf(msg, "Unable to move %s", mc.head->entry);
		record_warning(msg);
		mc.error = True;
		break;
	case 0:
		if (diagnostics >= NORMAL) {
			sprintf(msg, "*** Successfully moved:  %s\n",mc.head->entry);
			write_log(msg);
		}
	}
	ptr = mc.head;
	mc.head = mc.head->next;
	XtFree(ptr->entry);
	XtFree((char *)ptr);
	return False;

abort:

	hide_mp_monitor();
	record_abort("Move Entries");
	dirwins_out_of_date_alert(mc.snk_dirwin);
	goto final;

done:

	hide_mp_monitor();
	update_move_displays();
	goto final;

lost:

	hide_mp_monitor();
    restore_prev_cursor();
    restore_lost_connection(mc.host, mc.snk_dirwin);
    use_busy_cursor();

final:

	/* Clean up move control block data structure */
	release_linked_list(&mc.head);
	release_linked_list(&mc.head_src_dirs);
	XtFree(mc.snk_dir);

	/* Miscellaneous cleanup */
	clear_selected_entries();
	update_dir_controls();
	if (beep_when_ops_done)
		beep();
	restore_prev_cursor();

	/* Signal end of operation */
	hide_stop_button();
	end_op();
	return True;
}


/*
 * update_move_displays - Call after move operation is complete to update
 *                        source and sink directory displays.
 */
void
update_move_displays()
{
	struct entry_info *einfo;
	struct ref_st *ref_tree;
	struct entry_link *ptr;
	struct dirwin_st *snk_dirwin;
	int indx;
	char *anchor_rel_path;
	int retval;
	
	/* Update source display */
	if ((mc.src_dirwin->layout == TREE) && mc.head_src_dirs) {
		indx = mc.src_dirwin->first_visible_row;
		anchor_rel_path = entry_to_rel_path(&mc.src_dirwin->entries[indx]);
		ptr = mc.head_src_dirs;
		while (ptr) {
			einfo = rel_path_to_entry(mc.src_dirwin, ptr->entry);
			if (einfo) {
				ref_tree = create_reference_subtree(einfo);
				collapse_subtree(&einfo, False);
				retval = re_expand_subtree(&einfo, ref_tree,
					mc.src_dirwin->cache_mode, False);
				release_reference_subtree(ref_tree);
				switch (retval) {
				case -6:
					record_abort("Move Entries");
					dirwins_out_of_date_alert( mc.snk_dirwin);
					XtFree(anchor_rel_path);
					return;
				case -1:
					record_and_alert(msg1, mc.snk_dirwin);
					break;
				case -3:
					XtFree(anchor_rel_path);
					restore_lost_connection(mc.src_dirwin->host, mc.snk_dirwin);
					goto sink;
				}
			}
			ptr = ptr->next;
		}
		calc_dirwin_geometry(mc.src_dirwin);
		set_first_visible_entry(mc.src_dirwin, anchor_rel_path);
		XtFree(anchor_rel_path);
	} else {
		retval = display_dir(mc.src_dirwin->host, mc.src_dirwin,
			mc.src_dirwin->dirname, True, False, mc.src_dirwin->cache_mode,
			False);
		switch (retval) {
		case -6:
			record_abort("Move Entries");
			dirwins_out_of_date_alert( mc.snk_dirwin);
			return;
		case -1:
			record_and_alert(msg1, mc.snk_dirwin);
			break;
		case -3:
			restore_lost_connection(mc.src_dirwin->host, mc.snk_dirwin);
		}
	}

sink:

	/* Update sink display */
	if (is_dir_displayed(mc.host, mc.snk_dir, &snk_dirwin, &einfo)
			&& (snk_dirwin != mc.src_dirwin)) {
		if (einfo) {
			indx = snk_dirwin->first_visible_row;
			anchor_rel_path = entry_to_rel_path(&snk_dirwin->entries[indx]);
			ref_tree = create_reference_subtree(einfo);
			collapse_subtree(&einfo, False);
			retval = re_expand_subtree(&einfo, ref_tree, snk_dirwin->cache_mode,
				False);
			release_reference_subtree(ref_tree);
			switch (retval) {
			case -6:
				record_abort("Move Entries");
				dirwin_out_of_date_alert(mc.snk_dirwin);
				XtFree(anchor_rel_path);
				return;
			case -1:
				record_and_alert(msg2, mc.snk_dirwin);
				break;
			case -3:
				restore_lost_connection(mc.snk_dirwin->host, mc.snk_dirwin);
				XtFree(anchor_rel_path);
				return;
			}
			calc_dirwin_geometry(snk_dirwin);
			set_first_visible_entry(snk_dirwin, anchor_rel_path);
			XtFree(anchor_rel_path);
		} else {
			retval = display_dir(snk_dirwin->host, mc.snk_dirwin, mc.snk_dir,
				True, False, snk_dirwin->cache_mode, False);
			switch (retval) {
			case -6:
				record_abort("Move Entries");
				dirwin_out_of_date_alert(mc.snk_dirwin);
				return;
			case -1:
				record_and_alert(msg2, mc.snk_dirwin);
				break;
			case -3:
				restore_lost_connection(mc.snk_dirwin->host, mc.snk_dirwin);
			}
		}
	}
}



syntax highlighted by Code2HTML, v. 0.9.1