 /*
  * Khoros: $Id: list.c,v 1.3 1992/03/20 22:49:57 dkhoros Exp $
  */

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

 /*
  * $Log: list.c,v $
 * Revision 1.3  1992/03/20  22:49:57  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 "xvutils.h"	


/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   >>>>                                                       <<<<
   >>>>	    	    file name: list.c 	                      <<<<
   >>>>                                                       <<<<
   >>>>      Creates Single & Multi-Selection List Widgets    <<<<
   >>>>                                                       <<<<
   >>>>                xvf_run_list_wait()		      <<<<              
   >>>>                xvf_run_list_multsel_wait()	      <<<<              
   >>>>                xvf_create_list_widget()	              <<<<              
   >>>>                xvf_add_list_entry()	              <<<<              
   >>>>                xvf_delete_list_entry()	              <<<<              
   >>>>                xvf_list_cb()	              	      <<<<              
   >>>>                xvf_user_defined_cb()	       	      <<<<              
   >>>>                xvf_list_multsel_cb()	       	      <<<<              
   >>>>                xvf_user_defined_multsel_cb()   	      <<<<              
   >>>>                xvf_use_lists_cb()   	      	      <<<<              
   >>>>                xvf_cancel_lists_cb()   	      	      <<<<              
   >>>>                find_string_length()   	      	      <<<<              
   >>>>                                                       <<<<
   >>>>>>>>>>>>>>>>>>>>>>>>>>>>> <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */

static int		list_size, widget_size, USE;

/* Map <CR> and control-X to goto begining of file. */
char search_trans_table2[] =
"<Key>0xff0d:   beginning-of-file() \n\
Ctrl<Key>x:    beginning-of-file()";

typedef struct _xvf_list_entry
{
	int		       count;
	XawListReturnStruct     *entry;
	struct _xvf_list_entry *next, *prev;
} xvf_list_entry;

typedef struct _xvf_multlist_entry
{
	char *filename;
	Widget button;
	Widget list_back;
	int currently_mapped;
	char **items;
	int   numitems;
	struct _xvf_multlist_entry *next;
} xvf_multlist_entry;

typedef struct {
	Widget toplevel;
	Widget back;
	Widget label;
	xvf_multlist_entry *multlist;
	char *path;
	int duplicate_entries;
	} List_Data;

static xvf_list_entry *list_head, *list_tail;
static caddr_t *user_list, *widget_list;

void xvf_use_lists_cb();


/************************************************************
*
*  Routine Name:  char *xvf_run_list_wait()
*
*      Purpose:   This routine is called directly by the user application.
*		  (as opposed to by the forms).  It takes an array of strings,
*		  and uses them to create a List Widget.  The list widget is
*		  popped up, and the user selects a string from the list.
*		  This string is then returned to the application program.
*                 NOTE: this routine will grab all events 
*                 while running.  
*
*	 Input:   list - the array of strings that the appl. program needs to
*			 be displayed as elements of the list.
*		  size  - the size of the 'list' array
*		  col_num - number of columns to display list in
*		  prompt - a prompt or label to display on the top 
*			   of the list widget
*		  label - the label of the list widget
*		  current - the currently-selected string in the list
*			    (NULL implies that no string is currently selected
*		  user_defined - flag indicating if a string widget should be
*			        included to allow the user to enter a string
*				that is not part of the list array.
*
*       Output:   returns the string selected from the list by the user.
*
*    Called By:   the application program
*
*   Written By:   Danielle Argiro & Mark Young
*
*
*************************************************************/

XawListReturnStruct *xvf_run_list_wait(list, size, col_num, prompt,label,
				      current, user_defined)
char 	*list[]; 
int	size, col_num;
char	*prompt, *label;
int     current;
int     user_defined;
{
	Widget toplevel, xvf_create_list_widget();
	XawListReturnStruct  *entry;
  

	LIST_DONE = false;

	list_head = list_tail = NULL;
	toplevel  = xvf_create_list_widget(list, size, col_num, prompt,label,
					   current, user_defined, FALSE, FALSE);
        
	if (toplevel == NULL)
	   return(NULL);
        else
           xvf_add_toplevel(toplevel);

	while (!LIST_DONE)
	{
	   /* this does XtNextEvent/DispatchEvent*/
	   xvf_process_event();      
	}

       /*
        * delete the list widget's toplevel from the list 
        * used by journal playback before destroying it
        */
	xvf_delete_toplevel(toplevel);
	XtUnmapWidget(toplevel);
	XtDestroyWidget(toplevel); 

	if (list_head == NULL)
	{
	   return(NULL);
	}
	entry = list_head->entry;
	if (list_head != NULL)
	   free((caddr_t) list_head);

	return(entry);
}




/************************************************************
*
*  Routine Name:  char *xvf_run_list_multsel_wait()
*
*      Purpose:   This routine is called directly by the user application.
*		  (as opposed to by the forms).  It takes an array of strings,
*		  and uses them to create a List Widget.  The list widget is
*		  popped up, and the user may select as many strings from
*		  the list as desired.  An array of XawListReturnStruct
*		  structures are then returned to the application program.
*                 NOTE: this routine will grab all events 
*                 while running.  
*
*	 Input:   list - the array of strings that the appl. program needs to
*			 be displayed as elements of the list.
*		  size  - the size of the 'list' array
*                 col_num - number of columns to display list in
*		  prompt - a prompt or label to display on the top 
*			   of the list widget
*		  label - the label of the list widget
*		  user_defined - flag indicating if a string widget should be
*			        included to allow the user to enter a string
*				that is not part of the list array.
*		  duplicate_entries - should be TRUE if the user is allowed to
*				      select the same item twice.
*		  num - returns the number of items selected.
*
*       Output:   returns an array of XawListReturnStructs containing the
*		  information about the items selected from the list 
*		  by the user.
*
*    Called By:   the application program
*
*   Written By:   Danielle Argiro, Mark Young, & Mike Lang
*
*
*************************************************************/

