 /*
  * Khoros: $Id: connection.c,v 1.4 1992/03/20 22:42:42 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: connection.c,v 1.4 1992/03/20 22:42:42 dkhoros Exp $";
#endif

 /*
  * $Log: connection.c,v $
 * Revision 1.4  1992/03/20  22:42:42  dkhoros
 * VirtualPatch5
 *
  */ 


/*
 *----------------------------------------------------------------------
 *
 * Copyright 1990, University of New Mexico.  All rights reserved.
 *
 * Permission to copy and modify this software and its documen-
 * tation only for internal use in your organization is hereby
 * granted, provided that this notice is retained thereon and
 * on all copies.  UNM makes no representations as too the sui-
 * tability and operability of this software for any purpose.
 * It is provided "as is" without express or implied warranty.
 * 
 * UNM DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
 * NESS.  IN NO EVENT SHALL UNM BE LIABLE FOR ANY SPECIAL,
 * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY OTHER DAMAGES WHAT-
 * SOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PER-
 * FORMANCE OF THIS SOFTWARE.
 * 
 * No other rights, including for example, the right to redis-
 * tribute this software and its documentation or the right to
 * prepare derivative works, are granted unless specifically
 * provided in a separate license agreement.
 *---------------------------------------------------------------------
 */

#include "unmcopyright.h"	 /* Copyright 1990 by UNM */
#include "cantata.h"

static int bound();
static Node  *locate_copy_node();


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name:  connection.c				<<<<
   >>>>								<<<<
   >>>>   description:						<<<<
   >>>>								<<<<
   >>>>      routines:  xvl_delete_connection()			<<<<
   >>>>                 xvl_build_connection()			<<<<
   >>>>                 xvl_check_connection()			<<<<
   >>>>                 xvl_clear_connections()			<<<<
   >>>>                 xvl_erase_connection()			<<<<
   >>>>                 xvl_redraw_connections()		<<<<
   >>>>                 xvl_draw_connections()			<<<<
   >>>>                 xvl_draw_connection()			<<<<
   >>>>                 xvl_locate_connection()			<<<<
   >>>>								<<<<
   >>>>                 xvl_cut_connections()			<<<<
   >>>>                 xvl_create_connections()		<<<<
   >>>>                 xvl_copy_connections()			<<<<
   >>>>                 xvl_destroy_external_connections()	<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */


 
/************************************************************
*
* Routine Name:  xvl_delete_connection
*
*      Purpose:  The following routine is used to delete a connection
*		 between an input node and output node.
*
*        Input:  inode  -   the input node to be connected 
*		 onode  -   the output node to be connected
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_delete_connection(inode, onode)

Node	*inode, *onode;
{
	Node	*iparent, *oparent;


	/*
	 *  We need to erase the connection and then delete the input
	 *  and output node from the each other connection list.  If the
	 *  input node's connection list is empty we then have to also
	 *  delete the filename and set the input glyph's run type to be
	 *  INCOMPLETE.  We do the same for the output node, but only if
	 *  the output filename is a temporary file, in which case we delete
	 *  the temporary file.
	 */
	if (!xvl_check_connection(&inode, &onode, &iparent, &oparent))
	   return;

	xvl_erase_connection(inode, onode);
	if (iparent) xvl_erase_connection(iparent, onode);
	if (oparent) xvl_erase_connection(inode, oparent);
	if (iparent && oparent) xvl_erase_connection(iparent, oparent);

	onode->links = xvl_delete_from_nodelist(inode, onode->links);
	inode->links = xvl_delete_from_nodelist(onode, inode->links);
	if (iparent)
	{
	   onode->links   = xvl_delete_from_nodelist(iparent, onode->links);
	   iparent->links = xvl_delete_from_nodelist(onode, iparent->links);
	}

	if (oparent)
	{
	   inode->links   = xvl_delete_from_nodelist(oparent, inode->links);
	   oparent->links = xvl_delete_from_nodelist(inode, oparent->links);
	}

	if (oparent && iparent)
	{
	   iparent->links = xvl_delete_from_nodelist(oparent, iparent->links);
	   oparent->links = xvl_delete_from_nodelist(iparent, oparent->links);
	}

	if (inode->links == NULL)
	{
	   if (xvl_check_if_optional(inode))
	   {
	      inode->selected = False;
	      xvl_change_optional(inode);

	      if (iparent)
	      {
		 iparent->selected = False;
	         xvl_change_optional(iparent);
	      }
	   }

	   /*
	    *  Free the associated filename
	    */
	   if (inode->filename != NULL)
	   {
	      if (iparent)
	      {
		 free(iparent->filename);
	         iparent->filename = NULL;
	      }
	      free(inode->filename);
	      inode->filename = NULL;
	   }
	   xvl_update_filename(inode);
	}

	if (onode->links == NULL)
	{
	   if (xvl_check_if_optional(onode))
	   {
	      onode->selected = False;
	      xvl_change_optional(onode);

	      if (oparent)
	      {
		 oparent->selected = False;
	         xvl_change_optional(oparent);
	      }
	   }
	}

	/*
	 *  Now that we have deleted the node connections we need to
	 *  recompute the glyph's run type.
	 */
	xvl_get_node_type(inode->glyph);
	xvl_get_node_type(onode->glyph);

	if (iparent) xvl_get_node_type(iparent->glyph);
	if (oparent) xvl_get_node_type(oparent->glyph);
}



