#ifndef lint
static char rcsId[]="$Header: /usr/local/rcs/Newt/XmHTML/RCS/example_3.c,v 1.4 1997/10/23 00:28:41 newt Exp newt $";
#endif
/*****
* example_3.c : demonstration of the XmHTMLParserObject.
*
* This file Version	$Revision: 1.4 $
*
* Creation date:		Mon Apr  7 17:42:38 GMT+0100 1997
* Last modification: 	$Date: 1997/10/23 00:28:41 $
* By:					$Author: newt $
* Current State:		$State: Exp $
*
* Author:				newt
*
* Copyright (C) 1994-1997 by Ripley Software Development 
* All Rights Reserved
*
* This file is part of the XmHTML Widget Library.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*****/
/*****
* ChangeLog 
* $Log: example_3.c,v $
* Revision 1.4  1997/10/23 00:28:41  newt
* XmHTML Beta 1.1.0 release
*
* Revision 1.3  1997/08/30 01:58:42  newt
* my_strdup -> strdup changes.
*
* Revision 1.2  1997/05/28 02:07:04  newt
* Changes to reflect the new XmImageCreate proto.
*
* Revision 1.1  1997/04/29 14:34:12  newt
* Initial Revision
*
*****/ 
#include <X11/Intrinsic.h>
#include <X11/Xos.h>
#include <Xm/FileSB.h>
#include <Xm/Frame.h>
#include <Xm/Form.h>
#include <Xm/LabelG.h>
#include <Xm/List.h>
#include <Xm/MainW.h>
#include <Xm/MessageB.h>
#include <Xm/PushB.h>
#include <Xm/PushBG.h>
#include <Xm/SelectioB.h>
#include <Xm/Text.h>
#include <Xm/Xm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <XmHTML/XmHTML.h>
#include <XmHTML/Parser.h>			/* we want to have a standalone parser */

/*****
* Change this to change the application class of the examples
*****/
#define APP_CLASS		"HTMLDemos"

/*** External Function Prototype Declarations ***/

/*** Public Variable Declarations ***/

/*** Private Datatype Declarations ****/
#define FILE_NEW		0
#define FILE_OPEN		1
#define FILE_SAVEAS		2
#define FILE_QUIT		3

#define FILE_SAVETREE	5	/* save parser tree */

#define HTML_VERIFY		0
#define HTML_PROGRESSIVE 1
#define HTML_PREVIEW    2
#define HTML_SAVE		3

#define OPTIONS_SCROLL	0
#define OPTIONS_AUTO	1
#define OPTIONS_NOTIFY	2

#define OK_BUTTON		1
#define APPLY_BUTTON	2
#define CANCEL_BUTTON	3
#define HELP_BUTTON		4

/*** Private Function Prototype Declarations ****/
/* Menu bar callbacks */
static void fileCB(Widget widget, int item);
static void htmlCB(Widget widget, int item);
static void optionsCB(Widget widget, int item,
		XmToggleButtonCallbackStruct *cbs);

/* text callbacks */
static void modifyCB(Widget widget, XtPointer data, XmTextVerifyPtr cbs);

/* various text response routines */
static void cutText(int start_pos, int end_pos);
static void insertText(XmHTMLTextBlock text, int start_pos);
static void selectText(int start_pos, int end_pos);

/* HTML callbacks */
static void modifyHTMLCB(Widget widget, XtPointer data, XmHTMLVerifyPtr cbs);
static void parserCB(Widget widget, XtPointer data, XmHTMLParserPtr cbs);
static void docCB(Widget widget, XtPointer data, XmHTMLDocumentPtr cbs);
static void anchorCB(Widget widget, XtPointer data, XmHTMLAnchorPtr cbs);

/* various parser response routines */
static int parserFatal(XmHTMLParserPtr cbs);
static int parserBlock(XmHTMLParserPtr cbs);
static int parserBadHTML(XmHTMLParserPtr cbs);
static int parserUnknownHTML(XmHTMLParserPtr cbs);
static int parserOpenElement(XmHTMLParserPtr cbs);
static int parserSelectAlias(String unknown);
static int addAlias(Widget list, XmHTMLAliasTable global_table,
	int nglobal, XmHTMLAliasTable parser_table, int nparser,
	String element);

/* Misc. callback routines */
static void dismissCB(Widget widget, XtPointer data);
static void readFileCB(Widget widget, Widget dialog,
	XmFileSelectionBoxCallbackStruct *cbs);
static void response(Widget widget, int *answer, XmAnyCallbackStruct *cbs);

/* various functions */
static int askUser(String msg);
static void setTitle(String title);
static void askSave(void);
static void tellUser(String msg);
static void doPreview(Boolean show);
static void parseHTML(void);			/* test normal text parsing */
static void parseProgressiveHTML(void);	/* test progressive text parsing */
static String loadFile(String filename);

/*** Private Variable Declarations ***/
static String use_file, parser_buffer;
static Widget toplevel, info_label, html32, verified, balanced, busy_label;
static Widget text_w, html_w, preview_dialog;
static Boolean file_changed, parser_active;
static Boolean scroll_text = False;
static Boolean honor_document_cb = False;
static XtAppContext context;
static XtIntervalId current_id = None;		/* progressive timerProc id */
static String working_image_file, ready_image_file;
static XmImage *working_image;			/* a nice gifAnim */
static XmImage *ready_image;				/* a nice gif */

/* and what this example is all about: a html parser */
static Widget parser;

/*****
* Name: 		setTitle
* Return Type: 	void
* Description: 	sets the window title.
* In: 
*	title:		title to set
* Returns:
*	nothing.
*****/
static void
setTitle(String title)
{
	XtVaSetValues(toplevel,
		XmNtitle, title,
		NULL);
}

/*****
* Name: 		resetBusyLabel
* Return Type: 	void
* Description: 	resets the busy label when we are done processing by
*				changing the current label text to Ready and by displaying
*				the ready image
* In: 
*	nothing
* Returns:
*	nothing
*****/
static void
resetBusyLabel(void)
{
	if(working_image && working_image->proc_id)
	{
		XtRemoveTimeOut(working_image->proc_id);
		working_image->proc_id = None;
		working_image->current_frame = 0;
	}

	/* set ready image */
	if(ready_image)
		XtVaSetValues(busy_label,
			XmNlabelType, XmPIXMAP,
			XmNlabelPixmap, ready_image->pixmap,
			NULL);
	XtVaSetValues(info_label, 
		XtVaTypedArg, XmNlabelString, XmRString, "Ready", 6,
		NULL);
}

/*****
* Name: 		modifyCB
* Return Type: 	void
* Description: 	XmNmodifyVerifyCallback handler. Sets the file_changed flag.
* In: 
*	widget:		widget id
*	data:		client_data, unused
*	cbs:		call_data, unused
* Returns:
*	nothing.
*****/
static void
modifyCB(Widget w, XtPointer data, XmTextVerifyPtr cbs)
{
	if(!file_changed)
	{
		String title;
		title = (char*)malloc(strlen(use_file) + 12);
		sprintf(title, "%s (Modified)", use_file);
		setTitle(title);
		free(title);
	}
	file_changed = True;
}

/*****
* Name: 		selectText
* Return Type: 	void
* Description: 	selects a portion of text currently displayed in the text
*				widget.
* In: 
*	start_pos:	selection start position.
*	end_pos:	selection end position.
* Returns:
*	nothing.
*****/
static void
selectText(int start_pos, int end_pos)
{
	XmTextSetSelection(text_w, (XmTextPosition)start_pos,
		(XmTextPosition)end_pos+1, CurrentTime);
}

/*****
* Name: 		insertText
* Return Type: 	void
* Description: 	inserts text in the document currently displayed in the text
*				widget.
* In: 
*	text:		text to be inserted
*	start_pos:	insertion position.
* Returns:
*	nothing.
*****/
static void
insertText(XmHTMLTextBlock text, int start_pos)
{
	XmTextInsert(text_w, (XmTextPosition)start_pos, text->ptr);
}

/*****
* Name: 		cutText
* Return Type: 	void
* Description: 	removes text in the document currently displayed in the text
*				widget.
* In: 
*	start_pos:	selection start position
*	end_pos:	selection end position
* Returns:
*	nothing.
*****/
static void
cutText(int start_pos, int end_pos)
{
	/* put text to remove as the primary selection */
	XmTextSetSelection(text_w, (XmTextPosition)start_pos,
		(XmTextPosition)end_pos, CurrentTime);
	/* cut it */
	XmTextCut(text_w, CurrentTime);
}