XawListReturnStruct
**xvf_run_list_multsel_wait(list, size, col_num, prompt, label, user_defined,
			    duplicate_entries, num)

char 	*list[]; 
int	size;
int	col_num;
char	*prompt, *label;
int     user_defined, duplicate_entries;
int	*num;
{
	char   buffer[MaxLength];
	int    i, num_selections;
	Widget toplevel;
	xvf_list_entry  *selection, *temp;
	XawListReturnStruct **selections;

	LIST_DONE = false;
	USE = false;

	list_size = size;
	user_list = (caddr_t *) list;
	widget_list = (caddr_t *) XtMalloc(sizeof(caddr_t *) * size);
	for (i = 0; i < size; i++)
	{
	    if (duplicate_entries)
	       sprintf(buffer,"      %s", list[i]);
	    else
	       sprintf(buffer,"   %s", list[i]);

	    widget_list[i] = xvf_strcpy(buffer);
	}

	list_head = list_tail = NULL;
	toplevel = xvf_create_list_widget(widget_list, size, col_num,prompt,
					  label,NULL, user_defined,
					  duplicate_entries,TRUE);

	if (toplevel == NULL)
	   return(NULL);
        else
           xvf_add_toplevel(toplevel);

	while (!LIST_DONE)
	{
	   /* this does XtNextEvent/DispatchEvent*/
	   xvf_process_event();      
	}

       /*
        * delete the list widget's toplevel from the list 
        * used by journal playback before destroying it
        */
        xvf_delete_toplevel(toplevel);
	XtUnmapWidget(toplevel);
	XtDestroyWidget(toplevel); 

	if ((list_head == NULL) && (USE == true))
	{
	   *num = 0;
	   return(NULL);
	}
	else if (list_head == NULL)
	{
	   *num = -1;
	   return(NULL);
        }

	/*
	 *  Get the number of entries in the selection list.  This will be used
	 *  in malloc'ing the proper amount of memory for returning the list
	 *  to the user.
	 */
	selection = list_head;
	num_selections = 0;
	while (selection != NULL)
	{
	   num_selections++;
	   selection = selection->next;
	}

	/*
	 *  malloc the selections to be returned and then copy the entries.
	 *  While copying the entries we free() the xvf_list_entry structures.
	 */
	selections = (XawListReturnStruct **) XtMalloc(num_selections *
		      sizeof(XawListReturnStruct *));

	selection = list_head;
	for (i = 0; i < num_selections; i++)
	{
	   selections[i] = (XawListReturnStruct *) selection->entry;
	   temp = selection;
	   selection = selection->next;
	   if (temp != NULL)
	      free((caddr_t) temp);
	}

	/*
	 *  return the number of elements and the selections
	 */
	*num = num_selections;
	return(selections);
}




/************************************************************
*
*  Routine Name:  XawListReturnStruct **xvf_run_multlist_multsel_wait()
*
*      Purpose:   This routine is called directly by the user application.
*		  (as opposed to by the forms).  It takes an array of strings,
*		  and uses them to create a List Widget.  The list widget is
*		  popped up, and the user may select as many strings from
*		  the list as desired.  An array of XawListReturnStruct
*		  structures are then returned to the application program.
*                 NOTE: this routine will grab all events 
*                 while running.  
*
*	 Input:   list - the array of strings that the appl. program needs to
*			 be displayed as elements of the list.
*		  size  - the size of the 'list' array
*                 col_num - number of columns to display list in
*		  prompt - a prompt or label to display on the top 
*			   of the list widget
*		  label - the label of the list widget
*		  user_defined - flag indicating if a string widget should be
*			        included to allow the user to enter a string
*				that is not part of the list array.
*		  duplicate_entries - should be TRUE if the user is allowed to
*				      select the same item twice.
*		  num - returns the number of items selected.
*
*       Output:   returns an array of XawListReturnStructs containing the
*		  information about the items selected from the list 
*		  by the user.
*
*    Called By:   the application program
*
*   Written By:   Danielle Argiro, Mark Young, & Mike Lang
*
*
*************************************************************/

#define XVFMaxListSize 5000

XawListReturnStruct
**xvf_run_multlist_multsel_wait(path, col_num, prompt, label, user_defined,
			    duplicate_entries, num, default_filename)

char 	*path; 
int	col_num;
char	*prompt, *label;
int     user_defined, duplicate_entries;
int	*num;
char	*default_filename;
{
	int    i, num_selections, size;
	Widget toplevel;
	xvf_list_entry  *selection, *temp;
	XawListReturnStruct **selections;
	Widget xvf_create_multlist_widget();
	char **list;

	LIST_DONE = false;
	USE = false;

	list_head = list_tail = NULL;
	
	list = (char **) calloc(1,sizeof(char *) * XVFMaxListSize);
	toplevel = xvf_create_multlist_widget(path, col_num,prompt,
					      label,user_defined,
					      duplicate_entries,
					      list, &size, default_filename);

        user_list = (caddr_t *) list;
	list_size = size;
        widget_list = (caddr_t *) XtMalloc(sizeof(caddr_t *) * size);
	widget_size = 500;
        for (i = 0; i < list_size; i++)
            widget_list[i] = xvf_strcpy(list[i]);

	if (toplevel == NULL)
	   return(NULL);
        else
           xvf_add_toplevel(toplevel);

	while (!LIST_DONE)
	{
	   /* this does XtNextEvent/DispatchEvent*/
	   xvf_process_event();      
	}

       /*
        * delete the list widget's toplevel from the list 
        * used by journal playback before destroying it
        */
        xvf_delete_toplevel(toplevel);
	XtUnmapWidget(toplevel);
	XtDestroyWidget(toplevel);

	if ((list_head == NULL) && (USE == true))
	{
	   *num = 0;
	   return(NULL);
	}
	else if (list_head == NULL)
	{
	   *num = -1;
	   return(NULL);
        }

	/*
	 *  Get the number of entries in the selection list.  This will be used
	 *  in malloc'ing the proper amount of memory for returning the list
	 *  to the user.
	 */
	selection = list_head;
	num_selections = 0;
	while (selection != NULL)
	{
	   num_selections++;
	   selection = selection->next;
	}

	/*
	 *  malloc the selections to be returned and then copy the entries.
	 *  While copying the entries we free() the xvf_list_entry structures.
	 */
	selections = (XawListReturnStruct **) XtMalloc(num_selections *
		      sizeof(XawListReturnStruct *));

	selection = list_head;
	for (i = 0; i < num_selections; i++)
	{
	   selections[i] = (XawListReturnStruct *) selection->entry;
	   temp = selection;
	   selection = selection->next;
	   if (temp != NULL)
	      free((caddr_t) temp);
	}

	/*
	 *  return the number of elements and the selections
	 */
	*num = num_selections;
	return(selections);
}



