/****************************************************************************
 * 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 "HTMLP.h"
#ifdef MOTIF
#include <Xm/DrawingA.h>
#include <Xm/ScrollBar.h>
#else
#include "DrawingArea.h"
#include <X11/Xaw/Scrollbar.h>
#endif
#include <X11/cursorfont.h>


#define	MARGIN_DEFAULT		20
#define	CLICK_TIME		500
#define	SELECT_THRESHOLD	3
#define	MAX_UNDERLINES		3
#define DEFAULT_INCREMENT       18

#ifndef ABS
#define ABS(x)  (((x) > 0) ? (x) : ((x) * -1))
#endif

#define	W_TEXTFIELD	0
#define	W_CHECKBOX	1
#define	W_RADIOBOX	2
#define	W_PUSHBUTTON	3
#define	W_PASSWORD	4
#define	W_OPTIONMENU	5


extern int FormatAll();
extern int DocumentWidth();
extern void PlaceLine();
extern void TextRefresh();
extern void ImageRefresh();
extern void LinefeedRefresh();
extern void RefreshTextRange();
extern void FreeColors();
extern void FreeImages();
extern void HideWidgets();
extern void MapWidgets();
extern int SwapElements();
extern int ElementLessThan();
extern int IsDelayedHRef();
extern int IsIsMapForm();
extern int AnchoredHeight();
extern char *ParseMarkTag();
extern char *ParseTextToString();
extern char *ParseTextToPrettyString();
extern char *ParseTextToPSString();
extern struct mark_up *HTMLParse();
extern struct ele_rec *LocateElement();
extern struct ele_rec **MakeLineList();
extern void FreeHRefs();
extern struct ref_rec *AddHRef();
extern void FreeDelayedImages();
extern struct delay_rec *AddDelayedImage();
extern ImageInfo *NoImageData();
extern void ImageSubmitForm();


static void		SelectStart();
static void		ExtendStart();
static void		ExtendAdjust();
static void		ExtendEnd();
static void             TrackMotion();
static Boolean		ConvertSelection();
static void		LoseSelection();
static void		SelectionDone();


#ifdef _NO_PROTO

static void		_HTMLInput() ;
#ifndef MOTIF
static void		_HTMLpwdInput() ;
#endif
static void             Initialize() ;
static void             Redisplay() ;
static void             Resize() ;
static Boolean          SetValues() ;
static XtGeometryResult	GeometryManager() ;
static void		RecolorInternalHRefs() ;
static Dimension	VbarWidth();
static Dimension	HbarHeight();
static void		ViewRedisplay();
static void		ViewClearAndRefresh();
static void		CallLinkCallbacks();

#else /* _NO_PROTO */

static void		_HTMLInput(Widget w, XEvent *event,
				String *params, Cardinal *num_params);
#ifndef MOTIF
static void		_HTMLpwdInput(Widget w, XEvent *event,
				String *params, Cardinal *num_params);
#endif
static void             Initialize(HTMLWidget request, HTMLWidget new);
static void             Redisplay(HTMLWidget hw, XEvent *event, Region region);
static void             Resize(HTMLWidget hw);
static Boolean          SetValues(HTMLWidget current, HTMLWidget request,
				HTMLWidget new);
static XtGeometryResult GeometryManager(Widget w, XtWidgetGeometry *request,
				XtWidgetGeometry *reply);
static void		RecolorInternalHRefs(HTMLWidget hw, char *href);
static Dimension        VbarWidth(HTMLWidget hw);
static Dimension        HbarHeight(HTMLWidget hw);
static void		ViewRedisplay(HTMLWidget hw, int x, int y,
				int width, int height);
static void             ViewClearAndRefresh(HTMLWidget hw);
static void             CallLinkCallbacks(HTMLWidget hw);
#endif /* _NO_PROTO */


/*
 * Default translations
 * Selection of text, and activate anchors.
 * If motif, add manager translations.
 */
#ifdef MOTIF
static char defaultTranslations[] =
" \
<Btn1Down>:	select-start() ManagerGadgetArm()\n\
<Btn1Motion>:	extend-adjust() ManagerGadgetButtonMotion()\n\
<Btn1Up>:	extend-end(PRIMARY, CUT_BUFFER0) ManagerGadgetActivate()\n\
<Btn2Down>:	select-start()\n\
<Btn2Motion>:	extend-adjust()\n\
<Btn2Up>:	extend-end(PRIMARY, CUT_BUFFER0)\n\
<Btn3Down>:	extend-start()\n\
<Btn3Motion>:	extend-adjust()\n\
<Btn3Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
<Motion>:       track-motion()\n\
<Leave>:        track-motion()\n\
<FocusOut>:     track-motion()\n\
<Expose>:       track-motion()\
";
#else
static char defaultTranslations[] =
" \
<Btn1Down>:	select-start() \n\
<Btn1Motion>:	extend-adjust() \n\
<Btn1Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
<Btn2Down>:	select-start() \n\
<Btn2Motion>:	extend-adjust() \n\
<Btn2Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
<Btn3Down>:	extend-start()\n\
<Btn3Motion>:	extend-adjust()\n\
<Btn3Up>:	extend-end(PRIMARY, CUT_BUFFER0) \n\
<Motion>:       track-motion()\n\
<Leave>:        track-motion()\n\
<FocusOut>:     track-motion()\n\
<Expose>:       track-motion()\
";
#endif /* MOTIF */


static XtActionsRec actionsList[] =
{
   { "select-start",    (XtActionProc) SelectStart },
   { "extend-start",    (XtActionProc) ExtendStart },
   { "extend-adjust",   (XtActionProc) ExtendAdjust },
   { "extend-end",      (XtActionProc) ExtendEnd },
   { "track-motion",    (XtActionProc) TrackMotion },
   { "HTMLInput",	(XtActionProc) _HTMLInput },
#ifndef MOTIF
   { "HTMLpwdInput",	(XtActionProc) _HTMLpwdInput },
#endif

#ifdef MOTIF
#ifndef MOTIF1_2
   { "Arm",      (XtActionProc) _XmGadgetArm },         /* Motif 1.0 */
   { "Activate", (XtActionProc) _XmGadgetActivate },    /* Motif 1.0 */
   { "Enter",    (XtActionProc) _XmManagerEnter },      /* Motif 1.0 */
   { "FocusIn",  (XtActionProc) _XmManagerFocusIn },    /* Motif 1.0 */
   { "Help",     (XtActionProc) _XmManagerHelp },       /* Motif 1.0 */
#endif /* not MOTIF1_2 */
#endif /* MOTIF */
};

/*
 * For some reason, in Motif1.2/X11R5 the actionsList above gets corrupted
 * When the parent HTML widget is created.  This means we can't use
 * it later with XtAppAddActions to add to the viewing area.
 * So, we make a spare copy here to use with XtAppAddActions.
 */
static XtActionsRec SpareActionsList[] =
{
   { "select-start",    (XtActionProc) SelectStart },
   { "extend-start",    (XtActionProc) ExtendStart },
   { "extend-adjust",   (XtActionProc) ExtendAdjust },
   { "extend-end",      (XtActionProc) ExtendEnd },
   { "track-motion",    (XtActionProc) TrackMotion },
   { "HTMLInput",	(XtActionProc) _HTMLInput },
#ifndef MOTIF
   { "HTMLpwdInput",	(XtActionProc) _HTMLpwdInput },
#endif
};



/*
 *  Resource definitions for HTML widget
 */

static XtResource resources[] =
{
  /* Without Motif we need to override the borderWidth to 0 (from 1). */
#ifndef MOTIF
        {       XtNborderWidth,
                XtCBorderWidth, XtRDimension, sizeof (Dimension),
                XtOffset (HTMLWidget, core.border_width),
                XtRImmediate, (XtPointer) 0
        },
#endif

	{	WbNmarginWidth,
		WbCMarginWidth, XtRDimension, sizeof (Dimension),
		XtOffset (HTMLWidget, html.margin_width),
		XtRImmediate, (caddr_t) MARGIN_DEFAULT
	},

	{	WbNmarginHeight,
		WbCMarginHeight, XtRDimension, sizeof (Dimension),
		XtOffset (HTMLWidget, html.margin_height),
		XtRImmediate, (caddr_t) MARGIN_DEFAULT
	},

	{	WbNanchorCallback,
		XtCCallback, XtRCallback, sizeof (XtCallbackList),
		XtOffset (HTMLWidget, html.anchor_callback),
		XtRImmediate, (caddr_t) NULL
	},

	{	WbNlinkCallback,
		XtCCallback, XtRCallback, sizeof (XtCallbackList),
		XtOffset (HTMLWidget, html.link_callback),
		XtRImmediate, (caddr_t) NULL
	},

	{	WbNsubmitFormCallback,
		XtCCallback, XtRCallback, sizeof (XtCallbackList),
		XtOffset (HTMLWidget, html.form_callback),
		XtRImmediate, (caddr_t) NULL
	},

	{	WbNtext,
		WbCText, XtRString, sizeof (char *),
		XtOffset (HTMLWidget, html.raw_text),
		XtRString, (char *) NULL
	},

	{	WbNheaderText,
		WbCHeaderText, XtRString, sizeof (char *),
		XtOffset (HTMLWidget, html.header_text),
		XtRString, (char *) NULL
	},

	{	WbNfooterText,
		WbCFooterText, XtRString, sizeof (char *),
		XtOffset (HTMLWidget, html.footer_text),
		XtRString, (char *) NULL
	},

	{	WbNtitleText,
		WbCTitleText, XtRString, sizeof (char *),
		XtOffset (HTMLWidget, html.title),
		XtRString, (char *) NULL
	},

/*
 * Without motif we need our own foreground resource instead of
 * using the manager's
 */
#ifndef MOTIF
	{	XtNforeground,
		XtCForeground, XtRPixel, sizeof (Pixel),
		XtOffset (HTMLWidget, html.foreground),
		XtRString, "Black"
	},
#endif

	{	WbNanchorUnderlines,
		WbCAnchorUnderlines, XtRInt, sizeof (int),
		XtOffset (HTMLWidget, html.num_anchor_underlines),
		XtRString, "0"
	},

	{	WbNvisitedAnchorUnderlines,
		WbCVisitedAnchorUnderlines, XtRInt, sizeof (int),
		XtOffset (HTMLWidget, html.num_visitedAnchor_underlines),
		XtRString, "0"
	},

	{	WbNdashedAnchorUnderlines,
		WbCDashedAnchorUnderlines, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.dashed_anchor_lines),
		XtRString, "False"
	},

	{	WbNdashedVisitedAnchorUnderlines,
		WbCDashedVisitedAnchorUnderlines, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.dashed_visitedAnchor_lines),
		XtRString, "False"
	},

	{	WbNanchorColor,
		XtCForeground, XtRPixel, sizeof (Pixel),
		XtOffset (HTMLWidget, html.anchor_fg),
		XtRString, "blue2"
	},

	{	WbNvisitedAnchorColor,
		XtCForeground, XtRPixel, sizeof (Pixel),
		XtOffset (HTMLWidget, html.visitedAnchor_fg),
		XtRString, "purple4"
	},

	{	WbNactiveAnchorFG,
		XtCBackground, XtRPixel, sizeof (Pixel),
		XtOffset (HTMLWidget, html.activeAnchor_fg),
		XtRString, "Red"
	},

	{	WbNactiveAnchorBG,
		XtCForeground, XtRPixel, sizeof (Pixel),
		XtOffset (HTMLWidget, html.activeAnchor_bg),
		XtRString, "White"
	},

	{	WbNpercentVerticalSpace,
		WbCPercentVerticalSpace, XtRInt, sizeof (int),
		XtOffset (HTMLWidget, html.percent_vert_space),
		XtRString, "90"
	},

	{	WbNimageBorders,
		WbCImageBorders, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.border_images),
		XtRString, "False"
	},

	{	WbNdelayImageLoads,
		WbCDelayImageLoads, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.delay_images),
		XtRString, "False"
	},

	{	WbNfancySelections,
		WbCFancySelections, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.fancy_selections),
		XtRString, "False"
	},

	{	WbNisIndex,
		WbCIsIndex, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.is_index),
		XtRString, "False"
	},

	{	WbNview,
		WbCView, XtRWidget, sizeof (Widget),
		XtOffset (HTMLWidget, html.view),
		XtRImmediate, NULL
	},

	{	WbNverticalScrollBar,
		WbCVerticalScrollBar, XtRWidget, sizeof (Widget),
		XtOffset (HTMLWidget, html.vbar),
		XtRImmediate, NULL
	},

	{	WbNhorizontalScrollBar,
		WbCHorizontalScrollBar, XtRWidget, sizeof (Widget),
		XtOffset (HTMLWidget, html.hbar),
		XtRImmediate, NULL
	},

	{	WbNverticalScrollOnRight,
		WbCVerticalScrollOnRight, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.vbar_right),
		XtRString, "True"
	},

	{	WbNhorizontalScrollOnTop,
		WbCHorizontalScrollOnTop, XtRBoolean, sizeof (Boolean),
		XtOffset (HTMLWidget, html.hbar_top),
		XtRString, "False"
	},

	{	XtNfont,
		XtCFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.font),
		XtRString, "-adobe-times-medium-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNitalicFont,
		WbCItalicFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.italic_font),
		XtRString, "-adobe-times-medium-i-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNboldFont,
		WbCBoldFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.bold_font),
		XtRString, "-adobe-times-bold-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNfixedFont,
		WbCFixedFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.fixed_font),
		XtRString, "-adobe-courier-medium-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNfixedboldFont,
		WbCFixedboldFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.fixedbold_font),
		XtRString, "-adobe-courier-bold-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNfixeditalicFont,
		WbCFixeditalicFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.fixeditalic_font),
		XtRString, "-adobe-courier-medium-o-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNheader1Font,
		WbCHeader1Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header1_font),
		XtRString, "-adobe-times-bold-r-normal-*-24-*-*-*-*-*-*-*"
	},

	{	WbNheader2Font,
		WbCHeader2Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header2_font),
		XtRString, "-adobe-times-bold-r-normal-*-18-*-*-*-*-*-*-*"
	},

	{	WbNheader3Font,
		WbCHeader3Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header3_font),
		XtRString, "-adobe-times-bold-r-normal-*-17-*-*-*-*-*-*-*"
	},

	{	WbNheader4Font,
		WbCHeader4Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header4_font),
		XtRString, "-adobe-times-bold-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNheader5Font,
		WbCHeader5Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header5_font),
		XtRString, "-adobe-times-bold-r-normal-*-12-*-*-*-*-*-*-*"
	},

	{	WbNheader6Font,
		WbCHeader6Font, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.header6_font),
		XtRString, "-adobe-times-bold-r-normal-*-10-*-*-*-*-*-*-*"
	},

	{	WbNaddressFont,
		WbCAddressFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.address_font),
		XtRString, "-adobe-times-medium-i-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNplainFont,
		WbCPlainFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.plain_font),
		XtRString, "-adobe-courier-medium-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNplainboldFont,
		WbCPlainboldFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.plainbold_font),
		XtRString, "-adobe-courier-bold-r-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNplainitalicFont,
		WbCPlainitalicFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.plainitalic_font),
		XtRString, "-adobe-courier-medium-o-normal-*-14-*-*-*-*-*-*-*"
	},

	{	WbNlistingFont,
		WbCListingFont, XtRFontStruct, sizeof (XFontStruct *),
		XtOffset (HTMLWidget, html.listing_font),
		XtRString, "-adobe-courier-medium-r-normal-*-12-*-*-*-*-*-*-*"
	},
                  
        {       WbNpreviouslyVisitedTestFunction,
                WbCPreviouslyVisitedTestFunction, XtRPointer, 
                sizeof (XtPointer),
                XtOffset (HTMLWidget, html.previously_visited_test),
                XtRImmediate, (caddr_t) NULL
        },
                  
        {       WbNresolveImageFunction,
                WbCResolveImageFunction, XtRPointer, 
                sizeof (XtPointer),
                XtOffset (HTMLWidget, html.resolveImage),
                XtRImmediate, (caddr_t) NULL
        },
                  
        {       WbNresolveDelayedImage,
                WbCResolveDelayedImage, XtRPointer, 
                sizeof (XtPointer),
                XtOffset (HTMLWidget, html.resolveDelayedImage),
                XtRImmediate, (caddr_t) NULL
        },

        {
                WbNpointerMotionCallback,
                WbCPointerMotionCallback, XtRPointer, 
                sizeof (XtPointer),
                XtOffset (HTMLWidget, html.pointer_motion_callback),
                XtRImmediate, (caddr_t) NULL
        },
                    
};



HTMLClassRec htmlClassRec = {
   {						/* core class fields  */
#ifdef MOTIF
      (WidgetClass) &xmManagerClassRec,		/* superclass         */
#else
      (WidgetClass) &constraintClassRec,	/* superclass         */
#endif /* MOTIF */
      "HTML",					/* class_name         */
      sizeof(HTMLRec),				/* widget_size        */
      NULL,					/* class_initialize   */
      NULL,					/* class_part_init    */
      FALSE,					/* class_inited       */
      (XtInitProc) Initialize,			/* initialize         */
      NULL,					/* initialize_hook    */
      XtInheritRealize,				/* realize            */
      actionsList,				/* actions	      */
      XtNumber(actionsList),			/* num_actions	      */
      resources,				/* resources          */
      XtNumber(resources),			/* num_resources      */
      NULLQUARK,				/* xrm_class          */
      TRUE,					/* compress_motion    */
      FALSE,					/* compress_exposure  */
      TRUE,					/* compress_enterlv   */
      FALSE,					/* visible_interest   */
      NULL,			                /* destroy            */
      (XtWidgetProc) Resize,			/* resize             */
      (XtExposeProc) Redisplay,			/* expose             */
      (XtSetValuesFunc) SetValues,		/* set_values         */
      NULL,					/* set_values_hook    */
      XtInheritSetValuesAlmost,			/* set_values_almost  */
      NULL,					/* get_values_hook    */
      NULL,					/* accept_focus       */
      XtVersion,				/* version            */
      NULL,					/* callback_private   */
      defaultTranslations,			/* tm_table           */
      XtInheritQueryGeometry,			/* query_geometry     */
      XtInheritDisplayAccelerator,              /* display_accelerator*/
      NULL,		                        /* extension          */
   },

   {		/* composite_class fields */
      (XtGeometryHandler) GeometryManager,   	/* geometry_manager   */
      NULL,					/* change_managed     */
      XtInheritInsertChild,			/* insert_child       */
      XtInheritDeleteChild,			/* delete_child       */
      NULL,                                     /* extension          */
   },

   {		/* constraint_class fields */
      NULL,					/* resource list        */   
      0,					/* num resources        */   
      0,					/* constraint size      */   
      NULL,					/* init proc            */   
      NULL,					/* destroy proc         */   
      NULL,					/* set values proc      */   
      NULL,                                     /* extension            */
   },

#ifdef MOTIF
   {		/* manager_class fields */
      XtInheritTranslations,			/* translations           */
      NULL,					/* syn_resources      	  */
      0,					/* num_syn_resources 	  */
      NULL,					/* syn_cont_resources     */
      0,					/* num_syn_cont_resources */
      XmInheritParentProcess,                   /* parent_process         */
      NULL,					/* extension 	          */    
   },
#endif /* MOTIF */

   {		/* html_class fields */     
      0						/* none			  */
   }	
};

