/****************************************************************************
 * NCSA Mosaic for the X Window System                                      *
 * Software Development Group                                               *
 * National Center for Supercomputing Applications                          *
 * University of Illinois at Urbana-Champaign                               *
 * 605 E. Springfield, Champaign IL 61820                                   *
 * mosaic@ncsa.uiuc.edu                                                     *
 *                                                                          *
 * Copyright (C) 1993, Board of Trustees of the University of Illinois      *
 *                                                                          *
 * NCSA Mosaic software, both binary and source (hereafter, Software) is    *
 * copyrighted by The Board of Trustees of the University of Illinois       *
 * (UI), and ownership remains with the UI.                                 *
 *                                                                          *
 * The UI grants you (hereafter, Licensee) a license to use the Software    *
 * for academic, research and internal business purposes only, without a    *
 * fee.  Licensee may distribute the binary and source code (if released)   *
 * to third parties provided that the copyright notice and this statement   *
 * appears on all copies and that no charge is associated with such         *
 * copies.                                                                  *
 *                                                                          *
 * Licensee may make derivative works.  However, if Licensee distributes    *
 * any derivative work based on or derived from the Software, then          *
 * Licensee will (1) notify NCSA regarding its distribution of the          *
 * derivative work, and (2) clearly notify users that such derivative       *
 * work is a modified version and not the original NCSA Mosaic              *
 * distributed by the UI.                                                   *
 *                                                                          *
 * Any Licensee wishing to make commercial use of the Software should       *
 * contact the UI, c/o NCSA, to negotiate an appropriate license for such   *
 * commercial use.  Commercial use includes (1) integration of all or       *
 * part of the source code into a product for sale or license by or on      *
 * behalf of Licensee to third parties, or (2) distribution of the binary   *
 * code or source code to third parties that need it to utilize a           *
 * commercial product sold or licensed by or on behalf of Licensee.         *
 *                                                                          *
 * UI MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR   *
 * ANY PURPOSE.  IT IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED          *
 * WARRANTY.  THE UI SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY THE    *
 * USERS OF THIS SOFTWARE.                                                  *
 *                                                                          *
 * By using or copying this Software, Licensee agrees to abide by the       *
 * copyright law and all other applicable laws of the U.S. including, but   *
 * not limited to, export control laws, and the terms of this license.      *
 * UI shall have the right to terminate this license immediately by         *
 * written notice upon Licensee's breach of, or non-compliance with, any    *
 * of its terms.  Licensee may be held legally responsible for any          *
 * copyright infringement that is caused or encouraged by Licensee's        *
 * failure to abide by the terms of this license.                           *
 *                                                                          *
 * Comments and questions are welcome and can be sent to                    *
 * mosaic-x@ncsa.uiuc.edu.                                                  *
 ****************************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <Xm/Xm.h>
#include <Xm/Frame.h>
#include <Xm/DrawingA.h>
#include <Xm/ScrolledW.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/ToggleB.h>
#include <Xm/PushB.h>
#include <Xm/RowColumn.h>
#include <Xm/List.h>
#include "HTMLP.h"


#define X_NAME		"x"
#define Y_NAME		"y"

#define	W_TEXTFIELD	0
#define	W_CHECKBOX	1
#define	W_RADIOBOX	2
#define	W_PUSHBUTTON	3
#define	W_PASSWORD	4
#define	W_OPTIONMENU	5
#define	W_TEXTAREA	6
#define	W_LIST		7
#define	W_JOT		8


extern void NewJot();
extern void ClearJot();
extern void EVJotExpose();
extern void EVJotPress();
extern void EVJotMove();
extern void EVJotRelease();
extern char *EJB_JOTfromJot();
extern char *ParseMarkTag();


static Boolean ModifyIgnore = False;


char **ParseCommaList();
void FreeCommaList();


void
AddNewForm(hw, fptr)
	HTMLWidget hw;
	FormInfo *fptr;
{
	FormInfo *ptr;

	ptr = hw->html.form_list;
	if (ptr == NULL)
	{
		hw->html.form_list = fptr;
		fptr->next = NULL;
	}
	else
	{
		while (ptr->next != NULL)
		{
			ptr = ptr->next;
		}
		ptr->next = fptr;
		fptr->next = NULL;
	}
}


int
CollectSubmitInfo(fptr, name_list, value_list)
	FormInfo *fptr;
	char ***name_list;
	char ***value_list;
{
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WbFormCallbackData cbdata;
	WidgetInfo *wptr;
	int cnt;

	if (fptr->end == -1)  /* unterminated FORM tag */
	{
		wptr = hw->html.widget_list;
		cnt = 0;
		while (wptr != NULL)
		{
			cnt++;
			wptr = wptr->next;
		}
		cbdata.attribute_count = cnt;
	}
	else
	{
		cbdata.attribute_count = fptr->end - fptr->start;
	}
	cbdata.attribute_names = (char **)malloc(cbdata.attribute_count *
		sizeof(char *));
	cbdata.attribute_values = (char **)malloc(cbdata.attribute_count *
		sizeof(char *));

	if (fptr->start == 0)
	{
		wptr = hw->html.widget_list;
	}
	else
	{
		wptr = hw->html.widget_list;
		while (wptr != NULL)
		{
			if (wptr->id == fptr->start)
			{
				wptr = wptr->next;
				break;
			}
			wptr = wptr->next;
		}
	}

	cnt = 0;

	while ((wptr != NULL)&&(cnt < cbdata.attribute_count))
	{
	    if ((wptr->name)&&(wptr->type != W_PUSHBUTTON))
	    {
		Widget child;
		XmString label;
		XmString *str_list;
		int list_cnt;
		Cardinal argcnt;
		Arg arg[5];
		char *val;

		cbdata.attribute_names[cnt] = wptr->name;
		switch(wptr->type)
		{
			case W_TEXTFIELD:
				cbdata.attribute_values[cnt] =
					XmTextFieldGetString(wptr->w);
				if ((cbdata.attribute_values[cnt] != NULL)&&
				    (cbdata.attribute_values[cnt][0] == '\0'))
				{
					cbdata.attribute_values[cnt] = NULL;
				}
				break;
			case W_TEXTAREA:
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNworkWindow, &child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);
				cbdata.attribute_values[cnt] =
					XmTextGetString(child);
				if ((cbdata.attribute_values[cnt] != NULL)&&
				    (cbdata.attribute_values[cnt][0] == '\0'))
				{
					cbdata.attribute_values[cnt] = NULL;
				}
				break;
			case W_PASSWORD:
				cbdata.attribute_values[cnt] = wptr->password;
				if ((cbdata.attribute_values[cnt] != NULL)&&
				    (cbdata.attribute_values[cnt][0] == '\0'))
				{
					cbdata.attribute_values[cnt] = NULL;
				}
				break;
			case W_LIST:
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNworkWindow, &child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNselectedItemCount,
					&list_cnt);
				argcnt++;
				XtSetArg(arg[argcnt], XmNselectedItems,
					&str_list);
				argcnt++;
				XtGetValues(child, arg, argcnt);
				if (list_cnt == 0)
				{
					cnt--;
					cbdata.attribute_count--;
				}
				else /* list_cnt >= 1 */
				{
					int j, new_cnt;
					char **names;
					char **values;

					if (list_cnt > 1)
					{
					    new_cnt = cbdata.attribute_count +
						list_cnt - 1;
					    names = (char **)malloc(new_cnt *
						sizeof(char *));
					    values = (char **)malloc(new_cnt *
						sizeof(char *));
					    for (j=0; j<cnt; j++)
					    {
						names[j] =
						    cbdata.attribute_names[j];
						values[j] =
						    cbdata.attribute_values[j];
					    }
					    free((char *)
						cbdata.attribute_names);
					    free((char *)
						cbdata.attribute_values);
					    cbdata.attribute_names = names;
					    cbdata.attribute_values = values;
					    cbdata.attribute_count = new_cnt;
					}

					for (j=0; j<list_cnt; j++)
					{
						cbdata.attribute_names[cnt + j]
							= wptr->name;
						XmStringGetLtoR(str_list[j],
						    XmSTRING_DEFAULT_CHARSET,
						    &val);
						if ((val != NULL)&&
							(val[0] == '\0'))
						{
							val = NULL;
						}
						cbdata.attribute_values[cnt + j]
							= val;
					}
					cnt = cnt + list_cnt - 1;
				}
				break;
			/*
			 * For an option menu, first get the label gadget
			 * which holds the current value.
			 * Now get the text from that label as a character
			 * string.
			 */
			case W_OPTIONMENU:
				child = XmOptionButtonGadget(wptr->w);
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNlabelString, &label);
				argcnt++;
				XtGetValues(child, arg, argcnt);
				val = NULL;
				XmStringGetLtoR(label, XmSTRING_DEFAULT_CHARSET,
					&val);
				cbdata.attribute_values[cnt] = val;
				if ((cbdata.attribute_values[cnt] != NULL)&&
				    (cbdata.attribute_values[cnt][0] == '\0'))
				{
					cbdata.attribute_values[cnt] = NULL;
				}
				break;
			case W_CHECKBOX:
			case W_RADIOBOX:
				if (XmToggleButtonGetState(wptr->w) == True)
				{
				    cbdata.attribute_values[cnt] = wptr->value;
				}
				else
				{
				    cnt--;
				    cbdata.attribute_count--;
				}
				break;
			case W_JOT:
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNuserData,
					(XtPointer *)&child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);
				cbdata.attribute_values[cnt] =
					EJB_JOTfromJot(child);
				break;
			default:
				cbdata.attribute_values[cnt] = NULL;
				break;
		}
		cnt++;
	    }
	    else
	    {
		cbdata.attribute_count--;
	    }
	    wptr = wptr->next;
	}
	cbdata.attribute_count = cnt;

	*name_list = cbdata.attribute_names;
	*value_list = cbdata.attribute_values;
	return(cbdata.attribute_count);
}