/************************************************************
*
*  Routine Name:  char *xvf_create_multlist_widget()
*
*      Purpose:   This routine creates a multi-list widget.  This is like the 
*		  multi-selection list widget, but it differs in that it 
*		  it may display a number of lists to the user. Like the
*		  Help widget, it does an 'ls' in the specified directory
*		  and creates a button for each file in the directory.  When
*		  the user clicks on a button, the the contents of the 
*		  corresponding file (which should always be a list of strings)
*		  is displayed and the user is allowed to select one or more
*		  of these strings.  The main use for this routine is in a
*		  situation such as the "Routines" list widget needed by 
*		  cantata - when the list to be presented to the user is so 
*		  large as to be unmanageable, and it is preferable to divide
*		  the list into several categories. 
*
*		  After the user clicks on "Use", an array of 
*		  XawListReturnStruct structures are returned to the 
*		  application program, where each structure describes one of
*		  the items selected by the user.  Note that the information
*		  returned does not distinguish which files the items selected
*		  came from.      
*						
*                 NOTE: this routine will grab all events 
*                 while running.  
*
*	 Input:   directory - the directory which contains the files whose 
*			      contents are to be displayed as lists.
*                 col_num   - number of columns to display each list in
*		  prompt    - a prompt or label to display on the top 
*			      of the list widget
*		  label     - the label of the list widget
*		  user_defined - flag indicating if a string widget should be
*			        included to allow the user to enter a string
*				that is not part of the list array.
*		  duplicate_entries - should be TRUE if the user is allowed to
*				      select the same item twice.
*		  num - returns the number of items selected.
*		  
*
*       Output:   returns an array of XawListReturnStructs containing the
*		  information about the items selected from the list 
*		  by the user.
*
*    Called By:   the application program
*
*   Written By:   Danielle Argiro
*
*
*************************************************************/

#define MaxNumLinesInViewport 35

Widget xvf_create_multlist_widget(path, col_num, prompt, label, user_defined,
			    duplicate_entries, list, listsize, default_filename)

