 /*
  * Khoros: $Id: callbacks.c,v 1.5 1992/03/25 17:30:01 dkhoros Exp $
  */

#if !defined(lint) && !defined(SABER)
static char rcsid[] = "Khoros: $Id: callbacks.c,v 1.5 1992/03/25 17:30:01 dkhoros Exp $";
#endif

 /*
  * $Log: callbacks.c,v $
 * Revision 1.5  1992/03/25  17:30:01  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"
#include "vsignal.h"


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name:  callbacks.c				<<<<
   >>>>								<<<<
   >>>>   description:						<<<<
   >>>>								<<<<
   >>>>      routines:  xvl_destroy_cb()			<<<<
   >>>>                 xvl_form_cb()				<<<<
   >>>>                 xvl_run_cb()				<<<<
   >>>>                 xvl_input_cb()				<<<<
   >>>>                 xvl_output_cb()				<<<<
   >>>>                 xvl_error_cb()				<<<<
   >>>>                 xvl_reset_cb()				<<<<
   >>>>                 xvl_clipboard_cb()			<<<<
   >>>>                 xvl_exposure_cb()			<<<<
   >>>>                 xvl_connection_cb()			<<<<
   >>>>                 xvl_select_cb()				<<<<
   >>>>                 xvl_control_cb()			<<<<
   >>>>                 xvl_move_cb()				<<<<
   >>>>                 xvl_dispatch_cb()			<<<<
   >>>>                 xvl_distributed_cb()			<<<<
   >>>>                 xvl_phantomd_cb()			<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



Node *InputNode     = NULL,
     *OutputNode    = NULL;

static void select_glyphs();


/************************************************************
*
* Routine Name:  xvl_destroy_cb
*
*      Purpose:  The following callback is used to destroy
*		 a glyph.  The callback calls xvl_destroy_glyph()
*		 to actually destroy the glyph.
*
*        Input:  widget	    -   the destroy widget 
*		 clientData -   the glyph to be destroyed
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_destroy_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph	  *glyph = (Glyph *) clientData;
	Workspace *workspace;


	workspace = glyph->workspace;
	if (workspace->undo_workspace == NULL)
	{
	   xvl_destroy_glyph(glyph, True);
	}
	else
	{
	   xvl_clear_workspace(workspace->undo_workspace);
	   xvl_cut_connections(glyph, NULL);
	   xvl_move_glyph(glyph, workspace->undo_workspace, True);
	   xvl_update_undo(workspace);
	}
}



/************************************************************
*
* Routine Name:  xvl_form_cb
*
*      Purpose:  The following callback is used to unglyph a
*		 glyph.  This takes a glyph an unmaps the glyph
*		 and maps the associated subform or workspace,
*		 depending if the glyph is a macro or not.  The
*		 callback actually calls xvl_map_glyph() to actually
*		 unmap the the glyph and map the form.
*
*        Input:  widget	    -   the form widget 
*		 clientData -   the glyph to be unmapped
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_form_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph	*glyph = (Glyph *) clientData;

	Workspace    *macro;
	xvf_sub_form *subform;


	xvl_unmap_glyph(glyph);
	if (glyph->type == CONTROL || glyph->type == GLYPH ||
	    glyph->type == COMMENT || glyph->type == COMMAND)
	{
	   subform = glyph->val.subform;
	   subform->glyph_state = !subform->glyph_state;
	}
	else if (glyph->type == PROCEDURE)
	{
	   macro = glyph->val.macro;
	   macro->menuform->glyph_state = !macro->menuform->glyph_state;
	}
	xvl_map_glyph(glyph);
	xvl_update_mapped(glyph);
}



/************************************************************
*
* Routine Name:  xvl_run_cb
*
*      Purpose:  The following callback is used to run and stop
*		 a glyph.  If the glyph to be run is already running
*		 then we stop it, otherwise we run it.  If the
*		 workspace has demand scheduling turned on then
*		 we will call the demand scheduler to run the glyph.
*
*        Input:  widget	    -   the input widget 
*		 clientData -   the input connection to be connected
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_run_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph	  *glyph = (Glyph *) clientData;
	Workspace *workspace, *attributes, *macro;


	/*
	 *  If the widget is the run widget then run the glyph
	 *  otherwise abort the currently running glyph.
	 */
	workspace = glyph->workspace;
	attributes = xvl_get_attributes(workspace);

	if (widget == glyph->run)
	{
	   /*
	    *  When running a glyph we need to check if the demand
	    *  driven for this workspace is true.  If so then we schedule
	    *  the glyph by calling demand scheduler, otherwise we call
	    *  the dispatch glyph routine to run the glyph directly.
	    */
	   if (attributes->demand_driven)
	      xvl_demand_scheduler(glyph);
	   else
	   {
	      if (glyph->modified == False)
	      {
	         glyph->modified = True;
		 xvl_update_modified(glyph);
	      }
	      (void) xvl_dispatch_glyph(glyph);
	   }
	}
	else if (widget == glyph->stop)
	{
	   if (glyph->type == PROCEDURE)
	   {
	      macro = glyph->val.macro;
	      xvl_abort_autorun(macro);
	   }
	   else
	      xvl_stop_glyph(glyph);

	   if (workspace->autorun == True)
	      xvl_autorun_dispatcher(workspace);
	}
}



