/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */
/* 
   layout.c --- UI routines called by the layout module.
   Created: Jamie Zawinski <jwz@netscape.com>, 23-Jun-94.
 */


#include "mozilla.h"
#include "xfe.h"
#include "selection.h"
#include "fonts.h"
#include "felocale.h"
#include "fe_proto.h"
#include "msgcom.h"
#include "mozjava.h"
#include "np.h"
#include "nppriv.h"
#include "layout.h"
#include "Xfe/Xfe.h"

#include <X11/keysym.h>

extern Widget applet_storage;

extern PRLogModuleInfo* NSJAVA;
#define warn	PR_LOG_WARN

#include <Xm/SashP.h> /* for grid edges */
#include <Xm/DrawP.h> /* #### for _XmDrawShadows() */

#include "il_icons.h"           /* Image icon enumeration. */

#include "layers.h"

#include <plevent.h>
#include <prtypes.h>
#include "libevent.h"

#include <libi18n.h>
#include "intl_csi.h"
/* for XP_GetString() */
#include <xpgetstr.h>

#ifndef NO_WEB_FONTS
#include "nf.h"
#include "Mnfrc.h"
#include "Mnfrf.h"
#include "Mnffbu.h"
#endif

#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif

#if defined(DEBUG_tao)
#define XDBG(x) x
#else
#define XDBG(x) 
#endif

extern int XFE_UNTITLED;
extern int XFE_COMPOSE;
extern int XFE_NO_SUBJECT;
extern int XFE_MAIL_TITLE_FMT, XFE_NEWS_TITLE_FMT, XFE_TITLE_FMT;
extern int XFE_EDITOR_TITLE_FMT;
extern int XFE_LAY_UNKNOWN_PARAMETER_TO_ACTIVATE_LINK_ACTION;
extern int XFE_LAY_TOO_MANY_ARGS_TO_ACTIVATE_LINK_ACTION;
extern int XFE_LAY_LOCAL_FILE_URL_UNTITLED;

extern MWContext * XFE_showBrowser(Widget toplevel, URL_Struct *url);
extern void fe_HTMLViewTooltipsEH(MWContext *context, CL_Layer *layer,
								  CL_Event *layer_event, int state);

static void fe_get_url_x_selection_cb(Widget w,XtPointer client_data,
									Atom * sel,Atom * type,XtPointer value, 
									unsigned long * len,int * format);

void XFE_ClearView (MWContext *context, int which);

void fe_ClearArea (MWContext *context, int x, int y, unsigned int w,
                   unsigned int h);
void fe_ClearAreaWithColor (MWContext *context, int x, int y, unsigned int w,
                            unsigned int h, Pixel color);

/* State for the highlighted item (this should eventually be done by
   layout I think).  This is kind of a kludge, but it only assumes that: 
   there is only one mouse; and that the events and translations are 
   being dispatched correctly... */
static LO_Element *last_armed_xref = 0;
static struct {
  MWContext* context;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent xevent;
#else
  fe_EventStruct fe_event;
#endif
} last_armed_xref_closure_for_disarm;
static Boolean last_armed_xref_highlighted_p = False;
MWContext *last_documented_xref_context = 0;
LO_Element *last_documented_xref = 0;
LO_AnchorData *last_documented_anchor_data = 0;

int xfeKeycodeToWhich(KeyCode keycode,
		      Modifiers modifiers)
{
  Modifiers modout;
  KeySym res;
  XtTranslateKeycode(fe_display,
		     keycode, modifiers,
		     &modout,&res);
  return res;
}

/* takes the state field from a {Button,Key}{Press,Release} event and
 *  maps it into JS event flags.
 */
int xfeToLayerModifiers(int state)
{
  return (  ((state & ShiftMask) ? EVENT_SHIFT_MASK : 0)
	  | ((state & ControlMask) ? EVENT_CONTROL_MASK : 0)
	  | ((state & Mod1Mask) ? EVENT_ALT_MASK : 0)
	  | ((state & (  Mod2Mask
		       | Mod3Mask
		       | Mod4Mask
		       | Mod5Mask
		       )) ? EVENT_META_MASK : 0)
	  );
}

/* This is in the set of function pointers, but there is no
   definition for it. */
MWContext*
XFE_CreateNewDocWindow(MWContext * calling_context,URL_Struct * URL)
{
	if (calling_context) 
	{
		Widget widget = CONTEXT_WIDGET (calling_context);
		
		if (widget)
		{
			Widget app_shell = XfeAncestorFindApplicationShell(widget);

			if (XfeIsAlive(app_shell))
			{
				return XFE_showBrowser(app_shell,URL);
			}
		}
	}/* if */

	return NULL;
}

/* Translate the string from ISO-8859/1 to something that the window
   system can use (for X, this is nearly a no-op.)
 */
char *
XFE_TranslateISOText (MWContext *context, int charset, char *ISO_Text)
{
  unsigned char *s;

  /* charsets such as Shift-JIS contain 0240's that are valid */
  if (INTL_CharSetType(charset) != SINGLEBYTE)
    return ISO_Text;

  /* When &nbsp; is encountered, display a normal space character instead.
     This is necessary because the MIT fonts are messed up, and have a
     zero-width character for nobreakspace, so we need to print it as a
     normal space instead. */
  if (ISO_Text)
    for (s = (unsigned char *) ISO_Text; *s; s++)
      if (*s == 0240) *s = ' ';

  return ISO_Text;
}

struct fe_gc_data
{
  unsigned long flags;
  XGCValues gcv;
  Region clip_region;
  GC gc;
};

/* The GC cache is shared among all windows, since it doesn't hog
   any scarce resources (like colormap entries.) */
static struct fe_gc_data fe_gc_cache [30] = { { 0, }, };
static int fe_gc_cache_fp;
static int fe_gc_cache_wrapped_p = 0;

/* Dispose of entries matching the given flags, compressing the GC cache */
void
fe_FlushGCCache (Widget widget, unsigned long flags)
{
  int i, new_fp;

  Display *dpy = XtDisplay (widget);
  int maxi = (fe_gc_cache_wrapped_p ? countof (fe_gc_cache) : fe_gc_cache_fp);
  new_fp = 0;
  for (i = 0; i < maxi; i++)
    {
      if (fe_gc_cache [i].flags & flags)
        {
          XFreeGC (dpy, fe_gc_cache [i].gc);
          if (fe_gc_cache [i].clip_region)
              FE_DestroyRegion(fe_gc_cache [i].clip_region);
          memset (&fe_gc_cache [i], 0,  sizeof (fe_gc_cache [i]));
        }
      else
        fe_gc_cache[new_fp++] = fe_gc_cache[i];
    }
  if (new_fp == countof (fe_gc_cache))
    {
      fe_gc_cache_wrapped_p = 1;
      fe_gc_cache_fp = 0;
    }
  else
    {
      fe_gc_cache_wrapped_p = 0;
      fe_gc_cache_fp = new_fp;
    }
}

GC
fe_GetGCfromDW(Display* dpy, Window win, unsigned long flags, XGCValues *gcv,
               Region clip_region)
{
  int i;
  for (i = 0;
       i < (fe_gc_cache_wrapped_p ? countof (fe_gc_cache) : fe_gc_cache_fp);
       i++)
      {
          if (flags == fe_gc_cache [i].flags &&
              !memcmp (gcv, &fe_gc_cache [i].gcv, sizeof (*gcv)))
              if (clip_region)
                  {
                      if (fe_gc_cache[i].clip_region &&
                          XEqualRegion(clip_region,
                                       fe_gc_cache[i].clip_region))
                          return fe_gc_cache [i].gc;
                  }
              else
                  {
                      if(!fe_gc_cache[i].clip_region)
                          return fe_gc_cache [i].gc;
                  }
      }

  {
    GC gc;
    int this_slot = fe_gc_cache_fp;
    int clear_p = fe_gc_cache_wrapped_p;

    fe_gc_cache_fp++;
    if (fe_gc_cache_fp >= countof (fe_gc_cache))
      {
	fe_gc_cache_fp = 0;
	fe_gc_cache_wrapped_p = 1;
      }

    if (clear_p)
      {
		  XFreeGC (dpy, fe_gc_cache [this_slot].gc);
		  if (fe_gc_cache [this_slot].clip_region)
			  FE_DestroyRegion(fe_gc_cache [this_slot].clip_region);
		  fe_gc_cache [this_slot].gc = NULL;
		  fe_gc_cache [this_slot].clip_region = NULL;
      }

    gc = XCreateGC (dpy, win, flags, gcv);

    fe_gc_cache [this_slot].flags = flags;
    fe_gc_cache [this_slot].gcv = *gcv;
    fe_gc_cache [this_slot].clip_region = NULL;
    if (clip_region) {
		fe_gc_cache [this_slot].clip_region = FE_CopyRegion(clip_region, NULL);

		if (fe_gc_cache [this_slot].clip_region) {
			XSetRegion(dpy, gc, fe_gc_cache [this_slot].clip_region);
		}
	}

    fe_gc_cache [this_slot].gc = gc;

    return gc;
  }
}

GC
fe_GetClipGC(Widget widget, unsigned long flags, XGCValues *gcv,
             Region clip_region)
{
    Display *dpy = XtDisplay (widget);
    Window win = XtWindow (widget);

    return fe_GetGCfromDW(dpy, win, flags, gcv, clip_region);
}

GC
fe_GetGC(Widget widget, unsigned long flags, XGCValues *gcv)
{
    Display *dpy = XtDisplay (widget);
    Window win = XtWindow (widget);

    return fe_GetGCfromDW(dpy, win, flags, gcv, NULL);
}

static GC
fe_get_text_gc (MWContext *context, LO_TextAttr *text, fe_Font *font_ret,
		Boolean *selected_p, Boolean blunk)
{
  unsigned long flags;
  XGCValues gcv;
  Widget widget = CONTEXT_WIDGET (context);
  fe_Font font = fe_LoadFontFromFace (context, text, &text->charset, text->font_face,
			      text->size, text->fontmask);
  Display *dpy = XtDisplay (widget);
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  Drawable drawable = fe_drawable->xdrawable;

  Pixel fg, bg;
  bg = fe_GetPixel(context,
                   text->bg.red, text->bg.green, text->bg.blue);
  fg = fe_GetPixel(context,
                   text->fg.red, text->fg.green, text->fg.blue);

/*  if (text->attrmask & LO_ATTR_ANCHOR)
    fg = CONTEXT_DATA (context)->xref_pixel;*/

  if (selected_p && *selected_p)
    {
      Pixel nfg = CONTEXT_DATA (context)->select_fg_pixel;
      Pixel nbg = CONTEXT_DATA (context)->select_bg_pixel;

      fg = nfg;
      bg = nbg;
    }

  if (blunk)
    fg = bg;

  if (! font) return NULL;

  memset (&gcv, ~0, sizeof (gcv));

  flags = 0;
  FE_SET_GC_FONT(text->charset, &gcv, font, &flags);
  gcv.foreground = fg;
  gcv.background = bg;
  flags |= (GCForeground | GCBackground);

  if (font_ret) *font_ret = font;

  return fe_GetGCfromDW (dpy, drawable,
                         flags, &gcv, fe_drawable->clip_region);
}

/* Given text and attributes, returns the size of those characters.
 */
int
XFE_GetTextInfo (MWContext *context,
		LO_TextStruct *text,
		LO_TextInfo *text_info)
{
  fe_Font font;
  char *str = (char *) text->text;
  int length = text->text_len;
  int remaining = length;

  font = fe_LoadFontFromFace (context, text->text_attr,
			      &text->text_attr->charset,
			      text->text_attr->font_face,
			      text->text_attr->size,
			      text->text_attr->fontmask);
  /* X is such a winner, it uses 16 bit quantities to represent all pixel
     widths.  This is really swell, because it means that if you've got
     a large font, you can't correctly compute the size of strings which
     are only a few thousand characters long.  So, when the string is more
     than N characters long, we divide up our calls to XTextExtents to
     keep the size down so that the library doesn't run out of fingers
     and toes.
   */
#define SUCKY_X_MAX_LENGTH 600

  text_info->ascent = 14;
  text_info->descent = 3;
  text_info->max_width = 0;
  text_info->lbearing = 0;
  text_info->rbearing = 0;
  if (! font) return 0;

  do
    {
      int L = (remaining > SUCKY_X_MAX_LENGTH ? SUCKY_X_MAX_LENGTH :
	       remaining);
      int ascent, descent;
      XCharStruct overall;
      FE_TEXT_EXTENTS (text->text_attr->charset, font, str, L,
		    &ascent, &descent, &overall);
      /* ascent and descent are per the font, not per this text. */
      text_info->ascent = ascent;
      text_info->descent = descent;

      text_info->max_width += overall.width;

#define FOO(x,y) if (y > x) x = y
      FOO (text_info->lbearing,   overall.lbearing);
      FOO (text_info->rbearing,   overall.rbearing);
      /*
       * If font metrics were set right, overall.descent should never exceed
       * descent, but since there are broken fonts in the world.
       */
      FOO (text_info->descent,   overall.descent);
#undef FOO

      str += L;
      remaining -= L;
    }
  while (remaining > 0);

  /* What is the return value expected to be?
     layout/layout.c doesn't seem to use it. */
  return 0;
}


/* Draws a rectangle with dropshadows on the drawing_area window.
   shadow_width is the border thickness (they go inside the rect).
   shadow_style is XmSHADOW_IN or XmSHADOW_OUT.
 */
void
fe_DrawSelectedShadows(MWContext *context, fe_Drawable *fe_drawable,
                       int x, int y, int width, int height,
                       int shadow_width, int shadow_style, Boolean selected)
{
  Widget widget = CONTEXT_WIDGET (context);
  Display *dpy = XtDisplay (widget);
  XGCValues gcv1, gcv2;
  GC gc1, gc2;
  unsigned long flags;
  Drawable drawable = fe_drawable->xdrawable;

  memset (&gcv1, ~0, sizeof (gcv1));
  memset (&gcv2, ~0, sizeof (gcv2));

#ifdef EDITOR
  if (selected)
  {
      flags = GCForeground;
      gcv1.foreground = CONTEXT_DATA(context)->select_bg_pixel;
      gcv2.foreground = CONTEXT_DATA(context)->select_bg_pixel;
      if (shadow_width < 1)
          shadow_width = 1;
  }
  else if (EDT_IS_EDITOR(context) && shadow_width < 1)
  {
      flags = GCForeground;
      gcv1.foreground = CONTEXT_DATA(context)->bg_pixel;
      gcv2.foreground = CONTEXT_DATA(context)->bg_pixel;
      shadow_width = 1;
  }
  else
#endif /* EDITOR */

  if (CONTEXT_DATA (context)->backdrop_pixmap ||
      CONTEXT_DATA (context)->bg_pixel !=
      CONTEXT_DATA (context)->default_bg_pixel)
    {
      static Pixmap gray50 = 0;
      if (! gray50)
	{
#	  define gray50_width  8
#	  define gray50_height 2
	  static char gray50_bits[] = { 0x55, 0xAA };
	  gray50 =
	    XCreateBitmapFromData (XtDisplay (widget),
				   RootWindowOfScreen (XtScreen (widget)),
				   gray50_bits, gray50_width, gray50_height);
	}
      flags = (GCForeground | GCStipple | GCFillStyle |
	       GCTileStipXOrigin | GCTileStipYOrigin);

#ifdef EDITOR
      if (EDT_IS_EDITOR(context))
      {
          flags |= GCBackground;
          gcv1.background = CONTEXT_DATA(context)->bg_pixel;
          gcv1.fill_style = FillOpaqueStippled;
      }
      else
#endif /* EDITOR */
      gcv1.fill_style = FillStippled;
      gcv1.stipple = gray50;
      gcv1.ts_x_origin = -CONTEXT_DATA (context)->document_x;
      gcv1.ts_y_origin = -CONTEXT_DATA (context)->document_y;
      gcv1.foreground = fe_GetPixel (context, 0xFF, 0xFF, 0xFF);
      gcv2 = gcv1;
      gcv2.foreground = fe_GetPixel (context, 0x00, 0x00, 0x00);
    }
  else
    {
      flags = GCForeground;
      gcv1.foreground = CONTEXT_DATA (context)->top_shadow_pixel;
      gcv2.foreground = CONTEXT_DATA (context)->bottom_shadow_pixel;
    }

#ifdef EDITOR_OLD
  /* We don't want this for tables any more; need to test on
   * other elements which use this code to make sure nothing else
   * depends on this behavior.
   */
  if (selected) {
      gcv1.background = CONTEXT_DATA(context)->select_bg_pixel;
      gcv1.line_style = LineDoubleDash;

      gcv2.background = CONTEXT_DATA(context)->select_bg_pixel;
      gcv2.line_style = LineDoubleDash;

      flags |= GCLineStyle|GCBackground;
  }
#endif /*EDITOR*/

  gc1 = fe_GetGCfromDW (fe_display, drawable, flags, &gcv1, fe_drawable->clip_region);
  gc2 = fe_GetGCfromDW (fe_display, drawable, flags, &gcv2, fe_drawable->clip_region);

  _XmDrawShadows (dpy, drawable, gc1, gc2, x, y, width, height,
		  shadow_width, shadow_style);
}

void
fe_DrawShadows (MWContext *context, fe_Drawable *fe_drawable,
		int x, int y, int width, int height,
		int shadow_width, int shadow_style)
{
    fe_DrawSelectedShadows(context, fe_drawable,
                           x, y, width, height,
                           shadow_width, shadow_style, FALSE);

}


/* Put some text on the screen at the given location with the given
   attributes.  (What is iLocation?  It is ignored.)
 */
static void
fe_display_text (MWContext *context, int iLocation, LO_TextStruct *text,
		 int32 start, int32 end, Boolean blunk)
{
  Widget shell = CONTEXT_WIDGET (context);
  Widget drawing_area = CONTEXT_DATA (context)->drawing_area;
  Display *dpy = XtDisplay (shell);
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  Drawable drawable = fe_drawable->xdrawable;
  fe_Font font;
  int ascent, descent;
  GC gc;
  long x, y;
  long x_offset = 0;
  Boolean selected_p = False;


  if ((text->ele_attrmask & LO_ELE_SELECTED) &&
	(start >= text->sel_start) && (end-1 <= text->sel_end))
    selected_p = True;

  gc = fe_get_text_gc (context, text->text_attr, &font, &selected_p, blunk);
  if (!gc)
  {
	return;
  }

  FE_FONT_EXTENTS(text->text_attr->charset, font, &ascent, &descent);

  x = (text->x + text->x_offset
	    - CONTEXT_DATA (context)->document_x);
  y = (text->y + text->y_offset + ascent
	    - CONTEXT_DATA (context)->document_y);
  x += fe_drawable->x_origin;
  y += fe_drawable->y_origin;

  if (text->text_len == 0)
    return;

  if (! XtIsRealized (drawing_area))
    return;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > (CONTEXT_DATA (context)->scrolled_height +
	text->y_offset + ascent)) ||
      (x + text->width < 0) ||
      (y + text->line_height < 0))
    return;

  if (start < 0)
	start = 0;
  if (end > text->text_len)
	end = text->text_len;

  if (end - start > SUCKY_X_MAX_LENGTH)
    /* that's a fine way to make X blow up *real* good! */
    end = start + SUCKY_X_MAX_LENGTH;

  /* #### Oh, this doesn't even work, because Bina is
     passing us massively negative (> 16 bit) starting pixel positions.
   */

  if (start > 0)
    {
      XCharStruct overall;
      int ascent, descent;
      if (! font) abort ();
      FE_TEXT_EXTENTS (text->text_attr->charset, font, (char *) text->text,
			start, &ascent, &descent, &overall);
      x_offset = overall.width;
      x += x_offset;
    }


  if (blunk)
    ;	/* No text to draw. */
  else if (!selected_p && text->text_attr->no_background)
    FE_DRAW_STRING (text->text_attr->charset, dpy, drawable, font, gc, x, y,
	((char *) text->text) + start, end - start);
  else
  {
	GC gc2;
	gc2 = fe_get_text_gc (context, text->text_attr, &font, &selected_p, TRUE);
	if (!gc2)
		gc2 = gc;

    FE_DRAW_IMAGE_STRING (text->text_attr->charset, dpy, drawable, font, gc, gc2,
                          x, y, ((char *) text->text)+start, end - start);
  }

  /* Anchor text is no longer underlined by the front end.
   * We deliberately do not test for the LO_ATTR_ANCHOR bit in the attr mask.
   */
  if (text->text_attr->attrmask & 
      (LO_ATTR_UNDERLINE | LO_ATTR_SPELL | LO_ATTR_STRIKEOUT)
#if 0
      || (text->height < text->line_height)
#endif
    )
    {
      int upos;
      unsigned int uthick;
      int ul_width;

      if (start == 0 && end == text->text_len)
	{
	  ul_width = text->width;
	}
      else
	{
	  XCharStruct overall;
	  int ascent, descent;
	  if (! font) abort ();
	  FE_TEXT_EXTENTS (text->text_attr->charset, font,
		(char *) text->text+start, end-start, &ascent, &descent,
		&overall);
	  ul_width = overall.width;
	}

#if 0
      if (text->height < text->line_height)
	{
	  /* If the text is shorter than the line, then XDrawImageString()
	     won't fill in the whole background - so we need to do that by
	     hand. */
	  GC gc;
	  XGCValues gcv;
	  memset (&gcv, ~0, sizeof (gcv));
	  gcv.foreground = (text->selected
			    ? CONTEXT_DATA (context)->highlight_bg_pixel
			    : fe_GetPixel (context,
					   text->text_attr->bg.red,
					   text->text_attr->bg.green,
					   text->text_attr->bg.blue));
	}
#endif

      if (text->text_attr->attrmask & LO_ATTR_UNDERLINE)
	{
	  int lineDescent;
	  upos = fe_GetUnderlinePosition(text->text_attr->charset);
	  lineDescent = text->line_height - text->y_offset - ascent - 1;
	  if (upos > lineDescent)
	    upos = lineDescent;
	  XDrawLine (dpy, drawable, gc, x, y + upos, x + ul_width, y + upos);
	}

	  if (text->text_attr->attrmask & LO_ATTR_SPELL)
    {
		int lineDescent;
		GC gc2;
		XGCValues gcv2;

		memset (&gcv2, ~0, sizeof (gcv2));
		gcv2.foreground = fe_GetPixel(context, 0xFF, 0x00, 0x00);

		gc2 = fe_GetGC (CONTEXT_WIDGET (context), GCForeground, &gcv2);

		upos = fe_GetUnderlinePosition(text->text_attr->charset);
		lineDescent = text->line_height - text->y_offset - ascent - 1;
		if (upos > lineDescent)
			upos = lineDescent;
		XDrawLine (dpy, drawable, gc2, x, y + upos, x + ul_width, y + upos);
	}

      if (text->text_attr->attrmask & LO_ATTR_STRIKEOUT)
	{
	  upos = fe_GetStrikePosition(text->text_attr->charset, font);
	  uthick = (ascent / 8);
	  if (uthick <= 1)
	    XDrawLine (dpy, drawable, gc, x, y + upos, x + ul_width, y + upos);
	  else
	    XFillRectangle (dpy, drawable, gc, x, y + upos, ul_width, uthick);
	}
    }
}