char	*path;
int	col_num;
char	*prompt, *label;
int     user_defined, duplicate_entries;
char	**list;
int	*listsize;
char    *default_filename;
{
	int    i, n, dir_type, filenum, viewport_height;
	Arg    arg[MaxArgs];
	Widget viewport, listback, cancel, use, left_offset;
	char *exp_path, *top_name, *cap_top_name;
	char **filenames, **xvf_get_path_info(), 
		**xvf_compose_single_list();
	char  button_name[MaxLength], name[MaxLength];
	List_Data *list_data;
	void xvf_display_multlist();
	void xvf_multlist_multsel_cb();
	xvf_multlist_entry *multlist;

	/*
    	 *  check for null path
    	 */
   	if (path == NULL)
   	{
      	    fprintf(stderr,"xvf_run_multlist_multsel_wait: \n");
      	    fprintf(stderr,"You must provide a path to a directory \n");
      	    fprintf(stderr,"in which the list files exist.\n");
      	    return(NULL);
   	}

	if (label == NULL)
	   label = xvf_strcpy("List Widget");

	/*
     	 *  check for bogus path
     	 */
   	exp_path = vfullpath(path, NULL, NULL);
   	if (exp_path == NULL)
   	{
      	    fprintf(stderr,"xvf_run_multlist_multsel_wait: \n");
      	    fprintf(stderr,"Error in path provided.\n");
      	    return(NULL);
   	}

        list_data = (List_Data *) calloc(1, sizeof(List_Data));

	/*
	 *  go to the specified directory, do a listing into a file, 
	 *  and read in the filenames to be displayed.
         */
	filenames = xvf_get_path_info(exp_path, &dir_type, &filenum);

        if (dir_type == BOGUS_PATH)
	{
	    fprintf(stderr,"xvf_create_multlist_widget: \n");
            fprintf(stderr,"Invalid path provided.\n");
            return(NULL);
        }
	else if (dir_type == FILE_PATH)
	{
	    fprintf(stderr,"xvf_create_multlist_widget: \n");
            fprintf(stderr,"No support for single files provided.\n");
            return(NULL);
	}

	/*
         *  compose the list out of contents of the files specified in dir
         */
        xvf_compose_whole_list(exp_path, filenames, filenum, 
			       list, listsize, duplicate_entries);

        sprintf(name,"%d_list", List_cnt);
        top_name = xvf_strcpy(name);
        cap_top_name = xvf_cap_first_letter(top_name);
	List_cnt++;

        /* 
         * create the list's toplevel widget, and add it to the
         * list used by journal playback
         */
   	n = 0;
   	XtSetArg(arg[n], XtNscreen, xvf_screen);       		n++;
  	XtSetArg(arg[n], XtNargc, xvf_ac);                     	n++;
   	XtSetArg(arg[n], XtNargv, xvf_av);                      n++;
	XtSetArg(arg[n], XtNsaveUnder, False);			n++;
	XtSetArg(arg[n], XtNwinGravity, StaticGravity);		n++; 
   	list_data->toplevel = XtAppCreateShell(top_name,cap_top_name,
        		applicationShellWidgetClass, xvf_display, arg, n);
	xvf_add_toplevel(list_data->toplevel);


   	/* create back widget */
   	n = 0;
   	XtSetArg(arg[n],XtNmappedWhenManaged,True);          n++;
   	XtSetArg(arg[n],XtNborderWidth,1);                   n++;
   	XtSetArg(arg[n],XtNdefaultDistance,4);               n++;
   	list_data->back = XtCreateManagedWidget("list_back",
       	         formWidgetClass, list_data->toplevel,arg,n);


        /* create label widget */
   	n = 0;
       	XtSetArg(arg[n],XtNlabel, xvf_strcpy(label));    n++;
   	XtSetArg(arg[n],XtNwidth,(Dimension) (50*xvf_font_width)); n++;
   	XtSetArg(arg[n],XtNmappedWhenManaged,True);          n++;
   	XtSetArg(arg[n],XtNborderWidth, 0);                  n++;
   	if (xvf_font != NULL)
   	{
            XtSetArg(arg[n],XtNfont,xvf_font);              n++;
   	}
   	list_data->label = XtCreateManagedWidget("list_label",
        		labelWidgetClass,list_data->back,arg,n);


	/* 
     	 * create the viewport widget which will 
    	 * provide backplane with scroll bar
    	 */ 
   	if ((int)(*listsize/col_num) < MaxNumLinesInViewport) 
	    viewport_height = (int)(*listsize/col_num);
   	else viewport_height = MaxNumLinesInViewport;
    
	n = 0;
   	XtSetArg(arg[n], XtNallowVert, True); 	                n++;
   	XtSetArg(arg[n], XtNforceBars, True); 	                n++;
   	XtSetArg(arg[n], XtNfromVert, list_data->label); 	n++;
   	XtSetArg(arg[n], XtNheight, xvf_font_height*viewport_height); n++;
   	XtSetArg(arg[n], XtNx, xvf_font_height); 			n++;
   	viewport = XtCreateManagedWidget("list_view_back",viewportWidgetClass,
				      	list_data->back,arg,n);

	/* 
    	 * create the backplane widget for prompts & responses
    	 */ 
   	n = 0;
   	XtSetArg(arg[n],XtNmappedWhenManaged,True);	n++;
   	XtSetArg(arg[n],XtNborderWidth,1); 		n++;
   	listback = XtCreateManagedWidget("view_list_back",formWidgetClass,
				           viewport,arg,n);

   	/* if they provided a directory, need to put up one button widget
           for each file in the directory, labeled with its filename. */
        if (dir_type == DIRECTORY_PATH)
   	{
            left_offset = NULL;
            list_data->multlist = (xvf_multlist_entry *) 
				calloc(1, sizeof(xvf_multlist_entry));
	    multlist = list_data->multlist;
            for (i = 0; i < filenum; i++)
            {
            	sprintf(button_name, "file_button_%d", i);
		filenames[i][xvf_strlen(filenames[i])-1] = '\0';

		list_data->path = xvf_strcpy(path);
		list_data->duplicate_entries = duplicate_entries;
		multlist->filename = xvf_strcpy(filenames[i]);

            	n = 0;
            	XtSetArg(arg[n],XtNfromVert,list_data->label);    n++;
            	XtSetArg(arg[n],XtNfromHoriz,left_offset);           n++;
            	XtSetArg(arg[n],XtNlabel,filenames[i]);              n++;
            	if (xvf_font != NULL)
            	{
                    XtSetArg(arg[n],XtNfont,xvf_font);              n++;
            	}
           	multlist->button = XtCreateManagedWidget(button_name,
               	               commandWidgetClass,listback,arg,n);
            	XtAddCallback(multlist->button,XtNcallback,
                              xvf_display_multlist, list_data);
            	left_offset = multlist->button;

		/* create list widget */
		multlist->items = xvf_compose_single_list(path, filenames[i], 
				       &(multlist->numitems),duplicate_entries);
		n = 0;
     		if (xvf_font != NULL)
     		{
       	   		XtSetArg(arg[n],XtNfont,xvf_font);            n++;
     		}
		XtSetArg(arg[n],XtNfromHoriz,NULL);                n++;
     		XtSetArg(arg[n],XtNfromVert,left_offset);           n++;
     		XtSetArg(arg[n],XtNhorizDistance, (Dimension) 4);  n++;
		XtSetArg(arg[n],XtNlist, multlist->items);           n++;
		XtSetArg(arg[n],XtNlongest, widget_size );           n++;
     		XtSetArg(arg[n],XtNnumberStrings, multlist->numitems);     n++;
     		XtSetArg(arg[n],XtNforceColumns, True);            n++;
     		XtSetArg(arg[n],XtNdefaultColumns, col_num);       n++;
     		XtSetArg(arg[n],XtNverticalList, true);            n++;
		if (strcmp(filenames[i],default_filename)!= 0)	
		{
     		    XtSetArg(arg[n],XtNmappedWhenManaged, false);   n++;
	        }
	        else
		    multlist->currently_mapped = true;
		multlist->list_back = XtCreateManagedWidget("list", 
			listWidgetClass, listback,arg,n);
        	XtAddCallback(multlist->list_back, 
			      XtNcallback, xvf_multlist_multsel_cb, 
		              (caddr_t) list_data);
		if (i < filenum - 1 )
	 	{
		    multlist->next = (xvf_multlist_entry *)
                                    calloc(1, sizeof(xvf_multlist_entry));
		    multlist = multlist->next;
		}
		else multlist->next = NULL;
            }
       }

   	/* create use button */
   	n = 0;
   	XtSetArg(arg[n],XtNlabel,"USE");                    n++;
   	XtSetArg(arg[n],XtNfromHoriz,list_data->label); n++;
   	XtSetArg(arg[n],XtNfromVert,list_data->label); n++;
   	XtSetArg(arg[n],XtNhorizDistance,(Dimension)25); n++;
   	if (xvf_font != NULL)
   	{
            XtSetArg(arg[n],XtNfont,xvf_font);              n++;
    
   	}
   	use = XtCreateManagedWidget("use_wid",
       			 commandWidgetClass,list_data->back,arg,n);
   	XtAddCallback(use,XtNcallback,xvf_use_lists_cb,list_data);

   	/* create cancel button */
   	n = 0;
   	XtSetArg(arg[n],XtNlabel,"CANCEL");                    n++;
   	XtSetArg(arg[n],XtNfromHoriz,use); n++;
   	XtSetArg(arg[n],XtNfromVert,list_data->label); n++;
   	if (xvf_font != NULL)
   	{
            XtSetArg(arg[n],XtNfont,xvf_font);              n++;
    
   	}
   	cancel = XtCreateManagedWidget("cancel_wid",
       			 commandWidgetClass,list_data->back,arg,n);
   	XtAddCallback(cancel,XtNcallback,xvf_cancel_list_cb,list_data);
	xvf_add_protocol_handler(list_data->toplevel, "WM_DELETE_WINDOW", 
			         xvf_cancel_list_cb, list_data->toplevel);

        xvf_place_widget(list_data->toplevel, NULL);
	return(list_data->toplevel);
}