/************************************************************
*
* Routine Name:  xvl_build_connection
*
*      Purpose:  The following routine is used to build a connection
*		 between an input node and output node.
*
*        Input:  inode  -   the input node to be connected 
*		 onode  -   the output node to be connected
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_build_connection(inode, onode)

Node	*inode, *onode;
{
	Node	  *iparent, *oparent;
	char	  *filename, *machine, temp[MaxLength];


	if (!xvl_check_connection(&inode, &onode, &iparent, &oparent))
	   return;

	/*
	 *  If no filename exists for the output node then create 
	 *  temporary one.
	 */
	if (onode->filename == NULL)
	{
	   machine = xvl_get_machname(onode->glyph, temp);
	   filename = xvl_tempnam(onode->glyph->label_str, onode->transport,
					machine);
	   if (filename == NULL)
	      return;

	   onode->filename = filename;
	   

	   /*
	    *  Update the filename associated with the the output node.
	    */
	   onode->dav = False;
	   xvl_update_dav(onode);

	   onode->temp_file = True;
	   xvl_update_filename(onode);

	   if (oparent)
	   {
	      oparent->filename = xvf_strcpy(filename);
	      oparent->dav = False;
	      oparent->temp_file = True;
	   }
	}

	/*
	 *  Destroy any connections that currently exist for the input
	 *  side.  We do this since currently we only allow inputs to have
	 *  a single input connection.  Although maybe in the future we
	 *  will allow multiple inputs for a given input node, like we do
	 *  for output connections.
	 */
	if (inode->links != NULL)
	{
	   xvl_delete_connection(inode, inode->links->node);

	   inode->links = NULL;
	   if (iparent) iparent->links = NULL;
	}

	/*
	 *  Now that the output and input connection has been initialized,
	 *  we need to make sure that if the connection are currently not
	 *  selected (optional) that we update to reflect that the node
	 *  is currently selected.
	 */
	if (onode->selected == False)
	{
	   onode->selected = True;
	   xvl_change_optional(onode);
	   if (oparent)
	   {
	      oparent->selected = True;
	      xvl_change_optional(oparent);
	   }
	}

	if (inode->selected == False)
	{
	   inode->selected = True;
	   xvl_change_optional(inode);
	   if (iparent)
	   {
	      iparent->selected = True;
	      xvl_change_optional(iparent);
	   }
	}

	/*
	 *  Connect the input node's connection to the output node.
	 */
	inode->links = xvl_add_to_nodelist(onode, inode->links);
	onode->links = xvl_add_to_nodelist(inode, onode->links);
	if (iparent)
	{
	   onode->links   = xvl_add_to_nodelist(iparent, onode->links);
	   iparent->links = xvl_add_to_nodelist(onode, iparent->links);
	}

	if (oparent)
	{
	   inode->links   = xvl_add_to_nodelist(oparent, inode->links);
	   oparent->links = xvl_add_to_nodelist(inode, oparent->links);
	}

	if (oparent && iparent)
	{
	   iparent->links = xvl_add_to_nodelist(oparent, iparent->links);
	   oparent->links = xvl_add_to_nodelist(iparent, oparent->links);
	}


	/*
	 *  Update the input connection.
	 */
	inode->dav = onode->dav;
	xvl_update_dav(inode);

	if (inode->filename != NULL) free(inode->filename);
	inode->filename = xvf_strcpy(onode->filename);
	xvl_update_filename(inode);

	if (iparent)
	{
	   iparent->dav = onode->dav;
	   xvl_update_dav(iparent);

	   if (iparent->filename != NULL) free(iparent->filename);
	   iparent->filename = xvf_strcpy(onode->filename);
	}

	/*
	 *  Update the node types for the autorun scheduler.
	 */
	xvl_get_node_type(inode->glyph);
	xvl_get_node_type(onode->glyph);

	if (iparent) xvl_get_node_type(iparent->glyph);
	if (oparent) xvl_get_node_type(oparent->glyph);

	/*
	 *  Draw the newly built connection.
	 */
	xvl_draw_connection(inode, onode);
	if (iparent) xvl_draw_connection(iparent, onode);
	if (oparent) xvl_draw_connection(inode, oparent);
	if (iparent && oparent) xvl_draw_connection(iparent, oparent);
}



/************************************************************
*
* Routine Name:  xvl_check_connection
*
*      Purpose:  This routine is used to check two nodes
*		 (usually the first is an input node and the second
*		 an output node).  We check to see if the glyphs
*		 belong to the same workspace, if not then we
*		 will need to do a bunch of error checking to
*		 make sure that the nodes make a valid connection.
*
*		 A valid connection is one where two glyphs are within
*		 first generation of each other.  If this is not a
*		 valid connection then we return False, otherwise we
*		 return True.  But we must also find the glyph's
*		 counterpart connection so that xvl_build_glyph()
*		 or xvl_delete_glyph() can build or delete the
*		 connection.
*
*        Input:  inode  - the input node to check
*		 onode  - the output node to check
*
*       Output:  iparent	     - the input's other connection
*		 oparent     - the output's other connection
*
*		 returns True if valid connection or False if not
*		 a valid connection.
*
*
*   Written By: Mark Young
*
*************************************************************/


int xvl_check_connection(inode, onode, iparent, oparent)