void
XFE_DisplayText (MWContext *context, int iLocation, LO_TextStruct *text,
	XP_Bool need_bg)
{
  fe_display_text (context, iLocation, text, 0, text->text_len, False);
}

void
XFE_DisplaySubtext (MWContext *context, int iLocation,
		   LO_TextStruct *text, int32 start_pos, int32 end_pos,
		   XP_Bool need_bg)
{
  fe_display_text (context, iLocation, text, start_pos, end_pos + 1, False);
}

#ifdef EDITOR

/*
 *    End of line indicator.
 */
typedef struct fe_bitmap_info
{
    unsigned char* bits;
    Dimension      width;
    Dimension      height;
    Pixmap         pixmap;
} fe_bitmap_info;

#define line_feed_width 7
#define line_feed_height 10
static unsigned char line_feed_bits[] = { /* lifted from Lucid Emacs */
    0x00, 0xbc, 0xfc, 0xe0, 0xe0, 0x72, 0x3e, 0x1e, 0x1e, 0x3e};
#define page_mark_width 8
#define page_mark_height 15
static char page_mark_bits[] = { /* from RobinS */
 0xfe,0x4f,0x4f,0x4f,0x4f,0x4e,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48,0x48};

static fe_bitmap_info fe_line_feed_bitmap =
{ line_feed_bits, line_feed_width, line_feed_height };
static fe_bitmap_info fe_page_mark_bitmap =
{ page_mark_bits, page_mark_width, page_mark_height };

static Display* fe_line_feed_display;

static Pixmap
fe_make_line_feed_pixmap(Display* display, int type,
			 Dimension* r_width, Dimension* r_height)
{
    fe_bitmap_info* info;

    if (type == LO_LINEFEED_BREAK_PARAGRAPH)
	info = &fe_page_mark_bitmap;
    else
	info = &fe_line_feed_bitmap;

    if (fe_line_feed_display != display && fe_line_feed_display != 0) {
	if (info->pixmap != 0)
	    XFreePixmap(fe_line_feed_display, info->pixmap);
    }

    if (info->pixmap == 0) {

	info->pixmap = XCreateBitmapFromData(display,
					     DefaultRootWindow(display),
					     info->bits,
					     info->width,
					     info->height);
 	
	fe_line_feed_display = display;
    }

    *r_width = info->width;
    *r_height = info->height;
    
    return info->pixmap;
}

#endif /*EDITOR*/

/* Display a glyph representing a linefeed at the given location with the
   given attributes.  This looks just like a " " character.  (What is
   iLocation?  It is ignored.)
 */
void
XFE_DisplayLineFeed (MWContext *context,
		    int iLocation, LO_LinefeedStruct *line_feed, XP_Bool need_bg)
{
  GC gc;
  XGCValues gcv;
  unsigned long flags;
  LO_TextAttr *text = line_feed->text_attr;

#ifdef EDITOR
  if (EDT_IS_EDITOR(context)
      &&
      EDT_DISPLAY_PARAGRAPH_MARKS(context)
      &&
      (line_feed->break_type != LO_LINEFEED_BREAK_SOFT)
      &&
      (!line_feed->prev || line_feed->prev->lo_any.edit_offset >= 0)) {

      LO_Color* fg_color;
      Display*  display = XtDisplay(CONTEXT_WIDGET(context));
      Window    window = XtWindow(CONTEXT_DATA(context)->drawing_area);
      Position  target_x;
      Position  target_y;
      Dimension width;
      Dimension height;
      Pixmap    bitmap;

      bitmap = fe_make_line_feed_pixmap(display,
					line_feed->break_type,
					&width,
					&height);
      
      target_x = line_feed->x + line_feed->x_offset
	         - CONTEXT_DATA(context)->document_x;

      target_y = line_feed->y + line_feed->y_offset
		 - CONTEXT_DATA(context)->document_y;

      memset (&gcv, ~0, sizeof (gcv));

      if ((line_feed->ele_attrmask & LO_ELE_SELECTED) != 0)
	  fg_color = &text->bg; /* layout delivers inverted colors */
      else
	  fg_color = &text->fg;

      gcv.foreground = fe_GetPixel(context,
				   fg_color->red,
				   fg_color->green,
				   fg_color->blue);
      gcv.graphics_exposures = False;
      gcv.clip_mask = bitmap;
      gcv.clip_x_origin = target_x;
      gcv.clip_y_origin = target_y;

      flags = GCClipMask|GCForeground|GCClipXOrigin| \
	      GCClipYOrigin|GCGraphicsExposures;

      gc = fe_GetGC(CONTEXT_DATA(context)->drawing_area, flags, &gcv);

      if (height > line_feed->height)
	  height = line_feed->height;

      XCopyPlane(display, bitmap, window,
		 gc,
		 0, 0, width, height,
		 target_x, target_y,
		 1L);
  }
#endif /*EDITOR*/
}

/* Display a horizontal line at the given location.
 */
void
XFE_DisplayHR (MWContext *context, int iLocation, LO_HorizRuleStruct *hr)
{
  int shadow_width = 1;			/* #### customizable? */
  int shadow_style = XmSHADOW_IN;	/* #### customizable? */
  int thickness = hr->thickness;
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  Drawable drawable = fe_drawable->xdrawable;
  long x = hr->x + hr->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = hr->y + hr->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;
  int w = hr->width;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + hr->width < 0) ||
      (y + hr->line_height < 0))
    return;

  thickness -= (shadow_width * 2);
  if (thickness < 0) thickness = 0;

#ifdef EDITOR
  /*
   *    Don't draw the editor's end-of-document hrule unless we're
   *    displaying paragraph marks.
   */
  if (hr->edit_offset < 0 && !EDT_DISPLAY_PARAGRAPH_MARKS(context)) {
      return;
  }
#endif /* EDITOR */

  if (hr->ele_attrmask & LO_ELE_SHADED)
    {
#ifdef EDITOR
	fe_DrawSelectedShadows(context, fe_drawable,
			       x, y, w, thickness + (shadow_width * 2),
			       shadow_width, shadow_style,
			       ((hr->ele_attrmask & LO_ELE_SELECTED) != 0));
#else
	fe_DrawShadows (context, fe_drawable, x, y, w,
                        thickness + (shadow_width * 2),
                        shadow_width, shadow_style);
#endif /* EDITOR */
    }
  else
    {
      Display *dpy = XtDisplay (CONTEXT_DATA (context)->drawing_area);
      GC gc;
      XGCValues gcv;

      memset (&gcv, ~0, sizeof(gcv));
      gcv.foreground = CONTEXT_DATA(context)->fg_pixel;
      gc = fe_GetClipGC(CONTEXT_WIDGET (context), GCForeground, &gcv,
                        fe_drawable->clip_region);
      XFillRectangle(dpy, drawable, gc, x, y, w, hr->height);
#ifdef EDITOR
      if ((hr->ele_attrmask & LO_ELE_SELECTED) != 0) {
	  gcv.background = CONTEXT_DATA(context)->select_bg_pixel;
	  gcv.line_width = 1;
	  gcv.line_style = LineDoubleDash;
	  gc = fe_GetGC(CONTEXT_WIDGET(context),
			GCForeground|GCBackground|GCLineWidth|GCLineStyle,
			&gcv);
	  XDrawRectangle(dpy, drawable, gc, x, y, w-1, hr->height-1);
      }
#endif /* EDITOR */
    }
}

void
XFE_DisplayBullet (MWContext *context, int iLocation, LO_BulletStruct *bullet)
{
  int w,h;
  Boolean hollow_p;
  GC gc;
  Drawable drawable;

  Widget widget = CONTEXT_WIDGET (context);
  Display *dpy = XtDisplay (widget);

  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  long x = bullet->x + bullet->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = bullet->y + bullet->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;
  drawable = fe_drawable->xdrawable;
  w = bullet->width;
  h = bullet->height;
  hollow_p = (bullet->bullet_type != BULLET_BASIC);

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + w < 0) ||
      (y + h < 0))
    return;

  gc = fe_get_text_gc (context, bullet->text_attr, 0, 0, False);
  if (!gc)
  {
	return;
  }
  switch (bullet->bullet_type)
    {
    case BULLET_BASIC:
    case BULLET_ROUND:
      /* Subtract 1 to compensate for the behavior of XDrawArc(). */
      w -= 1;
      h -= 1;
      /* Now round up to an even number so that the circles look nice. */
      if (! (w & 1)) w++;
      if (! (h & 1)) h++;
      if (hollow_p)
	XDrawArc (dpy, drawable, gc, x, y, w, h, 0, 360*64);
      else
	XFillArc (dpy, drawable, gc, x, y, w, h, 0, 360*64);
      break;
    case BULLET_SQUARE:
      if (hollow_p)
	XDrawRectangle (dpy, drawable, gc, x, y, w, h);
      else
	XFillRectangle (dpy, drawable, gc, x, y, w, h);
      break;
    case BULLET_MQUOTE:
		/*
		 * WARNING... [ try drawing a 2 pixel wide filled rectangle ]
		 *
		 *
		 */
#ifdef DOH_WHICH_ONE
        XDrawLine (dpy, drawable, gc, x, y, x, (y + h));
#else
		w = 2;
		XFillRectangle (dpy, drawable, gc, x, y, w, h);
#endif
		break;

	case BULLET_NONE:
		/* Do nothing. */
		break;

    default:
		XP_ASSERT(0);
    }
}

void
XFE_DisplaySubDoc (MWContext *context, int loc, LO_SubDocStruct *sd)
{
  int shadow_style = XmSHADOW_IN;	/* #### customizable? */
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  /*Drawable drawable = fe_drawable->xdrawable;*/
  long x = sd->x + sd->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = sd->y + sd->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + sd->width < 0) ||
      (y + sd->line_height< 0))
    return;

  fe_DrawShadows (context, fe_drawable, x, y, sd->width, sd->height,
		  sd->border_width, shadow_style);
}

void
XFE_DisplayCell (MWContext *context, int loc, LO_CellStruct *cell)
{
  int shadow_style = XmSHADOW_IN;	/* #### customizable? */
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  long x = cell->x + cell->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = cell->y + cell->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;
  int border_width = cell->border_width;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + cell->width < 0) ||
      (y + cell->line_height< 0))
    return;

#ifdef EDITOR
  if (EDT_IS_EDITOR(context))
  {
      Boolean selected = ((cell->ele_attrmask & LO_ELE_SELECTED) != 0);
      fe_DrawSelectedShadows (context, fe_drawable,
                              x, y, cell->width, cell->height,
                              border_width, shadow_style, selected);
  }
  else
#endif /* EDITOR */
  fe_DrawShadows (context, fe_drawable, x, y, cell->width, cell->height,
                  cell->border_width, shadow_style);
}

typedef struct
{
  Dimension left;
  Dimension top;
  Dimension right;
  Dimension bottom;
} FE_BorderWidths;

static void
fe_DisplaySolidBorder(MWContext *context, LO_TableStruct *ts,
					  XRectangle *rect, FE_BorderWidths *widths)
{
  XGCValues gcv;
  unsigned long gc_flags;
  GC gc;
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;

  /* if they're all the same width, just vary the line thickness and use XDrawRectangle */
  if (widths->left == widths->top && widths->left == widths->right && widths->left == widths->bottom)
	{
	  gc_flags = GCForeground | GCLineWidth;

	  gcv.foreground = fe_GetPixel(context, 
								   ts->border_color.red,
								   ts->border_color.green,
								   ts->border_color.blue);

	  gcv.line_width = widths->left; /* doesn't really matter which one we choose. */

          if (ts->border_style == BORDER_DASHED
              || ts->border_style == BORDER_DOTTED)
          {
              gc_flags |= GCLineStyle;
              gcv.line_style = LineOnOffDash;
          }

	  gc = fe_GetGCfromDW(fe_display, fe_drawable->xdrawable, gc_flags, &gcv, fe_drawable->clip_region);

	  rect->x += widths->left / 2;
	  rect->y += widths->left / 2;
	  rect->width -= widths->left;
	  rect->height -= widths->left;

	  XDrawRectangle(fe_display,
					 XtWindow(CONTEXT_DATA(context)->drawing_area),
					 gc,
					 rect->x, rect->y, rect->width, rect->height);
          /* set the line attributes back to solid in case we changed them */
          if (ts->border_style == BORDER_DASHED
              || ts->border_style == BORDER_DOTTED)
          {
              gc_flags = GCLineStyle;
              gcv.line_style = LineSolid;     /* should get previous value? */
              XChangeGC(fe_display, gc, gc_flags, &gcv);
          }
	}
  else
	{
	  /* since they are (possibly) all different, we do each border will XFillRectangle */
	  gc_flags = GCForeground;

	  gcv.foreground = fe_GetPixel(context,
								   ts->border_color.red,
								   ts->border_color.green,
								   ts->border_color.blue);


	  gc = fe_GetGCfromDW(fe_display, fe_drawable->xdrawable, gc_flags, &gcv, fe_drawable->clip_region);

	  if (widths->left > 0)
		XFillRectangle(fe_display,
					   XtWindow(CONTEXT_DATA(context)->drawing_area),
					   gc,
					   rect->x, rect->y, widths->left, rect->height);

	  if (widths->top > 0)
		XFillRectangle(fe_display,
					   XtWindow(CONTEXT_DATA(context)->drawing_area),
					   gc,
					   rect->x, rect->y, rect->width, widths->top);

	  if (widths->right > 0)
		XFillRectangle(fe_display,
					   XtWindow(CONTEXT_DATA(context)->drawing_area),
					   gc,
					   rect->x + rect->width - widths->right, rect->y, widths->right, rect->height);

	  if (widths->bottom > 0)
		XFillRectangle(fe_display,
					   XtWindow(CONTEXT_DATA(context)->drawing_area),
					   gc,
					   rect->x, rect->y + rect->height - widths->bottom, rect->width, widths->bottom);
	}
}

static void
fe_DisplayDoubleBorder(MWContext *context, LO_TableStruct *ts,
					   XRectangle *rect, FE_BorderWidths *widths)
{
  FE_BorderWidths stroke_widths;

  stroke_widths.left = (widths->left + 1) / 3;
  stroke_widths.top = (widths->top + 1) / 3;
  stroke_widths.right = (widths->right + 1) / 3;
  stroke_widths.bottom = (widths->bottom + 1) / 3;

  /* draw the outer lines */
  fe_DisplaySolidBorder(context, ts, rect, &stroke_widths);

  /* adjust the rectangle to be the inner one */
  rect->x += (widths->left - stroke_widths.left);
  rect->y += (widths->top - stroke_widths.top);
  rect->width -= (widths->right - stroke_widths.right) + (widths->left - stroke_widths.left);
  rect->height -= (widths->bottom - stroke_widths.bottom) + (widths->top - stroke_widths.top);

  /* draw the inner lines */
  fe_DisplaySolidBorder(context, ts, rect, &stroke_widths);
}

/* warning.  much of this gc related code is duplicated from the fe_DrawSelectedShadows function above */
static void
fe_Display3DBorder(MWContext *context, LO_TableStruct *ts,
				   XRectangle *rect, FE_BorderWidths *widths,
				   Pixel top_shadow, Pixel bottom_shadow)
{
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  XGCValues gcv1, gcv2;
  unsigned long gc_flags = GCForeground;
  GC gc1, gc2;

  memset (&gcv1, ~0, sizeof (gcv1));
  memset (&gcv2, ~0, sizeof (gcv2));

  if (CONTEXT_DATA (context)->backdrop_pixmap ||
      CONTEXT_DATA (context)->bg_pixel !=
      CONTEXT_DATA (context)->default_bg_pixel)
    {
      static Pixmap gray50 = 0;
      if (! gray50)
		{
#	  define gray50_width  8
#	  define gray50_height 2
		  static char gray50_bits[] = { 0x55, 0xAA };
		  gray50 =
			XCreateBitmapFromData (fe_display,
								   RootWindowOfScreen (XtScreen (CONTEXT_WIDGET(context))),
								   gray50_bits, gray50_width, gray50_height);
		}
      gc_flags = (GCForeground | GCStipple | GCFillStyle |
				  GCTileStipXOrigin | GCTileStipYOrigin);
	  
      gcv1.fill_style = FillStippled;
      gcv1.stipple = gray50;
      gcv1.ts_x_origin = -CONTEXT_DATA (context)->document_x;
      gcv1.ts_y_origin = -CONTEXT_DATA (context)->document_y;
      gcv1.foreground = fe_GetPixel (context, 0xFF, 0xFF, 0xFF);
      gcv2 = gcv1;
      gcv2.foreground = fe_GetPixel (context, 0x00, 0x00, 0x00);
    }
  else
    {
      gc_flags = GCForeground;
	  gcv1.foreground = top_shadow;
	  gcv2.foreground = bottom_shadow;
    }
  
  gc1 = fe_GetGCfromDW (fe_display, fe_drawable->xdrawable, gc_flags, &gcv1, fe_drawable->clip_region);
  gc2 = fe_GetGCfromDW (fe_display, fe_drawable->xdrawable, gc_flags, &gcv2, fe_drawable->clip_region);
  
  if (widths->left == widths->top && widths->left == widths->right && widths->left == widths->bottom)
	{
	  _XmDrawShadows(fe_display,
					 fe_drawable->xdrawable,
					 gc1, gc2, rect->x, rect->y, rect->width, rect->height,
					 widths->left, XmSHADOW_OUT);
	}
  else
	{
	  XPoint points[6];

	  points[0].x = rect->x + widths->left;
	  points[0].y = rect->y + widths->top;
	  points[1].x = rect->x + widths->left;
	  points[1].y = rect->y + rect->height - widths->bottom;
	  points[2].x = rect->x;
	  points[2].y = rect->y + rect->height;
	  points[3].x = rect->x;
	  points[3].y = rect->y;
	  points[4].x = rect->x + rect->width;
	  points[4].y = rect->y;
	  points[5].x = rect->x + rect->width - widths->right;
	  points[5].y = rect->y + widths->top;
	  
	  XFillPolygon(fe_display,
				   fe_drawable->xdrawable,
				   gc1,
				   points, 6, 
				   Nonconvex,
				   CoordModeOrigin);

	  points[0].x = rect->x + rect->width - widths->right;
	  points[0].y = rect->y + rect->height - widths->bottom;
	  points[3].x = rect->x + rect->width;
	  points[3].y = rect->y + rect->height;

	  XFillPolygon(fe_display,
				   fe_drawable->xdrawable,
				   gc2,
				   points, 6, 
				   Nonconvex,
				   CoordModeOrigin);
	}
}

static void
fe_DisplayTableBorder(MWContext *context, LO_TableStruct *ts,
					  XRectangle *rect, FE_BorderWidths *widths)
{
  Pixel table_color, top_color, bottom_color;
  XRectangle inset_rect;
  
  if (ts->border_style == BORDER_GROOVE
	  || ts->border_style == BORDER_RIDGE
	  || ts->border_style == BORDER_INSET
	  || ts->border_style == BORDER_OUTSET)
	{
	  table_color = fe_GetPixel(context,
								ts->border_color.red,
								ts->border_color.green,
								ts->border_color.blue);
	  
	  XmGetColors(XtScreen(CONTEXT_WIDGET(context)),
				  fe_cmap(context),
				  table_color,
				  NULL,
				  &top_color, &bottom_color,
				  NULL);
	}
	  
  switch (ts->border_style)
	{
	case BORDER_NONE:
	  break;

	case BORDER_DOTTED:
	case BORDER_DASHED:
	case BORDER_SOLID:
	  fe_DisplaySolidBorder(context, ts, rect, widths);
	  break;

	case BORDER_DOUBLE:
	  fe_DisplayDoubleBorder(context, ts, rect, widths);
	  break;

	case BORDER_GROOVE:
	  widths->left /= 2;
	  widths->top /= 2;
	  widths->right /= 2;
	  widths->bottom /= 2;
	  fe_Display3DBorder(context, ts, rect, widths, bottom_color, top_color);
	  inset_rect.x = rect->x + widths->left;
	  inset_rect.y = rect->y + widths->top;
	  inset_rect.width = rect->width - widths->left - widths->right;
	  inset_rect.height = rect->height - widths->top - widths->bottom;
	  fe_Display3DBorder(context, ts, &inset_rect, widths, top_color, bottom_color);
	  break;

	case BORDER_RIDGE:
	  widths->left /= 2;
	  widths->top /= 2;
	  widths->right /= 2;
	  widths->bottom /= 2;
	  fe_Display3DBorder(context, ts, rect, widths, top_color, bottom_color);
	  inset_rect.x = rect->x + widths->left;
	  inset_rect.y = rect->y + widths->top;
	  inset_rect.width = rect->width - widths->left - widths->right;
	  inset_rect.height = rect->height - widths->top - widths->bottom;
	  fe_Display3DBorder(context, ts, &inset_rect, widths, bottom_color, top_color);
	  break;

	case BORDER_INSET:
	  fe_Display3DBorder(context, ts, rect, widths, bottom_color, top_color);
	  break;

	case BORDER_OUTSET:
	  fe_Display3DBorder(context, ts, rect, widths, top_color, bottom_color);
	  break;

	default:
	  XP_ASSERT(0);
	  break;
	}
}

