 /*
  * Khoros: $Id: glyph.c,v 1.5 1992/03/20 22:43:07 dkhoros Exp $
  */

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

 /*
  * $Log: glyph.c,v $
 * Revision 1.5  1992/03/20  22:43:07  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 void place_glyph();


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>								<<<<
   >>>>	    file name:  glyph.c					<<<<
   >>>>								<<<<
   >>>>   description:						<<<<
   >>>>								<<<<
   >>>>      routines:  xvl_destroy_glyph()			<<<<
   >>>>                 xvl_map_glyph()				<<<<
   >>>>                 xvl_unmap_glyph()			<<<<
   >>>>			xvl_copy_glyph()			<<<<
   >>>>			xvl_move_glyph()			<<<<
   >>>>								<<<<
   >>>>                 xvl_snap_glyph()			<<<<
   >>>>                 xvl_place_glyph()			<<<<
   >>>>                 xvl_manage_glyph()			<<<<
   >>>>                 xvl_translate_glyph()			<<<<
   >>>>								<<<<
   >>>>                 xvl_update_glyph()			<<<<
   >>>>                 xvl_restore_glyph()			<<<<
   >>>>                 xvl_stop_glyph()			<<<<
   >>>>                 xvl_invert_glyph()			<<<<
   >>>>								<<<<
   >>>>                 xvl_query_glyph()			<<<<
   >>>>                 xvl_error_glyphs()			<<<<
   >>>>								<<<<
   >>>> modifications:						<<<<
   >>>>    1-Sep-91 Scott Wilson - Changed stop glyph signal	<<<<
   >>>>             from SIGKILL to SIGINT in xvl_stop_glyph.	<<<<
   >>>>             This lets the kill be blocked by each 	<<<<
   >>>>             process, since they may have set sigblock() <<<<
   >>>>								<<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */



/************************************************************
*
* Routine Name: xvl_destroy_glyph
*
*      Purpose: This routine is used to destroy a glyph.  If
*		the glyph is destroyed according to the type
*		of glyph.
*
*        Input: glyph  -  glyph to be destroyed.
*		adjust -  whether to adjust the glyph form
*
*       Output: none
*
*   WRITTEN BY: Mark Young
*
*************************************************************/


xvl_destroy_glyph(glyph, adjust)

Glyph	  *glyph;
int	  adjust;
{
	char		temp[MaxLength];
	Workspace	*workspace, *attributes, *macro;


	/*
	 *  Need to destroy the temporary file and also go thru the glyph's
	 *  output list and destroy the glyph's connections.
	 */
	xvl_stop_glyph(glyph);
	xvl_unblink_glyph(glyph);
	xvl_destroy_glyph_nodelist(glyph);
	xvl_clear_label(glyph, xvl_get_machlabel(glyph, temp));

	workspace = glyph->workspace;
	workspace->glyphs = xvl_delete_from_glyphlist(glyph, workspace->glyphs);
	workspace->selected = xvl_delete_from_glyphlist(glyph,
			workspace->selected);

	/*
	 *  Destroy the associated glyph's information.
	 */
	if (glyph->type == PROCEDURE)
	{
	   macro = glyph->val.macro;
	   if (macro != NULL)
	      xvl_destroy_workspace(macro);
	}
	else if (glyph->type == IMAGES)
	{
	   free(glyph->val.imagelist);
	}
	else if ((glyph->type == GLYPH || glyph->type == CONTROL ||
		  glyph->type == COMMENT || glyph->type == COMMAND) &&
		 (adjust == True))
	{
	   xvf_delete_subform(workspace->glyphform, glyph->val.subform);
	   xvf_destroy_subform(glyph->val.subform); 
	   xvf_adjust_form_indices(workspace->glyphform); 
	}


	/*
	 *  Free the label and the glyph itself.
	 */
	if (glyph->errorfile != NULL)
	{
	   unlink(glyph->errorfile);
	   free(glyph->errorfile);
	}

	/*
	 *  If leave_glyph is false that means we are suppose to take care
	 *  of mapping, unmapping & destroy the glyph instead of the forms
	 *  library.
	 */
	attributes = xvl_get_attributes(workspace);
	if (attributes->leave_glyph == True)
	{
	   XtDestroyWidget(glyph->toplevel);
	}
	free(glyph->label_str);
	free(glyph);
}