Node	**inode, **onode, **iparent, **oparent;
{
	int	  error, get_iparent, get_oparent;
	Node	  *node;
	Glyph	  *iglyph, *oglyph;
	Workspace *owork, *iwork, *macro;


	*iparent = *oparent = NULL;
	iglyph = (*inode)->glyph;
	oglyph = (*onode)->glyph;
	iwork = iglyph->workspace;
	owork = oglyph->workspace;

	/*
	 *  Check to see if the nodes are external to each other.
	 *  If so then we need to do a bunch of error checking.  Basically
	 *  we will only allow the user to connect glyphs together that
	 *  are of a single generation within each other.  Meaning that
	 *  you can connect a parent to a children or siblings but nothing
	 *  else.
	 */
	if (iglyph->type != PROCEDURE && oglyph->type != PROCEDURE &&
	    (xvl_check_if_external(iglyph, oglyph) == False ||
	     xvl_check_if_undo(iglyph, oglyph)))
	{
	   return(True);
	}

	/*
	 *  Procedure glyphs must be on the same workspace in order to be
	 *  within the first genereation.
	 */
	if (iglyph->type == PROCEDURE && oglyph->type == PROCEDURE)
	{
	   if (xvl_check_if_external(iglyph, oglyph))
	   {
	      xvl_error_glyphs("Error!  You are not allowed to connect glyphs \
that are on levels of the macro hierarchy that differ in depth by more than \
one level.  'Merge' glyphs at the intermediate levels can be used to propagate \
the connection between levels.", iglyph, oglyph);
	      return(False);
	   }
	   else
	   {
	      *iparent = *inode;
	      macro = iglyph->val.macro;
	      if (!(*inode = xvl_find_node(macro, (*inode)->selection)))
	      {
		 xvl_error_glyphs("Internal Error! Unable to find the \
corresponding input glyph that this macro glyph represents.  Therefore the \
current connection request will not be performed.", oglyph, NULL);
		 return(False);
	      }
	      iglyph = (*inode)->glyph;
	      iwork = iglyph->workspace;

	      *oparent = *onode;
	      macro = oglyph->val.macro;
	      if (!(*onode = xvl_find_node(macro, (*onode)->selection)))
	      {
		 xvl_error_glyphs("Internal Error! Unable to find the \
corresponding output glyph that this macro glyph represents.  Therefore the \
current connection request will not be performed.", oglyph, NULL);
		 return(False);
	      }
	      oglyph = (*onode)->glyph;
	      owork = oglyph->workspace;
	   }
	}
	else if (iglyph->type == PROCEDURE || oglyph->type == PROCEDURE)
	{
	   error = False;
	   if (iglyph->type == PROCEDURE)
	   {
	      if (xvl_valid_mconnection(iwork, owork))
	      {
	         *iparent = *inode;
	         macro = iglyph->val.macro;
	         if (!(*inode = xvl_find_node(macro, (*inode)->selection)))
	         {
		    xvl_error_glyphs("Internal Error! Unable to find the \
corresponding input glyph that this macro glyph represents.  Therefore the \
current connection request will not be performed.", oglyph, NULL);
		   return(False);
	         }
		 iglyph = (*inode)->glyph;
		 iwork = iglyph->workspace;
	      }
	      else
		 error = True;
	   }

	   if (oglyph->type == PROCEDURE)
	   {
	      if (xvl_valid_mconnection(owork, iwork))
	      {
	         *oparent = *onode;
	         macro = oglyph->val.macro;
	         if (!(*onode = xvl_find_node(macro, (*onode)->selection)))
	         {
		    xvl_error_glyphs("Internal Error! Unable to find the \
corresponding output glyph that this macro glyph represents.  Therefore the \
current connection request will not be performed.", oglyph, NULL);
		   return(False);
	         }
		 oglyph = (*onode)->glyph;
		 owork = oglyph->workspace;
	      }
	      else
		 error = True;
	   }

	   if (error == True)
	   {
	      xvl_error_glyphs("Error!  You are not allowed to connect glyphs \
that are on levels of the macro hierarchy that differ in depth by more than \
one level.  'Merge' glyphs at the intermediate levels can be used to propagate \
the connection between levels.", iglyph, oglyph);
	      return(False);
	   }
	}

	/*
	 *  Those were the easy cases, now we need to check to see if the
	 *  the glyphs are within first generation of each other.
	 */
	error = True;
	get_iparent = get_oparent = False;
	if (iwork->parent != NULL && owork->parent != NULL)
	{
	   if (iwork->parent->workspace == owork->parent->workspace)
	   {
	      get_iparent = True;
	      get_oparent = True;
	      error = False;
	   }
	}

	if (owork->parent != NULL)
	{
	   if (iwork == owork->parent->workspace)
	   {
	      get_oparent = True;
	      error = False;
	   }
	}

	if (iwork->parent != NULL)
	{
	   if (owork == iwork->parent->workspace)
	   {
	      get_iparent = True;
	      error = False;
	   }
	}

	if (error == True)
	{
	   xvl_error_glyphs("Error!  You are not allowed to connect glyphs \
that are on levels of the macro hierarchy that differ in depth by more than \
one level.  'Merge' glyphs at the intermediate levels can be used to propagate \
the connection between levels.", iglyph, oglyph);
	   return(False);
	}

	if (get_oparent)
	{
	   if (!(*oparent = xvl_get_node(owork->parent,(*onode)->selection)))
	   {
	      node = (Node *) XtCalloc(1,sizeof(Node));
	      node->selected  = (*onode)->selected;
	      node->selection = (*onode)->selection;
	      node->temp_file = (*onode)->temp_file;
	      node->dav       = (*onode)->dav;
	      xvl_update_dav(node);

	      if ((*onode)->filename != NULL)
		 node->filename  = xvf_strcpy((*onode)->filename);

	      node->glyph = owork->parent;
	      node->links = NULL;
	      *oparent    = node;

	      owork->parent->output_list = xvl_add_to_nodelist(*oparent,
					  owork->parent->output_list);
	      xvl_create_glyph(owork->parent);
	   }
	}

	if (get_iparent)
	{
	   if (!(*iparent = xvl_get_node(iwork->parent,(*inode)->selection)))
	   {
	      node = (Node *) XtCalloc(1,sizeof(Node));
	      node->selected  = (*inode)->selected;
	      node->selection = (*inode)->selection;
	      node->temp_file = (*inode)->temp_file;
	      node->dav       = (*inode)->dav;
	      xvl_update_dav(node);

	      if ((*inode)->filename != NULL)
		 node->filename  = xvf_strcpy((*inode)->filename);

	      node->glyph = iwork->parent;
	      node->links = NULL;
	      *iparent    = node;

	      iwork->parent->input_list = xvl_add_to_nodelist(*iparent,
					iwork->parent->input_list);
	      xvl_create_glyph(iwork->parent);
	   }
	}
	return(True);
}