/************************************************************
*
*  Routine Name:  Widget xvf_create_list_widget()
*
*      Purpose:   This routine creates the list widget and maps it to the screen,
*		  It takes an array of strings, and uses each string as an item
*		  in the List. The Prompt and Label strings are used to complete
*		  the list widget, and if current is not NULL, it will come up
*		  highlighted.
*
*	 Input:   list - the array of strings that the appl. program needs to
*			 be displayed as elements of the list.
*		  size  - the size of the 'list' array
*		  prompt - a prompt or label to display on the top 
*			   of the list widget
*		  label - the label of the list widget
*		  current - the currently-selected string in the list
*			    (NULL implies that no string is currently selected
*		  col_num - application specifies # columns in list widget
*		  user_defined - flag indicating if a string widget should be
*			        included to allow the user to enter a string
*				that is not part of the list array.
*
*       Output:   returns the string selected from the list by the user.
*
*    Called By:   the application program
*
*   Written By:   Danielle Argiro, Stephanie Hallett, & Mark Young
*
*
*********************************************************************/
#define TEXT_WIDTH 35


Widget xvf_create_list_widget(list, size, col_num, prompt, label, current,
			       user_defined, duplicate_entries, multsel)
char *list[];
int   size;
char  *prompt, *label;
int   current;
int   col_num;  
int   user_defined, duplicate_entries, multsel;
{
     Widget toplevel;		/* toplevel widget */
     Widget back;		/* backplane for list widget */
     Widget label_widget;	/* label widget */
     Widget prompt_widget;	/* prompt widget */
     Widget list_widget;	/* list widget */
     Widget viewport;    	/* viewport for list widget if needed */
     Widget cancel_widget;	/* button to cancel list widget*/
     Widget string_wid;        /* button to register user-specified string */
     Widget spec_widget;	/* button to register user-specified string */
     XtTranslations translations;
     char	 *spec_buffer;
     char name[MaxLength];
     char *top_name, *cap_top_name;
     Arg    arg[25];
     int    i, n, max_strlen, label_width, prompt_width, width, viewport_height;

     spec_buffer = (char *) calloc(1,sizeof(char)*MaxLength);
     bzero(spec_buffer, MaxLength);

     if (label == NULL)
     {  
         label = xvf_strcpy("List");
         label_width = 5*xvf_font_width;
     }
     else
         label_width = find_string_width(label)*xvf_font_width;

     if (prompt == NULL)
     {  
	 prompt = xvf_strcpy("Choose from:");
	 prompt_width = 13*xvf_font_width;
     }
     else prompt_width = find_string_width(prompt)*xvf_font_width;

     if (list == NULL)
     {  
         fprintf(stderr,"xvf_create_list_widget: \n");
         fprintf(stderr,"need a list for the list widget!\n");
         return(NULL);
     }

    /*
     * create identifying widget name
     */
     sprintf(name,"%d_list",List_cnt);
     List_cnt++;
     top_name = xvf_strcpy(name);
     cap_top_name = xvf_cap_first_letter(top_name);

     /* 
      * create the list's toplevel widget and add it to the
      * list used by journal playback.
      */
     i = 0;
     XtSetArg(arg[i], XtNscreen, xvf_screen); 		i++;
     XtSetArg(arg[i], XtNargc, xvf_ac); 	        i++;
     XtSetArg(arg[i], XtNargv, xvf_av);	          	i++; 
     XtSetArg(arg[i], XtNinput, True);			i++; 
     XtSetArg(arg[i], XtNwinGravity, StaticGravity);	i++; 
     toplevel = XtAppCreateShell(top_name,cap_top_name,
				transientShellWidgetClass,
			 	xvf_display, arg, i);
     xvf_add_toplevel(toplevel);

     /*
      * Compute the longest selection length.  This is to include the
      * prompt and label strings, but only if the number of columns
      * (col_num) is greater than 1.
      */
     if (col_num > 1)
     {
        max_strlen = xvf_strlen(list[0]);
     }
     else
     {
        max_strlen = find_string_width(prompt);
        if (xvf_strlen(label) > max_strlen)
	   max_strlen = find_string_width(label);
     }

     for (i = 0; i < size; i++)
     {
	  if (xvf_strlen(list[i]) > max_strlen) 
	     max_strlen = xvf_strlen(list[i]);
     }

     /* 
      * create backplane widget 
      */ 
      back = XtCreateManagedWidget("list_back",
           formWidgetClass, toplevel, NULL, 0);


     /* 
      * create cancel button to upper left hand corner
      */
     n = 0;
     XtSetArg(arg[n],XtNwidth, (Dimension) 7*xvf_font_width); n++;
     XtSetArg(arg[n],XtNheight,(Dimension) xvf_font_height);  n++;
     XtSetArg(arg[n],XtNlabel,"Cancel"); 		      n++;
     if (xvf_font != NULL)
     {
         XtSetArg(arg[n],XtNfont,xvf_font);		      n++;
     }
     XtSetArg(arg[n],XtNmappedWhenManaged,True);      	      n++;
     cancel_widget = XtCreateManagedWidget("Cancel", commandWidgetClass,
					    back,arg,n);
     XtAddCallback(cancel_widget, XtNcallback, xvf_cancel_list_cb, toplevel);
     xvf_add_protocol_handler(toplevel, "WM_DELETE_WINDOW", 
			      xvf_cancel_list_cb, toplevel);

     /* 
      * create the label widget to right of cancel widget
      */
      i = 0;
      XtSetArg(arg[i],XtNlabel,label);				i++;
      XtSetArg(arg[i],XtNwidth, (Dimension) label_width); 	i++;
      XtSetArg(arg[i],XtNmappedWhenManaged,True);       	i++;
      XtSetArg(arg[i],XtNborderWidth, 0);               	i++;
      XtSetArg(arg[i],XtNfromHoriz, cancel_widget);            	i++;
      if (xvf_font != NULL)
      {
        XtSetArg(arg[i],XtNfont,xvf_font);             		i++;
      }
      label_widget = XtCreateManagedWidget("label_wid",
                     labelWidgetClass,back,arg,i);

     /* 
      * if we have a multi-selection list widget, or have a 
      * text widget in which they can type an entry, need a "Use" button
      */
      if (multsel || user_defined) 
      {
        n = 0;
        XtSetArg(arg[n], XtNwidth,(Dimension) 4*xvf_font_width);n++;
        XtSetArg(arg[n],XtNheight,(Dimension) xvf_font_height);	n++;
        XtSetArg(arg[n],XtNfromHoriz, label_widget);  	    	n++;
        XtSetArg(arg[n],XtNlabel, "Use");		     	n++;
        XtSetArg(arg[n],XtNmappedWhenManaged,True);    		n++;
        if (xvf_font != NULL)
	{
            XtSetArg(arg[n],XtNfont,xvf_font);		 	n++;
	}
        spec_widget = XtCreateManagedWidget("Use", commandWidgetClass,
					     back,arg,n);
      }

     /* 
      * create prompt widget 
      */
     width = xvf_font_width + label_width + 7*xvf_font_width;
     if ((multsel) || (user_defined))
       width += 4*xvf_font_width;
     if (width < prompt_width) width = prompt_width;

     n = 0;
     if (xvf_font != NULL)
     {
          XtSetArg(arg[n],XtNfont,xvf_font);			 n++;
     }
     XtSetArg(arg[n],XtNlabel,xvf_strcpy(prompt)); 		 n++;
     XtSetArg(arg[n],XtNfromHoriz,NULL);			 n++; 
     XtSetArg(arg[n],XtNfromVert,label_widget);  	 	 n++;  
     XtSetArg(arg[n],XtNwidth, (Dimension) width);	 	 n++;
     XtSetArg(arg[n],XtNmappedWhenManaged,True);		 n++;
     XtSetArg(arg[n],XtNborderWidth, 0);			 n++;
     if (xvf_font != NULL)
     {
        XtSetArg(arg[n],XtNfont,xvf_font);             		 n++;
     }
     prompt_widget = XtCreateManagedWidget("prompt", labelWidgetClass,
					    back,arg,n);	

     if ((int)(size/col_num) > MaxNumLinesInViewport)
     {
	/* 
     	 * create the viewport widget which will 
    	 * provide backplane with scroll bar
    	 */ 
	n = 0;
   	XtSetArg(arg[n], XtNallowVert, True); 	                n++;
   	XtSetArg(arg[n], XtNforceBars, True); 	                n++;
   	XtSetArg(arg[n], XtNfromVert, prompt_widget); 	n++;
   	XtSetArg(arg[n], XtNheight, 
		    (xvf_font_height+3)*MaxNumLinesInViewport); n++;
        if (col_num == 1)
        {
             XtSetArg(arg[n],XtNwidth, (xvf_font_width+1)*max_strlen);  	n++;   
        }
   	XtSetArg(arg[n], XtNx, xvf_font_height); 			n++;
   	viewport = XtCreateManagedWidget("list_view_back",viewportWidgetClass,
				      	back,arg,n);

	/* 
    	 * create the backplane widget for prompts & responses
    	 */ 
   	n = 0;
   	XtSetArg(arg[n],XtNmappedWhenManaged,True);	n++;
   	XtSetArg(arg[n],XtNborderWidth,0); 		n++;
   	back = XtCreateManagedWidget("view_list_back",formWidgetClass,
				           viewport,arg,n);

     }

     /* 
      * create list widget 
      */
     n = 0;
     if (xvf_font != NULL)
     {
          XtSetArg(arg[n],XtNfont,xvf_font);		n++;
     }
     XtSetArg(arg[n],XtNlist, list );             	n++;   
     XtSetArg(arg[n],XtNlongest, widget_size);	  	n++;   
     if (col_num == 1)
     {
         XtSetArg(arg[n],XtNwidth, xvf_font_width*max_strlen);  	n++;   
     }
     if ((int)(size/col_num) <= MaxNumLinesInViewport)
     {
        XtSetArg(arg[n],XtNfromVert, prompt_widget);   	n++;
     }
     XtSetArg(arg[n],XtNnumberStrings, size);     	n++;
     XtSetArg(arg[n],XtNforceColumns, True);     	n++;
     XtSetArg(arg[n],XtNdefaultColumns, col_num); 	n++; 
     XtSetArg(arg[n],XtNdefaultDistance, 0); 		n++; 
     XtSetArg(arg[n],XtNverticalList, true);      	n++;
     if (xvf_font != NULL)
     {
        XtSetArg(arg[n],XtNfont,xvf_font);             	n++;
     }
     list_widget = XtCreateManagedWidget("list", listWidgetClass,back,arg,n);

     if (multsel)
     {
        XtAddCallback(list_widget, XtNcallback, xvf_list_multsel_cb, (caddr_t)
		      duplicate_entries);
     }
     else
     {
        XtAddCallback(list_widget, XtNcallback, xvf_list_cb, NULL);
     }

     /* 
      *  if specified, create text widget in which user 
      *  may specify their own string response 
      */
     if (user_defined)
     {

        n = 0;
        XtSetArg(arg[n],XtNwidth, (Dimension) width); 	n++;
        XtSetArg(arg[n],XtNeditType, XawtextEdit);      n++;
        XtSetArg(arg[n],XtNtype, XawAsciiString);       n++;
        XtSetArg(arg[n],XtNfromVert,list_widget);    	n++;
        if (xvf_font != NULL)
	{
             XtSetArg(arg[n],XtNfont,xvf_font);		n++;
	}
        XtSetArg(arg[n],XtNstring, spec_buffer);	n++;
        XtSetArg(arg[n],XtNlength,MaxLength);		n++;
        string_wid = XtCreateManagedWidget("user_defined",
                          asciiTextWidgetClass,back,arg,n);
        translations = XtParseTranslationTable(search_trans_table2);
        XtOverrideTranslations(string_wid, translations);
        XtAddCallback(spec_widget, XtNcallback, xvf_user_defined_cb,
		      (caddr_t) string_wid);

     }
     if (user_defined && multsel)
     {
           XtAddCallback(spec_widget, XtNcallback, 
			 xvf_user_defined_multsel_cb, (caddr_t) spec_buffer);
     }
     else if (multsel)
     {
	   XtAddCallback(spec_widget, XtNcallback, xvf_use_lists_cb, NULL);
     }

     xvf_place_widget(toplevel, NULL);
     return(toplevel);
}