/************************************************************
*
* Routine Name: xvl_map_glyph
*
*      Purpose: This routine is used to map a glyph.  If the
*		glyph is of type PROCEDURE then we call ourselves
*		to map the glyph and thus all it's children.
*		We then unmap the workspace (macro) toplevel.
*
*        Input: glyph  -  glyph to be mapped.
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

xvl_map_glyph(glyph)

Glyph *glyph;
{
	Workspace	*macro;


	if (glyph->type == PROCEDURE)
	{
	   macro = glyph->val.macro;
	   if (macro != NULL)
	   {
	      if (!macro->attach_canvas)
	         XtMapWidget(macro->toplevel);

	      if (macro->menuform->glyph_state)
	      {
	         xvl_position_glyph(glyph);
	         XtMapWidget(glyph->toplevel);
	      }
	      xvf_map_form(macro->menuform);
	   }
	}
	else if (glyph->type == GLYPH || glyph->type == CONTROL ||
		 glyph->type == COMMENT || glyph->type == COMMAND)
	{
	   if (glyph->val.subform->glyph_state)
	   {
	      xvl_position_glyph(glyph);
	      XtMapWidget(glyph->toplevel);
	   }
	   xvf_map_subform(glyph->workspace->glyphform, glyph->val.subform);
	}
	else if (glyph->type == IMAGES)
	{
	   /* not sure what to do at this time */
	}
}



/************************************************************
*
* Routine Name: xvl_unmap_glyph
*
*      Purpose: This routine is used to unmap a glyph.  If the
*		glyph is of type PROCEDURE then we unmap the glyph
*	        and all it's children.  Otherwise we just unmap
*		the glyph.
*
*        Input: glyph  -  glyph to be mapped.
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

xvl_unmap_glyph(glyph)

Glyph *glyph;
{
	Workspace	*macro;


	if (glyph->type == PROCEDURE)
	{
	   macro = glyph->val.macro;
	   if (macro != NULL)
	   {
	      if (!macro->attach_canvas)
	         XtUnmapWidget(macro->toplevel);

	      xvf_unmap_form(macro->menuform);
	   }
	}
	else if (glyph->type == GLYPH || glyph->type == CONTROL ||
		 glyph->type == COMMENT || glyph->type == COMMAND)
	{
	   xvf_unmap_subform(glyph->val.subform);
	}
	else if (glyph->type == IMAGES)
	{
	   /* not sure what to do at this time */
	}
}



/************************************************************
*
* Routine Name: xvl_copy_glyph
*
*      Purpose: This routine is used to copy a glyph from it's
*		current workspace to the specified workspace.
*
*        Input: glyph     -  glyph to be copied.
*		new       -  workspace to receive the copied glyph
*	        adjust	  -  whether to adjust the workspace forms
*			     after modifying the receiving glyph form.
*		xoffset   -  the x amount to offset the glyph
*		yoffset   -  the y amount to offset the glyph
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

Glyph *xvl_copy_glyph(glyph, new, adjust, xoffset, yoffset)

Glyph	  *glyph;
Workspace *new;
int	  adjust;
int	  xoffset, yoffset;
{
	Glyph		*new_glyph, *temp;
	GlyphList	*glyphlist;
	Workspace	*old, *procedure;

	xvf_form	*form;
	int		xpos, ypos;
	char		error[MaxLength];
	xvf_sub_form	*subform, *new_subform;


	old = glyph->workspace;
	xpos = glyph->xpos+xoffset;
	ypos = glyph->ypos+yoffset;
	if (glyph->type != PROCEDURE)
	{
	   /*
	    *  Delete the subform from the old workspace.
	    */
	   subform = glyph->val.subform;

	   /*
	    *  Copy and add the subform to the new workspace.
	    */
	   form = new->glyphform;
	   if ((new_subform = xvf_copy_and_add_subform(form, subform)) == NULL)
	   {
	      xvf_adjust_form_indices(form);
	      return(NULL);
	   }
	   if (adjust) xvf_adjust_form_indices(form);

	   new_glyph = xvl_build_glyph(new, glyph->type, (char *) new_subform,
			subform->glyph_state, xpos, ypos);
	   if (new_glyph == NULL)
	      return(NULL);
	}
	else
	{
	   if ((new_glyph = xvl_build_glyph(new, PROCEDURE, glyph->label_str,
			True, xpos, ypos)) == NULL)
	   {
	      sprintf(error,"Error!  Unable to create procedure glyph '%s' in \
which to copy the glyphs.  Therefore no sub-procedure will be copied.",
			glyph->label_str);
	      xvf_error_wait(error, "xvl_copy_glyph", NULL);
	      return(NULL);
	   }


	   procedure = new_glyph->val.macro;
	   glyphlist = glyph->val.macro->glyphs;
	   while (glyphlist != NULL)
	   {
	      temp = glyphlist->glyph;
	      xvl_copy_glyph(temp, procedure, False);
	      glyphlist = glyphlist->next;
	   }
	   form = procedure->glyphform;
	   xvf_adjust_form_indices(form);
	}

	/*
	 *  Check to see if the old glyph is currently selected, if so then
	 *  add the newly created glyph to the new workspace's selected
	 *  list.
	 */
	if (xvl_check_if_glyphlist(glyph, old->selected))
	{
	   new->selected = xvl_add_to_glyphlist(new_glyph, new->selected);
	   xvl_update_selected(new_glyph);
	}
	return(new_glyph);
}