void
ImageSubmitForm(fptr, event, name, x, y)
	FormInfo *fptr;
	XEvent *event;
	char *name;
	int x, y;
{
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WbFormCallbackData cbdata;
	int i, cnt;
	char **name_list;
	char **value_list;
	char valstr[100];

	cbdata.event = event;
	cbdata.href = fptr->action;
        cbdata.method = fptr->method;
        cbdata.enctype = fptr->enctype;

	name_list = NULL;
	value_list = NULL;
	cnt = CollectSubmitInfo(fptr, &name_list, &value_list);

	cbdata.attribute_count = cnt + 2;
	cbdata.attribute_names = (char **)malloc(cbdata.attribute_count *
		sizeof(char *));
	cbdata.attribute_values = (char **)malloc(cbdata.attribute_count *
		sizeof(char *));
	for (i=0; i<cnt; i++)
	{
		cbdata.attribute_names[i] = name_list[i];
		cbdata.attribute_values[i] = value_list[i];
	}
	if (name_list != NULL)
	{
		free((char *)name_list);
	}
	if (value_list != NULL)
	{
		free((char *)value_list);
	}

	if ((name != NULL)&&(name[0] != '\0'))
	{
		cbdata.attribute_names[cnt] = (char *)malloc(strlen(name) +
			strlen(X_NAME) + 2);
		strcpy(cbdata.attribute_names[cnt], name);
		strcat(cbdata.attribute_names[cnt], ".");
		strcat(cbdata.attribute_names[cnt], X_NAME);
	}
	else
	{
		cbdata.attribute_names[cnt] = (char *)malloc(strlen(X_NAME) +1);
		strcpy(cbdata.attribute_names[cnt], X_NAME);
	}
	sprintf(valstr, "%d", x);
	cbdata.attribute_values[cnt] = (char *)malloc(strlen(valstr) + 1);
	strcpy(cbdata.attribute_values[cnt], valstr);

	cnt++;
	if ((name != NULL)&&(name[0] != '\0'))
	{
		cbdata.attribute_names[cnt] = (char *)malloc(strlen(name) +
			strlen(Y_NAME) + 2);
		strcpy(cbdata.attribute_names[cnt], name);
		strcat(cbdata.attribute_names[cnt], ".");
		strcat(cbdata.attribute_names[cnt], Y_NAME);
	}
	else
	{
		cbdata.attribute_names[cnt] = (char *)malloc(strlen(Y_NAME) +1);
		strcpy(cbdata.attribute_names[cnt], Y_NAME);
	}
	sprintf(valstr, "%d", y);
	cbdata.attribute_values[cnt] = (char *)malloc(strlen(valstr) + 1);
	strcpy(cbdata.attribute_values[cnt], valstr);

	XtCallCallbackList ((Widget)hw, hw->html.form_callback,
		(XtPointer)&cbdata);
}


void
CBSubmitForm(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	FormInfo *fptr = (FormInfo *)client_data;
	XmPushButtonCallbackStruct *pb =
		(XmPushButtonCallbackStruct *)call_data;
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WbFormCallbackData cbdata;

	cbdata.event = pb->event;
	cbdata.href = fptr->action;
        cbdata.method = fptr->method;
        cbdata.enctype = fptr->enctype;

	cbdata.attribute_count = CollectSubmitInfo(fptr,
		&cbdata.attribute_names, &cbdata.attribute_values);

	XtCallCallbackList ((Widget)hw, hw->html.form_callback,
		(XtPointer)&cbdata);
}


/*
 * A radio buttom was toggled on in a form.
 * If there are other radios of the same name, turn them off.
 */
void
CBChangeRadio(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	FormInfo *fptr = (FormInfo *)client_data;
	XmToggleButtonCallbackStruct *tb =
		(XmToggleButtonCallbackStruct *)call_data;
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WidgetInfo *wptr;
	WidgetInfo *wtmp;
	char *name;
	int cnt, count;

	/*
	 * Bad button
	 */
	if (tb == NULL)
	{
		return;
	}

	/*
	 * Only do stuff when the button is turned on.
	 * Don't let the button be turned off, by clicking on
	 * it, as that would leave all buttons off.
	 */
	if ((tb == NULL)||(tb->set == False))
	{
		XmToggleButtonSetState(w, True, False);
		return;
	}

	/*
	 * Terminate the form if it was never properly terminated.
	 */
	if (fptr->end == -1)  /* unterminated FORM tag */
	{
		wptr = hw->html.widget_list;
		cnt = 0;
		while (wptr != NULL)
		{
			cnt++;
			wptr = wptr->next;
		}
		count = cnt;
	}
	else
	{
		count = fptr->end - fptr->start;
	}

	/*
	 * Locate the start of the form.
	 */
	if (fptr->start == 0)
	{
		wptr = hw->html.widget_list;
	}
	else
	{
		wptr = hw->html.widget_list;
		while (wptr != NULL)
		{
			if (wptr->id == fptr->start)
			{
				wptr = wptr->next;
				break;
			}
			wptr = wptr->next;
		}
	}

	/*
	 * Find the name of the toggle button just pressed.
	 */
	name = NULL;
	wtmp = wptr;
	while (wtmp != NULL)
	{
		if (wtmp->w == w)
		{
			name = wtmp->name;
			break;
		}
		wtmp = wtmp->next;
	}

	/*
	 * Check for other checked radioboxes of the same name.
	 */
	cnt = 0;
	while ((wptr != NULL)&&(cnt < count))
	{
		if ((wptr->type == W_RADIOBOX)&&
			(wptr->w != w)&&
			(XmToggleButtonGetState(wptr->w) == True)&&
			(wptr->name != NULL)&&
			(name != NULL)&&
			(strcmp(wptr->name, name) == 0))
		{
			XmToggleButtonSetState(wptr->w, False, False);
		}
		cnt++;
		wptr = wptr->next;
	}
}