/***************************************************************
*
*   Routines to create and free an xvf_list_entry structure
*
***************************************************************/

xvf_list_entry *xvf_add_list_entry()
{
	xvf_list_entry *new;

	new = (xvf_list_entry *) XtMalloc(sizeof(xvf_list_entry));
	new->entry =(XawListReturnStruct *) XtMalloc(sizeof(XawListReturnStruct));
	new->count = 1;

	if (list_head == NULL)
	{
	   list_head = list_tail = new;
	   list_head->next = list_tail->next = NULL;
	   list_head->prev = list_tail->prev = NULL;
	}
	else if (list_head == list_tail)
	{
	   list_head->next = new;
	   new->prev = list_head;
	   new->next = NULL;
	   list_tail = new;
	}
	else
	{
	   list_tail->next = new;
	   new->prev = list_tail;
	   new->next = NULL;
	   list_tail = new;
	}
	return(new);
}


xvf_delete_list_entry(selection)

xvf_list_entry *selection;
{
	if (selection == NULL)
	   return;

	if (list_tail == list_head)
	{
	   list_head = list_tail = NULL;
	}
	else if (selection == list_head)
	{
	   list_head = list_head->next;
	   list_head->prev = NULL;
	}
	else if (selection == list_tail)
	{
	   list_tail = list_tail->prev;
	   list_tail->next = NULL;
	}
	else
	{
	   selection->next->prev = selection->prev;
	   selection->prev->next = selection->next;
	}

	if (selection->entry->string != NULL)
	   free((caddr_t) selection->entry->string);
	if (selection->entry != NULL)
	   free((caddr_t) selection->entry);
	if (selection != NULL)
	   free((caddr_t) selection);
}