/************************************************************
*
* Routine Name: xvl_move_glyph
*
*      Purpose: This routine is used to move a glyph from it's
*		current workspace to the specified workspace.
*
*        Input: glyph     -  glyph to be copied.
*		new	  -  workspace to receive the copied glyph
*		adjust    -  whether to adjust the workspace forms
*			     after modifying both glyph forms.
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

xvl_move_glyph(glyph, new, adjust)

Glyph	  *glyph;
Workspace *new;
int	  adjust;
{
	xvf_form	*old_form, *new_form;
	xvf_sub_form	*subform;
	Workspace	*old;


	xvl_clear_connections(glyph);
	old = glyph->workspace;
	if (glyph->type != PROCEDURE)
	{
	   /*
	    *  Move the subform from the old form to the new form.
	    */
	   subform = glyph->val.subform;
	   old_form  = glyph->workspace->glyphform;
	   new_form  = new->glyphform;
	   xvf_move_subform(old_form, new_form, subform);

	   if (adjust)
	   {
	      xvf_adjust_form_indices(new_form);
	      xvf_adjust_form_indices(old_form);
	   }
	}

	if (xvl_check_if_scheduled(glyph) == True)
	{
	   old->frontlist = xvl_delete_from_glyphlist(glyph, old->frontlist);
	   new->frontlist = xvl_add_to_glyphlist(glyph, new->frontlist);
	}

	if (xvl_check_if_glyphlist(glyph, old->selected))
	{
	   old->selected = xvl_delete_from_glyphlist(glyph, old->selected);
	   new->selected = xvl_add_to_glyphlist(glyph, new->selected);
	}

	old->glyphs = xvl_delete_from_glyphlist(glyph, old->glyphs);
	new->glyphs = xvl_add_to_glyphlist(glyph, new->glyphs);
	glyph->workspace = new;

	if (xvl_create_glyph(glyph) == False)
	{
	   if (glyph->type != PROCEDURE)
	   {
	      xvf_adjust_form_indices(new->glyphform);
	      xvf_adjust_form_indices(old->glyphform);
	   }
	   xvl_destroy_glyph(glyph, True);
	}
}



