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

struct ref_st {
	char *name;
	struct ref_st *subtree;
};

extern AppData app_data;
extern struct st_host_info hinfo[];

char *entry_to_rel_path();
char *entry_to_full_path();


/*
 * expand_subtree - Expand subtree at directory entry specified by
 *                  "expand_einfo".  Set "cfetch_ok" to True if it
 *                  is ok to fetch the directory list from the cache.
 *                  Set "cstore_ok" to True if it is ok to use the
 *                  directory list to update the cache.  Returns 0 if
 *                  successful, 1 if cache was used, -3 for lost
 *                  connection, -6 for stop button pressed, and -1
 *                  for other errors.
 */
expand_subtree(expand_einfo, cstore_ok, cfetch_ok)
struct entry_info **expand_einfo;
int cstore_ok;
int cfetch_ok;
{
	struct dirwin_st *dirwin = (*expand_einfo)->dirwin;
	int expand_index;
	int retval;
	int used_cache = False;

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

	/* Expand the directory */
	expand_index = (*expand_einfo)->indx;
	retval = re_expand_subtree(expand_einfo, NULL, cstore_ok, cfetch_ok);
	if (retval < 0) {
		update_dir_controls();
		restore_prev_cursor();
		return retval;
	}
	if (retval == 1)
		used_cache = True;

	/* Perform geometry calculations for directory display */
    calc_dirwin_geometry(dirwin);

	/* Fix up scrollbars */
	reset_scrollbars(dirwin, -1, -1);

	/* Redraw changed part of directory display */
	redraw_dir_rows(dirwin, expand_index);

	update_dir_controls();
	restore_prev_cursor();
	if (used_cache)
		return 1;
	else
		return 0;
}


/*
 * collapse_subtree - Collapse subtree at directory entry specified by
 *                    "collapse_einfo".  Update display if "update_display"
 *                    is set.
 */
void
collapse_subtree(collapse_einfo, update_display)
struct entry_info **collapse_einfo;
int update_display;
{
	struct dirwin_st *dirwin = (*collapse_einfo)->dirwin;
	int x;
	int width;
	Dimension drawing_area_width;
	Dimension drawing_area_height;
	int count = 0;
	int start_index;
	struct entry_info *new_entries;
	int i;
	int collapse_index = (*collapse_einfo)->indx;
	int old_first_visible_row;

	/* Get rid of collapsed directory's entries */
	start_index = collapse_index+1;
	for (i=start_index; i<dirwin->nentries; i++)
		if (dirwin->entries[i].level > (*collapse_einfo)->level)
			count++;
		else
			break;
	new_entries = (struct entry_info *)XtMalloc(
		(dirwin->nentries-count)*sizeof(struct entry_info));
	bcopy(dirwin->entries, new_entries,
		start_index*sizeof(struct entry_info));
	bcopy(&dirwin->entries[start_index+count], &new_entries[start_index],
		(dirwin->nentries-count-start_index)*sizeof(struct entry_info));
	for (i=start_index; i<start_index+count; i++) {
		XtFree(dirwin->entries[i].name);
		XtFree(dirwin->entries[i].info);
	}
	XtFree((char *)dirwin->entries);
	dirwin->entries = new_entries;
	dirwin->nentries -= count;
	for (i=start_index; i<dirwin->nentries; i++)
		dirwin->entries[i].indx = i;
	dirwin->entries[collapse_index].expanded = False;

	/* Adjust entry pointer */
	*collapse_einfo = &dirwin->entries[collapse_index];

	/* Determine new maximum entry width */
	dirwin->max_entry_width = 0;
	for (i=0; i<dirwin->nentries; i++) {
		width = XTextWidth(app_data.variable_width_layout_font,
			dirwin->entries[i].name,
			strlen(dirwin->entries[i].name))+2*TMARGIN+IWIDTH+IMARGIN;
		dirwin->max_entry_width = MAX(dirwin->max_entry_width, width);
	}

	if (update_display == False)
		return;

	/* Perform geometry calculations for directory display */
    calc_dirwin_geometry(dirwin);
	old_first_visible_row = dirwin->first_visible_row;
	reset_scrollbars(dirwin, -1, -1);

	/* Redraw changed part of directory display */
	XtVaGetValues(dirwin->w_drawingArea,
		XmNwidth,	&drawing_area_width,
		XmNheight,	&drawing_area_height,
		NULL
	);
	x = virtual_x(dirwin, 0);
	width = drawing_area_width;
	if (dirwin->first_visible_row != old_first_visible_row)
		redraw_entire_dir(dirwin);
	else
		redraw_dir_rows(dirwin, collapse_index);

	update_dir_controls();
}