int xvl_valid_mconnection(mwork, gwork)

Workspace *mwork, *gwork;
{
	if (mwork == gwork)
	   return(True);
	else if (gwork->parent == NULL)
	   return(False);
	else if (mwork == gwork->parent->workspace)
	   return(True);
	else
	   return(False);
}
Node *xvl_find_node(workspace, selection)

Workspace	*workspace;
xvf_selection	*selection;
{
	Node	  *node;
	Glyph	  *glyph;
	GlyphList *glyphlist;


	glyphlist = workspace->glyphs;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;
	   if ((node = xvl_get_node(glyph, selection)) != NULL)
	      return(node);

	   glyphlist = glyphlist->next;
	}
	return(NULL);
}



Node *xvl_get_node(glyph, selection)

Glyph		*glyph;
xvf_selection	*selection;
{
	NodeList  *nodelist;


	nodelist = glyph->input_list;
	while (nodelist != NULL)
	{
	   if (nodelist->node->selection == selection)
	      return(nodelist->node);

	   nodelist = nodelist->next;
	}

	nodelist = glyph->output_list;
	while (nodelist != NULL)
	{
	   if (nodelist->node->selection == selection)
	      return(nodelist->node);

	   nodelist = nodelist->next;
	}
	return(NULL);
}



/************************************************************
*
* Routine Name: xvl_clear_connections
*
*      Purpose: This routine is used to erase the glyph's con-
*		nections.  The procedure is to race thru the con-
*		nections list looking for all the glyph's connections.
*		When a connection is found a bounding box is computed
*		for the connection.  This is then made into a clip
*		mask which is then used when exposing the window.
*		This way only glyphs that are located within the glyph's
*		area of connections are refreshed, not the entire workspace.
*
*        Input: glyph     - the glyph that we want to erase the connections
*			    for.
*
*       Output: erases the glyph's connections
*
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


xvl_clear_connections(glyph)

Glyph	  *glyph;
{
	Node	    *node;
	char	    temp[MaxLength];
	NodeList    *nodelist, *links;


	if (glyph->input_list == NULL && glyph->output_list == NULL)
	   return;

	/*
	 *  Clear the regions for the input connections to the glyph.
	 */
	nodelist = glyph->input_list;
	while (nodelist != NULL)
	{
	   node = nodelist->node;
	   if (node->selected == True)
	   {
	      links = node->links;
	      while (links != NULL)
	      {
	         xvl_erase_connection(node, links->node);
	         links = links->next;
	      }
	   }
	   nodelist = nodelist->next;
	}

	/*
	 *  Clear the regions for the output connections to the glyph.
	 */
	nodelist = glyph->output_list;
	while (nodelist != NULL)
	{
	   node = nodelist->node;
	   if (node->selected == True)
	   {
	      links = node->links;
	      while (links != NULL)
	      {
	         xvl_erase_connection(links->node, node);
	         links = links->next;
	      }
	   }
	   nodelist = nodelist->next;
	}
	xvl_clear_label(glyph, xvl_get_machlabel(glyph, temp));
}