/***************************************************************
*
*   Callback routines used by xvf_run_list_wait
*
***************************************************************/

/* callback used to get string from list */
void xvf_list_cb(widget, clientData, callData)  

Widget widget;
caddr_t clientData, callData;
{
	/* cast the pointer to the Callback data */
	XawListReturnStruct *latest = (XawListReturnStruct *) callData;

	if (list_head != NULL)
	{
	   xvf_delete_list_entry(list_head);
	}

	list_head = xvf_add_list_entry();
	list_head->entry->list_index = latest->list_index;
	list_head->entry->string = xvf_strcpy(latest->string);

	LIST_DONE = TRUE;
}


/* callback used to register a user-specified string */
void xvf_user_defined_cb(widget, clientData, callData)

Widget widget;
caddr_t clientData, callData;
{
	char   *string_return;
	int    i;
	Arg    arg[MaxArgs];

	Widget string_wid = (Widget) clientData;

	if (list_head != NULL)
	{
	   xvf_delete_list_entry(list_head);
	}

	list_head = xvf_add_list_entry();
	list_head->entry->list_index =  -1;

        i = 0;
	XtSetArg(arg[i], XtNstring, &string_return); i++;
        XtGetValues(string_wid, arg, i);
	list_head->entry->string = xvf_strcpy(string_return);

	LIST_DONE = TRUE;
}