WidgetClass htmlWidgetClass = (WidgetClass)&htmlClassRec;

static Cursor in_anchor_cursor = (Cursor)NULL;


/*
 * Process an expose event in the View (or drawing area).  This
 * Can be a regular expose event, or perhaps a GraphicsExpose Event.
 */
static void
DrawExpose(w, data, event)
	Widget w;
	caddr_t data;
	XEvent *event;
{
	XExposeEvent *ExEvent = (XExposeEvent *)event;
	HTMLWidget hw = (HTMLWidget)data;
	int x, y;
	int width, height;

	if ((event->xany.type != Expose)&&(event->xany.type != GraphicsExpose))
	{
		return;
	}

	/*
	 * Make sure we have a valid GC to draw with.
	 */
	if (hw->html.drawGC == NULL)
	{
		unsigned long valuemask;
		XGCValues values;

		values.function = GXcopy;
		values.plane_mask = AllPlanes;
/*
 * Without motif we use our own foreground resource instead of
 * using the manager's
 */
#ifdef MOTIF
		values.foreground = hw->manager.foreground;
#else
		values.foreground = hw->html.foreground;
#endif /* MOTIF */
		values.background = hw->core.background_pixel;

		valuemask = GCFunction|GCPlaneMask|GCForeground|GCBackground;

		hw->html.drawGC = XCreateGC(XtDisplay(hw), XtWindow(hw),
			valuemask, &values);
	}

	x = ExEvent->x;
	y = ExEvent->y;
	width = (int)ExEvent->width;
	height = (int)ExEvent->height;

#ifdef DEBUG
DebugHook(x, y, width, height);
#endif

	ViewRedisplay(hw, x, y, width, height);
}


void
ScrollWidgets(hw)
	HTMLWidget hw;
{
	WidgetInfo *wptr;
	int xval, yval;

	xval = hw->html.scroll_x;
	yval = hw->html.scroll_y;
	wptr = hw->html.widget_list;
	while (wptr != NULL)
	{
		if (wptr->w != NULL)
		{
			Widget w;
			int x, y;

			w = wptr->w;
			x = wptr->x;
			y = wptr->y;
			XtMoveWidget(w, (x - xval), (y - yval));
		}
		wptr = wptr->next;
	}
}


#ifndef MOTIF
/*
 * Set the Athena Scrollbar's thumb position properly.
 */
static void
setScrollBar(sb, topPosition, totalLength, currentLength)
	Widget sb;
	Position topPosition;
	Dimension totalLength, currentLength;
{
	float top   = (float)topPosition  /(float)(totalLength);
	float shown = (float)currentLength/(float)(totalLength);

	XawScrollbarSetThumb(sb, top, shown);
}
#endif


/*
 * Either the vertical or hortizontal scrollbar has been moved
 */
void
ScrollToPos(w, hw, value)
	Widget w;
	HTMLWidget hw;
	int value;
{
	/*
	 * Special code incase the scrollbar is "moved" before we have a window
	 * (if we have a GC we have a window)
	 */
	if (hw->html.drawGC == NULL)
	{
		if (w == hw->html.vbar)
		{
			hw->html.scroll_y = value;
		}
		else if (w == hw->html.hbar)
		{
			hw->html.scroll_x = value;
		}
		return;
	}

	/*
	 * get our widgets out of the way (No Expose events)
	HideWidgets(hw);
	 */

	/*
	 * If we've moved the vertical scrollbar
	 */
	if (w == hw->html.vbar)
	{
		/*
		 * We've scrolled down. Copy up the untouched part of the
		 * window.  Then Clear and redraw the new area
		 * exposed.
		 */
		if (value > hw->html.scroll_y)
		{
			int dy;
	    
			dy = value - hw->html.scroll_y;
			if (dy > hw->html.view_height)
			{
				hw->html.scroll_y = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					hw->html.view_width,
					hw->html.view_height, False);
				ViewRedisplay(hw,
					0, 0,
					hw->html.view_width,
					hw->html.view_height);
			}
			else
			{
				XCopyArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					XtWindow(hw->html.view),
					hw->html.drawGC, 0, dy,
					hw->html.view_width,
					hw->html.view_height - dy,
					0, 0);
				hw->html.scroll_y = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, (int)hw->html.view_height - dy,
					hw->html.view_width, dy, False);
				ViewRedisplay(hw,
					0, (int)hw->html.view_height - dy,
					hw->html.view_width, dy);
			}
		}
		/*
		 * We've scrolled up. Copy down the untouched part of the
		 * window.  Then Clear and redraw the new area
		 * exposed.
		 */
		else if (value < hw->html.scroll_y)
		{
			int dy;
	    
			dy = hw->html.scroll_y - value;
			if (dy > hw->html.view_height)
			{
				hw->html.scroll_y = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					hw->html.view_width,
					hw->html.view_height, False);
				ViewRedisplay(hw,
					0, 0,
					hw->html.view_width,
					hw->html.view_height);
			}
			else
			{
				XCopyArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					XtWindow(hw->html.view),
					hw->html.drawGC, 0, 0,
					hw->html.view_width,
					hw->html.view_height - dy,
					0, dy);
				hw->html.scroll_y = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					hw->html.view_width, dy, False);
				ViewRedisplay(hw,
					0, 0,
					hw->html.view_width, dy);
			}
		}
	}
	/*
	 * Else we've moved the horizontal scrollbar
	 */
	else if (w == hw->html.hbar)
	{
		/*
		 * We've scrolled right. Copy left the untouched part of the
		 * window.  Then Clear and redraw the new area
		 * exposed.
		 */
		if (value > hw->html.scroll_x)
		{
			int dx;
	    
			dx = value - hw->html.scroll_x;
			if (dx > hw->html.view_width)
			{
				hw->html.scroll_x = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					hw->html.view_width,
					hw->html.view_height, False);
				ViewRedisplay(hw,
					0, 0,
					hw->html.view_width,
					hw->html.view_height);
			}
			else
			{
				XCopyArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					XtWindow(hw->html.view),
					hw->html.drawGC, dx, 0,
					hw->html.view_width - dx,
					hw->html.view_height,
					0, 0);
				hw->html.scroll_x = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					(int)hw->html.view_width - dx, 0,
					dx, hw->html.view_height, False);
				ViewRedisplay(hw,
					(int)hw->html.view_width - dx, 0,
					dx, hw->html.view_height);
			}
		}
		/*
		 * We've scrolled left. Copy right the untouched part of the
		 * window.  Then Clear and redraw the new area
		 * exposed.
		 */
		else if (value < hw->html.scroll_x)
		{
			int dx;
	    
			dx = hw->html.scroll_x - value;
			if (dx > hw->html.view_width)
			{
				hw->html.scroll_x = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					hw->html.view_width,
					hw->html.view_height, False);
				ViewRedisplay(hw,
					0, 0,
					hw->html.view_width,
					hw->html.view_height);
			}
			else
			{
				XCopyArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					XtWindow(hw->html.view),
					hw->html.drawGC, 0, 0,
					hw->html.view_width - dx,
					hw->html.view_height,
					dx, 0);
				hw->html.scroll_x = value;
				XClearArea(XtDisplay(hw->html.view),
					XtWindow(hw->html.view),
					0, 0,
					dx, hw->html.view_height, False);
				ViewRedisplay(hw,
					0, 0,
					dx, hw->html.view_height);
			}
		}
	}

	/*
	 * Move the now hidden widgets
	 * Flush any Copyed or Cleared text first.
	XFlush(XtDisplay(hw));
	 */
	ScrollWidgets(hw);

	/*
	 * Remap the widgets to their new location
	MapWidgets(hw);
	 */
}


/*
 * Either the vertical or hortizontal scrollbar has been moved
 */
void
ScrollMove(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
#ifdef MOTIF
	XmScrollBarCallbackStruct *sc = (XmScrollBarCallbackStruct *)call_data;

	ScrollToPos(w, (HTMLWidget)client_data, sc->value);
#else
	float scrollDir = (int)call_data < 0 ? -0.3 : 0.3;
	HTMLWidget hw = (HTMLWidget)client_data;
	int value;
	Dimension totalLength, currentLength;

	if (w == hw->html.vbar)
	{
		totalLength = hw->html.doc_height;
		currentLength = hw->html.view_height;
		value = hw->html.scroll_y + scrollDir * currentLength;
	}
	else 
	{
		totalLength = hw->html.doc_width;
		currentLength = hw->html.view_width;
		value = hw->html.scroll_x + scrollDir * currentLength;
	}

	if (value > (int)totalLength) value = totalLength;
	if (value < 0) value = 0;
  
	setScrollBar(w, value, totalLength, currentLength);
	ScrollToPos(w, hw, value);
#endif
}


#ifndef MOTIF
void
JumpMove(w, client_data, call_data)
	Widget w;
	caddr_t client_data;
	caddr_t call_data;
{
	HTMLWidget hw = (HTMLWidget)client_data;
	int value = (int)(*(float *)call_data * 
			(w == hw->html.vbar ?  
			hw->html.doc_height : 
			hw->html.doc_width));
	ScrollToPos(w, hw, value);
}
#endif
  

/*
 * Create the horizontal and vertical scroll bars.
 * Size them later.
 */
static void
#ifdef _NO_PROTO
CreateScrollbars (hw)
            HTMLWidget hw ;
#else
CreateScrollbars(
            HTMLWidget hw)