/************************************************************
*
* Routine Name: xvl_erase_connection
*
*      Purpose: This routine is used to erase a single connection
*		between two nodes.  The input and output nodes.
*
*        Input: 1.  input  - the first node is the input connection
*		2.  output - the second node in the output connection
*
*       Output: erae a line between two nodes.
*
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


xvl_erase_connection(inode, onode)

Node	*inode, *onode;
{
	Window	    draw;
	Display	    *display;
	Workspace   *workspace;
	Position    xpos, ypos;
	unsigned    int width, height;
	int	    x, y, x1, x2, y1, y2, rootx, rooty;


	if (inode->glyph->toplevel == NULL ||
	    onode->glyph->toplevel == NULL)
	{
	   return;
	}

	if (!xvl_check_if_viewable(inode->glyph) || 
	    !xvl_check_if_viewable(onode->glyph))
	{
	   return;
	}

	if (xvl_check_if_external(inode->glyph, onode->glyph))
	{
	   workspace = inode->glyph->workspace;
	   XtTranslateCoords(workspace->toplevel, 0, 0, &xpos, &ypos);
	   rootx = xpos;
	   rooty = ypos;

	   workspace = onode->glyph->workspace;
	   XtTranslateCoords(workspace->toplevel, 0, 0, &xpos, &ypos);
	   rootx -= xpos;
	   rooty -= ypos;
	}
	else
	{
	   rootx = 0;
	   rooty = 0;
	}

	x1 = inode->x + rootx;
	x2 = onode->x - rootx;
	if (x1 < x2)
	{
	   x1 = inode->x - StubLength - 5;
	   x2 = onode->x + StubLength + 5;
	}
	else if ((x1 - x2) < 2*StubLength)
	{
	   x1 = inode->x + StubLength + 5;
	   x2 = onode->x - StubLength - 5;
	}
	else
	{
	   x1 = inode->x + 5;
	   x2 = onode->x - 5;
	}

	y1 = inode->y - rooty;
	y2 = onode->y + rooty;
	if (y1 < y2)
	{
	   y1 = inode->y - 5;
	   y2 = onode->y + 5;
	}
	else
	{
	   y1 = inode->y + 5;
	   y2 = onode->y - 5;
	}


	if (xvl_check_if_external(inode->glyph, onode->glyph))
	{
	   x = MIN(x1 + rootx, x2);
	   y = MIN(y1 + rooty, y2);
	   width  = ABS(x2 - (x1 + rootx));
	   height = ABS(y2 - (y1 + rooty));

	   workspace = onode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);
	   XClearArea(display, draw, x, y, width, height, True);

	   x = MIN(x1, x2 - rootx);
	   y = MIN(y1, y2 - rooty);
	   width  = ABS((x2 - rootx) - x1);
	   height = ABS((y2 - rooty) - y1);

	   workspace = inode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);
	   XClearArea(display, draw, x, y, width, height, True);
	}
	else
	{
	   x = MIN(x1, x2);
	   y = MIN(y1, y2);
	   width  = ABS(x2 - x1);
	   height = ABS(y2 - y1);

	   workspace = inode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);
	   XClearArea(display, draw, x, y, width, height, True);
	}
}



/************************************************************
*
* Routine Name: xvl_redraw_connections
*
*      Purpose: This routine is used to redraw all the glyph's
*		connections.  The procedure is to race thru the con-
*		nections list looking for all the selected glyph
*		connections.  Calling xvl_draw_connection() to
*		actually draw the connection.
*
*        Input: 1.  workspace - the workspace in which the glyph is located.
*
*       Output: draws all the workspace glyph connections
*
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


xvl_redraw_connections(workspace)

Workspace  *workspace;
{
	Glyph		*glyph;
	GlyphList	*glyphlist;
	Node		*inode, *onode;
	NodeList	*nodelist, *links;
	char		temp[MaxLength];


	/*
	 *  According to each glyph draw the connections.
	 */
	glyphlist = workspace->glyphs;
	while (glyphlist != NULL)
	{
	   glyph = glyphlist->glyph;

	   /*
	    *  Race thru the output connections drawing the connections
	    *  to the input part of the connection list.
	    */
	   nodelist = glyph->output_list;
	   while (nodelist != NULL)
	   {
	      onode = nodelist->node;
	      if (onode->selected == True)
	      {
	         links = onode->links;
	         while (links != NULL)
	         {
	            xvl_draw_connection(links->node, onode);
	            links = links->next;
	         }
	      }
	      nodelist = nodelist->next;
	   }

	   /*
	    *  Race thru the input connections drawing the foreign connections
	    *  to the input part of the connection list.
	    */
	   nodelist = glyph->input_list;
	   while (nodelist != NULL)
	   {
	      inode = nodelist->node;
	      if (inode->selected == True)
	      {
	         links = inode->links;
	         while (links != NULL)
	         {
		    if (xvl_check_if_external(inode->glyph, links->node->glyph))
		    {
	               xvl_draw_connection(inode, links->node);
		    }
	            links = links->next;
	         }
	      }
	      nodelist = nodelist->next;
	   }
	   xvl_draw_label(glyph, xvl_get_machlabel(glyph, temp));
	   glyphlist = glyphlist->next;
	}
}



/************************************************************
*
* Routine Name: xvl_draw_connections
*
*      Purpose: This routine is used to redraw a single the glyph's
*		connections.  The procedure is to race thru the con-
*		nections list looking for all the selected glyph
*		connections.  Calling xvl_draw_connection() to
*		actually draw the connection.
*
*        Input: 1.  glyph - the glyph in which the connections should
*			    be redrawn
*
*       Output: draws single glyph's connections
*
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


xvl_draw_connections(glyph)

Glyph	*glyph;
{
	Node		*node;
	char		temp[MaxLength];
	NodeList	*nodelist, *links;


	/*
	 *  Race thru the output connections drawing the connections
	 *  to the input part of the connection list.
	 */
	nodelist = glyph->output_list;
	while (nodelist != NULL)
	{
	   node = nodelist->node;
	   if (node->selected == True)
	   {
	      links = node->links;
	      while (links != NULL)
	      {
	         xvl_draw_connection(links->node, node);
	         links = links->next;
	      }
	   }
	   nodelist = nodelist->next;
	}

	/*
	 *  Race thru the output connections drawing the connections
	 *  to the input part of the connection list.
	 */
	nodelist = glyph->input_list;
	while (nodelist != NULL)
	{
	   node = nodelist->node;
	   if (node->selected == True)
	   {
	      links = node->links;
	      while (links != NULL)
	      {
	         xvl_draw_connection(node, links->node);
	         links = links->next;
	      }
	   }
	   nodelist = nodelist->next;
	}
	xvl_draw_label(glyph, xvl_get_machlabel(glyph, temp));
}