/*
 * Catch all attempted modifications to the textfield for password
 * entry.  This is so we can prevent the password from showing
 * uponm the screen.
 * I would prefer that for all insereted characters a random 1-3 '*'s
 * were added, and any delete deleted the whole string, but due to
 * bugs in somve version of Motif 1.1 this won't work.
 */
void
CBPasswordModify(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	FormInfo *fptr = (FormInfo *)client_data;
	XmTextVerifyCallbackStruct *tv =(XmTextVerifyCallbackStruct *)call_data;
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WidgetInfo *wptr;
	int i, len;

	/*
	 * by default accept nothing
	tv->doit = False;
	 */

	/*
	 * Ignore when ModifyIgnore is true
	 */
	if (ModifyIgnore == True)
	{
		return;
	}

	/*
	 * only accept text modification of password fields
	 */
	if (tv->reason != XmCR_MODIFYING_TEXT_VALUE)
	{
		return;
	}

	/*
	 * find the structure for this widget
	 */
	wptr = hw->html.widget_list;
	while (wptr != NULL)
	{
		if (wptr->w == w)
		{
			break;
		}
		wptr = wptr->next;
	}
	if (wptr == NULL)
	{
		return;
	}

	/*
	 * Deletion.
	 */
	if (tv->text->ptr == NULL)
	{
		tv->doit = True;

		/*
		 * Only can delete if we have stuff to delete.
		 */
		if ((wptr->password != NULL)&&(wptr->password[0] != '\0'))
		{
			int start;
			char *tptr;

			len = strlen(wptr->password);
			/*
			 * Find the start of the chunk of text to
			 * delete.
			 */
			if (tv->startPos < len)
			{
				start = tv->startPos;
			}
			else
			{
				start = len - 1;
			}

			/*
			 * might be more stuff after the end that we
			 * want to move up
			 */
			if (tv->endPos > len)
			{
				tptr = &(wptr->password[len]);
			}
			else
			{
				tptr = &(wptr->password[tv->endPos]);
			}
			wptr->password[start] = '\0';
			strcat(wptr->password, tptr);
		}
	}
	/*
	 * Else insert character.
	 */
	else if (tv->text->length >= 1)
	{
		int i, maxlength, plen;
		Cardinal argcnt;
		Arg arg[5];

		/*
		 * No insertion if it makes you exceed maxLength
		 */
		if (wptr->password == NULL)
		{
			plen = 0;
		}
		else
		{
			plen = strlen(wptr->password);
		}
		maxlength = 1000000;
		argcnt = 0;
		XtSetArg(arg[argcnt], XmNmaxLength, &maxlength); argcnt++;
		XtGetValues(w, arg, argcnt);
		if ((plen + tv->text->length) > maxlength)
		{
			return;
		}

		if (wptr->password == NULL)
		{
			wptr->password = (char *)malloc(tv->text->length + 1);
			for (i=0; i < tv->text->length; i++)
			{
				wptr->password[i] = tv->text->ptr[i];
			}
			wptr->password[tv->text->length] = '\0';
		}
		/*
		 * else insert a char somewhere.
		 * Make a new buffer.  Put everything from before the insert
		 * postion into it.  Now insert the character.
		 * Finally append any remaining text.
		 */
		else
		{
			char *buf;
			char *tptr;
			char tchar;
			int start;

			len = strlen(wptr->password);
			if (tv->startPos < len)
			{
				start = tv->startPos;
			}
			else
			{
				start = len;
			}
			tptr = &(wptr->password[start]);
			tchar = *tptr;
			*tptr = '\0';
			buf = (char *)malloc(len + tv->text->length + 1);
			strcpy(buf, wptr->password);
			for (i=0; i < tv->text->length; i++)
			{
				buf[start + i] = tv->text->ptr[i];
			}
			buf[start + tv->text->length] = '\0';
			*tptr = tchar;
			strcat(buf, tptr);
			free(wptr->password);
			wptr->password = buf;
		}

		tv->doit = True;
		/*
		 * make a '*' show up instead of what they typed
		 */
		for (i=0; i < tv->text->length; i++)
		{
			tv->text->ptr[i] = '*';
		}
	}
}



/*
 * RETURN was hit in a textfield in a form.
 * If this is the only textfield in this form, submit the form.
 */
void
CBActivateField(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	FormInfo *fptr = (FormInfo *)client_data;
	XmAnyCallbackStruct *cb = (XmAnyCallbackStruct *)call_data;
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WidgetInfo *wptr;
	int cnt, count;

	/*
	 * Terminate the form if it was never properly terminated.
	 */
	if (fptr->end == -1)  /* unterminated FORM tag */
	{
		wptr = hw->html.widget_list;
		cnt = 0;
		while (wptr != NULL)
		{
			cnt++;
			wptr = wptr->next;
		}
		count = cnt;
	}
	else
	{
		count = fptr->end - fptr->start;
	}

	/*
	 * Locate the start of the form.
	 */
	if (fptr->start == 0)
	{
		wptr = hw->html.widget_list;
	}
	else
	{
		wptr = hw->html.widget_list;
		while (wptr != NULL)
		{
			if (wptr->id == fptr->start)
			{
				wptr = wptr->next;
				break;
			}
			wptr = wptr->next;
		}
	}

	/*
	 * Count the textfields in this form.
	 */
	cnt = 0;
	while ((wptr != NULL)&&(cnt < count))
	{
		if ((wptr->type == W_TEXTFIELD)||(wptr->type == W_PASSWORD))
		{
			cnt++;
		}
		wptr = wptr->next;
	}

	/*
	 * If this is the only textfield in this form, submit the form.
	 */
	if (cnt == 1)
	{
		CBSubmitForm(w, client_data, call_data);
	}
}