#define ED_SELECTION_BORDER 3

void
XFE_DisplayTable (MWContext *context, int loc, LO_TableStruct *ts)
{
  XP_Bool hasBorder;
  int32 savedBorderStyle;
  LO_Color savedBorderColor;
  XRectangle table_rect;
  FE_BorderWidths widths;
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  long x = ts->x + ts->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = ts->y + ts->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + ts->width < 0) ||
      (y + ts->line_height< 0))
    return;

  hasBorder = (ts->border_top_width > 0 || ts->border_right_width > 0
                || ts->border_bottom_width > 0 || ts->border_left_width > 0);

  /* Set the border rect if we have a border or if the table is selected */
  table_rect.x = x;
  table_rect.y = y;
  table_rect.width = ts->width;
  table_rect.height = ts->height;

#ifdef EDITOR
  /* Figure out the boundaries for the selection highlight.
   * This needs to be done whether or not we're selected,
   * because we may have to clear the selection (but only in the editor).
   * Some of this code is stolen from the macfe.
   */
  if (EDT_IS_EDITOR(context))
  {
      int iSelectionBorderThickness;
      XGCValues gcv;
      unsigned long gc_flags;
      GC gc;
      fe_Drawable *fe_drawable = CONTEXT_DATA(context)->drawable;

      /* This is what the macfe does, but on X it's slow.
       * lo_DisplayLine ends up calling lo_DisplayTable for every
       * table element!  This has something to do with the entropy
       * calculated for the layer in liblayer/cl_util.h, which causes
       * the table to be laid out cell by cell even if the whole page
       * is being redisplayed; and the table border gets entirely
       * redrawn for each of these redraws.  Yuck!
       */

      /* set the border thickness to be the minimum of all border widths */
      if (!hasBorder && (0 == ts->inter_cell_space))
          iSelectionBorderThickness = 1;
      else
      {
          iSelectionBorderThickness = ts->border_left_width;
          if ( ts->border_right_width < iSelectionBorderThickness )
              iSelectionBorderThickness = ts->border_right_width;
          if ( ts->border_top_width < iSelectionBorderThickness )
              iSelectionBorderThickness = ts->border_top_width;
          if ( ts->border_bottom_width < iSelectionBorderThickness )
              iSelectionBorderThickness = ts->border_bottom_width;

          /* allow for a larger selection if the border is large */
          if ( iSelectionBorderThickness > 2 * ED_SELECTION_BORDER )
              iSelectionBorderThickness = 2 * ED_SELECTION_BORDER;

      /* else if the area is too small, use the spacing between cells */
          else if ( iSelectionBorderThickness < ED_SELECTION_BORDER )
          {
              iSelectionBorderThickness += ts->inter_cell_space;

              /* but don't use it all; stick to the minimal amount */
              if ( iSelectionBorderThickness > ED_SELECTION_BORDER )
                  iSelectionBorderThickness = ED_SELECTION_BORDER;
          }
      }

      if (ts->ele_attrmask & LO_ELE_SELECTED)
      {
          gc_flags = GCForeground | GCLineWidth | GCLineStyle;
          gcv.foreground = CONTEXT_DATA(context)->select_bg_pixel;
          gcv.line_style = LineOnOffDash;
      }
      else
      {
          gc_flags = GCForeground | GCLineWidth;
          gcv.foreground =
              CONTEXT_DATA(context)->drawing_area->core.background_pixel;
      }

      gcv.line_width = iSelectionBorderThickness;
      gc = fe_GetGCfromDW(fe_display, fe_drawable->xdrawable,
                          gc_flags, &gcv, fe_drawable->clip_region);
      XDrawRectangle(fe_display,
                     XtWindow(CONTEXT_DATA(context)->drawing_area), gc,
                     table_rect.x, table_rect.y,
                     table_rect.width-1, table_rect.height-1);
  } /* end showing selection highlight if editor */
#endif /* EDITOR */

  if (hasBorder)
  {
      widths.top = ts->border_top_width;
      widths.right = ts->border_right_width;
      widths.bottom = ts->border_bottom_width;
      widths.left = ts->border_left_width;
      fe_DisplayTableBorder(context, ts, &table_rect, &widths);
  }
}

typedef struct _SashInfo {
  Widget sash;
  Widget separator;
  LO_EdgeStruct *edge;
  MWContext *context;
  time_t last;
} SashInfo;

static void
fe_sash_cb (Widget widget, XtPointer closure, XtPointer call_data)
{
  SashInfo *sashinfo = (SashInfo *) closure;
  MWContext *context;
  LO_EdgeStruct *edge;
  SashCallData sash_data = (SashCallData) call_data;

  static EventMask activity = 0;
  static GC trackgc = 0;
  static int lastx = 0;
  static int lasty = 0;
  static int lastw = 0;
  static int lasth = 0;

  TRACEMSG (("fe_sash_cb\n"));

  if (!sashinfo) return;
  context = sashinfo->context;
  edge = sashinfo->edge;

  switch (sash_data->event->xany.type) {
  case ButtonPress: {
      XGCValues values;
      unsigned long valuemask;

      if (activity) return;
      activity = ButtonPressMask;

      if (!trackgc) {
	valuemask = GCForeground | GCSubwindowMode | GCFunction;
	values.foreground = CONTEXT_DATA (context)->default_bg_pixel;
	values.subwindow_mode = IncludeInferiors;
	values.function = GXinvert;
	trackgc = XCreateGC (XtDisplay (widget),
			     XtWindow (CONTEXT_WIDGET (context)),
			     valuemask, &values);
      }
    }
    break;
  case ButtonRelease:
    if (activity & PointerMotionMask) {
      static time_t last = 0;
      time_t now;

      /* Clean up the last line drawn */
      XDrawLine (XtDisplay (widget),
		   XtWindow (CONTEXT_DATA (context)->drawing_area),
		   trackgc, lastx, lasty, lastw, lasth);

      activity = 0; /* make sure we clear this for next time */

      if (trackgc) XFreeGC (XtDisplay (widget), trackgc);
      trackgc = 0;

      /* What's the scrolling policy for this context? */
      if (!edge->movable)
	return;

      /* Don't thrash */
      now = time ((time_t) 0);
      if (now > last)
        LO_MoveGridEdge (context, edge, lastx, lasty);
      last = now;

      lastx = lasty = lastw = lasth = 0;
    }
    break;
  case MotionNotify: {
      Display *dpy = XtDisplay (widget);
      Window kid;
      int da_x, da_y;

      if (!(activity & ButtonPressMask)) return;

      /* What's the scrolling policy for this context? */
      if (!edge->movable)
	return;

      /* Now that we know we're going to do something */
      activity |= PointerMotionMask;

      XTranslateCoordinates(dpy,
                            XtWindow (CONTEXT_DATA (context)->drawing_area),
                            DefaultRootWindow (dpy),
                            0, 0, &da_x, &da_y, &kid);

      if (lastw && lasth)
        XDrawLine (XtDisplay (widget),
		   XtWindow (CONTEXT_DATA (context)->drawing_area),
		   trackgc, lastx, lasty, lastw, lasth);

      if (edge->is_vertical)
        {
          int cx = sash_data->event->xmotion.x_root;

	  lastx = cx - da_x;
	  lasty = edge->y + edge->y_offset;

	  if (lastx < edge->left_top_bound + 20)
	    lastx = edge->left_top_bound + 20;

	  if (lastx > edge->right_bottom_bound - 20)
	    lastx = edge->right_bottom_bound - 20;

	  lastw = lastx;
	  lasth = lasty + edge->height - 1;
        }
      else
        {
          int cy = sash_data->event->xmotion.y_root;

	  lastx = edge->x + edge->x_offset;
	  lasty = cy - da_y;

	  if (lasty < edge->left_top_bound + 20)
	    lasty = edge->left_top_bound + 20;

	  if (lasty > edge->right_bottom_bound - 20)
	    lasty = edge->right_bottom_bound - 20;

	  lastw = lastx + edge->width - 1;
	  lasth = lasty;
        }

      XDrawLine (XtDisplay (widget),
		 XtWindow (CONTEXT_DATA (context)->drawing_area),
		 trackgc, lastx, lasty, lastw, lasth);
    }
    break;
  default:
    break;
  }
}

static void
fe_sash_destroy_cb (Widget w, XtPointer closure, XtPointer cb)
{
  SashInfo *sashinfo = (SashInfo *) closure;
  sashinfo->sash = NULL;
}

void
XFE_DisplayEdge (MWContext *context, int loc, LO_EdgeStruct *edge)
{
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  long x = edge->x + edge->x_offset - CONTEXT_DATA (context)->document_x +
      fe_drawable->x_origin;
  long y = edge->y + edge->y_offset - CONTEXT_DATA (context)->document_y +
      fe_drawable->y_origin;
  Widget drawing_area = CONTEXT_DATA (context)->drawing_area;
  Widget sash;
  static XtCallbackRec sashCallback[] = { {fe_sash_cb, 0}, {0, 0} };
  SashInfo *sashinfo;
  Arg av [50];
  int ac;

  if ((x > 0 && x > CONTEXT_DATA (context)->scrolled_width) ||
      (y > 0 && y > CONTEXT_DATA (context)->scrolled_height) ||
      (x + edge->width < 0) ||
      (y + edge->height< 0))
    return;

  /* Set up the args for the sash.
   * Careful! This is the only place we initialize av.
   */
  ac = 0;
  XtSetArg (av[ac], XmNx, x); ac++;
  XtSetArg (av[ac], XmNy, y); ac++;
  XtSetArg (av[ac], XmNwidth, edge->width); ac++;
  XtSetArg (av[ac], XmNheight, edge->height); ac++;
  if (edge->bg_color) {
    Pixel color = fe_GetPixel (context,
			       edge->bg_color->red,
			       edge->bg_color->green,
			       edge->bg_color->blue);
    XtSetArg (av[ac], XmNbackground, color); ac++;
  }

  if (edge->FE_Data) {
    time_t now = time((time_t) 0);

    sashinfo = (SashInfo *) edge->FE_Data;
    if (now <= sashinfo->last) return;
    sashinfo->last = now;

    if (sashinfo->sash) {
      XtSetValues (sashinfo->sash, av, ac);
      return;
    }
      
    edge->FE_Data = NULL;
    XP_FREE (sashinfo);
  }

  /* Otherwise, create and display a new one */

  sashinfo = (SashInfo *) XP_ALLOC (sizeof (SashInfo));
  if (!sashinfo) return;

  sashCallback[0].closure = (XtPointer) sashinfo;

  /* av and ac were initialized above */
  XtSetArg (av[ac], XmNcallback, (XtArgVal) sashCallback); ac++;
  sash = XtCreateWidget("sash", xmSashWidgetClass, drawing_area, av, ac);
  if (!edge->movable)
    XtVaSetValues (sash, XmNsensitive, False, 0);

  XtAddCallback (sash, XtNdestroyCallback, fe_sash_destroy_cb,
		 (XtPointer) sashinfo);
  XtManageChild (sash);

  sashinfo->sash = sash;
  sashinfo->edge = edge;
  sashinfo->context = context;
  sashinfo->last = time((time_t) 0);

  edge->FE_Data = (void *) sashinfo;
}


void
XFE_SetBackgroundColor (MWContext *context, uint8 red, uint8 green,
			uint8 blue)
{
  Pixel bg = fe_GetPixel (context, red, green, blue);
  CONTEXT_DATA (context)->bg_red   = red;
  CONTEXT_DATA (context)->bg_green = green;
  CONTEXT_DATA (context)->bg_blue  = blue;
  CONTEXT_DATA (context)->bg_pixel = bg;

/* Set the transparent pixel color.  The transparent pixel is passed into
   calls to IL_GetImage for image requests that do not use a mask. */
  fe_SetTransparentPixel(context, red, green, blue, bg);

  XSetWindowBackground (XtDisplay(CONTEXT_DATA (context)->drawing_area),
			XtWindow(CONTEXT_DATA (context)->drawing_area), bg);
  CONTEXT_DATA (context)->drawing_area->core.background_pixel = bg;
}


/* XXXM12N FE_EraseBackground needs to be completely restructured.  The way
   it is planned, layout would only call FE_EraseBackground to clear an
   area with a solid color.  Backdrop pixmaps would be handled by calling
   IL_DisplaySubImage, which can perform tiling if necessary. */
/* Erase the background at the given rect. x, y, width and height are */
/* in document coordinates. */
void
XFE_EraseBackground(MWContext *context, int iLocation, int32 x, int32 y,
                    uint32 width, uint32 height, LO_Color *bg)
{
    if (width > 0 && height > 0) {
        if (bg) {
            Pixel color = fe_GetPixel(context, bg->red, bg->green, bg->blue);
            fe_ClearAreaWithColor(context,
                                  -CONTEXT_DATA(context)->document_x + x,
                                  -CONTEXT_DATA(context)->document_y + y,
                                  width, height, color);
        }
        else {
            fe_ClearArea(context,
                         -CONTEXT_DATA(context)->document_x + x,
                         -CONTEXT_DATA(context)->document_y + y,
                         width, height);
        }
    }
}

void
FE_GetEdgeMinSize(MWContext *context, int32 *size_p)
{
	*size_p = 5;
}


void
FE_GetFullWindowSize(MWContext *context, int32 *width, int32 *height)
{
  Dimension     w = 0;
  Dimension     h = 0;
  Dimension  hpad = CONTEXT_DATA (context)->sb_w;
  Dimension  vpad = CONTEXT_DATA (context)->sb_h;
  
  if (context->is_grid_cell) {
    Position    sx, sy;
    
    XtVaGetValues (CONTEXT_DATA (context)->scrolled, 
		   XmNwidth, &w, XmNheight, &h, 0);
    
    *width = w;
    *height = h;
    
    XtVaGetValues (CONTEXT_DATA (context)->vscroll, XmNx, &sx, 0);
    XtVaGetValues (CONTEXT_DATA (context)->hscroll, XmNy, &sy, 0);
    
    if ((long) sx < (long) w)
      *width = (int32)w;
    else
      *width = (int32)(w + vpad);
    
    if ((long) sy < (long) h) 
      *height = (int32)h;
    else
      *height = (int32)(h + hpad);
    
    XtVaSetValues (CONTEXT_DATA (context)->scrolled, 
		   XmNwidth, *width, XmNheight, *height, 0);
	}
  else {
    Widget dap = XtParent( CONTEXT_DATA (context)->drawing_area );
    
    XtVaGetValues (dap, XmNwidth, &w, XmNheight, &h, 0);
    *width = (int32) w;
    *height = (int32) h;

    XtVaSetValues (CONTEXT_DATA (context)->drawing_area, 
		   XmNwidth, *width, XmNheight, *height, 0);
  }
}

void
fe_GetMargin(MWContext *context, int32 *marginw_ptr, int32 *marginh_ptr)
{
  int32 w, h;
  if (context->is_grid_cell) {
    w = FEUNITS_X(7, context);
    h = FEUNITS_X(4, context);
  } else if (context->type == MWContextMail ||
	     context->type == MWContextNews) {
    w = FEUNITS_X(8, context);
    h = 0; /* No top margin for mail and news windows */
  } else {
    w = FEUNITS_X(8, context);
    h = FEUNITS_X(8, context);
  }

  if (marginw_ptr) *marginw_ptr = w;
  if (marginh_ptr) *marginh_ptr = h;
}


void
XFE_LayoutNewDocument (MWContext *context, URL_Struct *url,
		      int32 *iWidth, int32 *iHeight,
		       int32 *mWidth, int32 *mHeight)
{
  Dimension w = 0, h = 0;
  XColor color;
  int32 fe_mWidth, fe_mHeight;
  Boolean grid_cell_p = context->is_grid_cell;

  /* Fix for bug #29631 */
  if (context == last_documented_xref_context)
        {
                last_documented_xref_context = 0;
                last_documented_xref = 0;
                last_documented_anchor_data = 0;
        }

  fe_FreeTransientColors(context);
  
  CONTEXT_DATA (context)->delayed_images_p = False;

  color.pixel = CONTEXT_DATA (context)->default_bg_pixel;
  fe_QueryColor (context, &color);

  /* The pixmap itself is freed when its IL_Image is destroyed. */
  CONTEXT_DATA (context)->backdrop_pixmap = 0;

  /* Set background after making the backdrop_pixmap 0 as SetBackground
   * will ignore a background setting request if backdrop_pixmap is
   * available.
   */
  XFE_SetBackgroundColor (context,
			  color.red >> 8,
			  color.green >> 8,
			  color.blue >> 8);

  if (grid_cell_p) {
    if (!CONTEXT_DATA (context)->drawing_area) return;

    XtVaGetValues (CONTEXT_DATA (context)->drawing_area,
               XmNwidth, &w, XmNheight, &h, 0);
  } else {
    if (!CONTEXT_DATA (context)->scrolled) return;

    XtVaGetValues (CONTEXT_DATA (context)->scrolled,
		 XmNwidth, &w, XmNheight, &h, 0);
  }
  if (!w || !h) abort ();

  /* Clear the background since, in the case of grid cells without borders, 
     the grid boundaries don't get cleared */
  XClearArea (XtDisplay (CONTEXT_WIDGET (context)),
	      XtWindow (CONTEXT_DATA (context)->drawing_area), 
	      0, 0,
	      CONTEXT_DATA (context)->scrolled_width,
	      CONTEXT_DATA (context)->scrolled_height, 
	      False);

  /* Only make room for scrollbar if is non-grid cell,
   * or is grid cell but scrolling is turned on.  -slamm 
   */
  if (!context->is_grid_cell || CONTEXT_DATA(context)->grid_scrolling) {
  /* Subtract out the size of the scrollbars - they might not end up being
     present, but tell layout that all it has to work with is the area that
     would be available if there *were* scrollbars. */
	w -= CONTEXT_DATA (context)->sb_w;
	h -= CONTEXT_DATA (context)->sb_h;
  }

  w -= 2;	/* No, this isn't a hack.  What makes you think that? */

  *iWidth = w;
  *iHeight = h;

  fe_GetMargin(context, &fe_mWidth, &fe_mHeight);

  /*
   * If layout already knows margin width, let it pass unless it
   * is just too big.
   */
  if (*mWidth != 0)
    {
      if (*mWidth > ((w / 2) - 1))
        *mWidth = ((w / 2) - 1);
    }
  else
    {
      *mWidth = fe_mWidth;
    }

  /*
   * If layout already knows margin height, let it pass unless it
   * is just too big.
   */
  if (*mHeight != 0)
    {
      if (*mHeight > ((h / 2) - 1))
        *mHeight = ((h / 2) - 1);
    }
  else
    {
      *mHeight = fe_mHeight;
    }

  /* Get rid of the old title; don't install "Unknown" until we've gotten
     to the end of the document without XFE_SetDocTitle() having been called.
   */
  if (context->title)
    free (context->title);
  context->title = 0;

  if (!grid_cell_p && !CONTEXT_DATA (context)->is_resizing)
    fe_SetURLString (context, url);

  /* This is set in fe_resize_cb */
  CONTEXT_DATA (context)->is_resizing = FALSE;

  if (url->address && !(sploosh && !strcmp (url->address, sploosh)))
    {
      if (sploosh)
	{
	  free (sploosh);
	  sploosh = 0;
	}
      
      SHIST_AddDocument (context, SHIST_CreateHistoryEntry (url, ""));
    }

  /* Make sure we clear the string from the previous document */
  if (context->defaultStatus) {
    XP_FREE (context->defaultStatus);
    context->defaultStatus = 0;
  }

  /* #### temporary, until we support printing GIFs that don't have HTML
     wrapped around them. */
#if 0
  if (CONTEXT_DATA (context)->print_menuitem)
    XtVaSetValues (CONTEXT_DATA (context)->print_menuitem,
		   XmNsensitive, !url->is_binary, 0);
  if (CONTEXT_DATA (context)->print_button)
    XtVaSetValues (CONTEXT_DATA (context)->print_button,
		   XmNsensitive, !url->is_binary, 0);
#endif

#ifdef LEDGES
  XFE_ClearView (context, FE_TLEDGE);
  XFE_ClearView (context, FE_BLEDGE);
#endif
  fe_SetDocPosition (context, 0, 0);

  fe_FindReset (context);
  if (!grid_cell_p)
    fe_UpdateDocInfoDialog (context);
}


void
fe_FormatDocTitle (const char *title, const char *url, char *output, int size)
{
  if (size < 0) return;

  if (title && !XP_STRCASECMP(title, XP_GetString(XFE_UNTITLED)))
    title = 0;  /* Losers!!! */

  if (title)
	{
	  XP_SAFE_SPRINTF (output, size, "%.200s", title);
	}
  else if (!url || 
		   !*url || 
		   strcmp(url,XP_GetString(XFE_LAY_LOCAL_FILE_URL_UNTITLED)) == 0)
    {
      XP_STRNCPY_SAFE(output, XP_GetString(XFE_UNTITLED), size);
    }
  else
    {
      const char *s = (const char *) strrchr (url, '/');
      if (s)
	s++;
      else
	s = url;
      PR_snprintf (output, size, "%.200s", s);
    }
}