/************************************************************
*
* Routine Name: xvl_update_glyph
*
*      Purpose: This routine is used to update a glyph.
*
*        Input: glyph  -  glyph to be mapped.
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_update_glyph(glyph)

Glyph *glyph;
{
	XtEnum		 exec_type;
	Boolean		 glyph_state;
	Workspace	 *macro;
	int		 modified, type, index;

	Line_Info	 lineinfo;
	xvf_guide_button *guide;
	xvf_sub_form     *subform;
	xvf_selection	 *selection;


	xvl_update_mapped(glyph);
	if (glyph->type == PROCEDURE)
	{
	   /*
	    *  Check if anything got modified
	    */
	   macro = glyph->val.macro;
	   if ((modified = xvl_check_if_modified(macro)) != glyph->modified)
	  {
	      glyph->modified = modified;
	      xvl_update_modified(glyph);
	   }
	   glyph_state = macro->menuform->glyph_state;
	}
	else if (glyph->type == GLYPH || glyph->type == CONTROL ||
		 glyph->type == COMMAND)
	{
	   subform = glyph->val.subform;
           xvf_clear_line_info(&lineinfo);

           /*
	    *  search for title to be in glyph
	    */
           if (!(guide = xvf_search_sel_guide(subform)))
              xvf_gen_parse(subform->db[subform->index], &lineinfo);
           else
              xvf_gen_parse(subform->db[guide->pane->index], &lineinfo);

	   if (glyph->label_str == NULL || lineinfo.variable == NULL)
	      return;

	   /*
	    * Opps.  The user changed panes on us.  Better kill off the
	    * glyph and create a new one.
	    */
	   if (strcmp(glyph->label_str, lineinfo.variable) != 0 &&
	       glyph->type != COMMAND)
	   {
	      free(glyph->label_str);
	      glyph->label_str = xvf_strcpy(lineinfo.variable);

	      if (glyph->type == CONTROL)
		 glyph->exec_type = RERUN;
	      else if (!xvl_find_run_button(subform->db, guide, &exec_type))
	         glyph->exec_type = NORUN;
	      else
	         glyph->exec_type = exec_type;

	      xvl_clear_connections(glyph);
	      xvl_destroy_glyph_nodelist(glyph);
              xvl_update_glyph_nodelist(glyph);
	      xvl_create_glyph(glyph);
	   }
	   else
	   {
	      if (guide == NULL)
	         return;
	      else if (guide->pane == NULL)
	         return;

	      modified = False;
	      selection = guide->pane->sel_list;
	      while (selection != NULL)
	      {
	         if (selection->modified)
		 {
		    modified = True;
		    type = xvf_get_line_type(subform->db[selection->index]);
		    if (type == InputFile || type == OutputFile)
		    {
                       xvl_update_glyph_nodelist(glyph);
		       break;
		    }
		    else if (type == Toggle && selection->toggle_next != NULL)
		    {
		       index = selection->toggle_next->index;
		       type = xvf_get_line_type(subform->db[index]);
		       if (type == InputFile || type == OutputFile)
		       {
                          xvl_update_glyph_nodelist(glyph);
		          break;
		       }
		    }
		    selection->modified = False;
		 }
	         selection = selection->next;
	      }

	      if (modified == True)
	      {
		 glyph->modified = True;
		 glyph->input = NULL;
	         xvl_update_modified(glyph);
	      }
	   }
	   glyph_state = subform->glyph_state;
	}
	else if (glyph->type == COMMENT)
	{
	   subform = glyph->val.subform;
	   glyph_state = subform->glyph_state;
	}
	else return;

	if (glyph->type == COMMENT || glyph->type == COMMAND)
	   xvl_update_glyph_label(glyph);

	if ((glyph->xpos == -1 || glyph->ypos == -1) && glyph_state == True)
	   xvl_map_glyph(glyph);
}



/************************************************************
*
* Routine Name: xvl_snap_glyph
*
*      Purpose: This routine is used to snap a glyph according
*		to the workspace's grid size.
*
*        Input: workspace glyph  -  glyph to be mapped.
*		redraw_connection - redraw the glyph's connections
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_snap_glyph(glyph, redraw_connections)

Glyph *glyph;
int   redraw_connections;
{
	double      dx, dy;
	int         grid_size;
	Position    xpos, ypos;
	Workspace   *workspace, *attributes;


	workspace = glyph->workspace;
	attributes = xvl_get_attributes(workspace);

	if (redraw_connections)
	   xvl_clear_connections(glyph);

	grid_size = attributes->grid_size;
	xpos = glyph->xpos;
	ypos = glyph->ypos;

	if (grid_size > 1)
	{
	   dx = xpos;
	   dy = ypos;
	   xpos = ((int) (dx/grid_size + 0.5)) * grid_size;
	   ypos = ((int) (dy/grid_size + 0.5)) * grid_size;
	}
	xvl_translate_glyph(glyph, xpos, ypos);

	if (redraw_connections)
	   xvl_draw_connections(glyph);
}



/************************************************************
*
* Routine Name:  xvl_restore_glyph
*
*      Purpose:  This routine is used as to restore the glyph
*		 to it's usually state of not being run.  This
*		 means that the glyph's color is not inverted
*		 and that the run button is being displayed
*		 not the stop button.  We do this to indicate
*		 to the user that processing has finished.
*
*        Input:  glyph  - the glyph to be restored
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_restore_glyph(glyph)

Glyph *glyph;
{
	Workspace *workspace;


	if (glyph == NULL)
	   return;

	/*
	 *  Get the workspace that the glyph belongs to.
	 */
	workspace = glyph->workspace;

	if (glyph->exec_type != NORUN)
	{
	   XtUnmapWidget(glyph->stop);
	}


	if (glyph->type == CONTROL || glyph->type == GLYPH ||
	    glyph->type == COMMAND)
	{
	   glyph->pid = 0;
	}

	if (workspace->autorun == True)
	{
	   workspace->frontlist = xvl_delete_from_glyphlist(glyph,
			workspace->frontlist);
	}
	workspace->running = xvl_delete_from_glyphlist(glyph,
			workspace->running);
	xvl_invert_glyph(glyph, False);
}