#endif
{
	Arg arg[20];
	Cardinal argcnt;
	XtTranslations trans;

	/*
	 * If the user hasn't provided a viewing area Widget (which they
	 * should not most of the time) make a drawing are to use.
	 */
	if (hw->html.view == NULL)
	{
		argcnt = 0;
		XtSetArg(arg[argcnt], XxNwidth, 10); argcnt++;
		XtSetArg(arg[argcnt], XxNheight, 10); argcnt++;
		hw->html.view = XtCreateWidget("View",
#ifdef MOTIF		
			xmDrawingAreaWidgetClass,
#else
			drawingAreaWidgetClass, 
#endif
			(Widget)hw, arg, argcnt);
		XtManageChild(hw->html.view);
	}

	/*
	 * For the view widget catch all Expose and GraphicsExpose
	 * events.  Replace its translations with ours, and make
	 * sure all the actions are in order.
	 */
	XtAddEventHandler((Widget)hw->html.view, ExposureMask, True,
		(XtEventHandler)DrawExpose, (caddr_t)hw);
	/*
	 * As described previoisly, for some reason with Motif1.2/X11R5
	 * the list actionsList is corrupted when we get here,
	 * so we have to use the special copy SpareActionsList
	 */
	XtAppAddActions(XtWidgetToApplicationContext(hw->html.view),
		SpareActionsList, XtNumber(SpareActionsList));
	trans = XtParseTranslationTable(defaultTranslations);
	argcnt = 0;
	XtSetArg(arg[argcnt], XtNtranslations, trans); argcnt++;
	XtSetValues(hw->html.view, arg, argcnt);

	/*
	 * If the user hasn't provided a vertical scrollbar (which they
	 * should not most of the time) make one.
	 */
	if (hw->html.vbar == NULL)
	{
		argcnt = 0;
#ifdef MOTIF
		XtSetArg(arg[argcnt], XmNorientation, XmVERTICAL); argcnt++;
		hw->html.vbar = XtCreateWidget("Vbar", xmScrollBarWidgetClass,
			(Widget)hw, arg, argcnt);
#else
		XtSetArg(arg[argcnt],XtNorientation,XtorientVertical); argcnt++;
		hw->html.vbar = XtCreateWidget("Vbar", scrollbarWidgetClass,
			(Widget)hw, arg, argcnt);
#endif
		XtManageChild(hw->html.vbar);
	}

	/*
	 * Add callbacks to catch scrollbar changes
	 */
#ifdef MOTIF
	XtAddCallback(hw->html.vbar, XmNvalueChangedCallback,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
	XtAddCallback(hw->html.vbar, XmNdragCallback,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
#else
	XtAddCallback(hw->html.vbar, XtNjumpProc,
		(XtCallbackProc)JumpMove, (caddr_t)hw);
	XtAddCallback(hw->html.vbar, XtNscrollProc,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
#endif

	/*
	 * If the user hasn't provided a horizontal scrollbar (which they
	 * should not most of the time) make one.
	 */
	if (hw->html.hbar == NULL)
	{
		argcnt = 0;
#ifdef MOTIF
		XtSetArg(arg[argcnt], XmNorientation, XmHORIZONTAL); argcnt++;
		hw->html.hbar = XtCreateWidget("Hbar", xmScrollBarWidgetClass,
			(Widget)hw, arg, argcnt);
#else
		XtSetArg(arg[argcnt], XtNorientation, XtorientHorizontal);
		argcnt++;
		hw->html.hbar = XtCreateWidget("Hbar", scrollbarWidgetClass,
			(Widget)hw, arg, argcnt);
#endif
		XtManageChild(hw->html.hbar);
	}

	/*
	 * Add callbacks to catch scrollbar changes
	 */
#ifdef MOTIF
	XtAddCallback(hw->html.hbar, XmNvalueChangedCallback,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
	XtAddCallback(hw->html.hbar, XmNdragCallback,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
#else
	XtAddCallback(hw->html.hbar, XtNjumpProc,
		(XtCallbackProc)JumpMove, (caddr_t)hw);
	XtAddCallback(hw->html.hbar, XtNscrollProc,
		(XtCallbackProc)ScrollMove, (caddr_t)hw);
#endif
}


/*
 * Return the width of the vertical scrollbar
 */
static Dimension
#ifdef _NO_PROTO
VbarWidth (hw)
            HTMLWidget hw ;
#else
VbarWidth(
            HTMLWidget hw)
#endif
{
	Arg arg[4];
	Cardinal argcnt;
	Dimension width;

	width = 0;
	if (hw->html.vbar != NULL)
	{
		argcnt = 0;
		XtSetArg(arg[argcnt], XxNwidth, &width); argcnt++;
		XtGetValues(hw->html.vbar, arg, argcnt);
	}

	return(width);
}


/*
 * Return the height of the horizontal scrollbar
 */
static Dimension
#ifdef _NO_PROTO
HbarHeight (hw)
            HTMLWidget hw ;
#else
HbarHeight(
            HTMLWidget hw)
#endif
{
	Arg arg[4];
	Cardinal argcnt;
	Dimension height;

	height = 0;
	if (hw->html.hbar != NULL)
	{
		argcnt = 0;
		XtSetArg(arg[argcnt], XxNheight, &height); argcnt++;
		XtGetValues(hw->html.hbar, arg, argcnt);
	}

	return(height);
}


/*
 * Resize and set the min and max values of the scrollbars.  Position viewing
 * area based on scrollbar locations.
 */
static void
#ifdef _NO_PROTO
ConfigScrollBars (hw)
            HTMLWidget hw ;
#else
ConfigScrollBars(
            HTMLWidget hw)
#endif
{
#ifdef MOTIF
	Arg arg[20];
	Cardinal argcnt;
#endif
	int vx, vy;

	/*
	 * Move and size the viewing area
	 */
#ifdef MOTIF
	vx = hw->manager.shadow_thickness;
	vy = hw->manager.shadow_thickness;
#else
	vx = vy = 0;
#endif
	if ((hw->html.use_vbar == True)&&(hw->html.vbar_right == False))
	{
		vx += VbarWidth(hw);
	}
	if ((hw->html.use_hbar == True)&&(hw->html.hbar_top == True))
	{
		vy += HbarHeight(hw);
	}
	XtMoveWidget(hw->html.view, vx, vy);
	XtResizeWidget(hw->html.view, hw->html.view_width, hw->html.view_height,
		hw->html.view->core.border_width);

	/*
	 * Set up vertical scrollbar
	 */
	if (hw->html.use_vbar == True)
	{
		int maxv;
		int ss;

		/*
		 * Size the vertical scrollbar to the height of
		 * the viewing area
		 */
		XtResizeWidget(hw->html.vbar, hw->html.vbar->core.width,
		    hw->html.view_height + (2 * 
#ifdef MOTIF
					    hw->manager.shadow_thickness
#else
					    0
#endif
					    ),
		    hw->html.vbar->core.border_width);

		/*
		 * Set the slider size to be the percentage of the
		 * viewing area that the viewing area is of the
		 * document area.  Or set it to 1 if that isn't possible.
		 */
		if (hw->html.doc_height == 0)
		{
			ss = 1;
		}
		else
		{
#ifdef DEBUG
fprintf (stderr, "view_height %d, doc_height %d\n", 
hw->html.view_height, hw->html.doc_height);
#endif
#ifdef NOT_RIGHT
                        /* Eric -- your previous equation wasn't doing it.
                           This isn't either... */
			ss = 
                          (int)((float)hw->html.view_height *
                                ((float)hw->html.view_height / 
                                 (float)(hw->html.doc_height - (int)hw->html.view_height)));
			if (ss > hw->html.view_height)
			{
				ss = hw->html.view_height;
			}
#endif
                        /* Added by marca: this produces results *very* close (~1 pixel)
                           to the original scrolled window behavior. */
                        ss = hw->html.view_height;
		}
		if (ss < 1)
		{
			ss = 1;
		}
#ifdef DEBUG
fprintf (stderr, "computed ss to be %d\n", ss);
#endif

		/*
		 * If resizing of the document has made scroll_y
		 * greater than the max, we want to hold it at the max.
		 */
		maxv = hw->html.doc_height - (int)hw->html.view_height;
		if (maxv < 0)
		{
			maxv = 0;
		}
		if (hw->html.scroll_y > maxv)
		{
			hw->html.scroll_y = maxv;
		}

		/*
		 * Prevent the Motif max value and slider size
		 * from going to zero, which is illegal
		 */
		maxv = maxv + ss;
		if (maxv < 1)
		{
			maxv = 1;
		}

		/*
		 * Motif will not allow the actual value to be equal to
		 * its max value.  Adjust accordingly.
		 * Since we might decrease scroll_y, cap it at zero.
		 */
		if (hw->html.scroll_y >= maxv)
		{
			hw->html.scroll_y = maxv - 1;
		}
		if (hw->html.scroll_y < 0)
		{
			hw->html.scroll_y = 0;
		}

#ifdef MOTIF
		argcnt = 0;
		XtSetArg(arg[argcnt], XmNminimum, 0); argcnt++;
		XtSetArg(arg[argcnt], XmNmaximum, maxv); argcnt++;
		XtSetArg(arg[argcnt], XmNvalue, hw->html.scroll_y); argcnt++;
		XtSetArg(arg[argcnt], XmNsliderSize, ss); argcnt++;
                XtSetArg(arg[argcnt], XmNincrement, DEFAULT_INCREMENT); argcnt++;
                XtSetArg(arg[argcnt], XmNpageIncrement, 
                         hw->html.view_height > DEFAULT_INCREMENT ? 
                           hw->html.view_height - DEFAULT_INCREMENT : 1); argcnt++;
		XtSetValues(hw->html.vbar, arg, argcnt);
#else
		setScrollBar(hw->html.vbar, 
			     hw->html.scroll_y,
			     hw->html.doc_height, 
			     hw->html.view_height);
#endif /* MOTIF */

#ifdef DEBUG
XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL);
fprintf (stderr, "real slider size %d\n", ss);
#endif
	}

	/*
	 * Set up horizontal scrollbar
	 */
	if (hw->html.use_hbar == True)
	{
		int maxv;
		int ss;

		/*
		 * Size the horizontal scrollbar to the width of
		 * the viewing area
		 */
		XtResizeWidget(hw->html.hbar,
		    hw->html.view_width + (2 *
#ifdef MOTIF
					   hw->manager.shadow_thickness
#else
					   0
#endif /* MOTIF */
					   ),
		    hw->html.hbar->core.height,
		    hw->html.hbar->core.border_width);

		/*
		 * Set the slider size to be the percentage of the
		 * viewing area that the viewing area is of the
		 * document area.  Or set it to 1 if that isn't possible.
		 */
		if (hw->html.doc_width == 0)
		{
			ss = 1;
		}
		else
		{
#ifdef NOT_RIGHT
			ss = hw->html.view_width *
				hw->html.view_width / hw->html.doc_width;
			if (ss > hw->html.view_width)
			{
				ss = hw->html.view_width;
			}
#endif
                        /* Added by marca: this produces results *very* close (~1 pixel)
                           to the original scrolled window behavior. */
                        ss = hw->html.view_width;
		}
		if (ss < 1)
		{
			ss = 1;
		}

		/*
		 * If resizing of the document has made scroll_x
		 * greater than the max, we want to hold it at the max.
		 */
		maxv = hw->html.doc_width - (int)hw->html.view_width;
		if (maxv < 0)
		{
			maxv = 0;
		}
		if (hw->html.scroll_x > maxv)
		{
			hw->html.scroll_x = maxv;
		}

		/*
		 * Prevent the Motif max value and slider size
		 * from going to zero, which is illegal
		 */
		maxv = maxv + ss;
		if (maxv < 1)
		{
			maxv = 1;
		}

		/*
		 * Motif will not allow the actual value to be equal to
		 * its max value.  Adjust accordingly.
		 * Since we might decrease scroll_x, cap it at zero.
		 */
		if (hw->html.scroll_x >= maxv)
		{
			hw->html.scroll_x = maxv - 1;
		}
		if (hw->html.scroll_x < 0)
		{
			hw->html.scroll_x = 0;
		}

#ifdef MOTIF
		argcnt = 0;
		XtSetArg(arg[argcnt], XmNminimum, 0); argcnt++;
		XtSetArg(arg[argcnt], XmNmaximum, maxv); argcnt++;
		XtSetArg(arg[argcnt], XmNvalue, hw->html.scroll_x); argcnt++;
		XtSetArg(arg[argcnt], XmNsliderSize, ss); argcnt++;
                XtSetArg(arg[argcnt], XmNincrement, DEFAULT_INCREMENT); argcnt++;
                XtSetArg(arg[argcnt], XmNpageIncrement, 
                         hw->html.view_width > DEFAULT_INCREMENT ? 
                         hw->html.view_width - DEFAULT_INCREMENT : 1); argcnt++;
		XtSetValues(hw->html.hbar, arg, argcnt);
#else
		setScrollBar(hw->html.hbar, 	
			     hw->html.scroll_x,
			     hw->html.doc_width, 
			     hw->html.view_width);
#endif /* MOTIF */
	}

#ifdef DEBUG
        {
          int ss;
XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL);
fprintf (stderr, "real slider size %d\n", ss);
        }
#endif
}


/*
 * Reformat the window and scrollbars.
 * May be called because of a changed document, or because of a changed
 * window size.
 */
static void
#ifdef _NO_PROTO
ReformatWindow (hw)
            HTMLWidget hw ;
#else
ReformatWindow(
            HTMLWidget hw)
#endif
{
	int temp;
	int new_width;
	Dimension swidth, sheight;
	Dimension st;

	/*
	 * Find the current scrollbar sizes, and shadow thickness and format
	 * the document to the current window width
	 * (assume a vertical scrollbar)
	 */
	swidth = VbarWidth(hw);
	sheight = HbarHeight(hw);
#ifdef MOTIF
	st = hw->manager.shadow_thickness;
#else
	st = 0;
#endif /* MOTIF */
	if (hw->core.width <= swidth)
	{
		hw->core.width = swidth + 10;
	} 
	new_width = hw->core.width - swidth - (2 * st);
	temp = FormatAll(hw, &new_width);

	/*
	 * If we need the vertical scrollbar, place and manage it,
	 * and store the current viewing area width.
	 */
	if (temp > hw->core.height - sheight)
	{
		hw->html.use_vbar = True;
		if (hw->html.vbar_right == True)
		{
			XtMoveWidget(hw->html.vbar,
				(hw->core.width - swidth), 0);
		}
		else
		{
			XtMoveWidget(hw->html.vbar, 0, 0);
		}
		XtManageChild(hw->html.vbar);
		hw->html.view_width = hw->core.width - swidth - (2 * st);
	}
	/*
	 * Else we were wrong to assume a vertical scrollbar.
	 * Remove it, and reformat the document to the wider width.
	 * Save the as the current viewing are width.
	 */
	else
	{
		hw->html.use_vbar = False;
		XtUnmanageChild(hw->html.vbar);
		hw->html.scroll_y = 0;
		new_width = hw->core.width - (2 * st);
		temp = FormatAll(hw, &new_width);
		hw->html.view_width = hw->core.width - (2 * st);
		/* fake out later horizontal scrollbars */
		swidth = 0;
	}

	/*
	 * Calculate the actual max width and height of the complete
	 * formatted document.
	 * The max width may exceed the preformatted width due to special
	 * factors in the formatting of the widget.
	 * Use the max of the 2 here, but leave max_pre_width unchanged
	 * for future formatting calls.
	 */
	/*
	 * new_width includes the margins, and hw->html.max_pre_width
	 * does not, fix that here.
	 */
	new_width = new_width - (2 * hw->html.margin_width);
	if (hw->html.max_pre_width > new_width)
	{
		new_width = hw->html.max_pre_width;
	}
	/*
	 * If the maximum width derives from a formatted, as opposed to
	 * unformatted piece of text, allow a 20% of margin width slop
	 * over into the margin to cover up a minor glick with terminaing
	 * punctuation after anchors at the end of the line.
	 */
	else
	{
		new_width = new_width - (20 * hw->html.margin_width / 100);
	}

	hw->html.doc_height = temp;
	hw->html.doc_width = new_width + (2 * hw->html.margin_width);
	if (hw->html.view_width > hw->html.doc_width)
	{
		hw->html.doc_width = hw->html.view_width;
	}

	/*
	 * If we need a horizontal scrollbar
	 * Place it and manage it.  Save the height of the current
	 * viewing area.
	 */
	if (hw->html.doc_width > hw->html.view_width)
	{
		hw->html.use_hbar = True;
		if (hw->html.hbar_top == True)
		{
			if (hw->html.use_vbar == True)
			{
				XtMoveWidget(hw->html.vbar,
					hw->html.vbar->core.x, sheight);
			}

			if (hw->html.vbar_right == True)
			{
				XtMoveWidget(hw->html.hbar, 0, 0);
			}
			else
			{
				XtMoveWidget(hw->html.hbar, swidth, 0);
			}
		}
		else
		{
			if (hw->html.vbar_right == True)
			{
				XtMoveWidget(hw->html.hbar, 0,
					(hw->core.height - sheight));
			}
			else
			{
				XtMoveWidget(hw->html.hbar, swidth,
					(hw->core.height - sheight));
			}
		}
		XtManageChild(hw->html.hbar);
		hw->html.view_height = hw->core.height - sheight - (2 * st);
	}
	/*
	 * Else we don't need a horizontal scrollbar.
	 * Remove it and save the current viewing area height.
	 */
	else
	{
		hw->html.use_hbar = False;
		XtUnmanageChild(hw->html.hbar);
		hw->html.scroll_x = 0;
		hw->html.view_height = hw->core.height - (2 * st);
	}

	/*
	 * Configure the scrollbar min, max, and slider sizes
	 */
#ifdef DEBUG
fprintf (stderr, "calling in ReformatWindow\n");
#endif
	ConfigScrollBars(hw);
}


/*
 * We're a happy widget.  We let any child move or resize themselves
 * however they want, we don't care.
 */
static XtGeometryResult
#ifdef _NO_PROTO
GeometryManager (w, request, reply)
	Widget w;
	XtWidgetGeometry * request;
	XtWidgetGeometry * reply;
#else
GeometryManager (
	Widget w,
	XtWidgetGeometry * request,
	XtWidgetGeometry * reply)
#endif
{
	reply->x = request->x;
	reply->y = request->y;
	reply->width = request->width;
	reply->height = request->height;
	reply->border_width = request->border_width;
	reply->request_mode = request->request_mode;
	return (XtGeometryYes);
}


/*
 * Initialize is called when the widget is first initialized.
 * Check to see that all the starting resources are valid.
 */
static void
#ifdef _NO_PROTO
Initialize (request, new)
            HTMLWidget request ;
            HTMLWidget new ;
#else
Initialize(
            HTMLWidget request,
            HTMLWidget new)
#endif
{
	/*
	 *	Make sure height and width are not zero.
	 */
	if (new->core.width == 0)
	{
		new->core.width = new->html.margin_width << 1 ;
	} 
	if (new->core.width == 0)
	{
		new->core.width = 10 ;
	} 
	if (new->core.height == 0)
	{
		new->core.height = new->html.margin_height << 1 ;
	} 
	if (new->core.height == 0)
	{
		new->core.height = 10 ;
	} 

	/*
	 *	Make sure the underline numbers are within bounds.
	 */
	if (new->html.num_anchor_underlines < 0)
	{
		new->html.num_anchor_underlines = 0;
	} 
	if (new->html.num_anchor_underlines > MAX_UNDERLINES)
	{
		new->html.num_anchor_underlines = MAX_UNDERLINES;
	} 
	if (new->html.num_visitedAnchor_underlines < 0)
	{
		new->html.num_visitedAnchor_underlines = 0;
	} 
	if (new->html.num_visitedAnchor_underlines > MAX_UNDERLINES)
	{
		new->html.num_visitedAnchor_underlines = MAX_UNDERLINES;
	} 

	/*
	 * Parse the raw text with the HTML parser.  And set the formatted 
	 * element list to NULL.
	 */
	new->html.html_objects = HTMLParse(NULL, request->html.raw_text);
	CallLinkCallbacks(new);
	new->html.html_header_objects =
		HTMLParse(NULL, request->html.header_text);
	new->html.html_footer_objects =
		HTMLParse(NULL, request->html.footer_text);
	new->html.formatted_elements = NULL;
	new->html.my_visited_hrefs = NULL;
	new->html.my_delayed_images = NULL;
	new->html.widget_list = NULL;
	new->html.form_list = NULL;

	/*
	 * Blank document
	 */
	new->html.line_array = NULL;
	new->html.line_count = 0;

	/*
	 * Find the max width of a preformatted
	 * line in this document.
	 */
	new->html.max_pre_width = DocumentWidth(new, new->html.html_objects);

	/*
	 * Create the scrollbars.
	 * Find their dimensions and then decide which scrollbars you
	 * will need, and what the dimensions of the viewing area are.
	 * Start assuming a vertical scrollbar and a horizontal one.
	 * The remove vertical if short enough, and remove horizontal
	 * if narrow enough.
	 */
	CreateScrollbars(new);
	new->html.scroll_x = 0;
	new->html.scroll_y = 0;
	ReformatWindow(new);

	/*
	 * Initialize private widget resources
	 */
	new->html.drawGC = NULL;
	new->html.select_start = NULL;
	new->html.select_end = NULL;
	new->html.sel_start_pos = 0;
	new->html.sel_end_pos = 0;
	new->html.new_start = NULL;
	new->html.new_end = NULL;
	new->html.new_start_pos = 0;
	new->html.new_end_pos = 0;
	new->html.active_anchor = NULL;
	new->html.press_x = 0;
	new->html.press_y = 0;

        new->html.cached_tracked_ele = NULL;

        /* Initialize cursor used when pointer is inside anchor. */
        if (in_anchor_cursor == (Cursor)NULL)
          in_anchor_cursor = XCreateFontCursor (XtDisplay (new), XC_hand2);

        return;
}


#ifdef DEBUG
void
DebugHook(x, y, width, height)
	int x, y, width, height;
{
fprintf(stderr, "Redrawing (%d,%d) %dx%d\n", x, y, width, height);
}
#endif


/*
 * This is called by redisplay.  It is passed a rectangle
 * in the viewing area, and it redisplays that portion of the
 * underlying document area.
 */
static void
#ifdef _NO_PROTO
ViewRedisplay (hw, x, y, width, height)
            HTMLWidget hw;
	    int x, y;
	    int width, height;
#else
ViewRedisplay(
            HTMLWidget hw,
            int x,
            int y,
            int width,
            int height)
#endif
{
	int sx, sy;
	int doc_x, doc_y;
	int i, start, end, guess;

	/*
	 * Use scrollbar values to map from view space to document space.
	 */
	sx = sy = 0;
	if (hw->html.use_vbar == True)
	{
		sy += hw->html.scroll_y;
	}
	if (hw->html.use_hbar == True)
	{
		sx += hw->html.scroll_x;
	}

	doc_x = x + sx;
	doc_y = y + sy;

	/*
	 * Find the lines that overlap the exposed area.
	 */
	start = 0;
	end = hw->html.line_count - 1;

	/*
	 * Heuristic to speed up redraws by guessing at the starting line.
	 */
	guess = doc_y / (hw->html.font->max_bounds.ascent +
		hw->html.font->max_bounds.descent);
	if (guess > end)
	{
		guess = end;
	}
	while (guess > 0)
	{
		if ((hw->html.line_array[guess] != NULL)&&
			(hw->html.line_array[guess]->y < doc_y))
		{
			break;
		}
		guess--;
	}
	if (guess < start)
	{
		guess = start;
	}

	for (i=guess; i<hw->html.line_count; i++)
	{
		if (hw->html.line_array[i] == NULL)
		{
			continue;
		}

		if (hw->html.line_array[i]->y < doc_y)
		{
			start = i;
		}
		if (hw->html.line_array[i]->y > (doc_y + height))
		{
			end = i;
			break;
		}
	}

	/*
	 * If we have a GC draw the lines that overlap the exposed area.
	 */
	if (hw->html.drawGC != NULL)
	{
		for (i=start; i<=end; i++)
		{
			PlaceLine(hw, i);
		}
#ifdef EXTRA_FLUSH
		XFlush(XtDisplay(hw));
#endif
	}
}


static void
#ifdef _NO_PROTO
ViewClearAndRefresh (hw)
            HTMLWidget hw;
#else
ViewClearAndRefresh(
            HTMLWidget hw)
#endif
{
	/*
	 * Only refresh if we have a window already.
	 * (if we have a GC we have a window)
	 */
	if (hw->html.drawGC != NULL)
	{
		XClearArea(XtDisplay(hw->html.view), XtWindow(hw->html.view),
			0, 0, 0, 0, False);
		ViewRedisplay(hw, 0, 0,
			hw->html.view_width, hw->html.view_height);
		/*
		 * This is a fake deal to make an Expose event tocall Redisplay
		 * to redraw the shadow around the view area
		 */
		XClearArea(XtDisplay(hw), XtWindow(hw),
			0, 0, 1, 1, True);
	}
}


/*
 * The Redisplay function is what you do with an expose event.
 * Right now we call user callbacks, and then call the CompositeWidget's
 * Redisplay routine.
 */
static void
#ifdef _NO_PROTO
Redisplay (hw, event, region)
            HTMLWidget hw;
            XEvent * event;
            Region region;
#else
Redisplay(
            HTMLWidget hw,
            XEvent * event,
            Region region)
#endif
{
	XExposeEvent *ExEvent = (XExposeEvent *)event;
	int dx, dy;

#ifdef MOTIF
	/*
	 * find out where the shadow is based on scrollbars
	 */

	Dimension st = hw->manager.shadow_thickness;
#endif /* MOTIF */

	dx = dy = 0;
	if ((hw->html.use_vbar == True)&&(hw->html.vbar_right == False))
	{
		dx += VbarWidth(hw);
	}
	if ((hw->html.use_hbar == True)&&(hw->html.hbar_top == True))
	{
		dy += HbarHeight(hw);
	}

#ifdef MOTIF
	/*
	 * Redraw the shadow around the scrolling area which may have been
	 * messed up.
	 */
	_XmDrawShadow(XtDisplay(hw), XtWindow(hw),
		hw->manager.bottom_shadow_GC, hw->manager.top_shadow_GC,
		hw->manager.shadow_thickness, dx, dy,
		hw->html.view_width + (2 * st),hw->html.view_height + (2 * st));
#endif /* MOTIF */

#ifdef MOTIF
#ifdef MOTIF1_2
	_XmRedisplayGadgets ((Widget)hw, (XEvent*)event, region);
#else
	_XmRedisplayGadgets ((CompositeWidget)hw, (XExposeEvent*)event, region);
#endif /* MOTIF1_2 */
#endif /* MOTIF */

	return;
}


/*
 * Resize is called when the widget changes size.
 * Mostly any resize causes a reformat, except for the special case
 * where the width doesn't change, and the height doesn't change
 * enought to affect the vertical scrollbar.
 * It is too complex to guess exactly what needs to be redrawn, so refresh the
 * whole window on any resize.
 */
static void
#ifdef _NO_PROTO
Resize (hw)
            HTMLWidget hw;
#else
Resize(
            HTMLWidget hw)
#endif
{
	int tempw;
	Dimension swidth, sheight;
	Dimension st;

	/*
	 * Find the new widht of the viewing area.
	 */
	swidth = VbarWidth(hw);
	sheight = HbarHeight(hw);
#ifdef MOTIF
	st = hw->manager.shadow_thickness;
#else
	st = 0;
#endif /* MOTIF */
	if (hw->core.width <= swidth)
	{
		hw->core.width = swidth + 10 ;
	} 

	if (hw->html.use_vbar == True)
	{
		tempw = hw->core.width - swidth - (2 * st);
	}
	else
	{
		tempw = hw->core.width - (2 * st);
		/* fool positioning of horz scrollbar later */
		swidth = 0;
	}

	/*
	 * Special case where we don't have to reformat to a new width.
	 * The width has not changed, and the height has not changed
	 * significantly to change the state of the vertical scrollbar.
	 */
	if ((tempw == hw->html.view_width)&&
	    (((hw->html.use_vbar == True)&&
	      ((hw->core.height - sheight - (2 * st)) < hw->html.doc_height))||
	     ((hw->html.use_vbar == False)&&
	      ((hw->core.height - sheight - (2 * st)) >= hw->html.doc_height))))
	{
		/*
		 * Super special case where the size of the window hasn't
		 * changed at ALL!
		 */
		if (((hw->html.use_hbar == True)&&(hw->html.view_height ==
			(hw->core.height - sheight - (2 * st))))||
			((hw->html.use_hbar == False)&&(hw->html.view_height ==
			(hw->core.height - (2 * st)))))
		{
			return;
		}

		if (hw->html.use_hbar == True)
		{
			if (hw->html.hbar_top == True)
			{
				if (hw->html.vbar_right == True)
				{
					XtMoveWidget(hw->html.hbar, 0, 0);
				}
				else
				{
					XtMoveWidget(hw->html.hbar, swidth, 0);
				}
			}
			else
			{
				if (hw->html.vbar_right == True)
				{
					XtMoveWidget(hw->html.hbar, 0,
						(hw->core.height - sheight));
				}
				else
				{
					XtMoveWidget(hw->html.hbar, swidth,
						(hw->core.height - sheight));
				}
			}
			hw->html.view_height = hw->core.height - sheight -
				(2 * st);
		}
		else
		{
			hw->html.view_height = hw->core.height - (2 * st);
		}
#ifdef DEBUG
fprintf (stderr, "calling in Resize\n");
#endif
		ConfigScrollBars(hw);
		ScrollWidgets(hw);
		ViewClearAndRefresh(hw);
	}
	/*
	 * Otherwise we have to do a full reformat on every resize.
	 */
	else
	{
		ReformatWindow(hw);
		ScrollWidgets(hw);
		ViewClearAndRefresh(hw);
	}

#ifdef DEBUG
        {
          int ss;
XtVaGetValues(hw->html.vbar, XmNsliderSize, &ss, NULL);
fprintf (stderr, "leaving; slider size %d\n", ss);
        }
#endif

	return;
}


/*
 * Find the complete text for this the anchor that aptr is a part of
 * and set it into the selection.
 */
static void
FindSelectAnchor(hw, aptr)
	HTMLWidget hw;
	struct ele_rec *aptr;
{
	struct ele_rec *eptr;

	eptr = aptr;
	while ((eptr->prev != NULL)&&
		(eptr->prev->anchorHRef != NULL)&&
		(strcmp(eptr->prev->anchorHRef, eptr->anchorHRef) == 0))
	{
		eptr = eptr->prev;
	}
	hw->html.select_start = eptr;
	hw->html.sel_start_pos = 0;

	eptr = aptr;
	while ((eptr->next != NULL)&&
		(eptr->next->anchorHRef != NULL)&&
		(strcmp(eptr->next->anchorHRef, eptr->anchorHRef) == 0))
	{
		eptr = eptr->next;
	}
	hw->html.select_end = eptr;
	hw->html.sel_end_pos = eptr->edata_len - 2;
}


/*
 * Set as active all elements in the widget that are part of the anchor
 * in the widget's start ptr.
 */
static void
SetAnchor(hw)
	HTMLWidget hw;
{
	struct ele_rec *eptr;
	struct ele_rec *start;
	struct ele_rec *end;
	unsigned long fg, bg;
	unsigned long old_fg, old_bg;

	eptr = hw->html.active_anchor;
	if ((eptr == NULL)||(eptr->anchorHRef == NULL))
	{
		return;
	}
	fg = hw->html.activeAnchor_fg;
	bg = hw->html.activeAnchor_bg;

	FindSelectAnchor(hw, eptr);

	start = hw->html.select_start;
	end = hw->html.select_end;

	eptr = start;
	while ((eptr != NULL)&&(eptr != end))
	{
		if (eptr->type == E_TEXT)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			TextRefresh(hw, eptr,
				0, (eptr->edata_len - 2));
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
		else if (eptr->type == E_IMAGE)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			ImageRefresh(hw, eptr);
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
	/*
	 * Linefeeds in anchor spanning multiple lines should NOT
	 * be highlighted!
		else if (eptr->type == E_LINEFEED)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			LinefeedRefresh(hw, eptr);
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
	*/
		eptr = eptr->next;
	}
	if (eptr != NULL)
	{
		if (eptr->type == E_TEXT)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			TextRefresh(hw, eptr,
				0, (eptr->edata_len - 2));
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
		else if (eptr->type == E_IMAGE)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			ImageRefresh(hw, eptr);
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
	/*
	 * Linefeeds in anchor spanning multiple lines should NOT
	 * be highlighted!
		else if (eptr->type == E_LINEFEED)
		{
			old_fg = eptr->fg;
			old_bg = eptr->bg;
			eptr->fg = fg;
			eptr->bg = bg;
			LinefeedRefresh(hw, eptr);
			eptr->fg = old_fg;
			eptr->bg = old_bg;
		}
	*/
	}
}


/*
 * Draw selection for all elements in the widget
 * from start to end.
 */
static void
DrawSelection(hw, start, end, start_pos, end_pos)
	HTMLWidget hw;
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;
{
	struct ele_rec *eptr;
	int epos;

	if ((start == NULL)||(end == NULL))
	{
		return;
	}

	/*
	 * Keep positions within bounds (allows us to be sloppy elsewhere)
	 */
	if (start_pos < 0)
	{
		start_pos = 0;
	}
	if (start_pos >= start->edata_len - 1)
	{
		start_pos = start->edata_len - 2;
	}
	if (end_pos < 0)
	{
		end_pos = 0;
	}
	if (end_pos >= end->edata_len - 1)
	{
		end_pos = end->edata_len - 2;
	}

	if (SwapElements(start, end, start_pos, end_pos))
	{
		eptr = start;
		start = end;
		end = eptr;
		epos = start_pos;
		start_pos = end_pos;
		end_pos = epos;
	}

	eptr = start;
	while ((eptr != NULL)&&(eptr != end))
	{
		int p1, p2;

		if (eptr == start)
		{
			p1 = start_pos;
		}
		else
		{
			p1 = 0;
		}
		p2 = eptr->edata_len - 2;

		if (eptr->type == E_TEXT)
		{
			eptr->selected = True;
			eptr->start_pos = p1;
			eptr->end_pos = p2;
			TextRefresh(hw, eptr, p1, p2);
		}
		else if (eptr->type == E_LINEFEED)
		{
			eptr->selected = True;
			LinefeedRefresh(hw, eptr);
		}
		eptr = eptr->next;
	}
	if (eptr != NULL)
	{
		int p1, p2;

		if (eptr == start)
		{
			p1 = start_pos;
		}
		else
		{
			p1 = 0;
		}

		if (eptr == end)
		{
			p2 = end_pos;
		}
		else
		{
			p2 = eptr->edata_len - 2;
		}

		if (eptr->type == E_TEXT)
		{
			eptr->selected = True;
			eptr->start_pos = p1;
			eptr->end_pos = p2;
			TextRefresh(hw, eptr, p1, p2);
		}
		else if (eptr->type == E_LINEFEED)
		{
			eptr->selected = True;
			LinefeedRefresh(hw, eptr);
		}
	}
}


/*
 * Set selection for all elements in the widget's
 * start to end list.
 */
static void
SetSelection(hw)
	HTMLWidget hw;
{
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;

	start = hw->html.select_start;
	end = hw->html.select_end;
	start_pos = hw->html.sel_start_pos;
	end_pos = hw->html.sel_end_pos;
	DrawSelection(hw, start, end, start_pos, end_pos);
}


/*
 * Erase the selection from start to end
 */
static void
EraseSelection(hw, start, end, start_pos, end_pos)
	HTMLWidget hw;
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;
{
	struct ele_rec *eptr;
	int epos;

	if ((start == NULL)||(end == NULL))
	{
		return;
	}

	/*
	 * Keep positoins within bounds (allows us to be sloppy elsewhere)
	 */
	if (start_pos < 0)
	{
		start_pos = 0;
	}
	if (start_pos >= start->edata_len - 1)
	{
		start_pos = start->edata_len - 2;
	}
	if (end_pos < 0)
	{
		end_pos = 0;
	}
	if (end_pos >= end->edata_len - 1)
	{
		end_pos = end->edata_len - 2;
	}

	if (SwapElements(start, end, start_pos, end_pos))
	{
		eptr = start;
		start = end;
		end = eptr;
		epos = start_pos;
		start_pos = end_pos;
		end_pos = epos;
	}

	eptr = start;
	while ((eptr != NULL)&&(eptr != end))
	{
		int p1, p2;

		if (eptr == start)
		{
			p1 = start_pos;
		}
		else
		{
			p1 = 0;
		}
		p2 = eptr->edata_len - 2;

		if (eptr->type == E_TEXT)
		{
			eptr->selected = False;
			TextRefresh(hw, eptr, p1, p2);
		}
		else if (eptr->type == E_LINEFEED)
		{
			eptr->selected = False;
			LinefeedRefresh(hw, eptr);
		}
		eptr = eptr->next;
	}
	if (eptr != NULL)
	{
		int p1, p2;

		if (eptr == start)
		{
			p1 = start_pos;
		}
		else
		{
			p1 = 0;
		}

		if (eptr == end)
		{
			p2 = end_pos;
		}
		else
		{
			p2 = eptr->edata_len - 2;
		}

		if (eptr->type == E_TEXT)
		{
			eptr->selected = False;
			TextRefresh(hw, eptr, p1, p2);
		}
		else if (eptr->type == E_LINEFEED)
		{
			eptr->selected = False;
			LinefeedRefresh(hw, eptr);
		}
	}
}


/*
 * Clear the current selection (if there is one)
 */
static void
ClearSelection(hw)
	HTMLWidget hw;
{
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;

	start = hw->html.select_start;
	end = hw->html.select_end;
	start_pos = hw->html.sel_start_pos;
	end_pos = hw->html.sel_end_pos;
	EraseSelection(hw, start, end, start_pos, end_pos);

	if ((start == NULL)||(end == NULL))
	{
		hw->html.select_start = NULL;
		hw->html.select_end = NULL;
		hw->html.sel_start_pos = 0;
		hw->html.sel_end_pos = 0;
		hw->html.active_anchor = NULL;
		return;
	}

	hw->html.select_start = NULL;
	hw->html.select_end = NULL;
	hw->html.sel_start_pos = 0;
	hw->html.sel_end_pos = 0;
	hw->html.active_anchor = NULL;
}


/*
 * clear from active all elements in the widget that are part of the anchor.
 * (These have already been previously set into the start and end of the
 * selection.
 */
static void
UnsetAnchor(hw)
	HTMLWidget hw;
{
	struct ele_rec *eptr;

	/*
	 * Clear any activated images
	 */
	eptr = hw->html.select_start;
	while ((eptr != NULL)&&(eptr != hw->html.select_end))
	{
		if (eptr->type == E_IMAGE)
		{
			ImageRefresh(hw, eptr);
		}
		eptr = eptr->next;
	}
	if ((eptr != NULL)&&(eptr->type == E_IMAGE))
	{
		ImageRefresh(hw, eptr);
	}

	/*
	 * Clear the activated anchor
	 */
	ClearSelection(hw);
}


/*
 * Erase the old selection, and draw the new one in such a way
 * that advantage is taken of overlap, and there is no obnoxious
 * flashing.
 */
static void
ChangeSelection(hw, start, end, start_pos, end_pos)
	HTMLWidget hw;
	struct ele_rec *start;
	struct ele_rec *end;
	int start_pos, end_pos;
{
	struct ele_rec *old_start;
	struct ele_rec *old_end;
	struct ele_rec *new_start;
	struct ele_rec *new_end;
	struct ele_rec *eptr;
	int epos;
	int new_start_pos, new_end_pos;
	int old_start_pos, old_end_pos;

	old_start = hw->html.new_start;
	old_end = hw->html.new_end;
	old_start_pos = hw->html.new_start_pos;
	old_end_pos = hw->html.new_end_pos;
	new_start = start;
	new_end = end;
	new_start_pos = start_pos;
	new_end_pos = end_pos;

	if ((new_start == NULL)||(new_end == NULL))
	{
		return;
	}

	if ((old_start == NULL)||(old_end == NULL))
	{
		DrawSelection(hw, new_start, new_end,
			new_start_pos, new_end_pos);
		return;
	}

	if (SwapElements(old_start, old_end, old_start_pos, old_end_pos))
	{
		eptr = old_start;
		old_start = old_end;
		old_end = eptr;
		epos = old_start_pos;
		old_start_pos = old_end_pos;
		old_end_pos = epos;
	}

	if (SwapElements(new_start, new_end, new_start_pos, new_end_pos))
	{
		eptr = new_start;
		new_start = new_end;
		new_end = eptr;
		epos = new_start_pos;
		new_start_pos = new_end_pos;
		new_end_pos = epos;
	}

	/*
	 * Deal with all possible intersections of the 2 selection sets.
	 *
	 ********************************************************
	 *			*				*
	 *      |--		*	     |--		*
	 * old--|		*	new--|			*
	 *      |--		*	     |--		*
	 *			*				*
	 *      |--		*	     |--		*
	 * new--|		*	old--|			*
	 *      |--		*	     |--		*
	 *			*				*
	 ********************************************************
	 *			*				*
	 *      |----		*	       |--		*
	 * old--|		*	  new--|		*
	 *      | |--		*	       |		*
	 *      |-+--		*	     |-+--		*
	 *        |		*	     | |--		*
	 *   new--|		*	old--|			*
	 *        |--		*	     |----		*
	 *			*				*
	 ********************************************************
	 *			*				*
	 *      |---------	*	     |---------		*
	 *      |		*	     |			*
	 *      |      |--	*	     |      |--		*
	 * new--| old--|	*	old--| new--|		*
	 *      |      |--	*	     |      |--		*
	 *      |		*	     |			*
	 *      |---------	*	     |---------		*
	 *			*				*
	 ********************************************************
	 *
	 */
	if ((ElementLessThan(old_end, new_start, old_end_pos, new_start_pos))||
	    (ElementLessThan(new_end, old_start, new_end_pos, old_start_pos)))
	{
		EraseSelection(hw, old_start, old_end,
			old_start_pos, old_end_pos);
		DrawSelection(hw, new_start, new_end,
			new_start_pos, new_end_pos);
	}
	else if ((ElementLessThan(old_start, new_start,
			old_start_pos, new_start_pos))&&
		 (ElementLessThan(old_end, new_end, old_end_pos, new_end_pos)))
	{
		if (new_start_pos != 0)
		{
			EraseSelection(hw, old_start, new_start,
				old_start_pos, new_start_pos - 1);
		}
		else
		{
			EraseSelection(hw, old_start, new_start->prev,
				old_start_pos, new_start->prev->edata_len - 2);
		}
		if (old_end_pos < (old_end->edata_len - 2))
		{
			DrawSelection(hw, old_end, new_end,
				old_end_pos + 1, new_end_pos);
		}
		else
		{
			DrawSelection(hw, old_end->next, new_end,
				0, new_end_pos);
		}
	}
	else if ((ElementLessThan(new_start, old_start,
			new_start_pos, old_start_pos))&&
		 (ElementLessThan(new_end, old_end, new_end_pos, old_end_pos)))
	{
		if (old_start_pos != 0)
		{
			DrawSelection(hw, new_start, old_start,
				new_start_pos, old_start_pos - 1);
		}
		else
		{
			DrawSelection(hw, new_start, old_start->prev,
				new_start_pos, old_start->prev->edata_len - 2);
		}
		if (new_end_pos < (new_end->edata_len - 2))
		{
			EraseSelection(hw, new_end, old_end,
				new_end_pos + 1, old_end_pos);
		}
		else
		{
			EraseSelection(hw, new_end->next, old_end,
				0, old_end_pos);
		}
	}
	else if ((ElementLessThan(new_start, old_start,
			new_start_pos, old_start_pos))||
		 (ElementLessThan(old_end, new_end, old_end_pos, new_end_pos)))
	{
		if ((new_start != old_start)||(new_start_pos != old_start_pos))
		{
			if (old_start_pos != 0)
			{
				DrawSelection(hw, new_start, old_start,
					new_start_pos, old_start_pos - 1);
			}
			else
			{
				DrawSelection(hw, new_start, old_start->prev,
					new_start_pos,
					old_start->prev->edata_len - 2);
			}
		}
		if ((old_end != new_end)||(old_end_pos != new_end_pos))
		{
			if (old_end_pos < (old_end->edata_len - 2))
			{
				DrawSelection(hw, old_end, new_end,
					old_end_pos + 1, new_end_pos);
			}
			else
			{
				DrawSelection(hw, old_end->next, new_end,
					0, new_end_pos);
			}
		}
	}
	else
	{
		if ((old_start != new_start)||(old_start_pos != new_start_pos))
		{
			if (new_start_pos != 0)
			{
				EraseSelection(hw, old_start, new_start,
					old_start_pos, new_start_pos - 1);
			}
			else
			{
				EraseSelection(hw, old_start, new_start->prev,
					old_start_pos,
					new_start->prev->edata_len - 2);
			}
		}
		if ((new_end != old_end)||(new_end_pos != old_end_pos))
		{
			if (new_end_pos < (new_end->edata_len - 2))
			{
				EraseSelection(hw, new_end, old_end,
					new_end_pos + 1, old_end_pos);
			}
			else
			{
				EraseSelection(hw, new_end->next, old_end,
					0, old_end_pos);
			}
		}
	}
}


static void
SelectStart(w, event, params, num_params)
	Widget w;
	XEvent *event;
	String *params;         /* unused */
	Cardinal *num_params;   /* unused */
{
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	XButtonPressedEvent *BuEvent = (XButtonPressedEvent *)event;
	struct ele_rec *eptr;
	int epos;

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

#ifdef NOT_RIGHT
        XUndefineCursor(XtDisplay(hw->html.view), XtWindow(hw->html.view));
#endif
        XUndefineCursor(XtDisplay(hw), XtWindow(hw));

	/*
	 * Because X sucks, we can get the button pressed in the window, but
	 * released out of the window.  This will highlight some text, but
	 * never complete the selection.  Now on the next button press we
	 * have to clean up this mess.
	 */
	EraseSelection(hw, hw->html.new_start, hw->html.new_end,
		hw->html.new_start_pos, hw->html.new_end_pos);

	/*
	 * We want to erase the currently selected text, but still save the
	 * selection internally in case we don't create a new one.
	 */
	EraseSelection(hw, hw->html.select_start, hw->html.select_end,
		hw->html.sel_start_pos, hw->html.sel_end_pos);
	hw->html.new_start = hw->html.select_start;
	hw->html.new_end = NULL;
	hw->html.new_start_pos = hw->html.sel_start_pos;
	hw->html.new_end_pos = 0;

	eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos);
	if (eptr != NULL)
	{
		/*
		 * If this is an anchor assume for now we are activating it
		 * and not selecting it.
		 */
		if (eptr->anchorHRef != NULL)
		{
			hw->html.active_anchor = eptr;
			hw->html.press_x = BuEvent->x;
			hw->html.press_y = BuEvent->y;
			SetAnchor(hw);
		}
		/*
		 * Else if we are on an image we can't select text so
		 * pretend we got eptr==NULL, and exit here.
		 */
		else if (eptr->type == E_IMAGE)
		{
			hw->html.new_start = NULL;
			hw->html.new_end = NULL;
			hw->html.new_start_pos = 0;
			hw->html.new_end_pos = 0;
			hw->html.press_x = BuEvent->x;
			hw->html.press_y = BuEvent->y;
			hw->html.but_press_time = BuEvent->time;
			return;
		}
		/*
		 * Else if we used button2, we can't select text, so exit
		 * here.
		 */
		else if (BuEvent->button == Button2)
		{
			hw->html.press_x = BuEvent->x;
			hw->html.press_y = BuEvent->y;
			hw->html.but_press_time = BuEvent->time;
			return;
		}
		/*
		 * Else a single click will not select a new object
		 * but it will prime that selection on the next mouse
		 * move.
		 * Ignore special internal text
		 */
		else if (eptr->internal == False)
		{
			hw->html.new_start = eptr;
			hw->html.new_start_pos = epos;
			hw->html.new_end = NULL;
			hw->html.new_end_pos = 0;
			hw->html.press_x = BuEvent->x;
			hw->html.press_y = BuEvent->y;
		}
		else
		{
			hw->html.new_start = NULL;
			hw->html.new_end = NULL;
			hw->html.new_start_pos = 0;
			hw->html.new_end_pos = 0;
			hw->html.press_x = BuEvent->x;
			hw->html.press_y = BuEvent->y;
		}
	}
	else
	{
		hw->html.new_start = NULL;
		hw->html.new_end = NULL;
		hw->html.new_start_pos = 0;
		hw->html.new_end_pos = 0;
		hw->html.press_x = BuEvent->x;
		hw->html.press_y = BuEvent->y;
	}
	hw->html.but_press_time = BuEvent->time;
}


static void
ExtendStart(w, event, params, num_params)
	Widget w;
	XEvent *event;
	String *params;         /* unused */
	Cardinal *num_params;   /* unused */
{
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	XButtonPressedEvent *BuEvent = (XButtonPressedEvent *)event;
	struct ele_rec *eptr;
	struct ele_rec *start, *end;
	struct ele_rec *old_start, *old_end;
	int old_start_pos, old_end_pos;
	int start_pos, end_pos;
	int epos;

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

	eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos);

	/*
	 * Ignore IMAGE elements.
	 */
	if ((eptr != NULL)&&(eptr->type == E_IMAGE))
	{
		eptr = NULL;
	}

	/*
	 * Ignore NULL elements.
	 * Ignore special internal text
	 * documents.
	 */
	if ((eptr != NULL)&&(eptr->internal == False))
	{
		old_start = hw->html.new_start;
		old_start_pos = hw->html.new_start_pos;
		old_end = hw->html.new_end;
		old_end_pos = hw->html.new_end_pos;
		if (hw->html.new_start == NULL)
		{
			hw->html.new_start = hw->html.select_start;
			hw->html.new_start_pos = hw->html.sel_start_pos;
			hw->html.new_end = hw->html.select_end;
			hw->html.new_end_pos = hw->html.sel_end_pos;
		}
		else
		{
			hw->html.new_end = eptr;
			hw->html.new_end_pos = epos;
		}

		if (SwapElements(hw->html.new_start, hw->html.new_end,
			hw->html.new_start_pos, hw->html.new_end_pos))
		{
			if (SwapElements(eptr, hw->html.new_end,
				epos, hw->html.new_end_pos))
			{
				start = hw->html.new_end;
				start_pos = hw->html.new_end_pos;
				end = eptr;
				end_pos = epos;
			}
			else
			{
				start = hw->html.new_start;
				start_pos = hw->html.new_start_pos;
				end = eptr;
				end_pos = epos;
			}
		}
		else
		{
			if (SwapElements(eptr, hw->html.new_start,
				epos, hw->html.new_start_pos))
			{
				start = hw->html.new_start;
				start_pos = hw->html.new_start_pos;
				end = eptr;
				end_pos = epos;
			}
			else
			{
				start = hw->html.new_end;
				start_pos = hw->html.new_end_pos;
				end = eptr;
				end_pos = epos;
			}
		}

		if (start == NULL)
		{
			start = eptr;
			start_pos = epos;
		}

		if (old_start == NULL)
		{
			hw->html.new_start = hw->html.select_start;
			hw->html.new_end = hw->html.select_end;
			hw->html.new_start_pos = hw->html.sel_start_pos;
			hw->html.new_end_pos = hw->html.sel_end_pos;
		}
		else
		{
			hw->html.new_start = old_start;
			hw->html.new_end = old_end;
			hw->html.new_start_pos = old_start_pos;
			hw->html.new_end_pos = old_end_pos;
		}
		ChangeSelection(hw, start, end, start_pos, end_pos);
		hw->html.new_start = start;
		hw->html.new_end = end;
		hw->html.new_start_pos = start_pos;
		hw->html.new_end_pos = end_pos;
	}
	else
	{
		if (hw->html.new_start == NULL)
		{
			hw->html.new_start = hw->html.select_start;
			hw->html.new_start_pos = hw->html.sel_start_pos;
			hw->html.new_end = hw->html.select_end;
			hw->html.new_end_pos = hw->html.sel_end_pos;
		}
	}
	hw->html.press_x = BuEvent->x;
	hw->html.press_y = BuEvent->y;
}


static void
ExtendAdjust(w, event, params, num_params)
	Widget w;
	XEvent *event;
	String *params;         /* unused */
	Cardinal *num_params;   /* unused */
{
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	XPointerMovedEvent *MoEvent = (XPointerMovedEvent *)event;
	struct ele_rec *eptr;
	struct ele_rec *start, *end;
	int start_pos, end_pos;
	int epos;

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

	/*
	 * Very small mouse motion immediately after button press
	 * is ignored.
	 */
	if ((ABS((hw->html.press_x - MoEvent->x)) <= SELECT_THRESHOLD)&&
	    (ABS((hw->html.press_y - MoEvent->y)) <= SELECT_THRESHOLD))
	{
		return;
	}

	/*
	 * If we have an active anchor and we got here, we have moved the
	 * mouse too far.  Deactivate anchor, and prime a selection.
	 * If the anchor is internal text, don't
	 * prime a selection.
	 */
	if (hw->html.active_anchor != NULL)
	{
		eptr = hw->html.active_anchor;
		UnsetAnchor(hw);
		if (eptr->internal == False)
		{
			hw->html.new_start = NULL;
			hw->html.new_start_pos = 0;
			hw->html.new_end = NULL;
			hw->html.new_end_pos = 0;
		}
	}

	/*
	 * If we used button2, we can't select text, so
	 * clear selection and exit here.
	 */
	if ((MoEvent->state & Button2Mask) != 0)
	{
		hw->html.select_start = NULL;
		hw->html.select_end = NULL;
		hw->html.sel_start_pos = 0;
		hw->html.sel_end_pos = 0;
		hw->html.new_start = NULL;
		hw->html.new_end = NULL;
		hw->html.new_start_pos = 0;
		hw->html.new_end_pos = 0;
		return;
	}

	eptr = LocateElement(hw, MoEvent->x, MoEvent->y, &epos);

	/*
	 * If we are on an image pretend we are nowhere
	 * and just return;
	 */
	if ((eptr != NULL)&&(eptr->type == E_IMAGE))
	{
		return;
	}

	/*
	 * Ignore NULL items.
	 * Ignore if the same as last selected item and position.
	 * Ignore special internal text
	 */
	if ((eptr != NULL)&&
	    ((eptr != hw->html.new_end)||(epos != hw->html.new_end_pos))&&
	    (eptr->internal == False))
	{
		start = hw->html.new_start;
		start_pos = hw->html.new_start_pos;
		end = eptr;
		end_pos = epos;
		if (start == NULL)
		{
			start = eptr;
			start_pos = epos;
		}

		ChangeSelection(hw, start, end, start_pos, end_pos);
		hw->html.new_start = start;
		hw->html.new_end = end;
		hw->html.new_start_pos = start_pos;
		hw->html.new_end_pos = end_pos;
	}
}


static void
ExtendEnd(w, event, params, num_params)
	Widget w;
	XEvent *event;
	String *params;
	Cardinal *num_params;
{
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	XButtonReleasedEvent *BuEvent = (XButtonReleasedEvent *)event;
	struct ele_rec *eptr;
	struct ele_rec *start, *end;
	Atom *atoms;
	int i, buffer;
	int start_pos, end_pos;
	int epos;
	char *text;

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

	eptr = LocateElement(hw, BuEvent->x, BuEvent->y, &epos);

	/*
	 * If we just released button one or two, and we are on an object,
	 * and we have an active anchor, and we are on the active anchor,
	 * and if we havn't waited too long.  Activate that anchor.
	 */
	if (((BuEvent->button == Button1)||(BuEvent->button == Button2))&&
		(eptr != NULL)&&
		(hw->html.active_anchor != NULL)&&
		(eptr == hw->html.active_anchor)&&
		((BuEvent->time - hw->html.but_press_time) < CLICK_TIME))
	{
		_HTMLInput(w, event, params, num_params);
		return;
	}
	else if (hw->html.active_anchor != NULL)
	{
		start = hw->html.active_anchor;
		UnsetAnchor(hw);
		if (start->internal == False)
		{
			hw->html.new_start = eptr;
			hw->html.new_start_pos = epos;
			hw->html.new_end = NULL;
			hw->html.new_end_pos = 0;
		}
	}

	/*
	 * If we used button2, we can't select text, so clear
	 * selection and exit here.
	 */
	if (BuEvent->button == Button2)
	{
		hw->html.new_start = hw->html.select_start;
		hw->html.new_end = NULL;
		hw->html.new_start_pos = hw->html.sel_start_pos;
		hw->html.new_end_pos = 0;
		return;
	}

	/*
	 * If we are on an image, pretend we are nowhere
	 * and NULL out the eptr
	 */
	if ((eptr != NULL)&&(eptr->type == E_IMAGE))
	{
		eptr = NULL;
	}

	/*
	 * If button released on a NULL item, take the last non-NULL
	 * item that we highlighted.
	 */
	if ((eptr == NULL)&&(hw->html.new_end != NULL))
	{
		eptr = hw->html.new_end;
		epos = hw->html.new_end_pos;
	}

	if ((eptr != NULL)&&(eptr->internal == False)&&
		(hw->html.new_end != NULL))
	{
		start = hw->html.new_start;
		start_pos = hw->html.new_start_pos;
		end = eptr;
		end_pos = epos;
		if (start == NULL)
		{
			start = eptr;
			start_pos = epos;
		}
		ChangeSelection(hw, start, end, start_pos, end_pos);
		hw->html.select_start = start;
		hw->html.sel_start_pos = start_pos;
		hw->html.select_end = end;
		hw->html.sel_end_pos = end_pos;
		SetSelection(hw);
		hw->html.new_start = NULL;
		hw->html.new_end = NULL;
		hw->html.new_start_pos = 0;
		hw->html.new_end_pos = 0;

		atoms = (Atom *)malloc(*num_params * sizeof(Atom));
		if (atoms == NULL)
		{
			fprintf(stderr, "cannot allocate atom list\n");
			return;
		}
		XmuInternStrings(XtDisplay((Widget)hw), params, *num_params, atoms);
		hw->html.selection_time = BuEvent->time;
		for (i=0; i< *num_params; i++)
		{
			switch (atoms[i])
			{
				case XA_CUT_BUFFER0: buffer = 0; break;
				case XA_CUT_BUFFER1: buffer = 1; break;
				case XA_CUT_BUFFER2: buffer = 2; break;
				case XA_CUT_BUFFER3: buffer = 3; break;
				case XA_CUT_BUFFER4: buffer = 4; break;
				case XA_CUT_BUFFER5: buffer = 5; break;
				case XA_CUT_BUFFER6: buffer = 6; break;
				case XA_CUT_BUFFER7: buffer = 7; break;
				default: buffer = -1; break;
			}
			if (buffer >= 0)
			{
				if (hw->html.fancy_selections == True)
				{
				    text = ParseTextToPrettyString(hw,
					hw->html.formatted_elements,
					hw->html.select_start,
					hw->html.select_end,
					hw->html.sel_start_pos,
					hw->html.sel_end_pos,
					hw->html.font->max_bounds.width,
					hw->html.margin_width);
				}
				else
				{
				    text = ParseTextToString(
					hw->html.formatted_elements,
					hw->html.select_start,
					hw->html.select_end,
					hw->html.sel_start_pos,
					hw->html.sel_end_pos,
					hw->html.font->max_bounds.width,
					hw->html.margin_width);
				}
				XStoreBuffer(XtDisplay((Widget)hw),
					text, strlen(text), buffer);
				if (text != NULL)
				{
					free(text);
				}
			}
			else
			{
				XtOwnSelection((Widget)hw, atoms[i],
					       BuEvent->time,
					       (XtConvertSelectionProc )ConvertSelection,
					       (XtLoseSelectionProc )LoseSelection,
					       (XtSelectionDoneProc )SelectionDone);
			}
		}
		free((char *)atoms);
	}
	else if (eptr == NULL)
	{
		hw->html.select_start = NULL;
		hw->html.sel_start_pos = 0;
		hw->html.select_end = NULL;
		hw->html.sel_end_pos = 0;
		hw->html.new_start = NULL;
		hw->html.new_start_pos = 0;
		hw->html.new_end = NULL;
		hw->html.new_end_pos = 0;
	}
}


#define LEAVING_ANCHOR(hw) \
  hw->html.cached_tracked_ele = NULL; \
  (*(pointerTrackProc) \
    (hw->html.pointer_motion_callback))(hw, ""); \
  XUndefineCursor (XtDisplay (hw), XtWindow (hw));

/* KNOWN PROBLEM: We never get LeaveNotify or FocusOut events,
   despite the fact we've requested them.  Bummer. */
static void
TrackMotion(w, event, params, num_params)
	Widget w;
	XEvent *event;
	String *params;         /* unused */
	Cardinal *num_params;   /* unused */
{
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	struct ele_rec *eptr;
	int epos, x, y;

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

        if (!hw->html.pointer_motion_callback)
          return;

        if (event->type == MotionNotify)
          {
            x = ((XMotionEvent *)event)->x;
            y = ((XMotionEvent *)event)->y;
          }
        else if (event->type == LeaveNotify || event->type == FocusOut ||
                 event->type == Expose)
          {
            /* Wipe out. */
            if (hw->html.cached_tracked_ele)
              {
                LEAVING_ANCHOR (hw);
              }
            return;
          }
        else
          {
            return;
          }

	eptr = LocateElement(hw, x, y, &epos);
        
        /* We're hitting a new anchor if eptr exists and
           eptr != cached tracked element and anchorHRef != NULL. */
	if (eptr != NULL && eptr != hw->html.cached_tracked_ele &&
            eptr->anchorHRef != NULL)
          {
            hw->html.cached_tracked_ele = eptr;
	    (*(pointerTrackProc)
             (hw->html.pointer_motion_callback))(hw, eptr->anchorHRef);
            XDefineCursor (XtDisplay (hw), XtWindow (hw), in_anchor_cursor);
          }
        /* We're leaving an anchor if eptr exists and
           a cached ele exists and we're not entering a new anchor. */
        else if (eptr != NULL && hw->html.cached_tracked_ele != NULL && 
                 eptr->anchorHRef == NULL)
          {
            LEAVING_ANCHOR (hw);
          }

        return;
}


/*
 * Process mouse input to the HTML widget
 * Currently only processes an anchor-activate when Button1
 * is pressed
 */
static void
#ifdef _NO_PROTO
_HTMLInput( w, event, params, num_params)
	Widget w ;
	XEvent *event ;
	String *params;		/* unused */
	Cardinal *num_params;	/* unused */
#else
_HTMLInput(
	Widget w,
	XEvent *event,
	String *params,		/* unused */
	Cardinal *num_params)	/* unused */
#endif
{   
	HTMLWidget hw = (HTMLWidget)XtParent(w);
	struct ele_rec *eptr;
	WbAnchorCallbackData cbdata;
	int epos;
#ifdef MOTIF
	Boolean on_gadget;
#endif /* MOTIF */

	if (XtClass(XtParent(w)) != htmlWidgetClass)
	{
		return;
	}

#ifdef MOTIF
	/*
	 * If motif is defined, we don't want to process this button press
	 * if it is on a gadget
	 */
#ifdef MOTIF1_2
	on_gadget = (_XmInputForGadget((Widget)hw,
#else
	on_gadget = (_XmInputForGadget((CompositeWidget)hw,
#endif /* MOTIF1_2 */
				event->xbutton.x, event->xbutton.y) != NULL);

	if (on_gadget)
	{
		return;
	}
#endif /* MOTIF */

	if (event->type == ButtonRelease)
	{
		eptr = LocateElement(hw, event->xbutton.x, event->xbutton.y,
				&epos);
		if (eptr != NULL)
		{
			if (eptr->anchorHRef != NULL)
			{
			    char *tptr, *ptr;

			   /*
			    * Save the anchor text, replace newlines with
			    * spaces.
			    */
			    tptr = ParseTextToString(hw->html.select_start,
				hw->html.select_start, hw->html.select_end,
				hw->html.sel_start_pos, hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
			    ptr = tptr;
			    while ((ptr != NULL)&&(*ptr != '\0'))
			    {
				if (*ptr == '\n')
				{
					*ptr = ' ';
				}
				ptr++;
			    }

			   /*
			    * Clear the activated anchor
			    */
			    UnsetAnchor(hw);
#ifdef EXTRA_FLUSH
			    XFlush(XtDisplay(hw));
#endif

			    if ((IsDelayedHRef(hw, eptr->anchorHRef))&&
				(hw->html.resolveDelayedImage != NULL))
			    {
				eptr->pic_data = (*(resolveImageProc)
				    (hw->html.resolveDelayedImage))(hw, eptr->edata);

				if (eptr->pic_data == NULL)
				{
					eptr->pic_data = NoImageData(hw);
					eptr->pic_data->delayed = 0;
					eptr->pic_data->internal = 0;
				}
				else
				{
					eptr->pic_data->delayed = 0;
					/*
					 * Mark images we have sucessfully
					 * loaded at least once
					 */
					if (eptr->pic_data->image_data != NULL)
					{
					    eptr->pic_data->fetched = 1;
					}
					/*
					 * See if this is a special
					 * internal image
					 */
					if ((eptr->edata != NULL)&&
						(strncmp(eptr->edata,
						INTERNAL_IMAGE,
						strlen(INTERNAL_IMAGE)) == 0))
					{
					    eptr->pic_data->internal = 1;
					}
					else
					{
					    eptr->pic_data->internal = 0;
					}
				}

				ReformatWindow(hw);
				ScrollWidgets(hw);
				ViewClearAndRefresh(hw);
			    }
			    else if ((eptr->pic_data != NULL)&&
				(eptr->pic_data->delayed)&&
				(eptr->anchorHRef != NULL)&&
				(IsIsMapForm(hw, eptr->anchorHRef)))
			    {
				eptr->pic_data = (*(resolveImageProc)
				    (hw->html.resolveDelayedImage))(hw, eptr->edata);

				if (eptr->pic_data == NULL)
				{
					eptr->pic_data = NoImageData(hw);
					eptr->pic_data->delayed = 0;
					eptr->pic_data->internal = 0;
				}
				else
				{
					eptr->pic_data->delayed = 0;
					/*
					 * Mark images we have sucessfully
					 * loaded at least once
					 */
					if (eptr->pic_data->image_data != NULL)
					{
					    eptr->pic_data->fetched = 1;
					}
					/*
					 * See if this is a special
					 * internal image
					 */
					if ((eptr->edata != NULL)&&
						(strncmp(eptr->edata,
						INTERNAL_IMAGE,
						strlen(INTERNAL_IMAGE)) == 0))
					{
					    eptr->pic_data->internal = 1;
					}
					else
					{
					    eptr->pic_data->internal = 0;
					}
				}

				ReformatWindow(hw);
				ScrollWidgets(hw);
				ViewClearAndRefresh(hw);
			    }
			    else if ((eptr->pic_data != NULL)&&
				(eptr->pic_data->delayed)&&
				(eptr->anchorHRef != NULL)&&
				(!IsDelayedHRef(hw, eptr->anchorHRef))&&
				(!IsIsMapForm(hw, eptr->anchorHRef))&&
				(((event->xbutton.y + hw->html.scroll_y) -
				  (eptr->y + eptr->y_offset)) >
				  AnchoredHeight(hw)))
			    {
				eptr->pic_data = (*(resolveImageProc)
				    (hw->html.resolveDelayedImage))(hw, eptr->edata);

				if (eptr->pic_data == NULL)
				{
					eptr->pic_data = NoImageData(hw);
					eptr->pic_data->delayed = 0;
					eptr->pic_data->internal = 0;
				}
				else
				{
					/*
					 * Mark images we have sucessfully
					 * loaded at least once
					 */
					if (eptr->pic_data->image_data != NULL)
					{
					    eptr->pic_data->fetched = 1;
					}
					/*
					 * See if this is a special
					 * internal image
					 */
					if ((eptr->edata != NULL)&&
						(strncmp(eptr->edata,
						INTERNAL_IMAGE,
						strlen(INTERNAL_IMAGE)) == 0))
					{
					    eptr->pic_data->internal = 1;
					}
					else
					{
					    eptr->pic_data->internal = 0;
					}
				}
				eptr->pic_data->delayed = 0;

				ReformatWindow(hw);
				ScrollWidgets(hw);
				ViewClearAndRefresh(hw);
			    }
			    else if ((eptr->pic_data != NULL)&&
				(eptr->pic_data->ismap)&&
				(eptr->anchorHRef != NULL)&&
				(IsIsMapForm(hw, eptr->anchorHRef)))
			    {
				int form_x, form_y;

				form_x = event->xbutton.x + hw->html.scroll_x -
					eptr->x;
				form_y = event->xbutton.y + hw->html.scroll_y -
					eptr->y;
				ImageSubmitForm(eptr->pic_data->fptr, event,
					eptr->pic_data->text, form_x, form_y);
			    }
			    else
			    {
			     	/* The following is a hack to send the
			     	 * selection location along with the HRef
			     	 * for images.  This allows you to
			     	 * point at a location on a map and have
			     	 * the server send you the related document.
			     	 * Tony Sanders, April 1993 <sanders@bsdi.com>
			     	 */
			     	int alloced = 0;
			     	char *buf = eptr->anchorHRef;
				if (eptr->type == E_IMAGE && eptr->pic_data
				        && eptr->pic_data->ismap) {
				    buf = (char *)
				        malloc(strlen(eptr->anchorHRef) + 256);
				    alloced = 1;
				    sprintf(buf, "%s?%d,%d",
					eptr->anchorHRef,
					event->xbutton.x + hw->html.scroll_x - eptr->x,
					event->xbutton.y + hw->html.scroll_y - eptr->y);
			        }
				/*
				 * XXX: should call a media dependent
				 * function that decides how to munge the
				 * HRef.  For example mpeg data will want
				 * to know on what frame the event occured.
				 *
				 * cddata.href = *(eptr->eventf)(eptr, event);
			         */
				cbdata.event = event;
				cbdata.element_id = eptr->ele_id;
				cbdata.href = buf;
				/* cbdata.href = eptr->anchorHRef; */
				cbdata.text = tptr;
				XtCallCallbackList ((Widget)hw,
					hw->html.anchor_callback,
					(XtPointer)&cbdata);
			        if (alloced) free(buf);
			        if (tptr != NULL) free(tptr);
			    }
			}
		}
	}

	return;
}


#ifndef MOTIF
#include <X11/Xaw/AsciiText.h>
/*
 * Process key input passwd widgets
 */
static void
#ifdef _NO_PROTO
_HTMLpwdInput( w, event, params, num_params)
	Widget w ;
	XEvent *event ;
	String *params;		/* unused */
	Cardinal *num_params;	/* unused */
#else
_HTMLpwdInput(
	Widget w,
	XEvent *event,
	String *params,		/* unused */
	Cardinal *num_params)	/* unused */
#endif
     {
     char buffer[50];
     KeySym ks;
     char *keySymString;
     char *star = "*";
     int length, passwdLength, i, insertPos, maxLength;
     Boolean stringInPlace; 
     
     if (event->type == KeyPress)
	 {
	 HTMLWidget hw = (HTMLWidget)XtParent(XtParent(w));
	 WidgetInfo *wptr;

	 if (XtClass((Widget)hw) != htmlWidgetClass) 
	     return;   /* it was not for us */
 
	/*
	 * 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;

	 passwdLength = wptr->password ? strlen(wptr->password) : 0;

	 length = XLookupString((XKeyEvent *)event, 
				buffer, 50, &ks, NULL);
	 keySymString = XKeysymToString(ks);
	 XtVaGetValues(w,
		       XtNinsertPosition,&insertPos,
		       XtNuseStringInPlace, &stringInPlace,
		       XtNlength, &maxLength,
		       NULL);

         if (maxLength<1) maxLength = 1000000;

	 if (!strcmp("Left",keySymString))
	     {
	     if (insertPos > 0)
		 XtVaSetValues(w,XtNinsertPosition,--insertPos,NULL);
	     return;
	     }
	 else if (!strcmp("Right",keySymString))
	     {
	     if (insertPos < passwdLength)
		 XtVaSetValues(w,XtNinsertPosition,++insertPos,NULL);
	     return;
	     }

	 if ((!strcmp("BackSpace",keySymString))
	     || (!strcmp("Backspace",keySymString))
	     || (!strcmp("Delete",keySymString)) ) 
	     {
	     insertPos --;

	     if (passwdLength>0)
		 {
		 char *pwd = &(wptr->password[insertPos]);

		 for (i = passwdLength - insertPos; i>0; i--,pwd++)
		     *pwd = *(pwd+1);

/*		 fprintf(stderr,"modified passwd <%s>\n", wptr->password);*/
		 XtCallActionProc(w, 
				  insertPos>-1 ? "delete-previous-character" :
				  "delete-next-character",
				  event, NULL,0);
		 }
	     /* else nothing to erase */
	     return;
	     }

	 if (length == 1)
	     {
	     buffer[1] = '\0';

	     if (passwdLength>0)
		 {

		 if (passwdLength < maxLength)
		     {
		     char *pwd = wptr->password =
			 (char *)realloc(wptr->password, 
				 sizeof(char)*(passwdLength+2));
		     for (i=passwdLength+1; i>insertPos; i--)
			 pwd[i] = pwd[i-1];
		     
		     pwd[insertPos] = buffer[0];
		     }
		 }
	     else 
		 {
		 if (wptr->password == NULL)
		     wptr->password = (char *)malloc(sizeof(char)*2);

		 wptr->password[0] = buffer[0];
		 wptr->password[1] = '\0';
		 }

	     if (stringInPlace && passwdLength<maxLength)
		 {
		 char *txt;
		 /* insert string dumps core when  string in place is true */

		 XtVaGetValues(w,XtNstring,&txt,NULL);
		 txt[passwdLength] = star[0];
		 txt[passwdLength+1] = '\0';

		 /* the combined set values command does not work */
		 XtVaSetValues(w, XtNstring,txt, NULL);
		 XtVaSetValues(w, XtNinsertPosition,insertPos+1, NULL);
		 }
	     else
		 XtCallActionProc(w, "insert-string", event, &star, 1);
/*	     fprintf(stderr,"modified passwd <%s>\n",  wptr->password); */
	     }
	 }
     }
#endif /* not MOTIF */


/*
 * SetValues is called when XtSetValues is used to change resources in this
 * widget.
 */
static Boolean
#ifdef _NO_PROTO
SetValues (current, request, new)
            HTMLWidget current ;
            HTMLWidget request ;
            HTMLWidget new ;
#else
SetValues(
            HTMLWidget current,
            HTMLWidget request,
            HTMLWidget new)
#endif
{
	int reformatted;

	/*
	 *	Make sure the underline numbers are within bounds.
	 */
	if (request->html.num_anchor_underlines < 0)
	{
		new->html.num_anchor_underlines = 0;
	} 
	if (request->html.num_anchor_underlines > MAX_UNDERLINES)
	{
		new->html.num_anchor_underlines = MAX_UNDERLINES;
	} 
	if (request->html.num_visitedAnchor_underlines < 0)
	{
		new->html.num_visitedAnchor_underlines = 0;
	} 
	if (request->html.num_visitedAnchor_underlines > MAX_UNDERLINES)
	{
		new->html.num_visitedAnchor_underlines = MAX_UNDERLINES;
	} 

	reformatted = 0;
	if ((request->html.raw_text != current->html.raw_text)||
	    (request->html.header_text != current->html.header_text)||
	    (request->html.footer_text != current->html.footer_text))
	{
		/*
		 * Free up the old visited href list.
		 */
		FreeHRefs(current->html.my_visited_hrefs);
		new->html.my_visited_hrefs = NULL;

		/*
		 * Free up the old visited delayed images list.
		 */
		FreeDelayedImages(current->html.my_delayed_images);
		new->html.my_delayed_images = NULL;

		/*
		 * Free any old colors and pixmaps
		 */
		FreeColors(XtDisplay(current), DefaultColormapOfScreen(XtScreen(current)));
		FreeImages(current);

		/*
		 * Hide any old widgets
		 */
		HideWidgets(current);
		new->html.widget_list = NULL;
		new->html.form_list = NULL;

		/*
		 * Parse the raw text with the HTML parser
		 */
		new->html.html_objects = HTMLParse(current->html.html_objects,
			request->html.raw_text);
		CallLinkCallbacks(new);
		new->html.html_header_objects =
			HTMLParse(current->html.html_header_objects,
			request->html.header_text);
		new->html.html_footer_objects =
			HTMLParse(current->html.html_footer_objects,
			request->html.footer_text);

		/*
		 * Redisplay for the changed data.
		 */
		{
			new->html.scroll_x = 0;
			new->html.scroll_y = 0;
			new->html.max_pre_width = DocumentWidth(new,
				new->html.html_objects);
			ReformatWindow(new);
			ViewClearAndRefresh(new);
			reformatted = 1;
		}

		/*
		 * Clear any previous selection
		 */
		new->html.select_start = NULL;
		new->html.select_end = NULL;
		new->html.sel_start_pos = 0;
		new->html.sel_end_pos = 0;
		new->html.new_start = NULL;
		new->html.new_end = NULL;
		new->html.new_start_pos = 0;
		new->html.new_end_pos = 0;
		new->html.active_anchor = NULL;
	}
	else if ((request->html.font != current->html.font)||
	         (request->html.italic_font != current->html.italic_font)||
	         (request->html.bold_font != current->html.bold_font)||
	         (request->html.fixed_font != current->html.fixed_font)||
	         (request->html.fixedbold_font != current->html.fixedbold_font)||
	         (request->html.fixeditalic_font != current->html.fixeditalic_font)||
	         (request->html.header1_font != current->html.header1_font)||
	         (request->html.header2_font != current->html.header2_font)||
	         (request->html.header3_font != current->html.header3_font)||
	         (request->html.header4_font != current->html.header4_font)||
	         (request->html.header5_font != current->html.header5_font)||
	         (request->html.header6_font != current->html.header6_font)||
	         (request->html.address_font != current->html.address_font)||
	         (request->html.plain_font != current->html.plain_font)||
	         (request->html.plainbold_font != current->html.plainbold_font)||
	         (request->html.plainitalic_font != current->html.plainitalic_font)||
	         (request->html.listing_font != current->html.listing_font)||
	         (request->html.activeAnchor_fg != current->html.activeAnchor_fg)||
	         (request->html.activeAnchor_bg != current->html.activeAnchor_bg)||
	         (request->html.anchor_fg != current->html.anchor_fg)||
	         (request->html.visitedAnchor_fg != current->html.visitedAnchor_fg)||
	         (request->html.dashed_anchor_lines != current->html.dashed_anchor_lines)||
	         (request->html.dashed_visitedAnchor_lines != current->html.dashed_visitedAnchor_lines)||
	         (request->html.num_anchor_underlines != current->html.num_anchor_underlines)||
	         (request->html.num_visitedAnchor_underlines != current->html.num_visitedAnchor_underlines))
	{
		if ((request->html.plain_font != current->html.plain_font)||
		    (request->html.listing_font != current->html.listing_font))
		{
			new->html.max_pre_width = DocumentWidth(new,
				new->html.html_objects);
		}

		ReformatWindow(new);
		ScrollWidgets(new);
		ViewClearAndRefresh(new);
		reformatted = 1;
	}

	/*
	 * image borders have been changed
	 */
	if (request->html.border_images != current->html.border_images)
	{
		ReformatWindow(new);
		ScrollWidgets(new);
		ViewClearAndRefresh(new);
		reformatted = 1;
	}

	/*
	 * vertical space has been changed
	 */
	if(request->html.percent_vert_space != current->html.percent_vert_space)
	{
		ReformatWindow(new);
		ScrollWidgets(new);
		ViewClearAndRefresh(new);
		reformatted = 1;
	}

	return(False);
}


/*
 * Go through the parsed marks and for all the <LINK> tags in the document
 * call the LinkCallback.
 */
static void
#ifdef _NO_PROTO
CallLinkCallbacks(hw)
	HTMLWidget hw;
#else
CallLinkCallbacks(HTMLWidget hw)
#endif
{
	struct mark_up *mptr;
	LinkInfo l_info;

	mptr = hw->html.html_objects;
	while (mptr != NULL)
	{
		if (mptr->type == M_BASE)
		{
			l_info.href = ParseMarkTag(mptr->start, MT_BASE,
				"HREF");
			l_info.role = ParseMarkTag(mptr->start, MT_BASE,
				"ROLE");
			XtCallCallbackList ((Widget)hw, hw->html.link_callback,
				(XtPointer)&l_info);
			if (l_info.href != NULL)
			{
				free(l_info.href);
			}
			if (l_info.role != NULL)
			{
				free(l_info.role);
			}
		}
		mptr = mptr->next;
	}
}


/*
 * Search through the whole document, and recolor the internal elements with
 * the passed HREF.
 */
static void
#ifdef _NO_PROTO
RecolorInternalHRefs(hw, href)
	HTMLWidget hw;
	char *href;
#else
RecolorInternalHRefs(HTMLWidget hw, char *href)
#endif
{
	struct ele_rec *start;
	unsigned long fg;

	fg = hw->html.visitedAnchor_fg;
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		/*
		 * This one is internal
		 * This one has an href
		 * This is the href we want
		 */
		if ((start->internal == True)&&
		    (start->anchorHRef != NULL)&&
		    (strcmp(start->anchorHRef, href) == 0))
		{
			start->fg = fg;
			start->underline_number =
				hw->html.num_visitedAnchor_underlines;
			start->dashed_underline =
				hw->html.dashed_visitedAnchor_lines;
		}
		start = start->next;
	}
}


static Boolean
ConvertSelection(w, selection, target, type, value, length, format)
	Widget w;
	Atom *selection, *target, *type;
	caddr_t *value;
	unsigned long *length;
	int *format;
{
	Display *d = XtDisplay(w);
	HTMLWidget hw = (HTMLWidget)w;
	char *text;

	if (hw->html.select_start == NULL)
	{
		return False;
	}

	if (*target == XA_TARGETS(d))
	{
		Atom *targetP;
		Atom *std_targets;
		unsigned long std_length;
		XmuConvertStandardSelection( w, hw->html.selection_time,
			selection, target, type, (caddr_t*)&std_targets,
			&std_length, format);

		*length = std_length + 5;
		*value = (caddr_t)XtMalloc(sizeof(Atom)*(*length));
		targetP = *(Atom**)value;
		*targetP++ = XA_STRING;
		*targetP++ = XA_TEXT(d);
		*targetP++ = XA_COMPOUND_TEXT(d);
		*targetP++ = XA_LENGTH(d);
		*targetP++ = XA_LIST_LENGTH(d);

		bcopy((char*)std_targets, (char*)targetP,
			sizeof(Atom)*std_length);
		XtFree((char*)std_targets);
		*type = XA_ATOM;
		*format = 32;
		return True;
	}

	if (*target == XA_STRING || *target == XA_TEXT(d) ||
		*target == XA_COMPOUND_TEXT(d))
	{
		if (*target == XA_COMPOUND_TEXT(d))
		{
			*type = *target;
		}
		else
		{
			*type = XA_STRING;
		}
		if (hw->html.fancy_selections == True)
		{
			text = ParseTextToPrettyString(hw,
				hw->html.formatted_elements,
				hw->html.select_start, hw->html.select_end,
				hw->html.sel_start_pos, hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
		}
		else
		{
			text = ParseTextToString(hw->html.formatted_elements,
				hw->html.select_start, hw->html.select_end,
				hw->html.sel_start_pos, hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
		}
		*value = text;
		*length = strlen(*value);
		*format = 8;
		return True;
	}

	if (*target == XA_LIST_LENGTH(d))
	{
		*value = XtMalloc(4);
		if (sizeof(long) == 4)
		{
			*(long*)*value = 1;
		}
		else
		{
			long temp = 1;
			bcopy( ((char*)&temp)+sizeof(long)-4, (char*)*value, 4);
		}
		*type = XA_INTEGER;
		*length = 1;
		*format = 32;
		return True;
	}

	if (*target == XA_LENGTH(d))
	{
		if (hw->html.fancy_selections == True)
		{
			text = ParseTextToPrettyString(hw,
				hw->html.formatted_elements,
				hw->html.select_start, hw->html.select_end,
				hw->html.sel_start_pos, hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
		}
		else
		{
			text = ParseTextToString(hw->html.formatted_elements,
				hw->html.select_start, hw->html.select_end,
				hw->html.sel_start_pos, hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
		}
		*value = XtMalloc(4);
		if (sizeof(long) == 4)
		{
			*(long*)*value = strlen(text);
		}
		else
		{
			long temp = strlen(text);
			bcopy( ((char*)&temp)+sizeof(long)-4, (char*)*value, 4);
		}
		free(text);
		*type = XA_INTEGER;
		*length = 1;
		*format = 32;
		return True;
	}

	if (XmuConvertStandardSelection(w, hw->html.selection_time, selection,
				    target, type, value, length, format))
	{
		return True;
	}

	return False;
}


static void
LoseSelection(w, selection)
	Widget w;
	Atom *selection;
{
	HTMLWidget hw = (HTMLWidget)w;

	ClearSelection(hw);
}


static void
SelectionDone(w, selection, target)
	Widget w;
	Atom *selection, *target;
{
	/* empty proc so Intrinsics know we want to keep storage */
}


/*
 *************************************************************************
 ******************************* PUBLIC FUNCTIONS ************************
 *************************************************************************
 */


/*
 * Convenience function to return the text of the HTML document as a plain
 * ascii text string.
 * This function allocates memory for the returned string, that it is up
 * to the user to free.
 * Extra option flags "pretty" text to be returned.
 * when pretty is two or larger, Postscript is returned. The font used is
 * encoded in the pretty parameter:
 * pretty = 2: Times
 * pretty = 3: Helvetica
 * pretty = 4: New century schoolbook
 * pretty = 5: Lucida Bright
 */
char *
#ifdef _NO_PROTO
HTMLGetText (w, pretty)
	Widget w;
	int pretty;
#else
HTMLGetText(Widget w, int pretty)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	char *text;
	char *tptr, *buf;
	struct ele_rec *start;
	struct ele_rec *end;

	text = NULL;
	start = hw->html.formatted_elements;
	end = start;
	while (end != NULL)
	{
		end = end->next;
	}

	if (pretty >= 2)
	{
		tptr = ParseTextToPSString(hw, start, start, end,
				0, 0,
				hw->html.font->max_bounds.width,
				hw->html.margin_width , pretty-2);
	}
	else if (pretty)
	{
		tptr = ParseTextToPrettyString(hw, start, start, end,
				0, 0,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
	}
	else
	{
		tptr = ParseTextToString(start, start, end,
				0, 0,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
	}
	if (tptr != NULL)
	{
		if (text == NULL)
		{
			text = tptr;
		}
		else
		{
			buf = (char *)malloc(strlen(text) +
				strlen(tptr) + 1);
			strcpy(buf, text);
			strcat(buf, tptr);
			free(text);
			free(tptr);
			text = buf;
		}
	}
	return(text);
}


/*
 * Convenience function to return the element id of the element
 * nearest to the x,y coordinates passed in.
 * If there is no element there, return the first element in the
 * line we are on.  If there we are on no line, either return the
 * beginning, or the end of the document.
 */
int
#ifdef _NO_PROTO
HTMLPositionToId(w, x, y)
	Widget w;
	int x, y;
#else
HTMLPositionToId(Widget w, int x, int y)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	int i;
	int epos;
	struct ele_rec *eptr;

	eptr = LocateElement(hw, x, y, &epos);
	if (eptr == NULL)
	{
		x = x + hw->html.scroll_x;
		y = y + hw->html.scroll_y;
		eptr = hw->html.line_array[0];
		for (i=0; i<hw->html.line_count; i++)
		{
			if (hw->html.line_array[i] == NULL)
			{
				continue;
			}
			else if (hw->html.line_array[i]->y <= y)
			{
				eptr = hw->html.line_array[i];
				continue;
			}
			else
			{
				break;
			}
		}
	}

	/*
	 * 0 means the very top of the document.  We put you there for
	 * unfound elements.
	 * We also special case for when the scrollbar is at the
	 * absolute top.
	 */
	if ((eptr == NULL)||(hw->html.scroll_y == 0))
	{
		return(0);
	}
	else
	{
		return(eptr->ele_id);
	}
}


/*
 * Convenience function to return the position of the element
 * based on the element id passed in.
 * Function returns 1 on success and fills in x,y pixel values.
 * If there is no such element, x=0, y=0 and -1 is returned.
 */
int
#ifdef _NO_PROTO
HTMLIdToPosition(w, element_id, x, y)
	Widget w;
	int element_id;
	int *x, *y;
#else
HTMLIdToPosition(Widget w, int element_id, int *x, int *y)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct ele_rec *start;
	struct ele_rec *eptr;

	eptr = NULL;
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		if (start->ele_id == element_id)
		{
			eptr = start;
			break;
		}
		start = start->next;
	}

	if (eptr == NULL)
	{
		*x = 0;
		*y = 0;
		return(-1);
	}
	else
	{
		*x = eptr->x;
		*y = eptr->y;
		return(1);
	}
}


/*
 * Convenience function to position the element
 * based on the element id passed at the top of the viewing area.
 * A passed in id of 0 means goto the top.
 */
void
#ifdef _NO_PROTO
HTMLGotoId(w, element_id)
	Widget w;
	int element_id;
#else
HTMLGotoId(Widget w, int element_id)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct ele_rec *start;
	struct ele_rec *eptr;
	int newy;
#ifdef MOTIF
	int val, size, inc, pageinc;
#endif

	/*
	 * If we have no scrollbar, just return.
	 */
	if (hw->html.use_vbar == False)
	{
		return;
	}

	/*
	 * Find the element corrsponding to the id passed in.
	 */
	eptr = NULL;
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		if (start->ele_id == element_id)
		{
			eptr = start;
			break;
		}
		start = start->next;
	}

	/*
	 * No such element, do nothing.
	 */
	if ((element_id != 0)&&(eptr == NULL))
	{
		return;
	}

	if (element_id == 0)
	{
		newy = 0;
	}
	else
	{
		newy = eptr->y - 2;
	}
	if (newy < 0)
	{
		newy = 0;
	}
	if (newy > (hw->html.doc_height - (int)hw->html.view_height))
	{
		newy = hw->html.doc_height - (int)hw->html.view_height;
	}
	if (newy < 0)
	{
		newy = 0;
	}
#ifdef MOTIF
	XmScrollBarGetValues(hw->html.vbar, &val, &size, &inc, &pageinc);
	XmScrollBarSetValues(hw->html.vbar, newy, size, inc, pageinc, True);
	XmScrollBarGetValues(hw->html.hbar, &val, &size, &inc, &pageinc);
	XmScrollBarSetValues(hw->html.hbar, 0, size, inc, pageinc, True);
#else
	ScrollToPos(hw->html.vbar, hw, newy);
	ScrollToPos(hw->html.hbar, hw, 0);
	setScrollBar(hw->html.vbar, newy, 
		     hw->html.doc_height,
		     hw->html.view_height);
#endif
}


/*
 * Convenience function to return the position of the anchor
 * based on the anchor NAME passed.
 * Function returns 1 on success and fills in x,y pixel values.
 * If there is no such element, x=0, y=0 and -1 is returned.
 */
int
#ifdef _NO_PROTO
HTMLAnchorToPosition(w, name, x, y)
	Widget w;
	char *name;
	int *x, *y;
#else
HTMLAnchorToPosition(Widget w, char *name, int *x, int *y)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct ele_rec *start;
	struct ele_rec *eptr;

	eptr = NULL;
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		if ((start->anchorName)&&
		    (strcmp(start->anchorName, name) == 0))
		{
			eptr = start;
			break;
		}
		start = start->next;
	}

	if (eptr == NULL)
	{
		*x = 0;
		*y = 0;
		return(-1);
	}
	else
	{
		*x = eptr->x;
		*y = eptr->y;
		return(1);
	}
}