/*
 * create_reference_subtree - Create a tree containing the names of the
 *                            subdirectories of the specified directory
 *                            entry that are expanded.  Caller must call
 *                            release_reference_subtree() to destroy
 *                            the returned tree.
 */
struct ref_st *
create_reference_subtree(einfo)
struct entry_info *einfo;
{
	struct dirwin_st *dirwin = einfo->dirwin;
	struct ref_st *subtree;
	int count;
	int i;

	/* Count number of expanded entries in directory */
	count = 0;
	for (i=einfo->indx+1; i<dirwin->nentries; i++) {
		if (dirwin->entries[i].level <= einfo->level)
			break;
		if (dirwin->entries[i].level == einfo->level+1
				&& dirwin->entries[i].expanded)
			count++;
	}

	/* Allocate memory for the tree of expanded subdirectories */
	subtree = (struct ref_st *)XtMalloc((count+1)*sizeof(struct ref_st));

	/* Record names of expanded entries */
	count = 0;
	for (i=einfo->indx+1; i<dirwin->nentries; i++) {
		if (dirwin->entries[i].level <= einfo->level)
			break;
		if (dirwin->entries[i].level == einfo->level+1
				&& dirwin->entries[i].expanded) {
			subtree[count].name = XtNewString(dirwin->entries[i].name);
			subtree[count].subtree = 
				create_reference_subtree(&dirwin->entries[i]);
			count++;
		}
	}
	subtree[count].name = NULL;          /* End of list */
	subtree[count].subtree = NULL;

	return subtree;
}


/*
 * create_reference_tree - Create a tree containing the names of the
 *                  	   subdirectories in the directory display
 *                         that are expanded.  Caller must call
 *                         release_reference_subtree() to destroy
 *                         the returned tree.
 */
struct ref_st *
create_reference_tree(dirwin)
struct dirwin_st *dirwin;
{
	struct ref_st *tree;
	int count;
	int i;

	/* Count number of expanded entries in directory */
	count = 0;
	for (i=0; i<dirwin->nentries; i++)
		if (dirwin->entries[i].level == 0 && dirwin->entries[i].expanded)
			count++;

	/* Allocate memory for the tree of expanded subdirectories */
	tree = (struct ref_st *)XtMalloc((count+1)*sizeof(struct ref_st));

	/* Record names of expanded entries */
	count = 0;
	for (i=0; i<dirwin->nentries; i++) {
		if (dirwin->entries[i].level == 0 && dirwin->entries[i].expanded) {
			tree[count].name = XtNewString(dirwin->entries[i].name);
			tree[count].subtree =create_reference_subtree(&dirwin->entries[i]);
			count++;
		}
	}
	tree[count].name = NULL;          /* End of list */
	tree[count].subtree = NULL;

	return tree;
}


/*
 * release_reference_subtree - Destroy the tree created by
 *                             create_reference_subtree() or
 *                             create_reference_tree();
 *
 */
void
release_reference_subtree(subtree)
struct ref_st *subtree;
{
	int indx = 0;

	while (subtree[indx].name) {
		XtFree(subtree[indx].name);
		release_reference_subtree(subtree[indx].subtree);
		indx++;
	}

	XtFree((char *)subtree);
}


/*
 * re_expand_subtree - Re-expand the subdirectory specified by einfo.
 *                     "ref_tree" is a structure created by
 *                     create_reference_subtree() that describes the
 *                     subdirectories to re-expand.  If no subdirectories
 *                     are to be re-expanded, set "ref_tree" to NULL.
 *                     Set "cfetch_ok" to True if it is ok to fetch the
 *                     directory list from the cache.  Set "cstore_ok"
 *                     to True if it is ok to use the directory list
 *                     to update the cache.  Assumes that subtree is not
 *                     already expanded.  Returns 0 if successful, 1 if
 *                     cache was used, -3 for lost connection, -6 for
 *                     stop button pushed, and -1 for other errors.
 */