/************************************************************
*
* Routine Name:  xvl_stop_glyph
*
*      Purpose:  This routine is used to stop a running glyph.
*		 If the glyph is a GLYPH and the glyph is on the
*		 running list then we kill off the glyph and call
*		 xvl_restore_glyph to restore the glyph's appearanace.
*		 If the glyph is a PROCEDURE then we destroy the work
*		 callback.
*
*        Input:  glyph - the glyph to be stopped.
*
*
*   Written By: Mark Young
*
*************************************************************/


xvl_stop_glyph(glyph)

Glyph *glyph;
{
	int   pid = glyph->pid;


	/*
	 *  Make sure that a glyph exist to be stopped.
	 */
	if (glyph == NULL)
	   return;

	/*
	 *  Restore the glyph to it's natural state.
	 */
	xvl_restore_glyph(glyph);

	if (glyph->type == GLYPH && pid != 0)
	{
	   /*
	    *  Remove the signal handler (if one exists), otherwise killing
	    *  the child process may signal the "xvl_dispatch_cb" callback.
	    */
	   xvf_remove_handler(glyph->pid);
#ifdef DEBUG
	   fprintf(stderr,"kill for pid %d = %d\n", pid, kill(pid, SIGINT));
#else
	   (void) kill(pid, SIGINT);
#endif
	   glyph->modified = True;
	   xvl_update_modified(glyph);
	}
}



/************************************************************
*
* Routine Name: xvl_position_glyph
*
*      Purpose: This routine is used to position the glyph in
*		the workspace.
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

#ifndef XtNxoffset
#define XtNxoffset "xoffset"
#endif
#ifndef XtNyoffset
#define XtNyoffset "yoffset"
#endif

xvl_position_glyph(glyph)

Glyph	  *glyph;
{
	unsigned  long mask;
	Widget	  parent;
	Position  xpos, ypos;
	Workspace *workspace, *attributes;


	if (glyph->xpos == -1 || glyph->ypos == -1)
	{
	   workspace = glyph->workspace;
	   attributes = xvl_get_attributes(workspace);
	   parent = workspace->draw;

	   /*
	    *  Place the glyph by either prompting the user or by querying the
	    *  mouse relative to our workspace.
	    */
	   if (attributes->glyph_placement)
	      xvl_place_glyph(parent, glyph, &xpos, &ypos);
	   else
	      xvl_query_position(parent, glyph->toplevel, &xpos, &ypos, True);
	}
	else
	{
	   xpos = glyph->xpos;
	   ypos = glyph->ypos;
	}
	glyph->xpos = xpos;
	glyph->ypos = ypos;
	xvl_snap_glyph(glyph, False);
	xvl_update_mapping(glyph);

	/*
	 *  Add event handler to manage the translating and moving of the
	 *  glyphs.  We use xvl_manage_glyph to both place the glyph initially
	 *  as well as manage the glyph in the future.
	 */
        mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask
		| PointerMotionHintMask;
	XtAddEventHandler(glyph->toplevel, mask, True, xvl_manage_glyph,
			  (char *) glyph);
}



