/* Program Name            : DIRECTKPD.C                                */
/*   Original Author       : C. K. Hung					*/
/*   Date                  : 7-12-89					*/
/*   Program Description   :                                            */
/*                         :                                            */
/* References                                                           */
/*   Files open for Input  :                                            */
/*   Files open for Output :                                            */
/*   Modules Referenced    :                                            */
/* Revision History follows                                             */
 
/* ****  INCLUDE FILES *** */
#include "global.h"
#include "filer.h"
#include "direct.h"
#include "directkpd.h"
#include strdef
 
 
/* ****  MACRO DEFINITIONS *** */
 
/**  DIRTREE INACTIVE SCROLL REGION		**/
#define DIRTREE_INACTIVE_ROWS			(dirtree_display.view_rows/4)
#define DIRTREE_INACTIVE_FRONT_ROW		(1+DIRTREE_INACTIVE_ROWS)
 
 
/*
**
**	GLOBAL DECLARATIONS
**
**/
 
extern struct node_tag *cwdnode,
		       *root;
extern struct display_tag dirtree_display;
extern enum directions dirtree_search_direction;
extern char dirtree_search_pattern[];
 
/*
**
**	INTERNAL FUNCTION PROTOTYPING
**
**/
    static int	dirtree_find_next$1(char *, struct node_tag **);
    static int	dirtree_find_first$2(struct fil_dx_tag *, struct fil_dx_tag **);
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	dirtree_find_first()
{
    char errmsg[256];
 
    strcpy(errmsg, "Invalid directory specification or directory not found");
    get_userinput_and_execute(
	dirtree_find_first$1,
	(dirtree_search_direction == advance? "Find Directory Forward" : "Find Directory Backward"),
	"Find What: ",
	"",
	errmsg,
	"");
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      to be specified
**
**--
**/
int	dirtree_find_first$1(pattern, dummy, errmsg)
char *pattern;
char *dummy;
char *errmsg;
{
    struct FAB fab;
    struct NAM nam;
    char expanded_pattern[NAM$C_MAXRSS];
    char *cp;
    unsigned long int status;
 
    /*
    **	    INITIALIZE FAB AND NAM DATA STRUCTURES
    **/
 
    fab = cc$rms_fab;
    nam = cc$rms_nam;
 
    fab.fab$l_fna = pattern;
    fab.fab$b_fns = strlen(pattern);
    fab.fab$l_nam = &nam;
    fab.fab$l_dna = "*.*;*";
    fab.fab$b_dns = strlen(fab.fab$l_dna);
 
    nam.nam$l_esa = &expanded_pattern;
    nam.nam$b_ess = NAM$C_MAXRSS;
 
    /*
    **	    GET DIRECTORY SPEC USE 'OPEN' SYSTEM SERVICE.
    **	    IGNORE THE ERROR STATUS (RMS$WLD).
    **/
 
    if (sys$open(&fab, 0, 0) == RMS$_NORMAL)
	sys$close(&fab, 0, 0);
 
    expanded_pattern[ nam.nam$b_esl ] = EOS;
    if ((cp = strchr(expanded_pattern, ']')) != 0)
	*++cp = EOS;
 
    /**  Preserve search string so that it can be used by FIND_NEXT.  **/
    strcpy(dirtree_search_pattern, expanded_pattern);
 
    return dirtree_find_next();
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	dirtree_find_next()
{
    struct node_tag *n;
    int found;
    char *indx;
    char patternfilespec[MAXFILESPEC+1];
    char name[MAXFILESPEC+1];
    char short_path_name[MAXDIRSPEC+1];
    $DESCRIPTOR(short_path_name_descrip, short_path_name);
 
    /*
    **	    Convert from DEVNAME:[...DIRNAME] to DEVNAME:[.-]DIRNAME.DIR;1
    **/
    strcpy(patternfilespec, dirtree_search_pattern);
    if ((indx = strrchr(patternfilespec, '.')) == NULL)
	if ((indx = strchr(patternfilespec, '[')) == NULL)
	    return -1;
 
    /*
    **	    Get DIRNAME
    **/
    indx++;
    strcpy(name, indx);
    if ((indx = strchr(name, ']')) == NULL)
	return -1;
    *indx = EOS;
 
    if (strcmp(name, "000000")) {
 
	/*
	**	NOT A SYSTEM MASTER DIRECTORY
	**/
	indx = strchr(patternfilespec, ']');
	*indx++ = '.';
	*indx++ = '-';
	*indx++ = ']';
	*indx = EOS;
    }
 
    strcat(patternfilespec, name);
    strcat(patternfilespec, ".DIR;1");
 
    found = dirtree_find_next$1(patternfilespec, &n);
 
    if (!found)
	return -1;
    else {
 
	sprintf(short_path_name, "%.8s", cwdnode->short_path_name);
	LENGTH(short_path_name_descrip) = strlen(short_path_name);
	check_OK(smg$put_chars (
		    &dirtree_display.id,
		    &short_path_name_descrip,
		    &(cwdnode->row),
		    &(cwdnode->column),
		    0,
		    &SMG$M_NORMAL,
		    0,
		    0))
	
	cwdnode = n;
 
    }
 
    return 1;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**	tbs
**
**--
**/
static int	dirtree_find_next$1(pattern, nodep)
char *pattern;
struct node_tag **nodep;
{
    static unsigned long context;
    $DESCRIPTOR(pattern_descrip, pattern);
    char candidate[MAXFILESPEC+1];
    char candid_buf[MAXFILESPEC+1];
    $DESCRIPTOR(candidate_descrip, candidate);
    unsigned long status;
    struct node_tag *n;
    char *p;
 
    *nodep = (struct node_tag *)NULL;
    LENGTH(pattern_descrip) = strlen(pattern);
 
    while ((status = lib$find_file (
	       &pattern_descrip,
	       &candidate_descrip,
	       &context,
	       0,
	       0,
	       0,
	       0)) != RMS$_NMF)
    {
	if (status == RMS$_NORMAL)
	{
	    if ((p = strchr(candidate, ']')) != (char *)NULL)
	    {
		/*
		**  Convert DEVNAME:[DIRNAME...]FILENAME.DIR;1
		**  to DEVNAME:[DIRNAME...FILENAME]
		**/
		*p = '.';
		p = strrchr(candidate, '.');
		*p = ']';
		*++p = EOS;
 
		n = search_node(root, strchr(candidate, '[')+1);
 
		if (n == (struct node_tag *)NULL)
		{
		    lib$find_file_end (&context);
		    return 0;
		}
 
		if (dirtree_search_direction == backup)
		{
		    if (n->row < cwdnode->row ||
			(n->row == cwdnode->row && n->column < cwdnode->column))
		    {
			if (*nodep == (struct node_tag *)NULL)
			{
			    *nodep = n;
			}
			else if ((*nodep)->row < n->row ||
				 ((*nodep)->row == n->row &&
				  (*nodep)->column < n->column))
			{
			    *nodep = n;
			}
		    }
		}
		else if (n->row > cwdnode->row ||
		   (n->row == cwdnode->row && n->column > cwdnode->column))
	        {
		    if (*nodep == (struct node_tag *)NULL)
		    {
			*nodep = n;
		    }
		    else if ((*nodep)->row > n->row ||
			     ((*nodep)->row == n->row &&
			      (*nodep)->column > n->column))
		    {
			*nodep = n;
		    }
		}
	    }
	}
    }
 
    lib$find_file_end (&context);
 
    if (*nodep == (struct node_tag *)NULL)
    {
	return 0;
    }
    else
    {
	return 1;
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      to be specified
**
**--
**/
int	change_dirtree_viewport(dir_row, dir_col, dirtree_viewport_rowp, dirtree_viewport_colp)
int dir_row, dir_col;
int *dirtree_viewport_rowp, *dirtree_viewport_colp;
{
    int filler_rows, number_of_scroll;
    int find_down_flag;
    struct node_tag *lastnode;
    int new_viewport_row, new_viewport_row1, lastnode_viewport_row;
    register int i;
    int dirtree_last_inactive_row =
	1+((dirtree_display.view_rows-1)/LINESPACE)*LINESPACE;
    int dirtree_inactive_rear_row =
	dirtree_last_inactive_row-DIRTREE_INACTIVE_ROWS;
 
    /*
    **	Find the row number of the last node
    **/
 
    find_down_flag = 0;
    find_down(root, (struct node_tag *)NULL, &lastnode, &find_down_flag);
 
    /*
    **	Compute new viewport row position
    **/
 
    filler_rows = dirtree_display.view_rows-dirtree_last_inactive_row;
    lastnode_viewport_row =
	max(1, (int) lastnode->row+filler_rows-dirtree_display.view_rows+1);
 
    if (dir_row < *dirtree_viewport_rowp+DIRTREE_INACTIVE_FRONT_ROW-1)
    {	    /**  TARGET ROW BEFORE INACTIVE FRONT ROW  **/
	new_viewport_row1 =
	    max(1, (int) (dir_row-(DIRTREE_INACTIVE_FRONT_ROW-1)));
	new_viewport_row =
	    min(new_viewport_row1, lastnode_viewport_row);
 
	number_of_scroll = *dirtree_viewport_rowp-new_viewport_row;
	if (number_of_scroll <= dirtree_display.view_rows/2)
	{
	    /*
	    **	The displacement between target row and current row
	    **	is leq half of the screen, use scroll by line.
	    **/
 
	    for (i = 1; i <= number_of_scroll; i++)
		check_OK(smg$scroll_viewport (
			  &dirtree_display.id,
			  &SMG$M_DOWN,
			  &1))
	    *dirtree_viewport_rowp = new_viewport_row;
	}
	else
	{	
	    /*	
	    **	The displacement is larger than half the screen,
	    **	use jump scroll instead.
	    **/
 
	    new_viewport_row1 =
		max(1, (int) (dir_row-(dirtree_inactive_rear_row-1)));
	    new_viewport_row =
		min(new_viewport_row1, lastnode_viewport_row);
 
	    number_of_scroll = *dirtree_viewport_rowp-new_viewport_row;
	    check_OK(smg$scroll_viewport (
		      &dirtree_display.id,
		      &SMG$M_DOWN,
		      &number_of_scroll))
	    *dirtree_viewport_rowp = new_viewport_row;
	}
    }
    else if (dir_row >
	*dirtree_viewport_rowp+dirtree_inactive_rear_row-1)
    {	    /**  Target row beyond inactive rear row  **/
	new_viewport_row1 =
	    max(1, (int) (dir_row-(dirtree_inactive_rear_row-1)));
	new_viewport_row =
	    min(new_viewport_row1, lastnode_viewport_row);
 
	number_of_scroll = new_viewport_row-*dirtree_viewport_rowp;
	if (abs(number_of_scroll) <= dirtree_display.view_rows/2)
	{
	    /*
	    **	The displacement between target row and current row
	    **	is leq half of the screen, use scroll by line.
	    **/
 
	    for (i = 1; i <= abs(number_of_scroll); i++)
		if (number_of_scroll > 0)
		    check_OK(smg$scroll_viewport (
			      &dirtree_display.id,
			      &SMG$M_UP,
			      &1))
		else
		    check_OK(smg$scroll_viewport (
			      &dirtree_display.id,
			      &SMG$M_DOWN,
			      &1))
	    *dirtree_viewport_rowp = new_viewport_row;
	}
	else
	{	
	    /*	
	    **	The displacement is larger than half the screen,
	    **	use jump scroll instead.
	    **/
 
	    new_viewport_row1 =
		max(1, (int) (dir_row-(DIRTREE_INACTIVE_FRONT_ROW-1)));
	    new_viewport_row =
		min(new_viewport_row1, lastnode_viewport_row);
 
	    number_of_scroll = new_viewport_row-*dirtree_viewport_rowp;
	    if (number_of_scroll > 0)
		check_OK(smg$scroll_viewport (
			  &dirtree_display.id,
			  &SMG$M_UP,
			  &number_of_scroll))
	    else
		check_OK(smg$scroll_viewport (
			  &dirtree_display.id,
			  &SMG$M_DOWN,
			  &number_of_scroll))
	    *dirtree_viewport_rowp = new_viewport_row;
	}
    }
    else if (lastnode->row <=
		*dirtree_viewport_rowp+dirtree_last_inactive_row-1)
    {
	/*
	**  Target node is in active area, and the
	**  last node in this directory tree is also in current screen.
	**/
 
	new_viewport_row = lastnode_viewport_row;
	number_of_scroll = *dirtree_viewport_rowp-new_viewport_row;
	for (i = 1;
	     i <= number_of_scroll && *dirtree_viewport_rowp > 1;
	     i++, (*dirtree_viewport_rowp)--)
	    check_OK(smg$scroll_viewport (
		      &dirtree_display.id,
		      &SMG$M_DOWN,
		      &1))
    }
	
    /*
    **	Compute new viewport column position
    **/
 
    while (dir_col+MAXDIRSPEC >
	*dirtree_viewport_colp+dirtree_display.view_width-1)
    {
	check_OK(smg$scroll_viewport (
		&dirtree_display.id,
		&SMG$M_LEFT,
		&MAXDIRSPEC))
	 *dirtree_viewport_colp += MAXDIRSPEC;
    }
 
    while (dir_col < *dirtree_viewport_colp)
    {
	check_OK(smg$scroll_viewport (
		    &dirtree_display.id,
		    &SMG$M_RIGHT,
		    &MAXDIRSPEC))
	*dirtree_viewport_colp -= MAXDIRSPEC;
    }
    return DX__NORMAL;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      FIND_DOWN(), FIND_RIGHT(), and FIND_LEFT() functions handle GOLD-key
**	fast move.
**
**--
**/
void	    find_down(curnode, parnode, q, flagp)
struct node_tag *curnode;
struct node_tag *parnode;
struct node_tag **q;
int *flagp;
{
    if (curnode != (struct node_tag *)NULL)
    {
	if (curnode->left == parnode)
	{
	    find_down(curnode->down, parnode, q, flagp);
	    find_down(curnode->right, curnode, q, flagp);
	
	    if (! *flagp)
	    {
		*flagp = 1;
		*q = curnode;
	    }
	}
    }
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
struct node_tag *	find_right(p)
struct node_tag *p;
{
    if (p->right == (struct node_tag *)NULL)
	return p;
    else
	return find_right(p->right);
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
struct node_tag *	find_left(p)
struct node_tag *p;
{
    if (p != (struct node_tag *)NULL)
	if (p->left->right == p)
	    return find_left(p->left);
	else
	    return p;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
struct node_tag *dirtree_page_down(n, lastnode)
struct node_tag *n;
struct node_tag *lastnode;
{
    char short_path_name[MAXDIRSPEC+1];	/**  Buffer to node name     **/
    $DESCRIPTOR(short_path_name_descrip, short_path_name);
    int max_scroll, number_of_scroll;
    int row, col, new_row, adjustment, i;
    int dirtree_last_inactive_row = 1+((dirtree_display.view_rows-1)/LINESPACE)*LINESPACE;
 
    sprintf(short_path_name, "%.8s", n->short_path_name);
    LENGTH(short_path_name_descrip) = strlen(short_path_name);
    check_OK(smg$put_chars (
	    &dirtree_display.id,
	    &short_path_name_descrip,
	    &(n->row),
	    &(n->column),
	    0,
	    &SMG$M_NORMAL,
	    0,
	    0))
 
    max_scroll = LINESPACE*((dirtree_display.view_rows-1)/LINESPACE);
    new_row = min((int)lastnode->row, (int)(cwdnode->row+max_scroll));
 
    if (n->row == lastnode->row)    /**  CHANGE TO LAST NODE IF LAST ROW     **/
	n = lastnode;
    else			    /**  FIND NEW NODE			     **/
	n = search_node_page(root, new_row, n->column);
 
    /*
    **	    SCROLL LESS THAN ONE FULL SCREEN IF NEW DIRECTORY IS NOT
    **	    WITHIN THE INACTIVE AREA
    **/
 
    adjustment = dirtree_display.view_rows-dirtree_last_inactive_row+1;
    if (n->row <
	    dirtree_display.view_beg_y+dirtree_display.view_rows-adjustment+
	    DIRTREE_INACTIVE_FRONT_ROW-1)
	number_of_scroll = dirtree_display.view_rows-adjustment-
			    (DIRTREE_INACTIVE_FRONT_ROW-1);
    else
	number_of_scroll = dirtree_display.view_rows-adjustment;
	
    /*
    **	    SCROLL FILER DISPLAY.  STOP AS SOON AS BOTTOM OF RECORD
    **	    IS REACHED.
    **/
 
    for (i = 1;
	 i <= number_of_scroll &&
	    dirtree_display.view_beg_y+dirtree_display.view_rows-adjustment < lastnode->row;
	 i++, dirtree_display.view_beg_y++)
	check_OK(smg$scroll_viewport (
		      &dirtree_display.id,
		      &SMG$M_UP,
		      &1))
 
    return n;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
struct node_tag *search_node_page (p, row, col)
struct node_tag *p;
int row;
int col;
{
    if (p == (struct node_tag *)NULL)
 
	return (struct node_tag *)NULL;
 
    else if (p->row < row)
	/**  No node has been found with the given row  **/
 
	if (p->down == (struct node_tag *)NULL)
 
	    return search_node_page(p->right, row, col);
 
	else if (p->down->row > row)
 
	    return search_node_page(p->right, row, col);
 
	else
 
	    return search_node_page(p->down, row, col);
 
    else if (p->column >= col || p->right == (struct node_tag *)NULL)
 
	return p;  /**  Target node found  **/
 
    else  /**  Search the nodes in the same row  **/
 
	return search_node_page(p->right, row, col);
 
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
struct node_tag *dirtree_page_up (n)
struct node_tag *n;
{
    char short_path_name[MAXDIRSPEC+1];	/**  Buffer to node name     **/
    $DESCRIPTOR(short_path_name_descrip, short_path_name);
    int max_scroll, number_of_scroll;
    int adjustment, new_row, i;
    int dirtree_last_inactive_row = 1+((dirtree_display.view_rows-1)/LINESPACE)*LINESPACE;
    int dirtree_inactive_rear_row = dirtree_last_inactive_row-DIRTREE_INACTIVE_ROWS;
 
    sprintf(short_path_name, "%.8s", n->short_path_name);
    LENGTH(short_path_name_descrip) = strlen(short_path_name);
    check_OK(smg$put_chars (
	    &dirtree_display.id,
	    &short_path_name_descrip,
	    &(n->row),
	    &(n->column),
	    0,
	    &SMG$M_NORMAL,
	    0,
	    0))
 
    if (n->row == 1)	    /**  ALREADY IN THE TOP ROW			     **/
 
	n = root;
 
    else {		    /**  SCROLL UP ONE SCREEN			     **/
 
	max_scroll = LINESPACE*((dirtree_display.view_rows-1)/LINESPACE);
	new_row = max(1, (int)(n->row-max_scroll));
	n = search_node_page(root, new_row, n->column);
 
    }
 
    /**  CALCULATE NUMBER OF ROWS TO BE SCROLLED  **/
 
    adjustment = dirtree_display.view_rows-dirtree_last_inactive_row+1;
    if (n->row > dirtree_display.view_beg_y-(dirtree_display.view_rows-adjustment)+dirtree_inactive_rear_row-1)
	number_of_scroll = dirtree_display.view_rows-adjustment-(DIRTREE_INACTIVE_FRONT_ROW-1);
    else
	number_of_scroll = dirtree_display.view_rows-adjustment;
	
    /*
    **	    SCROLL FILER DISPLAY.  STOP AS SOON AS TOP OF RECORD
    **	    IS REACHED.
    **/
 
    for (i = 1; i <= number_of_scroll && dirtree_display.view_beg_y > 1; i++, dirtree_display.view_beg_y--)
	check_OK(smg$scroll_viewport (
		      &dirtree_display.id,
		      &SMG$M_DOWN,
		      &1))
 
    return n;
}
 
 

/*
**++
**  FUNCTIONAL DESCRIPTION:
**
**      tbs
**
**--
**/
int	direct_initial()
{
    char initial_root[MAXFILESPEC+1];
    char initial_usrnam[MAXFILESPEC+1];
    char cwd_usrnam[MAXFILESPEC+1];
    char prompt[MAXFILESPEC+1];
    char errmsg[MAXFILESPEC+1];
    char short_path_name[MAXDIRSPEC+1];
    $DESCRIPTOR(short_path_name_descrip, short_path_name);
    char *cp;
    struct node_tag *n;
    char dirbuf[MAXFILESPEC+1];
 
    /*
    **  Compare initial root directory name with current root
    **  directory name
    **/
 
    find_root(DX_CURRENT_DIRECTORY.cur_dir, initial_root);
 
    /*
    **	Un-highlight current directory
    **/
 
    sprintf(short_path_name, "%.8s", cwdnode->short_path_name);
    LENGTH(short_path_name_descrip) = strlen(short_path_name);
    check_OK(smg$put_chars (
	    &dirtree_display.id,
	    &short_path_name_descrip,
	    &(cwdnode->row) ,
	    &(cwdnode->column) ,
	    0,
	    &SMG$M_NORMAL,
	    0,
	    0 ))
 
    cwdnode = search_node(root, strchr(DX_CURRENT_DIRECTORY.cur_dir, '[')+1);
 
    return DX__NORMAL;
}