/*****
* Name: 		response
* Return Type: 	void
* Description: 	callback routine for all modal message dialogs in this app.
* In: 
*	widget:		widget id
*	answer:		button pressed, updated upon return;
*	cbs:		call_data.
* Returns:
*	nothing.
*****/
static void
response(Widget widget, int *answer, XmAnyCallbackStruct *cbs)
{
	switch(cbs->reason)
	{
		case XmCR_OK:
			*answer = OK_BUTTON;
			break;
		case XmCR_APPLY:
			*answer = APPLY_BUTTON;
			break;
		case XmCR_CANCEL:
			*answer = CANCEL_BUTTON;
			break;
		case XmCR_HELP:
			*answer = HELP_BUTTON;
			break;
	}
}

/*****
* Name: 		askUser
* Return Type: 	Boolean
* Description: 	modal question message dialog.
* In: 
*	msg:		text to display.
* Returns:
*	True when OK has been pressed, False if cancel has been pressed.
*****/
static int 
askUser(String msg)
{
	static Widget msg_box;
	static int answer;
	XmString xms;

	if(msg_box == NULL)
	{
		XmString ok_label, cancel_label;

		msg_box= XmCreateQuestionDialog(toplevel, "Question", NULL, 0);

		ok_label = XmStringCreateLocalized("Yes");
		cancel_label = XmStringCreateLocalized("No");
		XtVaSetValues(msg_box,
			XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, 
			XmNokLabelString, ok_label,
			XmNcancelLabelString, cancel_label,
			NULL);

		XmStringFree(ok_label);
		XmStringFree(cancel_label);

		/* unmanage help button */
		XtUnmanageChild(XmMessageBoxGetChild(msg_box, XmDIALOG_HELP_BUTTON));

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Question", NULL);
	}
	/* create message string */
	xms = XmStringCreateLtoR(msg, XmFONTLIST_DEFAULT_TAG);

	XtVaSetValues(msg_box,
		XmNmessageString, xms,
		XmNdefaultButton, XmDIALOG_OK_BUTTON,
		NULL);
	XmStringFree(xms);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	return((answer == OK_BUTTON ? True : False));
}

/*****
* Name: 		tellUser
* Return Type: 	boolean
* Description: 	modal information message dialog.
* In: 
*	msg:		text to display.
* Returns:
*	Nothing.
*****/
static void
tellUser(String msg)
{
	static Widget msg_box;
	static int answer;
	XmString xms;

	if(msg_box == NULL)
	{
		XmString ok_label;

		msg_box= XmCreateInformationDialog(toplevel, "Notice", NULL, 0);

		ok_label = XmStringCreateLocalized("OK");
		XtVaSetValues(msg_box,
			XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, 
			XmNokLabelString, ok_label,
			NULL);
		XmStringFree(ok_label);

		/* unmanage help button */
		XtUnmanageChild(XmMessageBoxGetChild(msg_box, XmDIALOG_HELP_BUTTON));
		/* unmanage cancel button */
		XtUnmanageChild(XmMessageBoxGetChild(msg_box, XmDIALOG_CANCEL_BUTTON));

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Notice", NULL);
	}
	/* create message string */
	xms = XmStringCreateLtoR(msg, XmFONTLIST_DEFAULT_TAG);

	XtVaSetValues(msg_box,
		XmNmessageString, xms,
		NULL);
	XmStringFree(xms);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));
}

/*****
* Name: 		askSave
* Return Type: 	void
* Description: 	put up a message box asking user if he wants to save changes
*				to the file.
*				When changes should be saved, it also puts up a fileSB box,
*				saves the file and resets file_changed to False.
* In: 
*	nothing
* Returns:
*	nothing.
*****/
static void
askSave(void)
{
	if(!file_changed)
		return;

	if(file_changed)
	{
		char text[128];
		sprintf(text, "%s has been modified.\nDo you wish to save it?",
			use_file);
		if(askUser(text))
			fileCB(toplevel, FILE_SAVEAS);
	}
	/* just reset for now */
	file_changed = False;
	setTitle(use_file);
	return;
}

/*****
* Name: 		loadFile
* Return Type: 	String
* Description: 	loads the contents of the given file.
* In: 
*	filename:	name of the file to load
*	mime_type:	mimetype of file to load, updated upon return.
* Returns:
*	File contents upon success, NULL on failure.
*****/
static String
loadFile(String filename)
{
	FILE *file;
	int size;
	static String buffer;

	/* open the given file */
	if((file = fopen(filename, "r")) == NULL)
	{
		perror(filename);
		return(False);
	}

	/* see how large this file is */
	fseek(file, 0, SEEK_END);
	size = ftell(file);
	rewind(file);

	/* allocate a buffer large enough to contain the entire file */
	if((buffer = malloc(size+1)) == NULL)
	{
		fprintf(stderr, "malloc failed for %i bytes\n", size);
		exit(EXIT_FAILURE);
	}

	/* now read the contents of this file */
	if((fread(buffer, 1, size, file)) != size)
		printf("Warning: did not read entire file!\n");
	buffer[size] = '\0';	/* sanity */

	fclose(file);

	/* set current file */
	if(use_file)
		free(use_file);
	use_file = strdup(filename);
	return(buffer);
}

/*****
* Name: 		readFileCB
* Return Type: 	void
* Description: 	XmNokCallback handler for the fileSelectionDialog: retrieves
*				the entered filename and loads it.
* In: 
*	w:			widget
*	dialog:		widget id of the fileSelectionDialog
*	cbs:		callback data
* Returns:
*	nothing.
*****/
static void
readFileCB(Widget widget, Widget dialog, XmFileSelectionBoxCallbackStruct *cbs)
{
	String filename;
	int item;

	/* remove the fileSelectionDialog */
	XtPopdown(XtParent(dialog));

	/* get the entered filename */
	XmStringGetLtoR(cbs->value, XmSTRING_DEFAULT_CHARSET, &filename);

	/* sanity check */
	if(!filename || !*filename)
	{
		if(filename)
			XtFree(filename);
		return;
	}

	/* get item data */
	XtVaGetValues(dialog, XmNuserData, &item, NULL);

	if(item == FILE_OPEN)
	{
		String buf;

		/* ask to save changes (if any) */
		askSave();

		/* load the file */
		if((buf = loadFile(filename)) != NULL)
		{
			/* set text in text widget */
			XmTextSetString(text_w, buf);

			/* reset, TextSetString invokes XmText's modifyVerify callback. */
			file_changed = False;
			setTitle(use_file);

			/* clear html widget contents */
			XmHTMLTextSetString(html_w, NULL);

			/* reset parser */
			XmHTMLParserSetString(parser, NULL);

			/* clear the labels as well */
			docCB(NULL, NULL, NULL);

			/* and free loaded file */
			free(buf);
		}
		XtFree(filename);
	}
	else if(item == FILE_SAVEAS || item == FILE_SAVETREE)
	{
		FILE *fp;
		String buffer;

		if(item == FILE_SAVEAS)
			buffer = XmTextGetString(text_w);
		else
			buffer = XmHTMLParserGetString(parser);

		if(buffer)
		{
			if((fp = fopen(filename, "w")) == NULL)
				perror(filename);
			else
			{
				fputs(buffer, fp);
				fputs("\n", fp);
				fclose(fp);
			}
			XtFree(buffer);
		}
		XtFree(filename);
		file_changed = False;
		setTitle(use_file);
	}
}

/*****
* Name: 		fileCB
* Return Type: 	void
* Description: 	callback for the File menu.
* In: 
*	widget:		widget id
*	item:		id of selected menu item.
* Returns:
*	nothing.
*****/
static void
fileCB(Widget widget, int item)
{
	static Widget dialog;

	/* first ask if changes should be saved */
	askSave();

	/* three menu items: Open, Save and Quit */
	if(item == FILE_QUIT)
	{
		/* Free images. It returns immediatly if the image argument is NULL */
		XmImageDestroy(working_image);
		XmImageDestroy(ready_image);
		printf("Bye!\n");
		exit(EXIT_SUCCESS);
	}

	if(item == FILE_NEW)
	{
		/* clear contents of the text widget */
		XmTextSetString(text_w, NULL);

		/* clear contents of the html widget */
		XmHTMLTextSetString(html_w, NULL);

		/* reset parser */
		XmHTMLParserSetString(parser, NULL);

		/* and reset all visible flags from the previous document */
		file_changed = False;
		free(use_file);
		use_file = strdup("<Untitled>");
		docCB(NULL, NULL, NULL);
		return;
	}
	if(!dialog)
	{
		Arg args[1];
		Widget menu = XtParent(widget);
		XmString xms;

		/* set the file selection pattern */
		xms = XmStringCreateLocalized("*.html");
		XtSetArg(args[0], XmNpattern, xms);

		/* create the dialog */
		dialog = XmCreateFileSelectionDialog(menu, "fileOpen", args, 1);

		/* no longer needed, free it */
		XmStringFree(xms);

		/* 
		* register the dialog itself as callback data, so we can pop it down
		* when pressed on the OK button.
		*/
		XtAddCallback(dialog, XmNokCallback, 
			(XtCallbackProc)readFileCB, dialog);
		XtAddCallback(dialog, XmNcancelCallback, 
			(XtCallbackProc)XtUnmanageChild, NULL);
	}
	/* set appropriate dialog title */
	if(item == FILE_SAVEAS)
		XtVaSetValues(XtParent(dialog), XmNtitle, "Save Document As", NULL);
	else if(item == FILE_SAVETREE)
		XtVaSetValues(XtParent(dialog), XmNtitle, "Save HTML Parser Tree",
			NULL);
	else
		XtVaSetValues(XtParent(dialog), XmNtitle, "Open a File", NULL);

	/* save item no as user data */
	XtVaSetValues(dialog, XmNuserData, (XtPointer)item, NULL);

	XtManageChild(dialog);
	XtPopup(XtParent(dialog), XtGrabNone);
	XMapRaised(XtDisplay(dialog), XtWindow(XtParent(dialog)));
}