/*
 * Convenience function to return the element id of the anchor
 * based on the anchor NAME passed.
 * Function returns id on success.
 * If there is no such element, 0 is returned.
 */
int
#ifdef _NO_PROTO
HTMLAnchorToId(w, name)
	Widget w;
	char *name;
#else
HTMLAnchorToId(Widget w, char *name)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct ele_rec *start;
	struct ele_rec *eptr;

	/*
	 * Find the passed anchor name
	 */
	eptr = NULL;
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		if ((start->anchorName)&&
		    (strcmp(start->anchorName, name) == 0))
		{
			eptr = start;
			break;
		}
		start = start->next;
	}

	if (eptr == NULL)
	{
		return(0);
	}
	else
	{
		return(eptr->ele_id);
	}
}


/*
 * Convenience function to return the HREFs of all active anchors in the
 * document.
 * Function returns an array of strings and fills num_hrefs passed.
 * If there are no HREFs NULL returned.
 */
char **
#ifdef _NO_PROTO
HTMLGetHRefs(w, num_hrefs)
	Widget w;
	int *num_hrefs;
#else
HTMLGetHRefs(Widget w, int *num_hrefs)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	int cnt;
	struct ele_rec *start;
	struct ele_rec *list;
	struct ele_rec *eptr;
	char **harray;

	list = NULL;
	cnt = 0;
	/*
	 * Construct a linked list of all the diffeent hrefs, counting
	 * then as we go.
	 */
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		/*
		 * This one has an HREF
		 */
		if (start->anchorHRef != NULL)
		{
			/*
			 * Check to see if we already have
			 * this HREF in our list.
			 */
			eptr = list;
			while (eptr != NULL)
			{
				if (strcmp(eptr->anchorHRef,
					start->anchorHRef) == 0)
				{
					break;
				}
				eptr = eptr->next;
			}
			/*
			 * This HREF is not, in our list.  Add it.
			 * That is, if it's not an internal reference.
			 */
			if ((eptr == NULL)&&(start->internal == False))
			{
				eptr = (struct ele_rec *)
					malloc(sizeof(struct ele_rec));
				eptr->anchorHRef = start->anchorHRef;
				eptr->next = list;
				list = eptr;
				cnt++;
			}
		}
		start = start->next;
	}

	if (cnt == 0)
	{
		*num_hrefs = 0;
		return(NULL);
	}
	else
	{
		*num_hrefs = cnt;
		harray = (char **)malloc(sizeof(char *) * cnt);
		eptr = list;
		cnt--;
		while (eptr != NULL)
		{
			harray[cnt] = (char *)
				malloc(strlen(eptr->anchorHRef) + 1);
			strcpy(harray[cnt], eptr->anchorHRef);
			start = eptr;
			eptr = eptr->next;
			free((char *)start);
			cnt--;
		}
		return(harray);
	}
}