/************************************************************
*
* Routine Name:  xvl_input_cb
*
*      Purpose:  The following callback is used to specify an
*		 input glyph.  If there is already an output 
*		 node then make a connection and then set both
*		 InputNode & OutputNode to NULL which is used to
*		 indicate unspecified connection.
*
*        Input:  widget	    -   the input widget 
*		 clientData -   the input connection to be connected
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_input_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Node  *inode = (Node *) clientData;

	Glyph *glyph;
	Workspace *workspace, *attributes;


	InputNode = inode;
	if (OutputNode != NULL)
	{
	   xvl_build_connection(InputNode, OutputNode);

	   glyph = InputNode->glyph;
	   workspace = glyph->workspace;
	   if (xvl_check_if_glyphlist(glyph, workspace->running) == True)
	   {
	      xvl_stop_glyph(glyph);

	      attributes = xvl_get_attributes(workspace);
	      if (attributes->demand_driven == True)
		 xvl_demand_scheduler(glyph);
	      else
		 (void) xvl_dispatch_glyph(glyph);
	   }
	   else
	   {
	      glyph->modified = True;
	      xvl_update_modified(glyph);
	   }
	   InputNode = OutputNode = NULL;
	}
}



/************************************************************
*
* Routine Name:  xvl_output_cb
*
*      Purpose:  The following callback is used to specify an
*		 output glyph.  If there is already an input
*		 node then make a connection and then set both
*		 InputNode & OutputNode to NULL which is used to
*		 indicate unspecified connection.
*
*        Input:  widget	    -   the output widget 
*		 clientData -   the output connection to be connected
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_output_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Node  *onode = (Node *) clientData;

	Glyph *glyph;
	Workspace *workspace, *attributes;


	OutputNode = onode;
	if (InputNode != NULL)
	{
	   xvl_build_connection(InputNode, OutputNode);

	   glyph = InputNode->glyph;
	   workspace = glyph->workspace;
	   if (xvl_check_if_glyphlist(glyph, workspace->running) == True)
	   {
	      xvl_stop_glyph(glyph);

	      attributes = xvl_get_attributes(workspace);
	      if (attributes->demand_driven == True)
		 xvl_demand_scheduler(glyph);
	      else
		 (void) xvl_dispatch_glyph(glyph);
	   }
	   else
	   {
	      glyph->modified = True;
	      xvl_update_modified(glyph);
	   }
	   InputNode = OutputNode = NULL;
	}
}



/************************************************************
*
* Routine Name:  xvl_error_cb
*
*      Purpose:  The following callback is used to look at
*		 the errorfile associated with the glyph.
*
*        Input:  widget	    -   the error widget 
*		 clientData -   the glyph associated with the error
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_error_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph	*glyph = (Glyph *) clientData;

	char  temp[50];
	Glyph *parent;


	/*
	 *  Remove the error so that no can call us multiple times
	 *  with the same error.
	 */
	xvl_unblink_glyph(glyph);
	XtUnmapWidget(widget);

	/*
	 *  If the glyph is inside a macro then reset all the parents
	 */
	parent = glyph->workspace->parent;
	while (parent != NULL)
	{
	   xvl_unblink_glyph(parent);
	   XtUnmapWidget(parent->error);
	   parent = parent->workspace->parent;
	}

	if (glyph->errorfile != NULL)
	{

	   /*
	    *  Call xvl_help() to display the error file.  After displaying the
	    *  error message then free the associated memory with the error
	    *  file.
	    */
	   (void) sprintf(temp,"Error found for glyph '%s'", glyph->label_str);
	   xvl_help(glyph->errorfile, temp, True);
	   free(glyph->errorfile);
	   glyph->errorfile = NULL;

	}
	else if (glyph->type == PROCEDURE)
	{
	   xvf_error_wait("An error has occurred while running this macro. \
To see more information about this error please open this glyph.",
			  "xvl_error_cb", NULL);
	}
	else
	{
	   xvf_error_wait("Opps! Unable to find error file originally \
associated with this error message.", "xvl_error_cb", NULL);
	}
}