/************************************************************
*
* Routine Name: xvl_draw_connection
*
*      Purpose: This routine is used to draw a single connection
*		between two nodes.  The input and output nodes.
*
*        Input: 1.  inode  - the first node is the input connection
*		2.  onode  - the second node in the output connection
*
*       Output: draws a line between two nodes.
*
*
*   WRITTEN BY: Mark Young & Stephanie Hallet
*
*************************************************************/


xvl_draw_connection(inode, onode)

Node	  *inode, *onode;
{
	Window    draw;
	Display   *display;
	XPoint    polyline[4];
	int	  rootx, rooty;
	Dimension width, height;
	Position  x, y, xpos, ypos;
	Workspace *workspace, *attributes;



	if (inode->glyph->toplevel == NULL ||
	    onode->glyph->toplevel == NULL)
	{
	   return;
	}

	if (!xvl_check_if_viewable(inode->glyph) || 
	    !xvl_check_if_viewable(onode->glyph))
	{
	   return;
	}
	workspace = inode->glyph->workspace;
	attributes = xvl_get_attributes(workspace);
	if (inode->glyph->managed && attributes->rubberband == False)
	   return;

	workspace = onode->glyph->workspace;
	attributes = xvl_get_attributes(workspace);
	if (onode->glyph->managed && attributes->rubberband == False)
	   return;


	xvl_query_glyph(inode->glyph, &x, &y);
	xvl_query_widget(inode->widget, &xpos, &ypos, &width, &height);
	inode->x = x + xpos;
	inode->y = y + ypos + height/2;

	xvl_query_glyph(onode->glyph, &x, &y);
	xvl_query_widget(onode->widget, &xpos, &ypos, &width, &height);
	onode->x = x + xpos + width;
	onode->y = y + ypos + height/2;


	if (xvl_check_if_external(inode->glyph, onode->glyph))
	{
	   workspace = inode->glyph->workspace;
	   XtTranslateCoords(workspace->toplevel, 0, 0, &x, &y);
	   rootx = x;
	   rooty = y;

	   workspace = onode->glyph->workspace;
	   XtTranslateCoords(workspace->toplevel, 0, 0, &x, &y);
	   rootx -= x;
	   rooty -= y;

	   workspace = onode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);

	   polyline[0].x = onode->x;
	   polyline[0].y = onode->y;
	   polyline[1].x = onode->x + StubLength;
	   polyline[1].y = onode->y;
	   polyline[2].x = inode->x  - StubLength + rootx;
	   polyline[2].y = inode->y + rooty;
	   XDrawLines(display, draw, workspace->gc, polyline,3,CoordModeOrigin);

	   
	   workspace = inode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);

	   polyline[0].x = onode->x + StubLength - rootx;
	   polyline[0].y = onode->y - rooty;
	   polyline[1].x = inode->x  - StubLength;
	   polyline[1].y = inode->y;
	   polyline[2].x = inode->x;
	   polyline[2].y = inode->y;
	   XDrawLines(display, draw, workspace->gc, polyline,3,CoordModeOrigin);
	}
	else
	{
	   workspace = inode->glyph->workspace;
	   draw = XtWindow(workspace->draw);
	   display = XtDisplay(workspace->draw);

	   polyline[0].x = onode->x;
	   polyline[0].y = onode->y;
	   polyline[1].x = onode->x + StubLength;
	   polyline[1].y = onode->y;
	   polyline[2].x = inode->x  - StubLength;
	   polyline[2].y = inode->y;
	   polyline[3].x = inode->x;
	   polyline[3].y = inode->y;
	   XDrawLines(display, draw, workspace->gc, polyline,4,CoordModeOrigin);
	}

}



/************************************************************
*
* Routine Name:  xvl_locate_connection
*
*      Purpose:  This routine is used to find a connection given
*		 a node and an x and y postion.  The routine looks
*		 thru the node's connection list to see if the x
*		 and y coordinates match any of the connections
*		 between the node and it's foreign connections.
*
*        Input:  
*
*       Output:  returns True both glyph is enabled otherwise False
*		 is returned.
*
*
*   Written By: Mark Young
*
*************************************************************/


Node *xvl_locate_connection(node, xpos, ypos)

Node	*node;
int	xpos, ypos;
{
	NodeList   *links;
	Node	   *foreign;
	int	   x1, y1, x2, y2, tmpy;


	/*
	 *  If the node is not selected then there will not be any connections
	 *  associated with the node.
	 */
	if (node->selected == False || node->links == NULL)
	   return(NULL);

	x1 = node->x;
	y1 = node->y;
	links = node->links;

	/*
         *  Check to see if they selected the butt of the node.
         */
        if (bound(x1, y1, xpos, ypos, x1+StubLength, y1, 5))
	   return(links->node);


	x1 += StubLength;
	while (links != NULL)
	{
	   foreign = links->node;

	   x2 = foreign->x;
	   y2 = foreign->y;

	   /*
	    *  Check to see if the point is bounded between the two
	    *  connections.
	    */
           if (bound(x2, y2, xpos, ypos, x2-StubLength, y2, 5))
	      return(links->node);
	   else
	   {
	      x2 -= StubLength;
	      if (bound(x1, y1, xpos, ypos, x2, y2, 5))
	      {
	         if (ABS(x2 - x1) < 10)
		    return(foreign);

	         tmpy = ((float) y2 - y1)/(x2 - x1) * (xpos - x1);
	         if (ABS(tmpy - (ypos - y1)) < 10)
		    return(foreign);
	      }
	   }
	   links = links->next;
	}
	return(NULL);
}