/************************************************************
*
* Routine Name: xvl_place_glyph
*
*      Purpose: This routine is used to initially place the glyph
*		in the workspace.  The user, thru their Xdefaults
*		file, specifies whether they want to interactively
*		place the glyph or not.  If so, as the user moves
*		throughout the workspace a little box will be drawn.
*		When the user depresses the
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/

static int       glyph_placed;
static Position  glyph_xpos, glyph_ypos;
static int	 label_xoffset, label_yoffset, label_size;


xvl_place_glyph(parent, glyph, xpos, ypos)

Widget   parent;
Glyph    *glyph;
Position *xpos, *ypos;
{
	float	  temp;
	char	  *label;
	unsigned  long mask;
	int	  x, y, w, h;
	Workspace *workspace;

	XFontStruct *font;
	Window      window   = XtWindow(parent);
	Display     *display = XtDisplay(parent);


	/*
	 *  Get the default position and font, etc.
	 */
	workspace = glyph->workspace;
	xvl_query_position(parent, glyph->toplevel, &glyph_xpos, &glyph_ypos,
			True);

	x = glyph_xpos;  y = glyph_ypos;
	w = glyph->width; h = glyph->height;

	label = glyph->label_str;
	font  = workspace->font;
	label_size = xvf_strlen(label);
	label_yoffset = glyph->height/2;

	if (font == NULL)
	   label_xoffset = 4;
	else
	{
	   label_xoffset = (w - XTextWidth(font, label, label_size))/2;
	   if (label_xoffset < 0)
	   {
	      temp = ((float) label_size)/(w + ABS(label_xoffset*2));
	      label_size = (int) (w*temp);
	      label_xoffset = (w - label_size/temp)/2;
	   }
	}

	XDrawRectangle(display, window, workspace->gc_xor, x, y, w, h);

	x += label_xoffset;
	y += label_yoffset;
	XDrawString(display, window, workspace->gc_xor, x, y, label,label_size);
	XFlush(XtDisplay(parent));

	/*
	 *  Add the event handler to place the glyph
	 */
        mask = ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
		PointerMotionHintMask;
	XtInsertEventHandler(parent, mask, True, place_glyph, (char *) glyph,
			XtListHead);

	/*
	 *  Place the glyph
	 */
	glyph_placed = False;
	while (glyph_placed == False)
	   xvf_process_event();      /* this does XtNextEvent/DispatchEvent*/

	/*
	 *  Remove the event handler to place the glyph
	 */
	*xpos = glyph_xpos;
	*ypos = glyph_ypos;
	XtRemoveEventHandler(parent, mask, True, place_glyph, (char *) glyph);
}



/************************************************************
*
* Routine Name: place_glyph
*
*      Purpose: This routine is used to place a glyph interactively
*		in the workspace.  As the user moves throughout the
*		workspace a little box will be drawn.  When the user
*		depresses the
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


static void place_glyph(widget, clientData, event, dispatch)

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

	int	    x, y;
	Window      window   = XtWindow(widget);
	Display     *display = XtDisplay(widget);


	x = glyph_xpos;
	y = glyph_ypos;
	*dispatch = False;
	if (event->type == MotionNotify)
	{
	   if (xvl_check_motion_event(widget, &glyph_xpos, &glyph_ypos))
	   {
	      glyph_xpos = x;
	      glyph_ypos = y;
	      return;
	   }
	   if (event->xmotion.is_hint == False) 
	   {
	      glyph_xpos = event->xmotion.x;
	      glyph_ypos = event->xmotion.y;
	   }
	}
	else if (event->type == ButtonPress)
	{
	   glyph_xpos = event->xbutton.x;
	   glyph_ypos = event->xbutton.y;
	}
	else if (event->type == ButtonRelease)
	{
	   glyph_placed = True;
	   glyph_xpos = event->xbutton.x;
	   glyph_ypos = event->xbutton.y;
	}

	/*
	 *  Erase the original rectangle
	 */
	XDrawRectangle(display, window, workspace->gc_xor, x, y,
			(int) glyph->width, (int) glyph->height);
	x += label_xoffset;
	y += label_yoffset;
	XDrawString(display, window, workspace->gc_xor, x, y,
			glyph->label_str, label_size);

	if (glyph_placed == True)
	{
	   XFlush(display);
	   return;
	}

	/*
	 *  Draw current position
	 */
	x = glyph_xpos;
	y = glyph_ypos;
	XDrawRectangle(display, window, workspace->gc_xor, x, y,
			(int) glyph->width, (int) glyph->height);
	x += label_xoffset;
	y += label_yoffset;
	XDrawString(display, window, workspace->gc_xor, x, y,
			glyph->label_str, label_size);
}