void
CBResetForm(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	FormInfo *fptr = (FormInfo *)client_data;
	XmPushButtonCallbackStruct *pb =
		(XmPushButtonCallbackStruct *)call_data;
	HTMLWidget hw = (HTMLWidget)(fptr->hw);
	WidgetInfo *wptr;
	int widget_count, cnt;

	if (fptr->end == -1)  /* unterminated FORM tag */
	{
		wptr = hw->html.widget_list;
		cnt = 0;
		while (wptr != NULL)
		{
			cnt++;
			wptr = wptr->next;
		}
		widget_count = cnt;
	}
	else
	{
		widget_count = fptr->end - fptr->start;
	}

	if (fptr->start == 0)
	{
		wptr = hw->html.widget_list;
	}
	else
	{
		wptr = hw->html.widget_list;
		while (wptr != NULL)
		{
			if (wptr->id == fptr->start)
			{
				wptr = wptr->next;
				break;
			}
			wptr = wptr->next;
		}
	}

	cnt = 0;
	while ((wptr != NULL)&&(cnt < widget_count))
	{
		Widget child;
		Cardinal argcnt;
		Arg arg[5];
		XmString label;
		switch(wptr->type)
		{
			case W_TEXTFIELD:
				if (wptr->value == NULL)
				{
				    XmTextFieldSetString(wptr->w, "");
				}
				else
				{
				    XmTextFieldSetString(wptr->w, wptr->value);
				}
				break;
			case W_TEXTAREA:
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNworkWindow, &child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);
				if (wptr->value == NULL)
				{
				    XmTextSetString(child, "");
				}
				else
				{
				    XmTextSetString(child, wptr->value);
				}
				break;
			case W_PASSWORD:
				if (wptr->value == NULL)
				{
				    /*
				     * Due to errors in Motif1.1, I can't
				     * call XmTextFieldSetString() here.
				     * Because I have a modifyVerify callback
				     * registered for this widget.
				     * I don't know if this error exists
				     * in Motif1.2 or not.
				     */
				    argcnt = 0;
				    XtSetArg(arg[argcnt], XmNvalue, "");
				    argcnt++;
				    XtSetValues(wptr->w, arg, argcnt);
				    if (wptr->password != NULL)
				    {
					free(wptr->password);
					wptr->password = NULL;
				    }
				}
				else
				{
				    int i, len;

				    if (wptr->password != NULL)
				    {
					free(wptr->password);
					wptr->password = NULL;
				    }
				    len = strlen(wptr->value);
				    wptr->password = (char *)malloc(len + 1);
				    for (i=0; i<len; i++)
				    {
					wptr->password[i] = '*';
				    }
				    wptr->password[len] = '\0';
				    XmTextFieldSetString(wptr->w,
					wptr->password);
				    strcpy(wptr->password, wptr->value);
				}
				break;
			case W_LIST:
			    {
				char **vlist;
				int vlist_cnt;
				XmString *val_list;
				XmString label;
				int i;

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNworkWindow, &child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);

				if (wptr->value != NULL)
				{
				    vlist = ParseCommaList(wptr->value,
					&vlist_cnt);
				    val_list = (XmString *)malloc(vlist_cnt *
					sizeof(XmString));
				    XmListDeselectAllItems(child);
				    for (i=0; i<vlist_cnt; i++)
				    {
					val_list[i] =
						XmStringCreateSimple(vlist[i]);
				    }
				    FreeCommaList(vlist, vlist_cnt);
				    if (vlist_cnt > 0)
				    {
					argcnt = 0;
					XtSetArg(arg[argcnt], XmNselectedItems,
						val_list);
					argcnt++;
					XtSetArg(arg[argcnt],
						XmNselectedItemCount,
						vlist_cnt);
					argcnt++;
					XtSetValues(child, arg, argcnt);
				    }
				    for (i=0; i<vlist_cnt; i++)
				    {
					XmStringFree(val_list[i]);
				    }
				    if (val_list != NULL)
				    {
					free((char *)val_list);
				    }
				}
				else
				{
					XmListDeselectAllItems(child);
				}
			    }
				break;
			/*
			 * gack, we saved the widget id of the starting default
			 * into the value character pointer, just so we could
			 * yank it out here, and restore the default.
			 */
			case W_OPTIONMENU:
				if (wptr->value != NULL)
				{
					Widget hist = (Widget)wptr->value;
					Cardinal argcnt;
					Arg arg[5];

					argcnt = 0;
					XtSetArg(arg[argcnt], XmNmenuHistory,
						hist);
					argcnt++;
					XtSetValues(wptr->w, arg, argcnt);
				}
				break;
			case W_CHECKBOX:
			case W_RADIOBOX:
				if (wptr->checked == True)
				{
				  XmToggleButtonSetState(wptr->w, True, False);
				}
				else
				{
				  XmToggleButtonSetState(wptr->w, False, False);
				}
				break;
			case W_JOT:
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNuserData,
					(XtPointer *)&child);
				argcnt++;
				XtGetValues(wptr->w, arg, argcnt);
				ClearJot(hw, child, wptr->width, wptr->height);
				break;
			default:
				break;
		}
		cnt++;
		wptr = wptr->next;
	}
}


void
PrepareFormEnd(hw, w, fptr)
	HTMLWidget hw;
	Widget w;
	FormInfo *fptr;
{
	XtAddCallback(w, XmNactivateCallback, 
                      (XtCallbackProc)CBSubmitForm, (caddr_t)fptr);
}


void
PrepareFormReset(hw, w, fptr)
	HTMLWidget hw;
	Widget w;
	FormInfo *fptr;
{
	XtAddCallback(w, XmNactivateCallback, 
                      (XtCallbackProc)CBResetForm, (caddr_t)fptr);
}


void
HideWidgets(hw)
	HTMLWidget hw;
{
	WidgetInfo *wptr;
	XEvent event;

	/*
	 * Make sure all expose events have been dealt with first.
	 */
	XmUpdateDisplay((Widget)hw);

	wptr = hw->html.widget_list;
	while (wptr != NULL)
	{
		if (wptr->w != NULL)
		{
			XtSetMappedWhenManaged(wptr->w, False);
			wptr->mapped = False;
		}
		wptr = wptr->next;
	}

	/*
	 * Force the exposure events into the queue
	 */
	XSync(XtDisplay(hw), False);

	/*
	 * Remove all Expose events for the view window
	 */
	while (XCheckWindowEvent(XtDisplay(hw->html.view),
		XtWindow(hw->html.view), ExposureMask, &event) == True)
	{
	}
}


void
MapWidgets(hw)
	HTMLWidget hw;
{
	WidgetInfo *wptr;

	wptr = hw->html.widget_list;
	while (wptr != NULL)
	{
		if (wptr->w != NULL)
		{
			wptr->mapped = True;
			XtSetMappedWhenManaged(wptr->w, True);
		}
		wptr = wptr->next;
	}
}


Boolean
AlreadyChecked(hw, fptr, name)
	HTMLWidget hw;
	FormInfo *fptr;
	char *name;
{
	WidgetInfo *wptr;
	Boolean radio_checked;

	radio_checked = False;
	wptr = hw->html.widget_list;
	while (wptr != NULL)
	{
		if ((wptr->id >= fptr->start)&&
			(wptr->type == W_RADIOBOX)&&
			(wptr->checked == True)&&
			(wptr->name != NULL)&&
			(name != NULL)&&
			(strcmp(wptr->name, name) == 0))
		{
			radio_checked = True;
			break;
		}
		wptr = wptr->next;
	}
	return(radio_checked);
}


WidgetInfo *
AddNewWidget(hw, fptr, w, type, id, x, y, width, height, name, value, checked)
	HTMLWidget hw;
	FormInfo *fptr;
	Widget w;
	int type;
	int id;
	int x, y;
	int width, height;
	char *name;
	char *value;
	Boolean checked;
{
	WidgetInfo *wptr;

	wptr = hw->html.widget_list;
	if (wptr == NULL)
	{
		wptr = (WidgetInfo *)malloc(sizeof(WidgetInfo));
		wptr->w = w;
		wptr->type = type;
		wptr->id = id;
		wptr->x = x;
		wptr->y = y;
		wptr->width = width;
		wptr->height = height;
		wptr->name = name;
		wptr->value = value;
		wptr->password = NULL;
		wptr->checked = checked;
		wptr->mapped = False;
		wptr->next = NULL;
		hw->html.widget_list = wptr;
	}
	else
	{
		while (wptr->next != NULL)
		{
			wptr = wptr->next;
		}
		wptr->next = (WidgetInfo *)malloc(sizeof(WidgetInfo));
		wptr = wptr->next;
		wptr->w = w;
		wptr->type = type;
		wptr->id = id;
		wptr->x = x;
		wptr->y = y;
		wptr->width = width;
		wptr->height = height;
		wptr->name = name;
		wptr->value = value;
		wptr->password = NULL;
		wptr->checked = checked;
		wptr->mapped = False;
		wptr->next = NULL;
	}

	if ((wptr->type == W_PASSWORD)&&(wptr->value != NULL))
	{
		wptr->password = (char *)malloc(strlen(wptr->value) + 1);
		strcpy(wptr->password, wptr->value);
	}

	return(wptr);
}


/*
 * For the various widgets, return their fon structures so
 * we can use the font's baseline to place them.
 */