/*****
* Name: 		htmlCB
* Return Type: 	void
* Description: 	callback for the HTML menu.
* In: 
*	widget:		widget id
*	item:		id of selected menu item.
* Returns:
*	nothing.
*****/
static void
htmlCB(Widget widget, int item)
{
	switch(item)
	{
		case HTML_VERIFY:
			parseHTML();
			break;
		case HTML_PROGRESSIVE:
			parseProgressiveHTML();
			break;
		case HTML_SAVE:
			fileCB(toplevel, FILE_SAVETREE);
			break;
		case HTML_PREVIEW:
			doPreview(True);
			break;
		default:
			fprintf(stderr, "htmlCB: impossible menu selection!\n");
	}
}

/*****
* Name: 		optionsCB
* Return Type: 	void
* Description: 	callback for the Options menu
* In: 
*	widget:		widget id
*	item:		selected menu item
* Returns:
*	nothing
*****/
static void
optionsCB(Widget widget, int item, XmToggleButtonCallbackStruct *cbs)
{
	switch(item)
	{
		case OPTIONS_SCROLL:
			scroll_text = cbs->set;
			break;
		case OPTIONS_AUTO:
			XmHTMLParserSetAutoMode(parser, !cbs->set);
			break;
		case OPTIONS_NOTIFY:
			honor_document_cb = cbs->set;
			break;
	}
}

/*****
* Name: 		anchorCB
* Return Type: 	void
* Description: 	XmNactivateCallback for the XmHTML widget
* In: 
*	widget:		widget id, in this case that of the HTML widget 
*	data:		data registered with callback, unused
*	cbs:		XmHTML callback structure.
* Returns:
*	nothing.
*****/
static void
anchorCB(Widget widget, XtPointer data, XmHTMLAnchorPtr cbs)
{
	cbs->doit = True;
	cbs->visited = True;
}

/*****
* Name: 		dismissCB
* Return Type: 	void
* Description: 	pops down the HTML previewer dialog
* In: 
*	widget:		unused;
*	data:		widget to pop down;
* Returns:
*	nothing.
*****/
static void
dismissCB(Widget widget, XtPointer data)
{
	XtUnmanageChild((Widget)data);
}

/*****
* Name: 		addParserAliasTable
* Return Type: 	int
* Description: 	gets the selected alias from the given list widget and
*				adds an alias to the parser alias table.
* In: 
*	w:			list widget
*	global_table:	global parser table
*	nglobal:		no of items in global parser table;
*	parser_table:	current parser table;
*	nparser:		no of items in parser table;
*	element:		element to be aliased
* Returns:
*	HTML_ALIAS when alias was successfully installed, HTML_REMOVE
*	otherwise.
*****/
static int
addAlias(Widget list, XmHTMLAliasTable global_table,
	int nglobal, XmHTMLAliasTable parser_table, int nparser,
	String element)
{
	int *pos_list;	/* selected list position */
	int pos_cnt, selected;	/* no of selected items */
	htmlEnum alias;

	if(!(XmListGetSelectedPos(list, &pos_list, &pos_cnt)))
	{
		/* no items selected */
		return(HTML_REMOVE);
	}

	/*
	* See if the selected item is part of the global alias or the current
	* parser alias table
	*/
	selected = pos_list[0];
	/* list positions start at 1 instead of zero, so be sure to adjust */
	selected--;
	if(selected < nglobal)
		alias = global_table[selected].alias;
	else
	{
		/* current parser alias table */
		selected -= nglobal;
		alias = parser_table[selected].alias;
	}

	if(!(XmHTMLParserAddAlias(parser, element, alias)))
	{
		char text[128];
		sprintf(text, "Could not add an alias for unknown element %s", element);
		tellUser(text);
		free(pos_list);
		return(HTML_REMOVE);
	}
	free(pos_list);

	return(HTML_ALIAS);
}

/*****
* Name: 		parserAddAlias
* Return Type: 	int
* Description: 	brings up a selection dialog for adding an alias for an unknown
*				element.
* In: 
*	element:	element for which to add an alias.
* Returns:
*	HTML_ALIAS when an alias has been inserted, HTML_REMOVE otherwise.
*****/
static int 
parserSelectAlias(String unknown)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val, i, j, nglobal, nparser;
	XmString xms, *table;
	char text[128];
	XmHTMLAliasTable global_table, parser_table;

	if(msg_box == NULL)
	{
		XmString ok_label, cancel_label;
		Arg args[10];
		int argc = 0;

		ok_label     = XmStringCreateLocalized("OK");
		cancel_label = XmStringCreateLocalized("Cancel");

		XtSetArg(args[argc], XmNokLabelString, ok_label); argc++;
		XtSetArg(args[argc], XmNcancelLabelString, cancel_label); argc++;

		msg_box= XmCreateSelectionDialog(toplevel, "aliasUnknown",
			args, argc);

		XmStringFree(ok_label);
		XmStringFree(cancel_label);

		/* unmanage apply button */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box,
			XmDIALOG_APPLY_BUTTON));

		/* no need for the text area */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));
		/* and the selection label */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box,
			XmDIALOG_SELECTION_LABEL));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Select Element Alias",
			NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
	}
	/* clear current list contents */
	XmListDeleteAllItems(XmSelectionBoxGetChild(msg_box, XmDIALOG_LIST));

	/* get the global alias table */
	global_table = XmHTMLGetGlobalAliasTable(&nglobal);

	/* get current parser alias table */
	parser_table = XmHTMLParserGetAliasTable(parser, &nparser);

	/* create the XmString table for these aliases */
	table = (XmString*)malloc((nglobal+nparser)*sizeof(XmString));

	/* add all strings from the global table */
	for(i = 0, j = 0; i < nglobal; i++, j++)
		table[j] = XmStringCreateLocalized(global_table[i].element);

	/* add all strings from the current parser table */
	for(i = 0; i < nparser; i++, j++)
		table[j] = XmStringCreateLocalized(parser_table[i].element);

	/* create list label string */
	sprintf(text, "Select an alias for %s", unknown);
	xms = XmStringCreateLocalized(text);

	/* set everything in one go */
	XtVaSetValues(msg_box,
		XmNlistLabelString, xms,
		XmNdefaultButton, def_bttn,
		XmNlistItems, table,
		XmNlistItemCount, nglobal+nparser,
		XmNlistVisibleItemCount, 15,
		NULL);

	/* and free all strings */
	XmStringFree(xms);
	for(i = 0; i < nparser + nglobal; i++)
		XmStringFree(table[i]);
	free(table);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	/* convert pushed button to a valid action code */
	switch(answer)
	{
		case OK_BUTTON:
			/*
			* an alias has been selected, figure out which one it was and
			* add it.
			*/
			ret_val = addAlias(XmSelectionBoxGetChild(msg_box,
				XmDIALOG_LIST), global_table, nglobal, parser_table,
				nparser, unknown);
			break;
		case CANCEL_BUTTON:
		default:
			ret_val = HTML_REMOVE;
			break;
	}
	/* free global alias table */
	XmHTMLParserDestroyAliasTable(global_table, nglobal);

	/* free parser alias table */
	XmHTMLParserDestroyAliasTable(parser_table, nparser);

	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserUnknownHTML
* Return Type: 	int
* Description: 	handles HTML_UNKNOWN_ELEMENT parser error
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserUnknownHTML(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val, len;
	XmString xms;
	char msg[1024], tmp[128];

	selectText(cbs->start_pos, cbs->end_pos);

	if(msg_box == NULL)
	{
		XmString ok_label, help_label, cancel_label;
		Arg args[10];
		int argc = 0;

		ok_label     = XmStringCreateLocalized("Remove");
		cancel_label = XmStringCreateLocalized("Add Alias...");
		help_label   = XmStringCreateLocalized("Terminate");

		XtSetArg(args[argc], XmNokLabelString, ok_label); argc++;
		XtSetArg(args[argc], XmNcancelLabelString, cancel_label); argc++;
		XtSetArg(args[argc], XmNhelpLabelString, help_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserUnknown",
			args, argc);

		XmStringFree(ok_label);
		XmStringFree(cancel_label);
		XmStringFree(help_label);

		/* unmanage apply button */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box,
			XmDIALOG_APPLY_BUTTON));

		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Unknown Element", NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNhelpCallback, (XtCallbackProc)response,
			&answer);
	}
	len = cbs->offender->len > 127 ? 127 : cbs->offender->len;
	strncpy(tmp, cbs->offender->ptr, len);
	tmp[len] = '\0';
	sprintf(msg, "%s: unknown HTML identifier.", tmp);
		
	/* create message string */
	xms = XmStringCreateLocalized(msg);
	XtVaSetValues(msg_box,
		XmNselectionLabelString, xms,
		XmNdefaultButton, def_bttn,
		NULL);
	XmStringFree(xms);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	/* convert pushed button to a valid action code */
	switch(answer)
	{
		case HELP_BUTTON:
			ret_val = HTML_TERMINATE;
			break;
		case CANCEL_BUTTON:
			ret_val = parserSelectAlias(cbs->offender->ptr);
			break;
		case OK_BUTTON:
		default:
			ret_val = HTML_REMOVE;
	}
	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserBadHTML
* Return Type: 	int
* Description: 	handles HTML_BAD and HTML_VIOLATION parser errors
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserBadHTML(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val, ok_val, cancel_val;
	XmString xms, ok_label, cancel_label;
	char msg[1024];

	selectText(cbs->start_pos, cbs->end_pos);

	if(msg_box == NULL)
	{
		XmString help_label;
		Arg args[10];
		int argc = 0;

		help_label = XmStringCreateLocalized("Terminate");

		XtSetArg(args[argc], XmNhelpLabelString, help_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserBadHTML", args, argc);

		XmStringFree(help_label);

		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));
		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "HTML Violation", NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNhelpCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNapplyCallback, (XtCallbackProc)response,
			&answer);
	}
	if(cbs->error == HTML_BAD)
	{
		ok_label     = XmStringCreateLocalized("Remove");
		cancel_label = XmStringCreateLocalized("Ignore");
		ok_val       = HTML_REMOVE;
		cancel_val   = HTML_IGNORE;
		sprintf(msg, "Terrible HTML! element %s completely out of balance!",
			cbs->offender->ptr);
	}
	/*
	* cbs->error == HTML_VIOLATION
	* default action depends on current state of the XmNstrictHTMLChecking
	* resource and is provided as the default action in the callback
	* structure.
	*/
	else
	{
		/* XmNstrictHTMLChecking == True */
		if(cbs->action == HTML_REMOVE)
		{
			ok_label     = XmStringCreateLocalized("Remove");
			cancel_label = XmStringCreateLocalized("Keep");
			ok_val       = HTML_REMOVE;
			cancel_val   = HTML_KEEP;
		}
		else
		{
			ok_label     = XmStringCreateLocalized("Keep");
			cancel_label = XmStringCreateLocalized("Remove");
			ok_val       = HTML_KEEP;
			cancel_val   = HTML_REMOVE;
		}
		sprintf(msg, "HTML 3.2 violation: %s may not occur inside %s",
			cbs->offender->ptr, cbs->current->ptr);
	}
	/* the parser suggested an element which could repair this violation */
	if(cbs->repair->len)
	{
		char text[128];
		XmString apply_label;
		sprintf(text, "Insert %s", cbs->repair->ptr);
		apply_label  = XmStringCreateLocalized(text);
		XtManageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_APPLY_BUTTON));
		XtVaSetValues(msg_box, XmNapplyLabelString, apply_label, NULL);
		XmStringFree(apply_label);
	}
	else
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_APPLY_BUTTON));

	xms = XmStringCreateLocalized(msg);
	XtVaSetValues(msg_box,
		XmNokLabelString, ok_label,
		XmNcancelLabelString, cancel_label,
		XmNselectionLabelString, xms,
		XmNdefaultButton, def_bttn,
		NULL);
	XmStringFree(ok_label);
	XmStringFree(cancel_label);
	XmStringFree(xms);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	/* convert pushed button to a valid action code */
	switch(answer)
	{
		case HELP_BUTTON:
			ret_val = HTML_TERMINATE;
			break;
		case CANCEL_BUTTON:
			ret_val = cancel_val;
			break;
		case APPLY_BUTTON:
			ret_val = HTML_INSERT;
			break;
		case OK_BUTTON:
		default:
			ret_val = ok_val;
	}
	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserBlock
* Return Type: 	int
* Description: 	handles HTML_OPEN_BLOCK and HTML_CLOSE_BLOCK parser errors
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserBlock(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val, ok_val, apply_val;
	XmString xms, ok_label, apply_label;
	char msg[1024];

	selectText(cbs->start_pos, cbs->end_pos);

	if(msg_box == NULL)
	{
		XmString cancel_label, help_label;
		Arg args[10];
		int argc = 0;

		cancel_label  = XmStringCreateLocalized("Keep");
		help_label   = XmStringCreateLocalized("Terminate");

		XtSetArg(args[argc], XmNhelpLabelString, help_label); argc++;
		XtSetArg(args[argc], XmNcancelLabelString, cancel_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserBlock", args, argc);

		XmStringFree(cancel_label);
		XmStringFree(help_label);

		/* manage apply button */
		XtManageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_APPLY_BUTTON));
		/* unmanage text area */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Invalid Block Element",
			NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNapplyCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNhelpCallback, (XtCallbackProc)response,
			&answer);
	}

	if(cbs->error == HTML_OPEN_BLOCK)
	{
		char text[128];

		sprintf(text, "Insert %s", cbs->repair->ptr);
		ok_label    = XmStringCreateLocalized(text);
		sprintf(text, "Remove %s", cbs->offender->ptr);
		apply_label = XmStringCreateLocalized(text);
		ok_val      = HTML_INSERT;
		apply_val   = HTML_REMOVE;
		sprintf(msg, "A new block level element (%s) was encountered while %s "
			"is still open.", cbs->offender->ptr, cbs->current->ptr);
	}
	else
	{
		char text[128];
		sprintf(text, "Remove %s", cbs->offender->ptr);
		ok_label    = XmStringCreateLocalized(text);
		sprintf(text, "Insert %s", cbs->repair->ptr);
		apply_label = XmStringCreateLocalized(text);
		ok_val      = HTML_REMOVE;
		apply_val   = HTML_INSERT;
		sprintf(msg, "A closing element (%s) was encountered while it was "
			"never opened.", cbs->offender->ptr);
	}
	xms = XmStringCreateLocalized(msg);

	XtVaSetValues(msg_box,
		XmNokLabelString, ok_label,
		XmNapplyLabelString, apply_label,
		XmNselectionLabelString, xms,
		XmNdefaultButton, def_bttn,
		NULL);
	XmStringFree(ok_label);
	XmStringFree(apply_label);
	XmStringFree(xms);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	switch(answer)
	{
		case HELP_BUTTON:
			ret_val = HTML_TERMINATE;
			break;
		case APPLY_BUTTON:
			ret_val = apply_val;
			break;
		case CANCEL_BUTTON:
			ret_val = HTML_KEEP;
		case OK_BUTTON:
		default:
			ret_val = ok_val;
	}
	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserNestedElement
* Return Type: 	int
* Description: 	handles HTML_NESTED parser errors
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserNestedElement(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val;
	XmString xms;
	char msg[1024];

	selectText(cbs->start_pos, cbs->end_pos);

	if(msg_box == NULL)
	{
		XmString ok_label, apply_label, cancel_label, help_label;
		Arg args[10];
		int argc = 0;

		ok_label     = XmStringCreateLocalized("Ignore");
		apply_label  = XmStringCreateLocalized("Remove");
		cancel_label = XmStringCreateLocalized("Keep");
		help_label   = XmStringCreateLocalized("Terminate");

		XtSetArg(args[argc], XmNokLabelString, ok_label); argc++;
		XtSetArg(args[argc], XmNapplyLabelString, apply_label); argc++;
		XtSetArg(args[argc], XmNhelpLabelString, help_label); argc++;
		XtSetArg(args[argc], XmNcancelLabelString, cancel_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserNested", args, argc);

		XmStringFree(ok_label);
		XmStringFree(apply_label);
		XmStringFree(cancel_label);
		XmStringFree(help_label);

		/* manage apply button */
		XtManageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_APPLY_BUTTON));
		/* unmanage text area */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Improperly Nested Element",
			NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNapplyCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNhelpCallback, (XtCallbackProc)response,
			&answer);
	}
	/* current is same as offender */
	sprintf(msg, "%s may not be nested", cbs->current->ptr);

	xms = XmStringCreateLocalized(msg);

	XtVaSetValues(msg_box,
		XmNselectionLabelString, xms,
		NULL);
	XmStringFree(xms);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	switch(answer)
	{
		case HELP_BUTTON:
			ret_val = HTML_TERMINATE;
			break;
		case APPLY_BUTTON:
			ret_val = HTML_REMOVE;
			break;
		case CANCEL_BUTTON:
			ret_val = HTML_KEEP;
		case OK_BUTTON:
		default:
			ret_val = HTML_IGNORE;
	}
	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserOpenElement
* Return Type: 	int
* Description: 	handles HTML_OPEN_ELEMENT parser error
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserOpenElement(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	int ret_val;
	XmString xms, ok_label, cancel_label;
	char text[128];
	char msg[1024];

	selectText(cbs->start_pos, cbs->end_pos);

	if(msg_box == NULL)
	{
		XmString help_label;
		Arg args[10];
		int argc = 0;

		help_label   = XmStringCreateLocalized("Terminate");

		XtSetArg(args[argc], XmNhelpLabelString, help_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserOpenElement",
			args, argc);

		XmStringFree(help_label);

		/* unmanage text area */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Unbalanced Element", NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNhelpCallback, (XtCallbackProc)response,
			&answer);
	}
	/* create message string */
	sprintf(msg, "Unbalanced Terminator: %s where %s is required",
		cbs->offender->ptr, cbs->current->ptr);
	xms = XmStringCreateLocalized(msg);

	sprintf(text, "Remove %s", cbs->offender->ptr);
	ok_label = XmStringCreateLocalized(text);

	sprintf(text, "Switch %s and %s", cbs->offender->ptr, cbs->repair->ptr);
	cancel_label = XmStringCreateLocalized(text);

	XtVaSetValues(msg_box,
		XmNselectionLabelString, xms,
		XmNokLabelString, ok_label,
		XmNcancelLabelString, cancel_label,
		XmNdefaultButton, def_bttn,
		NULL);
	XmStringFree(xms);
	XmStringFree(ok_label);
	XmStringFree(cancel_label);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	switch(answer)
	{
		case HELP_BUTTON:
			ret_val = HTML_TERMINATE;
			break;
		case CANCEL_BUTTON:
			ret_val = HTML_SWITCH;
			break;
		case OK_BUTTON:
		default:
			ret_val = HTML_REMOVE;
	}
	/* enable input mode */
	XmTextSetEditable(text_w, True);
	return(ret_val);
}

/*****
* Name: 		parserFatal
* Return Type: 	int
* Description: 	handles HTML_INTERNAL parser error
* In: 
*	cbs:		current parserCallbackStruct
* Returns:
*	selected action
*****/
static int 
parserFatal(XmHTMLParserPtr cbs)
{
	static Widget msg_box, def_bttn;
	static int answer;
	XmString xms;

	if(msg_box == NULL)
	{
		XmString ok_label, cancel_label;
		Arg args[10];
		int argc = 0;

		ok_label     = XmStringCreateLocalized("Terminate");
		cancel_label = XmStringCreateLocalized("Ignore");

		XtSetArg(args[argc], XmNokLabelString, ok_label); argc++;
		XtSetArg(args[argc], XmNcancelLabelString, cancel_label); argc++;

		msg_box= XmCreatePromptDialog(toplevel, "parserUnbalanced", args, argc);

		XmStringFree(ok_label);
		XmStringFree(cancel_label);

		/* unmanage help button */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_HELP_BUTTON));
		/* unmanage text area */
		XtUnmanageChild(XmSelectionBoxGetChild(msg_box, XmDIALOG_TEXT));

		def_bttn = XmSelectionBoxGetChild(msg_box, XmDIALOG_OK_BUTTON);

		XtVaSetValues(XtParent(msg_box), XtNtitle, "Fatal Parser Error", NULL);

		XtAddCallback(msg_box, XmNokCallback, (XtCallbackProc)response,
			&answer);
		XtAddCallback(msg_box, XmNcancelCallback, (XtCallbackProc)response,
			&answer);
	}
	/* create message string */
	xms = XmStringCreateLocalized("Internal Parser Error!");

	XtVaSetValues(msg_box,
		XmNselectionLabelString, xms,
		XmNdefaultButton, def_bttn,
		NULL);
	XmStringFree(xms);

	/* disable input mode */
	XmTextSetEditable(text_w, False);

	answer = 0;
	XtManageChild(msg_box);
	XtPopup(XtParent(msg_box), XtGrabNone);
	while(answer == 0)
		XtAppProcessEvent(context, XtIMAll);
	XtPopdown(XtParent(msg_box));

	/* enable input mode */
	XmTextSetEditable(text_w, True);

	return((answer == OK_BUTTON ? HTML_TERMINATE : HTML_IGNORE));
}

/*****
* Name: 		parserCB
* Return Type: 	void
* Description: 	the entire reason for the existance of this example.
*				Demonstrates a possible handling of the XmNparserCallback
*				resoure.
* In: 
*	widget:		owner of this callback
*	data:		client_data, unused
*	cbs:		call_data, parsercallback structure.
* Returns:
*	nothing.
*****/
static void
parserCB(Widget widget, XtPointer data, XmHTMLParserPtr cbs)
{
	int action;

	/*
	* There is also a HTML_NOTIFY error. This is not an error but just
	* a notification that XmHTML is inserting a missing closing element
	* on elements that have an optional terminator. These elements are:
	* <dd>, <dt>, <li>, <p>, <option>, <td>, <th> and <tr>.
	* XmHTML always inserts these elements when one is not found.
	* We choose not to display this info since we also have installed a
	* XmNmodifyVerifyCallback resource.
	*/
	switch(cbs->error)
	{
		case HTML_UNKNOWN_ELEMENT:
			action = parserUnknownHTML(cbs);
			break;
		case HTML_BAD:
		case HTML_VIOLATION:
			action = parserBadHTML(cbs);
			break;
		case HTML_OPEN_BLOCK:
		case HTML_CLOSE_BLOCK:
			action = parserBlock(cbs);
			break;
		case HTML_NESTED:
			action = parserNestedElement(cbs);
			break;
		case HTML_OPEN_ELEMENT:
			action = parserOpenElement(cbs);
			break;
		case HTML_INTERNAL:
			action = parserFatal(cbs);
			break;
		default:
			return;
	}
	cbs->action = action;
}

/*****
* Name: 		modifyHTMLCB
* Return Type: 	XmHTML's XmNmodifyVerifyCallback handler.
* Description:  inserts or cuts text depending on the type of modification.
* In: 
*	widget:		widget id;
*	data:		client_data, unused;
*	cbs:		description of modification.
* Returns:
*	nothing.
*****/
static char remove_text[] = "Removed %s at line %i";
static char insert_text[] = "Inserted missing %s at line %i";
/* make sure we don't exceed the text buffer */
#define MAKETEXT(FMT,TEXT,LINE) { \
	if(TEXT->len > 60) { \
		char tmp[60]; \
		strncpy(tmp,TEXT->ptr,59); tmp[59] = '\0'; \
		sprintf(text, FMT, tmp, LINE); \
	} else sprintf(text, FMT, TEXT->ptr, LINE); \
}

static void
modifyHTMLCB(Widget widget, XtPointer data, XmHTMLVerifyPtr cbs)
{
	char text[128];
	XmString xms;

	if(cbs->action == HTML_REMOVE)
	{
		cutText(cbs->start_pos, cbs->end_pos);
		MAKETEXT(remove_text, cbs->text, cbs->line);
	}
	else	/* cbs->action == HTML_INSERT */
	{
		insertText(cbs->text, cbs->start_pos);
		MAKETEXT(insert_text, cbs->text, cbs->line);
		/* and scroll if requested */
		if(scroll_text)
			XmTextShowPosition(text_w, cbs->start_pos);
	}
	xms = XmStringCreateLocalized(text);
	XtVaSetValues(info_label, XmNlabelString, xms, NULL);
	XmStringFree(xms);
	XmUpdateDisplay(text_w);
}