/************************************************************
*
* Routine Name: xvl_manage_glyph
*
*      Purpose: This routine is used to manipulate the glyph's
*		in a workspace.  The user is allowed to translate
*		glyphs by dragging them around the screen or
*	        to raise them by performing a ButtonPress/ButtonRelease
*		sequence.
*
*       Output: 
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


void xvl_manage_glyph(widget, clientData, event)

Widget	widget;
caddr_t clientData;
XEvent	*event;
{
	Glyph	  *glyph = (Glyph *) clientData;

	GlyphList *list;
	Workspace *workspace, *attributes;

	static   int selected;
	char	 temp[MaxLength];
	Position xoffset, yoffset, x, y;
	static   Position xpos, ypos, xorig, yorig;
	static	 Position deltax, deltay;
	static   GlyphList *glyphlist;


	workspace = glyph->workspace;
	attributes = xvl_get_attributes(workspace);
	if (event->type == MotionNotify)
	{
	   if (glyphlist == NULL)
	      return;

	   if (xvl_check_motion_event(XtParent(widget), &x, &y))
	      return;
	   else if (event->xmotion.is_hint == False)
	   {
	      XtTranslateCoords(widget, 0, 0, &xoffset, &yoffset);
	      XtTranslateCoords(XtParent(widget), 0, 0, &x, &y);
	      x = (xoffset - x) + event->xmotion.x;
	      y = (yoffset - y) + event->xmotion.y;
	      if (x < 0) x = 0;
	      if (y < 0) y = 0;
	   }

	   xoffset = (x - deltax) - xpos;
	   yoffset = (y - deltay) - ypos;
	   xpos += xoffset;
	   ypos += yoffset;
	   if (ABS(xorig - xpos) > attributes->grid_size/2 ||
	       ABS(yorig - ypos) > attributes->grid_size/2)
	      selected = False;

	   if (attributes->rubberband)
	      xvl_clear_connections(glyph);

	   list = glyphlist;
	   while (list != NULL)
	   {
	      glyph = list->glyph;

	      /*
	       *  Clear the label for the list of glyphs being moved
	       */
	      xvl_clear_label(glyph, xvl_get_machlabel(glyph, temp));

	      glyph->xpos += xoffset;
	      glyph->ypos += yoffset;
	      (void) XtMoveWidget(glyph->toplevel, glyph->xpos, glyph->ypos);

	      list = list->next;
	   }
	}
	else if (event->type == ButtonPress)
	{
	   selected = True;
	   XtTranslateCoords(workspace->draw, 0, 0, &x, &y);
	   deltax = event->xbutton.x;
	   deltay = event->xbutton.y;
	   xpos = (event->xbutton.x_root - deltax - x);
	   ypos = (event->xbutton.y_root - deltay - y);
	   xorig = xpos; yorig = ypos;

	   if (xvl_check_if_glyphlist(glyph, workspace->selected))
	      glyphlist = xvl_copy_glyphlist(workspace->selected);
	   else
	      glyphlist = xvl_add_to_glyphlist(glyph, NULL);

	   list = glyphlist;
	   while (list != NULL)
	   {
	      glyph = list->glyph;
	      xvl_clear_label(glyph, xvl_get_machlabel(glyph, temp));
	      glyph->managed = True;
	      list = list->next;
	   }
	}
	else if (event->type == ButtonRelease)
	{
	   if (selected == True)
	   {
	      if (xvl_check_if_glyphlist(glyph, workspace->selected))
		 workspace->selected = xvl_delete_from_glyphlist(glyph,
				workspace->selected);
	      else
		 workspace->selected = xvl_add_to_glyphlist(glyph,
				workspace->selected);
	      xvl_update_selected(glyph);
	      xoffset = yoffset = 0;
	   }
	   else
	   {
	      XtTranslateCoords(workspace->draw, 0, 0, &x, &y);
	      xoffset = (event->xbutton.x_root - deltax - x) - xpos;
	      yoffset = (event->xbutton.y_root - deltay - y ) - ypos;
	   }

	   list = glyphlist;
	   while (list != NULL)
	   {
	      glyph = list->glyph;

	      glyph->xpos += xoffset;
	      glyph->ypos += yoffset;
	      glyph->managed = False;
	      if (!attributes->rubberband)
	         xvl_snap_glyph(glyph, True);
	      else
	         xvl_snap_glyph(glyph, False);

	      list = list->next;
	   }
	   xvl_destroy_glyphlist(glyphlist);
	   glyphlist = NULL;
	}
}