void
XFE_SetDocTitle (MWContext *context, char *title)
{
  char buf [1024];
  char buf2 [1024];
  Widget shell = CONTEXT_WIDGET (context);
  XTextProperty text_prop;
  char *fmt;
  INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(context);

  if (context->type == MWContextSaveToDisk || !shell)
    return;

  /* For some context types like MWContextDialog, the shell is the parent
   * of the CONTEXT_WIDGET. In general, traverse back and get the shell.
   */
  while(!XtIsWMShell(shell) && (XtParent(shell)!=0))
    shell = XtParent(shell);

  /* We don't need to set the title for grid cells;
   * the backend sets the toplevel's title for us.
   */
  if (context->is_grid_cell)
    return;

  if (context->type == MWContextMessageComposition)
    {
      /* I18N watch */
      if (context->title) free (context->title);
      context->title = (title ? strdup (title) : 0);
      if (!title)
	title = XP_GetString( XFE_NO_SUBJECT );
      PR_snprintf (buf, sizeof(buf), "%.200s", title);
    }
  else
    {
      History_entry *he = SHIST_GetCurrent (&context->hist);
      char *url = (he && he->address ? he->address : 0);

      SHIST_SetTitleOfCurrentDoc (&context->hist, title);

      fe_UpdateDocInfoDialog (context);

      if (context->title) free (context->title);
      context->title = (title ? strdup (title) : 0);

#ifdef EDITOR
		/*
		 *    For the editor we would rather at least have the filename,
		 *    not so for Browser where it may be intentional.
		 */
	    if (context->type == MWContextEditor) {
			if (title == NULL || (title != NULL && title[0] == '\0'))
				title = url;
		}
#endif /*EDITOR*/

		fe_FormatDocTitle (title, url, buf, sizeof(buf));
    }

  switch (context->type) {
    case MWContextAddressBook:
    case MWContextBookmarks:
    case MWContextHistory:
	  fmt = "%s";
	  break;
    case MWContextMail:
        /* Don't reset the title on the folders window */
        if (shell && !strcmp(XtName(shell), "MailFolder"))
	  return;
	fmt = XP_GetString(XFE_MAIL_TITLE_FMT);
	break;
    case MWContextNews:
	fmt = XP_GetString(XFE_NEWS_TITLE_FMT);
	break;
    case MWContextMessageComposition:
        fmt = XP_GetString(XFE_COMPOSE);
	break;
    case MWContextEditor:
	fmt = XP_GetString(XFE_EDITOR_TITLE_FMT);
	break;
    case MWContextBrowser: 	/* FALL THROUGH */
    default:
	fmt = XP_GetString(XFE_TITLE_FMT);
  }
  XP_SAFE_SPRINTF(buf2, sizeof (buf2), fmt, buf);

  /* For some context types like MWContextDialog, the shell is the parent
   * of the CONTEXT_WIDGET. In general, traverse back and get the shell.
   */
  while(!XtIsWMShell(shell) && (XtParent(shell)!=0))
    shell = XtParent(shell);

  if (INTL_GetCSIWinCSID(c) == CS_LATIN1)
    {
      text_prop.value = (unsigned char *) buf2;
      text_prop.encoding = XA_STRING;
      text_prop.format = 8;
      text_prop.nitems = strlen(buf2);
    }
  else
    {
      char *loc;
      int status;

      loc = (char *) fe_ConvertToLocaleEncoding(INTL_GetCSIWinCSID(c),
                                                (unsigned char *) buf2);
      status = XmbTextListToTextProperty(XtDisplay(shell), &loc, 1,
                                         XStdICCTextStyle, &text_prop);
      if (loc != buf2)
        {
          XP_FREE(loc);
        }
      if (status != Success)
        {
          text_prop.value = (unsigned char *) buf2;
          text_prop.encoding = XA_STRING;
          text_prop.format = 8;
          text_prop.nitems = strlen(buf2);
        }
    }

   /* We should not call XSetWMName here because the shell might not
      be realized yet. You are going to get NULL window id when shell
      is not realized. Then, we will get X Error (Bad Window) with
      ChangeProperty error id.

      To fix this, we should use XtSetValues on both
      titleEncoding and title. And, when shell is realized, it will
      turn around, and change the window property (ie Calling
      XSetWMName) at the right time.

     I really don't know why they say that SetValues
     does not work for high-bit characters earlier. 

     Here, I set the titleEncoding too. I think that the hight-bit
     character problem should not exist now once we set the encoding.

     If I was proven to be wrong, please let me know - dh */
   XtVaSetValues (shell, XtNtitleEncoding, text_prop.encoding, 0);
   XtVaSetValues (shell, XtNtitle, text_prop.value, 0);

    /* Only set the icon title on browser windows - not mail, news or
       download. */
  if (context->type == MWContextBrowser)
  {
   /* Same comments at the block for XtNtitle */
   XtVaSetValues (shell, XtNiconNameEncoding, text_prop.encoding, 0);
   XtVaSetValues (shell, XtNiconName, text_prop.value, 0);
  }
}


void
fe_DestroyLayoutData (MWContext *context)
{
  LO_DiscardDocument (context);
  free (CONTEXT_DATA (context)->color_data);
}

void
XFE_FinishedLayout (MWContext *context)
{
  /* Since our processing of XFE_SetDocDimension() may have been lazy,
     do it for real this time. */
  CONTEXT_DATA (context)->doc_size_last_update_time = 0;
  /* Update scrollbars using final dimensions. */
  fe_SetDocPosition(context, CONTEXT_DATA (context)->document_x,
                    CONTEXT_DATA (context)->document_y);
}


void
fe_ClearArea (MWContext *context, int x, int y, unsigned int w, unsigned int h)
{
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  Drawable drawable = fe_drawable->xdrawable;
  Display *dpy = fe_display;
  GC gc;
  XGCValues gcv;

  memset (&gcv, ~0, sizeof (gcv));
  gcv.foreground = CONTEXT_DATA (context)->bg_pixel;
  gc = fe_GetGCfromDW (dpy, drawable, (GCForeground), &gcv,
                       fe_drawable->clip_region);

  XFillRectangle (dpy, drawable, gc, x, y, w, h);
}

void
fe_ClearAreaWithColor (MWContext *context, int x, int y, unsigned int w,
                       unsigned int h, Pixel color)
{
  fe_Drawable *fe_drawable = CONTEXT_DATA (context)->drawable;
  Drawable drawable = fe_drawable->xdrawable;
  Display *dpy = fe_display;
  GC gc;
  XGCValues gcv;

  memset (&gcv, ~0, sizeof (gcv));
  gcv.foreground = color;
  gc = fe_GetGCfromDW (dpy, drawable, (GCForeground), &gcv,
                       fe_drawable->clip_region);
  XFillRectangle (dpy, drawable, gc, x, y, w, h);
}

void
XFE_ClearView (MWContext *context, int which)
{
  if (!XtIsManaged (CONTEXT_WIDGET (context)))
    return;

  /* Clear out the data for the mouse-highlighted item.
     #### What if one ledge is being cleared but not all, and the
     highlighted item is in the other?
   */
  last_armed_xref = 0;
  last_armed_xref_highlighted_p = False;
  last_documented_xref_context = 0;
  last_documented_xref = 0;
  last_documented_anchor_data = 0;
  fe_SetCursor (context, False);

  switch (which)
    {
#ifdef LEDGES
    case FE_TLEDGE:
      XClearWindow (XtDisplay (CONTEXT_WIDGET (context)),
		    XtWindow (CONTEXT_DATA (context)->top_ledge));
      break;
    case FE_BLEDGE:
      XClearWindow (XtDisplay (CONTEXT_WIDGET (context)),
		    XtWindow (CONTEXT_DATA (context)->bottom_ledge));
      break;
#endif
    case FE_VIEW:
      fe_ClearArea (context, 0, 0,
		    /* Some random big number (but if it's too big,
		       like most-possible-short, it will make some
		       MIT R4 servers mallog excessively, sigh.) */
		    CONTEXT_WIDGET (context)->core.width * 2,
		    CONTEXT_WIDGET (context)->core.height * 2);
      break;
    default:
      abort ();
    }
}

void
XFE_BeginPreSection (MWContext *context)
{
}

void
XFE_EndPreSection (MWContext *context)
{
}

void
XFE_FreeEdgeElement (MWContext *context, LO_EdgeStruct *edge)
{
  SashInfo *sashinfo = edge->FE_Data;

  if (!sashinfo) return;

  if (sashinfo->sash) {
    XtRemoveCallback (sashinfo->sash, XtNdestroyCallback,
		      fe_sash_destroy_cb, (XtPointer) sashinfo);
    XtDestroyWidget (sashinfo->sash);
    sashinfo->sash = NULL;
  }

  edge->FE_Data = NULL;
  XP_FREE (sashinfo);
}

/*
 * This is called by the plugin code to create a new embedded window
 * for the plugin in the specified context.
 */
void
XFE_CreateEmbedWindow(MWContext *context, NPEmbeddedApp *app)
{
    Widget parent = CONTEXT_DATA (context)->drawing_area;
    LO_EmbedStruct* lo_struct;
    Widget embed;
    Window win;
    int xp, yp;
    int32 xs, ys;

    /* now we have a live embed and its the first time so we need to
       prepare a window for it */
    if (XP_FAIL_ASSERT(app->np_data != NULL))
        return;

    lo_struct = ((np_data *) app->np_data)->lo_struct;
    if (XP_FAIL_ASSERT(lo_struct != NULL))
        return;

    xp = lo_struct->x;
    yp = lo_struct->y;
    xs = lo_struct->width;
    ys = lo_struct->height;
	
    if (CONTEXT_DATA(context)->is_fullpage_plugin) {
        /* This is a full page plugin */
        int32 mWidth, mHeight;

        FE_GetFullWindowSize(context, &xs, &ys);
        fe_GetMargin(context, &mWidth, &mHeight);
        xs -= mWidth;
        ys -= mHeight;
	    
        xp = yp = 0;
    }

    {
        Pixel bg;
        Arg av[20];
        int ac = 0;

        XtVaGetValues(parent, XmNbackground, &bg, 0);

        /* XtSetArg(av[ac], XmNborderWidth, 1);         ac++ */
        XtSetArg(av[ac], XmNx, (Position)xp);           ac++;
        XtSetArg(av[ac], XmNy, (Position)yp);           ac++;
        XtSetArg(av[ac], XmNwidth, (Dimension)xs);      ac++;
        XtSetArg(av[ac], XmNheight, (Dimension)ys);     ac++;
#ifdef X_PLUGINS
        XtSetArg(av[ac], XmNmarginWidth, 0);            ac++;
        XtSetArg(av[ac], XmNmarginHeight, 0);           ac++;
#endif
        XtSetArg(av[ac], XmNbackground, bg);            ac++;
        embed = XmCreateDrawingArea(parent, "netscapeEmbed", av, ac);
    }

    XtRealizeWidget (embed); /* create window, but don't map */
    win = XtWindow(embed);

    if (fe_globalData.fe_guffaw_scroll == 1) {
        XSetWindowAttributes attr;
        unsigned long valuemask;

        valuemask = CWBitGravity | CWWinGravity;
        attr.win_gravity = StaticGravity;
        attr.bit_gravity = StaticGravity;
        XChangeWindowAttributes(XtDisplay (embed), XtWindow (embed),
                                valuemask, &attr);
    }
    /* XtManageChild (embed); */

    /* make a plugin wininfo */
    {
        NPWindow *nWin = (NPWindow *)malloc(sizeof(NPWindow));
        if(nWin) {
#ifdef X_PLUGINS
            NPSetWindowCallbackStruct *fe_data;
#endif /* X_PLUGINS */

            nWin->window = (void *)win;
            nWin->x = xp;
            nWin->y = yp;
            nWin->width = xs;
            nWin->height = ys;
            nWin->type = NPWindowTypeWindow;
		
#ifdef X_PLUGINS
            fe_data = (NPSetWindowCallbackStruct *)
                malloc(sizeof(NPSetWindowCallbackStruct));

            if (fe_data) {
                Visual *v = 0;
                Colormap cmap = 0;
                Cardinal depth = 0;

                XtVaGetValues(CONTEXT_WIDGET(context), XtNvisual, &v,
                              XtNcolormap, &cmap, XtNdepth, &depth, 0);

                fe_data->type = NP_SETWINDOW;
                fe_data->display = (void *) XtDisplay(embed);
                fe_data->visual = v;
                fe_data->colormap = cmap;
                fe_data->depth = depth;
                nWin->ws_info = (void *) fe_data;
            }
#endif /* X_PLUGINS */
        }
        app->wdata = nWin;
        app->fe_data = (void *)embed;
    }
}

/*
 * This is called by the plugin code to save an embed window in a
 * "safe place" (i.e., where it won't get destroyed when the context
 * goes away. It is called in response to NPL_DeleteEmbed().
 */
void
XFE_SaveEmbedWindow(MWContext *context, NPEmbeddedApp *app)
{
    Widget embedWidget;
    MWContext *safeContext;
    Widget safeWidget;
    Widget parentWidget;

    if (XP_FAIL_ASSERT(app->fe_data != NULL))
        return;

    embedWidget = (Widget) app->fe_data;
    if (XP_FAIL_ASSERT(XtIsWidget(embedWidget)))
        return;

    XUnmapWindow(XtDisplay(embedWidget), XtWindow(embedWidget));

    safeContext = XP_GetNonGridContext(context);
    if (XP_FAIL_ASSERT(safeContext != NULL))
        return;

    safeWidget = CONTEXT_DATA(safeContext)->drawing_area;
    if (XP_FAIL_ASSERT(safeWidget != NULL &&
                       XtIsWidget(safeWidget) &&
                       XtIsComposite(safeWidget)))
        return;
            
    parentWidget = XtParent(embedWidget);

    if (safeWidget != parentWidget) {
        /* If the safe widget and the parent widget are not one in the
           same, then we have a situation where the embedded object is
           on a grid context. Reparent the embedded object's widget to
           the safe context. First, we'll remove it from the current
           context... */
        if (XP_OK_ASSERT(parentWidget != NULL && XtIsComposite(parentWidget))) {
            CompositeWidgetClass c;
            c = (CompositeWidgetClass) XtClass(parentWidget);
            (c->composite_class.delete_child)(embedWidget);
        }

        /* Then, reparent it to the safe widget; both at the X and Xt
           levels. */
        XtParent(embedWidget) = safeWidget;

        {
            CompositeWidgetClass c;
            c = (CompositeWidgetClass) XtClass(safeWidget);
            (c->composite_class.insert_child)(embedWidget);
        }

        XReparentWindow(XtDisplay(embedWidget), XtWindow(embedWidget),
                        XtWindow(safeWidget), 0, 0);
    }
}

/*
 * This is called by the plugin code to restore a previously "saved"
 * embedded window to the new context.
 */
void
XFE_RestoreEmbedWindow(MWContext *context, NPEmbeddedApp *app)
{
    Widget embedWidget;
    Widget safeWidget;

    if (XP_FAIL_ASSERT(app != NULL && app->fe_data != NULL))
        return;

    embedWidget = (Widget) app->fe_data;
    if (XP_FAIL_ASSERT(XtIsWidget(embedWidget)))
        return;

    if (XtParent(embedWidget) != CONTEXT_DATA(context)->drawing_area) {
        /* Reparent the embedded object's widget from the safe context
           to the current context. */
        Widget safeWidget = XtParent(embedWidget);
        Widget parentWidget = CONTEXT_DATA(context)->drawing_area;

        /* Start by reparenting it at the X level */
        {
            int xp = 0;
            int yp = 0;

            if (app->np_data) {
                LO_EmbedStruct* embed_struct
                    = ((np_data*) app->np_data)->lo_struct;

                if (embed_struct) {
                    xp = embed_struct->x + embed_struct->x_offset -
                        CONTEXT_DATA(context)->document_x;
                    yp = embed_struct->y + embed_struct->y_offset -
                        CONTEXT_DATA(context)->document_y;
                }
            }

            XReparentWindow(XtDisplay(embedWidget), XtWindow(embedWidget),
                            XtWindow(parentWidget), xp, yp);
        }

        /* Now remove it from the safe context. Check to make sure we
           can really do composite ops on this thing... */
        if (XP_OK_ASSERT(safeWidget != NULL && XtIsComposite(safeWidget))) {
            CompositeWidgetClass c;
            c = (CompositeWidgetClass) XtClass(safeWidget);
            (c->composite_class.delete_child)(embedWidget);
        }

        /* Now reparent it to the current context */
        XtParent(embedWidget) = parentWidget;

        /* Again, a sanity check... */
        if (XP_OK_ASSERT(parentWidget != NULL && XtIsComposite(parentWidget))) {
            CompositeWidgetClass c;
            c = (CompositeWidgetClass) XtClass(parentWidget);
            (c->composite_class.insert_child)(embedWidget);
        }
    }

    XtMapWidget(embedWidget);
}

/*
 * This is called by the plugin code to destroy the embedded window.
 * It is either called _immediately_ in response to NPL_DeleteEmbed(),
 * or via layout when a saved embed window is released from the
 * history.
 */
void
XFE_DestroyEmbedWindow(MWContext *context, NPEmbeddedApp *app)
{
    if (app) {
        NPWindow* nWin = app->wdata;
        Widget embed_widget = (Widget)app->fe_data;

        if (embed_widget)
            XtDestroyWidget(embed_widget);

        if (nWin) {
            if (nWin->ws_info)
                free(nWin->ws_info);

            free(nWin);
        }
    }

    /* Reset fullpage plugin */
    CONTEXT_DATA(context)->is_fullpage_plugin = 0;
}

void
XFE_FreeEmbedElement (MWContext *context, LO_EmbedStruct *embed_struct)
{
    NPL_EmbedDelete(context, embed_struct);
}

#ifdef SHACK
extern void fe_showRDFView (Widget w, int width, int height);
#endif /* SHACK */

void
XFE_DisplayEmbed (MWContext *context,
		  int iLocation, LO_EmbedStruct *embed_struct)
{
    NPEmbeddedApp *eApp;
    int32 xs, ys;

    if (!embed_struct) return;
    eApp = (NPEmbeddedApp *)embed_struct->FE_Data;
    if (!eApp) return;

    /* Shouldn't be here if HIDDEN */
    if (embed_struct->ele_attrmask & LO_ELE_HIDDEN) 
      return;
    

    /* Layout might have changed the location of the embed since we
     * created the embed in XFE_GetEmbedSize()
     */
    xs = embed_struct->x + embed_struct->x_offset -
      CONTEXT_DATA (context)->document_x;
    ys = embed_struct->y + embed_struct->y_offset -
      CONTEXT_DATA (context)->document_y;

    /* If this is a full page plugin, then plugin needs to be notified of
       the new size as relayout never happens for this when we resize.
       Our resize handler marks this context as a fullpage plugin. */
	
    if (CONTEXT_DATA(context)->is_fullpage_plugin) {
	NPWindow *nWin = (NPWindow *)eApp->wdata;

	FE_GetFullWindowSize(context, &xs, &ys);
	
#if 0
	int32 mWidth, mHeight;
	/* Normally the right thing to do is to subtract the margin width.
	 * But we wont do this and give the plugin the full html area.
	 * Remember, layout still thinks the we offset the fullpage plugin
	 * by the margin offset.
	 */
	fe_GetMargin(context, &mWidth, &mHeight);
	xs -= mWidth;
	ys -= mHeight;
#else /* 0 */
	/* In following suit with our hack of no margins for fullpage plugins
	 * we force the plugin to (0,0) position.
	 */
	XtVaSetValues((Widget)eApp->fe_data, XmNx, (Position)0,
		      XmNy, (Position)0, 0);
#endif /* 0 */

	if (nWin->width != xs || nWin->height != ys) {
	    nWin->width = xs;
	    nWin->height = ys;
	    (void)NPL_EmbedSize(eApp);
	}
    }
    else {
	/* The layer containing the plugin may be hidden or clipped by
	   an enclosing layer, in which case we should unmap the
	   plugin's window */
	XtSetMappedWhenManaged((Widget)eApp->fe_data,
			       !(embed_struct->ele_attrmask &LO_ELE_INVISIBLE));

	/* The location of the plugin may have changed since it was
	 * created, either at the behest of Layout or due to movement
	 * of an enclosing layer.  So, change the position of the
	 * plugin. Do this only if we are not a fullpage plugin as
	 * fullpage plugins are always at (0,0).  */

	/* but first, update the size and call a set window... */
	(void)NPL_EmbedSize(eApp);

	XtVaSetValues((Widget)eApp->fe_data, XmNx, (Position)xs,
		      XmNy, (Position)ys, 0);
    }

    /* Manage the embed window. XFE_GetEmbedSize() only creates the it. */
    if (!XtIsManaged((Widget)eApp->fe_data))
	XtManageChild((Widget)eApp->fe_data);

#ifdef SHACK
    /* ... and now for the SHACK stuff */
    {
        NPWindow *nWin = (NPWindow *) eApp->wdata;
        int width = nWin->width;
       	int height = nWin->height;
       	Widget w = (Widget) eApp->fe_data;

        fe_showRDFView (w, width, height);
    }
#endif /* SHACK */
}