/************************************************************
*
* Routine Name:  xvl_reset_cb
*
*      Purpose:  The following callback is used to reset a
*		 control glyph.  The user activates the call-
*		 back by depressing the "reset" button on the
*		 control glyph.
*
*        Input:  widget	    -   the reset widget 
*		 clientData -   the glyph to be reset
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_reset_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph	*glyph = (Glyph *) clientData;


	xvl_reset_control(glyph);
}



/************************************************************
*
* Routine Name:  xvl_clipboard_cb
*
*      Purpose:  The following callback is used to to open and
*		 close the clipboard workspace.  The user activates
*		 the callback by depressing the clipboard glyph.
*
*        Input:  widget	    -   the reset widget 
*		 clientData -   the glyph to be reset
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/

void xvl_clipboard_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	xvf_form  *form;

	form = clipboard->menuform;
	xvf_unmap_form(form);
	form->glyph_state = !form->glyph_state;
	xvf_map_form(form);
	xvl_update_all_clipboard(main_workspace);
}



/************************************************************
*
* Routine Name:  xvl_exposure_cb
*
*      Purpose:  The following callback is used to refresh
*		 a workspace.  This routine is really an event handler
*		 that gets called when the workspace get an exposure
*		 event.  The routine then refreshes the connections.
*
*        Input:  widget	    -   the destroy widget 
*		 clientData -   the glyph to be destroyed
*		 event      -   the exposure event
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_exposure_cb(widget, clientData, event)

Widget	widget;
caddr_t	clientData;
XEvent	*event;
{
	Workspace   *workspace = (Workspace *) clientData;

	XEvent  next;
	Display	*display = XtDisplay(workspace->draw);
	Window	window = XtWindow(workspace->draw);


	if (event->xexpose.window != XtWindow(workspace->draw))
	   return;

	/*
	 *  eat all expose events on window drawing window from the event 
	 *  queue to avoid multiple redraws
	 */
	while (XCheckWindowEvent(display, window, ExposureMask, &next))
				;
	xvl_redraw_connections(workspace);
}



/************************************************************
*
* Routine Name:  xvl_connection_cb
*
*      Purpose:  The following callback is used to see if the user
*		 is trying to select a connection by performing a button
*		 press on the draw workspace.  This routine is also used
*		 to see if the user wants to clear the current build of
*		 connections.  If the user clicks on an InputNode then
*		 decides they wish to cancel they can do so by clicking
*		 in the root window.  The procedure is check to first
*		 if the user is selecting a connection then we clear
*		 any current connection.
*
*		 To figure out which connection was selected we
*		 race thru the connection list looking to see if
*		 the x & y position is near any of the currently
*		  displayed connections.
*
*        Input:  widget	    -   the destroy widget 
*		 clientData -   the glyph to be destroyed
*		 event      -   the button press event
*		 dispatch   -   the button press event
*
*
*   Written By: Mark Young
*
*  Modified By: Carla William (6-9-90) to SAVE connection file
*
*************************************************************/


void xvl_connection_cb(widget, clientData, event, dispatch)