re_expand_subtree(expand_einfo, ref_tree, cstore_ok, cfetch_ok)
struct entry_info **expand_einfo;
struct ref_st *ref_tree;
int cstore_ok;
int cfetch_ok;
{
	struct dirwin_st *dirwin = (*expand_einfo)->dirwin;
	int expand_index = (*expand_einfo)->indx;
	char *path;
	int retval;
	char *true_path;
	struct sl_struct *dlist;
	int start_index;
	struct entry_info *new_entries;
	int level;
	int width;
	struct entry_info *einfo;
	int i;
	int j;
	int count;
	int indx;
	char msg[MAXPATHLEN+30];
	int used_cache = False;

	/* Determine true path of directory */
	path = entry_to_full_path(*expand_einfo);
	retval = determine_true_path(dirwin->host, path, True, cstore_ok, cfetch_ok,
		&true_path);
	switch (retval) {
	case -1:
		sprintf(msg, "Unable to expand directory subtree %s.", path);
		record_warning(msg);
	case -3:
	case -6:
		XtFree(path);
        return retval;
	}
	XtFree(path);

	/* Get directory list */
	retval = get_dirlist(dirwin->host, true_path, TREE, dirwin->dotfiles_mode,
		cstore_ok, cfetch_ok, &dlist);
	switch (retval) {
	case -1:
		sprintf(msg, "Unable to expand directory subtree %s.", path);
		record_warning(msg);
	case -3:
	case -6:
		XtFree(true_path);
        return retval;
	case 1:
		used_cache = True;
	}
	XtFree(true_path);

    /* Allocate space to hold directory entry info */
	new_entries = (struct entry_info *)XtMalloc(
		(dirwin->nentries+dlist->nentries)*sizeof(struct entry_info));
	start_index = (*expand_einfo)->indx+1;
	bcopy(dirwin->entries, new_entries,
		start_index*sizeof(struct entry_info));
	bcopy(&dirwin->entries[start_index],
		&new_entries[start_index+dlist->nentries],
		(dirwin->nentries-start_index)*sizeof(struct entry_info));
	XtFree((char *)dirwin->entries);
	dirwin->entries = new_entries;
	dirwin->nentries += dlist->nentries;
	for (i=start_index+dlist->nentries; i<dirwin->nentries; i++)
		dirwin->entries[i].indx = i;
	level = dirwin->entries[expand_index].level+1;
	dirwin->entries[expand_index].expanded = True;

	/* Adjust entry pointer */
	*expand_einfo = &dirwin->entries[expand_index];

    /* Save information about directory entries */
    for (i=0; i<dlist->nentries; i++) {
		einfo = &dirwin->entries[start_index+i];
		parse_short_entry(hinfo[dirwin->host].system, dlist->entries[i],
			&einfo->name, &einfo->type);
		einfo->info = NULL;
        width = XTextWidth(app_data.variable_width_layout_font,
			einfo->name, strlen(einfo->name))+2*TMARGIN+IWIDTH+IMARGIN;
		einfo->width = width;
		einfo->state = UNSELECTED;
		einfo->indx = start_index+i;
		einfo->level = level;
		einfo->dirwin = dirwin;
		einfo->expanded = False;
		dirwin->max_entry_width = MAX(dirwin->max_entry_width, width);
	}

	/* Re-expand subdirectories.  Be super careful of recursion here! */
	if (ref_tree) {
		count = 0;
		indx = start_index;
		while (count < dlist->nentries) {
			if (dirwin->entries[indx].level == level) {
				count++;
				if (dirwin->entries[indx].type == DIRECTORY_TYPE) {
					einfo = &dirwin->entries[indx];
					for (j=0; ref_tree[j].name; j++)
						if (!strcmp(einfo->name, ref_tree[j].name)) {
							retval = re_expand_subtree(&einfo,
								ref_tree[j].subtree, cstore_ok, cfetch_ok);
							if (retval < 0) {
								release_array_list(dlist);
								return retval;
							}
							if (retval == 1)
								used_cache = True;
							break;
						}
				}
			}
			indx++;
		}
	}

	/* Free memory */
	release_array_list(dlist);

	if (used_cache)
		return 1;
	else
		return 0;
}