/*
 * Convenience function to return the SRCs of all images in the
 * document.
 * Function returns an array of strings and fills num_srcs passed.
 * If there are no SRCs NULL returned.
 */
char **
#ifdef _NO_PROTO
HTMLGetImageSrcs(w, num_srcs)
	Widget w;
	int *num_srcs;
#else
HTMLGetImageSrcs(Widget w, int *num_srcs)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct mark_up *mptr;
	int cnt;
	char *tptr;
	char **harray;

	cnt = 0;
	mptr = hw->html.html_objects;
	while (mptr != NULL)
	{
		if (mptr->type == M_IMAGE)
		{
			tptr = ParseMarkTag(mptr->start, MT_IMAGE, "SRC");
			if ((tptr != NULL)&&(*tptr != '\0'))
			{
				cnt++;
				free(tptr);
			}
		}
		mptr = mptr->next;
	}

	if (cnt == 0)
	{
		*num_srcs = 0;
		return(NULL);
	}
	else
	{
		*num_srcs = cnt;
		harray = (char **)malloc(sizeof(char *) * cnt);
		mptr = hw->html.html_objects;
		cnt = 0;
		while (mptr != NULL)
		{
			if (mptr->type == M_IMAGE)
			{
				tptr = ParseMarkTag(mptr->start,MT_IMAGE,"SRC");
				if ((tptr != NULL)&&(*tptr != '\0'))
				{
					harray[cnt] = tptr;
					cnt++;
				}
			}
			mptr = mptr->next;
		}
		return(harray);
	}
}