Widget	widget;
caddr_t	clientData;
XEvent	*event;
Boolean *dispatch;
{
	Workspace   *workspace = (Workspace *) clientData;

	Glyph	   *glyph;
	GlyphList  *glyphlist;
	NodeList   *links;
	Node	   *onode, *inode;
	int	   xpos, ypos, found = False;


	if (event->type != ButtonPress)
	   return;
	else
	{
	   xpos = event->xbutton.x;
	   ypos = event->xbutton.y;
	}

	glyphlist = workspace->glyphs;
	while (glyphlist != NULL && !found)
	{
	   glyph = glyphlist->glyph;

	   /*
	    *  Search the output list for the connection.
	    */
	   links = glyph->output_list;
	   while (links != NULL && !found)
	   {
	      onode = links->node;
	      if ((inode = xvl_locate_connection(onode, xpos, ypos)) != NULL)
	         found = True;

	      links = links->next;
	   }
	   glyphlist = glyphlist->next;
	}

	if (found)
	{
	   connection_cb(inode, onode);
	   *dispatch = False;
	}
	else
	{
	   /*
	    *  Since the user clicked on the root window they must mean to
	    *  clear any current connections.
	    */
	   InputNode = OutputNode = NULL;
	}
}

connection_cb(inode, onode)

Node *inode, *onode;
{
	Glyph     *glyph;
	NodeList  *links;
	Workspace *workspace;
	char      *machine, *filename, error[MaxLength], transport[MaxLength],
	          temp[MaxLength];

	char     *selection;
	XawListReturnStruct *choice;
	char     *label = "Choose action or transport for the connection";
	char     *prompt = "Actions and Transports:";
	static   char *choices[] = {
				     "Delete Connection",
				     "Save Data to File",
				     "",
				   };


	/*
	 *  Check to see if the user wants to save or delete the connection.
	 */
	xvl_invert_glyph(onode->glyph, True);
	xvl_invert_glyph(inode->glyph, True);

	if (onode->temp_file == True)
	{
	   workspace = onode->glyph->workspace;
	   selection = xvl_transport_menu(workspace, label, onode->filename,
				choices, 3);
	}
	else
	{
	   choice = xvf_run_list_wait(choices, 2, 1, onode->filename, label, 0,
				False);
	   if (choice != NULL)
	      selection = choice->string;
	   else
	      selection = NULL;
	}

	if (selection != NULL)
	{
	   if (strcmp(selection, "Save Data to File") == 0)
	   {
	      (void) xvl_save_file(onode->filename);
	   }
	   else if (strcmp(selection, "Delete Connection") == 0)
	   {
	      xvl_delete_connection(inode, onode);
	   }
	   else if (onode->temp_file == True)
	   {
	      if (VStrcmp(onode->transport, selection) != 0)
	      {
	         if (sscanf(selection, "%s", transport) == 1)
		 {
		    glyph = onode->glyph;
		    machine = xvl_get_machname(glyph, temp);

		    filename = xvl_tempnam(glyph->label_str,transport,machine);
		    if (filename != NULL)
	            {
		       if (onode->filename != NULL)
		       {
		          xvl_move(onode->filename, filename);
			  free(onode->filename);
		       }

		       /*
			*   Update the new temporary filename...
			*/
		       onode->filename = filename;
	               onode->transport = transport;
	               xvl_update_filename(onode);

		       /*
			*  Update the output node's link list or connection
			*  list to the corresponding inputs.
			*/
		       links = onode->links;
		       while (links != NULL)
		       {
			  if (links->node->filename != NULL)
			     free(links->node->filename);

			  links->node->filename = xvf_strcpy(filename);
			  xvl_update_filename(links->node);

			  links = links->next;
		       }
	            }
		    else
		    {
		       sprintf(error, "Error!  Failed to create a temporary \
file using the following transport '%s'", selection);
		       xvf_error_wait(error, "connection_cb", NULL);
		    }
		 }
	      }
	   }
	}
	xvl_invert_glyph(onode->glyph, False);
	xvl_invert_glyph(inode->glyph, False);
}