/*
 * re_expand_tree - Refresh the directory being displayed in tree
 *                  layout mode in the specified directory window.
 *                  Set "cfetch_ok" to True if it is ok to fetch the
 *                  directory list from the cache.  Set "cstore_ok" to
 *                  True if it is ok to use the directory list to
 *                  update the cache.  Returns 0 if successful, 1 if
 *                  cache was used, -3 for lost connection,  -6 for
 *                  stop button pressed, and  -1 for other errors.
 */
re_expand_tree(dirwin, cstore_ok, cfetch_ok)
struct dirwin_st *dirwin;
int cstore_ok;
int cfetch_ok;
{
    int retval;
    struct sl_struct *dlist;
    int width;
    struct entry_info *einfo;
    int i;
	int j;
    int count = 0;
    int indx;
	struct ref_st *ref_tree;
	char *anchor_rel_path;
	int indx1;
	char msg[MAXPATHLEN+30];
	int used_cache = False;

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

    /* Get directory list */
    retval = get_dirlist(dirwin->host, dirwin->dirname, TREE,
		dirwin->dotfiles_mode, cstore_ok, cfetch_ok, &dlist);
	switch (retval) {
	case -1:
		sprintf(msg, "Unable to expand directory tree %s.", dirwin->dirname);
		record_warning(msg, dirwin->w_shell);
	case -3:
	case -6:
        restore_prev_cursor();
        return retval;
	case 1:
		used_cache = True;
    }

	/* Remember vertical scrollbar position */
	if (dirwin->nentries) {
		indx1 = dirwin->first_visible_row;
		anchor_rel_path = entry_to_rel_path(&dirwin->entries[indx1]);
	} else
		anchor_rel_path = NULL;

	/* Remember which subdirectories are expanded */
	ref_tree = create_reference_tree(dirwin);

    /* Allocate space to hold directory entry info */
    for (i=0; i<dirwin->nentries; i++) {
        XtFree(dirwin->entries[i].name);
		XtFree(dirwin->entries[i].info);
	}
	if (dirwin->nentries)
		XtFree((char *)dirwin->entries);
    if (dlist->nentries)
        dirwin->entries = (struct entry_info *)XtMalloc(dlist->nentries*
            sizeof(struct entry_info));
    dirwin->nentries = dlist->nentries;

    /* Save information about directory entries */
    for (i=0; i<dlist->nentries; i++) {
        einfo = &dirwin->entries[i];
        parse_short_entry(hinfo[dirwin->host].system, dlist->entries[i],
			&einfo->name, &einfo->type);
        einfo->info = NULL;
        width = XTextWidth(app_data.variable_width_layout_font,
            einfo->name, strlen(einfo->name))+2*TMARGIN+IWIDTH+IMARGIN;
        einfo->width = width;
        einfo->state = UNSELECTED;
        einfo->level = 0;
        einfo->dirwin = dirwin;
        einfo->expanded = False;
		einfo->indx = i;
        dirwin->max_entry_width = MAX(dirwin->max_entry_width, width);
    }

	/* Re-expand the subdirectories.  Be super careful of recursion! */
	count = 0;
	indx = 0;
	while (count < dlist->nentries) {
		if (dirwin->entries[indx].level == 0) {
			count++;
			if (dirwin->entries[indx].type == DIRECTORY_TYPE) {
				einfo = &dirwin->entries[indx];
				for (j=0; ref_tree[j].name; j++)
					if (!strcmp(einfo->name, ref_tree[j].name)) {
						retval = re_expand_subtree(&einfo, ref_tree[j].subtree,
							cstore_ok, cfetch_ok);
						if (retval < 0) {
						    release_array_list(dlist);
							release_reference_subtree(ref_tree);
							if (anchor_rel_path)
								XtFree(anchor_rel_path);
							return retval;
						}
						if (retval == 1)
							used_cache = True;
						break;
					}
			}
		}
		indx++;
	}

	/* Release memory */
    release_array_list(dlist);
	release_reference_subtree(ref_tree);

    /* Perform geometry calculations for directory display */
    calc_dirwin_geometry(dirwin);

    /* Redraw changed part of directory display */
	if (anchor_rel_path) {
		set_first_visible_entry(dirwin, anchor_rel_path);
		XtFree(anchor_rel_path);
	} else {
		reset_scrollbars(dirwin, 0, 0);
		redraw_entire_dir(dirwin);
	}

    update_dir_controls();
    restore_prev_cursor();
	if (used_cache)
		return 1;
	else
		return 0;
}



syntax highlighted by Code2HTML, v. 0.9.1