/*
 * Convenience function to return the link information
 * for all the <LINK> tags in the document.
 * Function returns an array of LinkInfo structures and fills
 * num_links passed.
 * If there are no LINKs NULL returned.
 */
LinkInfo *
#ifdef _NO_PROTO
HTMLGetLinks(w, num_links)
	Widget w;
	int *num_links;
#else
HTMLGetLinks(Widget w, int *num_links)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct mark_up *mptr;
	int cnt;
	char *tptr;
	LinkInfo *larray;

	cnt = 0;
	mptr = hw->html.html_objects;
	while (mptr != NULL)
	{
		if (mptr->type == M_BASE)
		{
			cnt++;
		}
		mptr = mptr->next;
	}

	if (cnt == 0)
	{
		*num_links = 0;
		return(NULL);
	}
	else
	{
		*num_links = cnt;
		larray = (LinkInfo *)malloc(sizeof(LinkInfo) * cnt);
		mptr = hw->html.html_objects;
		cnt = 0;
		while (mptr != NULL)
		{
			if (mptr->type == M_BASE)
			{
				tptr = ParseMarkTag(mptr->start,
					MT_BASE, "HREF");
				larray[cnt].href = tptr;
				tptr = ParseMarkTag(mptr->start,
					MT_BASE, "ROLE");
				larray[cnt].role = tptr;
				cnt++;
			}
			mptr = mptr->next;
		}
		return(larray);
	}
}