/************************************************************
*
* Routine Name:  xvl_select_cb
*
*      Purpose:  The following callback is used to select a group of glyphs
*		 to be added onto a workspaces selected list.
*
*        Input:  widget	    -   the destroy widget 
*		 clientData -   the glyph to be destroyed
*		 event      -   the button press event
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_select_cb(widget, clientData, event)

Widget	widget;
caddr_t	clientData;
XEvent	*event;
{
	Workspace   *workspace = (Workspace *) clientData;
	Window	    window = XtWindow(widget);
	Display	    *display = XtDisplay(widget);

	int x, y, w, h;
	static  int active;
	static  Position x1 = 0, x2 = 0,
		         y1 = 0, y2 = 0;


	/*
	 *  Make sure that the user performs a button press first before
	 *  starting the rubberband of an area.
	 */
	if (active == False && event->type != ButtonPress)
	   return;

	x = x2;
	y = y2;
	if (event->type == MotionNotify)
	{

	   if (xvl_check_motion_event(widget, &x2, &y2))
	   {
	      x2 = x; y2 = y;
	      return;
	   }
	   if (event->xmotion.is_hint == False) 
	   {
	      x2 = event->xmotion.x;
	      y2 = event->xmotion.y;
	   }
	}
	else if (event->type == ButtonPress)
	{
	   x1 = event->xbutton.x;
	   y1 = event->xbutton.y;
	   x2 = x1 + 5;
	   y2 = y1 + 5;
	   active = True;
	}
	else if (event->type == ButtonRelease)
	{
	   active = False;
	   x2 = event->xbutton.x;
	   y2 = event->xbutton.y;
	   select_glyphs(workspace,MIN(x1,x2),MAX(x1,x2),MIN(y1,y2),MAX(y1,y2));
	}

	if (event->type != ButtonPress)
	{
	   /*
	    *  Erase the old rectangle.
	    */
	   w = ABS(x1 - x); h = ABS(y1 - y);
	   x = MIN(x1, x); y = MIN(y1, y);
	   XDrawRectangle(display, window, workspace->gc_xor, x, y, w, h);
	}

	if (event->type != ButtonRelease)
	{
	   /*
	    *  Draw the new rectangle.
	    */
	   w = ABS(x1 - x2); h = ABS(y1 - y2);
	   x = MIN(x1, x2); y = MIN(y1, y2);
	   XDrawRectangle(display, window, workspace->gc_xor, x, y, w, h);
	}
}

static void select_glyphs(workspace, xmin, xmax, ymin, ymax)

Workspace *workspace;
Position  xmin, xmax, ymin, ymax;
{
	Glyph	  *glyph;
	GlyphList *glyphlist;
	Position  xpos, ypos;
	Boolean	  selected, clear_selected = False;
	Workspace *attributes;


	attributes = xvl_get_attributes(workspace);
	if (ABS(xmax - xmin) > attributes->grid_size &&
	    ABS(ymax - ymin) > attributes->grid_size)
	{
	   xvl_unselect_glyphs(workspace);
	   clear_selected = True;
	}

	glyphlist = workspace->glyphs;
	while (glyphlist != NULL)
	{
	   selected = False;
	   glyph = glyphlist->glyph;
	   if (glyph->xpos >= xmin && glyph->xpos <= xmax &&
	       glyph->ypos >= ymin && glyph->ypos <= ymax)
	   {
	      selected = True;
	   }
	   else
	   {
	      xpos = glyph->xpos + glyph->width;
	      ypos = glyph->ypos + glyph->height;
	      if (xpos >= xmin && xpos <= xmax && ypos >= ymin && ypos <= ymax)
	      {
		 selected = True;
	      }
	   }

	   /*
	    *  Should we add this glyph to the selected list
	    */
	   if (selected == True)
	   {
	      /*
	       *  If the glyph found is the first to be added the workspace's
	       *  selected list then unselect the original selected glyphs and
	       *  add the new glyph to the list.
	       */
	      if (clear_selected == False)
	      {
		 xvl_unselect_glyphs(workspace);
		 clear_selected = True;
	      }
	      workspace->selected = xvl_add_to_glyphlist(glyph,
			workspace->selected);
	      xvl_update_selected(glyph);
	   }
	   glyphlist = glyphlist->next;
	}
}