void
XFE_GetEmbedSize (MWContext *context, LO_EmbedStruct *embed_struct,
		  NET_ReloadMethod force_reload)
{
    NPEmbeddedApp *eApp = (NPEmbeddedApp *)embed_struct->FE_Data;
    int32 doc_id;
    lo_TopState *top_state;

    /* here we need only decrement the number of embeds expected to load */
    doc_id = XP_DOCID(context);
    top_state = lo_FetchTopState(doc_id);

    if(!eApp)
    {
	/* Determine if this is a fullpage plugin. Do this _now_ so
           that it'll be available when NPL_EmbedCreate() calls back
           to XFE_CreateEmbedWindow() */
	if((embed_struct->width == 1) &&
           (embed_struct->height == 1) &&
           (embed_struct->attribute_cnt > 0) &&
           (!strcmp(embed_struct->attribute_list[0], "src")) &&
           (!strcmp(embed_struct->value_list[0], "internal-external-plugin"))) {
            CONTEXT_DATA(context)->is_fullpage_plugin = 1;
        }

	/* attempt to make a plugin */
#ifdef UNIX_EMBED
	if(!(eApp = NPL_EmbedCreate(context, embed_struct)))
#else
	if(1)  /* disable unix plugin's */
#endif
	{
	    /* hmm, that failed which is unusual */
	    embed_struct->width = embed_struct->height=1;
	    return;
	}
	eApp->type = NP_Plugin;

	if (embed_struct->ele_attrmask & LO_ELE_HIDDEN) {
	    /* Hidden plugin. Dont create window for it. */
	    eApp->fe_data = 0;
	    eApp->wdata = 0;
	    embed_struct->width = embed_struct->height=0;
	    /* --- begin fix for bug# 35087 --- */
	    embed_struct->FE_Data = (void *)eApp;

            if (NPL_EmbedStart(context, embed_struct, eApp) != NPERR_NO_ERROR) {
	        /* Spoil sport! */
                /* XXX This used to be a call to fe_destroyEmbed,
                   which has now been massaged into a front-end
                   callback. However, it doesn't (and didn't!) _do_
                   anything unless eApp->fe_data or eApp->wdata
                   contain something, and we've just hard-coded them
                   to zero!

                XFE_DestroyEmbedWindow(context, eApp) */
                embed_struct->FE_Data = NULL;
	        return;
	    }
	    /* --- end fix for bug# 35087 --- */

            /* XXX NPL_EmbedSize does nothing if eApp->wdata == NULL;
               makes sense because this thing is _hidden_.

            (void)NPL_EmbedSize(eApp); */
	    return;
	}

	if (NPL_EmbedStart(context, embed_struct, eApp) != NPERR_NO_ERROR) {
	    /* Spoil sport! */
	    XFE_DestroyEmbedWindow(context, eApp);
            embed_struct->FE_Data = NULL;
	    return;
	}
    }

    /* always inform plugins of size changes */
    (void)NPL_EmbedSize(eApp);
}

/*************************************************************************
 * Java Stuff
 ************************************************************************/

#ifdef JAVA
static void* PR_CALLBACK 
FE_GetAwtWindow(MWContext *context, LJAppletData* ad)
{
    return ad->fe_data;
}
#endif

#ifdef JAVA
static void PR_CALLBACK
FE_SaveJavaWindow(MWContext *context, LJAppletData* ad, void* window)
{
    Widget * kids;
    Cardinal nkids = 0;
    Widget contextWidget = (Widget)window;
    XtUnmapWidget(contextWidget);  
	 
    /*
    ** We are about to destroy contextWidget, but we want to hang on
    ** to its child, so that we can remap it when the applet's page
    ** is browsed again. We reparent the child to mozilla's main 
    ** drawing area since we know that it will be there as long as
    ** mozilla is there. We do the reparenting at both X and Xt level.
    */
    
    XtVaGetValues(contextWidget, XmNchildren, &kids,
		  XmNnumChildren, &nkids, NULL);
    /* XP_ASSERT(nkids == 1);  */

    if (nkids >= 1) {
	Widget kid;

	kid = kids[0];
	(((CompositeWidgetClass) contextWidget->core.widget_class)->
	 composite_class.delete_child)(kid);
	kid->core.parent = CONTEXT_DATA(ad->context)->drawing_area; 

	(((CompositeWidgetClass) XtParent(kid)->core.widget_class)->
	 composite_class.insert_child)(kid);

	XUnmapWindow(XtDisplay(kid), XtWindow(kid));
		XReparentWindow(XtDisplay(kid), XtWindow(kid),
			XtWindow(CONTEXT_DATA(ad->context)->drawing_area), 0, 0); 
    }

    /*
    ** Destroy the window and set the pointer to null because it will
    ** need to get recreated.
    */
    XtDestroyWidget(contextWidget); 
    ad->window = NULL; 
}
#endif

void
XFE_HideJavaAppElement(MWContext *context, struct LJAppletData* session_data)
{
#ifdef JAVA
    LJ_HideJavaAppElement(context, session_data, FE_SaveJavaWindow);
#endif /* JAVA */
}

static void PR_CALLBACK
FE_FreeJavaWindow(MWContext *context, struct LJAppletData *appletData,
		  void* window)
{
    Widget contextWidget = (Widget)window;
    XtDestroyWidget(contextWidget);
}

void
XFE_FreeJavaAppElement(MWContext *context, struct LJAppletData *appletData)
{
#ifdef JAVA
    LJ_FreeJavaAppElement(context, appletData, 
			  FE_SaveJavaWindow,
			  FE_FreeJavaWindow);
#endif /* JAVA */
}


static void PR_CALLBACK 
FE_DisplayNoJavaIcon(MWContext *pContext, LO_JavaAppStruct *java_struct)
{
    /* write me */
}

#ifdef JAVA

static void* PR_CALLBACK 
FE_CreateJavaWindow(MWContext *context, LO_JavaAppStruct *java_struct,
		    int32 xp, int32 yp, int32 xs, int32 ys)
{
    LJAppletData* ad = (LJAppletData*)java_struct->session_data;
    Widget parent;
    Arg av[20];
    int ac = 0;
    Pixel bg;
    Widget contextWidget;

    parent = CONTEXT_DATA(context)->drawing_area;

    /* Adjust xp and yp for their offsets within the window */
    xp -= CONTEXT_DATA(context)->document_x;
    yp -= CONTEXT_DATA(context)->document_y;
    
    /*
    ** First time in for this applet; create motif widget for it
    */
    XtVaGetValues(parent, XmNbackground, &bg, 0);
    ac = 0;
    XtSetArg(av[ac], XmNborderWidth, 0); ac++;
    XtSetArg(av[ac], XmNx, (Position)xp); ac++;
    XtSetArg(av[ac], XmNy, (Position)yp); ac++;
    XtSetArg(av[ac], XmNwidth, (Dimension)xs); ac++;
    XtSetArg(av[ac], XmNheight, (Dimension)ys); ac++;
    XtSetArg(av[ac], XmNmarginWidth, 0); ac++;
    XtSetArg(av[ac], XmNmarginHeight, 0); ac++;
    XtSetArg(av[ac], XmNresizePolicy, XmRESIZE_NONE); ac++;
    XtSetArg(av[ac], XmNbackground, bg); ac++;
#ifdef DEBUG
    XtSetArg(av[ac], XmNtitle, ad->documentURL); ac++;
#endif /* DEBUG */
    contextWidget = XmCreateDrawingArea(parent,
					(char *)java_struct->attr_name,/* XXX */
					av, ac);
    XtSetMappedWhenManaged(contextWidget, FALSE);
    XtRealizeWidget(contextWidget); /* create window, but don't map */

    if (fe_globalData.fe_guffaw_scroll == 1)
    {
	XSetWindowAttributes attr;
	unsigned long valuemask;
	valuemask = CWBitGravity | CWWinGravity;
	attr.win_gravity = StaticGravity;
	attr.bit_gravity = StaticGravity;
	XChangeWindowAttributes(XtDisplay(contextWidget),
				XtWindow(contextWidget),
				valuemask, &attr);
    }
    XtManageChild(contextWidget);
    /* XSync(XtDisplay(contextWidget), 0); */

    return contextWidget;
}

static void PR_CALLBACK 
FE_RestoreJavaWindow(MWContext *context, LJAppletData* ad,
		     int32 xp, int32 yp, int32 xs, int32 ys)
{
    /*
    ** If the user goes  to another page and comes back to the applet's 
    ** page, the applet needs to be shown, So reparent the applet to the
    ** embedParent, when we have to show it. We don't need the old 
    ** squirrelling away code anymore.
    */

    Widget kid            = ad->fe_data;
    Widget embedParent    = ad->window;

    if (kid == NULL) return;
    
    /* Adjust xp and yp for their offsets within the window */
    xp -= CONTEXT_DATA(context)->document_x;
    yp -= CONTEXT_DATA(context)->document_y;
    
    XReparentWindow(XtDisplay(kid), XtWindow(kid), XtWindow(embedParent), 0, 0);
    if (XtParent(kid) != embedParent) {
	/* Motif hackery */
	(((CompositeWidgetClass) XtParent(kid)->core.widget_class)-> composite_class.delete_child) (kid);
	kid->core.parent = embedParent;
	(((CompositeWidgetClass) embedParent->core.widget_class)->composite_class.insert_child) (kid);
	(((CompositeWidgetClass) embedParent->core.widget_class)-> composite_class.change_managed) (embedParent);

    }
    XtMapWidget(kid); 
	    
}

static void PR_CALLBACK 
FE_SetJavaWindowPos(MWContext *context, void* window,
		    int32 xp, int32 yp, int32 xs, int32 ys)
{
    /* Adjust xp and yp for their offsets within the window */
    xp -= CONTEXT_DATA(context)->document_x;
    yp -= CONTEXT_DATA(context)->document_y;
    
    XtVaSetValues((Widget)window,
		  XmNx, (Position)xp,
		  XmNy, (Position)yp, 0);
}

static void PR_CALLBACK 
FE_SetJavaWindowVisibility(MWContext *context, void* window, PRBool visible)
{
    /* The layer containing the applet may be hidden or clipped by
       an enclosing layer, in which case we should unmap the
       applet's window */
    XtSetMappedWhenManaged((Widget)window, visible);
}

#endif /* JAVA */

void
XFE_DisplayJavaApp(MWContext *context,
		   int iLocation, LO_JavaAppStruct *java_struct)
{
#ifdef JAVA
    LJ_DisplayJavaApp(context, java_struct,
		      FE_DisplayNoJavaIcon,
		      FE_GetFullWindowSize,
		      FE_CreateJavaWindow,
                      FE_GetAwtWindow,
		      FE_RestoreJavaWindow,
		      FE_SetJavaWindowPos,
		      FE_SetJavaWindowVisibility);
#endif /* JAVA */
}

void
XFE_DrawJavaApp(MWContext *context,
		   int iLocation, LO_JavaAppStruct *java_struct)
{
}

void
XFE_GetJavaAppSize (MWContext *context, LO_JavaAppStruct *java_struct,
		    NET_ReloadMethod reloadMethod)
{
#ifdef JAVA
    LJ_GetJavaAppSize(context, java_struct, reloadMethod);
#else
    FE_DisplayNoJavaIcon(context, java_struct);
    java_struct->width = 1;
    java_struct->height = 1;
#endif
}

/*************************************************************************
 * End of Java Stuff
 ************************************************************************/


void 
XFE_HandleClippingView(MWContext *context, struct LJAppletData *appletD,
		       int x, int y, int width, int height)
{
}

void
fe_ReLayout (MWContext *context, NET_ReloadMethod force_reload)
{
  LO_Element *e = LO_XYToNearestElement (context,
					 CONTEXT_DATA (context)->document_x,
					 CONTEXT_DATA (context)->document_y,
                                         NULL);
  History_entry *he = SHIST_GetCurrent (&context->hist);
  URL_Struct *url;
  /* We must store the position into the History_entry before making
     a URL_Struct from it. */
  if (e && he)
    SHIST_SetPositionOfCurrentDoc (&context->hist, e->lo_any.ele_id);

  if (he)
    url = (force_reload == NET_RESIZE_RELOAD)
	? SHIST_CreateWysiwygURLStruct (context, he)
	: SHIST_CreateURLStructFromHistoryEntry (context, he);
  else if (sploosh)
    url = NET_CreateURLStruct (sploosh, FALSE);
  else
    url = 0;

  if (url)
    {
      if (force_reload != NET_DONT_RELOAD)
	url->force_reload = force_reload;

      /* warn plugins that the page relayout is not disasterous so that
	 it can fake caching their instances */
      /* XXX Only need to do this if you're eventually going to call
         NPL_EmbedDelete(), which doesn't appear to be the case?
      if (force_reload == NET_RESIZE_RELOAD || force_reload == NET_DONT_RELOAD)
	NPL_SamePage (context);
      */

      fe_GetURL (context, url, FALSE);
    }
}


/* Following links */

/* Returns the URL string of the LO_Element, if it has one.
   Returns "" for LO_ATTR_ISFORM, which are a total kludge...
 */
static char *
fe_url_of_xref (MWContext *context, LO_Element *xref, long x, long y)
{
  switch (xref->type)
    {
    case LO_TEXT:
      if (xref->lo_text.anchor_href)
	{
	  return (char *) xref->lo_text.anchor_href->anchor;
	}
      else
	{
          return (char *) NULL;
	}

    case LO_IMAGE:
      if (xref->lo_image.is_icon &&
          xref->lo_image.icon_number == IL_IMAGE_DELAYED)
        {
          long width, height;

          fe_IconSize(IL_IMAGE_DELAYED, &width, &height);
          if (xref->lo_image.alt &&
              xref->lo_image.alt_len &&
              (x > xref->lo_image.x + xref->lo_image.x_offset + 1 + 4 +
                  width))
            {
              if (xref->lo_image.anchor_href)
                {
                  return (char *) xref->lo_image.anchor_href->anchor;
                }
              else
                {
                  return (char *) NULL;
                }
            }
          else
            {
              return (char *) xref->lo_image.image_url;
            }
        }
      else if (xref->lo_image.image_attr->attrmask & LO_ATTR_ISFORM)
        {
          return "";
        }
      /*
       * This would be a client-side usemap image.
       */
      else if (xref->lo_image.image_attr->usemap_name != NULL)
        {
          LO_AnchorData *anchor_href;

          long ix = xref->lo_image.x + xref->lo_image.x_offset;
          long iy = xref->lo_image.y + xref->lo_image.y_offset;
          long mx = x - ix - xref->lo_image.border_width;
          long my = y - iy - xref->lo_image.border_width;

          anchor_href = LO_MapXYToAreaAnchor(context, (LO_ImageStruct *)xref,
							mx, my);
          if (anchor_href)
            {
              if (anchor_href->alt)
                {
                  return (char *) anchor_href->alt;
                }
              else
                {
                  return (char *) anchor_href->anchor;
                }
            }
          else
            {
              return (char *) NULL;
            }
        }
      else
        {
          if (xref->lo_image.anchor_href)
            {
              return (char *) xref->lo_image.anchor_href->anchor;
            }
          else
            {
              return (char *) NULL;
            }
        }

    default:
      return 0;
    }
}

void
fe_EventLOCoords (MWContext *context, XEvent *event,
		  unsigned long *x, unsigned long *y)
{
  *x = 0;
  *y = 0;

  switch (event->xany.type)
    {
    case ButtonPress:
    case ButtonRelease:
      *x = event->xbutton.x;
      *y = event->xbutton.y;
      break;

    case MotionNotify:
      *x = event->xmotion.x;
      *y = event->xmotion.y;
      break;

    case KeyPress:
    case KeyRelease:
      *x=event->xkey.x;
      *x=event->xkey.y;
      break;

    default:
      fprintf(stderr,
	      "fe_EventLOCoords(): unknown XEvent type %d\n",
	      event->xany.type);
      abort ();
      break;
    }

  *x += CONTEXT_DATA (context)->document_x;
  *y += CONTEXT_DATA (context)->document_y;
}

/* Returns the LO_Element under the mouse, if it is an anchor. */
static LO_Element *
fe_anchor_of_action (MWContext *context, CL_Event *layer_event,
                     CL_Layer *layer)
{
  LO_Element *le;
  unsigned long x, y;
  x = layer_event->x;
  y = layer_event->y;
  le = LO_XYToElement (context, x, y, layer);
  if (le && !fe_url_of_xref (context, le, x, y) && (le->type != LO_EDGE))
    le = 0;
  return le;
}

static void fe_WidgetLOCoords(MWContext *context, Widget widget,
			      unsigned long *x, unsigned long *y)
{
  Position wx, wy;

  XtVaGetValues(widget,XmNx,&wx,XmNy,&wy,NULL);

  (*x)=wx;
  (*y)=wy;

  *x += CONTEXT_DATA (context)->document_x;
  *y += CONTEXT_DATA (context)->document_y;
}
     

/* Returns the LO_Element of the widget, if it is a form element. */
static LO_Element *
fe_text_of_widget (MWContext *context, Widget widget,
                     CL_Layer *layer)
{
  LO_Element *le;
  unsigned long x, y;

  fe_WidgetLOCoords(context, widget, &x, &y);

  le = LO_XYToElement (context, x, y, layer);
  if (le && (le->type != LO_FORM_ELE))
    return NULL;

  return le;
}


void
fe_SetCursor (MWContext *context, Boolean over_link_p)
{
  Cursor c;

  if (CONTEXT_DATA (context)->save_next_mode_p)
    {
      if (over_link_p)
	c = CONTEXT_DATA (context)->save_next_link_cursor;
      else
	c = CONTEXT_DATA (context)->save_next_nonlink_cursor;
    }
  else if (CONTEXT_DATA (context)->clicking_blocked ||
	   CONTEXT_DATA (context)->synchronous_url_dialog)
    {
      c = CONTEXT_DATA (context)->busy_cursor;
    }
  else
    {
      if (over_link_p)
	c = CONTEXT_DATA (context)->link_cursor;
      else
	c = None;
    }
  if (CONTEXT_DATA (context)->drawing_area) {
    XDefineCursor (XtDisplay (CONTEXT_DATA (context)->drawing_area),
		   XtWindow (CONTEXT_DATA (context)->drawing_area),
		   c);
  }
}

static int click_x = -1, click_y = -1;	/* gag */
static Boolean moving = False;
static XtIntervalId auto_scroll_timer = 0;
static int fe_auto_scroll_x = 0;
static int fe_auto_scroll_y = 0;

static void
fe_auto_scroll_timer (XtPointer closure, XtIntervalId *id)
{
  MWContext *context = closure;
  int scale = 50; /* #### */
  int msecs = 10; /* #### */
  long new_x = (CONTEXT_DATA (context)->document_x +
		(scale * fe_auto_scroll_x));
  long new_y = (CONTEXT_DATA (context)->document_y +
		(scale * fe_auto_scroll_y));

  LO_ExtendSelection (context, new_x, new_y);
  fe_ScrollTo (context, (new_x > 0 ? new_x : 0), (new_y > 0 ? new_y : 0));

  auto_scroll_timer =
    XtAppAddTimeOut (fe_XtAppContext, msecs, fe_auto_scroll_timer, closure);
}

/* Invoked via a translation on <Btn1Down> and <Btn2Down>.
 */
extern void fe_HTMLDragSetLayer(CL_Layer *layer);

static void
fe_arm_link_action (Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  /* Clear global HTMLView drag layer.
   * If event gets dispatched to a layer, then a new value will be set
   * by fe_arm_link_action_for_layer()
   */
  fe_HTMLDragSetLayer(NULL);

  XP_ASSERT (context);
  if (!context) return;

  fe_UserActivity (context);

  fe_NeutralizeFocus (context);

  if (CONTEXT_DATA (context)->clicking_blocked ||
      CONTEXT_DATA (context)->synchronous_url_dialog)
    {
      XBell (XtDisplay (widget), 0);
      return;
    }

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_ARM_LINK;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_ARM_LINK);
  layer_event.fe_event_size = sizeof(fe_event);
#endif

  layer_event.fe_event = (void *)&fe_event;


  layer_event.type = CL_EVENT_MOUSE_BUTTON_DOWN;
  layer_event.which = event->xbutton.button;
  layer_event.modifiers = xfeToLayerModifiers(event->xbutton.state);

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;

          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_arm_link_action_for_layer(context, NULL, &layer_event);
      }
}


/* Layer specific actions.  fe_arm_link_action() */
void
fe_arm_link_action_for_layer(MWContext *context, CL_Layer *layer,
                             CL_Event *layer_event)
{
  LO_Element *xref;
  unsigned long x, y;
  Time time;

  /* Note that the av and ac parameters that were passed to 
	 fe_arm_link_action() can be obtained from the fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event->event;
#else
  XEvent *event = fe_event_extract(fe_event,NULL,NULL,NULL);
#endif

  if (context->compositor)
      CL_GrabMouseEvents(context->compositor, layer);

  xref = fe_anchor_of_action (context, layer_event, layer);

  time = (event && (event->type == KeyPress ||
		    event->type == KeyRelease)
	       ? event->xkey.time :
	       event && (event->type == ButtonPress ||
			 event->type == ButtonRelease)
	       ? event->xbutton.time :
	       XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))));

  x = layer_event->x;
  y = layer_event->y;
  fe_DisownSelection (context, time, False);
  LO_StartSelection (context, x, y, layer);

  click_x = x;
  click_y = y;
  moving = False;

#ifdef DEBUG
  if (last_armed_xref)
    fprintf (stderr,
	     "%s: ArmLink() invoked twice without intervening DisarmLink()?\n",
	     fe_progname);
#endif

  last_armed_xref = xref;
  if (xref)
    {
      LO_HighlightAnchor (context, last_armed_xref, True);
      last_armed_xref_highlighted_p = True;
    }
  else
    {
      last_armed_xref_highlighted_p = False;
    }

  if (CONTEXT_DATA (context)->save_next_mode_p)
    {
      if (! xref)
	{
	  XBell (XtDisplay (CONTEXT_WIDGET(context)), 0);
	  CONTEXT_DATA (context)->save_next_mode_p = False;
	  fe_SetCursor (context, False);
	  XFE_Progress (context,
			fe_globalData.click_to_save_cancelled_message);
	}
    }
}

static void
fe_disarm_link_action_by_context(MWContext* context, XEvent *event,
				 String *av, Cardinal *ac);

/* Invoked via a translation on <Btn1Up>
 */
static void
fe_disarm_link_action (Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  fe_disarm_link_action_by_context(context, event, av, ac);
}

static void
fe_disarm_link_action_by_context(MWContext* context, XEvent *event,
				 String *av, Cardinal *ac)
{
#ifndef LAYERS_SEPARATE_DISARM
  Time time;
#endif /* LAYERS_SEPARATE_DISARM */

  XP_ASSERT (context);

  if (!context) return;

  if (auto_scroll_timer)
    {
      XtRemoveTimeOut (auto_scroll_timer);
      auto_scroll_timer = 0;
    }

  fe_UserActivity (context);

#ifdef LAYERS_SEPARATE_DISARM

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_DISARM_LINK;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_DISARM_LINK);
  layer_event.fe_event_size = sizeof(fe_event);
#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_MOUSE_BUTTON_UP;
  layer_event.which = event->xbutton.button;
  layer_event.modifiers = xfeToLayerModifiers(event->xbutton.state);

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;

          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_disarm_link_action_for_layer(context, NULL, &layer_event);
      }
}