/***************************************************************
*
*   Callback routines used by xvf_run_list_multsel_wait
*
***************************************************************/


/* callback used to get string from list */
void xvf_list_multsel_cb(widget, clientData, callData)  

Widget widget;
caddr_t clientData, callData;
{
	int	count = 1;
	char	buffer[10];
	xvf_list_entry *list, *new;

	/* cast the pointer to the Callback data */
	XawListReturnStruct *latest = (XawListReturnStruct *) callData;
	int duplicate_entries = (int) clientData;

	list = list_head;
	while (list != NULL)
	{
	   if (list->entry->list_index == latest->list_index)
	   {
	      if (duplicate_entries == FALSE)
	      {
		 latest->string[1] = ' ';
	         XawListChange(widget, (String *) widget_list, list_size,
			      widget_size, FALSE);

		 xvf_delete_list_entry(list);
	         return;
	      }
	      else
	      {
		 list->count++;
		 count = list->count;
		 break;
	      }
	   }
	   else
	      list = list->next;
	}

	new = xvf_add_list_entry();
	new->entry->list_index = latest->list_index;
	new->entry->string = xvf_strcpy(user_list[latest->list_index]);

	if (duplicate_entries == FALSE)
	   latest->string[1] = '*';
	else
	{
	   (void) sprintf(buffer,"(%1d)", count);
	   (void) strncpy(latest->string,buffer,strlen(buffer));
	}

	XawListChange(widget, (String *) widget_list, list_size, widget_size,
		     FALSE);
}


/* callback used to get string from list */
void xvf_multlist_multsel_cb(widget, clientData, callData)  

Widget widget;
caddr_t clientData, callData;
{
	int	count = 1;
	char	buffer[10], *latest_string, *list_string;
	xvf_list_entry *list, *new;
	List_Data *list_data;
	xvf_multlist_entry *multlist;

	/* cast the pointer to the Callback data */
	XawListReturnStruct *latest = (XawListReturnStruct *) callData;
	list_data = (List_Data *) clientData;

	multlist = list_data->multlist;
	while (!multlist->currently_mapped) multlist = multlist->next;

	list = list_head;
	while (list != NULL)
	{
	   if (list_data->duplicate_entries) 
	   {
		latest_string = &latest->string[5];
		list_string = &list->entry->string[5];
	   }
	   else 
	   {
		latest_string = &latest->string[3];
		list_string = &list->entry->string[3];
	   }
	   if (strcmp(list_string, latest_string) == 0)
	   {
	      if (list_data->duplicate_entries == FALSE)
	      {
		 latest->string[1] = ' ';
	         XawListChange(widget, (String *) multlist->items, 
				multlist->numitems, widget_size, FALSE);

		 xvf_delete_list_entry(list);
	         return;
	      }
	      else
	      {
		 list->count++;
		 count = list->count;
		 break;
	      }
	   }
	   else
	      list = list->next;
	}

	new = xvf_add_list_entry();
	new->entry->list_index = latest->list_index;
	new->entry->string = xvf_strcpy(latest->string);

	if (list_data->duplicate_entries == FALSE)
	   latest->string[1] = '*';
	else
	{
	   (void) sprintf(buffer,"(%1d)", count);
	   (void) strncpy(latest->string,buffer,strlen(buffer));
	}


	XawListChange(widget, (String *) multlist->items, 
			multlist->numitems, widget_size, FALSE);
}


/* callback used to register a user-specified string */
void xvf_user_defined_multsel_cb(widget, clientData, callData)

Widget widget;
caddr_t clientData, callData;
{
	char *spec_buffer = (char *) clientData;
	xvf_list_entry *new;

	new = xvf_add_list_entry();
	new->entry->list_index =  -1;
	new->entry->string = xvf_strcpy(spec_buffer);
}



/***************************************************************
*
*   Callback routines used by both xvf_run_list_wait &
*   xvf_run_list_multsel_wait
*
***************************************************************/


/* callback used to end the lists */
void xvf_use_lists_cb(widget, clientData, callData)

Widget widget;
caddr_t clientData, callData;
{
	LIST_DONE = true;
	USE = true;
}



void xvf_display_multlist(widget, clientData, callData)
Widget widget;
caddr_t clientData, callData;
{
	List_Data *list_data;
	xvf_multlist_entry *multlist;
	

	list_data = (List_Data *) clientData;

	/* unmap the old list widget */
	multlist = list_data->multlist;
	while (!multlist->currently_mapped)
	        multlist = multlist->next;
	XtUnmapWidget(multlist->list_back);
	multlist->currently_mapped = false;

	/* map the new list widget */
	multlist = list_data->multlist;
	while (multlist->button != widget)
		multlist = multlist->next;
	XtMapWidget(multlist->list_back);
	multlist->currently_mapped = true;

}

/* callback used to cancel list */
void xvf_cancel_list_cb(widget, clientData, callData)

Widget widget;
caddr_t clientData, callData;
{
	if (list_head != NULL)
	{
	   xvf_delete_list_entry(list_head);
	}
	list_head = NULL;

	LIST_DONE = true;

}

/* this utility checks the prompt string for \n's.
   if there is \n's in the string, returns the
   length of the longest line in the prompt.  
   else returns the length of the prompt */
int find_string_width(string)
char *string;
{
     int maxlen = 0, count = 0, i = 0;

     while (string[i] != '\0')
     {
	if (string[i] == '\n') 
        {
	   if (count > maxlen) maxlen = count;
           count = 0;
	}
	else count++;
	i++;
     }
     if (count > maxlen) maxlen = count;
     return(maxlen);
}