/************************************************************
*
* Routine Name:  xvl_control_cb
*
*      Purpose:  The following callback is used to reset a
*		 control glyph.  The user activates the call-
*		 back by depressing the "reset" button on the
*		 control glyph.
*
*        Input:  widget	    -   the reset widget 
*		 clientData -   the glyph to be reset
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_control_cb(pid, status, clientData, errorfile)

int	pid;
vstatus status;
caddr_t clientData;
char	*errorfile;
{
	Node *node = (Node *) clientData;

	FILE	  *file;
	Glyph	  *parent, *glyph;
	Workspace *workspace;


	/*
	 *  Now that the glyph is done we need to restore the
	 *  glyph to it's normal state.  Restore glyph also
	 *  will take the glyph off the frontier list if autorun
	 *  is being used.  But we need to make sure the pid
	 *  matches the glyph's current process id, since if the
	 *  run type is MULTIRUN then there may be several processes
	 *  running per glyph.
	 */
	glyph = node->glyph;
	if (WIFEXITED(status) != 0 && WEXITSTATUS(status) == 0 &&
			pid == glyph->pid)
	{
	   xvl_restore_glyph(glyph);
	   xvl_schedule_links(glyph, node);

	   if (glyph->workspace->autorun == True)
	      xvl_autorun_dispatcher(glyph->workspace);
	}
	else if (pid == glyph->pid)
	{
	   xvl_restore_glyph(glyph);
	   glyph->modified = True;
	   xvl_update_modified(glyph);

	   workspace = glyph->workspace;
	   if (workspace->autorun == True)
	      xvl_autorun_dispatcher(workspace);

	   if (errorfile == NULL)
	   {
	      errorfile = xvl_tempnam("errorfile.XXXXX", NULL, NULL);
	      if (!(file = fopen(errorfile,"w")))
	      {
	         free(errorfile);
		 errorfile = NULL;
	      }
	      else
	      {
		 fprintf(file,
"Error!  The following routine has terminated abnormally but no error\n\
message was found.  The following information can be determined\n\
about the terminating\n process:\n\n\t Routine Name: (%s)\n\t Process \
id (%d)\n\t Exit Status (%d)\n\t Termination Signal: ", glyph->label_str,
pid, WEXITSTATUS(status));

		 xvl_print_signal(file, WTERMSIG(status));
		 fprintf(file,"\n\n\n");
		 fclose(file);
	      }
	   }
	}

	/*
	 *  If the errorfile is not null then blink the glyph to inform
	 *  the user an error has occured.  We also need to check to see
	 *  if this glyph is within a macro, in which case we will need
	 *  to make the macro glyph blink also.
	 */
	if (errorfile != NULL)
	{
	   if (glyph->errorfile != NULL)
	   {
	      unlink(glyph->errorfile);
	      free(glyph->errorfile);
	      glyph->errorfile = NULL;
	   }
	   glyph->errorfile = errorfile;
	   XtMapWidget(glyph->error);
	   xvl_blink_glyph(glyph);

	   parent = glyph->workspace->parent;
	   while (parent != NULL)
	   {
	      xvl_blink_glyph(parent);
	      XtMapWidget(parent->error);
	      parent = parent->workspace->parent;
	   }
	}
}



/************************************************************
*
* Routine Name:  xvl_move_cb
*
*      Purpose:  The following callback is used to unlink the
*		 file once it's been moved over.
*
*        Input:  pid     -   the reset widget
*                status  -   the glyph to be reset
*                clientData - filename to be unlinked
*                errorfile - In case an error occurred
*
*
*   Written By: Mark Young
*
*************************************************************/
 
 
void xvl_move_cb(pid, status, clientData, errorfile)
 
int     pid;
vstatus status;
caddr_t clientData;
char    *errorfile;
{
	if (errorfile != NULL) kunlink(errorfile);
        kunlink((char *) clientData);
}