void *
#ifdef _NO_PROTO
HTMLGetWidgetInfo(w)
	Widget w;
#else
HTMLGetWidgetInfo(Widget w)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;

	return((void *)hw->html.widget_list);
}


void
#ifdef _NO_PROTO
HTMLFreeImageInfo(w)
	Widget w;
#else
HTMLFreeImageInfo(Widget w)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;

	FreeColors(XtDisplay(w), DefaultColormapOfScreen(XtScreen(w)));
	FreeImages(hw);
}


void
#ifdef _NO_PROTO
HTMLFreeWidgetInfo(ptr)
	void *ptr;
#else
HTMLFreeWidgetInfo(void *ptr)
#endif
{
	WidgetInfo *wptr = (WidgetInfo *)ptr;
	WidgetInfo *tptr;

	while (wptr != NULL)
	{
		tptr = wptr;
		wptr = wptr->next;
		if (tptr->w != NULL)
		{
			/*
			 * This is REALLY DUMB, but X generates an expose event
			 * for the destruction of the Widgte, even if it isn't
			 * mapped at the time it is destroyed.
			 * So I move the invisible widget to -1000,-1000
			 * before destroying it, to avoid a visible flash.
			 */
			XtMoveWidget(tptr->w, -1000, -1000);
			XtDestroyWidget(tptr->w);
		}
		if (tptr->name != NULL)
		{
			free(tptr->name);
		}
		if ((tptr->value != NULL)&&(tptr->type != W_OPTIONMENU))
		{
			free(tptr->value);
		}
		free((char *)tptr);
	}
}


/*
 * Convenience function to redraw all active anchors in the
 * document.
 * Can also pass a new predicate function to check visited
 * anchors.  If NULL passed for function, uses default predicate
 * function.
 */
void
#ifdef _NO_PROTO
HTMLRetestAnchors(w, testFunc)
	Widget w;
	visitTestProc testFunc;
#else
HTMLRetestAnchors(Widget w, visitTestProc testFunc)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	struct ele_rec *start;

	if (testFunc == NULL)
	{
		testFunc = (visitTestProc)hw->html.previously_visited_test;
	}

	/*
	 * Search all elements
	 */
	start = hw->html.formatted_elements;
	while (start != NULL)
	{
		if ((start->internal == True)||
		    (start->anchorHRef == NULL))
		{
			start = start->next;
			continue;
		}

		if (testFunc != NULL)
		{
			if ((*testFunc)(hw, start->anchorHRef))
			{
			    start->fg = hw->html.visitedAnchor_fg;
			    start->underline_number =
				hw->html.num_visitedAnchor_underlines;
			    start->dashed_underline =
				hw->html.dashed_visitedAnchor_lines;
			}
			else
			{
			    start->fg = hw->html.anchor_fg;
			    start->underline_number =
				hw->html.num_anchor_underlines;
			    start->dashed_underline =
				hw->html.dashed_anchor_lines;
			}
		}
		else
		{
			start->fg = hw->html.anchor_fg;
			start->underline_number =
				hw->html.num_anchor_underlines;
			start->dashed_underline =
				hw->html.dashed_anchor_lines;
		}

		/*
		 * Since the element may have changed, redraw it
		 */
		switch(start->type)
		{
			case E_TEXT:
				TextRefresh(hw, start,
				     0, (start->edata_len - 2));
				break;
			case E_IMAGE:
				ImageRefresh(hw, start);
				break;
			case E_BULLET:
				BulletRefresh(hw, start);
				break;
			case E_LINEFEED:
				LinefeedRefresh(hw, start);
				break;
		}

		start = start->next;
	}
}


void
#ifdef _NO_PROTO
HTMLClearSelection (w)
	Widget w;
#else
HTMLClearSelection(Widget w)
#endif
{
	LoseSelection (w, NULL);
}


/*
 * Set the current selection based on the ElementRefs passed in.
 * Both refs must be valid.
 */
void
#ifdef _NO_PROTO
HTMLSetSelection (w, start, end)
	Widget w;
	ElementRef *start;
	ElementRef *end;
#else
HTMLSetSelection(Widget w, ElementRef *start, ElementRef *end)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	int found;
	struct ele_rec *eptr;
	struct ele_rec *e_start;
	struct ele_rec *e_end;
	int start_pos, end_pos;
	Atom *atoms;
	int i, buffer;
	char *text;
	char *params[2];

	/*
	 * If the starting position is not valid, fail the selection
	 */
	if ((start->id > 0)&&(start->pos >= 0))
	{
		found = 0;
		eptr = hw->html.formatted_elements;

		while (eptr != NULL)
		{
			if (eptr->ele_id == start->id)
			{
				e_start = eptr;
				start_pos = start->pos;
				found = 1;
				break;
			}
			eptr = eptr->next;
		}
		if (!found)
		{
			return;
		}
	}

	/*
	 * If the ending position is not valid, fail the selection
	 */
	if ((end->id > 0)&&(end->pos >= 0))
	{
		found = 0;
		eptr = hw->html.formatted_elements;

		while (eptr != NULL)
		{
			if (eptr->ele_id == end->id)
			{
				e_end = eptr;
				end_pos = end->pos;
				found = 1;
				break;
			}
			eptr = eptr->next;
		}
		if (!found)
		{
			return;
		}
	}

	LoseSelection (w, NULL);

	/*
	 * We expect the ElementRefs came from HTMLSearchText, so we know
	 * that the end_pos is one past what we want to select.
	 */
	end_pos = end_pos - 1;

	/*
	 * Sanify the position data
	 */
	if ((start_pos > 0)&&(start_pos >= e_start->edata_len - 1))
	{
		start_pos = e_start->edata_len - 2;
	}
	if ((end_pos > 0)&&(end_pos >= e_end->edata_len - 1))
	{
		end_pos = e_end->edata_len - 2;
	}

	hw->html.select_start = e_start;
	hw->html.sel_start_pos = start_pos;
	hw->html.select_end = e_end;
	hw->html.sel_end_pos = end_pos;
	SetSelection(hw);
	hw->html.new_start = NULL;
	hw->html.new_end = NULL;
	hw->html.new_start_pos = 0;
	hw->html.new_end_pos = 0;

	/*
	 * Do all the gunk from the end of the ExtendEnd function
	 */
	params[0] = "PRIMARY";
	params[1] = "CUT_BUFFER0";
	atoms = (Atom *)malloc(2 * sizeof(Atom));
	if (atoms == NULL)
	{
		fprintf(stderr, "cannot allocate atom list\n");
		return;
	}
	XmuInternStrings(XtDisplay((Widget)hw), params, 2, atoms);
	hw->html.selection_time = CurrentTime;
	for (i=0; i< 2; i++)
	{
		switch (atoms[i])
		{
			case XA_CUT_BUFFER0: buffer = 0; break;
			case XA_CUT_BUFFER1: buffer = 1; break;
			case XA_CUT_BUFFER2: buffer = 2; break;
			case XA_CUT_BUFFER3: buffer = 3; break;
			case XA_CUT_BUFFER4: buffer = 4; break;
			case XA_CUT_BUFFER5: buffer = 5; break;
			case XA_CUT_BUFFER6: buffer = 6; break;
			case XA_CUT_BUFFER7: buffer = 7; break;
			default: buffer = -1; break;
		}
		if (buffer >= 0)
		{
			if (hw->html.fancy_selections == True)
			{
			    text = ParseTextToPrettyString(hw,
				hw->html.formatted_elements,
				hw->html.select_start,
				hw->html.select_end,
				hw->html.sel_start_pos,
				hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
			}
			else
			{
			    text = ParseTextToString(
				hw->html.formatted_elements,
				hw->html.select_start,
				hw->html.select_end,
				hw->html.sel_start_pos,
				hw->html.sel_end_pos,
				hw->html.font->max_bounds.width,
				hw->html.margin_width);
			}
			XStoreBuffer(XtDisplay((Widget)hw),
				text, strlen(text), buffer);
			free(text);
		}
		else
		{
			XtOwnSelection((Widget)hw, atoms[i], CurrentTime,
				       (XtConvertSelectionProc )ConvertSelection,
				       (XtLoseSelectionProc )LoseSelection,
				       (XtSelectionDoneProc )SelectionDone);
		}
	}
	free((char *)atoms);
}