static int bound(x1, y1, x, y, x2, y2, error)

int x1, y1, x, y, x2, y2, error;
{
	int temp;


	if (x1 > x2)
	{
	   temp = x1;
	   x1   = x2;
	   x2   = temp;
	}

	if (y1 > y2)
	{
	   temp = y1;
	   y1   = y2;
	   y2   = temp;
	}

	if (x1 <= (x + error) && (x - error) <= x2)
	{
	   if (y1 <= (y + error) && (y - error) <= y2)
	      return(True);
	}
	return(False);
}



/************************************************************
*
* Routine Name: xvl_cut_connections
*
*      Purpose: This routine is used to 
*
*        Input: workspace    -  the workspace to set the mapping
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_cut_connections(glyph, glyphlist)

Glyph	  *glyph;
GlyphList *glyphlist;
{
        Glyph     *parent;
        Node      *inode, *onode;
        NodeList  *links, *temp, *input_list, *output_list;


	/*
         *  Check the node list of input connections. Currently there
         *  could be more than one input connections for this one output
         *  connection.
	 */
	output_list = glyph->output_list;
	while (output_list != NULL)
	{
	   onode = output_list->node;
	   temp = links = xvl_copy_nodelist(onode->links);
	   while (links != NULL)
	   {
	      inode = links->node;
	      links = links->next;

	      if (!xvl_check_if_glyphlist(inode->glyph, glyphlist))
	      {
		 if (xvl_check_if_external(inode->glyph, onode->glyph))
		 {
		    parent = inode->glyph->workspace->parent;
		    if (parent != NULL)
		    {
		       if (!xvl_check_if_glyphlist(parent, glyphlist))
			  xvl_delete_connection(inode, onode);
		    }
		 }
		 else xvl_delete_connection(inode, onode);
	      }
	   }
	   xvl_destroy_nodelist(temp);
	   output_list = output_list->next;
	}

	input_list = glyph->input_list;
	while (input_list != NULL)
	{
	   /*
            *  Check the node list of output connections.
	    */
	   inode = input_list->node;
	   temp = links = xvl_copy_nodelist(inode->links);
	   while (links != NULL)
	   {
	      onode = links->node;
	      links = links->next;

	      if (!xvl_check_if_glyphlist(onode->glyph, glyphlist))
	      {
		 if (xvl_check_if_external(inode->glyph, onode->glyph))
		 {
		    parent = onode->glyph->workspace->parent;
		    if (parent != NULL)
		    {
		       if (!xvl_check_if_glyphlist(parent, glyphlist))
			  xvl_delete_connection(inode, onode);
		    }
		 }
		 else xvl_delete_connection(inode, onode);
	      }
	   }
	   xvl_destroy_nodelist(temp);
	   input_list = input_list->next;
	}
}



/************************************************************
*
* Routine Name: xvl_create_connections
*
*      Purpose: This routine is used to 
*
*        Input: workspace    -  the workspace to set the mapping
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_create_connections(glyph, glyphlist)

Glyph	  *glyph;
GlyphList *glyphlist;
{
        Glyph     *parent;
        Node      *inode, *onode;
        NodeList  *links, *temp, *input_list, *output_list;


	/*
         *  Check the node list of input connections. Currently there
         *  could be more than one input connections for this one output
         *  connection.
	 */
	output_list = glyph->output_list;
	while (output_list != NULL)
	{
	   onode = output_list->node;
	   temp = links = xvl_copy_nodelist(onode->links);
	   while (links != NULL)
	   {
	      inode = links->node;
	      if (!xvl_check_if_glyphlist(inode->glyph, glyphlist))
	      {
		 if (xvl_check_if_external(inode->glyph, onode->glyph))
		 {
		    parent = inode->glyph->workspace->parent;
		    if (parent != NULL)
		    {
		       if (!xvl_check_if_glyphlist(parent, glyphlist))
			  xvl_build_connection(inode, onode);
		    }
		    else xvl_build_connection(inode, onode);
		 }
		 else xvl_build_connection(inode, onode);
	      }
	      links = links->next;
	   }
	   xvl_destroy_nodelist(temp);
	   output_list = output_list->next;
	}

	input_list = glyph->input_list;
	while (input_list != NULL)
	{
	   /*
            *  Check the node list of output connections.
	    */
	   inode = input_list->node;
	   temp = links = xvl_copy_nodelist(inode->links);
	   while (links != NULL)
	   {
	      onode = links->node;
	      if (!xvl_check_if_glyphlist(onode->glyph, glyphlist))
	      {
		 if (xvl_check_if_external(inode->glyph, onode->glyph))
		 {
		    parent = onode->glyph->workspace->parent;
		    if (parent != NULL)
		    {
		       if (!xvl_check_if_glyphlist(parent, glyphlist))
			  xvl_build_connection(inode, onode);
		    }
		    else xvl_build_connection(inode, onode);
		 }
		 else xvl_build_connection(inode, onode);
	      }
	      links = links->next;
	   }
	   xvl_destroy_nodelist(temp);
	   input_list = input_list->next;
	}
}