/*****
* Name: 		docCB
* Return Type: 	void
* Description: 	displays current document state as given by XmHTML.
* In: 
*	widget:		owner of this callback
*	data:		client_data, unused
*	cbs:		call_data, documentcallback structure.
* Returns:
*	nothing
*****/
static void
docCB(Widget widget, XtPointer data, XmHTMLDocumentPtr cbs)
{
	XmString xms;
	char text[128];
	Pixel my_pix = (Pixel)0;
	static Pixel red, green;

	/* internal call to reset the labels */
	if(cbs == NULL)
	{
		XtUnmanageChild(html32);
		XtUnmanageChild(verified);
		XtUnmanageChild(balanced);
		return;
	}

	/* see if we have been called with a valid reason */
	if(cbs->reason != XmCR_HTML_DOCUMENT)
		return;

	/* manage the labels */
	XtManageChild(verified);
	XtManageChild(balanced);
	XtManageChild(html32);

	if(!red)
		red = XmHTMLAllocColor(html_w, "Red", 
			BlackPixelOfScreen(XtScreen(toplevel)));

	if(!green)
		green = XmHTMLAllocColor(html_w, "green", 
			BlackPixelOfScreen(XtScreen(toplevel)));

	/* check if parser was terminated prematurely */
	if(cbs->terminated)
	{
		xms = XmStringCreateLocalized("Parser Terminated by User");
		XtVaSetValues(verified,
			XmNbackground, red,
			XmNlabelString, xms,
		NULL);
		XmStringFree(xms);
		XtUnmanageChild(balanced);
		XtUnmanageChild(html32);
		return;
	}

	if(cbs->html32)
	{
		sprintf(text, "HTML 3.2");
		my_pix = green;
	}
	else
	{
		sprintf(text, "Bad HTML 3.2");
		my_pix = red;
	}
	xms = XmStringCreateLocalized(text);
	XtVaSetValues(html32,
		XmNbackground, my_pix,
		XmNlabelString, xms,
		NULL);
	XmStringFree(xms);

	if(cbs->verified)
	{
		sprintf(text, "Verified");
		my_pix = green;
	}
	else
	{
		sprintf(text, "Unverified");
		my_pix = red;
	}
	xms = XmStringCreateLocalized(text);
	XtVaSetValues(verified,
		XmNbackground, my_pix,
		XmNlabelString, xms,
		NULL);
	XmStringFree(xms);

	if(cbs->balanced)
	{
		sprintf(text, "Balanced");
		my_pix = green;
	}
	else
	{
		sprintf(text, "Unbalanced");
		my_pix = red;
	}
	xms = XmStringCreateLocalized(text);
	XtVaSetValues(balanced,
		XmNbackground, my_pix,
		XmNlabelString, xms,
		NULL);
	XmStringFree(xms);

	/* use default action */
	if(!honor_document_cb)
		return;

	if(!cbs->balanced || !cbs->verified || !cbs->html32)
	{
		/* this document doesn't comply, ask if we should repeat it */
		if(!cbs->balanced)
			sprintf(text, "This document is not properly balanced.\n"
				"Do you want to me to try and repair this document again?");
		else if(!cbs->verified)
			sprintf(text, "This document is balanced, but verification "
				"failed.\nDo you want to me to verify this document again?");
		else if(!cbs->html32)
			sprintf(text, "This document is balanced and verified but "
			"not HTML 3.2 compliant\nDo you want me to try again?");
		/****
		* cbs->redo defines the return value from XmHTMLParserSetString and
		* XmHTMLParserEndSource.
		* When set to False, both will return True indicating that the parser
		* was successfull in parsing the given text. When set to True,
		* both will return False indicating another pass on the document
		* was requested.
		****/
		if(askUser(text))
			cbs->redo = True;
		else
			cbs->redo = False;
	}
	else
		tellUser("Document verified");
}

/*****
* Name:			doPreview
* Return Type:	void
* Description:	creates and possibly pop up the HTML preview dialog.
* In:
*	show:		True when file should be displayed.
* Returns:
*	nothing.
*****/
static void
doPreview(Boolean show)
{
	if(!preview_dialog)
	{
		Widget pb, frame;

		preview_dialog = XmCreateFormDialog(toplevel, "Preview", NULL, 0);
		
		XtVaSetValues(XtParent(preview_dialog),
			XtNtitle, "XmHTML Preview",
			NULL);

		/* create the exit button */
		pb = XtVaCreateManagedWidget("Dismiss",
			xmPushButtonWidgetClass, preview_dialog,
			XmNtopAttachment, XmATTACH_FORM,
			XmNtopOffset, 10,
			XmNleftAttachment, XmATTACH_FORM,
			XmNleftOffset, 10,
			NULL);

		/* add the exit callback */
		XtAddCallback(pb, XmNactivateCallback, (XtCallbackProc)dismissCB, 
			preview_dialog);

		/* create a frame as the html container */
		frame = XtVaCreateManagedWidget("frame",
			xmFrameWidgetClass, preview_dialog,
			XmNtopAttachment, XmATTACH_WIDGET,
			XmNtopWidget, pb,
			XmNtopOffset, 10,
			XmNleftAttachment, XmATTACH_FORM,
			XmNleftOffset, 10,
			XmNbottomAttachment, XmATTACH_FORM,
			XmNbottomOffset, 10,
			XmNrightAttachment, XmATTACH_FORM,
			XmNrightOffset, 10,
			XmNshadowType, XmSHADOW_IN,
			NULL);

		/* create the HTML widget using the default resources */
		html_w = XtVaCreateManagedWidget("htmlPreview",
			xmHTMLWidgetClass, frame,
			XmNwidth, 525,
			XmNheight, 550,
			NULL);

		/* add a simple anchor callback so XmHTML can jump to local anchors */
		XtAddCallback(html_w, XmNactivateCallback, 
			(XtCallbackProc)anchorCB, NULL);
	}

	if(show)
	{
		String buf;

		/* get text widget contents */
		buf = XmTextGetString(text_w);

		/* set it in the widget */
		XmHTMLTextSetString(html_w, buf);

		/* free it */
		if(buf)
			XtFree(buf);

		XtManageChild(preview_dialog);
		XMapRaised(XtDisplay(preview_dialog), XtWindow(preview_dialog));

		/* set focus on the HTML widget so we can use the keys */
		XmProcessTraversal(html_w, XmTRAVERSE_CURRENT);
	}
	else
		XtUnmanageChild(preview_dialog);
}

/*****
* Name: 		parseHTML
* Return Type: 	void
* Description: 	demonstrates how to use the XmHTMLParser object for regular
*				use.
* In: 
*	nothing
* Returns:
*	nothing
*****/
static void
parseHTML(void)
{
	String buf = NULL;

	/* reset progressive parsing mode */
	XmHTMLParserSetProgressiveMode(parser, False);

	/* we are busy and can't be interrupted */
	if(working_image)
	{
		XtVaSetValues(busy_label,
			XmNlabelType, XmPIXMAP,
			XmNlabelPixmap, working_image->frames[0].pixmap, NULL);
		/* make sure it's visible */
		XmUpdateDisplay(toplevel);
	}

	/* get contents from the text widget */
	if((buf = XmTextGetString(text_w)) == NULL)
		return;

	/* ParserSetString returns False when another pass was requested */
	while(!XmHTMLParserSetString(parser, buf))
	{
		/* free current buffer */
		free(buf);
		buf = NULL;

		/* get new contents from the text widget */
		if((buf = XmTextGetString(text_w)) == NULL)
			break;
	}
	/* reset busy label */
	resetBusyLabel();
	/* free current buffer */
	if(buf)
		free(buf);
}

static void
progressiveEnd(Widget work_dialog, XtPointer client_data, XtPointer call_data)
{
	/* get parser timeout id */
	if(current_id != None)
	{
		XtRemoveTimeOut(current_id);
		current_id = None;
	}

	/* premature end, abort parser */
	XmHTMLParserTerminateSource(parser);

	/* free current parser buffer */
	if(parser_buffer)
		free(parser_buffer);
	parser_buffer = NULL;

	resetBusyLabel();

	XtUnmanageChild(work_dialog);
}

static void
timerCB(XtPointer data, XtIntervalId *prev_id)
{
	/* next animation frame */
	XtVaSetValues(busy_label,
		XmNlabelType, XmPIXMAP,
		XmNlabelPixmap,
			working_image->frames[working_image->current_frame].pixmap,
		NULL);

	/* update frame count */
	working_image->current_frame++;
	if(working_image->current_frame == working_image->nframes)
		working_image->current_frame = 0;

	/* set new timeout for next frame */
	working_image->proc_id = XtAppAddTimeOut(context,
		working_image->frames[working_image->current_frame].timeout, timerCB,
		data);
}