XFontStruct *
GetWidgetFont(hw, wptr)
	HTMLWidget hw;
	WidgetInfo *wptr;
{
	Boolean ret;
	Widget child;
	Cardinal argcnt;
	Arg arg[5];
	XmFontList font_list = (XmFontList)NULL;
	XmFontContext font_context;
	XmStringCharSet charset;
	XFontStruct *font;

	/*
	 * For option menus we have to first get the child that has the
	 * font info.
	 */
	if (wptr->type == W_OPTIONMENU)
	{
		Widget child;

		child = XmOptionButtonGadget(wptr->w);

		argcnt = 0;
		XtSetArg(arg[argcnt], XmNfontList, &font_list); argcnt++;
		XtGetValues(child, arg, argcnt);
	}
	else
	{
		Widget child;

		if ((wptr->type == W_TEXTAREA)||(wptr->type == W_LIST))
		{
			child = NULL;
			argcnt = 0;
			XtSetArg(arg[argcnt], XmNworkWindow, &child); argcnt++;
			XtGetValues(wptr->w, arg, argcnt);
			argcnt = 0;
			XtSetArg(arg[argcnt], XmNfontList,&font_list); argcnt++;
			XtGetValues(child, arg, argcnt);
		}
		else
		{
			argcnt = 0;
			XtSetArg(arg[argcnt], XmNfontList,&font_list); argcnt++;
			XtGetValues(wptr->w, arg, argcnt);
		}
	}

	if (font_list == (XmFontList)NULL)
	{
		return((XFontStruct *)NULL);
	}

	ret = XmFontListInitFontContext(&font_context, font_list);
	if (ret == False)
	{
		return((XFontStruct *)NULL);
	}

	ret = XmFontListGetNextFont(font_context, &charset, &font);
	if (ret == False)
	{
		return((XFontStruct *)NULL);
	}
	else
	{
		XmFontListFreeFontContext(font_context);
		free((char *)charset);
		return(font);
	}
}


/*
 * Get the next value in a comma separated list.
 * Also unescape the '\' escaping done in ComposeCommaList
 * and convert the single ''' characters back to '"'
 * characters
 */
char *
NextComma(string)
        char *string;
{
        char *tptr;

        tptr = string;
        while (*tptr != '\0')
        {
                if (*tptr == '\\')
                {
                        *tptr = '\0';
                        strcat(string, (char *)(tptr + 1));
                        tptr++;
                }
                else if (*tptr == '\'')
                {
                        *tptr = '\"';
                        tptr++;
                }
                else if (*tptr == ',')
                {
                        return(tptr);
                }
                else
                {
                        tptr++;
                }
        }
        return(tptr);
}


char **
ParseCommaList(str, count)
	char *str;
	int *count;
{
	char *str_copy;
	char **list;
	char **tlist;
	char *tptr;
	char *val;
	int i, cnt;
	int max_cnt;

	*count = 0;
	if ((str == NULL)||(*str == '\0'))
	{
		return((char **)NULL);
	}
	str_copy = (char *)malloc(strlen(str) + 1);
	if (str_copy == NULL)
	{
		return((char **)NULL);
	}
	strcpy(str_copy, str);

	list = (char **)malloc(50 * sizeof(char *));
	if (list == NULL)
	{
		return((char **)NULL);
	}
	max_cnt = 50;

	/*
	 * This loop counts the number of objects
	 * in this list.
	 * As a side effect, NextComma() unescapes in place so
	 * "\\" becomes '\' and "\," becomes ',' and "\"" becomes '"'
	 */
	cnt = 0;
	val = str_copy;
	tptr = NextComma(val);
	while (*tptr != '\0')
	{
		if ((cnt + 1) == max_cnt)
		{
			tlist = (char **)malloc((max_cnt +50) * sizeof(char *));
			if (tlist == NULL)
			{
				return((char **)NULL);
			}
			for (i=0; i<cnt; i++)
			{
				tlist[i] = list[i];
			}
			free((char *)list);
			list = tlist;
			max_cnt += 50;
		}
		*tptr = '\0';
		list[cnt] = (char *)malloc(strlen(val) + 1);
		if (list[cnt] == NULL)
		{
			return((char **)NULL);
		}
		strcpy(list[cnt], val);
		cnt++;

		val = (char *)(tptr + 1);
		tptr = NextComma(val);
	}
	list[cnt] = (char *)malloc(strlen(val) + 1);
	if (list[cnt] == NULL)
	{
		return((char **)NULL);
	}
	strcpy(list[cnt], val);
	cnt++;

	free(str_copy);
	tlist = (char **)malloc(cnt * sizeof(char *));
	if (tlist == NULL)
	{
		return((char **)NULL);
	}
	for (i=0; i<cnt; i++)
	{
		tlist[i] = list[i];
	}
	free((char *)list);
	list = tlist;

	*count = cnt;
	return(list);
}


/*
 * Compose a single string comma separated list from
 * an array of strings.  Any '\', or ',' in the
 * list are escaped with a prepending '\'.
 * So they become '\\' and '\,'
 * Also we want to allow '"' characters in the list, but
 * they would get eaten by the later parsing code, so we will
 * turn '"' into ''', and turn ''' into '\''
 */
char *
ComposeCommaList(list, cnt)
	char **list;
	int cnt;
{
	int i;
	char *fail;
	char *buf;
	char *tbuf;
	int len, max_len;

	fail = (char *)malloc(1);
	*fail = '\0';

	if (cnt == 0)
	{
		return(fail);
	}

	buf = (char *)malloc(1024);
	if (buf == NULL)
	{
		return(fail);
	}
	max_len = 1024;
	len = 0;
	buf[0] = '\0';

	for (i=0; i<cnt; i++)
	{
		char *option;
		char *tptr;
		int olen;

		option = list[i];
		if (option == NULL)
		{
			olen = 0;
		}
		else
		{
			olen = strlen(option);
		}
		if ((len + olen + 2) > max_len)
		{
			tbuf = (char *)malloc(max_len + olen + 1024);
			if (tbuf == NULL)
			{
				return(fail);
			}
			strcpy(tbuf, buf);
			free(buf);
			buf = tbuf;
			max_len = max_len + olen + 1024;
		}
		tptr = (char *)(buf + len);
		while ((option != NULL)&&(*option != '\0'))
		{
			if ((*option == '\\')||(*option == ',')||
				(*option == '\''))
			{
				*tptr++ = '\\';
				*tptr++ = *option++;
				len += 2;
			}
			else if (*option == '\"')
			{
				*tptr++ = '\'';
				option++;
				len++;
			}
			else
			{
				*tptr++ = *option++;
				len++;
			}
		}
		if (i != (cnt - 1))
		{
			*tptr++ = ',';
			len++;
		}
		*tptr = '\0';
	}

	tbuf = (char *)malloc(len + 1);
	if (tbuf == NULL)
	{
		return(fail);
	}
	strcpy(tbuf, buf);
	free(buf);
	buf = tbuf;
	free(fail);
	return(buf);
}


void
FreeCommaList(list, cnt)
	char **list;
	int cnt;
{
	int i;

	for (i=0; i<cnt; i++)
	{
		if (list[i] != NULL)
		{
			free(list[i]);
		}
	}
	if (list != NULL)
	{
		free((char *)list);
	}
}


/*
 * Clean up the mucked value field for a TEXTAREA.
 * Unescape the things with '\' in front of them, and transform
 * lone ' back to "
 */
void
UnMuckTextAreaValue(value)
	char *value;
{
	char *tptr;

	if ((value == NULL)||(value[0] == '\0'))
	{
		return;
	}

	tptr = value;
        while (*tptr != '\0')
        {
                if (*tptr == '\\')
                {
                        *tptr = '\0';
                        strcat(value, (char *)(tptr + 1));
                        tptr++;
                }
                else if (*tptr == '\'')
                {
                        *tptr = '\"';
                        tptr++;
                }
                else
                {
                        tptr++;
                }
        }
}