/* Layer specific actions.  fe_disarm_link_action() */
void
fe_disarm_link_action_for_layer(MWContext *context, CL_Layer *layer,
                                CL_Event *layer_event)
{
  Time time;
  /* Note that the av and ac parameters that were passed to 
	 fe_disarm_link_action() can be obtained from the fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event->event;
#else
  XEvent *event = fe_event_extract(fe_event,NULL,NULL,NULL);
#endif

  if (context->compositor)
      CL_GrabMouseEvents(context->compositor, NULL);
#endif /* LAYERS_SEPARATE_DISARM */

  time = (event && (event->type == KeyPress ||
		    event->type == KeyRelease)
	       ? event->xkey.time :
	       event && (event->type == ButtonPress ||
			 event->type == ButtonRelease)
	       ? event->xbutton.time :
	       XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))));
  LO_EndSelection (context);
  fe_OwnSelection (context, time, False);

  if (last_armed_xref)
    {
      LO_HighlightAnchor (context, last_armed_xref, False);
    }

  last_armed_xref = 0;
  last_armed_xref_highlighted_p = False;
}

/* Invoked via a translation on <Btn1Motion>
 */
static void
fe_disarm_link_if_moved_action (Widget widget, XEvent *event,
				String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  XP_ASSERT (context);
  if (!context) return;

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_DISARM_LINK_IF_MOVED;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_DISARM_LINK_IF_MOVED);
  layer_event.fe_event_size = sizeof(fe_event);
#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_MOUSE_MOVE;
  layer_event.which = 0;
  layer_event.modifiers=0;
  
  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;
          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_disarm_link_if_moved_action_for_layer(context, NULL,
                                                   &layer_event);
      }
}


/* Layer specific actions.  fe_disarm_link_if_moved action() */
void
fe_disarm_link_if_moved_action_for_layer(MWContext *context, CL_Layer *layer,
                                         CL_Event *layer_event)
{
  LO_Element *xref;
  Boolean same_xref;
  unsigned long x, y;
  /* Note that the av and ac parameters that were passed to 
	 fe_disarm_link_if_moved_action() can be obtained from the 
	 fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event->event;
#else
  XEvent *event = fe_event_extract(fe_event,NULL,NULL,NULL);
#endif

  xref = fe_anchor_of_action (context, layer_event, layer);

  x = layer_event->x;
  y = layer_event->y;

  same_xref = (last_armed_xref && xref &&
	       fe_url_of_xref (context, last_armed_xref, x, y) ==
	       fe_url_of_xref (context, xref, x, y));

  if (!moving &&
      (x > click_x + CONTEXT_DATA (context)->hysteresis ||
       x < click_x - CONTEXT_DATA (context)->hysteresis ||
       y > click_y + CONTEXT_DATA (context)->hysteresis ||
       y < click_y - CONTEXT_DATA (context)->hysteresis))
    moving = True;

  if (moving &&
      !CONTEXT_DATA (context)->clicking_blocked &&
      !CONTEXT_DATA (context)->synchronous_url_dialog)
    {
      int x_region, y_region;

      if (event->xmotion.x < 0)
	x_region = -1;
      else if (event->xmotion.x > CONTEXT_DATA (context)->scrolled_width)
	x_region = 1;
      else
	x_region = 0;

      if (event->xmotion.y < 0)
	y_region = -1;
      else if (event->xmotion.y > CONTEXT_DATA (context)->scrolled_height)
	y_region = 1;
      else
	y_region = 0;

      if (last_armed_xref && last_armed_xref_highlighted_p)
	{
	  LO_HighlightAnchor (context, last_armed_xref, False);
	  last_armed_xref = 0;
	  last_armed_xref_highlighted_p = False;
	  fe_SetCursor (context, False);
	}
      LO_ExtendSelection (context, x, y);

      fe_auto_scroll_x = x_region;
      fe_auto_scroll_y = y_region;

      if ((x_region != 0 || y_region != 0) && !auto_scroll_timer)
	{
	  /* turn on the timer */
	  fe_auto_scroll_timer (context, 0);
	}
      else if ((x_region == 0 && y_region == 0) && auto_scroll_timer)
	{
	  /* cancel the timer */
	  XtRemoveTimeOut (auto_scroll_timer);
	  auto_scroll_timer = 0;
	}
    }

  if (!last_armed_xref)
    return;

  if (!same_xref && last_armed_xref_highlighted_p)
    {
      LO_HighlightAnchor (context, last_armed_xref, False);
      last_armed_xref_highlighted_p = False;
    }
  else if (same_xref && !last_armed_xref_highlighted_p)
    {
      LO_HighlightAnchor (context, last_armed_xref, True);
      last_armed_xref_highlighted_p = True;
    }
}

typedef struct fe_mocha_closure {
  long x;
  long y;
  LO_FormSubmitData *data;
  LO_AnchorData *anchor_data;
  URL_Struct *url;
  XEvent *event;
  CL_Event *layer_event;
  Boolean save_p;
  Boolean other_p;
  Boolean image_delayed_p;
  Boolean free_element_p;
  String *av;
  Cardinal *ac;
} fe_mocha_closure;

static Boolean fe_FinishHREF (MWContext *context, 
			      LO_Element *element, 
			      fe_mocha_closure *closure);

static void fe_ParseHREF (MWContext *context,
			  LO_Element *element,
			  fe_mocha_closure *closure);

static void
fe_mocha_handle_submit (MWContext *context, LO_Element *element, int32 event,
			void *closure, ETEventStatus status)
{
  fe_mocha_closure *mocha_closure = (fe_mocha_closure *) closure;
  LO_FormSubmitData *data = NULL;
  char *action = NULL;

  if (status != EVENT_OK)  {
    XP_FREE (mocha_closure);
    return;
  }

  data = LO_SubmitImageForm (context, &element->lo_image,
			     mocha_closure->x, 
			     mocha_closure->y);
  if (data == NULL) {
    XP_FREE (mocha_closure);
    return;	/* XXX ignored anyway? what is right? */
  }
    
  action = (char *) data->action;
  mocha_closure->data = data;
  mocha_closure->url = NET_CreateURLStruct (action, FALSE);
  NET_AddLOSubmitDataToURLStruct (data, mocha_closure->url);
  fe_FinishHREF (context, element, mocha_closure);

  if (mocha_closure->event) XP_FREE (mocha_closure->event);
  XP_FREE (mocha_closure);
}

void fe_disarm_last_xref(void)
{
  XEvent* xevent;

#ifdef LAYERS_FULL_FE_EVENT
  xevent=&(last_armed_xref_closure_for_disarm.xevent);
#else
  xevent=fe_event_extract(&(last_armed_xref_closure_for_disarm.fe_event),
			  NULL,NULL,NULL);
#endif

  fe_disarm_link_action_by_context(last_armed_xref_closure_for_disarm.context,
			    xevent,
			    NULL,NULL);
}

static void
fe_mocha_handle_click (MWContext *context, LO_Element *element, int32 event,
		       void *closure, ETEventStatus status)
{
  fe_mocha_closure *mocha_closure = (fe_mocha_closure *) closure;

  if (status != EVENT_OK)
    {
      if (status==EVENT_PANIC)
	{
	  last_armed_xref = 0;
	  last_armed_xref_highlighted_p = False;
	}
      else
	fe_disarm_last_xref();
      if (mocha_closure)
	XP_FREE (mocha_closure);
      return;
    }

  fe_disarm_last_xref();

  /* mocha may have swapped our url - call the parsing code now. */
  fe_ParseHREF (context, element, mocha_closure);
  fe_FinishHREF (context, element, mocha_closure);

  if (mocha_closure->free_element_p) XP_DELETE (element);
  if (mocha_closure->event) XP_FREE (mocha_closure->event);
  XP_FREE (mocha_closure);
}


/* Ok. Now we have to delay the parsing of the anchor info until
 * after mocha has had a chance to change anything it wants.
 */

static void fe_ParseHREF (MWContext *context,
			  LO_Element *xref,
			  fe_mocha_closure *mocha_closure)
{
  if (xref->type == LO_IMAGE)
    {
      if (xref->lo_image.is_icon &&
	  xref->lo_image.icon_number == IL_IMAGE_DELAYED)
	{
	  long width, height;

	  fe_IconSize(IL_IMAGE_DELAYED, &width, &height);
	  if (xref->lo_image.alt &&
	      xref->lo_image.alt_len &&
	      (mocha_closure->layer_event->x > xref->lo_image.x +
	       xref->lo_image.x_offset + 1 + 4 + width))
		{
		  char *anchor = NULL;
	  
		  if (xref->lo_image.anchor_href)
		    {
		      anchor = (char *) xref->lo_image.anchor_href->anchor;
		      mocha_closure->anchor_data = xref->lo_image.anchor_href;
		    }
		  mocha_closure->url = NET_CreateURLStruct (anchor, FALSE);
		}
	      else
		{
		  mocha_closure->image_delayed_p = True;
		  mocha_closure->url = NET_CreateURLStruct (
					  (char *) xref->lo_image.image_url,
					  FALSE);
		}
	}
      else if (xref->lo_image.image_attr->usemap_name != NULL)
	/* If this is a usemap image, map the x,y to a url */
	{
	  char *anchor = NULL;
	  LO_AnchorData *anchor_href;
	  
	  anchor_href = LO_MapXYToAreaAnchor(context,
					     (LO_ImageStruct *)xref, 
					     mocha_closure->x, 
					     mocha_closure->y);
	  if (anchor_href)
	    {
	      mocha_closure->anchor_data = anchor_href;
	      anchor = (char *) anchor_href->anchor;
	    }

	  /* The user clicked; tell libmocha */
	  mocha_closure->url = NET_CreateURLStruct (anchor, FALSE);
	}
      else if (xref->lo_image.image_attr->attrmask & LO_ATTR_ISMAP)
	/* If this is an image map, append ?x?y to the URL. */
	{
	  char *anchor = NULL;
	  int x = mocha_closure->x;
	  int y = mocha_closure->y;
      
	  if (xref->lo_image.anchor_href)
	    {
	      anchor = (char *) xref->lo_image.anchor_href->anchor;
	      mocha_closure->anchor_data = xref->lo_image.anchor_href;
	    }
	  mocha_closure->url = NET_CreateURLStruct (anchor, FALSE);
	  NET_AddCoordinatesToURLStruct (mocha_closure->url, 
					 ((x < 0) ? 0 : x),
					 ((y < 0) ? 0 : y));
	}
      else
	{
	  char *anchor = NULL;
	  
	  if (xref->lo_image.anchor_href)
	    {
	      anchor = (char *) xref->lo_image.anchor_href->anchor;
	      mocha_closure->anchor_data = xref->lo_image.anchor_href;
	    }
	  mocha_closure->url = NET_CreateURLStruct (anchor, FALSE);
	}
    }
  else if (xref->type == LO_TEXT)
    {
      char *anchor = NULL;
      
      if (xref->lo_text.anchor_href)
	{
	  anchor = (char *) xref->lo_text.anchor_href->anchor;
	  mocha_closure->anchor_data = xref->lo_text.anchor_href;
	}
      mocha_closure->url = NET_CreateURLStruct (anchor, FALSE);
    }
  else if (xref->type == LO_EDGE)
    {
      /* Nothing to do here - should we ever get here? ### */
      ;
    }
  else
    {
      XP_ASSERT (False);
    }
}


Boolean fe_HandleHREF (MWContext *context,
		       LO_Element *xref,
                       Boolean save_p,
		       Boolean other_p,
		       CL_Event *layer_event,
		       CL_Layer *layer) /* in: may be NULL */
{
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event->event;
  String *av = fe_event->av;
  Cardinal *ac = fe_event->ac;
#else
  String *av;
  Cardinal *ac;
  XEvent *event = fe_event_extract(fe_event,&av,&ac,NULL);
#endif
  /*MWContext *top = NULL;*/
  /*LO_AnchorData *anchor_data = NULL;*/
  /*LO_FormSubmitData *data = NULL;*/
  fe_mocha_closure *mocha_closure = XP_NEW_ZAP(fe_mocha_closure);

  /* setup the mocha callback data */
  mocha_closure->save_p = save_p;
  mocha_closure->other_p = other_p;
  mocha_closure->event = XP_NEW_ZAP (XEvent);
  XP_MEMCPY (mocha_closure->event, event, sizeof (XEvent));
  mocha_closure->layer_event = XP_NEW_ZAP (CL_Event);
  XP_MEMCPY (mocha_closure->layer_event, layer_event, sizeof (CL_Event));
  /* mocha_closure->av = av; */
  /* mocha_closure->ac = ac; */

  if (xref->type == LO_IMAGE)
    {
      long cx = layer_event->x;
      long cy = layer_event->y;
      long ix = xref->lo_image.x + xref->lo_image.x_offset;
      long iy = xref->lo_image.y + xref->lo_image.y_offset;
      long x = cx - ix - xref->lo_image.border_width;
      long y = cy - iy - xref->lo_image.border_width;

      /* store these away */
      mocha_closure->x = x;
      mocha_closure->y = y;

      if (xref->lo_image.image_attr->attrmask & LO_ATTR_ISFORM)
	/* If this is a form image, submit it... */
	{
	  {
	    JSEvent *event = XP_NEW_ZAP(JSEvent);
		
	    event->type = EVENT_SUBMIT;
		
	    ET_SendEvent (context, (LO_Element *) &xref->lo_image, 
			  event, fe_mocha_handle_submit,
			  mocha_closure);
	    return True;
	  }
	}
      else if (xref->lo_image.image_attr->usemap_name != NULL)
	{
	  LO_AnchorData *anchor_data;

	  anchor_data = LO_MapXYToAreaAnchor(context, (LO_ImageStruct *)xref, x, y);
	  if (anchor_data)
	    {
	      /* Imagemap area pretend to be links for JavaScript. */
	      mocha_closure->free_element_p = True;
	      xref = (LO_Element *) XP_NEW_ZAP(LO_Element);
	      xref->lo_text.type = LO_TEXT;
	      xref->lo_text.anchor_href = anchor_data;

	      /* We use the text of the element to determine if it is still
	         valid later so give the dummy text struct's text a value. */
	      if (anchor_data->anchor)
		xref->lo_text.text = anchor_data->anchor;
	    }
	}
    }
  {
    JSEvent *jsevent = XP_NEW_ZAP(JSEvent);

    jsevent->type = EVENT_CLICK;

    jsevent->x = layer_event->x;
    jsevent->y = layer_event->y;
    if (layer) {
      jsevent->docx = layer_event->x + CL_GetLayerXOrigin(layer);
      jsevent->docy = layer_event->y + CL_GetLayerYOrigin(layer);
    }
    else {
      jsevent->docx = layer_event->x;
      jsevent->docy = layer_event->y;
    }
    jsevent->which = layer_event->which;
    jsevent->modifiers = layer_event->modifiers;
    jsevent->screenx = event->xbutton.x_root;
    jsevent->screeny = event->xbutton.y_root;
    ET_SendEvent (context, (LO_Element *) xref,
		  jsevent, fe_mocha_handle_click,
		  mocha_closure);
    return True;
  }

  return False;
}

static Boolean
fe_FinishHREF (MWContext *context, 
	       LO_Element *element, 
	       fe_mocha_closure *mocha_closure)
{
  MWContext *top = NULL;
  URL_Struct *url		= mocha_closure->url;
  LO_FormSubmitData *data	= mocha_closure->data;
  LO_AnchorData *anchor_data	= mocha_closure->anchor_data;
  XEvent *event			= mocha_closure->event;
  Boolean image_delayed_p	= mocha_closure->image_delayed_p;
  Boolean other_p		= mocha_closure->other_p;
  Boolean save_p		= mocha_closure->save_p;
  String *av			= mocha_closure->av;
  Cardinal *ac			= mocha_closure->ac;
  Boolean link_selected_p = False;

    {

		/* Add the referer to the URL. */
		History_entry *he = SHIST_GetCurrent (&context->hist);
		if (url->referer) {
			free (url->referer);
			url->referer = 0;
		}
		
		url->referer = fe_GetURLForReferral(he);

#ifdef MOZ_MAIL_NEWS
      if (MSG_NewWindowProhibited (context, url->address))
	{
	  XP_ASSERT (!MSG_NewWindowRequired (context, url->address));
	  other_p = False;
	}
      else if (MSG_NewWindowRequired (context, url->address))
	{
	  MWContext *new_context = 0;
	  XP_ASSERT (!MSG_NewWindowProhibited (context, url->address));

	  /* If the user has clicked left (the "open in this window" gesture)
	     on a link in a window which is not able to display that kind of
	     URL (like, clicking on an HTTP link in a mail message) then we
	     find an existing context of an appropriate type (in this case,
	     a browser window) to display it in.  If there is no window of
	     the appropriate type, of if they had used the `new window'
	     gesture, then we create a new context of the apropriate type.
	   */
	  if (other_p)
	    new_context = 0;
	  else if (MSG_RequiresMailWindow (url->address))
	    new_context = XP_FindContextOfType (context, MWContextMail);
	  else if (MSG_RequiresNewsWindow (url->address))
	    new_context = XP_FindContextOfType (context, MWContextNews);
	  else if (MSG_RequiresBrowserWindow (url->address))
	    {
		  /* Be sure to skip nethelps when looking for context */
		  new_context = fe_FindNonCustomBrowserContext(context);
	    }

	  if (!new_context)
	    other_p = True;
	  else
	    {
	      if (context != new_context)
		/* If we have picked an existing context that isn't this
		   one in which to display this document, make sure that
		   context is uniconified and raised first. */
		XMapRaised(XtDisplay(CONTEXT_WIDGET(new_context)),
			   XtWindow(CONTEXT_WIDGET(new_context)));
	      context = new_context;
	    }
	}
#endif  /* MOZ_MAIL_NEWS */

      /* Regardless of how we got here, we need to make sure and
       * and use the toplevel context if our current one is a grid
       * cell. Grid cell's don't have chrome, and our new window
       * should.
       */
      top = XP_GetNonGridContext(context);

      if (save_p)
	{
	  fe_SaveURL (context, url);
	}
      /*
       * definitely get here from middle-click, are there other ways?
       */
      else if (other_p)
	{
	  /* Need to clear it right away, or it doesn't get cleared because
	     we blast last_armed_xref from fe_ClearArea...  Sigh. */
	  fe_disarm_link_action (CONTEXT_DATA (context)->drawing_area, event, av, ac);

	  /*
	   * When we middle-click for a new window we need
	   * to ignore all window targets.  It is easy to ignore
	   * the target on the anchor here, but we also need to
	   * ignore other targets that might be set later.  We do
	   * this by setting window_target in the URL struct, but
	   * not setting a window name in the context.
	   */
	  url->window_target = strdup ("");

	  /*
	   * We no longer want to follow anchor targets from middle-clicks.
	   */
	  fe_MakeWindow (XtParent (CONTEXT_WIDGET (top)), top,
			     url, NULL, MWContextBrowser, FALSE);
	}
      else if (image_delayed_p)
        {
          fe_LoadDelayedImage (context, url->address);
          NET_FreeURLStruct (url);
        }
      /*
       * Else a normal click on a link.
       * Follow that link in this window.
       */
      else
	{
	  /*
	   * If this link was targetted to a name window we need to either
	   * open it in that window (if it exists) or create a new window
	   * to open this link in (and assign the name to).
	   *
	   * Ignore targets for ComposeWindow urls.
	   */
	  if ( ((anchor_data)&&(anchor_data->target))
#ifdef MOZ_MAIL_NEWS
		&& !MSG_RequiresComposeWindow(url->address)
#endif
	  )
	    {
		MWContext *target_context = XP_FindNamedContextInList(context,
						(char *)anchor_data->target);
		/*
		 * If we copy the real target it, it will get processed
		 * again at parse time.  This is bad, because magic names
		 * like _parent return different values each time.
		 * So if we put the magic empty string here, it prevents
		 * us being overridden later, while not causing reprocessing.
		 */
		url->window_target = strdup ("");
		/*
		 * We found the named window, open this link there.
		 */
		if (target_context)
		  {
		    fe_GetURL (target_context, url, FALSE);
		  }
		/*
		 * No such named window, create one and open the link there.
		 */
		else
		  {
		    fe_MakeWindow (XtParent (CONTEXT_WIDGET (top)), top,
				   url, (char *)anchor_data->target,
				   MWContextBrowser, FALSE);
		  }
	    }
	  /*
	   * Else no target, just follow the link in this window.
	   */
	  else
	    {
	      fe_GetURL (context, url, FALSE);
	    }
	}

      if (data)
	LO_FreeSubmitData (data);

      link_selected_p = True;
    }
    return link_selected_p;
}

/* Invoked via a translation on <Btn1Up>
 */
static void
fe_activate_link_action (Widget widget, XEvent *event,
			 String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  XP_ASSERT (context);
  if (!context) return;

  fe_NeutralizeFocus (context);

  if (auto_scroll_timer)
    {
      XtRemoveTimeOut (auto_scroll_timer);
      auto_scroll_timer = 0;
    }

  fe_UserActivity (context);

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_ACTIVATE_LINK;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_ACTIVATE_LINK);
  layer_event.fe_event_size = sizeof(fe_event);
#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_MOUSE_BUTTON_UP;
  layer_event.which = event->xbutton.button;
  layer_event.modifiers = xfeToLayerModifiers(event->xbutton.state);

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;

          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_activate_link_action_for_layer(context, NULL, &layer_event);
      }
}