/************************************************************
*
* Routine Name: xvl_invert_glyph
*
*      Purpose: Changes the appearance of the glyph by changing
*		the foreground and background color.  If invert
*		is False then the we want photo-positive (normal)
*		otherwise we want photo-negative (inverted).
*
*        Input:  glyph  - the glyph to be inverted
*		 invert - whether the glyph is to be inverted or
*			  not.
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_invert_glyph(glyph, invert)

Glyph	 *glyph;
int	 invert;
{
	Pixel	  fg, bg;
	Node	  *node;
	int	  running;
	NodeList  *nodelist;
	Workspace *attributes;

	int	  i;
	Arg	  args[MaxArgs];


	if (wresource.glyph_fg == ~0L || wresource.glyph_bg == ~0L)
	{
	   xvl_init_color(glyph->label, &wresource.glyph_fg,&wresource.glyph_bg,
			NULL);
	}

	if (invert)
	{
	   fg = wresource.glyph_bg;
	   bg = wresource.glyph_fg;
	}
	else
	{
	   fg = wresource.glyph_fg;
	   bg = wresource.glyph_bg;
	}

	i = 0;
	XtSetArg(args[i], XtNforeground, fg);		i++;
	XtSetArg(args[i], XtNbackground, bg);		i++;

	/*
	 *  Set the glyph background but only if it is currently running
	 */
	running = xvl_check_if_glyphlist(glyph, glyph->workspace->running);
	if (running == False || invert == True)
	{
	   XtSetValues(glyph->toplevel, args, i);
	   XtSetValues(glyph->label, args, i);
	   if (glyph->back != glyph->toplevel)
	      XtSetValues(glyph->back, args, i);
	}

	attributes = xvl_get_attributes(glyph->workspace);
	if (attributes->show_dav)
	{
	   nodelist = glyph->input_list;
	   while (nodelist != NULL)
	   {
	      node = nodelist->node;
	      XtSetValues(node->dav_widget, args, i);
	      nodelist = nodelist->next;
	   }

	   nodelist = glyph->output_list;
	   while (nodelist != NULL)
	   {
	      node = nodelist->node;
	      XtSetValues(node->dav_widget, args, i);
	      nodelist = nodelist->next;
	   }
	}

	if (glyph->modified == True && attributes->show_modified == True)
	   xvl_update_modified(glyph);

	XFlush(XtDisplay(glyph->toplevel));
}



/************************************************************
*
* Routine Name: xvl_translate_glyph
*
*      Purpose: This routine is used to translate a glyph to
*		the deisred location.
*
*       Output: None
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_translate_glyph(glyph, xpos, ypos)

Glyph	 *glyph;
Position xpos, ypos;
{
	int	i;
	Arg	args[5];


	glyph->xpos = xpos;
	glyph->ypos = ypos;

	if (xvl_check_if_glyphlist(glyph, glyph->workspace->selected))
	{
	   xpos -= 3;
	   ypos -= 3;
	}
	XtMoveWidget(glyph->toplevel, xpos, ypos);

	i = 0;
	XtSetArg(args[i], XtNx, xpos);				i++;
	XtSetArg(args[i], XtNy, ypos);				i++;
	XtSetArg(args[i], XtNhorizDistance, (int) xpos);	i++;
	XtSetArg(args[i], XtNvertDistance,  (int) ypos);	i++;
	XtSetValues(glyph->toplevel, args, i);
}



/************************************************************
*
* Routine Name: xvl_query_glyph
*
*      Purpose: This routine is used to translate a glyph to
*		the deisred location.
*
*       Output: None
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_query_glyph(glyph, xpos, ypos)

Glyph	 *glyph;
Position *xpos, *ypos;
{
	Position x, y;


	XtTranslateCoords(XtParent(glyph->toplevel), 0, 0, &x, &y);
	XtTranslateCoords(glyph->toplevel, 0, 0, xpos, ypos);
	*xpos = *xpos - x;
	*ypos = *ypos - y;
}



/************************************************************
*
* Routine Name: xvl_error_glyphs
*
*      Purpose: This routine is used to display an error message
*		associated with at least two glyphs.  
*		blinking.
*
*       Output: None
*
*    CALLED BY:
*
*   WRITTEN BY: Mark Young
*
*
*************************************************************/


xvl_error_glyphs(error, glyph1, glyph2)

char	 *error;
Glyph	 *glyph1, *glyph2;
{
	if (glyph1) xvl_invert_glyph(glyph1, True);
	if (glyph2) xvl_invert_glyph(glyph2, True);
	xvf_error_wait(error, "cantata", NULL);
	if (glyph1) xvl_invert_glyph(glyph1, False);
	if (glyph2) xvl_invert_glyph(glyph2, False);
}