/************************************************************
*
* Routine Name: xvl_copy_connections
*
*      Purpose: This routine is used to 
*
*        Input: workspace    -  the workspace to set the mapping
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_copy_connections(oldlist, newlist)

GlyphList *oldlist, *newlist;
{
        Glyph     *oglyph, *nglyph;
	GlyphList *oglist, *nglist;

	Workspace *oproc, *nproc;
        Node      *nnode, *onode, *node;
        NodeList  *links, *olinks, *nlinks;


	oglist = oldlist;
	nglist = newlist;
	while (oglist != NULL && nglist != NULL)
	{
	   oglyph = oglist->glyph;
	   nglyph = nglist->glyph;

	   olinks = oglyph->output_list;
	   nlinks = nglyph->output_list;
	   while (olinks != NULL && nlinks != NULL)
	   {
	      onode = olinks->node;
	      nnode = nlinks->node;
	      if (onode->temp_file == True)
	      {
		 nnode->filename = NULL;
		 xvl_update_filename(nnode);
	      }

	      links = onode->links;
	      while (links != NULL)
	      {
		 if ((node = locate_copy_node(oldlist, newlist, links->node))
			!= NULL)
		 {
		    xvl_build_connection(node, nnode);
		 }
		 links = links->next;
	      }
	      olinks = olinks->next;
	      nlinks = nlinks->next;
	   }

	   if (oglyph->type == PROCEDURE)
	   {
	      oproc = oglyph->val.macro;
	      nproc = nglyph->val.macro;
	      xvl_copy_connections(oproc->glyphs, nproc->glyphs);
	   }
	   oglist = oglist->next;
	   nglist = nglist->next;
	}
}



/************************************************************
*
* Routine Name: locate_copy_node
*
*      Purpose: This routine is used to locate the corresponding
*		copied node in the new list.
*
*        Input: oldlist   -  the old node list
*		newlist   -  the new node list
*		onode	  -  the old node
*
*       Output: returns the new node or NULL if not valid
*
*
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


static Node *locate_copy_node(oldlist, newlist, onode)

GlyphList *oldlist, *newlist;
Node	  *onode;
{
	Glyph    *oglyph, *nglyph;
	NodeList *olink, *nlink;


	while (oldlist != NULL && newlist != NULL)
	{
	   oglyph = oldlist->glyph;
	   nglyph = newlist->glyph;
	   if (oldlist->glyph == oglyph)
	   {
	      olink = oglyph->input_list;
	      nlink = nglyph->input_list;
	      while (olink != NULL && nlink != NULL)
	      {
		 if (olink->node == onode)
	            return(nlink->node);

		 olink = olink->next;
		 nlink = nlink->next;
	      }
	   }
	   oldlist = oldlist->next;
	   newlist = newlist->next;
	}
	return(NULL);
}



/************************************************************
*
* Routine Name: xvl_destroy_external_connections
*
*      Purpose: This routine is used to delete external connections
*		from the glyph.
*
*        Input: glyph -  the glyph in which we will be deleting these
*			 external connections.
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

extern Node *InputNode, *OutputNode;


xvl_destroy_external_connections(glyph)

Glyph *glyph;
{
	Glyph	  *parent;
	Node	  *inode, *onode, *iparent, *oparent;
	NodeList  *input_list, *output_list, *links, *temp;


	/*
	 *  Free the input and output node list.  This is done by going
	 *  thru each of the lists and calling xvl_delete_connection()
	 *  to delete the connections.
	 */

	/*
	 *  Free the output list.
	 */
	output_list = glyph->output_list;
	while (output_list != NULL)
	{
	   onode = output_list->node;
	   if (onode == OutputNode)
	      OutputNode = NULL;

	   /*
	    *  Destroy the node list of input connections. Currently there
	    *  could be more than one input connections for this one output
	    *  connection.
	    */
	   temp = links = xvl_copy_nodelist(onode->links);
	   while (links != NULL)
	   {
	      inode = links->node;
	      if (xvl_check_connection(&inode, &onode, &iparent, &oparent))
	      {
		 if (oparent != NULL)
		 {
	            xvl_delete_connection(inode, onode);
		    parent = oparent->glyph;
		    parent->output_list = xvl_delete_from_nodelist(oparent,
				parent->output_list);
		    xvl_create_glyph(parent);
		 }
		 if (iparent != NULL)
		 {
	            xvl_delete_connection(inode, onode);
		    parent = iparent->glyph;
		    parent->input_list = xvl_delete_from_nodelist(iparent,
				parent->input_list);
		    xvl_create_glyph(parent);
		 }
	      }
	      links = links->next;
	   }
	   xvl_destroy_nodelist(temp);
	   output_list = output_list->next;
	}

	/*
	 *  Free the input list.
	 */
	input_list = glyph->input_list;
	while (input_list != NULL)
	{
	   inode = input_list->node;
	   if (inode == InputNode)
	      InputNode = NULL;

	   /*
	    *  Destroy the node list of output connections. (note: currently
	    *  there should only be one, but there may be a time when we allow
	    *  multiple input connections).
	    */
	   temp = links = xvl_copy_nodelist(inode->links);
	   while (links != NULL)
	   {
	      onode = links->node;
	      if (xvl_check_connection(&inode, &onode, &iparent, &oparent))
	      {
		 if (iparent != NULL)
		 {
	            xvl_delete_connection(inode, onode);
		    parent = iparent->glyph;
		    parent->input_list = xvl_delete_from_nodelist(iparent,
				parent->input_list);
		    xvl_create_glyph(parent);
		 }
		 if (oparent != NULL)
		 {
	            xvl_delete_connection(inode, onode);
		    parent = oparent->glyph;
		    parent->output_list = xvl_delete_from_nodelist(oparent,
				parent->output_list);
		    xvl_create_glyph(parent);
		 }
	      }
	      links = links->next;
	   }
	   xvl_destroy_nodelist(temp);
	   input_list = input_list->next;
	}
}