/************************************************************
*
* Routine Name:  xvl_dispatch_cb
*
*      Purpose:  This routine is used as the callback in order
*		 to change the color of the glyph indicating that
*		 processing has finished.
*
*        Input:  pid    - the pid id of the completed child.
*		 status - the child's status
*		 clientData - a piece of data associated with
*			      the child.  In this case the glyph.
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_dispatch_cb(pid, status, clientData, errorfile)

int	pid;
vstatus status;
caddr_t clientData;
char	*errorfile;
{
	Glyph *glyph = (Glyph *) clientData;

	FILE	  *file;
	Glyph	  *parent;
	Workspace *workspace;


	/*
	 *  Now that the glyph is done we need to restore the
	 *  glyph to it's normal state.  Restore glyph also
	 *  will take the glyph off the frontier list if autorun
	 *  is being used.  But we need to make sure the pid
	 *  matches the glyph's current process id, since if the
	 *  run type is MULTIRUN then there may be several processes
	 *  running per glyph.
	 */
	if (WIFEXITED(status) != 0 && WEXITSTATUS(status) == 0 &&
			pid == glyph->pid)
	{
	   xvl_restore_glyph(glyph);
	   xvl_schedule_glyph(glyph);

	   if (glyph->workspace->autorun == True)
	      xvl_autorun_dispatcher(glyph->workspace);
	}
	else if (pid == glyph->pid)
	{
	   xvl_restore_glyph(glyph);
	   glyph->modified = True;
	   xvl_update_modified(glyph);

	   workspace = glyph->workspace;
	   if (workspace->autorun == True)
	      xvl_autorun_dispatcher(workspace);

	   if (errorfile == NULL)
	   {
	      errorfile = xvl_tempnam("errorfile.XXXXX", NULL, NULL);
	      if (!(file = fopen(errorfile,"w")))
	      {
	         free(errorfile);
		 errorfile = NULL;
	      }
	      else
	      {
		 fprintf(file,
"Error!  The following routine has terminated abnormally but no error\n\
message was found.  The following information can be determined\n\
about the terminating process:\n\n\t Routine Name: (%s)\n\t Process \
id (%d)\n\t Exit Status (%d)\n\t Termination Signal: ", glyph->label_str,
pid, WEXITSTATUS(status));

		 xvl_print_signal(file, WTERMSIG(status));
		 fprintf(file,"\n\n\n");
		 fclose(file);
	      }
	   }
	}

	/*
	 *  If the errorfile is not null then blink the glyph to inform
	 *  the user an error has occured.  We also need to check to see
	 *  if this glyph is within a macro, in which case we will need
	 *  to make the macro glyph blink also.
	 */
	if (errorfile != NULL)
	{
	   if (glyph->errorfile != NULL)
	   {
	      unlink(glyph->errorfile);
	      free(glyph->errorfile);
	      glyph->errorfile = NULL;
	   }
	   glyph->errorfile = errorfile;
	   XtMapWidget(glyph->error);
	   xvl_blink_glyph(glyph);

	   parent = glyph->workspace->parent;
	   while (parent != NULL)
	   {
	      xvl_blink_glyph(parent);
	      XtMapWidget(parent->error);
	      parent = parent->workspace->parent;
	   }
	}
}



/************************************************************
*
* Routine Name:  xvl_distributed_cb
*
*      Purpose:  The following callback is used to 
*
*        Input:  widget	    -   the input widget 
*		 clientData -   the input connection to be connected
*		 callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/


void xvl_distributed_cb(widget, clientData, callData)

Widget	widget;
caddr_t	clientData, callData;
{
	Glyph *glyph = (Glyph *) clientData;

	GlyphList *glyphlist;


	glyphlist = xvl_add_to_glyphlist(glyph, NULL);
	xvl_change_machine(glyphlist);
	xvl_destroy_glyphlist(glyphlist);
}



/************************************************************
*
* Routine Name:  xvl_phantomd_cb
*
*      Purpose:  The following callback is used to set the
*                khoros_phantomd_pid to 0.  This global is used
*                by the system to indicate that the local
*		 phantom daemon is running.
*
*        Input:  widget     -   the reset widget
*                clientData -   the glyph to be reset
*                callData   -   Not Used
*
*
*   Written By: Mark Young
*
*************************************************************/
 
 
void xvl_phantomd_cb(pid, status, clientData, errorfile)
 
int     pid;
vstatus status;
caddr_t clientData;
char    *errorfile;
{
	if (errorfile != NULL) kunlink(errorfile);
        khoros_phantomd_pid = 0;
}