static void
progressiveParse(XtPointer data, XtIntervalId *prev_id)
{
	static int source_len, part_len;
	String part_buf;
	Widget work_dialog = (Widget)data;
	XmString xms;
	char text[96];
	int done = False;

	if(!parser_active)
	{
		/* reset the progressive parser */
		XmHTMLParserSetString(parser, NULL);

		source_len = strlen(parser_buffer);
		part_len = 0;
		parser_active = True;
	}

	/* use small partial buffers for demonstration: 1024 bytes at a time */
	part_len +=1024;
	if(part_len > source_len)
		part_len = source_len;

	part_buf = (String)malloc(part_len+1);
	strncpy(part_buf, parser_buffer, part_len);
	part_buf[part_len] = '\0';	/* NULL terminate */

	/*
	* returns True as long as it can keep on parsing, the incoming text is
	* increasing in size *and* when the text already parsed doesn't differ
	* from the lower part of the newly added text. When the lower part
	* *does* differ or is smaller in size than the previous text, the parser
	* will assume that the buffer is corrupted and terminate, returning False
	* as it does. 
	* Since we are checking on end-of-text by ourselves, it's an error when
	* the parser should return False.
	*/
	done = !XmHTMLParserUpdateSource(parser, part_buf);

	free(part_buf);

	/* tell user how much we did so far */
	sprintf(text, "Percentage done: %-3i%%.", 
		(int)(100.*(part_len/(float)source_len)));
	xms = XmStringCreateLocalized(text);
	XtVaSetValues(work_dialog, XmNmessageString, xms, NULL);
	XmStringFree(xms);

	/* we are finished, wrapup and return */
	if(part_len == source_len || done)
	{
		/* free current parser buffer */
		if(parser_buffer)
			free(parser_buffer);
		parser_buffer = NULL;

		/* this parser is no longer active */
		parser_active = False;
		part_len = 0;
		current_id = None;
		/* pop down the dialog */
		XtUnmanageChild(work_dialog);
		XmUpdateDisplay(toplevel);

		/*
		* End the progressive parser by calling the XmHTMLProgressiveParseEnd
		* function. This function will perform all required cleanups, flush
		* any states left on the parser stack (and thus make the document
		* balanced) and call the XmNdocumentCallback.
		*
		* This function returns False when another pass on the document was
		* requested thru an installed XmNdocumentCallback. When such a
		* callback is not installed, it will advise another pass *only* if
		* the document isn't properly balanced.
		* Returns True when parser wrapup was successfully and no other pass
		* was requested.
		*/
		if(XmHTMLParserEndSource(parser))
		{
			/* visual feedback */
			resetBusyLabel();
			return;
		}
		/* Need to reparse the current document */

		/* get new parser buffer (text_w contains the updated source) */
		if((parser_buffer = XmTextGetString(text_w)) == NULL)
		{
			resetBusyLabel();
			return;
		}

		/* show the dialog again */
		XtManageChild(work_dialog);

		/* make sure dialog is up */
		XmUpdateDisplay(toplevel);
	}

	/* set new timeout proc and enter next loop */
	current_id = XtAppAddTimeOut(context, 250L, progressiveParse, data);
}

/*****
* Name: 		parseProgressiveHTML
* Return Type: 	void
* Description: 	use the progressive HTML parser to verify a document.
*				This is just an example routine to test whether the
*				progressive parser works as it should.
*				It has extra furry features ;-)
* In: 
*	nothing
* Returns:
*	nothing
*****/
static void
parseProgressiveHTML(void)
{
	static Widget work_dialog;
	XmString xms;

	/*
	* first get buffer from the text widget. We don't do a thing if
	* it's empty
	*/
	if((parser_buffer = XmTextGetString(text_w)) == NULL)
		return;

	if(!work_dialog)
	{
		work_dialog = XmCreateMessageDialog(toplevel, "parsing", NULL, 0);

		xms = XmStringCreateLocalized("Abort");
		XtVaSetValues(work_dialog, XmNcancelLabelString, xms, NULL);
		XmStringFree(xms);

		/* unmanage ok and help buttons */
		XtUnmanageChild(XmMessageBoxGetChild(work_dialog,XmDIALOG_OK_BUTTON));
		XtUnmanageChild(XmMessageBoxGetChild(work_dialog,XmDIALOG_HELP_BUTTON));

		/* set a title */
		XtVaSetValues(XtParent(work_dialog), XtNtitle, "Parsing", NULL);

		/* add a callback so we can abort the parser */
		XtAddCallback(work_dialog, XmNcancelCallback,
			(XtCallbackProc)progressiveEnd, (XtPointer)True);
	}

	/* switch parser in progressive mode */
	XmHTMLParserSetProgressiveMode(parser, True);

	/* and it is not active yet */
	parser_active = False;

	/* tell user we are initializing stuff */
	xms = XmStringCreateLocalized("Initialising Parser");
	XtVaSetValues(work_dialog, XmNmessageString, xms, NULL);
	XmStringFree(xms);

	if(working_image)
	{
		/* start label updating */
		XtVaSetValues(busy_label,
			XmNlabelType, XmPIXMAP,
			XmNlabelPixmap,
			working_image->frames[working_image->current_frame].pixmap,
			NULL);

		working_image->current_frame++;

		working_image->proc_id = XtAppAddTimeOut(context,
			working_image->frames[working_image->current_frame].timeout,
			timerCB, NULL);
	}

	/*
	* Set initial timeout proc. Uses a delay of a full second so X
	* will get the time to set up all dialogs and stuff.
	*/
	current_id = XtAppAddTimeOut(context, 1000L, progressiveParse, work_dialog);

	/* start it up */
	XtManageChild(work_dialog);
	XtPopup(XtParent(work_dialog), XtGrabNone);
}