/* Layer specific actions.  fe_activate_link_action() */
void
fe_activate_link_action_for_layer(MWContext *context, CL_Layer *layer,
                                  CL_Event *layer_event)
{
  LO_Element *xref;
  Boolean other_p = False;
  Boolean save_p = False;
  Boolean link_selected_p = False;
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  String *av = fe_event->av;
  Cardinal *ac = fe_event->ac;
#else
  String *av;
  Cardinal *ac;
  XEvent* event=fe_event_extract(fe_event,&av,&ac,NULL);
#endif

  if (context->compositor)
      CL_GrabMouseEvents(context->compositor, NULL);

  xref = fe_anchor_of_action (context, layer_event, layer);

  if (*ac > 2)
    fprintf (stderr,
			 XP_GetString(XFE_LAY_TOO_MANY_ARGS_TO_ACTIVATE_LINK_ACTION),
			 fe_progname,*ac);
  else if (*ac == 1 && !strcmp ("new-window", av[0]))
    other_p = True;
  else if (*ac == 1 && !strcmp ("save-only", av[0]))
    save_p = True;
  else if (*ac > 0)
    fprintf (stderr,
			 XP_GetString(XFE_LAY_UNKNOWN_PARAMETER_TO_ACTIVATE_LINK_ACTION),
	     fe_progname, av[0]);

  if (CONTEXT_DATA (context)->save_next_mode_p)
    {
      save_p = True;
      CONTEXT_DATA (context)->save_next_mode_p = False;
    }

  /* Turn off the selection cursor.  It'll be updated again at next motion. */
  fe_SetCursor (context, False);

  if (   /* If a selection was made, don't follow the link. */
         (LO_HaveSelection (context))
      || CONTEXT_DATA (context)->clicking_blocked
      || CONTEXT_DATA (context)->synchronous_url_dialog
      || (!xref)
      || ((last_armed_xref) && (xref != last_armed_xref))
      )
    {
      fe_disarm_link_action_by_context(context,event,NULL,NULL);

	  /*  If (1) there was no link and
       *     (2) there was no selection and
	   *	 (3) mouse button 2 was pressed and
	   *	 (4) mouse button was released and
	   *	 (5) this is a browser context
	   *
	   *  The user clicked button 2 on nothing.
	   *
	   *  Try to do the primary selection magic.
	   */
	   	
	  if (!xref && 
		  !LO_HaveSelection (context) &&
		  layer_event &&
		  (layer_event->which == 2) &&
		  (layer_event->type == CL_EVENT_MOUSE_BUTTON_UP) &&
		  (context->type == MWContextBrowser) &&
		  CONTEXT_WIDGET(context))
	  {
		  fe_PrimarySelectionFetchURL(context);
	  }
    }
  else
    {
#ifdef LAYERS_FULL_FE_EVENT
      memcpy(&(last_armed_xref_closure_for_disarm.xevent),
	     event,
	     sizeof(XEvent));
#else
      last_armed_xref_closure_for_disarm.fe_event=(*fe_event);
#endif
      last_armed_xref_closure_for_disarm.context=context;

      link_selected_p = fe_HandleHREF (context, xref, save_p, other_p,
				       layer_event, layer);
    }

/* DONT ACCESS context AFTER A GetURL. fe_HandleHREF could do fe_GetURL. */

}

/* Invoked via a translation on <Motion>
 */
void
fe_describe_link_action (Widget widget, XEvent *event,
			 String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

/*   XP_ASSERT (context); */
  if (!context) return;

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_DESCRIBE_LINK;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_DESCRIBE_LINK);
  layer_event.fe_event_size = sizeof(fe_event);
#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_MOUSE_MOVE;
  layer_event.which = 0;
  layer_event.modifiers=0;

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;
          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_describe_link_action_for_layer(context, NULL, &layer_event);
      }
}

static void
fe_mouse_over_callback(MWContext * context, LO_Element * lo_element, int32 event,
		      void * pObj, ETEventStatus status)
{

	switch(status) {
	case EVENT_OK:
#ifdef DEBUG_spence
		printf ("fe_mouse_over_cb: event ok\n");
#endif
		break;
	case EVENT_PANIC:
		/* backend says don't do anything */
#ifdef DEBUG_spence
		printf ("fe_mouse_over_cb: event panic!\n");
#endif
		break;
    default:
	  {
/*  		char *url = NULL; */
#ifdef DEBUG_spence
		printf ("fe_mouse_over_cb: event !ok; we'll set the status bar\n");
#endif
#if 0
		/* backend didn't set the status bar, so we'll do it */
		if (event == EVENT_MOUSEOVER) {
			if (lo_element) {
				url = (char *) lo_element->lo_text.anchor_href->alt;
				if (url == NULL)
					url = (char *) lo_element->lo_text.anchor_href->anchor;
			}
			if (url)
				fe_MidTruncatedProgress (context, url);
		}
#endif /* 0 */
		break;
	  }
	} /* end switch */

	/* Free the temporary dummy layout element. */
	XP_FREE(lo_element);
}

/* Layer specific actions.  fe_describe_link_action() */
void
fe_describe_link_action_for_layer(MWContext *context, CL_Layer *layer,
                                  CL_Event *layer_event)
{
  static XP_Bool m_isImage = False;
  MWContext *top = XP_GetNonGridContext (context);
  LO_Element *xref;
  LO_AnchorData *anchor_data = NULL;
  unsigned long x, y;
  long ix, iy, mx, my;

  /* Note that the av and ac parameters that were passed to 
	 fe_describe_link_action() can be obtained from the fe_event structure. */

  xref = fe_anchor_of_action (context, layer_event, layer);

  x = layer_event->x;
  y = layer_event->y;

  {
	  static LO_Element *m_lastLE = NULL;

	  LO_Element *le = LO_XYToElement (context, x, y, layer);

	  if (le && le->type == LO_IMAGE) {
		  /* In image 
		   */
		  if (!m_isImage) {
			  /* Enter image
			   */
			  fe_HTMLViewTooltipsEH(context, layer,	layer_event, 1);
		  }/* if */


		  m_isImage = True;
	  }/* if */
	  else {
		  if (m_isImage) {
			  /* Leave image
			   */
			  fe_HTMLViewTooltipsEH(context, layer,	layer_event, 4);
		  }/* */
		  m_isImage = False;
	  }/* else */
	  m_lastLE = le;
	  /*
	  XDBG(printf("\n fe_describe_link_action_for_layer, le->type=%d %s\n", 
				  le?le->type:-10,
				  (le && le->type==LO_IMAGE)?"-->>LO_IMAGE":""));
				  */
  }
  if (xref == NULL || xref != last_documented_xref  ||
      (last_documented_xref && (last_documented_xref->type == LO_IMAGE)&&
       (last_documented_xref->lo_image.image_attr->usemap_name != NULL)))
    {
      char *url = (xref ? fe_url_of_xref (context, xref, x, y) : 0);
      anchor_data = NULL;
      if (xref) {
		  if (last_documented_xref != xref && xref->type == LO_TEXT)
			  anchor_data = xref->lo_text.anchor_href;
		  else if (xref->type == LO_IMAGE)
			  if (xref->lo_image.image_attr->usemap_name != NULL) {
				  /* Image map */
				  ix = xref->lo_image.x + xref->lo_image.x_offset;
				  iy = xref->lo_image.y + xref->lo_image.y_offset;
				  mx = x - ix - xref->lo_image.border_width;
				  my = y - iy - xref->lo_image.border_width;
				  anchor_data =
					  LO_MapXYToAreaAnchor(context, (LO_ImageStruct *)xref, mx, my);
			  }
			  else if (last_documented_xref != xref)
				  anchor_data = xref->lo_image.anchor_href;
      }
      
      /* send mouse out mocha event only if we have left a link.
       * conditions are :
       *  i) left a link to go to a non-link
       * ii) left a link to go to another link
       * iii) Moving around inside an image
       * Note: Mouse Out must happen before mouse over.
       */
      if (last_documented_anchor_data && last_documented_xref
		  && last_documented_xref_context)
		  if (last_documented_anchor_data != anchor_data) {
			  JSEvent *event;
			  LO_Element *dummy_xref = (LO_Element *) XP_NEW_ZAP (LO_Element);

			  TRACEMSG (("sending MouseOut\n"));

			  dummy_xref->lo_text.type = LO_TEXT;

			  /* this is problematic -- what if the anchor has been destroyed? */
			  dummy_xref->lo_text.anchor_href = last_documented_anchor_data;
			  dummy_xref->lo_text.text = dummy_xref->lo_text.anchor_href->anchor;

			  event = XP_NEW_ZAP(JSEvent);
			  event->type = EVENT_MOUSEOUT;
			  event->x = layer_event->x;
			  event->y = layer_event->y;
			  {
			    fe_EventStruct* e=(fe_EventStruct*)layer_event->fe_event;
			    event->screenx=e->compressedEvent.pos.root.x;
			    event->screeny=e->compressedEvent.pos.root.y;
			  }
			  if (layer) {
			    event->docx = layer_event->x + CL_GetLayerXOrigin(layer);
			    event->docy = layer_event->y + CL_GetLayerYOrigin(layer);
			  }
			  else {
			    event->docx = layer_event->x;
			    event->docy = layer_event->y;
			  }

			  if (m_isImage) {
				  fe_HTMLViewTooltipsEH(context, layer,	layer_event, 2);
			  }/* if */
			  
#ifdef DEBUG_spence
			  printf ("Sending MouseOut\n");
#endif
			  ET_SendEvent (last_documented_xref_context, dummy_xref,
							event, fe_mouse_over_callback, NULL);
		  }

      if (CONTEXT_DATA (context)->active_url_count == 0) {
		  /* If there are transfers in progress, don't document the URL under
			 the mouse, since that message would interfere with the transfer
			 messages.  Do change the cursor, however. */
		  XP_Bool used = False;
		  if (anchor_data) {
			  if (anchor_data != last_documented_anchor_data) {
				  JSEvent *event;
				  LO_Element *dummy_xref = (LO_Element *) XP_NEW_ZAP (LO_Element);

				  XP_MEMSET (dummy_xref, 0, sizeof (LO_Element));

				  dummy_xref->lo_text.type = LO_TEXT;
				  dummy_xref->lo_text.anchor_href = anchor_data;

				  /* we use the text of the element to determine if it is still
					 valid later so give the dummy text struct's text a value.
					 */
				  dummy_xref->lo_text.text = anchor_data->anchor;
				  
				  /* just tell mocha - nothing else to do? */
				  event = XP_NEW_ZAP(JSEvent);
				  event->type = EVENT_MOUSEOVER;

				  /* get a valid layer id */
				  event->layer_id = LO_GetIdFromLayer (context, layer);

				  event->x = layer_event->x;
				  event->y = layer_event->y;
				  {
				    fe_EventStruct* e=(fe_EventStruct*)layer_event->fe_event;
				    event->screenx=e->compressedEvent.pos.root.x;
				    event->screeny=e->compressedEvent.pos.root.y;
				  }

				  if (layer) {
				    event->docx = layer_event->x + CL_GetLayerXOrigin(layer);
				    event->docy = layer_event->y + CL_GetLayerYOrigin(layer);
				  }
				  else {
				    event->docx = layer_event->x;
				    event->docy = layer_event->y;
				  }

				  if (m_isImage) {
					  fe_HTMLViewTooltipsEH(context, layer,	layer_event, 3);
				  }/* if */
#ifdef DEBUG_spence
				  printf ("Sending MouseOver\n");
#endif
				  ET_SendEvent (context, dummy_xref, event,
								fe_mouse_over_callback, NULL);
			  }
			  else
				  /* Dont update url too as we haven't moved to a new AREA */
				  used = True;
		  } 
#if 0
		  else {
			  printf ("anchor_data == NULL\n");
		  }
#endif

	if (!used)
		fe_MidTruncatedProgress (context, (xref ? url : ""));
      }
	  
      last_documented_xref_context = context;
      last_documented_xref = xref;
      last_documented_anchor_data = anchor_data;

      fe_SetCursor (top, !!xref);
    }
}

/* Invoked via a translation on <Btn3Down>
 */
void
fe_extend_selection_action (Widget widget, XEvent *event,
			    String *av, Cardinal *ac)
{
  MWContext *context = fe_MotionWidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  XP_ASSERT (context);
  if (!context) return;

  if (auto_scroll_timer)
    {
      XtRemoveTimeOut (auto_scroll_timer);
      auto_scroll_timer = 0;
    }

  fe_UserActivity (context);

  fe_NeutralizeFocus (context);

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_EXTEND_SELECTION;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_EXTEND_SELECTION);
  layer_event.fe_event_size = sizeof(fe_event);
#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_MOUSE_BUTTON_DOWN;
  layer_event.which = event->xbutton.button;
  layer_event.modifiers = xfeToLayerModifiers(event->xbutton.state);

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;
          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_extend_selection_action_for_layer(context, NULL, &layer_event);
      }
}

/* Layer specific actions.  fe_extend_selection_action() */
void
fe_extend_selection_action_for_layer(MWContext *context, CL_Layer *layer,
                                     CL_Event *layer_event)
{
  Time time;
  unsigned long x, y;

  /* Note that the av and ac parameters that were passed to 
	 fe_extend_selection_action() can be obtained from the 
	 fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifdef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event->event;
#else
  XEvent *event = fe_event_extract(fe_event,NULL,NULL,NULL);
#endif

  time = (event && (event->type == KeyPress ||
		    event->type == KeyRelease)
	  ? event->xkey.time :
	  event && (event->type == ButtonPress ||
		    event->type == ButtonRelease)
	  ? event->xbutton.time :
	  XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))));

  x = layer_event->x;
  y = layer_event->y;

  LO_ExtendSelection (context, x, y);
  fe_OwnSelection (context, time, False);

  /* Making a selection turns off "Save Next" mode. */
  if (CONTEXT_DATA (context)->save_next_mode_p)
    {
      XBell (XtDisplay (CONTEXT_WIDGET(context)), 0);
      CONTEXT_DATA (context)->save_next_mode_p = False;
      fe_SetCursor (context, False);
      XFE_Progress (context, fe_globalData.click_to_save_cancelled_message);
    }
}


#ifdef DEBUG_francis
static void printKeyEvent(XEvent* event)
{
  if (!(   ((event->xany.type)==KeyPress)
	|| ((event->xany.type)==KeyRelease)
	)
      )
    {
      printf("{non-key event}\n");
      return;
    }

  printf("{key event:\n"
	 "\tserial==%u\n"
	 "\tsend_event==%s\n"
	 "\tdisplay==0x%x\n"
	 "\twindow==0x%x\n"
	 "\troot==0x%x\n"
	 "\tsubwindow==0x%x\n"
	 "\ttime==0x%x\n"
	 "\t(x,y)==(%d,%d)\n"
	 "\t(x_root,y_root)==(%d,%d)\n"
	 "\tstate==%d\n"
	 "\tkeycode==%d\n"
	 "\tsame_screen==%s}\n",
	 event->xkey.serial,
	 (event->xkey.send_event ? "true" : "false"),
	 event->xkey.display,
	 event->xkey.window,
	 event->xkey.root,
	 event->xkey.subwindow,
	 event->xkey.time,
	 event->xkey.x,event->xkey.y,
	 event->xkey.x_root,event->xkey.y_root,
	 event->xkey.state,event->xkey.keycode,
	 (event->xkey.same_screen ? "true" : "false")
	 );
}
#endif

static XP_Bool keyStates[65536];
static XP_Bool keyStatesInited=False;

static void keyStatesInit(void)
{
  if (keyStatesInited)
    return;
  memset(keyStates,0,sizeof(keyStates));
  keyStatesInited=True;
}

static XP_Bool keyStatesDown(int keycode)
{
  if ((keycode<0) || (keycode>=65536))
    return 0;

  keyStatesInit();

  {
    XP_Bool res=keyStates[keycode];
    keyStates[keycode]=1;
    return res;
  }
}

static XP_Bool keyStatesUp(int keycode)
{
  if ((keycode<0) || (keycode>=65536))
    return 0;

  keyStatesInit();

  {
    XP_Bool res=keyStates[keycode];
    keyStates[keycode]=0;
    return res;
  }
}

static void fe_key_up_in_text_action(Widget widget,
				     XEvent *event,
				     String *av, Cardinal *ac)
{
  MWContext *context = fe_WidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  XP_ASSERT (context);
  if (!context) return;

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_KEY_UP;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_KEY_UP);
  layer_event.fe_event_size = sizeof(fe_event);

  fe_event.data=widget;
#endif

  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_KEY_UP;
  layer_event.which = xfeKeycodeToWhich(event->xkey.keycode,
										event->xkey.state);
  layer_event.modifiers = xfeToLayerModifiers(event->xkey.state);

  if (context->compositor)
      {
          unsigned long x, y;

          fe_EventLOCoords (context, event, &x, &y);
          layer_event.x = x;
          layer_event.y = y;
          CL_DispatchEvent(context->compositor, &layer_event);
      }
  else
      {
          fe_key_up_in_text_action_for_layer(context, NULL, &layer_event);
      }
}

/* Layer specific actions.  fe_extend_selection_action() */
void
fe_key_up_in_text_action_for_layer(MWContext *context, CL_Layer *layer,
				   CL_Event *layer_event)
{
  Time time;
  unsigned long x, y;
  /* Note that the av and ac parameters that were passed to 
	 fe_extend_selection_action() can be obtained from the 
	 fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifndef LAYERS_FULL_FE_EVENT
  XEvent *event = fe_event_extract(fe_event,NULL,NULL,NULL);
  Widget widget=(Widget)fe_event->data;
#endif

  time = (event && (event->type == KeyPress ||
		    event->type == KeyRelease)
	  ? event->xkey.time :
	  event && (event->type == ButtonPress ||
		    event->type == ButtonRelease)
	  ? event->xbutton.time :
	  XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))));

  x = layer_event->x;
  y = layer_event->y;

  {
    LO_Element* text=fe_text_of_widget(context, widget, layer);

    keyStatesUp(layer_event->which);
    
    {
      JSEvent *jsevent = (JSEvent*)XP_NEW_ZAP(JSEvent);

      jsevent->type = EVENT_KEYUP;

      jsevent->x = layer_event->x;
      jsevent->y = layer_event->y;
      if (layer) {
	jsevent->docx = layer_event->x + CL_GetLayerXOrigin(layer);
	jsevent->docy = layer_event->y + CL_GetLayerYOrigin(layer);
      }
      else {
	jsevent->docx = layer_event->x;
	jsevent->docy = layer_event->y;
      }
      jsevent->which = layer_event->which;
      jsevent->modifiers = layer_event->modifiers;
      jsevent->screenx = event->xbutton.x_root;
      jsevent->screeny = event->xbutton.y_root;
      
      ET_SendEvent (context, text,
		    jsevent,
		    NULL, NULL);
    }
  }
}

typedef struct {
  Widget widget;
  fe_EventStruct evt;
  int newInsertionPoint;
} KeydownClosure;

static int fe_textModifyVerifyCallbackInhibited=0;

int fe_isTextModifyVerifyCallbackInhibited(void)
{
  return fe_textModifyVerifyCallbackInhibited;
}

static int fe_textModifyVerifyCallbackNewInsertionPoint=-1;

static void finish_keydown(void* _closure)
{
  KeydownClosure* closure=(KeydownClosure*)_closure;
  String* av;
  Cardinal* ac;
  XEvent *event = fe_event_extract(&(closure->evt),&av,&ac,NULL);
  KeySym keysym=xfeKeycodeToWhich(event->xkey.keycode,
				  event->xkey.state);
  
  fe_textModifyVerifyCallbackInhibited++;
  if ((closure->newInsertionPoint)<0)
    XtCallActionProc(closure->widget,
		     (  (keysym==XK_Return)
		      ? "process-return"
		      : "self-insert"
		      ),
		     event,
		     av,*ac);
  else
    XmTextSetInsertionPosition(closure->widget,
			       closure->newInsertionPoint);
  fe_textModifyVerifyCallbackInhibited--;
  XP_FREE(closure);
}

static void fe_mocha_handle_keydown(MWContext* context,
				    LO_Element* element,
				    int32 _event,
				    void* closure,
				    ETEventStatus status)
{
  if (status == EVENT_OK)
    finish_keydown(closure);
  else
    XP_FREE(closure);
}

static void fe_key_down_in_text_action(Widget widget,
				       XEvent *event,
				       String *av, Cardinal *ac)
{
  MWContext *context = fe_WidgetToMWContext (widget);
  CL_Event layer_event;
  fe_EventStruct fe_event;

  XP_ASSERT (context);
  if (!context) return;

  /* Fill in FE part of layer_event. */
#ifdef LAYERS_FULL_FE_EVENT
  fe_event.event = event;
  fe_event.av = av;
  fe_event.ac = ac;
  fe_event.mouse_action = FE_KEY_DOWN;
  fe_event.data=widget;
#else
  fe_event_stuff(context,&fe_event,event,av,ac,FE_KEY_DOWN);
  fe_event.data=widget;
  layer_event.fe_event_size = sizeof(fe_event);

#endif
  layer_event.fe_event = (void *)&fe_event;

  layer_event.type = CL_EVENT_KEY_DOWN;
  layer_event.which = xfeKeycodeToWhich(event->xkey.keycode,
										event->xkey.state);
  layer_event.modifiers = xfeToLayerModifiers(event->xkey.state);

  if (context->compositor)
    {
      unsigned long x, y;

      fe_EventLOCoords (context, event, &x, &y);
      layer_event.x = x;
      layer_event.y = y;
      CL_DispatchEvent(context->compositor, &layer_event);
    }
  else
    {
      fe_key_down_in_text_action_for_layer(context, NULL, &layer_event);
    }
}