/*
 * Convenience function to return the text of the HTML document as a single
 * white space separated string, with pointers to the various start and
 * end points of selections.
 * This function allocates memory for the returned string, that it is up
 * to the user to free.
 */
char *
#ifdef _NO_PROTO
HTMLGetTextAndSelection (w, startp, endp, insertp)
	Widget w;
	char **startp;
	char **endp;
	char **insertp;
#else
HTMLGetTextAndSelection(Widget w, char **startp, char **endp, char **insertp)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	int length;
	char *text;
	char *tptr;
	struct ele_rec *eptr;
	struct ele_rec *sel_start;
	struct ele_rec *sel_end;
	struct ele_rec *insert_start;
	int start_pos, end_pos, insert_pos;

	if (SwapElements(hw->html.select_start, hw->html.select_end,
		hw->html.sel_start_pos, hw->html.sel_end_pos))
	{
		sel_end = hw->html.select_start;
		end_pos = hw->html.sel_start_pos;
		sel_start = hw->html.select_end;
		start_pos = hw->html.sel_end_pos;
	}
	else
	{
		sel_start = hw->html.select_start;
		start_pos = hw->html.sel_start_pos;
		sel_end = hw->html.select_end;
		end_pos = hw->html.sel_end_pos;
	}

	insert_start = hw->html.new_start;
	insert_pos = hw->html.new_start_pos;
	*startp = NULL;
	*endp = NULL;
	*insertp = NULL;

	length = 0;

	eptr = hw->html.formatted_elements;
	while (eptr != NULL)
	{
		/*
		 * Skip the special internal text
		 */
		if (eptr->internal == True)
		{
			eptr = eptr->next;
			continue;
		}

		if (eptr->type == E_TEXT)
		{
			length = length + eptr->edata_len - 1;
		}
		else if (eptr->type == E_LINEFEED)
		{
			length = length + 1;
		}
		eptr = eptr->next;
	}

	text = (char *)malloc(length + 1);
	if (text == NULL)
	{
		fprintf(stderr, "No space for return string\n");
		return(NULL);
	}
	strcpy(text, "");

	tptr = text;

	eptr = hw->html.formatted_elements;
	while (eptr != NULL)
	{
		/*
		 * Skip the special internal text
		 */
		if (eptr->internal == True)
		{
			eptr = eptr->next;
			continue;
		}

		if (eptr->type == E_TEXT)
		{
			if (eptr == sel_start)
			{
				*startp = (char *)(tptr + start_pos);
			}

			if (eptr == sel_end)
			{
				*endp = (char *)(tptr + end_pos);
			}

			if (eptr == insert_start)
			{
				*insertp = (char *)(tptr + insert_pos);
			}

			strcat(text, (char *)eptr->edata);
			tptr = tptr + eptr->edata_len - 1;
		}
		else if (eptr->type == E_LINEFEED)
		{
			if (eptr == sel_start)
			{
				*startp = tptr;
			}

			if (eptr == sel_end)
			{
				*endp = tptr;
			}

			if (eptr == insert_start)
			{
				*insertp = tptr;
			}

			strcat(text, " ");
			tptr = tptr + 1;
		}
		eptr = eptr->next;
	}
	return(text);
}


/*
 * Convenience function to set the raw text into the widget.
 * Forces a reparse and a reformat.
 * If any pointer is passed in as NULL that text is unchanged,
 * if a pointer points to an empty string, that text is set to NULL;
 * Also pass an element ID to set the view area to that section of the new
 * text.  Finally pass an anchor NAME to set position of the new text
 * to that anchor.
 */
void
#ifdef _NO_PROTO
HTMLSetText (w, text, header_text, footer_text, element_id, target_anchor, ptr)
	Widget w;
	char *text;
	char *header_text;
	char *footer_text;
	int element_id;
	char *target_anchor;
	void *ptr;
#else
HTMLSetText(Widget w, char *text, char *header_text, char *footer_text, int element_id, char *target_anchor, void *ptr)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	WidgetInfo *wptr = (WidgetInfo *)ptr;
	struct ele_rec *start;
	struct ele_rec *eptr;
	int newy;

	if ((text == NULL)&&(header_text == NULL)&&(footer_text == NULL))
	{
		return;
	}

	/*
	 * Free up the old visited href list.
	 */
	FreeHRefs(hw->html.my_visited_hrefs);
	hw->html.my_visited_hrefs = NULL;

	/*
	 * Free up the old visited delayed images list.
	 */
	FreeDelayedImages(hw->html.my_delayed_images);
	hw->html.my_delayed_images = NULL;

	/*
	 * Hide any old widgets
	 */
	HideWidgets(hw);
	hw->html.widget_list = wptr;
	hw->html.form_list = NULL;

	if (text != NULL)
	{
		if (*text == '\0')
		{
			text = NULL;
		}
		hw->html.raw_text = text;

		/*
		 * Free any old colors and pixmaps
		 */
		FreeColors(XtDisplay(hw),DefaultColormapOfScreen(XtScreen(hw)));
		FreeImages(hw);

		/*
		 * Parse the raw text with the HTML parser
		 */
		hw->html.html_objects = HTMLParse(hw->html.html_objects,
			hw->html.raw_text);
		CallLinkCallbacks(hw);
	}
	if (header_text != NULL)
	{
		if (*header_text == '\0')
		{
			header_text = NULL;
		}
		hw->html.header_text = header_text;

		/*
		 * Parse the header text with the HTML parser
		 */
		hw->html.html_header_objects =
			HTMLParse(hw->html.html_header_objects,
			hw->html.header_text);
	}
	if (footer_text != NULL)
	{
		if (*footer_text == '\0')
		{
			footer_text = NULL;
		}
		hw->html.footer_text = footer_text;

		/*
		 * Parse the footer text with the HTML parser
		 */
		hw->html.html_footer_objects =
			HTMLParse(hw->html.html_footer_objects,
			hw->html.footer_text);
	}

	/*
	 * Reformat the new text
	 */
	hw->html.max_pre_width = DocumentWidth(hw, hw->html.html_objects);
	ReformatWindow(hw);

	/*
	 * If a target anchor is passed, override the element id
	 * with the id of that anchor.
	 */
	if (target_anchor != NULL)
	{
		int id;

		id = HTMLAnchorToId(w, target_anchor);
		if (id != 0)
		{
			element_id = id;
		}
	}

	/*
	 * Position text at id specified, or at top if no position
	 * specified.
	 * Find the element corrsponding to the id passed in.
	 */
	eptr = NULL;
	if (element_id != 0)
	{
		start = hw->html.formatted_elements;
		while (start != NULL)
		{
			if (start->ele_id == element_id)
			{
				eptr = start;
				break;
			}
			start = start->next;
		}
	}
	if (eptr == NULL)
	{
		newy = 0;
	}
	else
	{
		newy = eptr->y - 2;
	}
	if (newy < 0)
	{
		newy = 0;
	}
	if (newy > (hw->html.doc_height - (int)hw->html.view_height))
	{
		newy = hw->html.doc_height - (int)hw->html.view_height;
	}
	if (newy < 0)
	{
		newy = 0;
	}
	hw->html.scroll_x = 0;
	hw->html.scroll_y = newy;
#ifdef DEBUG
fprintf (stderr, "calling in HTMLSetText\n");
#endif
	ConfigScrollBars(hw);
	ScrollWidgets(hw);

	/*
	 * Display the new text
	 */
	ViewClearAndRefresh(hw);

	/*
	 * Clear any previous selection
	 */
	hw->html.select_start = NULL;
	hw->html.select_end = NULL;
	hw->html.sel_start_pos = 0;
	hw->html.sel_end_pos = 0;
	hw->html.new_start = NULL;
	hw->html.new_end = NULL;
	hw->html.new_start_pos = 0;
	hw->html.new_end_pos = 0;
	hw->html.active_anchor = NULL;

        hw->html.cached_tracked_ele = NULL;
}


/*
 * To use faster TOLOWER as set up in HTMLparse.c
 */
#ifdef NOT_ASCII
#define TOLOWER(x)      (tolower(x))
#else
extern char map_table[];
#define TOLOWER(x)      (map_table[x])
#endif /* NOT_ASCII */


/*
 * Convenience function to search the text of the HTML document as a single
 * white space separated string. Linefeeds are converted into spaces.
 *
 * Takes a pattern, pointers to the start and end blocks to store the
 * start and end of the match into.  Start is also used as the location to
 * start the search from for incremental searching.  If start is an invalid
 * position (id = 0).  Default start is the beginning of the document for
 * forward searching, and the end of the document for backwards searching.
 * The backward and caseless parameters I hope are self-explanatory.
 *
 * returns 1 on success
 *      (and the start and end positions of the match).
 * returns -1 otherwise (and start and end are unchanged).
 */
int
#ifdef _NO_PROTO
HTMLSearchText (w, pattern, m_start, m_end, backward, caseless)
	Widget w;
	char *pattern;
	ElementRef *m_start;
	ElementRef *m_end;
	int backward;
	int caseless;
#else
HTMLSearchText (Widget w, char *pattern, ElementRef *m_start, ElementRef *m_end,
		int backward, int caseless)
#endif
{
	HTMLWidget hw = (HTMLWidget)w;
	int found, equal;
	char *match;
	char *tptr;
	char *mptr;
	char cval;
	struct ele_rec *eptr;
	int s_pos;
	struct ele_rec *s_eptr;
	ElementRef s_ref, e_ref;
	ElementRef *start, *end;

	/*
	 * If bad parameters are passed, just fail the search
	 */
	if ((pattern == NULL)||(*pattern == '\0')||
		(m_start == NULL)||(m_end == NULL))
	{
		return(-1);
	}

	/*
	 * If we are caseless, make a lower case copy of the pattern to
	 * match to use in compares.
	 *
	 * remember to free this before returning
	 */
	if (caseless)
	{
		match = (char *)malloc(strlen(pattern) + 1);
		tptr = pattern;
		mptr = match;
		while (*tptr != '\0')
		{
			*mptr = (char)TOLOWER((int)*tptr);
			mptr++;
			tptr++;
		}
		*mptr = '\0';
	}
	else
	{
		match = pattern;
	}

	/*
	 * Slimy coding.  I later decided I didn't want to change start and
	 * end if the search failed.  Rather than changing all the code,
	 * I just copy it into locals here, and copy it out again if a match
	 * is found.
	 */
	start = &s_ref;
	end = &e_ref;
	start->id = m_start->id;
	start->pos = m_start->pos;
	end->id = m_end->id;
	end->pos = m_end->pos;

	/*
	 * Find the user specified start position.
	 */
	if (start->id > 0)
	{
		found = 0;
		eptr = hw->html.formatted_elements;

		while (eptr != NULL)
		{
			if (eptr->ele_id == start->id)
			{
				s_eptr = eptr;
				found = 1;
				break;
			}
			eptr = eptr->next;
		}
		/*
		 * Bad start position, fail them out.
		 */
		if (!found)
		{
			if (caseless)
			{
				free(match);
			}
			return(-1);
		}
		/*
		 * Sanify the start position
		 */
		s_pos = start->pos;
		if (s_pos >= s_eptr->edata_len - 1)
		{
			s_pos = s_eptr->edata_len - 2;
		}
		if (s_pos < 0)
		{
			s_pos = 0;
		}
	}
	else
	{
		/*
		 * Default search starts at end for backward, and
		 * beginning for forwards.
		 */
		if (backward)
		{
			s_eptr = hw->html.formatted_elements;
			while (s_eptr->next != NULL)
			{
				s_eptr = s_eptr->next;
			}
			s_pos = s_eptr->edata_len - 2;
		}
		else
		{
			s_eptr = hw->html.formatted_elements;
			s_pos = 0;
		}
	}

	if (backward)
	{
		char *mend;

		/*
		 * Save the end of match here for easy end to start searching
		 */
		mend = match;
		while (*mend != '\0')
		{
			mend++;
		}
		if (mend > match)
		{
			mend--;
		}
		found = 0;
		equal = 0;
		mptr = mend;

		if (s_eptr != NULL)
		{
			eptr = s_eptr;
		}
		else
		{
			eptr = hw->html.formatted_elements;
			while (eptr->next != NULL)
			{
				eptr = eptr->next;
			}
		}

		while (eptr != NULL)
		{
			/*
			 * Skip the special internal text
			 */
			if (eptr->internal == True)
			{
				eptr = eptr->prev;
				continue;
			}

			if (eptr->type == E_TEXT)
			{
			    tptr = (char *)(eptr->edata + eptr->edata_len - 2);
			    if (eptr == s_eptr)
			    {
				tptr = (char *)(eptr->edata + s_pos);
			    }
			    while (tptr >= eptr->edata)
			    {
				if (equal)
				{
					if (caseless)
					{
						cval =(char)TOLOWER((int)*tptr);
					}
					else
					{
						cval = *tptr;
					}
					while ((mptr >= match)&&
						(tptr >= eptr->edata)&&
						(cval == *mptr))
					{
						tptr--;
						mptr--;
					    if (tptr >= eptr->edata)
					    {
						if (caseless)
						{
						cval =(char)TOLOWER((int)*tptr);
						}
						else
						{
							cval = *tptr;
						}
					    }
					}
					if (mptr < match)
					{
						found = 1;
						start->id = eptr->ele_id;
						start->pos = (int)
						    (tptr - eptr->edata + 1);
						break;
					}
					else if (tptr < eptr->edata)
					{
						break;
					}
					else
					{
						equal = 0;
					}
				}
				else
				{
					mptr = mend;
					if (caseless)
					{
						cval =(char)TOLOWER((int)*tptr);
					}
					else
					{
						cval = *tptr;
					}
					while ((tptr >= eptr->edata)&&
						(cval != *mptr))
					{
						tptr--;
					    if (tptr >= eptr->edata)
					    {
						if (caseless)
						{
						cval =(char)TOLOWER((int)*tptr);
						}
						else
						{
							cval = *tptr;
						}
					    }
					}
					if ((tptr >= eptr->edata)&&
						(cval == *mptr))
					{
						equal = 1;
						end->id = eptr->ele_id;
						end->pos = (int)
						    (tptr - eptr->edata + 1);
					}
				}
			    }
			}
			/*
			 * Linefeeds match to single space characters.
			 */
			else if (eptr->type == E_LINEFEED)
			{
				if (equal)
				{
					if (*mptr == ' ')
					{
						mptr--;
						if (mptr < match)
						{
							found = 1;
							start->id =eptr->ele_id;
							start->pos = 0;
						}
					}
					else
					{
						equal = 0;
					}
				}
				else
				{
					mptr = mend;
					if (*mptr == ' ')
					{
						equal = 1;
						end->id = eptr->ele_id;
						end->pos = 0;
						mptr--;
						if (mptr < match)
						{
							found = 1;
							start->id =eptr->ele_id;
							start->pos = 0;
						}
					}
				}
			}
			if (found)
			{
				break;
			}
			eptr = eptr->prev;
		}
	}
	else /* forward */
	{
		found = 0;
		equal = 0;
		mptr = match;

		if (s_eptr != NULL)
		{
			eptr = s_eptr;
		}
		else
		{
			eptr = hw->html.formatted_elements;
		}

		while (eptr != NULL)
		{
			/*
			 * Skip the special internal text
			 */
			if (eptr->internal == True)
			{
				eptr = eptr->next;
				continue;
			}

			if (eptr->type == E_TEXT)
			{
			    tptr = eptr->edata;
			    if (eptr == s_eptr)
			    {
				tptr = (char *)(tptr + s_pos);
			    }
			    while (*tptr != '\0')
			    {
				if (equal)
				{
					if (caseless)
					{
						cval =(char)TOLOWER((int)*tptr);
					}
					else
					{
						cval = *tptr;
					}
					while ((*mptr != '\0')&&
						(cval == *mptr))
					{
						tptr++;
						mptr++;
						if (caseless)
						{
						cval =(char)TOLOWER((int)*tptr);
						}
						else
						{
							cval = *tptr;
						}
					}
					if (*mptr == '\0')
					{
						found = 1;
						end->id = eptr->ele_id;
						end->pos = (int)
							(tptr - eptr->edata);
						break;
					}
					else if (*tptr == '\0')
					{
						break;
					}
					else
					{
						equal = 0;
					}
				}
				else
				{
					mptr = match;
					if (caseless)
					{
						cval =(char)TOLOWER((int)*tptr);
					}
					else
					{
						cval = *tptr;
					}
					while ((*tptr != '\0')&&
						(cval != *mptr))
					{
						tptr++;
						if (caseless)
						{
						cval =(char)TOLOWER((int)*tptr);
						}
						else
						{
							cval = *tptr;
						}
					}
					if (cval == *mptr)
					{
						equal = 1;
						start->id = eptr->ele_id;
						start->pos = (int)
							(tptr - eptr->edata);
					}
				}
			    }
			}
			else if (eptr->type == E_LINEFEED)
			{
				if (equal)
				{
					if (*mptr == ' ')
					{
						mptr++;
						if (*mptr == '\0')
						{
							found = 1;
							end->id = eptr->ele_id;
							end->pos = 0;
						}
					}
					else
					{
						equal = 0;
					}
				}
				else
				{
					mptr = match;
					if (*mptr == ' ')
					{
						equal = 1;
						start->id = eptr->ele_id;
						start->pos = 0;
						mptr++;
						if (*mptr == '\0')
						{
							found = 1;
							end->id = eptr->ele_id;
							end->pos = 0;
						}
					}
				}
			}
			if (found)
			{
				break;
			}
			eptr = eptr->next;
		}
	}

	if (found)
	{
		m_start->id = start->id;
		m_start->pos = start->pos;
		m_end->id = end->id;
		m_end->pos = end->pos;
	}

	if (caseless)
	{
		free(match);
	}

	if (found)
	{
		return(1);
	}
	else
	{
		return(-1);
	}
}