/*****
* Name:			main
* Return Type:	int
* Description:	main for the interactive parser example.
* In:
*	argc:		no of command line args
*	argv:		array of command line args
* Returns:
*	exit_failure upon some startup error.
*****/
int
main(int argc, char **argv)
{
	Widget main_w, menubar, form, fr, pb;
	XmString file, html, fnew, load, save, verify, preview, quit;
	Arg args[15];
	int i, argcnt;
	char text[256];

	fprintf(stderr, "%s, %i\n", XmHTMLVERSION_STRING, XmHTMLGetVersion());

	/* check command line args for images */
	for(i = 1; i < argc; i++)
	{
		if(argv[i][0] == '-')
		{
			switch(argv[i][1])
			{
				case 'b':
					i++;
					if(i < argc)
						working_image_file = strdup(argv[i]);
					else
					{
						fprintf(stderr, "-b requires an additional argument\n");
						exit(EXIT_FAILURE);
					}
					break;
				case 'r':
					i++;
					if(i < argc)
						ready_image_file = strdup(argv[i]);
					else
					{
						fprintf(stderr, "-r requires an additional argument\n");
						exit(EXIT_FAILURE);
					}
					break;
				case 'h':
					fprintf(stderr, "Usage: example_3 -b <busy image> "
						"-r <ready image>\n");
					exit(EXIT_SUCCESS);
			}
		}
	}
	/* try to find our busy image */
	if(!working_image_file)
	{
		for(i = 0; i < 3 && working_image_file == NULL; i++)
		{
			if(i == 0)
				sprintf(text, "./busy.gif");
			else if (i == 1)
				sprintf(text, "Images/busy.gif");
			else
				sprintf(text, "../Images/busy.gif");
			if(!(access(text, R_OK)))
				working_image_file = strdup(text);
		}
		if(!working_image_file)
			fprintf(stderr, "Could not locate busy.gif\n");
	}
	/* try to find our ready image */
	if(!ready_image_file)
	{
		for(i = 0; i < 3 && ready_image_file == NULL; i++)
		{
			if(i == 0)
				sprintf(text, "./ready.gif");
			else if (i == 1)
				sprintf(text, "Images/ready.gif");
			else
				sprintf(text, "../Images/ready.gif");
			if(!(access(text, R_OK)))
				ready_image_file = strdup(text);
		}
		if(!ready_image_file)
			fprintf(stderr, "Could not locate ready.gif\n");
	}

	/* check if busy.gif is an animation */
	if(working_image_file)
	{
		unsigned char img_type;
		img_type = XmHTMLImageGetType(working_image_file, NULL, 0);
		if(img_type != IMAGE_GIFANIM && img_type != IMAGE_GIFANIMLOOP)
		{
			fprintf(stderr, "busy image %s is not an animated gif, not used.\n",
				working_image_file);
			free(working_image_file);
			working_image_file = NULL;
		}
	}

	/* create toplevel widget */
	toplevel = XtVaAppInitialize(&context, APP_CLASS, NULL, 0,
		&argc, argv, NULL, NULL, NULL);

	/* create mainWindow */
	main_w = XtVaCreateManagedWidget("mainWindow",
		xmMainWindowWidgetClass, toplevel,
		NULL);

	/* simple menubar with a File menu */
	file = XmStringCreateSimple("File");
	html = XmStringCreateSimple("HTML");
	fnew  = XmStringCreateSimple("Options");
	menubar = XmVaCreateSimpleMenuBar(main_w, "menubar",
		XmVaCASCADEBUTTON, file, 'F',
		XmVaCASCADEBUTTON, html, 'H',
		XmVaCASCADEBUTTON, fnew, 'O',
		NULL);
	XmStringFree(file);
	XmStringFree(html);
	XmStringFree(fnew);

	/* file menu items */
	fnew    = XmStringCreateLocalized("New");
	load    = XmStringCreateLocalized("Open");
	save    = XmStringCreateLocalized("Save As...");
	quit    = XmStringCreateLocalized("Quit");
	XmVaCreateSimplePulldownMenu(menubar, "file_menu", 0, 
		(XtCallbackProc)fileCB,
		XmVaPUSHBUTTON, fnew, 'N', NULL, NULL,
		XmVaPUSHBUTTON, load, 'O', NULL, NULL,
		XmVaPUSHBUTTON, save, 'S', NULL, NULL,
		XmVaSEPARATOR,
		XmVaPUSHBUTTON, quit, 'Q', NULL, NULL,
		NULL);
	XmStringFree(fnew);
	XmStringFree(load);
	XmStringFree(save);
	XmStringFree(quit);
	
	/* HTML menu */
	verify  = XmStringCreateLocalized("Verify Document");
	preview = XmStringCreateLocalized("HTML Preview");
	load    = XmStringCreateLocalized("Use Progressive Parser");
	save    = XmStringCreateLocalized("Save Parser Tree");
	XmVaCreateSimplePulldownMenu(menubar, "html_menu", 1, 
		(XtCallbackProc)htmlCB,
		XmVaPUSHBUTTON, verify, 'V', NULL, NULL,
		XmVaPUSHBUTTON, load, 'P', NULL, NULL,
		XmVaSEPARATOR,
		XmVaPUSHBUTTON, preview, 'H', NULL, NULL,
		XmVaSEPARATOR,
		XmVaPUSHBUTTON, save, 'S', NULL, NULL,
		NULL);
	XmStringFree(verify);
	XmStringFree(preview);
	XmStringFree(load);
	XmStringFree(save);

	/* options menu */
	preview = XmStringCreateLocalized("Auto Scroll");
	load    = XmStringCreateLocalized("Interactive Mode");
	save    = XmStringCreateLocalized("Notify When Ready");
	XmVaCreateSimplePulldownMenu(menubar, "option_menu", 2, 
		(XtCallbackProc)optionsCB,
		XmVaTOGGLEBUTTON, preview, 'A', NULL, NULL,
		XmVaTOGGLEBUTTON, load , 'I', NULL, NULL,
		XmVaTOGGLEBUTTON, save, 'N', NULL, NULL,
		NULL);
	XmStringFree(preview);
	XmStringFree(load);
	XmStringFree(save);
	/* menubar is done */
	XtManageChild(menubar);

	/* form as the workarea */
	form = XtVaCreateWidget("form",
		xmFormWidgetClass, main_w,
		XmNresizePolicy, XmRESIZE_ANY,
		NULL);

	/* Text area */
	argcnt = 0;
	XtSetArg(args[argcnt], XmNrows, 25); argcnt++;
	XtSetArg(args[argcnt], XmNcolumns, 80); argcnt++;
	XtSetArg(args[argcnt], XmNeditMode, XmMULTI_LINE_EDIT); argcnt++;
	XtSetArg(args[argcnt], XmNtopAttachment, XmATTACH_FORM); argcnt++;
	XtSetArg(args[argcnt], XmNleftAttachment, XmATTACH_FORM); argcnt++;
	XtSetArg(args[argcnt], XmNrightAttachment, XmATTACH_FORM); argcnt++;
	XtSetArg(args[argcnt], XmNtopOffset, 10); argcnt++;
	XtSetArg(args[argcnt], XmNleftOffset, 10); argcnt++;
	XtSetArg(args[argcnt], XmNrightOffset, 10); argcnt++;
	text_w = XmCreateScrolledText(form, "textWindow", args, argcnt);

	XtAddCallback(text_w, XmNmodifyVerifyCallback,
		(XtCallbackProc)modifyCB, NULL);
	
	XtManageChild(text_w);

	/* a frame as the surrounder for the info area */
	fr = XtVaCreateManagedWidget("docInfoForm",
		xmFrameWidgetClass, form,
		XmNshadowType,    XmSHADOW_IN,
		XmNleftAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_FORM,
		XmNbottomAttachment,XmATTACH_FORM,
		XmNrightOffset, 10,
		XmNleftOffset, 10,
		XmNbottomOffset, 10,
		NULL);

	pb = XtVaCreateManagedWidget("anchorRefFrame",
		xmFormWidgetClass, fr,
		NULL);

    /* create a label to show some visible feedback */
	busy_label = XtVaCreateManagedWidget("busyLabel",
		xmLabelGadgetClass, pb,
		XmNalignment, XmALIGNMENT_BEGINNING,
		XmNlabelType, XmPIXMAP,
		XmNtopAttachment, XmATTACH_FORM,
		XmNleftAttachment, XmATTACH_FORM,
		NULL);

	info_label = XtVaCreateManagedWidget("busyLabel",
		xmLabelGadgetClass, pb,
		XmNalignment, XmALIGNMENT_BEGINNING,
		XtVaTypedArg, XmNlabelString, XmRString, "Ready", 6,
		XmNtopAttachment, XmATTACH_FORM,
		XmNleftAttachment, XmATTACH_WIDGET,
		XmNleftWidget, busy_label,
		NULL);

    /* a label to indicate whether parser succeeded with verification */
	verified = XtVaCreateManagedWidget("documentVerified",
		xmLabelGadgetClass, pb,
		XmNalignment, XmALIGNMENT_CENTER,
		XtVaTypedArg, XmNlabelString, XmRString, " ", 2,
		XmNtopAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_FORM,
		NULL);

    /* a label to a balanced document */
	balanced = XtVaCreateManagedWidget("documentBalanced",
		xmLabelGadgetClass, pb,
		XmNalignment, XmALIGNMENT_CENTER,
		XtVaTypedArg, XmNlabelString, XmRString, " ", 2,
		XmNtopAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, verified,
		XmNrightOffset, 5,
		XmNleftOffset, 10,
		NULL);

    /* a label to indicate HTML 3.2 conformance */
	html32 = XtVaCreateManagedWidget("documentHTML32",
		xmLabelGadgetClass, pb,
		XmNalignment, XmALIGNMENT_CENTER,
		XtVaTypedArg, XmNlabelString, XmRString, " ", 2,
		XmNtopAttachment, XmATTACH_FORM,
		XmNrightAttachment, XmATTACH_WIDGET,
		XmNrightWidget, balanced,
		XmNrightOffset, 5,
		XmNleftOffset, 10,
		NULL);

	/* set final constraints on the html container */
	XtVaSetValues(XtParent(text_w),
		XmNbottomAttachment, XmATTACH_WIDGET,
		XmNbottomWidget, fr,
		XmNbottomOffset, 10,
		NULL);

	/* all widgets have been created, manage them */
	XtManageChild(form);

	XmMainWindowSetAreas(main_w, menubar, NULL, NULL, NULL, form);

	/* create preview dialog */
	doPreview(False);

	/* create a HTML parser */
	parser = XmCreateHTMLParser(toplevel, "HTMLparser", NULL, 0);

	/* parserCallback for interactive checking */
	XtAddCallback(parser, XmNparserCallback, (XtCallbackProc)parserCB, NULL);

	/* modifyVerifyCallback for real-time source updating */
	XtAddCallback(parser, XmNmodifyVerifyCallback,
		(XtCallbackProc)modifyHTMLCB, NULL);

	/* documentCallback for end-of-document notification */
	XtAddCallback(parser, XmNdocumentCallback, (XtCallbackProc)docCB, NULL);

	/* no auto mode */
	XmHTMLParserSetAutoMode(parser, False);

	/* no notification */
	honor_document_cb = False;

	/* no auto scrolling */
	scroll_text = False;

	/* create the images we will be using for visual feedback */
	if(working_image_file)
	{
		Pixel bg_pixel = 0;
		XmImageConfig config;

		XtVaGetValues(toplevel, XmNbackground, &bg_pixel, NULL);

		config.ncolors      = 216;
		config.bg_color     = bg_pixel;
		config.flags        = ImageQuantize|ImageFSDither|ImageBackground;

		working_image = XmImageCreate(toplevel, working_image_file, 0, 0, 
			&config);
		if(working_image == NULL)
		{
			/* no warn, XmHTML will spit it out */
			free(working_image_file);
			working_image_file = NULL;
		}
	}
	if(ready_image_file)
	{
		Pixel bg_pixel = 0;
		XmImageConfig config;

		XtVaGetValues(toplevel, XmNbackground, &bg_pixel, NULL);

		config.ncolors      = 216;
		config.bg_color     = bg_pixel;
		config.flags        = ImageQuantize|ImageFSDither|ImageBackground;

		ready_image = XmImageCreate(toplevel, ready_image_file, 0, 0, &config);

		if(ready_image == NULL)
		{
			/* no warn, XmHTML will spit it out */
			free(ready_image_file);
			working_image_file = NULL;
		}
	}

	/* realize everything */
	XtRealizeWidget(toplevel);

	/* Set window title */
	setTitle(use_file);

	/* set ready label */
	resetBusyLabel();

	/* enter the event loop */
	XtAppMainLoop(context);

	/* never reached, but keeps compiler happy */
	exit(EXIT_SUCCESS);
}