/*
 * Make the appropriate widget for this tag, and fill in an
 * WidgetInfo structure and return it.
 */
WidgetInfo *
MakeWidget(hw, text, x, y, id, fptr)
	HTMLWidget hw;
	char *text;
	int x, y;
	int id;
	FormInfo *fptr;
{
	Arg arg[30];
	Cardinal argcnt;
	Widget w;
	WidgetInfo *wlist;
	WidgetInfo *wptr;
	Dimension width, height;

	wlist = hw->html.widget_list;
	while (wlist != NULL)
	{
		if (wlist->id == id)
		{
			break;
		}
		wlist = wlist->next;
	}

	/*
	 * If this widget is not on the list, we have never
	 * used it before.  Create it now.
	 */
	if (wlist == NULL)
	{
		char *tptr;
		char *value;
		char *name;
		char *type_str;
		int type;
		short size;
		int maxlength;
		Boolean checked;

		checked = False;
		name = ParseMarkTag(text, MT_INPUT, "NAME");

		type_str = ParseMarkTag(text, MT_INPUT, "TYPE");
		if ((type_str != NULL)&&(strcmp(type_str, "checkbox") == 0))
		{
			type = W_CHECKBOX;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			if (value == NULL)
			{
				value = (char *)malloc(strlen("on") + 1);
				strcpy(value, "on");
			}

			tptr = ParseMarkTag(text, MT_INPUT, "CHECKED");

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			if (tptr != NULL)
			{
				XtSetArg(arg[argcnt], XmNset, True); argcnt++;
				checked = True;
				free(tptr);
			}
			w = XmCreateToggleButton(hw->html.view, "",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "radio") == 0))
		{
			type = W_RADIOBOX;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			if (value == NULL)
			{
				value = (char *)malloc(strlen("on") + 1);
				strcpy(value, "on");
			}

			/*
			 * Only one checked radio button with the
			 * same name per form
			 */
			tptr = ParseMarkTag(text, MT_INPUT, "CHECKED");
			if ((tptr != NULL)&&
				(AlreadyChecked(hw, fptr, name) == True))
			{
				free(tptr);
				tptr = NULL;
			}

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			XtSetArg(arg[argcnt], XmNindicatorType, XmONE_OF_MANY);
			argcnt++;
			if (tptr != NULL)
			{
				XtSetArg(arg[argcnt], XmNset, True); argcnt++;
				checked = True;
				free(tptr);
			}
			w = XmCreateToggleButton(hw->html.view, "",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			XtAddCallback(w, XmNvalueChangedCallback,
				(XtCallbackProc)CBChangeRadio, (caddr_t)fptr);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "submit") == 0))
		{
			XmString label;

			type = W_PUSHBUTTON;
			label = NULL;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			if ((value == NULL)||(*value == '\0'))
			{
				value = (char *)malloc(strlen("Submit Query") +
					1);
				strcpy(value, "Submit Query");
			}

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			if (value != NULL)
			{
				label = XmStringCreateSimple(value);
				XtSetArg(arg[argcnt], XmNlabelString, label);
				argcnt++;
			}
			w = XmCreatePushButton(hw->html.view, "Button",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			if (label != NULL)
			{
				XmStringFree(label);
			}
			PrepareFormEnd(hw, w, fptr);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "reset") == 0))
		{
			XmString label;

			type = W_PUSHBUTTON;
			label = NULL;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			if ((value == NULL)||(*value == '\0'))
			{
				value = (char *)malloc(strlen("Reset") + 1);
				strcpy(value, "Reset");
			}

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			if (value != NULL)
			{
				label = XmStringCreateSimple(value);
				XtSetArg(arg[argcnt], XmNlabelString, label);
				argcnt++;
			}
			w = XmCreatePushButton(hw->html.view, "Button",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			if (label != NULL)
			{
				XmStringFree(label);
			}
			PrepareFormReset(hw, w, fptr);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "button") == 0))
		{
			XmString label;

			type = W_PUSHBUTTON;
			label = NULL;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			if (value != NULL)
			{
				label = XmStringCreateSimple(value);
				XtSetArg(arg[argcnt], XmNlabelString, label);
				argcnt++;
			}
			w = XmCreatePushButton(hw->html.view, "Button",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			if (label != NULL)
			{
				XmStringFree(label);
			}
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "jot") == 0))
		{
			XmString label;
			Dimension width, height;
			Widget frame;
			char **list;
			int list_cnt;

			type = W_JOT;
			label = NULL;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");

			/*
			 * SIZE is WIDTH,HEIGHT
			 */
			tptr = ParseMarkTag(text, MT_INPUT, "SIZE");
			list = ParseCommaList(tptr, &list_cnt);
			if (tptr != NULL)
			{
				free(tptr);
			}

			width = 200;
			height = 50;
			if (list_cnt == 1)
			{
				width = atoi(list[0]);
			}
			else if (list_cnt > 1)
			{
				width = atoi(list[0]);
				height = atoi(list[1]);
			}
			FreeCommaList(list, list_cnt);

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			XtSetArg(arg[argcnt], XmNshadowType, XmSHADOW_IN);
			argcnt++;
			frame = XmCreateFrame(hw->html.view, "Frame",
				arg, argcnt);

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNwidth, width); argcnt++;
			XtSetArg(arg[argcnt], XmNheight, height); argcnt++;
			w = XmCreateDrawingArea(frame, "Draw",
				arg, argcnt);
			XtManageChild(w);

			NewJot(w, width, height);
			XtAddEventHandler(w, ExposureMask, 0,
				EVJotExpose, (XtPointer)hw);
			XtAddEventHandler(w, ButtonPressMask, 0,
				EVJotPress, (XtPointer)hw);
			XtAddEventHandler(w, ButtonMotionMask, 0,
				EVJotMove, (XtPointer)hw);
			XtAddEventHandler(w, ButtonReleaseMask, 0,
				EVJotRelease, (XtPointer)hw);

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNuserData, (XtPointer)w);
			argcnt++;
			XtSetValues(frame, arg, argcnt);

			w = frame;

			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			if (label != NULL)
			{
				XmStringFree(label);
			}
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "select") == 0))
		{
			XmString label;
			Widget scroll;
			Widget pulldown, button, hist;
			char *options;
			char **list;
			int list_cnt;
			char **vlist;
			int vlist_cnt;
			int i, mult, size;

			type = -1;
			tptr = ParseMarkTag(text, MT_INPUT, "HINT");
			if ((tptr != NULL)&&(strcmp(tptr, "list") == 0))
			{
				type = W_LIST;
			}
			else if ((tptr != NULL)&&(strcmp(tptr, "menu") == 0))
			{
				type = W_OPTIONMENU;
			}
			if (tptr != NULL)
			{
				free(tptr);
			}

			size = 5;
			tptr = ParseMarkTag(text, MT_INPUT, "SIZE");
			if (tptr != NULL)
			{
				size = atoi(tptr);
				if ((size > 1)&&(type == -1))
				{
					type = W_LIST;
				}
				free(tptr);
			}

			mult = 0;
			tptr = ParseMarkTag(text, MT_INPUT, "MULTIPLE");
			if (tptr != NULL)
			{
				if (type == -1)
				{
					type = W_LIST;
				}
				mult = 1;
				free(tptr);
			}

			if (type == -1)
			{
				type = W_OPTIONMENU;
			}

			label = NULL;
			hist = NULL;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			options = ParseMarkTag(text, MT_INPUT, "OPTIONS");
			list = ParseCommaList(options, &list_cnt);
			if (options != NULL)
			{
				free(options);
			}

			vlist = ParseCommaList(value, &vlist_cnt);

			if (size > list_cnt)
			{
				size = list_cnt;
			}
			if (size < 1)
			{
				size = 1;
			}

			if (type == W_OPTIONMENU)
			{
                                Widget child;
                                XmString xmstr;
				argcnt = 0;
				pulldown = XmCreatePulldownMenu(hw->html.view,
					"menu", arg, argcnt);

				for (i=0; i<list_cnt; i++)
				{
					label = XmStringCreateSimple(list[i]);
					argcnt = 0;
					XtSetArg(arg[argcnt], XmNlabelString,
						label);
					argcnt++;
					button = XmCreatePushButton(pulldown,
						"Button", arg, argcnt);
					XtManageChild(button);
					XmStringFree(label);

					if ((vlist_cnt > 0)&&
						(vlist[0] != NULL)&&
						(strcmp(vlist[0], list[i]) ==0))
					{
						hist = button;
					}

					/*
					 * Start hist out as the first button
					 * so that if the user didn't set a
					 * default we always default to the
					 * first element.
					 */
					if ((i == 0)&&(hist == NULL))
					{
						hist = button;
					}
				}

				FreeCommaList(list, list_cnt);
				FreeCommaList(vlist, vlist_cnt);
				if (value != NULL)
				{
					free(value);
				}

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNx, x); argcnt++;
				XtSetArg(arg[argcnt], XmNy, y); argcnt++;
				XtSetArg(arg[argcnt], XmNsubMenuId, pulldown);
					argcnt++;
				if (hist != NULL)
				{
					XtSetArg(arg[argcnt], XmNmenuHistory,
						hist);
					argcnt++;
					/*
					 * A gaggage.  Value is used to later
					 * restore defaults.  For option menu
					 * this means we need to save a child
					 * widget id as opposed to the
					 * character string everyone else uses.
					 */
					value = (char *)hist;
				}
				w = XmCreateOptionMenu(hw->html.view, "Button",
					arg, argcnt);
                                argcnt = 0;
                                xmstr = XmStringCreateSimple ("");
                                XtSetArg(arg[argcnt], XmNlabelString,
                                         (XtArgVal)xmstr);
                                argcnt++;
                                child = XmOptionLabelGadget (w);
                                XtSetValues (child, arg, argcnt);
                                XmStringFree (xmstr);
                        }
			else /* type == W_LIST */
			{
				XmString *string_list;
				XmString *val_list;

				if ((!mult)&&(vlist_cnt > 1))
				{
					free(value);
					value = (char *)malloc(
						strlen(vlist[0]) + 1);
					strcpy(value, vlist[0]);
				}

				string_list = (XmString *)malloc(list_cnt *
					sizeof(XmString));
				val_list = (XmString *)malloc(vlist_cnt *
					sizeof(XmString));

				for (i=0; i<list_cnt; i++)
				{
					string_list[i] =
						XmStringCreateSimple(list[i]);
				}
				for (i=0; i<vlist_cnt; i++)
				{
					val_list[i] =
						XmStringCreateSimple(vlist[i]);
				}

				FreeCommaList(list, list_cnt);
				FreeCommaList(vlist, vlist_cnt);

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNx, x); argcnt++;
				XtSetArg(arg[argcnt], XmNy, y); argcnt++;
				scroll = XmCreateScrolledWindow(hw->html.view,
					"Scroll", arg, argcnt);

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNitems, string_list);
				argcnt++;
				XtSetArg(arg[argcnt], XmNitemCount, list_cnt);
				argcnt++;
				XtSetArg(arg[argcnt], XmNvisibleItemCount,size);
				argcnt++;
				if (mult)
				{
					XtSetArg(arg[argcnt],XmNselectionPolicy,
						XmEXTENDED_SELECT);
					argcnt++;
				}
				else
				{
					XtSetArg(arg[argcnt],XmNselectionPolicy,
						XmBROWSE_SELECT);
					argcnt++;
				}
				if ((vlist_cnt > 0)&&(mult))
				{
					XtSetArg(arg[argcnt], XmNselectedItems,
						val_list);
					argcnt++;
					XtSetArg(arg[argcnt],
						XmNselectedItemCount,
						vlist_cnt);
					argcnt++;
				}
				else if ((vlist_cnt > 0)&&(!mult))
				{
					XtSetArg(arg[argcnt], XmNselectedItems,
						&val_list[0]);
					argcnt++;
					XtSetArg(arg[argcnt],
						XmNselectedItemCount, 1);
					argcnt++;
				}
				w = XmCreateList(scroll, "List", arg, argcnt);
				XtManageChild(w);
				w = scroll;

				for (i=0; i<list_cnt; i++)
				{
					XmStringFree(string_list[i]);
				}
				if (string_list != NULL)
				{
					free((char *)string_list);
				}
				for (i=0; i<vlist_cnt; i++)
				{
					XmStringFree(val_list[i]);
				}
				if (val_list != NULL)
				{
					free((char *)val_list);
				}
			}
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "password") ==0))
		{
			type = W_PASSWORD;
			value = ParseMarkTag(text, MT_INPUT, "VALUE");

			size = -1;
			maxlength = -1;

			tptr = ParseMarkTag(text, MT_INPUT, "SIZE");
			if (tptr != NULL)
			{
				size = atoi(tptr);
				free(tptr);
			}

			tptr = ParseMarkTag(text, MT_INPUT, "MAXLENGTH");
			if (tptr != NULL)
			{
				maxlength = atoi(tptr);
				free(tptr);
			}

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			if (size > 0)
			{
				XtSetArg(arg[argcnt], XmNcolumns, size);
				argcnt++;
			}
			if (maxlength > 0)
			{
				XtSetArg(arg[argcnt], XmNmaxLength, maxlength);
				argcnt++;
			}
			if (value != NULL)
			{
				int i, len;
				char *bval;

				len = strlen(value);
				bval = (char *)malloc(len + 1);
				for (i=0; i<len; i++)
				{
					bval[i] = '*';
				}
				bval[len] = '\0';
				XtSetArg(arg[argcnt], XmNvalue, bval);
				argcnt++;
			}
			w = XmCreateTextField(hw->html.view, "Child",
				arg, argcnt);
			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
			XtAddCallback(w, XmNactivateCallback,
				(XtCallbackProc)CBActivateField, (caddr_t)fptr);
			XtAddCallback(w, XmNmodifyVerifyCallback,
				(XtCallbackProc)CBPasswordModify, (caddr_t)fptr);
		}
		else if ((type_str != NULL)&&(strcmp(type_str, "textarea") ==0))
		{
			char **list;
			int list_cnt;
			int rows, cols;
			Widget scroll;

			type = W_TEXTAREA;

			/*
			 * If there is no SIZE, look for ROWS and COLS
			 * directly.
			 * SIZE is COLUMNS,ROWS parse the list
			 */
			rows = -1;
			cols = -1;
			tptr = ParseMarkTag(text, MT_INPUT, "SIZE");
			if (tptr == NULL)
			{
				tptr = ParseMarkTag(text, MT_INPUT, "ROWS");
				if (tptr != NULL)
				{
					rows = atoi(tptr);
					free(tptr);
				}
				tptr = ParseMarkTag(text, MT_INPUT, "COLS");
				if (tptr != NULL)
				{
					cols = atoi(tptr);
					free(tptr);
				}
			}
			else
			{
				list = ParseCommaList(tptr, &list_cnt);
				free(tptr);

				if (list_cnt == 1)
				{
					cols = atoi(list[0]);
				}
				else if (list_cnt > 1)
				{
					cols = atoi(list[0]);
					rows = atoi(list[1]);
				}
				FreeCommaList(list, list_cnt);
			}

			/*
			 * Grab the starting value of the text here.
			 * NULL if none.
			 */
			value = ParseMarkTag(text, MT_INPUT, "VALUE");
			UnMuckTextAreaValue(value);

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNx, x); argcnt++;
			XtSetArg(arg[argcnt], XmNy, y); argcnt++;
			scroll = XmCreateScrolledWindow(hw->html.view,
				"Scroll", arg, argcnt);

			argcnt = 0;
			XtSetArg(arg[argcnt], XmNeditMode, XmMULTI_LINE_EDIT);
			argcnt++;
			if (cols > 0)
			{
				XtSetArg(arg[argcnt], XmNcolumns, cols);
				argcnt++;
			}
			if (rows > 0)
			{
				XtSetArg(arg[argcnt], XmNrows, rows);
				argcnt++;
			}
			if (value != NULL)
			{
				XtSetArg(arg[argcnt], XmNvalue, value);
				argcnt++;
			}
			w = XmCreateText(scroll, "Text", arg, argcnt);
			XtManageChild(w);
			w = scroll;

			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);
		}
		else /* if no type, assume type=text */
		{
			char **list;
			int list_cnt;
			int rows, cols;
			Widget scroll;

			/*
			 * SIZE can be either COLUMNS or COLUMNS,ROWS
			 * we assume COLUMNS,ROWS and parse the list
			 */
			tptr = ParseMarkTag(text, MT_INPUT, "SIZE");
			list = ParseCommaList(tptr, &list_cnt);
			if (tptr != NULL)
			{
				free(tptr);
			}

			/*
			 * If only COLUMNS specified, or SIZE not specified
			 * assume a TEXTFIELD
			 * Otherwise a TEXTAREA.
			 */
			if (list_cnt <= 1)
			{
				type = W_TEXTFIELD;
				if (list_cnt == 1)
				{
					cols = atoi(list[0]);
				}
				else
				{
					cols = -1;
				}
			}
			else
			{
				type = W_TEXTAREA;
				cols = atoi(list[0]);
				rows = atoi(list[1]);
			}
			/*
			 * Now that we have cols, and maybe rows, free the list
			 */
			FreeCommaList(list, list_cnt);

			/*
			 * Grab the starting value of the text here.
			 * NULL if none.
			 */
			value = ParseMarkTag(text, MT_INPUT, "VALUE");

			/*
			 * For textfileds parse maxlength and
			 * set up the widget.
			 */
			if (type == W_TEXTFIELD)
			{
				maxlength = -1;
				tptr = ParseMarkTag(text, MT_INPUT,"MAXLENGTH");
				if (tptr != NULL)
				{
					maxlength = atoi(tptr);
					free(tptr);
				}

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNx, x); argcnt++;
				XtSetArg(arg[argcnt], XmNy, y); argcnt++;
				if (cols > 0)
				{
					XtSetArg(arg[argcnt], XmNcolumns, cols);
					argcnt++;
				}
				if (maxlength > 0)
				{
					XtSetArg(arg[argcnt], XmNmaxLength,
						maxlength);
					argcnt++;
				}
				if (value != NULL)
				{
					XtSetArg(arg[argcnt], XmNvalue, value);
					argcnt++;
				}
				w = XmCreateTextField(hw->html.view, "Child",
					arg, argcnt);
			}
			/*
			 * Else this is a TEXTAREA.  Maxlength is ignored,
			 * and we set up the scrolled window
			 */
			else
			{
				argcnt = 0;
				XtSetArg(arg[argcnt], XmNx, x); argcnt++;
				XtSetArg(arg[argcnt], XmNy, y); argcnt++;
				scroll = XmCreateScrolledWindow(hw->html.view,
					"Scroll", arg, argcnt);

				argcnt = 0;
				XtSetArg(arg[argcnt], XmNeditMode,
					XmMULTI_LINE_EDIT);
				argcnt++;
				if (cols > 0)
				{
					XtSetArg(arg[argcnt], XmNcolumns, cols);
					argcnt++;
				}
				if (rows > 0)
				{
					XtSetArg(arg[argcnt], XmNrows, rows);
					argcnt++;
				}
				if (value != NULL)
				{
					XtSetArg(arg[argcnt], XmNvalue, value);
					argcnt++;
				}
				w = XmCreateText(scroll, "Text", arg, argcnt);
				XtManageChild(w);
				w = scroll;
			}

			XtSetMappedWhenManaged(w, False);
			XtManageChild(w);

			/*
			 * For textfields, a CR might be an activate
			 */
			if (type == W_TEXTFIELD)
			{
				XtAddCallback(w, XmNactivateCallback,
					(XtCallbackProc)CBActivateField, (caddr_t)fptr);
			}
		}
		if (type_str != NULL)
		{
			free(type_str);
		}

		argcnt = 0;
		XtSetArg(arg[argcnt], XmNwidth, &width); argcnt++;
		XtSetArg(arg[argcnt], XmNheight, &height); argcnt++;
		XtGetValues(w, arg, argcnt);

		wptr = AddNewWidget(hw, fptr, w, type, id, x, y, width, height,
			name, value, checked);
	}
	else
	/*
	 * We found this widget on the list of already created widgets.
	 * Put it in place for reuse.
	 */
	{
		wlist->x = x;
		wlist->y = y;

		argcnt = 0;
		XtSetArg(arg[argcnt], XmNx, x); argcnt++;
		XtSetArg(arg[argcnt], XmNy, y); argcnt++;
		XtSetValues(wlist->w, arg, argcnt);

		wptr = wlist;
	}

	return(wptr);
}