/* Layer specific actions.  fe_extend_selection_action() */
void
fe_key_down_in_text_action_for_layer(MWContext *context, CL_Layer *layer,
				     CL_Event *layer_event)
{
  Time time;
  unsigned long x, y;
  /* Note that the av and ac parameters that were passed to 
	 fe_extend_selection_action() can be obtained from the 
	 fe_event structure. */
  fe_EventStruct *fe_event = (fe_EventStruct *)layer_event->fe_event;
#ifndef LAYERS_FULL_FE_EVENT
  String *av;
  Cardinal *ac;
  XEvent *event = fe_event_extract(fe_event,&av,&ac,NULL);
  Widget widget=(Widget)fe_event->data;
#endif

  time = (event && (event->type == KeyPress ||
		    event->type == KeyRelease)
	  ? event->xkey.time :
	  event && (event->type == ButtonPress ||
		    event->type == ButtonRelease)
	  ? event->xbutton.time :
	  XtLastTimestampProcessed (XtDisplay(CONTEXT_WIDGET (context))));

  x = layer_event->x;
  y = layer_event->y;

  {
    LO_Element* text=fe_text_of_widget(context, widget, layer);
    KeydownClosure* closure=XP_NEW_ZAP(KeydownClosure);

    closure->widget=widget;
    closure->evt=(*fe_event);

#if 0
    if ((*ac)==2)
      sscanf(av[1],"%d",&(closure->newInsertionPoint));
    else
      closure->newInsertionPoint=-1;
#else
    closure->newInsertionPoint=fe_textModifyVerifyCallbackNewInsertionPoint;
#endif

    {
      JSEvent *jsevent = XP_NEW_ZAP(JSEvent);

      jsevent->type = (  keyStatesDown(layer_event->which)
		       ? EVENT_KEYPRESS
		       : EVENT_KEYDOWN
		       );

      jsevent->x = layer_event->x;
      jsevent->y = layer_event->y;
      if (layer) {
	jsevent->docx = layer_event->x + CL_GetLayerXOrigin(layer);
	jsevent->docy = layer_event->y + CL_GetLayerYOrigin(layer);
      }
      else {
	jsevent->docx = layer_event->x;
	jsevent->docy = layer_event->y;
      }
      jsevent->which = layer_event->which;
      jsevent->modifiers = layer_event->modifiers;
      jsevent->screenx = event->xbutton.x_root;
      jsevent->screeny = event->xbutton.y_root;
      
      ET_SendEvent (context, text,
		    jsevent, fe_mocha_handle_keydown,
		    closure);
    }
  }

}

void fe_textModifyVerifyCallback(Widget w,
								 XtPointer closure,
								 XtPointer call_data)
{
  if (!fe_textModifyVerifyCallbackInhibited)
    {
      XmTextVerifyCallbackStruct* cbs=(XmTextVerifyCallbackStruct*)call_data;
      XEvent* event=cbs->event;
      if (   event
		  && (event->type==KeyPress)
		  && (cbs->text)
		  && ((cbs->text->length)==1)
		  )
		{
		  cbs->doit=False;
		  fe_textModifyVerifyCallbackNewInsertionPoint=-1;
		  fe_key_down_in_text_action(w,event,NULL,NULL);
		}
	  else
		{
		  fe_textModifyVerifyCallbackInhibited++;
		  XtCallCallbacks(w,XmNmodifyVerifyCallback,call_data);
		  fe_textModifyVerifyCallbackInhibited--;
		}
    }
}

void fe_textMotionVerifyCallback(Widget w,
				 XtPointer closure,
				 XtPointer call_data)
{
  if (!fe_textModifyVerifyCallbackInhibited)
    {
      XmTextVerifyCallbackStruct* cbs=(XmTextVerifyCallbackStruct*)call_data;
      XEvent* event=cbs->event;
      if (   event
	  && (event->type==KeyPress)
	  )
	{
#if 0
	  char* buff=malloc(20);
	  String argv[3]={"motion",buff,0};
	  Cardinal argc=2;
	  sprintf(buff,"%d",cbs->newInsert);
#endif
	  cbs->doit=False;
	  fe_textModifyVerifyCallbackNewInsertionPoint=cbs->newInsert;
	  fe_key_down_in_text_action(w,event,
#if 0
				     argv,&argc
#else
				     NULL,NULL
#endif
				     );
	}
    }
}



static XtActionsRec fe_mouse_actions [] =
{
  { "ArmLink",		 fe_arm_link_action },
  { "DisarmLink",	 fe_disarm_link_action },
  { "ActivateLink",	 fe_activate_link_action },
  { "DisarmLinkIfMoved", fe_disarm_link_if_moved_action },
  { "ExtendSelection",	 fe_extend_selection_action },
  { "DescribeLink",	 fe_describe_link_action }
};

void
fe_InitMouseActions ()
{
  XtAppAddActions (fe_XtAppContext, fe_mouse_actions,
		   countof (fe_mouse_actions));
}

static XtActionsRec fe_key_actions [] =
{
  { "KeyUpInText",	 fe_key_up_in_text_action }
/*,
  { "KeyDownInText",	 fe_key_down_in_text_action }*/
};

void
fe_InitKeyActions ()
{
  XtAppAddActions (fe_XtAppContext, fe_key_actions,
		   countof (fe_key_actions));
}

static ContextFuncs _xfe_funcs = {
#define FE_DEFINE(func, returns, args) XFE##_##func,
#include "mk_cx_fn.h"
};

ContextFuncs *
fe_BuildDisplayFunctionTable(void)
{
	return &_xfe_funcs;
}

void
FE_LoadGridCellFromHistory(MWContext *context, void *hist,
			   NET_ReloadMethod force_reload)
{
  History_entry *he = (History_entry *)hist;
  URL_Struct *url;

  if (! he) return;
  url = SHIST_CreateURLStructFromHistoryEntry (context, he);
  url->force_reload = force_reload;
  fe_GetURL (context, url, FALSE);
}

void *
FE_FreeGridWindow(MWContext *context, XP_Bool save_history)
{
  LO_Element *e;
  History_entry *he;
  XP_List *hist_list;

  hist_list = NULL;
  he = NULL;
  if ((context)&&(context->is_grid_cell))
    {
      /* remove focus from this grid */
      CONTEXT_DATA (context)->focus_grid = False;

      /*
       * If we are going to save the history of this grid cell
       * we need to stuff the last scroll position into the
       * history structure, and then remove that structure
       * from its linked list so it won't be freed when the
       * context is destroyed.
       */
      if (save_history)
        {
          e = LO_XYToNearestElement (context,
                                     CONTEXT_DATA (context)->document_x,
                                     CONTEXT_DATA (context)->document_y,
                                     NULL);
          he = SHIST_GetCurrent (&context->hist);
          if (e)
            SHIST_SetPositionOfCurrentDoc (&context->hist, e->lo_any.ele_id);

	  hist_list = context->hist.list_ptr;
	  context->hist.list_ptr = NULL;
        }

      fe_DestroyContext(context);
    }
  return(hist_list);
}

void XFE_GetTextFrame(MWContext *context, LO_TextStruct *text, int32 start,
                      int32 end, XP_Rect *frame)
{
    LO_TextAttr * attr = text->text_attr;
    fe_Font font;
    int L, remaining, width, height, ascent, descent;
    char *str;
    XCharStruct overall;

    font = fe_LoadFontFromFace (context, attr, &attr->charset,
                                attr->font_face, attr->size, attr->fontmask);
    frame->left = text->x + text->x_offset;
    frame->top = text->y + text->y_offset;

  /* X is such a winner, it uses 16 bit quantities to represent all pixel
     widths.  This is really swell, because it means that if you've got
     a large font, you can't correctly compute the size of strings which
     are only a few thousand characters long.  So, when the string is more
     than N characters long, we divide up our calls to XTextExtents to
     keep the size down so that the library doesn't run out of fingers
     and toes.
   */
#define SUCKY_X_MAX_LENGTH 600

    XP_ASSERT(font);              /* Should really return FALSE on failure. */

    str = (char *) text->text;
    remaining = start;
    width = 0;
    height = 0;
    do
        {
            L = (remaining > SUCKY_X_MAX_LENGTH ? SUCKY_X_MAX_LENGTH :
                 remaining);
            FE_TEXT_EXTENTS (attr->charset, font, str, L,
                             &ascent, &descent, &overall);
            width += overall.width;
            height = MAX(height, ascent + descent);
            str += L;
            remaining -= L;
        }
    while (remaining > 0);
    frame->left += width;

    str = (char *) text->text + start;
    remaining = end - start + 1;
    width = 0;
    do
        {
            L = (remaining > SUCKY_X_MAX_LENGTH ? SUCKY_X_MAX_LENGTH :
                 remaining);
            FE_TEXT_EXTENTS (attr->charset, font, str, L,
                             &ascent, &descent, &overall);
            width += overall.width;
            height = MAX(height, ascent + descent);
            str += L;
            remaining -= L;
        }
    while (remaining > 0);
    frame->right = frame->left + width;
    frame->bottom = frame->top + height;
}

/* Display a border.  x, y, width and height specify the outer perimeter of the
   border. */
void
XFE_DisplayBorder(MWContext *context, int iLocation, int x, int y, int width,
                  int height, int bw, LO_Color *color, LO_LineStyle style) 
{
    fe_Drawable *fe_drawable = CONTEXT_DATA(context)->drawable;
    Drawable drawable = fe_drawable->xdrawable;
    Widget widget = CONTEXT_WIDGET(context);
    Display *dpy = XtDisplay(widget);

    if (bw > 0) {
        GC gc;
        XGCValues gcv;
        unsigned long flags;

        memset (&gcv, ~0, sizeof (gcv));
        gcv.function = GXcopy;
        gcv.foreground = fe_GetPixel (context, color->red, color->green,
                                      color->blue);
        gcv.line_width = bw;
        flags = GCFunction | GCForeground | GCLineWidth;
        gc = fe_GetGCfromDW (dpy, drawable, flags, &gcv,
                             fe_drawable->clip_region);

        /* Add in the layer origin. */
        x += fe_drawable->x_origin - CONTEXT_DATA(context)->document_x;
        y += fe_drawable->y_origin - CONTEXT_DATA(context)->document_y;

        switch (style) {
        case LO_SOLID:
            /* Beware: XDrawRectangle centers the line-thickness on the
               coords. */
            XDrawRectangle (dpy, drawable, gc, x + (bw / 2), y + (bw / 2),
                            width - bw, height - bw);
            break;
            
        case LO_BEVEL:
            fe_DrawShadows (context, fe_drawable, x, y, width, height, bw,
                            XmSHADOW_IN);
            break;

        default:
            break;
        }
    }
}

static void
xfe_display_image_feedback(MWContext* context, LO_ImageStruct* lo_image)
{
	Display*     display;
    fe_Drawable* fe_drawable;
	Drawable     drawable;
	XGCValues    gcv;
	GC           gc;
	int          x;
	int          y;
	unsigned     w;
	unsigned     h;
	unsigned     bw;

#ifdef EDITOR
    /*
     *    Draw selection effects.
     */
	if (EDT_IS_EDITOR(context) && /* still only for editor in 4.0? */
		(lo_image->ele_attrmask & LO_ELE_SELECTED) != 0) {

		display = XtDisplay(CONTEXT_DATA(context)->drawing_area);
		fe_drawable = CONTEXT_DATA(context)->drawable;
		drawable = fe_drawable->xdrawable;

		memset(&gcv, ~0, sizeof (gcv));
		gcv.foreground = CONTEXT_DATA(context)->fg_pixel;
		gcv.background = CONTEXT_DATA(context)->select_bg_pixel;
		gcv.line_width = 1;
		gcv.line_style = LineDoubleDash;
		gc = fe_GetGCfromDW(display, drawable,
							GCForeground|GCBackground|GCLineStyle|GCLineWidth,
							&gcv,
							fe_drawable->clip_region);

		bw = lo_image->border_width;
		x = fe_drawable->x_origin - CONTEXT_DATA(context)->document_x +
			lo_image->x + lo_image->x_offset;

		y = fe_drawable->y_origin - CONTEXT_DATA(context)->document_y +
			lo_image->y + lo_image->y_offset;
		w = lo_image->width  + bw + bw;
		h = lo_image->height + bw + bw;

		/* beware: XDrawRectangle centers the line-thickness on the coords. */
		XDrawRectangle(display, drawable,
					   gc, x, y, (w - gcv.line_width), (h - gcv.line_width));
	}
#endif /*EDITOR*/

	/*
	 *    Tab navigation.
	 */
}

/* Display feedback about a layout element e.g. editor selection, tab navigation
   highlighting, etc. */
void
XFE_DisplayFeedback(MWContext *context, int iLocation, LO_Element *element)
{
	if (element->lo_any.type == LO_IMAGE ) {
		xfe_display_image_feedback(context, (LO_ImageStruct*)element);
	}

    /* XXX Implement me. */
}

#ifndef LAYERS_FULL_FE_EVENT

static fe_EventActivateKind calcActivateKind(const String* av,
					     const Cardinal* ac,
					     fe_MouseActionEnum mouse_action)
{
  if (mouse_action!=FE_ACTIVATE_LINK)
    return fe_EventActivateKindNone;

  if (!(av && ac))
    return fe_EventActivateKindNone;

  {
    int N=*ac;

    if (N>2)
      {
	fprintf (stderr, 
			 XP_GetString(XFE_LAY_TOO_MANY_ARGS_TO_ACTIVATE_LINK_ACTION),
		 fe_progname, *ac);
	return fe_EventActivateKindNone;
      }
      
    if (N==0)
      return fe_EventActivateKindNormal;

    if (N==1)
      {
	if (!strcmp ("new-window", av[0]))
	  return fe_EventActivateKindNewWindow;

	if (!strcmp ("save-only", av[0]))
	  return fe_EventActivateKindSaveOnly;
      }
  }

  fprintf (stderr,
		   XP_GetString(XFE_LAY_UNKNOWN_PARAMETER_TO_ACTIVATE_LINK_ACTION),
	   fe_progname, av[0]);
  return fe_EventActivateKindNone;
}

static String* invertActivateKind(fe_EventActivateKind kind)
{
  static String res;

  switch (kind)
    {
    case fe_EventActivateKindSaveOnly:
      res="save-only";
      return &res;

    case fe_EventActivateKindNewWindow:
      res="new-window";
      return &res;

    case fe_EventActivateKindNone:
    case fe_EventActivateKindNormal:
    default:
      return NULL;
    }
}

void fe_event_stuff(MWContext* context,
		    fe_EventStruct* fe_event,
		    const XEvent* event,
		    const String* av,
		    const Cardinal* ac,
		    fe_MouseActionEnum mouse_action)
{
  if (!fe_event)
    return;

  fe_event->mouse_action=mouse_action;

  if (event)
    {
      switch (event->type)
	{
	case KeyPress:
	case KeyRelease:
	  if (   (mouse_action==FE_KEY_UP)
	      || (mouse_action==FE_KEY_DOWN)
	      )
	    {
	      fe_event->compressedEvent.pos.win.x=event->xkey.x;
	      fe_event->compressedEvent.pos.win.y=event->xkey.y;
	      fe_event->compressedEvent.pos.root.x=event->xkey.x_root;
	      fe_event->compressedEvent.pos.root.y=event->xkey.y_root;
	      fe_event->compressedEvent.arg.key.state=event->xkey.state;
	      fe_event->compressedEvent.arg.key.keycode=event->xkey.keycode;
	    }
	  else
	    {
	      fe_event->compressedEvent.pos.win.x=
		fe_event->compressedEvent.pos.win.y=
		  fe_event->compressedEvent.pos.root.x=
		    fe_event->compressedEvent.pos.root.y=
		      0;
	    }
	  fe_event->compressedEvent.time=event->xkey.time;
	  break;

	case ButtonPress:
	case ButtonRelease:
	  fe_event->compressedEvent.pos.win.x=event->xbutton.x;
	  fe_event->compressedEvent.pos.win.y=event->xbutton.y;
	  fe_event->compressedEvent.pos.root.x=event->xbutton.x_root;
	  fe_event->compressedEvent.pos.root.y=event->xbutton.y_root;
	  fe_event->compressedEvent.arg.button.root=event->xbutton.root;
	  fe_event->compressedEvent.time=event->xbutton.time;
	  break;

	case MotionNotify:
	  fe_event->compressedEvent.pos.win.x=event->xmotion.x;
	  fe_event->compressedEvent.pos.win.y=event->xmotion.y;
	  fe_event->compressedEvent.pos.root.x=event->xmotion.x_root;
	  fe_event->compressedEvent.pos.root.y=event->xmotion.y_root;
	  fe_event->compressedEvent.time=0;
	  break;

	default:
#ifdef DEBUG
	  fprintf(stderr,
		  "fe_event_stuff(): unsupported XEvent type %d\n",
		  event->type);
#endif
	  fe_event->compressedEvent.pos.win.x=
	    fe_event->compressedEvent.pos.win.y=
	      fe_event->compressedEvent.pos.root.x=
		fe_event->compressedEvent.pos.root.y=
		  0;
	  fe_event->compressedEvent.time=0;
	  break;
	}
      fe_event->compressedEvent.type=event->type;
    }
  else
    {
      fe_event->compressedEvent.pos.win.x=
	fe_event->compressedEvent.pos.win.y=
	  fe_event->compressedEvent.pos.root.x=
	    fe_event->compressedEvent.pos.root.y=
	      0;
      fe_event->compressedEvent.time=0;
      fe_event->compressedEvent.type=0;
    }

  fe_event->activateKind=calcActivateKind(av,ac,mouse_action);
  fe_event->data=0;

  if (context)
    fe_CacheWindowOffset(context,
			 ( fe_event->compressedEvent.pos.root.x
			  -fe_event->compressedEvent.pos.win.x
			  ),
			 ( fe_event->compressedEvent.pos.root.y
			  -fe_event->compressedEvent.pos.win.y
			  )
			 );
}

XEvent* fe_event_extract(const fe_EventStruct* fe_event,
			 String** av,
			 Cardinal** ac,
			 fe_MouseActionEnum* mouse_action)
{
  static XEvent event;
  static Cardinal zero=0;

  event.xany.display=fe_display;

  if (!fe_event)
    {
      if (av)
	(*av)=0;
      if (ac)
	(*ac)=&zero;

      if (mouse_action)
	(*mouse_action)=FE_INVALID_MOUSE_ACTION;
      return NULL;
    }

  event.type=fe_event->compressedEvent.type;

  switch (event.type)
    {
    case KeyPress:
    case KeyRelease:
      if (   (fe_event->mouse_action==FE_KEY_UP)
	  || (fe_event->mouse_action==FE_KEY_DOWN)
	  )
	{
	  event.xkey.x=fe_event->compressedEvent.pos.win.x;
	  event.xkey.y=fe_event->compressedEvent.pos.win.y;
	  event.xkey.x_root=fe_event->compressedEvent.pos.root.x;
	  event.xkey.y_root=fe_event->compressedEvent.pos.root.y;
	  event.xkey.state=fe_event->compressedEvent.arg.key.state;
	  event.xkey.keycode=fe_event->compressedEvent.arg.key.keycode;
	}
      event.xkey.time=fe_event->compressedEvent.time;
      break;
      
    case ButtonPress:
    case ButtonRelease:
      event.xbutton.x=fe_event->compressedEvent.pos.win.x;
      event.xbutton.y=fe_event->compressedEvent.pos.win.y;
      event.xbutton.x_root=fe_event->compressedEvent.pos.root.x;
      event.xbutton.y_root=fe_event->compressedEvent.pos.root.y;
      event.xbutton.root=fe_event->compressedEvent.arg.button.root;
      event.xbutton.time=fe_event->compressedEvent.time;
      break;
      
    case MotionNotify:
      event.xmotion.x=fe_event->compressedEvent.pos.win.x;
      event.xmotion.y=fe_event->compressedEvent.pos.win.y;
      event.xmotion.x_root=fe_event->compressedEvent.pos.root.x;
      event.xmotion.y_root=fe_event->compressedEvent.pos.root.y;
      break;
      
    default:
#ifdef DEBUG
      fprintf(stderr,
	      "fe_event_extract(): unsupported XEvent type %d\n",
	      event.type);
      event.type=0;
#endif
      break;
    }

  if (av)
    (*av)=invertActivateKind(fe_event->activateKind);

  if (ac)
    {
      if (av && (*av))
	{
	  static Cardinal one=1;
	  (*ac)=&one;
	}
      else
	(*ac)=&zero;
    }

  if (mouse_action)
    (*mouse_action)=fe_event->mouse_action;

  return &event;
}
#endif

extern void plonk_cancel(void);

void fe_PrimarySelectionFetchURL(MWContext * context)
{
	XP_ASSERT( context != NULL );

	if (!context ||
		LO_HaveSelection(context) ||
		(context->type != MWContextBrowser) ||
		!CONTEXT_WIDGET(context))
	{
		return;
	}

	XtGetSelectionValue(CONTEXT_WIDGET(context),
						XA_PRIMARY, 
						XA_STRING,
						fe_get_url_x_selection_cb,
						(XtPointer) context,
						CurrentTime);
}

static void
fe_get_url_x_selection_cb(Widget			w,
						  XtPointer			client_data,
						  Atom *			sel, 
						  Atom *			type, 
						  XtPointer			value, 
						  unsigned long *	len, 
						  int *				format)
{
	MWContext * context = (MWContext *) client_data;
	MWContext * top_context;

	if (!context)
	{
		return;
	}

	/* Load URL on the top most frame */
	top_context = XP_GetNonGridContext(context);

	if (!top_context)
	{
		return;
	}

	if (len && *len && value)
	{
		if (*type == XA_STRING)
		{
			String			str = (String) value;
			URL_Struct *	url = NET_CreateURLStruct(str,NET_DONT_RELOAD);

			/* hack to cancel initial sploosh-screen loader timeout */
			plonk_cancel();

			fe_GetURL(top_context,url,(url == NULL));
		}
	}
}