void
WidgetRefresh(hw, eptr)
        HTMLWidget hw;
        struct ele_rec *eptr;
{
	if ((eptr->widget_data != NULL)&&(eptr->widget_data->mapped == False))
	{
		eptr->widget_data->mapped = True;
		XtSetMappedWhenManaged(eptr->widget_data->w, True);
	}

#if 0
        if (eptr->pic_data != NULL)
        {
                int x, y, extra;

                x = eptr->x;
                y = eptr->y + eptr->y_offset;

                if ((hw->html.border_images == True)||
                        (eptr->anchorHRef != NULL))
                {
                        extra = IMAGE_BORDER;
                }
                else
                {
                        extra = 0;
                }

                x = x - hw->html.scroll_x;
                y = y - hw->html.scroll_y;
                XSetForeground(XtDisplay(hw), hw->html.drawGC, eptr->fg);
                XSetBackground(XtDisplay(hw), hw->html.drawGC, eptr->bg);
                XFillRectangle(XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), hw->html.drawGC,
                        x, y,
                        (eptr->pic_data->width + (2 * extra)),
                        extra);
                XFillRectangle(XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), hw->html.drawGC,
                        x,
                        (y + eptr->pic_data->height + extra),
                        (eptr->pic_data->width + (2 * extra)),
                        extra);
                XFillRectangle(XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), hw->html.drawGC,
                        x, y,
                        extra,
                        (eptr->pic_data->height + (2 * extra)));
                XFillRectangle(XtDisplay(hw->html.view),
                        XtWindow(hw->html.view), hw->html.drawGC,
                        (x + eptr->pic_data->width + extra),
                        y,
                        extra,
                        (eptr->pic_data->height + (2 * extra)));

                if (eptr->pic_data->image == (Pixmap)NULL)
                {
                        if (eptr->pic_data->image_data != NULL)
                        {
                                eptr->pic_data->image = InfoToImage(hw,
                                        eptr->pic_data);
                        }
                        else
                        {
                                eptr->pic_data->image = NoImage(hw);
                        }
                }

                if (eptr->pic_data->image != (Pixmap)NULL)
                {
                        XCopyArea(XtDisplay(hw->html.view),
                                eptr->pic_data->image,
                                XtWindow(hw->html.view), hw->html.drawGC, 0, 0,
                                eptr->pic_data->width, eptr->pic_data->height,
                                (x + extra),
                                (y + extra));
                }
        }
#endif
}

