Milán Major

netscape-revival

1 branch
Code

cmd/xfe/editor.c

/* -*- Mode: C; tab-width: 8 -*-
   editor.c --- XFE editor functions
   Copyright © 1996 Netscape Communications Corporation, all rights reserved.
   Created: Spencer Murray <spence@netscape.com>, 11-Jan-96.
 */


#ifdef EDITOR

#include "mozilla.h"
#include "xfe.h"
#include <X11/keysym.h>   /* for editor key translation */
#include <Xm/CutPaste.h>   /* for editor key translation */

#include <xpgetstr.h>     /* for XP_GetString() */
#include <edttypes.h>
#include <edt.h>
#include <layout.h>
#include <secnav.h>

#include "menu.h"
#include "fonts.h"
#include "felocale.h"

#include "xeditor.h"

#define CB_STATIC          /* let commands.c see it */

#define XFE_EDITOR_IM_SUPPORT 1

/*  Our event loop doesn't have workprocs, so we can't have two modal
 *  dialog conditions (one from frontend, one from backend)
#define EVENT_LOOP_HAS_WORKPROC
*/

extern int XFE_WARNING_AUTO_SAVE_NEW_MSG;
extern int XFE_FILE_OPEN;
extern int XFE_EDITOR_ALERT_FRAME_DOCUMENT;
extern int XFE_EDITOR_ALERT_ABOUT_DOCUMENT;
extern int XFE_ALERT_SAVE_CHANGES;
extern int XFE_WARNING_SAVE_CHANGES;
extern int XFE_ERROR_GENERIC_ERROR_CODE;
extern int XFE_EDITOR_COPY_DOCUMENT_BUSY;
extern int XFE_EDITOR_COPY_SELECTION_EMPTY;
extern int XFE_EDITOR_COPY_SELECTION_CROSSES_TABLE_DATA_CELL;
extern int XFE_EDITOR_COPY_DOCUMENT_BUSY;
extern int XFE_COMMAND_EMPTY;
extern int XFE_EDITOR_HTML_EDITOR_COMMAND_EMPTY;
extern int XFE_EDITOR_IMAGE_EDITOR_COMMAND_EMPTY;
extern int XFE_ACTION_SYNTAX_ERROR;
extern int XFE_ACTION_WRONG_CONTEXT;
extern int XFE_EDITOR_WARNING_REMOVE_LINK;
extern int XFE_UNKNOWN;
extern int XFE_ERROR_COPYRIGHT_HINT;
extern int XFE_ERROR_SAVING_OPTIONS;
extern int XFE_ERROR_READ_ONLY;
extern int XFE_ERROR_BLOCKED;
extern int XFE_ERROR_BAD_URL;
extern int XFE_ERROR_FILE_OPEN;
extern int XFE_ERROR_FILE_WRITE;
extern int XFE_ERROR_CREATE_BAKNAME;
extern int XFE_ERROR_DELETE_BAKFILE;
extern int XFE_ERROR_WRITING_FILE;
extern int XFE_ERROR_SRC_NOT_FOUND;
extern int XFE_WARNING_SAVE_CONTINUE;
extern int XFE_EDITOR_DOCUMENT_TEMPLATE_EMPTY;
extern int XFE_EDITOR_BROWSE_LOCATION_EMPTY;
extern int XFE_UPLOADING_FILE;
extern int XFE_SAVING_FILE;
extern int XFE_LOADING_IMAGE_FILE;
extern int XFE_FILE_N_OF_N;

static void
fe_Bell(MWContext* context)
{
    XBell(XtDisplay(CONTEXT_WIDGET(context)), 0);
}

/*
 *    Utility stuff that should be somewhere else.
 */
static Widget
fe_CreateFormDialog(MWContext* context, char* name)
{
  Widget mainw = CONTEXT_WIDGET(context);
  Widget form;
  Arg    av[20];
  int    ac;
  Visual* v = 0;
  Colormap cmap = 0;
  Cardinal depth = 0;

  /*
   *    Inherit MainW attributes.
   */
  XtVaGetValues(mainw, XtNvisual, &v, XtNcolormap, &cmap,
		XtNdepth, &depth, 0);
  ac = 0;
  XtSetArg(av[ac], XmNvisual, v); ac++;
  XtSetArg(av[ac], XmNdepth, depth); ac++;
  XtSetArg(av[ac], XmNcolormap, cmap); ac++;
  XtSetArg(av[ac], XmNtransientFor, mainw); ac++;

  XtSetArg(av[ac], XmNallowShellResize, TRUE); ac++;
  XtSetArg(av[ac], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); ac++;
  XtSetArg(av[ac], XmNautoUnmanage, False); ac++;

  form = XmCreateFormDialog(mainw, name, av, ac);

  return form;
}

static Widget
fe_CreateFramedToggle(
		      MWContext* context,
		      Widget     parent, 
		      char*      name,
		      Widget*    toggle_addr
		      )
{
  Arg      av[20];
  Cardinal ac;
  char     buf[64];
  Widget   title;
  Widget   frame;
  Widget   toggle;
  Widget   text;
  Widget   column;

  strcpy(buf, name);
  strcat(buf, "Frame");
  ac = 0;
  XtSetArg(av[ac], XmNshadowType, XmSHADOW_ETCHED_IN); ac++;
  frame = XmCreateFrame(parent, buf, av, ac);

  strcpy(buf, name);
  strcat(buf, "Title");
  ac = 0;
  XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
  XtSetArg(av[ac], XmNchildType, XmFRAME_TITLE_CHILD); ac++;
  XtSetArg(av[ac], XmNchildHorizontalAlignment, XmALIGNMENT_BEGINNING); ac++;
  XtSetArg(av[ac], XmNchildVerticalAlignment, XmALIGNMENT_CENTER); ac++;
  title = XmCreateLabelGadget(frame, buf, av, ac);
  XtManageChild(title);

  strcpy(buf, name);
  strcat(buf, "RowColumn");
  ac = 0;
  XtSetArg(av[ac], XmNorientation, XmVERTICAL); ac++;
  XtSetArg(av[ac], XmNpacking, XmPACK_TIGHT); ac++;
  XtSetArg(av[ac], XmNchildType, XmFRAME_WORKAREA_CHILD); ac++;
  column = XmCreateRowColumn(frame, buf, av, ac);
  XtManageChild(column);

  strcpy(buf, name);
  strcat(buf, "Toggle");
  ac = 0;
  XtSetArg(av[ac], XmNindicatorType, XmN_OF_MANY); ac++;
  toggle = XmCreateToggleButtonGadget(column, buf, av, ac);
  XtManageChild(toggle);
  if (toggle_addr)
    *toggle_addr = toggle;

  strcpy(buf, name);
  strcat(buf, "Text");
  ac = 0;
  XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
  text = XmCreateLabelGadget(column, buf, av, ac);
  XtManageChild(text);

  return frame;
}

static Widget
fe_CreateSaveCancelButtonGroup(
			   MWContext* context,
			   Widget     parent, 
			   char*      name,
			   Arg*       p_args,
			   Cardinal   np_args,
			   Widget*    save_addr,
			   Widget*    cancel_addr
			   )
{
  Arg      av[20];
  Cardinal ac;
  Widget   column;
  Widget   save_button;
  Widget   cancel_button;
  Widget   spacer_label;
  XmString xms;
  Widget   children[8];
  Cardinal nchildren = 0;

  for (ac = 0; ac < np_args; ac++) {
    XtSetArg(av[ac], p_args[ac].name, p_args[ac].value);
  }

  XtSetArg(av[ac], XmNorientation, XmVERTICAL); ac++;
  XtSetArg(av[ac], XmNisAligned, TRUE); ac++;
  XtSetArg(av[ac], XmNentryAlignment, XmALIGNMENT_CENTER); ac++;
  XtSetArg(av[ac], XmNpacking, XmPACK_TIGHT); ac++;
  column = XmCreateRowColumn(parent, name, av, ac);

  ac = 0;
  save_button = XmCreatePushButtonGadget(column, "save", av, ac);
  children[nchildren++] = save_button;
  if (save_addr)
    *save_addr = save_button;

  ac = 0;
  cancel_button = XmCreatePushButtonGadget(column, "cancel", av, ac);
  children[nchildren++] = cancel_button;
  if (cancel_addr)
    *cancel_addr = cancel_button;

  xms = XmStringCreateLtoR("", XmSTRING_DEFAULT_CHARSET);
  XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
  XtSetArg(av[ac], XmNlabelString, xms); ac++;
  spacer_label = XmCreateLabelGadget(column, "spacer", av, ac);
  XmStringFree (xms);
  children[nchildren++] = spacer_label;

#ifndef NO_HELP
  ac = 0;
  XtSetArg(av[ac], XtNsensitive, False); ac++;
  children[nchildren++] = XmCreatePushButtonGadget(column, "help", av, ac);
#endif

  XtManageChildren(children, nchildren);

  return column;
}

static int fe_GetStandardPixmap_pixmapsInitialized;

static Pixmap
fe_GetStandardPixmap(MWContext* context, char type)
{
  Widget mainw = CONTEXT_WIDGET(context);
  Arg    av[20];
  int    ac;
  Cardinal depth;
  Screen* screen;
  Pixel   fg;
  Pixel   bg;
  char* name;
  Pixmap pixmap;

  switch (type) {
  case XmDIALOG_ERROR:       name = "xm_error";       break;
  case XmDIALOG_INFORMATION: name = "xm_information"; break;
  case XmDIALOG_QUESTION:    name = "xm_question";    break;
  case XmDIALOG_WARNING:     name = "xm_warning";     break;
  case XmDIALOG_WORKING:     name = "xm_working";     break;
  default:
    return XmUNSPECIFIED_PIXMAP;
  }

  /*
   *    This is so broken. Init MesageBox class, so the Motif
   *    standard icons get installed. YUCK..djw
   */
  if (!fe_GetStandardPixmap_pixmapsInitialized) {
    XtInitializeWidgetClass(xmMessageBoxWidgetClass);
    fe_GetStandardPixmap_pixmapsInitialized++;
  }

  /*
   *    Inherit MainW attributes.
   */
  ac = 0;
  XtSetArg(av[ac], XmNbackground, &bg); ac++;
  XtSetArg(av[ac], XmNforeground, &fg); ac++;
  XtSetArg(av[ac], XmNdepth, &depth); ac++;
  XtSetArg(av[ac], XmNscreen, &screen); ac++;
  XtGetValues(mainw, av, ac);

  pixmap = XmGetPixmapByDepth(screen, name, fg, bg, depth);
  if (pixmap == XmUNSPECIFIED_PIXMAP) {
    char default_name[256];

    strcpy(default_name, "default_");
    strcat(default_name, name);

    pixmap = XmGetPixmapByDepth(screen, default_name, fg, bg, depth);
  }

  return pixmap;
}

XtPointer
fe_GetUserData(Widget widget)
{
    void* rv;
    XtVaGetValues(widget, XmNuserData, &rv, 0);
    return rv;
}

static struct {
  unsigned type;
  char*    name;
} fe_CreateYesToAllDialog_button_data[] = {
  { XFE_DIALOG_YES_BUTTON,      "yes"      }, 
  { XFE_DAILOG_YESTOALL_BUTTON, "yesToAll" },
  { XFE_DIALOG_NO_BUTTON,       "no"       },
  { XFE_DIALOG_NOTOALL_BUTTON,  "noToAll"  },
  { XFE_DIALOG_CANCEL_BUTTON,   "cancel"   },
  { 0, NULL }
};



#define XFE_YESTOALL_YES_BUTTON      (fe_CreateYesToAllDialog_button_data[0].name)
#define XFE_YESTOALL_YESTOALL_BUTTON (fe_CreateYesToAllDialog_button_data[1].name)
#define XFE_YESTOALL_NO_BUTTON       (fe_CreateYesToAllDialog_button_data[2].name)
#define XFE_YESTOALL_NOTOALL_BUTTON  (fe_CreateYesToAllDialog_button_data[3].name)
#define XFE_YESTOALL_CANCEL_BUTTON   (fe_CreateYesToAllDialog_button_data[4].name)

typedef struct {
    Widget widget;
    char*  name;
} GetChildInfo;

static XtPointer
fe_YesToAllDialogGetChildMappee(Widget widget, XtPointer data)
{
    GetChildInfo* info = (GetChildInfo*)data;
    char*         name = XtName(widget);
    
    if (strcmp(info->name, name) == 0) {
	info->widget = widget;
	return (XtPointer)1; /* don't look any more */
    }
    return 0;
}

Widget
fe_YesToAllDialogGetChild(Widget parent, unsigned char type)
{
  char*        name;
  GetChildInfo info;

  switch (type) {
  case XFE_DIALOG_YES_BUTTON:
    name = XFE_YESTOALL_YES_BUTTON;
    break;
  case XFE_DAILOG_YESTOALL_BUTTON:
    name = XFE_YESTOALL_YESTOALL_BUTTON;
    break;
  case XFE_DIALOG_NO_BUTTON:
    name = XFE_YESTOALL_NO_BUTTON;
    break;
  case XFE_DIALOG_NOTOALL_BUTTON:
    name = XFE_YESTOALL_NOTOALL_BUTTON;
    break;
  case XFE_DIALOG_CANCEL_BUTTON:
    name = XFE_YESTOALL_CANCEL_BUTTON;
    break;
  default:
    return NULL; /* kill, kill, kill */
  }

  info.name = name;
  info.widget = NULL;

  fe_WidgetTreeWalk(parent, fe_YesToAllDialogGetChildMappee, &info);

  return info.widget;
}

static Widget
fe_CreateYesToAllDialog(MWContext* context, char* name, Arg* p_av, Cardinal p_ac)
{
  Arg      av[20];
  Cardinal ac;
  Widget   form;
  Widget   children[16];
  Cardinal nchildren = 0;
  Pixmap   pixmap;
  char* bname;
  Widget button;
  Widget icon;
  Widget text;
  Widget separator;
  Widget row;
  int i;
  XtCallbackRec* button_callback_rec = NULL;
  XmString msg_string = NULL;
  char namebuf[64];
  char pixmapType = XmDIALOG_WARNING;

  for (i = 0; i < p_ac; i++) {
      if (p_av[i].name == XmNarmCallback)
	  button_callback_rec = (XtCallbackRec*)p_av[i].value;
      else if (p_av[i].name == XmNmessageString)
	  msg_string = (XmString)p_av[i].value;
      else if (p_av[i].name == XmNdialogType)
	  pixmapType = (unsigned char)p_av[i].value;
  }

  form = fe_CreateFormDialog(context, name);

  strcpy(namebuf, name); strcat(namebuf, "Message");
  ac = 0;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNorientation, XmHORIZONTAL); ac++;
  XtSetArg(av[ac], XmNentryAlignment, XmALIGNMENT_CENTER); ac++;
  XtSetArg(av[ac], XmNisAligned, TRUE); ac++;
  XtSetArg(av[ac], XmNpacking, XmPACK_TIGHT); ac++;
  row = XmCreateRowColumn(form, namebuf, av, ac);

  nchildren = 0;

  /*
   *    Make pixmap label.
   */
  pixmap = fe_GetStandardPixmap(context, pixmapType);

  ac = 0;
  XtSetArg(av[ac], XmNlabelType, XmPIXMAP); ac++;
  XtSetArg(av[ac], XmNlabelPixmap, pixmap); ac++;
  icon = XmCreateLabelGadget(row, "icon", av, ac);
  children[nchildren++] = icon;

  /*
   *    Text.
   */
  ac = 0;
  if (msg_string)
    XtSetArg(av[ac], XmNlabelString, msg_string); ac++;
  XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
  XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
  text = XmCreateLabelGadget(row, "text", av, ac);
  children[nchildren++] = text;

  XtManageChildren(children, nchildren); /* children of row */

  nchildren = 0;
  children[nchildren++] = row;

  /*
   *    Separator.
   */
  ac = 0;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNtopWidget, row); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  separator = XmCreateSeparatorGadget(form, "separator", av, ac);
  children[nchildren++] = separator;
 
  /*
   *    Yes, YesToAll, No, NoToAll, Cancel.
   */
  strcpy(namebuf, name); strcat(namebuf, "Buttons");
  ac = 0;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNtopWidget, separator); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNorientation, XmHORIZONTAL); ac++;
  XtSetArg(av[ac], XmNentryAlignment, XmALIGNMENT_CENTER); ac++;
  XtSetArg(av[ac], XmNisAligned, TRUE); ac++;
  XtSetArg(av[ac], XmNpacking, XmPACK_COLUMN); ac++;
  row = XmCreateRowColumn(form, namebuf, av, ac);
  children[nchildren++] = row;

  XtManageChildren(children, nchildren);

  nchildren = 0; /* now do buttons */
  button = NULL;
  for (i = 0; (bname = fe_CreateYesToAllDialog_button_data[i].name); i++) {

    ac = 0;
    XtSetArg(av[ac], XmNalignment, XmALIGNMENT_CENTER); ac++;
    XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
    button = XmCreatePushButtonGadget(row, bname, av, ac);

    if (button_callback_rec)
      XtAddCallback(
		    button,
		    XmNactivateCallback,
		    button_callback_rec->callback,
		    button_callback_rec->closure
		    );
    children[nchildren++] = button;
  }

  XtManageChildren(children, nchildren);

  return form;
}

Boolean
fe_EditorDocumentIsSaved(MWContext* context)
{
    return (context->is_new_document == FALSE);
}


Boolean fe_SaveAsDialog(MWContext* context, char* buf, int type);

static Boolean
fe_editor_report_file_error(MWContext*   context,
			    ED_FileError code,
			    char*        filename, 
			    Boolean      ask_question)
{
    int   id;
    char* msg;
    char  buf[MAXPATHLEN];

    switch (code) {
    case ED_ERROR_READ_ONLY: /* File is marked read-only */
	id = XFE_ERROR_READ_ONLY;
	break;
    case ED_ERROR_BLOCKED: /* Can't write at this time, edit buffer blocked */
	id = XFE_ERROR_BLOCKED;
	break;
    case ED_ERROR_BAD_URL: /* URL was not a "file:" type or no string */
	id = XFE_ERROR_BAD_URL;
	break;
    case ED_ERROR_FILE_OPEN:
	id = XFE_ERROR_FILE_OPEN;
	break;
    case ED_ERROR_FILE_WRITE:
	id = XFE_ERROR_FILE_WRITE;
	break;
    case ED_ERROR_CREATE_BAKNAME:
    case ED_ERROR_FILE_RENAME_TO_BAK:
	id = XFE_ERROR_CREATE_BAKNAME;
	break;
    case ED_ERROR_DELETE_BAKFILE:
	id = XFE_ERROR_DELETE_BAKFILE;
	break;
    case ED_ERROR_SRC_NOT_FOUND:
	id = XFE_ERROR_SRC_NOT_FOUND;
	break;
    default:
	id = XFE_ERROR_GENERIC_ERROR_CODE; /* generic "...code = (%d)" */
	break;
    }

    msg = XP_GetString(XFE_ERROR_WRITING_FILE);
    sprintf(buf, msg, filename);
    strcat(buf, "\n\n");

    msg = XP_GetString(id);
    sprintf(&buf[strlen(buf)], msg, code);

    if (ask_question) {
	strcat(buf, "\n\n");
	strcat(buf, XP_GetString(XFE_WARNING_SAVE_CONTINUE));
	return XFE_Confirm(context, buf);
    } else {
	FE_Alert(context, buf);
	return TRUE;
    }
}

static Boolean
fe_editor_save_common(MWContext* context, char* target_url)
{
    History_entry* hist_ent;
    ED_FileError   code;
    Boolean        images;
    Boolean        links;
    Boolean        save_as;

    /* url->address is used for filename */
    hist_ent = SHIST_GetCurrent(&context->hist);

    if (!hist_ent)
	return FALSE;

    /*
     *    Has the title changed since it was last saved?
     */
    if (hist_ent->title && strcmp(hist_ent->title, hist_ent->address) == 0) {
	/*
	 *    BE will set this to the new file, if we don't zap it,
	 *    BE will retain the old name - yuck!
	 */
	XP_FREE(hist_ent->title);
	hist_ent->title = NULL;
    }

    if (target_url) { /* save as */
	save_as = TRUE;
	fe_EditorPreferencesGetLinksAndImages(context, &links, &images);
    } else { /* save me */ 
	target_url = hist_ent->address;
	save_as = FALSE;
	images = FALSE;
	links = FALSE;
    }

    /*
     *    EDT_SaveFile returns true if it completed immediately.
     *    savesas uses source and dest, save just uses 
     */
    code = EDT_SaveFile(context, hist_ent->address, target_url,
			save_as, images, links);

    /* here we spin and spin until all the savings of images
     * and stuff are done so that we get a "real" status back from
     * the backend.  This makes saving semi-synchronous, good for
     * status reporting
     */
    while(CONTEXT_DATA(context)->is_file_saving) {
        /* do nothing, we wait here */
        fe_EventLoop();
    }

    code = CONTEXT_DATA(context)->file_save_status;
    if (code != ED_ERROR_NONE && code != ED_ERROR_CANCEL) {
        fe_editor_report_file_error(context, code, 
				    CONTEXT_DATA(context)->save_file_url,
				    FALSE);
    }
    
    if (code != ED_ERROR_NONE) {
	/*
	 *    I'm sure if it is bad if the title is NULL, but
	 *    just in case..
	 */
	if ((hist_ent = SHIST_GetCurrent(&context->hist)) != NULL
	    && 
	    hist_ent->title == NULL) {
	    hist_ent->title = XP_STRDUP(hist_ent->address);
	}
	fe_editor_report_file_error(context, code, target_url, FALSE);
	return FALSE;
    }

    return TRUE;
}

Boolean
fe_EditorSaveAs(MWContext* context) /* launches dialog */
{
    char filebuf[MAXPATHLEN];

    if (fe_SaveAsDialog(context, filebuf, fe_FILE_TYPE_HTML)) {
	
	char urlbuf[MAXPATHLEN];
	    
	PR_snprintf(urlbuf, sizeof(urlbuf), "file:%.900s", filebuf);

	return fe_editor_save_common(context, urlbuf);
    }

    return FALSE;
}

Boolean
fe_EditorSave(MWContext* context)
{
    /* 
     *    Always ask the user for the filename once.
     *
     *    if (EDT_IS_NEW_DOCUMENT(context))
     */
    if (!fe_EditorDocumentIsSaved(context)) {
	return fe_EditorSaveAs(context);
    }

    return fe_editor_save_common(context, NULL);
}

CB_STATIC void
fe_editor_save_as_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext *context = (MWContext *) closure;

    fe_UserActivity (context);
    
    {
	MWContext *ctx = fe_GetFocusGridOfContext(context);
	if (ctx)
	    context = ctx;
    }

    fe_EditorSaveAs(context);

    fe_EditorUpdateToolbar(context, 0);
}

CB_STATIC void
fe_editor_save_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext *context = (MWContext *) closure;

    fe_UserActivity (context);
    
    {
	MWContext *ctx = fe_GetFocusGridOfContext(context);
	if (ctx)
	    context = ctx;
    }

    fe_EditorSave(context);

    fe_EditorUpdateToolbar(context, 0);
}

#if 0
static void
fe_editor_browse_doc_update_cb(Widget widget, XtPointer closure, XtPointer cd)
{
    Boolean sensitive = fe_EditorDocumentIsSaved((MWContext*)closure);

    fe_WidgetSetSensitive(widget, sensitive);
}

static void
fe_editor_publish_update_cb(Widget widget, XtPointer closure, XtPointer cd)
{
    Boolean sensitive = fe_EditorDocumentIsSaved((MWContext*)closure);

    fe_WidgetSetSensitive(widget, sensitive);
}

CB_STATIC void
fe_editor_file_menu_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context =  (MWContext*)closure;
    Arg        args[4];
    Cardinal   n;
    Widget     pulldown_menu;
    Widget*    children;
    Cardinal   nchildren;
    char*      name;
    Widget     button;
    int        i;

    /*
     *    Get the popup menu from the cascade.
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, &pulldown_menu); n++;
    XtGetValues(widget, args, n);

    /*
     *    Get the children of the popup.
     */
    n = 0;
    XtSetArg(args[n], XmNchildren, &children); n++;
    XtSetArg(args[n], XmNnumChildren, &nchildren); n++;
    XtGetValues(pulldown_menu, args, n);

    for (i = 0; i < nchildren; i++) {

	button = children[i];
	name = XtName(button);
	
	if (strcmp(name, "browseDoc") == 0)
	    fe_editor_browse_doc_update_cb(button, (XtPointer)context, NULL);
	else if (strcmp(name, "publish") == 0)
	    fe_editor_publish_update_cb(button, (XtPointer)context, NULL);
    }
}
#endif

/*
 *    Editor dialogs.
 */
typedef struct SaveRemoteInfo
{
  Widget  widget;
  Boolean adjust_links;
  Boolean save_images;
  Boolean do_save;
  Boolean done;
} SaveRemoteInfo;

static void
fe_save_remote_save_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
  SaveRemoteInfo* info = (SaveRemoteInfo*)closure;

  info->do_save = TRUE;
  info->done = TRUE;

  XtUnmanageChild(info->widget);
}

static void
fe_save_remote_cancel_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
  SaveRemoteInfo* info = (SaveRemoteInfo*)closure;

  info->do_save = FALSE;
  info->done = TRUE;

  XtUnmanageChild(info->widget);
}

static void
fe_save_remote_images_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
  SaveRemoteInfo* info = (SaveRemoteInfo*)closure;

  info->save_images = XmToggleButtonGadgetGetState(widget);
}

static void
fe_save_remote_links_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
  SaveRemoteInfo* info = (SaveRemoteInfo*)closure;

  info->adjust_links = XmToggleButtonGadgetGetState(widget);
}


static Widget
fe_CreateSaveRemoteDialog(MWContext* context, SaveRemoteInfo* info)
{
  Arg      av[20];
  Cardinal ac;
  Widget   form;
  Widget   info_icon;
  Widget   info_text;
  Widget   links_frame;
  Widget   links_toggle;
  Widget   images_frame;
  Widget   images_toggle;
  Widget   buttons_column;
  Widget   save_button;
  Widget   cancel_button;
  Widget   children[16];
  Cardinal nchildren = 0;
  Pixmap   pixmap;
  Pixel    fg;
  Pixel    bg;
  Cardinal depth;
  Screen*  screen;

  form = fe_CreateFormDialog(context, "saveRemote");

  /*
   *   Info icon, info text,
   *   framed Links check box and text,
   *   framed Images check box and text,
   *   --------------------------------
   *   Save, Cancel, Help.
   */
#if 0
  pixmap = fe_ToolbarPixmap(context, 0, False, False); /*FIXME*/
#else
  ac = 0;
  XtSetArg(av[ac], XmNbackground, &bg); ac++;
  XtSetArg(av[ac], XmNforeground, &fg); ac++;
  XtSetArg(av[ac], XmNdepth, &depth); ac++;
  XtSetArg(av[ac], XmNscreen, &screen); ac++;
  XtGetValues(form, av, ac);

  /*
   *    This is so broken. Init MesageBox class, so the Motif
   *    standard icons get installed. YUCK..djw
   */
  XtInitializeWidgetClass(xmMessageBoxWidgetClass);

  pixmap = XmGetPixmapByDepth(screen, "xm_question", fg, bg, depth);
  if (pixmap == XmUNSPECIFIED_PIXMAP)
    pixmap = XmGetPixmapByDepth(screen, "default_xm_question", fg, bg, depth);
#endif
  ac = 0;
  XtSetArg(av[ac], XmNlabelType, XmPIXMAP); ac++;
  XtSetArg(av[ac], XmNlabelPixmap, pixmap); ac++;
  info_icon = XmCreateLabelGadget(form, "infoIcon", av, ac);
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  children[nchildren++] = info_icon;

  ac = 0;
  XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
  XtSetArg(av[ac], XmNlabelType, XmSTRING); ac++;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNleftWidget, info_icon); ac++;
  info_text = XmCreateLabelGadget(form, "infoText", av, ac);
  children[nchildren++] = info_text;

  ac = 0;
  XtSetArg(av[ac], XmNalignment, XmALIGNMENT_BEGINNING); ac++;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNleftWidget, info_text); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  buttons_column = fe_CreateSaveCancelButtonGroup(
						  context,
						  form,
						  "rowColumn",
						  av,
						  ac,
						  &save_button,
						  &cancel_button
						  );
  children[nchildren++] = buttons_column;

  XtAddCallback(save_button, XmNactivateCallback, fe_save_remote_save_cb, info);
  XtAddCallback(cancel_button, XmNactivateCallback, fe_save_remote_cancel_cb, info);

  XtVaSetValues(form, XmNdefaultButton, save_button, 0);

  /*
   *    Links group.
   */
  links_frame = fe_CreateFramedToggle(
				       context,
				       form,
				       "links",
				       &links_toggle
				       );
  children[nchildren++] = links_frame;

  ac = 0;
  XtSetArg(av[ac], XmNset, ((info->adjust_links)? TRUE: FALSE)); ac++;
  XtSetValues(links_toggle, av, ac);
  XtAddCallback(links_toggle, XmNvalueChangedCallback, fe_save_remote_links_cb, info);

  ac = 0;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNtopWidget, buttons_column); ac++;
  XtSetValues(links_frame, av, ac);

  /*
   *    Images group.
   */
  images_frame = fe_CreateFramedToggle(
					context,
					form,
					"images",
					&images_toggle
				       );

  children[nchildren++] = images_frame;

  ac = 0;
  XtSetArg(av[ac], XmNset, ((info->save_images)? TRUE: FALSE)); ac++;
  XtSetValues(images_toggle, av, ac);
  XtAddCallback(images_toggle, XmNvalueChangedCallback, fe_save_remote_images_cb, info);

  ac = 0;
  XtSetArg(av[ac], XmNleftAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNrightAttachment, XmATTACH_FORM); ac++;
  XtSetArg(av[ac], XmNtopAttachment, XmATTACH_WIDGET); ac++;
  XtSetArg(av[ac], XmNtopWidget, links_frame); ac++;
  XtSetArg(av[ac], XmNbottomAttachment, XmATTACH_FORM); ac++;
  XtSetValues(images_frame, av, ac);

  XtManageChildren(children, nchildren);

  return form;
}

static void
fe_DoSaveRemoteDialog(
		      MWContext* context,
		      Boolean* save_rv, Boolean* links_rv, Boolean* images_rv
)
{
  SaveRemoteInfo info;

  info.save_images = *images_rv;;
  info.adjust_links = *links_rv;
  info.do_save = TRUE;
  info.done = FALSE;

  info.widget = fe_CreateSaveRemoteDialog(context, &info);
    
  XtManageChild(info.widget);

  fe_NukeBackingStore(info.widget);
  
  while (!info.done)
    fe_EventLoop();

  XtDestroyWidget(info.widget);

  *save_rv = info.do_save;
  *links_rv = info.adjust_links;
  *images_rv = info.save_images;
}

/*
 *    I18N input method support.
 */
#ifdef XFE_EDITOR_IM_SUPPORT
static void
fe_editor_im_deregister(Widget widget, XtPointer closure, XtPointer cb_data)
{
    XmImUnregister(widget);
}

static void
fe_editor_im_get_coords(MWContext* context, XPoint* point)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;
    
    if (data->running == FALSE && data->showing == FALSE) {
	point->x = 0;
	point->y = 16;
    } else {
	point->x = data->x + data->width - CONTEXT_DATA(context)->document_x;
	point->y = data->y - 2 - CONTEXT_DATA(context)->document_y +
			data->height;
    }
}

static void
fe_editor_im_init(MWContext* context)
{
    Widget     widget = CONTEXT_DATA(context)->drawing_area;
    Arg        args[8];
    Cardinal   n;
    XmFontList font_list;
    Pixel      bg_pixel;
    Pixel      fg_pixel;
    /* Pixmap     bg_pixmap; */
    XPoint     xmim_point;
    /* int        xmim_height; */
    
    XtVaSetValues(CONTEXT_WIDGET(context), XmNallowShellResize, TRUE, NULL);

    XmImRegister(widget, 0);
    XtAddCallback(widget, XmNdestroyCallback, fe_editor_im_deregister, NULL);

    /*
     *    Should these change dynamically with point?
     */
    font_list = fe_GetFont(context, 3, LO_FONT_FIXED);
    bg_pixel = CONTEXT_DATA(context)->bg_pixel;
    fg_pixel = CONTEXT_DATA(context)->fg_pixel;
    /* bg_pixmap = CONTEXT_DATA(context)->backdrop_pixmap; */

    fe_editor_im_get_coords(context, &xmim_point);

    n = 0;
    XtSetArg(args[n], XmNfontList, font_list); n++;
    XtSetArg(args[n], XmNbackground, bg_pixel); n++;
    XtSetArg(args[n], XmNforeground, fg_pixel); n++;
    /* XtSetArg(args[n], XmNbackgroundPixmap, bg_pixmap); n++; */
    XtSetArg(args[n], XmNspotLocation, &xmim_point); n++;
    /* XtSetArg(args[n], XmNlineSpace, xmim_height); n++; */
    XmImSetValues(widget, args, n);
}

static void
fe_editor_im_set_coords(MWContext* context)
{
    Widget   widget = CONTEXT_DATA(context)->drawing_area;
    Arg      args[8];
    Cardinal n;
    XPoint   xmim_point;
    /* int      xmim_height; */

    fe_editor_im_get_coords(context, &xmim_point);

    n = 0;
    XtSetArg(args[n], XmNspotLocation, &xmim_point); n++;
    /* XtSetArg(args[n], XmNlineSpace, xmim_height); n++; */
    XmImSetFocusValues(widget, args, n);
}

static void
fe_editor_im_focus_in(MWContext* context)
{
    fe_editor_im_set_coords(context);

    XtSetKeyboardFocus(CONTEXT_WIDGET(context),
			CONTEXT_DATA(context)->drawing_area);
}

static void
fe_editor_im_focus_out(MWContext* context)
{
    Widget widget = CONTEXT_DATA(context)->drawing_area;

    XmImUnsetFocus(widget);
}
#endif /*XFE_EDITOR_IM_SUPPORT*/

static void fe_caret_pause(MWContext* context);
static void fe_caret_unpause(MWContext* context);

static void
fe_losing_focus_eh(Widget w, XtPointer closure, XEvent* event, Boolean* cont)
{
    MWContext* context = (MWContext *)closure;

    *cont = TRUE;
    
    if (event->type == FocusIn) {
        fe_caret_unpause(context);
#ifdef XFE_EDITOR_IM_SUPPORT
	fe_editor_im_focus_in(context);
#endif /*XFE_EDITOR_IM_SUPPORT*/
    } else if (event->type == FocusOut) {
        fe_caret_pause(context);
#ifdef XFE_EDITOR_IM_SUPPORT
	fe_editor_im_focus_out(context);
#endif /*XFE_EDITOR_IM_SUPPORT*/
    }
}

/*
 *    Menu stuff.
 */
typedef MWContext MWEditorContext; /* place holder */

MWContext*
fe_EditorNew(MWContext* context, char* address)
{
    MWContext*  new_context;
    URL_Struct* url;
    Boolean     is_editor_save;

    fe_UserActivity(context);

#if 0
    /*
     *    It would be really nice to test for remote urls right here,
     *    but we don't always know (it may be an alias). So we have
     *    to load the doc first, the check in FE_EditorDocumentLoaded().
     *    Plus we need to pass links & images through to
     *    FE_EditorDocumentLoaded() - choke....djw
     */
    if (address != NULL	&& !NET_IsLocalFileURL(address)) {
	/* not a local url */

	fe_EditorPreferencesGetLinksAndImages(context, &links, &images);

	/*
	 *    Prompt for saving remote document.
	 */
	fe_DoSaveRemoteDialog(context, &save, &links, &images);

	if (!save)
	    return NULL; /* we got cancelled */
    }
#endif

    /*
     *    Make a new editor window. All this stuff with ->is_editor
     *    is to fake out the callees that want to see that set.
     */
    is_editor_save = context->is_editor;
    context->is_editor = TRUE;

    new_context = fe_MakeWindow(XtParent(CONTEXT_WIDGET(context)),
				context,
				0,
				NULL,
				MWContextEditor,
				FALSE
				);
    context->is_editor = is_editor_save;
    new_context->is_editor = TRUE;

#if 0
    /*
     *    Just cannot get this to do what it is supposed to.
     */
    XtAddEventHandler(CONTEXT_WIDGET(new_context), PropertyChangeMask,
		      FALSE, (XtEventHandler)fe_property_notify_eh,
		      new_context);
#endif

    XtAddEventHandler(CONTEXT_WIDGET(new_context), FocusChangeMask,
		      FALSE, (XtEventHandler)fe_losing_focus_eh,
		      new_context);

    /*
     *    Register with input server.
     */
#ifdef XFE_EDITOR_IM_SUPPORT
    fe_editor_im_init(new_context);
#endif /*XFE_EDITOR_IM_SUPPORT*/
    
    if (!address)
	address = "about:editfilenew"; /* blank address */

    url = NET_CreateURLStruct(address, FALSE);

    fe_GetURL(new_context, url, FALSE);

    return new_context;
}

static MWContext*
fe_editor_find_context(char* address, int type)
{
    struct fe_MWContext_cons* rest;
    MWContext* context;
    History_entry* hist_entry;

    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
        context = rest->context;
	if (context->type == type) {
	    hist_entry = SHIST_GetCurrent(&context->hist);

	    if (hist_entry != NULL
		&&
		hist_entry->address
		&&
		strcmp(hist_entry->address, address) == 0) {
	        return context;
	    }
	}
    }
    return NULL;
}

static void
fe_map_raise_window(MWContext* context)
{
    Widget widget = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(widget);
    Window window = XtWindow(widget);

#if 1
    XMapWindow(display, window);
    XRaiseWindow(display, window);
#else
    XMapRaised(XtDisplay(CONTEXT_WIDGET(context)),
	       XtWindow(CONTEXT_WIDGET(context)));
#endif
}

static void
fe_map_raise_timeout(XtPointer closure, XtIntervalId* id)
{
    MWContext *context = (MWContext *)closure;

    fe_map_raise_window(context);
}

static void
fe_map_raise_window_when_idle(MWContext* context)
{
    /*
     *    Delay the raise until after any dialogs, etc have
     *    been unmanaged. Otherwise we bounce backwards and forwards,
     *    and sometimes the window is left unraised.
     */
    XtAppAddTimeOut(fe_XtAppContext, 0, fe_map_raise_timeout, (XtPointer)context);
}

MWContext*
fe_EditorGetURL(MWContext* context, char* address)
{
    MWContext* new_context = fe_editor_find_context(address, MWContextEditor);

    if (new_context) {
	fe_map_raise_window_when_idle(new_context);
	return new_context;
    } else {
	return fe_EditorNew(context, address);
    }
}

MWContext*
fe_BrowserGetURL(MWContext* context, char* address)
{
    return fe_GetURLInBrowser(context, NET_CreateURLStruct(address, FALSE));
}

CB_STATIC void
fe_editor_edit_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext *context = (MWContext *) closure;
    History_entry* hist_ent;
    URL_Struct*    url;

    fe_UserActivity(context);

    hist_ent = SHIST_GetCurrent(&context->hist);

    if (hist_ent != NULL) {

	/* If this is a grid doc, bail ... */
	if (context->is_grid_cell || fe_IsGridParent (context)) {
	    FE_Alert(context, XP_GetString(XFE_EDITOR_ALERT_FRAME_DOCUMENT));
	    return;
	}

	if (strncmp(hist_ent->address, "about:", 6) == 0) {
	    FE_Alert(context, XP_GetString(XFE_EDITOR_ALERT_ABOUT_DOCUMENT));
	    return;
	}

	url = SHIST_CreateURLStructFromHistoryEntry(context, hist_ent);

	/* url->address is used for filename */
	if (url != NULL && hist_ent != NULL && hist_ent->address) {
	    fe_EditorGetURL(context, hist_ent->address);
	    return;
	}
    }

    /* fall thru for no url loaded */
    FE_Alert(context, fe_globalData.no_url_loaded_message);
}

CB_STATIC void
fe_editor_new_from_template_cb(Widget widget, XtPointer closure,
			       XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;
    char*      template_url = fe_EditorPreferencesGetTemplate(context);
 
    if (template_url == NULL || template_url[0] == '\0') {
	char* msg = XP_GetString(XFE_EDITOR_DOCUMENT_TEMPLATE_EMPTY);

	/* The new document template location is not set. */
      	if (XFE_Confirm(context, msg)) {
	    fe_EditorPreferencesDialogDo(context,
				 XFE_EDITOR_DOCUMENT_PROPERTIES_GENERAL);
	}
	return;
    }

    fe_BrowserGetURL(context, template_url);
}

CB_STATIC void
fe_editor_new_from_wizard_cb(Widget widget, XtPointer closure,
			       XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;
    char*      template_url = XFE_WIZARD_TEMPLATE_URL;

    fe_BrowserGetURL(context, template_url);
}

CB_STATIC void
fe_editor_new_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;

    fe_EditorNew(context, NULL);
}

CB_STATIC void
fe_editor_open_url_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;

    fe_EditorOpenURLDialog(context);
}

void
fe_EditorDisplaySource(MWContext* context)
{
    EDT_DisplaySource(context);
}

CB_STATIC void
fe_editor_view_source_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;

    fe_EditorDisplaySource(context);
}

void
fe_EditorCleanup(MWContext* context)
{
    Boolean  as_enabled;
    unsigned as_time;

    /*
     *    If they have autosave on, try to do a save. Don't do it
     *    for a new doc, as that'll mean dialogs, and .....
     */
    if (!EDT_IS_NEW_DOCUMENT(context) &&
	(!EDT_IsBlocked(context) && EDT_DirtyFlag(context))) {
	fe_EditorPreferencesGetAutoSave(context, &as_enabled, &as_time);
	if (as_enabled)
	    fe_EditorSave(context);
    }
    
    FE_DestroyCaret(context);
    EDT_DestroyEditBuffer(context);
}

void
fe_EditorDelete(MWContext* context)
{
    /*
     *    We've already had this discussion....
     */
    EDT_SetDirtyFlag(context, FALSE);
    
    /*
     *    This will implicitly call fe_EditorCleanup().
     *    See fe_delete_cb() in xfe.c
     */
    XtDestroyWidget(CONTEXT_WIDGET(context));
}

char*
fe_EditorMakeName(MWContext* context, char* buf, unsigned maxsize)
{
    History_entry* entry;
    char*          local_name = NULL;

    if (!EDT_IS_NEW_DOCUMENT(context)
	&&
	(entry = SHIST_GetCurrent(&context->hist)) && (entry->address)) {

	local_name = NULL;
	if (XP_ConvertUrlToLocalFile(entry->address, &local_name)) {
	    FE_CondenseURL(buf, local_name, maxsize);
	    XP_FREE(local_name);
	} else {
	    FE_CondenseURL(buf, entry->address, maxsize);
	}
    } else {
	strcpy(buf, XFE_EDITOR_NEW_DOC_NAME);
    }
    return buf;
}

static Boolean
fe_named_question(MWContext* context, char* name, char* message)
{
    return (Boolean)((int)fe_dialog(CONTEXT_WIDGET (context),
				    name, message,
				    TRUE, 0, TRUE, FALSE, 0));
}

Boolean
fe_save_file_check(MWContext* context, Boolean file_must_exist, Boolean autoSaveNew)
{
    int rv;
    char filename[MAXPATHLEN];
    char buf[MAXPATHLEN];
    char dialogmessages[MAXPATHLEN+64];
    Boolean ok_cancel = (file_must_exist && EDT_IS_NEW_DOCUMENT(context));

    if (ok_cancel || EDT_DirtyFlag(context)) {
	fe_EditorMakeName(context, filename, sizeof(filename));
	
	if (ok_cancel) {
	    /* You must save <filename> as a local file */
	    sprintf(buf, XP_GetString(XFE_ALERT_SAVE_CHANGES), filename);
	    rv = (fe_named_question(context, "saveNewFile", buf))?
		XmDIALOG_OK_BUTTON: XmDIALOG_CANCEL_BUTTON;
	} else {
	    /* Save changes to <filename>? */
	    sprintf(buf, XP_GetString(XFE_WARNING_SAVE_CHANGES), filename);
	    if (autoSaveNew) {
            sprintf(dialogmessages,"%s\n\n%s", buf,
                    XP_GetString(XFE_WARNING_AUTO_SAVE_NEW_MSG));
			rv = fe_YesNoCancelDialog(context, "autoSaveNew", dialogmessages);
		} else
			rv = fe_YesNoCancelDialog(context, "saveFile", buf);
	}
	
	if (rv == XmDIALOG_OK_BUTTON)
	    return fe_EditorSave(context);
	else if (rv == XmDIALOG_CANCEL_BUTTON)
	    return FALSE;
    }
    return TRUE;
}

XP_Bool
FE_CheckAndSaveDocument(MWContext* context)
{
    return fe_save_file_check(context, TRUE, FALSE);
}

XP_Bool 
FE_CheckAndAutoSaveDocument(MWContext *context)
{
    return fe_save_file_check(context, FALSE, TRUE);
}



#if 0
/*
 *    Call this to check if we need to force saving file
 *    Conditions are New, unsaved document and when editing a remote file
 *    Returns TRUE for all cases except CANCEL by the user in any dialog
 */
XP_Bool
FE_SaveNonLocalDocument(MWContext* context, XP_Bool save_new_document) 
{
    History_entry* hist_entry;
    int type;
    Boolean links;
    Boolean images;
    Boolean save;
    char filebuf[MAXPATHLEN];
    char urlbuf[MAXPATHLEN];
    ED_FileError file_error;

    if (context == NULL || !EDT_IS_EDITOR(context)) {
        return TRUE;
    }
    
    hist_entry = SHIST_GetCurrent(&(context->hist));
    if (!hist_entry || !hist_entry->address)
        return TRUE;
    
    /*
     */
    type = NET_URL_Type(hist_entry->address);

    if ((type >  0 && type != FILE_TYPE_URL && type != FILE_CACHE_TYPE_URL &&
         type != MAILBOX_TYPE_URL && type != VIEW_SOURCE_TYPE_URL)
	||
	(save_new_document && EDT_IS_NEW_DOCUMENT(context)))
    {
	fe_EditorPreferencesGetLinksAndImages(context, &links, &images);
	fe_DoSaveRemoteDialog(context, &save, &links, &images);

	if (!save)
	    return FALSE;

#if 0
	if (!EDT_IS_NEW_DOCUMENT(context))
	    fe_EditorCopyrightWarningDialogDo();
#endif
	
	if (!fe_SaveAsDialog(context, filebuf, fe_FILE_TYPE_HTML))
	    return FALSE;

	/*
	 *    Grab the filename while we have it to form file: path.
	 */
	PR_snprintf(urlbuf, sizeof(urlbuf), "file:%.900s", filebuf);

	/* the c++ comments are here deliberately so that later
	 * on if we use this code we won't forget to modify it */

	/* this here is all disabled, we don't use it anymore but if later
	 * on we need to use it again, we should probably make it synchronous
	 */

	file_error = EDT_SaveFile(context,
				  hist_entry->address,
				  urlbuf,
				  TRUE,
				  images,
				  links);
	
    	if (file_error != ED_ERROR_NONE) {
	    fe_editor_report_file_error(context, file_error, urlbuf,
					FALSE);
	    return FALSE;
	}
    }
    return TRUE;
}
#endif

CB_STATIC void
fe_editor_delete_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext *context = (MWContext *)closure;

    fe_UserActivity (context);

    if (fe_WindowCount == 1) {
#if 0
	fe_Bell(context);
#else
	fe_QuitCallback(widget, closure, call_data);
#endif
    }  else {
	if (fe_save_file_check(context, FALSE, FALSE))
	    fe_EditorDelete(context);
    }
}

void fe_editor_delete_response(Widget widget, XtPointer closure, XtPointer call_data)
{
	fe_editor_delete_cb(widget, closure, call_data);
}


Boolean
fe_EditorCheckUnsaved(MWContext* context)
{
    struct fe_MWContext_cons* rest;

    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
	MWContext* context = rest->context;
	if (context->type == MWContextEditor) {
	    if (!fe_save_file_check(context, FALSE, FALSE)) {
		return FALSE;
	    }
	}
    }
    return TRUE;
}

/* this func is basically XP_FindContextOfType but does not return
 * view source type */

static MWContext*
fe_FindContextOfTypeNoViewSource(MWContext *context, MWContextType type)
{
	XP_List *context_list = XP_GetGlobalContextList();
	int i;

	/* The other types aren't "real" - they don't have windows.  (Actually,
   	 * neither do all of these, but it's damned useful to be able to find the
   	 * biff context...) */
	XP_ASSERT (type == MWContextBrowser ||
   			   type == MWContextMail ||
   			   type == MWContextNews ||
   			   type == MWContextMessageComposition ||
   			   type == MWContextBookmarks ||
   			   type == MWContextAddressBook ||
   			   type == MWContextBiff);

	/* If our current context has the right type, go there */
	if (context && context->type == type) {
		if (!context->name || strncmp(context->name,"view-source",11)!=0)
			return context;
	}

	/* otherwise, just get any other context */
	for (i=1; i<= XP_ListCount(context_list); i++) {
		MWContext * compContext = 
			(MWContext *)XP_ListGetObjectNum(context_list, i);
		if (compContext->type == type) {
			if (!compContext->name || 
				strncmp(compContext->name,"view-source",11)!=0)
				return compContext;
		}
	}
	return NULL;
}


MWContext*
fe_GetURLInBrowser(MWContext* context, URL_Struct *url)
{
    MWContext* new_context;
    MWContext* top_context;

	new_context = fe_FindContextOfTypeNoViewSource(context, MWContextBrowser);
 
    if (new_context) {
	top_context = fe_GridToTopContext(new_context);
	if (!top_context)
	    top_context = new_context;

	fe_GetURL(top_context, url, FALSE);
    } else {
	new_context = fe_MakeWindow(XtParent(CONTEXT_WIDGET(context)),
				    context, url, NULL,
				    MWContextBrowser, FALSE);
    }

    if (new_context) {
	XMapRaised(XtDisplay(CONTEXT_WIDGET(new_context)),
		   XtWindow(CONTEXT_WIDGET(new_context)));
    }
    return new_context;
}


CB_STATIC void
fe_editor_browse_doc_cb(Widget widget, XtPointer closure, XtPointer cd)
{
    MWContext *context = (MWContext *)closure;
    URL_Struct *url;

    fe_UserActivity(context);

    if (!FE_CheckAndSaveDocument(context))
	return;

    url = SHIST_CreateURLStructFromHistoryEntry(context,
					SHIST_GetCurrent(&context->hist));
    fe_GetURLInBrowser(context, url);
}

CB_STATIC void
fe_editor_about_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext *context = (MWContext *) closure;
    fe_UserActivity (context);
    fe_BrowserGetURL(context, "about:");
}
 
/*
 *    Crock timeout callback that is used by FE_EditorDocumentLoaded() to
 *    cancel an edit. See comments there.
 */
static void
fe_editor_exit_timeout(XtPointer closure, XtIntervalId* id)
{
    MWContext *context = (MWContext *)closure;

    fe_EditorDelete(context);
}

void
fe_EditorOpenFile(MWContext* context)
{
    char*      title;
    char*      out;

    title = XP_GetString(XFE_FILE_OPEN);
    out = fe_ReadFileName(context, title, 0, True, 0);

    if (out) {
      
	char buf[MAXPATHLEN];

	PR_snprintf(buf, sizeof(buf), "file:%.900s", out); 

	free(out);

	/*
	 *    Raise existing, or create a new window.
	 */
	fe_EditorGetURL(context, buf);
    }
}

CB_STATIC void
fe_editor_open_file_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *) closure;

    fe_UserActivity(context);

    fe_EditorOpenFile(context);
}

void
fe_EditorReload(MWContext* context, Boolean super_reload)
{
    if (EDT_IS_NEW_DOCUMENT(context))
	return; /* only from action */

    if (!FE_CheckAndSaveDocument(context))
	return;

#if 0 /* this does not exist! */
    EDT_Reload(context);
#else
    if (super_reload)
	fe_ReLayout (context, NET_SUPER_RELOAD);
    else
	fe_ReLayout (context, NET_NORMAL_RELOAD);
#endif
}

CB_STATIC void
fe_editor_reload_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    XmPushButtonCallbackStruct* cd = (XmPushButtonCallbackStruct *)call_data;

    fe_UserActivity(context);
    
    fe_EditorReload(context, (cd && cd->event->xkey.state & ShiftMask));
}

static void
fe_editor_reload_update_cb(Widget widget,
			   XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean sensitive = !EDT_IS_NEW_DOCUMENT(context);
    
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_cut_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    XmPushButtonCallbackStruct* info = (XmPushButtonCallbackStruct*)call_data;

    fe_EditorCut(context, info->event->xbutton.time);
}

static void
fe_editor_cut_update_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean sensitive = FALSE;

    if (fe_EditorCanCut(context))
	sensitive = TRUE;

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_copy_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    XmPushButtonCallbackStruct* info = (XmPushButtonCallbackStruct*)call_data;

    fe_EditorCopy(context, info->event->xbutton.time);
}

static void
fe_editor_copy_update_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean sensitive = FALSE;

    if (fe_EditorCanCopy(context))
	sensitive = TRUE;

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_paste_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    XmPushButtonCallbackStruct* info = (XmPushButtonCallbackStruct*)call_data;

    fe_EditorPaste(context, info->event->xbutton.time);
}

static void
fe_editor_paste_update_cb(Widget widget,
			  XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean sensitive = FALSE;

    if (fe_EditorCanPaste(context))
	sensitive = TRUE;

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_undo_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorUndo(context);
}

static void
fe_editor_undo_update_cb(Widget widget,
			  XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    sensitive = FALSE;
    XmString   string;
    Arg        args[8];
    Cardinal   n;

    if (fe_EditorCanUndo(context, &string))
	sensitive = TRUE;

    n = 0;
    XtSetArg(args[n], XtNsensitive, sensitive); n++;
    if (string) {
	XtSetArg(args[n], XmNlabelType, XmSTRING); n++;
	XtSetArg(args[n], XmNlabelString, string); n++;
    }
    XtSetValues(widget, args, n);
}

CB_STATIC void
fe_editor_redo_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorRedo(context);
}

static void
fe_editor_redo_update_cb(Widget widget,
			  XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    sensitive = FALSE;
    XmString   string;
    Arg        args[8];
    Cardinal   n;

    if (fe_EditorCanRedo(context, &string))
	sensitive = TRUE;

    n = 0;
    XtSetArg(args[n], XtNsensitive, sensitive); n++;
    if (string) {
	XtSetArg(args[n], XmNlabelType, XmSTRING); n++;
	XtSetArg(args[n], XmNlabelString, string); n++;
    }
    XtSetValues(widget, args, n);
}

void
fe_EditorFind(MWContext* context)
{
    MWContext* top_context = fe_GridToTopContext(context);
    
    if (!top_context)
	top_context = context;

    fe_UserActivity(top_context);

    fe_FindDialog(top_context, False);
}

static Boolean
fe_EditorCanFindAgain(MWContext* context)
{
    fe_FindData* find_data = CONTEXT_DATA(context)->find_data;
    
    return ((find_data != NULL)
	    &&
	    (find_data->string != NULL)
	    &&
	    (find_data->string[0] != '\0'));
}

void
fe_EditorFindAgain(MWContext* context)
{
    MWContext*   top_context = fe_GridToTopContext(context);
    fe_FindData* find_data;

    if (!top_context)
	top_context = context;

    fe_UserActivity(top_context);

    find_data = CONTEXT_DATA(top_context)->find_data;

  if (fe_EditorCanFindAgain(top_context))
      fe_FindDialog(top_context, TRUE);
  else
      fe_Bell(context);
}

CB_STATIC void
fe_editor_find_cb(Widget widget,
		  XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;

    fe_EditorFind(context);
}

CB_STATIC void
fe_editor_find_again_cb(Widget widget,
			XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;

    fe_EditorFindAgain(context);
}

static void
fe_editor_find_again_update_cb(Widget widget,
			       XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;
    Boolean      sensitive = fe_EditorCanFindAgain(context);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_select_all_cb(Widget widget,
			XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;

    fe_EditorSelectAll(context);
}

CB_STATIC void
fe_editor_delete_item_cb(Widget widget,
			XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;

    fe_EditorDeleteItem(context);
}

CB_STATIC void
fe_editor_select_table_cb(Widget widget,
			XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;

    fe_EditorSelectTable(context);
}

static void
fe_editor_select_table_update_cb(Widget widget,
			       XtPointer closure, XtPointer call_data)
{
    MWContext*   context = (MWContext *)closure;
    Boolean      sensitive = fe_EditorTableCanDelete(context);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_remove_links_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    fe_EditorRemoveLinks((MWContext*)closure);
}

static void
fe_editor_remove_links_update_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext*     context = (MWContext*)closure;
    ED_ElementType type = EDT_GetCurrentElementType(context);
    Boolean        sensitive = FALSE;

    if ((type == ED_ELEMENT_TEXT ||
	 type == ED_ELEMENT_SELECTION ||
	 type == ED_ELEMENT_IMAGE)
	&&
	EDT_CanSetHREF(context)) {
	sensitive = TRUE;
    }

    XtVaSetValues(widget, XtNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_edit_menu_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*         context =  (MWContext*)closure;

    Arg        args[4];
    Cardinal   n;
    Widget     pulldown_menu;
    Widget*    children;
    Cardinal   nchildren;
    char*      name;
    Widget     button;
    int        i;

    /*
     *    Get the popup menu from the cascade.
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, &pulldown_menu); n++;
    XtGetValues(widget, args, n);

    /*
     *    Get the children of the popup.
     */
    n = 0;
    XtSetArg(args[n], XmNchildren, &children); n++;
    XtSetArg(args[n], XmNnumChildren, &nchildren); n++;
    XtGetValues(pulldown_menu, args, n);

    for (i = 0; i < nchildren; i++) {

	button = children[i];
	name = XtName(button);
	
	if (strcmp(name, "undo") == 0)
	    fe_editor_undo_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "redo") == 0)
	    fe_editor_redo_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "cut") == 0) 
	    fe_editor_cut_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "copy") == 0) 
	    fe_editor_copy_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "paste") == 0) 
	    fe_editor_paste_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "findAgain") == 0)
	    fe_editor_find_again_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "selectTable") == 0)
	    fe_editor_select_table_update_cb(button, (XtPointer)context, NULL);

	else if (strcmp(name, "removeLinks") == 0)
	    fe_editor_remove_links_update_cb(button, (XtPointer)context, NULL);
    }
}

CB_STATIC void
fe_editor_insert_link_cb(Widget widget, XtPointer closure,
			 XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    if (EDT_IS_NEW_DOCUMENT(context)) {
	if (!FE_CheckAndSaveDocument(context))
	    return;
    }

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_LINK_INSERT);
}

CB_STATIC void
fe_editor_insert_target_cb(Widget widget, XtPointer closure,
			   XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorTargetPropertiesDialogDo(context);
}

CB_STATIC void
fe_editor_insert_image_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    if (EDT_IS_NEW_DOCUMENT(context)) {
	if (!FE_CheckAndSaveDocument(context))
	    return;
    }

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_IMAGE_INSERT);
}

CB_STATIC void
fe_editor_insert_hrule_dialog_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorHorizontalRulePropertiesDialogDo(context);
}

CB_STATIC void
fe_editor_insert_html_cb(Widget widget, XtPointer closure,
			 XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorHtmlPropertiesDialogDo(context);
}

CB_STATIC void
fe_editor_insert_line_break_cb(Widget widget, XtPointer closure,
			    XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    XtPointer* userdata;

    XtVaGetValues(widget, XmNuserData, &userdata, 0);
    
    fe_EditorLineBreak(context, (ED_BreakType)userdata);
}

CB_STATIC void
fe_editor_insert_line_break_update_cb(Widget widget,
				      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorLineBreakCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_insert_non_breaking_space_cb(Widget widget, XtPointer closure,
			    XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorNonBreakingSpace(context);    
}

static XtPointer
fe_insert_menu_mappee(Widget widget, XtPointer data)
{
    MWContext* context = (MWContext *)data;
    char*      name = XtName(widget);
    
    if (strcmp(name, "insertNewLineBreak") == 0)
	fe_editor_insert_line_break_update_cb(widget, context, NULL);
    else if (strcmp(name, "insertBreakBelow") == 0)
	fe_editor_insert_line_break_update_cb(widget, context, NULL);

    return 0;
}

CB_STATIC void
fe_editor_insert_menu_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    Widget menu;

    XtVaGetValues(widget, XmNsubMenuId, &menu, 0);

    fe_WidgetTreeWalk(menu, fe_insert_menu_mappee, closure);
}

CB_STATIC void
fe_editor_display_paragraph_marks_update_cb(Widget widget, XtPointer closure,
			    XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    set = fe_EditorParagraphMarksGetState(context);

    XmToggleButtonGadgetSetState(widget, set, FALSE);
}

CB_STATIC void
fe_editor_display_tables_cb(Widget widget, XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    set = fe_EditorDisplayTablesGet(context);
    fe_EditorDisplayTablesSet(context, !set);
}

CB_STATIC void
fe_editor_display_tables_update_cb(Widget widget,
				   XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    set = fe_EditorDisplayTablesGet(context);

    XmToggleButtonGadgetSetState(widget, set, FALSE);
}

static XtPointer
fe_view_menu_mappee(Widget widget, XtPointer data)
{
    MWContext* context = (MWContext *)data;
    char*      name = XtName(widget);
    
    if (strcmp(name, "paragraphMarks") == 0)
	fe_editor_display_paragraph_marks_update_cb(widget, context, NULL);
    else if (strcmp(name, "displayTables") == 0)
	fe_editor_display_tables_update_cb(widget, context, NULL);
    else if (strcmp(name, "reload") == 0)
	fe_editor_reload_update_cb(widget, context, NULL);

    return 0;
}

CB_STATIC void
fe_editor_view_menu_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    Widget menu;

    XtVaGetValues(widget, XmNsubMenuId, &menu, 0);

    fe_WidgetTreeWalk(menu, fe_view_menu_mappee, closure);
}

CB_STATIC void
fe_editor_windows_menu_cb(Widget widget, XtPointer closure,
			    XtPointer call_data)
{
    MWContext*      context = (MWContext *)closure;
    fe_ContextData* data = CONTEXT_DATA(context);
    Widget          submenu;

    fe_UserActivity(context);

    XtVaGetValues(widget, XmNsubMenuId, &submenu, 0);

    data->windows_menu_up_to_date_p = False;
    fe_GenerateWindowsMenu(context);
}

CB_STATIC void
fe_editor_paragraph_marks_cb(Widget widget, XtPointer closure,
			    XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    set;

    if (fe_EditorParagraphMarksGetState(context))
	set = FALSE; /* switch */
    else
	set = TRUE;

    fe_EditorParagraphMarksSetState(context, set);
    /* NOTE: view menu cascade callback sets button visual state */
}

#if 0
/* experimental, get rid of those fuckking switchs, and string switches */
typedef Widget (*fe_ToolbarWidgetCreateFunction)(
		  MWContext* context,
		  Widget     parent,
		  char*      name,
		  Arg*       args,
		  Cardinal   n
		  );
typedef struct fe_ToolbarWidgetClass {
    char*                          name;
    fe_ToolbarWidgetCreateFunction creator;
} fe_ToolbarWidgetClass;

static Widget
fe_CreateSizeGrowShrinkButton(
			      MWContext* context,
			      Widget     parent,
			      char*      name,
			      Arg*       args,
			      Cardinal   n
) {
    Widget button = fe_CreateToolbarButton(context, parent, name, args, n);
    XtAddCallback(buttons, XmNactivateCallback, fe_size_shrink_grow_cb, context);
}

static fe_ToolbarWidgetClass fe_SizeGrowShrinkButton = {
    "CharSizeGrowShrinkButton",
    fe_CreateSizeGrowShrinkButton
};

struct {
    fe_ToolbarWidgetClass* class;
    char*                  name; /* instance name */
    XtCallbackProc         callback;
    XtPointer              closure; /* passed as XmNuserdata */
} fe_editor_character_toolbar[] = {
  { fe_SizeGrowShrinkButton, "charSizeShrink", 0, (XtPointer)TRUE  },
  { fe_SizeGrowShrinkButton, "charSizeGrow",   0, (XtPointer)FALSE },
  { fe_SizeMenu,             "charSizeMenu" }
  { fe_SpacerLabel },
  { fe_TextAttributeButton,  "textBold",       0, (XtPointer)TF_BOLD },
  { fe_TextAttributeButton,  "textItalic",     0, (XtPointer)TF_ITALIC },
  { fe_TextAttributeButton,  "textFixed",      0,  (XtPointer)TF_FIXED },
  { fe_ToolbarButton,       "textColor",      color_callback }
  { fe_ToolbarButton,       "link",           link_callback }
  { fe_ToolbarButton,       "plain",          clear_callback }
  { fe_SpacerLabel },
  { fe_ToolbarButton,       "targetInsert",   target_callback }
  { fe_ToolbarButton,       "imageInsert",    image_callback }
  { fe_ToolbarButton,       "hruleInsert",    hrule_callback }
  { fe_SpacerLabel },
  { fe_ToolbarButton,       "properties",     properties_callback }
  { 0 }
};

Widget
fe_SizeMenuCreate(
		  MWContext* context,
		  Widget     parent,
		  char*      name
		  )
{
}
typedef Widget (*fe_ToolbarWidgetCreateFunction)(
		  MWContext* context,
		  Widget     parent,
		  char*      name
		  );
#endif

typedef struct fe_OptionMenuItemDescription {
    char* name;  /* of widget               */
    char* label; /* delete me */
    void* data;  /* gets passed as userdata */
} fe_OptionMenuItemDescription;

static Widget
create_option_menu(
		   MWContext* context,
		   Widget parent,
		   char* name,
		   fe_OptionMenuItemDescription* list,
		   Arg* p_argv, Cardinal p_argc
) {
  unsigned i;
  Widget   popup_menu;
  Widget   buttons[16];
  Widget   option_menu;
  Arg      argv[8];
  Cardinal argc;
  Visual*  v = 0;
  Colormap cmap = 0;
  Cardinal depth = 0;

  XtCallbackRec* button_callback_rec = NULL;

  for (i = 0; i < p_argc; i++) {
      if (p_argv[i].name == XmNactivateCallback)
	  button_callback_rec = (XtCallbackRec*)p_argv[i].value;
  }
  
  XtVaGetValues(CONTEXT_WIDGET (context), XtNvisual, &v, XtNcolormap, &cmap,
	XtNdepth, &depth, 0);

  argc = 0;
  XtSetArg (argv[argc], XmNvisual, v); argc++;
  XtSetArg (argv[argc], XmNdepth, depth); argc++;
  XtSetArg (argv[argc], XmNcolormap, cmap); argc++;
#if 0
  sprintf(buf, "%sPulldown", name);
#endif
  popup_menu = XmCreatePulldownMenu(parent, name, argv, argc);

  for (i = 0; list[i].name; i++) {
      argc = 0;
      XtSetArg(argv[argc], XmNuserData, (XtPointer)list[i].data); argc++;
      buttons[i] = XmCreatePushButtonGadget(popup_menu, list[i].name, argv, argc);
      
      if (button_callback_rec)
	  XtAddCallback(
			buttons[i],
			XmNactivateCallback,
			button_callback_rec->callback,
			(XtPointer)context
			);
  }

  XtManageChildren(buttons, i);

  argc = 0;
  XtSetArg(argv[argc], XmNsubMenuId, popup_menu); argc++;
  option_menu = fe_CreateOptionMenu(parent, name, argv, argc);

  /*
   *    Hide the label, as we don't use it.
   */
  fe_UnmanageChild_safe(XmOptionLabelGadget(option_menu));

  return option_menu;
}


/* -------------------------------------------------------------- */

/*
 *    Size Group.
 */
static fe_OptionMenuItemDescription fe_size_menu_items[] =
{
    { "minusTwo", "-2", (void*)ED_FONTSIZE_MINUS_TWO },
    { "minusOne", "-1", (void*)ED_FONTSIZE_MINUS_ONE },
    { "plusZero", "+0", (void*)ED_FONTSIZE_ZERO      },
    { "plusOne",  "+1", (void*)ED_FONTSIZE_PLUS_ONE  },
    { "plusTwo",  "+2", (void*)ED_FONTSIZE_PLUS_TWO  },
    { "plusThree","+3", (void*)ED_FONTSIZE_PLUS_THREE },
    { "plusFour", "+4", (void*)ED_FONTSIZE_PLUS_FOUR },
    { NULL }
};
#define FE_FONT_NSIZES (countof(fe_size_menu_items) - 1) /* -1 for null terminator */

void
fe_EditorFontSizeSet(MWContext* context, ED_FontSize edt_size)
{
    if (edt_size >= EDT_FONTSIZE_MIN && edt_size <= EDT_FONTSIZE_MAX) {
#if 1
	EDT_SetFontSize(context, edt_size);
#else
	/* this does not work */
	EDT_CharacterData* edt_cdata = EDT_GetCharacterData(context);
	edt_cdata->mask = TF_FONT_SIZE;
	edt_cdata->values = TF_FONT_SIZE;
	edt_cdata->iSize = edt_size;
	EDT_SetCharacterData(context, edt_cdata);
	EDT_FreeCharacterData(edt_cdata);
#endif
    } else {
	fe_Bell(context);
    }

    fe_EditorUpdateToolbar(context, TF_FONT_SIZE);
}

ED_FontSize
fe_EditorFontSizeGet(MWContext* context)
{
    EDT_CharacterData* edt_cdata = EDT_GetCharacterData(context);
    intn               edt_size = edt_cdata->iSize;

    if (edt_size < EDT_FONTSIZE_MIN)
	edt_size = ED_FONTSIZE_ZERO;
    
    EDT_FreeCharacterData(edt_cdata);

    return (ED_FontSize)edt_size;
}

static void
fe_size_button_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*  context = (MWContext *)closure;
    unsigned    is_growser = (unsigned)fe_GetUserData(widget);
    ED_FontSize edt_size = fe_EditorFontSizeGet(context);

    if (is_growser) /* up */
	edt_size++;
    else
	edt_size--;

    if (edt_size >= EDT_FONTSIZE_MIN && edt_size <= EDT_FONTSIZE_MAX)
	fe_EditorFontSizeSet(context, edt_size);
}

CB_STATIC void
fe_editor_font_size_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    unsigned   edt_size = (unsigned)fe_GetUserData(widget);

    fe_EditorFontSizeSet(context, edt_size);
}

static void
fe_set_toggle_state(MWContext* context, Widget cascade, unsigned match_userdata, Boolean radio)
{
    Arg        args[4];
    Cardinal   n;
    Widget     popup_menu;
    WidgetList children;
    Cardinal   nchildren;
    unsigned   userdata;
    Widget     widget;
    Boolean    set;
    int i;

    /*
     *    Get the pulldown menu from the cascade.
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, &popup_menu); n++;
    XtGetValues(cascade, args, n);

    /*
     *    Get the children of the popup.
     */
    n = 0;
    XtSetArg(args[n], XmNchildren, &children); n++;
    XtSetArg(args[n], XmNnumChildren, &nchildren); n++;
    XtGetValues(popup_menu, args, n);

    /*
     *    Set the state of the buttons.
     */
    for (i = 0; i < nchildren; i++) {

	widget = children[i];
	if (XtIsSubclass(widget, xmToggleButtonGadgetClass)) {
	    userdata = (unsigned)fe_GetUserData(widget);

	    if ((radio && (userdata == match_userdata)) || (!radio && (userdata & match_userdata)))
		set = TRUE;
	    else
		set = FALSE;

	    XmToggleButtonGadgetSetState(widget, set, FALSE);
	}
    }
}

CB_STATIC void
fe_editor_font_size_menu_cb(Widget cascade, XtPointer closure, XtPointer call_data)
{
    MWContext*  context = (MWContext *)closure;
    ED_FontSize size = fe_EditorFontSizeGet(context);

    fe_set_toggle_state(context, cascade, (unsigned)size, TRUE);
}

Widget
fe_OptionMenuSetHistory(Widget menu, unsigned index)
{
    Arg        args[4];
    Cardinal   n;
    Widget     cascade;
    Widget     popup_menu;
    WidgetList children;
    Cardinal   nchildren;

    /*
     *   Update the label, and set the position of the popup.
     */
    cascade = XmOptionButtonGadget(menu);

    /*
     *    Get the popup menu from the cascade.
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, &popup_menu); n++;
    XtGetValues(cascade, args, n);

    /*
     *    Get the children of the popup.
     */
    n = 0;
    XtSetArg(args[n], XmNchildren, &children); n++;
    XtSetArg(args[n], XmNnumChildren, &nchildren); n++;
    XtGetValues(popup_menu, args, n);
    if (index < nchildren) {
	/*
	 *    Finally, set the Nth button as history.
	 */
	n = 0;
	XtSetArg(args[n], XmNmenuHistory, children[index]); n++;
	/* NOTE: set it on the top level menu (strange) */
	XtSetValues(menu, args, n);
	
	return children[index];
    }
    return NULL;
}

static void
fe_SizeGroupUpdate(fe_EditorContextData* editor, int edt_size)
{
    Boolean sensitive;

    if (edt_size <= 0)
	edt_size = 3; /* + 0 */

    if (edt_size > EDT_FONTSIZE_MAX)
	edt_size = EDT_FONTSIZE_MAX;

    /*
     *    Set Shrink, grow buttons.
     */
    if (editor->toolbar_smaller) {
	if (edt_size > EDT_FONTSIZE_MIN)
	    sensitive = TRUE;
	else
	    sensitive = FALSE;
	XtVaSetValues(editor->toolbar_smaller, XtNsensitive, sensitive, 0);
    }

    if (editor->toolbar_bigger) {
	if (edt_size < EDT_FONTSIZE_MAX)
	    sensitive = TRUE;
	else
	    sensitive = FALSE;
	XtVaSetValues(editor->toolbar_bigger, XtNsensitive, sensitive, 0);
    }

    if (editor->toolbar_size) {
	fe_OptionMenuSetHistory(editor->toolbar_size, edt_size - 1);
    }
}

/* -------------------------------------------------------------- */
/*
 *    Text Attribute set.
 */
void
fe_EditorCharacterPropertiesSet(MWContext* context, ED_TextFormat values)
{
    EDT_CharacterData cdata;

    memset(&cdata, 0, sizeof(EDT_CharacterData));

    cdata.mask = TF_ALL_MASK;
    if (values == TF_NONE) {   /* pop everything */
	cdata.values = 0;
    } else if (values == (TF_SERVER|TF_SCRIPT)) {
	cdata.mask = TF_SERVER|TF_SCRIPT;
	cdata.values = 0;
    } else if (values == TF_SERVER || values == TF_SCRIPT) {
	cdata.mask = TF_SERVER|TF_SCRIPT;
	cdata.values = values;
    } else {
	values &= TF_ALL_MASK; /* don't let them shoot themselves */
	cdata.values = values;
    }

    EDT_SetCharacterData(context, &cdata);

    fe_EditorUpdateToolbar(context, values);
}

ED_TextFormat
fe_EditorCharacterPropertiesGet(MWContext* context)
{
    EDT_CharacterData* edt_cdata = EDT_GetCharacterData(context);
#if 0
    ED_TextFormat      values = (edt_cdata->values & TF_ALL_MASK);
#else
    ED_TextFormat      values = edt_cdata->values;
#endif

    EDT_FreeCharacterData(edt_cdata);
    
    return values;
}

static void
fe_EditorDoPoof(MWContext* context)
{
    Boolean            clear_link = TRUE;
    EDT_CharacterData* pData;
	
    if (EDT_SelectionContainsLink(context)) {
	/*Do you want to remove the link?*/
	if (!XFE_Confirm(context,
			 XP_GetString(XFE_EDITOR_WARNING_REMOVE_LINK)))
	    clear_link = FALSE;
    }

    if (clear_link) {
	EDT_FormatCharacter(context, TF_NONE);
    } else {
	pData = EDT_GetCharacterData(context);
        if (pData) {
            pData->mask = ~TF_HREF;
            pData->values = TF_NONE;
            EDT_SetCharacterData(context, pData);
            EDT_FreeCharacterData(pData);
        }
    }
    fe_EditorUpdateToolbar(context, 0);
}

CB_STATIC void
fe_editor_clear_char_props_cb(Widget widget, XtPointer closure,
			      XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorDoPoof(context);
}

CB_STATIC void
fe_editor_char_props_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*     context = (MWContext *)closure;
    ED_ETextFormat mask = (ED_ETextFormat)fe_GetUserData(widget);

    fe_EditorCharacterPropertiesSet(context, mask);
}

CB_STATIC void
fe_editor_toggle_char_props_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*     context = (MWContext *)closure;
    ED_ETextFormat mask = (ED_ETextFormat)fe_GetUserData(widget);
    ED_TextFormat  values = fe_EditorCharacterPropertiesGet(context);

    values ^= mask; /* toggle */

    /*
     *    Super and Sub should be mutually exclusive.
     */
    if (mask == TF_SUPER && (values & TF_SUPER) != 0)
	values &= ~TF_SUB;
    else if (mask == TF_SUB && (values & TF_SUB) != 0)
	values &= ~TF_SUPER;
    
    fe_EditorCharacterPropertiesSet(context, values);
}

CB_STATIC void
fe_editor_char_props_menu_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext*     context = (MWContext *)closure;
    ED_ETextFormat values = fe_EditorCharacterPropertiesGet(context);

    fe_set_toggle_state(context, widget, (unsigned)values, FALSE);
}

static void
fe_TextAttributeGroupUpdate(fe_EditorContextData* editor, ED_TextFormat attributes)
{
    XmToggleButtonGadgetSetState(editor->toolbar_bold, 
				 ((attributes & TF_BOLD) != 0),
				 FALSE);
    XmToggleButtonGadgetSetState(editor->toolbar_italic,
				 ((attributes & TF_ITALIC) != 0),
				 FALSE);
    XmToggleButtonGadgetSetState(editor->toolbar_fixed, 
				 ((attributes & TF_FIXED) != 0),
				 FALSE);
}

/* -------------------------------------------------------------- */
/*
 *    Horizontal Rule.
 */
CB_STATIC void
fe_editor_insert_hrule_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*     context = (MWContext *)closure;

    /*
     *    Hrule is so simple, just use default values
     *   instead of bringing up properties dialog
     */
    EDT_HorizRuleData *pData = EDT_NewHorizRuleData();
    if (pData) {
        EDT_InsertHorizRule(context, pData);
	EDT_FreeHorizRuleData(pData);
    }
}

void
fe_EditorIndent(MWContext* context, Boolean is_indent)
{
    if (is_indent)
	EDT_Outdent(context);
    else
	EDT_Indent(context);
}

/* -------------------------------------------------------------- */
/*
 *    List set.
 */

/* -------------------------------------------------------------- */
/*
 *    Indent Set.
 */
CB_STATIC void
fe_editor_indent_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    unsigned   is_indent = (unsigned)fe_GetUserData(widget);

    fe_EditorIndent(context, !is_indent);
}

/* -------------------------------------------------------------- */
/*
 *    Align Set.
 */
void
fe_EditorAlignSet(MWContext* pMWContext, ED_Alignment align)
{
    ED_ElementType type = EDT_GetCurrentElementType(pMWContext);

    switch ( type ){
        case ED_ELEMENT_HRULE:
        {
            EDT_HorizRuleData* pData = EDT_GetHorizRuleData(pMWContext);
            if ( pData ){
                pData->align = align;
                EDT_SetHorizRuleData(pMWContext, pData);
            }
            break;
        }
       default: /* For Images, Text, or selection, this will do all: */
            EDT_SetParagraphAlign( pMWContext, align );
            break;
    }

    fe_EditorUpdateToolbar(pMWContext, 0);
}

ED_Alignment
fe_EditorAlignGet(MWContext* pMWContext)
{
   ED_ElementType type = EDT_GetCurrentElementType(pMWContext);
   EDT_HorizRuleData h_data;

   if (type == ED_ELEMENT_HRULE) {
       fe_EditorHorizontalRulePropertiesGet(pMWContext, &h_data);
       return h_data.align;
   } else { /* For Images, Text, or selection, this will do all: */
       return EDT_GetParagraphAlign(pMWContext);
   }
}

static void
fe_align_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext*     context = (MWContext *)closure;
    ED_Alignment   align = (ED_Alignment)fe_GetUserData(widget);
    
    fe_EditorAlignSet(context, align);
}

static void
fe_AlignGroupUpdate(fe_EditorContextData* editor, ED_Alignment align)
{
    unsigned mask;

    switch (align) {
    case ED_ALIGN_LEFT:  
    case ED_ALIGN_DEFAULT:mask = 0x1; break;
    case ED_ALIGN_ABSCENTER:
    case ED_ALIGN_CENTER: mask = 0x2; break;
    case  ED_ALIGN_RIGHT: mask = 0x4; break;
    default:              mask = 0;   break;
    }

    XmToggleButtonGadgetSetState(editor->toolbar_left,
				 ((mask & 0x1) != 0), FALSE);
    XmToggleButtonGadgetSetState(editor->toolbar_center,
				 ((mask & 0x2) != 0), FALSE);
    XmToggleButtonGadgetSetState(editor->toolbar_right,
				 ((mask & 0x4) != 0), FALSE);
}

/* -------------------------------------------------------------- */
/*
 *    List Set.
 */
static void
fe_toggle_list_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    intn       iTagType = (intn)fe_GetUserData(widget);

    EDT_ToggleList(context, iTagType);

    fe_EditorUpdateToolbar(context, 0);
}

static void
fe_toggle_list_update(fe_EditorContextData* editor, intn iTagType)
{
    unsigned mask;

    switch (iTagType) {
    case P_UNUM_LIST: mask = 0x1; break;
    case P_NUM_LIST:  mask = 0x2; break;
    default:          mask = 0;   break;
    }

    XmToggleButtonGadgetSetState(editor->toolbar_list,
				 (mask & 0x1)!=0, FALSE);
    XmToggleButtonGadgetSetState(editor->toolbar_numbers,
				 (mask & 0x2)!=0, FALSE);
}

/* -------------------------------------------------------------- */
/*
 *    Paragraph Styles Set.
 */
/*
 *   This is major bad non-NLS, but I just want to get teh look going....djw
 */
static fe_OptionMenuItemDescription fe_paragraph_style_menu_items[] = {
    { "normal",          "Normal",          (void*)P_PARAGRAPH  },
    { "headingOne",      "Heading 1",       (void*)P_HEADER_1   },
    { "headingTwo",      "Heading 2",       (void*)P_HEADER_2   },
    { "headingThree",    "Heading 3",       (void*)P_HEADER_3   },
    { "headingFour",     "Heading 4",       (void*)P_HEADER_4   },
    { "headingFive",     "Heading 5",       (void*)P_HEADER_5   },
    { "headingSix",      "Heading 6",       (void*)P_HEADER_6   },
    { "address",         "Address",         (void*)P_ADDRESS    },
    { "formatted",       "Formatted",       (void*)P_PREFORMAT  },
    { "listItem",        "List Item",       (void*)P_LIST_ITEM  },
    { "descriptionItem", "Description Item",(void*)P_DESC_TITLE },
    { "descriptionText", "Description Text",(void*)P_DESC_TEXT  },
    { 0 }
};

#define FE_PARAGRAPH_NSTYLES (countof(fe_paragraph_style_menu_items) - 1) /* -1 for null terminator */

TagType
fe_EditorParagraphPropertiesGet(MWContext* context)
{
    return EDT_GetParagraphFormatting(context);
}

void
fe_EditorParagraphPropertiesSet(MWContext* context, TagType type)
{
#if 1
    TagType paragraph_style = EDT_GetParagraphFormatting(context);

    if (type != paragraph_style) {
	EDT_MorphContainer(context, type);

	fe_EditorUpdateToolbar(context, 0);
    }
#else
    /*
     *    This seems like the correct code, as it would mean the toolbar
     *    menu and the properties dialog have the same effect when you set
     *    a list. But this would be different from the Windows version.
     *    Do above, the same as Windows.
     */
    if (type == P_LIST_ITEM) {
        EDT_ListData list_data;

        list_data.iTagType = P_UNUM_LIST;
        list_data.eType = ED_LIST_TYPE_DISC;
        list_data.bCompact = FALSE;

        fe_EditorParagraphPropertiesSetAll(context, type, &list_data,
					   ED_ALIGN_DEFAULT);
    } else {
        fe_EditorParagraphPropertiesSetAll(context, type, NULL,
					   ED_ALIGN_DEFAULT);
    }
#endif
}

CB_STATIC void
fe_editor_paragraph_props_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    unsigned   foo = (unsigned)fe_GetUserData(widget);
    TagType    new_paragraph_style = foo;

    fe_EditorParagraphPropertiesSet(context, new_paragraph_style);
}

CB_STATIC void
fe_editor_paragraph_props_menu_cb(Widget widget,
				  XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    TagType    paragraph_style = fe_EditorParagraphPropertiesGet(context);

    fe_set_toggle_state(context, widget, (unsigned)paragraph_style, TRUE);
}

static void
fe_editor_character_properties_dialog_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_CHARACTER);
}

static void
fe_editor_paragraph_properties_dialog_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_PARAGRAPH);
}

static void
fe_editor_image_properties_dialog_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_IMAGE);
}

static void
fe_editor_image_properties_dialog_update_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    sensitive;
    sensitive = (EDT_GetCurrentElementType(context) == ED_ELEMENT_IMAGE);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

static void
fe_editor_link_properties_dialog_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_LINK);
}

static void
fe_editor_link_properties_dialog_update_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    sensitive;
    sensitive = (EDT_CanSetHREF(context) != FALSE);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

static void
fe_editor_target_properties_dialog_update_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    sensitive;
    sensitive = (EDT_GetCurrentElementType(context) == ED_ELEMENT_TARGET);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

static void
fe_editor_hrule_properties_dialog_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_HRULE);
}

static void
fe_editor_hrule_properties_dialog_update_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    sensitive;

    sensitive = (EDT_GetCurrentElementType(context) == ED_ELEMENT_HRULE);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_properties_menu_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    Arg        args[4];
    Cardinal   n;
    Widget     pulldown_menu;
    Widget*    children;
    Cardinal   nchildren;
    char*      name;
    Widget     button;
    Boolean    sensitive;
    int        i;
    fe_EditorPropertiesDialogType type;

    /*
     *    Get the popup menu from the cascade.
     */
    n = 0;
    XtSetArg(args[n], XmNsubMenuId, &pulldown_menu); n++;
    XtGetValues(widget, args, n);

    /*
     *    Get the children of the popup.
     */
    n = 0;
    XtSetArg(args[n], XmNchildren, &children); n++;
    XtSetArg(args[n], XmNnumChildren, &nchildren); n++;
    XtGetValues(pulldown_menu, args, n);

    for (i = 0; i < nchildren; i++) {

	button = children[i];
	name = XtName(button);

	if ( /* hack, hack, hack - we need dependencies!!!! */
	    strcmp(name, "linkProperties") == 0
	    ||
	    strcmp(name, "targetProperties") == 0
	    ||
	    strcmp(name, "imageProperties") == 0
	    ||
	    strcmp(name, "hruleProperties") == 0
	    ||
	    strcmp(name, "tagProperties") == 0
        ) {
	    type = (fe_EditorPropertiesDialogType)fe_GetUserData(button);
	    
	    if (fe_EditorPropertiesDialogCanDo(context, type))
		sensitive = TRUE;
	    else
		sensitive = FALSE;
	} else if (strcmp(name, "tableProperties") == 0) {
	    sensitive = fe_EditorTableCanDelete(context);
	} else {
	    continue; /* skip setval */
	}

	n = 0;
	XtSetArg(args[n], XtNsensitive, sensitive); n++;
	XtSetValues(button, args, n);
    }
}

CB_STATIC void
fe_editor_hrule_properties_cb(Widget widget, XtPointer closure,
			      XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    if (fe_EditorHorizontalRulePropertiesCanDo(context))
       fe_EditorHorizontalRulePropertiesDialogDo(context);
}

static void
fe_paragraph_style_toolbar_update(fe_EditorContextData* editor, TagType style)
{
    unsigned   index;

    if (editor->toolbar_style) {

	/*
	 *    Search through style table looking for match to this style.
	 */
	for (index = FE_PARAGRAPH_NSTYLES - 1; index > 0; index--) {
	    if ((TagType)(unsigned)fe_paragraph_style_menu_items[index].data == style)
		break;
	}

	fe_OptionMenuSetHistory(editor->toolbar_style, index);
    }
}

static Widget
fe_hack_out_editor_toggle_button(MWContext* context, Widget parent,
				 char* name, int icon_number)
{
    Pixmap   p;
    Arg      args[20];
    Cardinal n;

    p =  fe_ToolbarPixmap(context, icon_number, False, False);
      
    n = 0;
    XtSetArg(args[n], XmNlabelType, XmPIXMAP); n++;
    XtSetArg(args[n], XmNlabelPixmap, p); n++;
    XtSetArg(args[n], XmNindicatorOn, FALSE); n++; /* Windows-esk */
    XtSetArg(args[n], XmNvisibleWhenOff, TRUE); n++;
    XtSetArg(args[n], XmNindicatorType, XmN_OF_MANY); n++;
    XtSetArg(args[n], XmNshadowThickness, 2); n++; /* why do I need this? */
    return XmCreateToggleButtonGadget(parent, name, args, n);
}

static Widget
fe_hack_out_editor_push_button(MWContext* context, Widget parent,
				 char* name, int icon_number)
{
    Pixmap   p;
    Pixmap   ip;
    Arg      args[20];
    Cardinal n = 0;

    p =  fe_ToolbarPixmap(context, icon_number, False, False);
    ip = fe_ToolbarPixmap(context, icon_number, True, False);
      
    if (!ip)
	ip = p;
      
    XtSetArg(args[n], XmNlabelType, XmPIXMAP); n++;
    XtSetArg(args[n], XmNlabelPixmap, p); n++;
    XtSetArg(args[n], XmNlabelInsensitivePixmap, ip); n++;

    return XmCreatePushButtonGadget(parent, name, args, n);
}

CB_STATIC void 
fe_editor_properties_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    fe_EditorPropertiesDialogType type;
    ED_ElementType e_type;

    type = (fe_EditorPropertiesDialogType)fe_GetUserData(widget);

    if (type == XFE_EDITOR_PROPERTIES_OOPS) {

	if (EDT_GetHREF(context)) {
	    type = XFE_EDITOR_PROPERTIES_LINK;
	} else {
	    e_type = EDT_GetCurrentElementType(context);

	    if (e_type == ED_ELEMENT_HRULE)
		type = XFE_EDITOR_PROPERTIES_HRULE;
	    else if (e_type == ED_ELEMENT_IMAGE)
		type = XFE_EDITOR_PROPERTIES_IMAGE;
	    else if (fe_EditorTableCanDelete(context))
		type = XFE_EDITOR_PROPERTIES_TABLE;
	    else
		type = XFE_EDITOR_PROPERTIES_CHARACTER;
	}
    }

    /*
     *    Validate that we can do this kind of dialog right now.
     */
    if (fe_EditorPropertiesDialogCanDo(context, type)) {
	/*
	 *   Yuck, ugly. User must save before inerting links
	 *   or images.
	 */
	if (EDT_IS_NEW_DOCUMENT(context)
	    &&
	    (type == XFE_EDITOR_PROPERTIES_IMAGE_INSERT
	     ||
	     type == XFE_EDITOR_PROPERTIES_LINK_INSERT)
	) {
	    if (!FE_CheckAndSaveDocument(context))
		return;
	}
	fe_EditorPropertiesDialogDo(context, type);
    } else {
	fe_Bell(context);
    }
}

CB_STATIC void 
fe_editor_document_properties_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorDocumentPropertiesDialogDo(context,
			XFE_EDITOR_DOCUMENT_PROPERTIES_APPEARANCE);
}

CB_STATIC void
fe_editor_target_properties_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorTargetPropertiesDialogDo(context);
}

CB_STATIC void
fe_editor_html_properties_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorHtmlPropertiesDialogDo(context);
}

static void
fe_editor_html_properties_dialog_update_cb(Widget widget,
					    XtPointer closure,
					    XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean    sensitive;
    sensitive = (EDT_GetCurrentElementType(context) == ED_ELEMENT_UNKNOWN_TAG);

    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void 
fe_editor_preferences_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorPreferencesDialogDo(context,
				  XFE_EDITOR_DOCUMENT_PROPERTIES_APPEARANCE);
}

static void
fe_editor_show_menubar_cb(Widget widget,
			  XtPointer closure, XtPointer call_data)
{
  MWContext *context = (MWContext *) closure;
  fe_UserActivity (context);
  if (!CONTEXT_DATA(context)->show_menubar_p) {
      CONTEXT_DATA(context)->show_menubar_p = True;
      fe_RebuildWindow(context);
  }
}

CB_STATIC void
fe_editor_show_character_toolbar_cb(Widget widget,
				    XtPointer closure, XtPointer call_data)
{
  MWContext *context = (MWContext *) closure;
  Boolean toolbar_p = False;

  fe_UserActivity (context);
  XtVaGetValues (widget, XmNset, &toolbar_p, 0);
  if (CONTEXT_DATA (context)->show_character_toolbar_p == toolbar_p)
    return;
  CONTEXT_DATA (context)->show_character_toolbar_p = toolbar_p;
  fe_RebuildWindow (context);
}

static void
fe_editor_show_character_toolbar_update_cb(Widget widget,
				    XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    set = CONTEXT_DATA (context)->show_character_toolbar_p;

    XtVaSetValues(widget, XmNset, set, 0);
}

CB_STATIC void
fe_editor_show_paragraph_toolbar_cb(Widget widget,
				    XtPointer closure, XtPointer call_data)
{
  MWContext *context = (MWContext *) closure;
  Boolean toolbar_p = False;

  fe_UserActivity (context);
  XtVaGetValues (widget, XmNset, &toolbar_p, 0);
  if (CONTEXT_DATA (context)->show_paragraph_toolbar_p == toolbar_p)
    return;
  CONTEXT_DATA (context)->show_paragraph_toolbar_p = toolbar_p;
  fe_RebuildWindow (context);
}

static void
fe_editor_show_paragraph_toolbar_update_cb(Widget widget,
				    XtPointer closure, XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;
    Boolean    set = CONTEXT_DATA (context)->show_paragraph_toolbar_p;

    XtVaSetValues(widget, XmNset, set, 0);
}

CB_STATIC void
fe_editor_options_menu_cb(Widget cascade,
			  XtPointer closure, XtPointer call_data)
{
    Widget     pulldown;
    WidgetList children;
    Cardinal   nchildren;
    Cardinal i;
    Widget button;
    char* name;

    XtVaGetValues(cascade, XmNsubMenuId, &pulldown, 0);
    XtVaGetValues(pulldown, XmNchildren, &children,
		  XmNnumChildren, &nchildren, 0);

    for (i = 0; i < nchildren; i++) {

	button = children[i];
	name = XtName(button);
	/* update */

	if (strcmp(name, "showCharacter") == 0)
	    fe_editor_show_character_toolbar_update_cb(button, closure, 0);
	else if (strcmp(name, "showParagraph") == 0)
	    fe_editor_show_paragraph_toolbar_update_cb(button, closure, 0);
    }
}

CB_STATIC void
fe_editor_table_toolbar_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;

    /*
     *    Always do the insert dialog.
     */
    fe_EditorTableCreateDialogDo(context);
}

CB_STATIC void
fe_editor_set_colors_dialog_cb(Widget widget, XtPointer closure,
			     XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorSetColorsDialogDo(context);
}

CB_STATIC void
fe_editor_default_color_cb(Widget widget, XtPointer closure,
			   XtPointer call_data)
{
    MWContext* context = (MWContext *)closure;

    fe_EditorColorSet(context, NULL);
}

#define WOFF(x) ((void *) (XtOffset(fe_ContextData *, editor.x)))

static struct fe_button fe_character_toolbar[] =
{
  { "charSizeShrink", fe_size_button_cb, SELECTABLE, 0, WOFF(toolbar_smaller),(void*)FALSE      },
  { "charSizeGrow",   fe_size_button_cb, SELECTABLE, 0, WOFF(toolbar_bigger), (void*)TRUE       },
  { "fontSize",       fe_editor_font_size_cb,        CASCADE,   (void*)fe_size_menu_items, WOFF(toolbar_size) },
  FE_MENU_SEPARATOR,
  { "bold",       fe_editor_toggle_char_props_cb,   TOGGLE, 0, WOFF(toolbar_bold),   (void*)TF_BOLD    },
  { "italic",     fe_editor_toggle_char_props_cb,   TOGGLE, 0, WOFF(toolbar_italic), (void*)TF_ITALIC  },
  { "fixed",      fe_editor_toggle_char_props_cb,   TOGGLE, 0, WOFF(toolbar_fixed),  (void*)TF_FIXED   },
  { "textColor",      fe_editor_set_colors_dialog_cb,   SELECTABLE, 0 },
  { "makeLink",       fe_editor_properties_dialog_cb,   SELECTABLE,
    0, WOFF(toolbar_link), (void*)XFE_EDITOR_PROPERTIES_LINK_INSERT },
  { "clearAllStyles", fe_editor_clear_char_props_cb,   SELECTABLE, 0, WOFF(toolbar_plain),  (void*)TF_NONE },
  FE_MENU_SEPARATOR,
  { "insertTarget",   fe_editor_target_properties_dialog_cb, SELECTABLE, 0 },
  { "insertImage",    fe_editor_properties_dialog_cb,   SELECTABLE,
    0, 0, (void*)XFE_EDITOR_PROPERTIES_IMAGE_INSERT },
  { "insertHorizontalLine", fe_editor_insert_hrule_cb, SELECTABLE, 0,
    WOFF(toolbar_hrule) },
  { "insertTable",    fe_editor_table_toolbar_cb, SELECTABLE, 0 },
  FE_MENU_SEPARATOR,
  { "objectProperties",     fe_editor_properties_dialog_cb,SELECTABLE, 0 },
  { 0 }
};

static struct fe_button fe_paragraph_toolbar[] =
{
  { "paragraphProperties", fe_editor_paragraph_props_cb, CASCADE, (void*)fe_paragraph_style_menu_items, WOFF(toolbar_style) },
  FE_MENU_SEPARATOR,
  { "insertBulleted",  fe_toggle_list_cb,   TOGGLE, 0, WOFF(toolbar_list), (void*)P_UNUM_LIST },
  { "insertNumbered",  fe_toggle_list_cb,   TOGGLE, 0, WOFF(toolbar_numbers), (void*)P_NUM_LIST },
  FE_MENU_SEPARATOR,
  { "outdent",         fe_editor_indent_cb, SELECTABLE, 0, WOFF(toolbar_outdent), (void*)FALSE },
  { "indent",          fe_editor_indent_cb, SELECTABLE, 0, WOFF(toolbar_indent),  (void*)TRUE  },
  FE_MENU_SEPARATOR,
  { "alignLeft",       fe_align_cb,         TOGGLE, 0, WOFF(toolbar_left), (void*)ED_ALIGN_LEFT },
  { "alignCenter",     fe_align_cb,         TOGGLE, 0, WOFF(toolbar_center), (void*)ED_ALIGN_ABSCENTER },
  { "alignRight",      fe_align_cb,         TOGGLE, 0, WOFF(toolbar_right), (void*)ED_ALIGN_RIGHT },
  { 0 }
};
#undef WOFF

/*
 *    Special code to make editor toolbars.
 */
static void
do_work(MWContext* context, Widget toolbar, struct fe_button* tb, int icon_number)
{
  Widget kids[40];
  Arg    av[20];
  int ac;
  int i, j;
  char* name;
  int tb_size;
  Widget b;
  XtCallbackRec cb_data;

  for (tb_size = 0; tb[tb_size].name; tb_size++)
    ;

  for (i = 0, j = 0; i < tb_size; i++) {
    name = tb[i].name;
    ac = 0;

    if (name[0] == '\0') {
      XmString xms = XmStringCreateLtoR ("", XmSTRING_DEFAULT_CHARSET);
      XtSetArg (av[ac], XmNlabelType, XmSTRING); ac++;
      XtSetArg (av[ac], XmNlabelString, xms); ac++;
      b = XmCreateLabelGadget (toolbar, "spacer", av, ac);
      XmStringFree (xms);
    } else if (tb[i].type == CASCADE) {
	ac = 0;
	if (tb[i].callback) {
	    cb_data.callback = tb[i].callback;
	    cb_data.closure = context;
	    XtSetArg(av[ac], XmNactivateCallback, &cb_data); ac++;
	}
	b = create_option_menu(context, toolbar, name, tb[i].var, av, ac);
    } else if (tb[i].type == TOGGLE) {
	/* Install userData if available */
	b = fe_hack_out_editor_toggle_button(context, toolbar, name, icon_number++);
	if (tb[i].userdata) {
	    ac = 0;
	    XtSetArg(av[ac], XmNuserData, tb[i].userdata); ac++;
	    XtSetValues(b, av, ac);
	}
	
	if (tb[i].callback)
	    XtAddCallback(b, XmNvalueChangedCallback, tb[i].callback, context);
    } else {
	/* Install userData if available */
	b = fe_hack_out_editor_push_button(context, toolbar, name, icon_number++);
	ac = 0;
	if (tb[i].userdata) {
	    XtSetArg(av[ac], XmNuserData, tb[i].userdata); ac++;
	}
	if (tb[i].type == UNSELECTABLE) {
	    XtSetArg(av[ac], XtNsensitive, False); ac++;
	}
	if (ac > 0)
	    XtSetValues(b, av, ac);
	
	if (tb[i].callback)
	    XtAddCallback(b, XmNactivateCallback, tb[i].callback, context);
    }

    if (tb[i].offset) {
	fe_ContextData *data = CONTEXT_DATA (context);
	Widget* foo = (Widget*)(((char*) data) + (int)(tb[i].offset));
	*foo = b;
    }

    kids [j++] = b;
  }

  XtManageChildren (kids, j);
}

static Widget
fe_MakeCharacterToolbar(MWContext* context, Widget parent)
{
  Widget toolbar;
  Arg    av[8];
  int    ac;
  
  ac = 0;
  XtSetArg (av[ac], XmNskipAdjust, True); ac++;
  XtSetArg (av[ac], XmNseparatorOn, False); ac++;
  XtSetArg (av[ac], XmNorientation, XmHORIZONTAL); ac++;
  XtSetArg (av[ac], XmNpacking, XmPACK_TIGHT); ac++;
  toolbar = XmCreateRowColumn(parent, "characterToolbar", av, ac);

  do_work(context, toolbar, fe_character_toolbar, 10);
  
  return toolbar;
}

static Widget
fe_MakeParagraphToolbar(MWContext* context, Widget parent)
{
  Widget toolbar;
  Arg    av[8];
  int    ac;
  
  ac = 0;
  XtSetArg (av[ac], XmNskipAdjust, True); ac++;
  XtSetArg (av[ac], XmNseparatorOn, False); ac++;
  XtSetArg (av[ac], XmNorientation, XmHORIZONTAL); ac++;
  XtSetArg (av[ac], XmNpacking, XmPACK_TIGHT); ac++;
  toolbar = XmCreateRowColumn(parent, "paragraphToolbar", av, ac);

  do_work(context, toolbar, fe_paragraph_toolbar, 23);

  return toolbar;
}

Widget
fe_EditorCreatePropertiesToolbar(MWContext* context, Widget parent, char* name)
{
    fe_ContextData* data = CONTEXT_DATA(context);
    Widget form;
    Widget rc;
    Widget character_toolbar = 0;
    Widget paragraph_toolbar = 0;
    Arg    args[4];
    Cardinal n;

    if (data->show_character_toolbar_p && data->show_paragraph_toolbar_p) {
	n = 0;
	form = XmCreateForm(parent, name, args, n);
    } else {
	form = parent;
    }
	
    if (data->show_character_toolbar_p) {
	n = 0;
	XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_IN); n++;
	character_toolbar = XmCreateFrame(form, "characterToolbarFrame",
					  args, n);
	XtManageChild(character_toolbar);
        rc = fe_MakeCharacterToolbar(context, character_toolbar);
	XtManageChild(rc);
    }

    if (data->show_paragraph_toolbar_p)	{
	n = 0;
	XtSetArg(args[n], XmNshadowType, XmSHADOW_ETCHED_IN); n++;
	paragraph_toolbar = XmCreateFrame(form, "paragraphToolbarFrame",
					  args, n);
	XtManageChild(paragraph_toolbar);
	rc = fe_MakeParagraphToolbar(context, paragraph_toolbar);
	XtManageChild(rc);
    }

    if (character_toolbar && !paragraph_toolbar) {
	form = character_toolbar;
    } else if (!character_toolbar && paragraph_toolbar) {
	form = paragraph_toolbar;
    } else {
	n = 0;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_FORM); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetValues(character_toolbar, args, n);
	n = 0;
	XtSetArg(args[n], XmNtopAttachment, XmATTACH_WIDGET); n++;
	XtSetArg(args[n], XmNtopWidget, character_toolbar); n++;
	XtSetArg(args[n], XmNleftAttachment, XmATTACH_FORM); n++;
	XtSetArg(args[n], XmNbottomAttachment, XmATTACH_FORM); n++;
	XtSetValues(paragraph_toolbar, args, n);
    }

    data->character_toolbar = character_toolbar;
    data->paragraph_toolbar = paragraph_toolbar;
    
    return form;
}

/*
 *    Caret handling stuff. The caret is just a timed draw onto the
 *    screen, it doesn't exist in the image.
 */
#define FE_CARET_DEFAULT_TIME 500
#define FE_CARET_FLAGS_BLANK  0x1
#define FE_CARET_FLAGS_DRAW   0x2
#define FE_CARET_FLAGS_XOR    0x4
#define FE_CARET_DEFAULT_WIDTH 5

static void
fe_caret_draw(MWContext *context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;
    Widget    drawing_area = CONTEXT_DATA(context)->drawing_area;
    Display*  dpy = XtDisplay(drawing_area);
    Window    win = XtWindow(drawing_area);
    GC        gc;
    XGCValues gcv;
    int       x = data->x;
    int       y = data->y;
    unsigned  width = data->width;
    unsigned  height = data->height;

    memset(&gcv, ~0, sizeof(gcv));
#if 0
    {
    LO_Color  text_color;
    LO_Color  bg_color;
    fe_EditorDocumentGetColors(context, NULL, &bg_color, &text_color,
			       NULL, NULL, NULL);
    gcv.foreground = fe_GetPixel(context,
				 text_color.red,
				 text_color.green,
				 text_color.blue);
    }
#else
    gcv.foreground = CONTEXT_DATA(context)->fg_pixel;
#endif
    gc = fe_GetGC(drawing_area, GCForeground, &gcv);

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

    if ((width & 0x1) != 1)
	width++;
	
    /*
     *    Hack, hack, hack. Do something pretty david!
     */
#if 1
    XDrawLine(dpy, win, gc, x, y, x + width - 1, y);
    XDrawLine(dpy, win, gc, x + (width/2), y, x + (width/2), y + height - 1);
    XDrawLine(dpy, win, gc, x, y + height - 1, x + width - 1, y + height - 1);
#else
    XDrawRectangle(dpy, win, gc, x, y, width-1, height-1);
#endif
}

static void
fe_caret_undraw(MWContext *context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;
    Widget    drawing_area = CONTEXT_DATA (context)->drawing_area;
    Display*  dpy = XtDisplay(drawing_area);
    Window    win = XtWindow(drawing_area);
    GC        gc;
    XGCValues gcv;
    Visual*   visual;
    int       visual_depth;
    int       x = data->x;
    int       y = data->y;
    unsigned  width = data->width;
    unsigned  height = data->height;

    if (data->backing_store == 0) {
	/* screen co-ords */
	fe_ClearArea(context,
		     x - CONTEXT_DATA(context)->document_x,
		     y - CONTEXT_DATA(context)->document_y,
		     width + 1, height + 1);

	/* doc co-ords */ /* refreshArea seems to have off my one bugs */
	LO_RefreshArea(context, x, y, width + 1, height + 1);
	XFlush(dpy);

	XtVaGetValues(CONTEXT_WIDGET(context), XmNvisual, &visual, 0);
	if (!visual)
	    visual = fe_globalData.default_visual;
	
	visual_depth = fe_VisualDepth(dpy, visual);

	if (!height)
	    height++;

	data->backing_store = XCreatePixmap(dpy, win,
					    width, height, visual_depth);
	    
	x -= CONTEXT_DATA(context)->document_x;
	y -= CONTEXT_DATA(context)->document_y;

	memset(&gcv, ~0, sizeof(gcv));
	gc = fe_GetGC(drawing_area, 0, &gcv);
	XCopyArea(dpy, win, data->backing_store, gc,
		  x, y, width, height, 0, 0);
    } else {
	x -= CONTEXT_DATA(context)->document_x;
	y -= CONTEXT_DATA(context)->document_y;

	memset(&gcv, ~0, sizeof(gcv));
	gc = fe_GetGC(drawing_area, 0, &gcv);
	XCopyArea(dpy, data->backing_store, win, gc,
		  0, 0, width, height, x, y);
    }
}

static void
fe_caret_update(MWContext* context, XtIntervalId *id)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    if (data->showing) {
	fe_caret_undraw(context);
	data->showing = FALSE;
    } else {
	fe_caret_draw(context);
	data->showing = TRUE;
    }

    if (data->running) {
	data->timer_id = XtAppAddTimeOut(fe_XtAppContext,
					 data->time,
					 (XtTimerCallbackProc)fe_caret_update,
					 context);
    }
}

static void
fe_caret_cancel(MWContext* context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;
  
    if (data->running)
	XtRemoveTimeOut(data->timer_id);
    data->running = FALSE;

    if (data->showing)
	fe_caret_undraw(context);
    data->showing = FALSE;
    
    if (data->backing_store) {
	XFreePixmap(XtDisplay(CONTEXT_DATA(context)->drawing_area),
		    data->backing_store);
	data->backing_store = 0;
    }
}

static void
fe_caret_pause(MWContext* context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    if (data->running == FALSE) /* already paused or never started */
        return;

    XtRemoveTimeOut(data->timer_id);

    data->running = FALSE;

    if (data->showing == FALSE) /* always pause showing */
        fe_caret_update(context, NULL);
}

static void
fe_caret_unpause(MWContext* context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    if (data->running == TRUE || data->showing == FALSE) /* not paused */
        return;

    data->showing = FALSE;
    data->running = TRUE;

    /*
     *    Draw and set timer
     */
    fe_caret_update(context, NULL);
}

static void
fe_caret_begin(MWContext* context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    data->showing = FALSE;
    data->time = FE_CARET_DEFAULT_TIME;
    data->running = TRUE;

    fe_caret_update(context, NULL);

#ifdef XFE_EDITOR_IM_SUPPORT
    fe_editor_im_set_coords(context);
#endif /* XFE_EDITOR_IM_SUPPORT */
}

void
fe_EditorRefreshArea(MWContext* context, int x, int y, unsigned w, unsigned h)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    if (
#if 0
	/*
	 *    Don't use x, as LO_RefreshArea tends to redraw full width
	 *    anyway.
	 */
	((x < data->x && x + w >= data->x) ||
	 (x < data->x + data->width && x + w >= data->x + data->width))
	&&
#endif
	((y < data->y && y + h >= data->y) ||
	 (y < data->y + data->height && y + h >= data->y + data->height))) {

        if (data->backing_store) {
	    XFreePixmap(XtDisplay(CONTEXT_DATA(context)->drawing_area),
			data->backing_store);
	    data->backing_store = 0;
	}

	if (data->running) {
	    XtRemoveTimeOut(data->timer_id);
	    data->showing = TRUE; /* force restart of sequence next */
	}
	    
	if (data->showing) {
	    data->showing = FALSE;
	    fe_caret_update(context, NULL);
	}
    }
}

static void
fe_editor_keep_cursor_visible(MWContext* context)
{
    fe_EditorCaretData* c_data = &EDITOR_CONTEXT_DATA(context)->caret_data;
    int x = CONTEXT_DATA(context)->document_x;
    int y = CONTEXT_DATA(context)->document_y;
    unsigned win_height = CONTEXT_DATA(context)->scrolled_height;
    unsigned win_width = CONTEXT_DATA(context)->scrolled_width;
    Boolean  coffee_scroll = FALSE;

    if (c_data->y + c_data->height > y + win_height - 20) {
	y = c_data->y + c_data->height - win_height + 20; /* fudge */
	if (y + win_height > CONTEXT_DATA(context)->document_height)
	    y = CONTEXT_DATA(context)->document_height - win_height;
	coffee_scroll = TRUE;
    } else if (c_data->y < y + 10) {
	y = c_data->y - 10;
	if (y < 0)
	    y = 0;
	coffee_scroll = TRUE;
    }

     if (c_data->x + c_data->width > x + win_width - 20) {
	x = c_data->x + c_data->width - win_width + 20;
	if (x + win_width > CONTEXT_DATA(context)->document_width)
	    x = CONTEXT_DATA(context)->document_width - win_width;
	coffee_scroll = TRUE;
    } else if (c_data->x < x + 10) {
	x = c_data->x - 10;
	if (x < 0)
	    x = 0;
	coffee_scroll = TRUE;
    }

    if (coffee_scroll) {
        /*
	 *    Collect all pending exposures before we scroll.
	 */
	fe_SyncExposures(context);

	fe_ScrollTo(context, x, y);
    }
}

static void
fe_editor_keep_pointer_visible_autoscroll(XtPointer, XtIntervalId*);

static void
fe_editor_keep_pointer_visible(MWContext* context, int p_x, int p_y)
{
    fe_ContextData* data = CONTEXT_DATA(context);
    fe_EditorAscrollData* ascroll_data
	= &EDITOR_CONTEXT_DATA(context)->ascroll_data;
    Boolean coffee_scroll = FALSE;
    int x = data->document_x;
    int y = data->document_y;
    int delta = 0;

    if (ascroll_data->timer_id)
	XtRemoveTimeOut(ascroll_data->timer_id);

    ascroll_data->delta_x = 0;
    ascroll_data->delta_y = 0;

    if (p_y < data->document_y) {
	coffee_scroll = TRUE;
	y = p_y;
	delta = (data->document_y - p_y);
	ascroll_data->y = p_y;
	ascroll_data->delta_y = -delta;
    }

    if (p_y > data->document_y + data->scrolled_height) {
	coffee_scroll = TRUE;
	y = p_y - data->scrolled_height;
	delta = p_y - (data->document_y + data->scrolled_height);
	ascroll_data->y = p_y;
	ascroll_data->delta_y = delta;
    }

    if (coffee_scroll) {
        /*
	 *    Collect all pending exposures before we scroll.
	 */
	fe_SyncExposures(context);

	fe_ScrollTo(context, x, y);

	ascroll_data->timer_id = XtAppAddTimeOut(fe_XtAppContext,
				 (100),
				 fe_editor_keep_pointer_visible_autoscroll,
				 (XtPointer)context);
    }
}

static void
fe_editor_keep_pointer_visible_autoscroll(XtPointer closure, XtIntervalId* id)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorAscrollData* ascroll_data;

    ascroll_data = &EDITOR_CONTEXT_DATA(context)->ascroll_data;
    ascroll_data->timer_id = 0; /* because we won't pop again */
  
    ascroll_data->x += ascroll_data->delta_x;
    ascroll_data->y += ascroll_data->delta_y;

    EDT_ExtendSelection(context, ascroll_data->x, ascroll_data->y);

    fe_editor_keep_pointer_visible(context, ascroll_data->x, ascroll_data->y);
}

static void
fe_caret_set(MWContext* context, int x, int y, unsigned w, unsigned h)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;

    if (x != data->x || y != data->y || w != data->width || h != data->height)
	fe_caret_cancel(context);

    if (data->running)
	XtRemoveTimeOut(data->timer_id);

    data->x = x;
    data->y = y;
    data->width = w;
    data->height = h;

    fe_editor_keep_cursor_visible(context);
}

Boolean 
FE_GetCaretPosition(
		    MWContext *context,
		    LO_Position* where,
		    int32* caretX, int32* caretYLow, int32* caretYHigh
) {
  int32 xVal;
  int32 yVal;
  int32 yValHigh;

#if 0
  (void) fprintf(real_stderr, "FE_GetCaretPosition\n");
#endif

  if (!context || !where->element)
    return FALSE;
  
  xVal = where->element->lo_any.x + where->element->lo_any.x_offset;
  yVal = where->element->lo_any.y;
  yValHigh = yVal + where->element->lo_any.height;

  switch (where->element->type) {
  case LO_TEXT: {
    LO_TextStruct* text_data = & where->element->lo_text;
    LO_TextInfo    text_info;
    PA_Block       text_save = text_data->text;
    int            len_save = text_data->text_len;

    if (!text_data->text_attr)
      return FALSE;

    if (where->position <= text_data->text_len) {
      text_data->text += where->position;
      text_data->text_len -= where->position;
    }
    XFE_GetTextInfo(context, text_data, &text_info);
    text_data->text = text_save;
    text_data->text_len = len_save;
    
    xVal += text_info.max_width - 1;
  } break;

  case LO_IMAGE: {
    LO_ImageStruct *pLoImage = & where->element->lo_image;
    if (where->position == 0) {
      xVal -= 1;
    } else {
      xVal += pLoImage->width + 2 * pLoImage->border_width;
    }
  } break;
    
  default: {
    LO_Any *any = &where->element->lo_any;
    if (where->position == 0) {
      xVal -= 1;
    } else {
      xVal += any->width;
    }
  }
  }

  *caretX = xVal;
  *caretYLow = yVal;
  *caretYHigh = yValHigh;

  return TRUE;
}

/*
 *    char_offset is in characters!!!
 */
PUBLIC void
FE_DisplayTextCaret(MWContext* context, int iLocation, LO_TextStruct* text,
		    int char_offset)
{
  int       x;
  int       y;
  unsigned  width;
  unsigned  height;
  LO_TextInfo text_info;
  int16     save_len;

  /*
   *    Get info extent info about the first <char_offset> characters of
   *    text.
   */
  save_len = text->text_len;
  text->text_len = char_offset;
  XFE_GetTextInfo(context, text, &text_info);
  x = text->x + text->x_offset + text_info.max_width;
  y = text->y + text->y_offset;
  height =  text_info.ascent + text_info.descent;
  
  text->text_len = save_len;

#if 0
  /*
   *    Now get extent info about the next character, because we'll draw
   *    it's size as the cursor.
   */
  if (char_offset < save_len) {
  } else {
    width = 4;
  }
#endif

  width = FE_CARET_DEFAULT_WIDTH;
  x -= (FE_CARET_DEFAULT_WIDTH/2) + 1; /* middle of char and back */

  fe_caret_set(context, x, y, width, height);
  fe_caret_begin(context);
}

void 
FE_DisplayImageCaret(
		     MWContext*             context, 
		     LO_ImageStruct*        image,
		     ED_CaretObjectPosition caretPos)
{
#if 0
  fprintf (real_stderr,"FE_DisplayImageCaret\n");
#endif

  int       x;
  int       y;
  unsigned  width;
  unsigned  height;
  
  /*
   *    Get info extent info about the first <char_offset> characters of
   *    text.
   */
  x = image->x + image->x_offset;
  y = image->y + image->y_offset;
  width = FE_CARET_DEFAULT_WIDTH;
  height = image->height + (2 * image->border_width);

  if (caretPos == ED_CARET_BEFORE) {
    x -= 1;
  } else if (caretPos == ED_CARET_AFTER) {
    x += image->width + (2 * image->border_width);
  } else {
    width = image->width + (2 * image->border_width);
  }

  fe_caret_set(context, x, y, width, height);
  fe_caret_begin(context);
}

void FE_DisplayGenericCaret(MWContext * context, LO_Any * image,
                        ED_CaretObjectPosition caretPos )
{
#if 0
  fprintf (real_stderr,"FE_DisplayGenericCaret\n");
#endif
  int       x;
  int       y;
  unsigned  width;
  unsigned  height;
  
  /*
   *    Get info extent info about the first <char_offset> characters of
   *    text.
   */
  x = image->x + image->x_offset;
  y = image->y + image->y_offset;
  width = FE_CARET_DEFAULT_WIDTH;
  height = image->line_height;

  if (caretPos == ED_CARET_BEFORE) {
    x -= 1;
  } else if (caretPos == ED_CARET_AFTER) {
    x += image->width;
  } else {
    width = image->width;
  }

  fe_caret_set(context, x, y, width, height);
  fe_caret_begin(context);
}

PUBLIC void
FE_DestroyCaret(MWContext * context)
{
#if 0
  (void) fprintf(real_stderr, "FE_DestroyCaret\n");
#endif
  fe_caret_cancel(context);
}

PUBLIC void
FE_ShowCaret(MWContext * context)
{
#if 0
  (void) fprintf(real_stderr, "FE_ShowCaret\n");
#endif
  fe_caret_begin(context);
}

#if 0
void FE_DisplaySource(MWContext * context, char * source)
{
	(void) fprintf(real_stderr, "FE_DisplaySource\n");
}
#endif

/*
 *    Note all x, y co-ordinates are document relative.
 */
PUBLIC void 
FE_DocumentChanged(MWContext* context, int32 p_y, int32 p_height)
{
    int32  redraw_x = 0;
    int32  redraw_y;
    uint32 redraw_width = CONTEXT_DATA(context)->document_width;
    uint32 redraw_height;
    int32  win_y;
    uint32 win_height;
    int32  win_x;
#if 0
    uint32 win_width;
#endif
    int32  margin_height;
    int32  margin_width;

#if 0
    fprintf(
	    real_stderr,
	    "FE_DocumentChanged(%d,%d) "
	    "[doc=(0,0,%d,%d)] [win=(%d,%d,%d,%d)] ",
	    p_y, p_height,        /* passed in */
	    CONTEXT_DATA(context)->document_width,
	    CONTEXT_DATA(context)->document_height,
	    CONTEXT_DATA(context)->document_x,
	    CONTEXT_DATA(context)->document_y,
	    CONTEXT_DATA(context)->scrolled_width,
	    CONTEXT_DATA(context)->scrolled_height
	    );
#endif

    win_y = CONTEXT_DATA(context)->document_y;
    win_height = CONTEXT_DATA(context)->scrolled_height;

    win_x = CONTEXT_DATA(context)->document_x;
#if 0
    win_width = CONTEXT_DATA(context)->scrolled_width;
#endif

    /*
     *    Ok, it seems like layout doesn't take into account margins.
     *    If there was stuff in the margin before (because we *were*
     *    displaying something big, which was scrolled up), but now we
     *    are not, we must clear the margin. This code attempts
     *    to detect when we are close to the margin, and say, clear
     *    the whole thing. This seems kinda bogus, the back-end
     *    should know about margins (no?), but... djw
     */
    fe_GetMargin(context, &margin_width, &margin_height);
    
    if (p_y <= margin_height) {
        p_y = 0;
	if (p_height != -1)
	    p_height += margin_height;
    }

    redraw_y = p_y;

    if (p_height < 0) {
	redraw_height = CONTEXT_DATA(context)->document_height;
	/* make sure full repaint goes to end of window at least. */
	if (win_height > redraw_height)
	    redraw_height = win_height;
    } else {
	redraw_height = p_height;
    }

    /*
     *    Is doc change area above or below displayed region?
     */
    if (redraw_y > win_y + win_height || redraw_y + redraw_height < win_y)
	return; /* nothing to do */

    /*
     *    Clip redraw area.
     */
    if (redraw_y < win_y) {
	redraw_height -= (win_y - redraw_y);
	redraw_y = win_y;
    }

    if (redraw_y + redraw_height > win_y + win_height) {
	redraw_height = win_y + win_height - redraw_y;
    }

    /*
     *    Redraw background, and generate expose.
     */
    fe_ClearAreaWithExposures(context, redraw_x - win_x, redraw_y - win_y,
			      redraw_width, redraw_height, TRUE);
    
#if 0
    fprintf(
	    real_stderr,
	    "=> (%d, %d, %d, %d)\n",
	    redraw_x, redraw_y, redraw_width, redraw_height /* we did    */
	    );
#endif
}

void FE_GetDocAndWindowPosition(MWContext * context, int32 *pX, int32 *pY, 
    int32 *pWidth, int32 *pHeight )
{
#if 0
  (void) fprintf(real_stderr, "FE_GetDocAndWindowPosition\n");
#endif
  *pX = CONTEXT_DATA (context)->document_x;
  *pY = CONTEXT_DATA (context)->document_y;
  *pWidth = CONTEXT_DATA (context)->scrolled_width;
  *pHeight = CONTEXT_DATA (context)->scrolled_height;
}

void
FE_SetNewDocumentProperties(MWContext* context)
{

    LO_Color bg_color;
    LO_Color normal_color;
    LO_Color link_color;
    LO_Color active_color;
    LO_Color followed_color;
    char background_image[MAXPATHLEN];
    XP_Bool use_custom;
    Boolean keep_images;

    /*
     *    Set keep images state from prefs. 
     */
    fe_EditorPreferencesGetLinksAndImages(context, NULL, &keep_images);
    fe_EditorDocumentSetImagesWithDocument(context, keep_images);

    /*
     *    Get editor defaults.
     */
    use_custom = fe_EditorPreferencesGetColors(context,
					       background_image,
					       &bg_color,
					       &normal_color,
					       &link_color,
					       &active_color,
					       &followed_color);

    /*
     *    Apply.
     */
    if (use_custom) {
	fe_EditorDocumentSetColors(context,
				   background_image,
				   &bg_color,
				   &normal_color,
				   &link_color,
				   &active_color,
				   &followed_color);
    } else {
	fe_EditorDocumentSetColors(context,
				   background_image, /*independent of colors*/
				   NULL,
				   NULL,
				   NULL,
				   NULL,
				   NULL);
    }

    /*
     *    Now do other stuff.
     */
    /* Title */
    fe_EditorDocumentSetTitle(context, NULL); /* will show Untitled */

    /* Add fixed MetaData items for author, others?? */
    fe_EditorDocumentSetMetaData(context,
				 "Author",
				 fe_EditorPreferencesGetAuthor(context));
    fe_EditorDocumentSetMetaData(context,
				 "GENERATOR",
				 "Mozilla/X");
}

void
fe_EditorNotifyBackgroundChanged(MWContext* context)
{
    fe_EditorCaretData* data = &EDITOR_CONTEXT_DATA(context)->caret_data;
    
    if (data->backing_store) {
	XFreePixmap(XtDisplay(CONTEXT_DATA(context)->drawing_area),
		    data->backing_store);
	data->backing_store = 0;
    }
}

char* 
FE_URLToLocalName(char *name)
{
  char* qm;
  char* hm;
  char* begin;
  char* end;
  char* rv;
  char* nameSave;
  int   len;
  int   nameLen;

#if 0
  fprintf(real_stderr, "FE_URLToLocalName: in = %s\n", name);
#endif

  /*
   *    Save a copy of the name as we might drop characters after '?' or '#'
   */

  nameLen = strlen(name);
  nameSave = XP_ALLOC(nameLen + 1);
  memcpy(nameSave, name, nameLen);
  nameSave[nameLen] = '\0';

  /*
   *    Drop everything after a '?' or '#' character.
   */
  qm = strchr(nameSave, '?');
  hm = strchr(nameSave, '#');

  if (qm && hm) {
    end = (qm > hm)? qm: hm;
  } else if (qm) {
    end = qm;
  } else if (hm) {
    end = hm;
  } else {
    end = nameSave + nameLen;
  }

  *end = '\0';

  /*
   *    Then get the basename of what's left.
   */
  if ((begin = strrchr(nameSave, '/')) != NULL && begin[1] != '\0') {
    begin++;
  } else {
    begin = nameSave;
  }
    
  len = end - begin;
  rv = malloc(len + 1);
  memcpy(rv, begin, len);
  rv[len] = '\0';

#if 0
  fprintf(real_stderr, "FE_URLToLocalName: out = %s\n", rv);
#endif
  
  XP_FREE(nameSave);
  return rv;
}

Boolean FE_EditorPrefConvertFileCaseOnWrite(void)
{
  fprintf (real_stderr,"FE_EditorPrefConvertFileCaseOnWrite\n");
  return TRUE;
}

void 
FE_FinishedSave(MWContext* context, int status,
		char *pDestURL, int iFileNumber)
{
}

static void
fe_editor_copyright_hint_dialog(MWContext* context)
{
    if (!fe_globalPrefs.editor_copyright_hint)
	return;

    if (fe_HintDialog(context, XP_GetString(XFE_ERROR_COPYRIGHT_HINT))) {
	fe_globalPrefs.editor_copyright_hint = FALSE;
	
	/*
	 *    Save options.
	 */
	if (!XFE_SavePrefs((char *)fe_globalData.user_prefs_file,
			   &fe_globalPrefs)) {
	    fe_perror(context, XP_GetString(XFE_ERROR_SAVING_OPTIONS));
	}
    }
}


void
fe_EditorGetUrlExitRoutine(MWContext* context, URL_Struct* url, int status)
{
    if (status < 0) {
	/*
	 *    Make sure that it's not a publish. This code is only
	 *    meant for fe_GetURL() calls. Make sure the user gets to see
	 *    the error dialog, then request a delete on the editor.
	 */
	if (url != NULL && url->files_to_post == NULL) { /* not publish */

	   /*
	    *    Wait for popup menus to go away.
	    */
	    /* we need to check context->type because under certain cases we 
	     * reach this loop after the editor has been killed so context is
	     * garbage and if we don't check then ContextHasPopups fail
	     */
	    while (context && context->type == MWContextEditor && 
		   fe_ContextHasPopups(context))
    		fe_EventLoop();
	    
	    /*
	     *    Then request a dissapearing act.
	     */
	    XtAppAddTimeOut(fe_XtAppContext, 0, fe_editor_exit_timeout,
			    (XtPointer)context);
	}
    }
}


/*
 * Editor calls us when we are finished loading
 * We force saving document if not editing local file
 */
void
FE_EditorDocumentLoaded(MWContext* context)
{
    History_entry* hist_ent;
    Bool           do_save_dialog;
    Boolean        save = FALSE;
    Boolean        links = TRUE;
    Boolean        images = TRUE;
    char filebuf[MAXPATHLEN];
    char urlbuf[MAXPATHLEN];
    unsigned as_time;
    Boolean  as_enable;
    ED_FileError file_error;
    
    if (!context) {
	return;
    }
    
    /*
     *    Windows does a lot more gymnastics here to determine which
     *    docs should or should not be saved (especially new ones).
     *    Please look into this and match up.
     */
    /*FIXME*/
    do_save_dialog = FALSE;
    if ((hist_ent = SHIST_GetCurrent(&context->hist)) && hist_ent->address) {
	do_save_dialog = !NET_IsLocalFileURL(hist_ent->address);
    }
    
    if (!EDT_IS_NEW_DOCUMENT(context) && do_save_dialog) {
	
	/*
	 *    Prompt for saving remote document.
	 */
	fe_EditorPreferencesGetLinksAndImages(context, &links, &images);
	fe_DoSaveRemoteDialog(context, &save, &links, &images);

	/*
	 *    Tongue in cheek....
	 */
	if (save)
	    fe_editor_copyright_hint_dialog(context);

	if (save && !fe_SaveAsDialog(context, filebuf, fe_FILE_TYPE_HTML))
	    save = FALSE;

	if (save) {
	    /*
	     *    Grab the filename while we have it to form file: path.
	     */
	    PR_snprintf(urlbuf, sizeof(urlbuf), "file:%.900s", filebuf);
	
	    file_error = EDT_SaveFile(context,
				      hist_ent->address,
				      urlbuf,
				      TRUE,
				      images,
				      links);

	    if (file_error != ED_ERROR_NONE && file_error != ED_ERROR_CANCEL) {
		fe_editor_report_file_error(context, file_error, urlbuf,
					    FALSE);
	    }

		/* here we spin and spin until all the savings of images
	     * and stuff are done so that we get a "real" status back from
	     * the backend.  This makes saving semi-synchronous, good for
	     * status reporting
		 */
	    while(CONTEXT_DATA(context)->is_file_saving) {
		/* do nothing, we wait here */
		fe_EventLoop();
	    }

		/* now that we get the real status message, do stuff with it...*/
	    file_error = CONTEXT_DATA(context)->file_save_status;
	    if (file_error != ED_ERROR_NONE && file_error != ED_ERROR_CANCEL) {
	    	fe_editor_report_file_error(context, file_error, 
					    CONTEXT_DATA(context)->save_file_url,
					    FALSE);

	    } 
	    if (file_error != ED_ERROR_NONE)
		save = FALSE;
	}

	if (save) {
	    /*
	     *    Save the keep images with link thingy.
	     */
	    fe_EditorDocumentSetImagesWithDocument(context, images);

	} else { /* this means there was an error, or the user punted */

	    /*
	     *    Danger, danger Will Robinson. This is a crock.
	     *    The user cancelled the load (actually the load has
	     *    already happened, what a joke, so I guess they are
	     *    cancelling the edit), so we just need to delete
	     *    this context (closing the window, etc). We cannot
	     *    just call delete, because the editor lib, and 
	     *    net lib above us (our caller), still has a bunch of
	     *    pointers and goop into us (so why are they calling
	     *    ...Load<ed>), anyway, we need to setup a call back
	     *    to call back into the delete code after our callers
	     *    are done. Sounds like a job for a work proc huh?
	     *    Right you are Will, but Navigator's event loop
	     *    doesn't do work procs (I guess they would subvert
	     *    Netlib's hegemony), so I set a timeout (of zero)
	     *    which will be called as soon as we get back in the
	     *    event loop...djw
	     */
	    XtAppAddTimeOut(fe_XtAppContext, 0, fe_editor_exit_timeout,
			    (XtPointer)context);
	    return; /* out of here */
	}
    }

    /*
     *    Set autosave period.
     */
    fe_EditorPreferencesGetAutoSave(context, &as_enable, &as_time);
    EDT_SetAutoSavePeriod(context, as_time);

    fe_EditorUpdateToolbar(context, 0);
}

static char fe_SaveDialogSaveName[] = "saveMessageDialog";
static char fe_SaveDialogUploadName[] = "uploadMessageDialog";
static char fe_SaveDialogImageLoadName[] = "imageLoadMessageDialog";

typedef enum fe_SaveDialogType
{
    XFE_SAVE_DIALOG_SAVE,
    XFE_SAVE_DIALOG_UPLOAD,
    XFE_SAVE_DIALOG_IMAGELOAD
} fe_SaveDialogType;

typedef struct fe_SaveDialogInfo
{
    unsigned          nfiles;
    unsigned          current;
    fe_SaveDialogType type;
} fe_SaveDialogInfo;

static void
fe_save_dialog_info_init(Widget msg_box, fe_SaveDialogType type, unsigned n)
{
    fe_SaveDialogInfo* info = (fe_SaveDialogInfo*)XtNew(fe_SaveDialogInfo);
    
    if (info == NULL)
	return;

    info->nfiles = n;
    info->type = type;
    info->current = 0;

    XtVaSetValues(msg_box, XmNuserData, (XtPointer)info, 0);
}

#define XFE_SAVE_DIALOG_NEXT (-1)

static void
fe_save_dialog_info_set(Widget msg_box, char* filename, int index)
{
    XtPointer          user_data;
    fe_SaveDialogInfo* info;
    char               buf[MAXPATHLEN];
    int                id;
    char*              string;
    XmString           xm_string;

    XtVaGetValues(msg_box, XmNuserData, &user_data, 0);
    if (user_data != 0) {
	info = (fe_SaveDialogInfo*)user_data;

	if (index == XFE_SAVE_DIALOG_NEXT)
	    info->current++;
	else
	    info->current = index;

	if (info->current > info->nfiles)
	    info->nfiles = info->current;

	if (info->type == XFE_SAVE_DIALOG_IMAGELOAD)
	    id = XFE_LOADING_IMAGE_FILE;
	else if (info->type == XFE_SAVE_DIALOG_UPLOAD)
	    id = XFE_UPLOADING_FILE;
	else
	    id = XFE_SAVING_FILE;

	string = XP_GetString(id);

	if (!filename)
	    filename = "";
	sprintf(buf, string, filename);
	
	if (info->nfiles > 1) {
	    strcat(buf, "\n");
	    string = XP_GetString(XFE_FILE_N_OF_N);
	    sprintf(&buf[strlen(buf)], string, info->current, info->nfiles);
	}
	
	xm_string = XmStringCreateLocalized(buf);
    } else {
	xm_string = XmStringCreateLocalized(filename);
    }
    XtVaSetValues(msg_box, XmNmessageString, xm_string, 0);
    XmStringFree(xm_string);
}

static Boolean
fe_SaveDialogIsSave(Widget msg_box)
{
    char* name = XtName(msg_box);

    return (strcmp(name, fe_SaveDialogSaveName) == 0);
}

static void
fe_save_dialog_cancel_cb(Widget msg_box, XtPointer closure, XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;

    if (fe_SaveDialogIsSave(msg_box)) {
        EDT_SaveCancel(context);
    } else {
        NET_InterruptWindow(context);
    }
}

static Widget
fe_save_dialog_create(MWContext* context, char* name)
{
    Widget mainw = CONTEXT_WIDGET(context);
    Widget msg_box;
    Visual *v = 0;
    Colormap cmap = 0;
    Cardinal depth = 0;
    Arg args [20];
    Cardinal n;

    if (CONTEXT_DATA(context)->posted_msg_box != NULL)
	return CONTEXT_DATA(context)->posted_msg_box;

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

    n = 0;
    XtSetArg(args[n], XmNvisual, v); n++;
    XtSetArg(args[n], XmNdepth, depth); n++;
    XtSetArg(args[n], XmNcolormap, cmap); n++;
    XtSetArg(args[n], XmNallowShellResize, TRUE); n++;
    XtSetArg(args[n], XmNtransientFor, mainw); n++;
    XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++;
    XtSetArg(args[n], XmNdialogType, XmDIALOG_MESSAGE); n++;
    XtSetArg(args[n], XmNdeleteResponse, XmDESTROY); n++;
    XtSetArg(args[n], XmNautoUnmanage, FALSE); n++;
    XtSetArg(args[n], XmNresizePolicy, XmRESIZE_GROW); n++;

    msg_box = XmCreateMessageDialog(mainw, name, args, n);

    fe_UnmanageChild_safe(XmMessageBoxGetChild(msg_box, XmDIALOG_OK_BUTTON));
    fe_UnmanageChild_safe(XmMessageBoxGetChild(msg_box, XmDIALOG_HELP_BUTTON));

    XtAddCallback(msg_box, XmNcancelCallback, fe_save_dialog_cancel_cb,
		  (XtPointer)context);

    CONTEXT_DATA(context)->posted_msg_box = msg_box;

    return msg_box;
}

static void 
fe_save_dialog_destroy(Widget msg_box)
{
    XtPointer  user_data;
    
    XtVaGetValues(msg_box, XmNuserData, &user_data, 0);
    if (user_data != NULL)
	XtFree(user_data);

    XtDestroyWidget(msg_box);

}

/*
 *    Dialog to give feedback and allow canceling, overwrite protection
 *    when downloading remote files 
 *
 *    put up a save dialog that says "i'm saving foo right now", cancel?
 *    and return right away. 
 */
void 
FE_SaveDialogCreate(MWContext* context, int nfiles, Bool is_upload)
{
    Widget msg_box;
    char*  name;
    fe_SaveDialogType type;

    if (CONTEXT_DATA(context)->posted_msg_box != NULL)
	return;

    if (is_upload) {
	name = fe_SaveDialogUploadName;
	type = XFE_SAVE_DIALOG_UPLOAD;
    } else {
	name = fe_SaveDialogSaveName;
	type = XFE_SAVE_DIALOG_SAVE;
    }

    msg_box = fe_save_dialog_create(context, name);

    fe_save_dialog_info_init(msg_box, type, nfiles);

    CONTEXT_DATA(context)->posted_msg_box = msg_box;

	/* this is here so that we know when the file saying is over */
    CONTEXT_DATA(context)->is_file_saving = True;

    /*
     *    Postpone until we have something to say..
    XtManageChild(msg_box);
     */
}

void 
FE_SaveDialogSetFilename(MWContext* context, char* filename) 
{
    Widget msg_box = CONTEXT_DATA(context)->posted_msg_box;
    
    if (!msg_box)
	return;
    
    fe_save_dialog_info_set(msg_box, filename, XFE_SAVE_DIALOG_NEXT);
    
    if (!XtIsManaged(msg_box))
	XtManageChild(msg_box);
}

void 
FE_SaveDialogDestroy(MWContext* context, int status, char* file_url)
{
    Widget msg_box = CONTEXT_DATA(context)->posted_msg_box;
    
    if (msg_box == NULL)
	return;

    if (fe_SaveDialogIsSave(msg_box)) {
	CONTEXT_DATA(context)->save_file_url = file_url;
	CONTEXT_DATA(context)->file_save_status = status;
    } else { /* upload */
	/*
	 *    Do stuff for publish location.
	 */
    }
    
    fe_save_dialog_destroy(msg_box);
    CONTEXT_DATA(context)->posted_msg_box = NULL;
	/* this is here so that we know when the file saying is over */
    CONTEXT_DATA(context)->is_file_saving = False;
}

void
FE_ImageLoadDialog(MWContext* context)
{
    Widget msg_box;

    if (CONTEXT_DATA(context)->posted_msg_box != NULL)
	return;

    msg_box = fe_save_dialog_create(context, fe_SaveDialogImageLoadName);

    fe_save_dialog_info_init(msg_box, XFE_SAVE_DIALOG_IMAGELOAD, 1);
    CONTEXT_DATA(context)->posted_msg_box = msg_box;

    fe_save_dialog_info_set(msg_box, "", 1);

    XtManageChild(msg_box);
}

void
FE_ImageLoadDialogDestroy(MWContext* context)
{
    Widget msg_box = CONTEXT_DATA(context)->posted_msg_box;

    if (msg_box == NULL)
	return;

    fe_save_dialog_destroy(msg_box);

    CONTEXT_DATA(context)->posted_msg_box = NULL;
}

typedef struct {
  Widget  form;
  Widget  widget;
  unsigned response;
  Boolean done;
} YesToAllInfo;

static void
fe_yes_to_all_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
  YesToAllInfo* info = (YesToAllInfo*)closure;
  int i;
  char* name;
  char* widget_name = XtName(widget);

  for (i = 0; (name = fe_CreateYesToAllDialog_button_data[i].name); i++) {
    if (strcmp(name, widget_name) == 0) {
      info->response = fe_CreateYesToAllDialog_button_data[i].type;
      break;
    }
  }

  info->widget = widget;
  info->done = TRUE;

  XtUnmanageChild(info->form);
}

#if 0
static unsigned
fe_OverWriteFileQuestionDialog(MWContext* context, char* filename)
{
  char* msg;
  int   size;
  Bool  rv = FALSE;

  size = XP_STRLEN(filename) + 1 /* slash*/
       + XP_STRLEN(fe_globalData.overwrite_file_message) + 1; /*nul*/

  msg = (char *)XP_ALLOC(size);
  if (!msg)
    return False;

  PR_snprintf(msg, size, fe_globalData.overwrite_file_message, filename);

  if (FE_Confirm(context, msg))
    rv = TRUE;

  XP_FREE(msg);

  return rv;
}
#endif

static unsigned
fe_DoYesToAllDialog(MWContext* context, char* filename)
{
  YesToAllInfo info;
  Arg      av[20];
  Cardinal ac;
  XtCallbackRec cb_data;
  XmString msg_string;
  char* msg;
  int size;
  Widget form;
  Widget default_button;

  size = XP_STRLEN(filename) + 1 /* slash*/
       + XP_STRLEN(fe_globalData.overwrite_file_message) + 1; /*nul*/

  msg = (char *)XP_ALLOC(size);
  if (!msg)
    return XFE_DIALOG_CANCEL_BUTTON;

  PR_snprintf(msg, size, fe_globalData.overwrite_file_message, filename);
  msg_string = XmStringCreateLocalized(msg);
  XP_FREE(msg);

  cb_data.callback = fe_yes_to_all_cb;
  cb_data.closure = &info;

  ac = 0;
  XtSetArg(av[ac], XmNarmCallback, &cb_data); ac++;
  XtSetArg(av[ac], XmNmessageString, msg_string); ac++;
  form = fe_CreateYesToAllDialog(context, "confirmSaveFiles", av, ac);

  default_button = fe_YesToAllDialogGetChild(form, XFE_DAILOG_YESTOALL_BUTTON);
  ac = 0;
  XtSetArg(av[ac], XmNdefaultButton, default_button); ac++;
#if 0 /* why doesn't this work? */
  XtSetArg(av[ac], XmNinitialFocus, default_button); ac++;
#endif
  XtSetValues(form, av, ac);

  info.response = XFE_DIALOG_CANCEL_BUTTON;
  info.done = FALSE;
  info.widget = NULL;
  info.form = form;

  XmStringFree(msg_string);
    
  XtManageChild(form);

  fe_NukeBackingStore(form);
  while (!info.done)
    fe_EventLoop();

  XtDestroyWidget(form);

  return info.response;
}

/*
typedef enum {
    ED_SAVE_OVERWRITE_THIS,
    ED_SAVE_OVERWRITE_ALL,
    ED_SAVE_DONT_OVERWRITE_THIS,
    ED_SAVE_DONT_OVERWRITE_ALL,
    ED_SAVE_CANCEL
} ED_SaveOption;
*/

ED_SaveOption
FE_SaveFileExistsDialog(MWContext* context, char* filename) 
{
#if 0
  fprintf(real_stderr,"FE_SaveFileExistsDialog\n");
#endif

  switch (fe_DoYesToAllDialog(context, filename)) {
  case XFE_DIALOG_YES_BUTTON:
    return ED_SAVE_OVERWRITE_THIS;
  case XFE_DAILOG_YESTOALL_BUTTON:
    return ED_SAVE_OVERWRITE_ALL;
  case XFE_DIALOG_NO_BUTTON:
    return ED_SAVE_DONT_OVERWRITE_THIS;
  case XFE_DIALOG_NOTOALL_BUTTON:
    return ED_SAVE_DONT_OVERWRITE_ALL;
  default:
    return ED_SAVE_CANCEL;
  }
}

#if 0 /* I have no idea... */
/*
 *    lifted from winfe/edview.cpp
 */
void 
TellSiteManagerFileSaved( char *szFile )
{
    if ( talkhandle != NULL ) {
        TALK_HApp sitemanager = TALK_GetFirstApp( talk_TAppSiteManager );
        if ( sitemanager ) {
	    /* Send the URL address as the DataIn. No return data needed */
            TALK_SendMessage(sitemanager, lvp_NG_HaveSavedUrl, 
                             szFile,                /* DataIn */
                             XP_STRLEN(szFile)+1,   /* Size */
                             NULL, 0 );             /* DataOut and size */
        }
    }
}
#endif

Boolean
FE_SaveErrorContinueDialog(MWContext*   context,
			   char*        filename, 
			   ED_FileError error)
{
  if (error != ED_ERROR_NONE) {
      /* There was a an error while saving on:\n%s */
      /* The error code = (%d).*/
      return fe_editor_report_file_error(context, error, filename, TRUE);
  } else {
      return TRUE;
  }
}

Boolean XP_ConvertUrlToLocalFile (const char *url, char **localName)
{
  Boolean filefound = FALSE;
  char *path = NULL;
  char *file = NULL;
  XP_StatStruct statinfo;

#if 0
  fprintf (real_stderr,"XP_ConvertURLToLocalFile: %s\n", url);
#endif

  if (localName)
    {
      *localName = NULL;
    }

  /* Must have "file:" url type and at least 1 character after '//' */
  if (!url || !NET_IsLocalFileURL((char *) url))
    {
	return FALSE; 
    }

  /* Extract file path from url: e.g. "/cl/foo/file.html" */
  path = NET_ParseURL (url, GET_PATH_PART);
  if (!path || XP_STRLEN(path) < 1) {
    return FALSE;
  }

#if 0
  /* Get filename - skip over initial "/" */
  file = WH_FileName (path+1, xpURL);
  XP_FREE (path);
#endif
  file = XP_STRDUP(path);
 
  /* Test if file exists */
  if (XP_Stat (file, &statinfo, xpURL) >= 0 &&
      statinfo.st_mode & S_IFREG)
    {
      filefound = TRUE;
    }
 
  if (localName)
    {
      /* Pass string to caller; we didn't change it, so there's
         no need to strdup
         */
      *localName = file;
    }

  return filefound;
}

char *
XP_BackupFileName (const char *url)
{
  char *filename = NULL;
  char* path;

#if 0
  fprintf (real_stderr,"XP_BackupFileName: %s\n", url);
#endif
  /* Must have a "file:" url and at least 1 character after the "//" */
  if (!url || !NET_IsLocalFileURL((char *) url))
    {
      return FALSE;
    }

  /* Extract file path from url: e.g. "/cl/foo/file.html" */
  path = NET_ParseURL (url, GET_PATH_PART);
  if (!path || XP_STRLEN(path) < 1) {
    return FALSE;
  }

  filename = (char *) XP_ALLOC ((XP_STRLEN (path) + 5) * sizeof (char));
  if (!filename)
    {
      return NULL;
    }

  /* Get filename but ignore "file://" */
  {
      char* filename2 = WH_FileName (path, xpURL);
      if (!filename2) return NULL;
      XP_STRCPY (filename, filename2);
      XP_FREE(filename2);
  }

  /* Add extension to the filename */
  XP_STRCAT (filename, "~");
#if 0
  fprintf (real_stderr,"XP_BackupFileName is: %s\n", filename);
#endif
  return filename;
}

static void
fe_EditorUpdateToolbarTextAttributes(
				     MWEditorContext* context,
				     EDT_CharacterData* cdata
) {
    ED_TextFormat attributes = cdata->values;
    int           size = cdata->iSize;
    fe_EditorContextData* editor = EDITOR_CONTEXT_DATA(context);

    /*FIXME*/
    /*
     *    This function should do some kind of check to see if the widgets
     *    need to be updated. Like:
    if (editor->last_size == size && editor->last_attributes == attributes)
	return;
     */
    if ((attributes & TF_FONT_SIZE) == 0) {
	size = 0; /* default */
    }
    
    fe_SizeGroupUpdate(editor, size);
    fe_TextAttributeGroupUpdate(editor, attributes);
}

void
fe_EditorUpdateToolbar(MWEditorContext* context, ED_TextFormat unused_for_now)
{
    fe_EditorContextData* editor = EDITOR_CONTEXT_DATA(context);
    ED_ElementType type;
    TagType nParagraphFormat;
    intn iTagType;

    if (CONTEXT_DATA(context)->show_toolbar_p) {
        /*
	 *    Cut/Copy/Paste updates. These are setup for the dependency
	 *    model used in the dialogs, whcih I would like to
	 *   introduce here.
	 */
        fe_editor_cut_update_cb(editor->toolbar_cut, (XtPointer)context, NULL);
	fe_editor_copy_update_cb(editor->toolbar_copy, (XtPointer)context,
				 NULL);
#if 0
	fe_editor_paste_update_cb(editor->toolbar_paste, (XtPointer)context, 
				  NULL);
#endif
#if 0
	fe_editor_browse_doc_update_cb(editor->toolbar_browse, 
				       (XtPointer)context, NULL);
	fe_editor_publish_update_cb(editor->toolbar_publish, 
				    (XtPointer)context, NULL);
#endif
    }

    if (CONTEXT_DATA(context)->show_character_toolbar_p) {

        type = EDT_GetCurrentElementType(context);
	switch (type) {
	case ED_ELEMENT_TEXT:
	case ED_ELEMENT_SELECTION:{
	    EDT_CharacterData* edt_cdata = EDT_GetCharacterData(context);
	    if (edt_cdata) {
	        fe_EditorUpdateToolbarTextAttributes(context, edt_cdata);
		EDT_FreeCharacterData(edt_cdata);
	    }
	}
	    break;
	default:
	    break;
	}
    }

    if (CONTEXT_DATA(context)->show_paragraph_toolbar_p) {

        fe_AlignGroupUpdate(editor, fe_EditorAlignGet(context));
	/* lifted from editor.cpp, sort of */
	nParagraphFormat = EDT_GetParagraphFormatting(context);
	fe_paragraph_style_toolbar_update(editor, nParagraphFormat);

	iTagType = 0;

	if (nParagraphFormat == P_LIST_ITEM) {
	    EDT_ListData* pData = EDT_GetListData(context);
	    if (pData) {
	        iTagType = pData->iTagType;
		EDT_FreeListData(pData);
	    }

	    fe_toggle_list_update(editor, iTagType);
	} else {
	    fe_toggle_list_update(editor, -1);
	}
    }

    fe_EditorNotifyBackgroundChanged(context);
}

Boolean
fe_EditorLineBreakCanInsert(MWContext* context)
{
   return (EDT_IsSelected(context) == FALSE);
}

/*
 *    type             description
 *    ED_BREAK_NORMAL  insert line break, ignore images.
 *    ED_BREAK_LEFT    Break so it passes the image on the left
 *    ED_BREAK_RIGHT   Break past the right image.
 *    ED_BREAK_BOTH    Break below image(s).
 */
void
fe_EditorLineBreak(MWContext* context, ED_BreakType type)
{
    if (!fe_EditorLineBreakCanInsert(context))
        return;
    EDT_InsertBreak(context, type);
    fe_EditorUpdateToolbar(context, 0);
}

void
fe_EditorNonBreakingSpace(MWContext* context)
{
    EDT_InsertNonbreakingSpace(context);
    fe_EditorUpdateToolbar(context, 0);
}

void
fe_EditorParagraphMarksSetState(MWContext* context, Boolean display)
{
    EDT_SetDisplayParagraphMarks(context, (Bool)display);
}

Boolean
fe_EditorParagraphMarksGetState(MWContext* context)
{
    return (Boolean)EDT_GetDisplayParagraphMarks(context);
}

char*
fe_EditorDefaultGetTemplate()
{
    return XFE_EDITOR_DEFAULT_DOCUMENT_TEMPLATE_URL;
}

void
fe_EditorDefaultGetColors(LO_Color*  bg_color,
			  LO_Color*  normal_color,
			  LO_Color*  link_color,
			  LO_Color*  active_color,
			  LO_Color*  followed_color)
{
    if (bg_color)
        *bg_color = lo_master_colors[LO_COLOR_BG];

    if (normal_color)
        *normal_color = lo_master_colors[LO_COLOR_FG];

    if (link_color)
        *link_color = lo_master_colors[LO_COLOR_LINK];

    if (active_color)
        *active_color = lo_master_colors[LO_COLOR_ALINK];

    if (followed_color)
        *followed_color = lo_master_colors[LO_COLOR_VLINK];
}

static char* fe_last_publish_location;
static char* fe_last_publish_username;
static char* fe_last_publish_password;

#define SAFE_STRDUP(x) ((x)? XP_STRDUP(x): NULL)

Boolean
fe_EditorDefaultGetLastPublishLocation(MWContext* context,
				       char** location,
				       char** username,
				       char** password)
{
    if (location)
        *location = SAFE_STRDUP(fe_last_publish_location);
    if (username)
        *username = SAFE_STRDUP(fe_last_publish_username);
    if (password)
        *password = SECMOZ_UnMungeString(fe_last_publish_password);

    return fe_last_publish_password != NULL;
}

void
fe_EditorDefaultSetLastPublishLocation(MWContext* context,
				       char* location,
				       char* username,
				       char* password)
{
    /* NOTE: this function is not MT safe */
    if (fe_last_publish_location)
        XP_FREE(fe_last_publish_location);
    if (fe_last_publish_username)
        XP_FREE(fe_last_publish_username);
    if (fe_last_publish_password)
        XP_FREE(fe_last_publish_password);

    fe_last_publish_location = NULL;
    fe_last_publish_username = NULL;
    fe_last_publish_password = NULL;

    if (location) {
        fe_last_publish_location = XP_STRDUP(location);

	if (username)
	    fe_last_publish_username = XP_STRDUP(username);

	if (password)
  	    fe_last_publish_password = SECMOZ_MungeString(password);
    }
}

void
fe_EditorPreferencesGetAutoSave(MWContext* context,
				Boolean*   enable,
				unsigned*  time)
{
    if (fe_globalPrefs.editor_autosave_period == 0) {
	*time = 0;
	*enable = FALSE;
    } else {
	*time = fe_globalPrefs.editor_autosave_period;
	*enable = TRUE;
    }
}

void
fe_EditorPreferencesSetAutoSave(MWContext* context,
				Boolean    enable,
				unsigned   time)
{
    struct fe_MWContext_cons *rest;

    if (enable) {
	fe_globalPrefs.editor_autosave_period = time;
    } else {
	fe_globalPrefs.editor_autosave_period = 0;
	time = 0;
    }

    /*
     *    Walk over the contexts, and tell the BE to reset the
     *    autosave.
     */
    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
	if (rest->context->type == MWContextEditor) {
	    EDT_SetAutoSavePeriod(rest->context, time);
	}
    }
}

void
fe_EditorPreferencesGetLinksAndImages(MWContext* context,
				      Boolean*   links, 
				      Boolean*   images)
{
    if (links)
	*links = fe_globalPrefs.editor_maintain_links;
    if (images)
	*images = fe_globalPrefs.editor_keep_images;
}

void
fe_EditorPreferencesSetLinksAndImages(MWContext* context,
				      Boolean    links, 
				      Boolean    images)
{
    fe_globalPrefs.editor_maintain_links = links;
    fe_globalPrefs.editor_keep_images = images;
}

Boolean
fe_EditorPreferencesGetPublishLocation(MWContext* context,
				       char** location,
				       char** username,
				       char** password)
{
    if (location)
        *location = SAFE_STRDUP(fe_globalPrefs.editor_publish_location);
    if (username)
        *username = SAFE_STRDUP(fe_globalPrefs.editor_publish_username);
    if (password)
        *password =
	  SECMOZ_UnMungeString(fe_globalPrefs.editor_publish_password);

    return (fe_globalPrefs.editor_publish_password != NULL);
}

void
fe_EditorPreferencesSetPublishLocation(MWContext* context,
				       char* location,
				       char* username,
				       char* password)
{
    if (fe_globalPrefs.editor_publish_location)
        XP_FREE(fe_globalPrefs.editor_publish_location);
    if (fe_globalPrefs.editor_publish_username)
        XP_FREE(fe_globalPrefs.editor_publish_username);
    if (fe_globalPrefs.editor_publish_password)
        XP_FREE(fe_globalPrefs.editor_publish_password);

    fe_globalPrefs.editor_publish_location = NULL;
    fe_globalPrefs.editor_publish_username = NULL;
    fe_globalPrefs.editor_publish_password = NULL;

    if (location) {
        fe_globalPrefs.editor_publish_location = XP_STRDUP(location);

	if (username)
	    fe_globalPrefs.editor_publish_username = XP_STRDUP(username);

	if (password)
	    fe_globalPrefs.editor_publish_password
	      = SECMOZ_MungeString(password);
    }
    fe_globalPrefs.editor_save_publish_password = (password != NULL);
}

char*
fe_EditorPreferencesGetBrowseLocation(MWContext* context)
{
    return SAFE_STRDUP(fe_globalPrefs.editor_browse_location);
}

void
fe_EditorPreferencesSetBrowseLocation(MWContext* context, char* location)
{
    if (fe_globalPrefs.editor_browse_location)
        XP_FREE(fe_globalPrefs.editor_browse_location);
    fe_globalPrefs.editor_browse_location = NULL;

    if (location)
	fe_globalPrefs.editor_browse_location = XP_STRDUP(location);
}

char*
fe_EditorPreferencesGetAuthor(MWContext* context)
{
    if (fe_globalPrefs.editor_author_name)
	return fe_globalPrefs.editor_author_name;
    else
	return "";
}

void
fe_EditorPreferencesSetAuthor(MWContext* context, char* name)
{
    if (fe_globalPrefs.editor_author_name)
	XtFree(fe_globalPrefs.editor_author_name);
    fe_globalPrefs.editor_author_name = XtNewString(name);
}

char*
fe_EditorPreferencesGetTemplate(MWContext* context)
{
    if (fe_globalPrefs.editor_document_template)
	return fe_globalPrefs.editor_document_template;
    else
	return NULL;
}

void
fe_EditorPreferencesSetTemplate(MWContext* context, char* name)
{
    if (fe_globalPrefs.editor_document_template)
	XtFree(fe_globalPrefs.editor_document_template);
    fe_globalPrefs.editor_document_template = XtNewString(name);
}

void
fe_EditorPreferencesGetEditors(MWContext* context, char** html, char** image)
{
    if (html != NULL) {
	if (fe_globalPrefs.editor_html_editor)
	    *html = fe_globalPrefs.editor_html_editor;
	else
	    *html = NULL;
    }

    if (image != NULL) {
	if (fe_globalPrefs.editor_image_editor)
	    *image = fe_globalPrefs.editor_image_editor;
	else
	    *image = NULL;
    }
}

void
fe_EditorPreferencesSetEditors(MWContext* context, char* html, char* image)
{
    if (html != NULL) {
	if (fe_globalPrefs.editor_html_editor)
	    XtFree(fe_globalPrefs.editor_html_editor);
	fe_globalPrefs.editor_html_editor = XtNewString(html);
    }

    if (image != NULL) {
	if (fe_globalPrefs.editor_image_editor)
	    XtFree(fe_globalPrefs.editor_image_editor);
	fe_globalPrefs.editor_image_editor = XtNewString(image);
    }
}

Boolean
fe_EditorPreferencesGetColors(MWContext* context,
			      char*      background_image,
			      LO_Color*  bg_color,
			      LO_Color*  normal_color,
			      LO_Color*  link_color,
			      LO_Color*  active_color,
			      LO_Color*  followed_color)
{
    if (bg_color)
	*bg_color = fe_globalPrefs.editor_background_color;
    if (normal_color)
	*normal_color = fe_globalPrefs.editor_normal_color;
    if (link_color)
	*link_color = fe_globalPrefs.editor_link_color;
    if (active_color)
	*active_color = fe_globalPrefs.editor_active_color;
    if (followed_color)
	*followed_color = fe_globalPrefs.editor_followed_color;

    if (background_image) {
	if (fe_globalPrefs.editor_background_image)
	    strcpy(background_image,  fe_globalPrefs.editor_background_image);
	else
	    background_image[0] = '\0';
    }

    return fe_globalPrefs.editor_custom_colors;
}

void
fe_EditorPreferencesSetColors(MWContext* context,
			      char*      background_image,
			      LO_Color*  bg_color,
			      LO_Color*  normal_color,
			      LO_Color*  link_color,
			      LO_Color*  active_color,
			      LO_Color*  followed_color)
{
    XP_Bool set = FALSE;

    if (bg_color) {
	fe_globalPrefs.editor_background_color = *bg_color;
	set = TRUE;
    }
    if (normal_color) {
	fe_globalPrefs.editor_normal_color = *normal_color;
	set = TRUE;
    }
    if (link_color) {
	fe_globalPrefs.editor_link_color = *link_color;
	set = TRUE;
    }
    if (active_color) {
	fe_globalPrefs.editor_active_color = *active_color;
	set = TRUE;
    }
    if (followed_color) {
	fe_globalPrefs.editor_followed_color = *followed_color;
	set = TRUE;
    }

    fe_globalPrefs.editor_custom_colors = set;

    if (fe_globalPrefs.editor_background_image)
	XtFree(fe_globalPrefs.editor_background_image);
    if (background_image && background_image[0] != '\0')
	fe_globalPrefs.editor_background_image = XtNewString(background_image);
    else
	fe_globalPrefs.editor_background_image = NULL;
}

Boolean
fe_EditorDocumentGetImagesWithDocument(MWContext* context)
{
    Boolean keep;
    EDT_PageData* page_data = EDT_GetPageData(context);

    if (page_data == NULL) {
	fe_EditorPreferencesGetLinksAndImages(context, NULL, &keep);
    } else {
	keep = page_data->bKeepImagesWithDoc;
	EDT_FreePageData(page_data);
    }

    return keep;
}

void
fe_EditorDocumentSetImagesWithDocument(MWContext* context, Boolean keep)
{
    EDT_PageData* page_data = EDT_GetPageData(context);

    if (page_data != NULL) {
	page_data->bKeepImagesWithDoc = keep;
	EDT_SetPageData(context, page_data);
	EDT_FreePageData(page_data);
    }
}

char*
fe_EditorDocumentLocationGet(MWContext* context)
{
    History_entry* hist_ent;

    /* get the location */
    hist_ent = SHIST_GetCurrent(&context->hist);
    if (hist_ent)
	return hist_ent->address;
    else
	return NULL;
}

void
fe_EditorDocumentSetTitle(MWContext* context, char* name)
{
    EDT_PageData* page_data;

    page_data = EDT_GetPageData(context);
    if (page_data->pTitle)
	XP_FREE(page_data->pTitle);

    if (name && name[0] != '\0')
	page_data->pTitle = XP_STRDUP(name);
    else
	page_data->pTitle = NULL;

    EDT_SetPageData(context, page_data);

    EDT_FreePageData(page_data);
}

static char* fe_EditorDocumentGetTitle_value;

char*
fe_EditorDocumentGetTitle(MWContext* context)
{
    EDT_PageData* page_data;

    /* free the old */
    if (fe_EditorDocumentGetTitle_value)
	XtFree(fe_EditorDocumentGetTitle_value);

    fe_EditorDocumentGetTitle_value = NULL;

    /* get the title */
    page_data = EDT_GetPageData(context);
    if (page_data->pTitle)
	fe_EditorDocumentGetTitle_value = XtNewString(page_data->pTitle);

    EDT_FreePageData(page_data);

    return fe_EditorDocumentGetTitle_value;

}
void
fe_EditorDocumentSetMetaData(MWContext* context, char* name, char* value)
{
    EDT_MetaData *pData = EDT_NewMetaData();
    XP_Bool dirty = FALSE;
    if ( pData ) {
        pData->bHttpEquiv = FALSE;
        if ( name && XP_STRLEN(name) > 0 ) {
            pData->pName = XP_STRDUP(name);
            if ( value && XP_STRLEN(value) > 0 ) {
                pData->pContent = XP_STRDUP(value);
                EDT_SetMetaData(context, pData);
		dirty = TRUE;
            } else {
                /* (Don't really need to do this) */
		pData->pContent = NULL; 
                /* Remove the item */
		EDT_DeleteMetaData(context, pData);
		dirty = TRUE;
            }
	}
	EDT_FreeMetaData(pData);
    }
    if (dirty)
	EDT_SetDirtyFlag(context, dirty);
}

char*
fe_EditorDocumentGetMetaData(MWContext* context, char* name)
{
    EDT_MetaData* pData;
    int count;
    int i;

    /* lifted from winfe/edprops.cpp */
    count = EDT_MetaDataCount(context);
    for (i = 0; i < count; i++ ) {
        pData = EDT_GetMetaData(context, i);
        if (!pData->bHttpEquiv) {
            if (strcasecmp(pData->pName, name) == 0) {
		return pData->pContent;
	    }
	}
    }
    return NULL;
}

static char* fe_editor_known_meta_data_names[] = {
    "Author",
    "Description",
#if 0 /* this one need to end up in user variables */
    "Generator",
#endif
    "Last-Modified",
    "Created",
    "Classification",
    "Keywords",
    NULL
};

static Boolean
fe_is_known_meta_data_name(char* name)
{
    unsigned i;

    for (i = 0; fe_editor_known_meta_data_names[i] != NULL; i++) {
	if (strcasecmp(fe_editor_known_meta_data_names[i], name) == 0)
	    return TRUE;
    }
    return FALSE;
}

fe_NameValueItem*
fe_EditorDocumentGetHttpEquivMetaDataList(MWContext* context)
{
    int count = EDT_MetaDataCount(context);
    int i;
    EDT_MetaData* pData;
    fe_NameValueItem* items;
    unsigned nitems = 0;

    items = (fe_NameValueItem*)XtMalloc(sizeof(fe_NameValueItem)*(count+1));

    for (i = 0; i < count; i++) {
	pData = EDT_GetMetaData(context, i);

        if ((pData->bHttpEquiv)) {
	    items[nitems].name = XtNewString(pData->pName);
	    items[nitems].value = XtNewString(pData->pContent);
	    nitems++;
	}
    }
    items[nitems].name = NULL;
    items[nitems].value = NULL;

    items = (fe_NameValueItem*)XtRealloc((XtPointer)items,
					 sizeof(fe_NameValueItem)*(nitems+1));

    return items;
}

void
fe_EditorDocumentSetHttpEquivMetaDataList(MWContext* context,
					  fe_NameValueItem* list)
{
    fe_NameValueItem* old_list;
    int i;
    int j;
    EDT_MetaData *meta_data;
    XP_Bool dirty = FALSE;

    old_list = fe_EditorDocumentGetHttpEquivMetaDataList(context);

    /* first delete items no longer in set */
    for (i = 0; old_list[i].name != NULL; i++) {

	for (j = 0; list[j].name != NULL; j++) {
	    if (strcasecmp(old_list[i].name, list[j].name) == 0) /* match */
		break;
	}

	/* I'm sure all this thrashing of the heap can't be required ?? */
	if (list[j].name == NULL) { /* it's gone now */
	    meta_data = EDT_NewMetaData();
	    meta_data->pName = old_list[i].name;
	    meta_data->pContent = NULL;
	    EDT_DeleteMetaData(context, meta_data);
	    EDT_FreeMetaData(meta_data);
	    dirty = TRUE;
	}
    }

    /* now set the ones that have changed */
    for (j = 0; list[j].name != NULL; j++) {
	meta_data = EDT_NewMetaData();
	meta_data->pName = list[j].name;
	meta_data->pContent = list[j].value;
	meta_data->bHttpEquiv = TRUE;
	EDT_SetMetaData(context, meta_data);
	EDT_FreeMetaData(meta_data);
	dirty = TRUE;
    }
    if (dirty)
	EDT_SetDirtyFlag(context, dirty);
}

fe_NameValueItem*
fe_EditorDocumentGetAdvancedMetaDataList(MWContext* context)
{
    int count = EDT_MetaDataCount(context);
    int i;
    EDT_MetaData* pData;
    fe_NameValueItem* items;
    unsigned nitems = 0;

    items = (fe_NameValueItem*)XtMalloc(sizeof(fe_NameValueItem)*(count+1));

    for (i = 0; i < count; i++) {
	pData = EDT_GetMetaData(context, i);

	/* is it one of the ones handled in document general */
        if ((pData->bHttpEquiv) || fe_is_known_meta_data_name(pData->pName))
	    continue;
	
	items[nitems].name = XtNewString(pData->pName);
	items[nitems].value = XtNewString(pData->pContent);
	nitems++;
    }
    items[nitems].name = NULL;
    items[nitems].value = NULL;

    items = (fe_NameValueItem*)XtRealloc((XtPointer)items,
					 sizeof(fe_NameValueItem)*(nitems+1));

    return items;
}

void
fe_EditorDocumentSetAdvancedMetaDataList(MWContext* context,
					 fe_NameValueItem* list)
{
    fe_NameValueItem* old_list;
    int i;
    int j;
    EDT_MetaData *meta_data;
    XP_Bool dirty = FALSE;

    old_list = fe_EditorDocumentGetAdvancedMetaDataList(context);

    /* first delete items no longer in set */
    for (i = 0; old_list[i].name != NULL; i++) {

	for (j = 0; list[j].name != NULL; j++) {
	    if (strcasecmp(old_list[i].name, list[j].name) == 0) /* match */
		break;
	}

	/* I'm sure all this thrashing of the heap can't be required ?? */
	if (list[j].name == NULL) { /* it's gone now */
	    meta_data = EDT_NewMetaData();
	    meta_data->pName = old_list[i].name;
	    meta_data->pContent = NULL;
	    EDT_DeleteMetaData(context, meta_data);
	    EDT_FreeMetaData(meta_data);
	    dirty = TRUE;
	}
    }

    /* now set the ones that have changed */
    for (j = 0; list[j].name != NULL; j++) {
	meta_data = EDT_NewMetaData();
	meta_data->pName = list[j].name;
	meta_data->pContent = list[j].value;
	EDT_SetMetaData(context, meta_data);
	EDT_FreeMetaData(meta_data);
	dirty = TRUE;
    }
    if (dirty)
	EDT_SetDirtyFlag(context, dirty);
}

Boolean
fe_EditorDocumentGetColors(MWContext* context,
			   char*      background_image,
			   LO_Color*  bg_color,
			   LO_Color*  normal_color,
			   LO_Color*  link_color,
			   LO_Color*  active_color,
			   LO_Color*  followed_color)
{
    EDT_PageData* page_data = EDT_GetPageData(context);
    Boolean set = FALSE;

    fe_EditorDefaultGetColors(bg_color,
			      normal_color,
			      link_color, 
			      active_color,
			      followed_color);

    if (background_image != NULL) {
        if (page_data->pBackgroundImage != NULL) {
	    strcpy(background_image, page_data->pBackgroundImage);
	} else {
	    background_image[0] = '\0';
	}
    }

    if (bg_color) {
        if (page_data->pColorBackground) {
	    *bg_color = *page_data->pColorBackground;
	    set = TRUE;
	}
    }

    if (normal_color) {
        if (page_data->pColorText) {
	    *normal_color = *page_data->pColorText;
	    set = TRUE;
	}
    }

    if (link_color) {
        if (page_data->pColorLink) {
	    *link_color = *page_data->pColorLink;
	    set = TRUE;
	}
    }

    if (active_color) {
        if (page_data->pColorActiveLink) {
	    *active_color = *page_data->pColorActiveLink;
	    set = TRUE;
	}
    }

    if (followed_color) {
        if (page_data->pColorFollowedLink) {
	    *followed_color = *page_data->pColorFollowedLink;
	    set = TRUE;
	}
    }

    EDT_FreePageData(page_data);

    return set;
}

void
fe_EditorDocumentSetColors(MWContext* context,
			   char*      background_image,
			   LO_Color*  bg_color,
			   LO_Color*  normal_color,
			   LO_Color*  link_color,
			   LO_Color*  active_color,
			   LO_Color*  followed_color)
{
    EDT_PageData  save_data;
    EDT_PageData* page_data;
    
    if (background_image != NULL && background_image[0] == '\0')
	background_image = NULL;
    
    page_data = EDT_GetPageData(context);
    memcpy(&save_data, page_data, sizeof(EDT_PageData));
    
    page_data->pBackgroundImage = background_image;
    page_data->pColorBackground = bg_color;
    page_data->pColorText = normal_color;
    page_data->pColorLink = link_color;
    page_data->pColorActiveLink = active_color;
    page_data->pColorFollowedLink = followed_color;
    
    EDT_SetPageData(context, page_data);
    memcpy(page_data, &save_data, sizeof(EDT_PageData));
    EDT_FreePageData(page_data);
}

typedef struct fe_EditorCommandDescription {
    intn  id;
    char* name;
} fe_EditorCommandDescription;

/*
 *    Yuck lifted from editor.h
 */
#define kNullCommandID 0
#define kTypingCommandID 1
#define kAddTextCommandID 2
#define kDeleteTextCommandID 3
#define kCutCommandID 4
#define kPasteTextCommandID 5
#define kPasteHTMLCommandID 6
#define kPasteHREFCommandID 7
#define kChangeAttributesCommandID 8
#define kIndentCommandID 9
#define kParagraphAlignCommandID 10
#define kMorphContainerCommandID 11
#define kInsertHorizRuleCommandID 12
#define kSetHorizRuleDataCommandID 13
#define kInsertImageCommandID 14
#define kSetImageDataCommandID 15
#define kInsertBreakCommandID 16
#define kChangePageDataCommandID 17
#define kSetMetaDataCommandID 18
#define kDeleteMetaDataCommandID 19
#define kInsertTargetCommandID 20
#define kSetTargetDataCommandID 21
#define kInsertUnknownTagCommandID 22
#define kSetUnknownTagDataCommandID 23
#define kGroupOfChangesCommandID 24
#define kSetListDataCommandID 25

#define kInsertTableCommandID 26
#define kDeleteTableCommandID 27
#define kSetTableDataCommandID 28

#define kInsertTableCaptionCommandID 29
#define kSetTableCaptionDataCommandID 30
#define kDeleteTableCaptionCommandID 31

#define kInsertTableRowCommandID 32
#define kSetTableRowDataCommandID 33
#define kDeleteTableRowCommandID 34

#define kInsertTableColumnCommandID 35
#define kSetTableCellDataCommandID 36
#define kDeleteTableColumnCommandID 37

#define kInsertTableCellCommandID 38
#define kDeleteTableCellCommandID 39

static fe_EditorCommandDescription fe_editor_commands[] = {
    { kNullCommandID,             "Null"              },
    { kTypingCommandID,           "Typing"            },
    { kAddTextCommandID,          "AddText"           },
    { kDeleteTextCommandID,       "DeleteText"        },
    { kCutCommandID,              "Cut"               },
    { kPasteTextCommandID,        "PasteText"         },
    { kPasteHTMLCommandID,        "PasteHTML"         },
    { kPasteHREFCommandID,        "PasteHREF"         },
    { kChangeAttributesCommandID, "ChangeAttributes"  },
    { kIndentCommandID,           "Indent"            },
    { kParagraphAlignCommandID,   "ParagraphAlign"    },
    { kMorphContainerCommandID,   "MorphContainer"    },
    { kInsertHorizRuleCommandID,  "InsertHorizRule"   },
    { kSetHorizRuleDataCommandID, "SetHorizRuleData"  },
    { kInsertImageCommandID,      "InsertImage"       },
    { kSetImageDataCommandID,     "SetImageData"      },
    { kInsertBreakCommandID,      "InsertBreak"       },
    { kChangePageDataCommandID,   "ChangePageData"    },
    { kSetMetaDataCommandID,      "SetMetaData"       },
    { kDeleteMetaDataCommandID,   "DeleteMetaData"    },
    { kInsertTargetCommandID,     "InsertTarget"      },
    { kSetTargetDataCommandID,    "SetTargetData"     },
    { kInsertUnknownTagCommandID, "InsertUnknownTag"  },
    { kSetUnknownTagDataCommandID,"SetUnknownTagData" },
    { kGroupOfChangesCommandID,   "GroupOfChanges"    },
    { kSetListDataCommandID,      "SetListData"       },
    { kInsertTableCommandID,      "InsertTable"       },
    { kDeleteTableCommandID,      "DeleteTable"       },
    { kSetTableDataCommandID,     "SetTableData"      },
    { kInsertTableCaptionCommandID,  "InsertTableCaption"  },
    { kSetTableCaptionDataCommandID, "SetTableCaptionData" },
    { kDeleteTableCaptionCommandID,  "DeleteTableCaption"  },
    { kInsertTableRowCommandID,    "InsertTableRow"   },
    { kSetTableRowDataCommandID,   "SetTableRowData"  },
    { kDeleteTableRowCommandID,    "DeleteTableRow"   },
    { kInsertTableColumnCommandID, "InsertTableColumn" },
    { kSetTableCellDataCommandID,  "SetTableCellData"  },
    { kDeleteTableColumnCommandID, "DeleteTableColumn" },
    { kInsertTableCellCommandID,   "InsertTableCell"  },
    { kDeleteTableCellCommandID,   "DeleteTableCell"  },

    { -1, NULL }
};

static XmString
resource_motif_string(Widget widget, char* name)
{
  XtResource resource;
  XmString result = 0;
  
  resource.resource_name = XmNlabelString;
  resource.resource_class = XmCXmString;
  resource.resource_type = XmRXmString;
  resource.resource_size = sizeof (XmString);
  resource.resource_offset = 0;
  resource.default_type = XtRImmediate;
  resource.default_addr = 0;

  XtGetSubresources(widget, (XtPointer)&result, name,
		     "UndoItem", &resource, 1, NULL, 0);
  if (!result)
      result = XmStringCreateLocalized(name);

  return result;
}

static XmString string_cache;

static XmString
fe_editor_undo_get_label(MWContext* context, intn id, Boolean is_undo)
{
    char     buf[64];
    XmString string;
    char* name;
    int n;

    name = NULL;
    for (n = 0; fe_editor_commands[n].name != NULL; n++) {
	if (fe_editor_commands[n].id == id) {
	    name = fe_editor_commands[n].name;
	    break;
	}
    }

    /*
     *    For ids we don't know yet, just show "Undo" or "Redo"
     */
    if (!name)
	name = "";

    /*
     *    Look for it in the cache here!!!!
     */
    /*FIXME*/
    if (string_cache)
	XmStringFree(string_cache);

    if (is_undo)
	XP_STRCPY(buf, "undo");
    else
	XP_STRCPY(buf, "redo");
    XP_STRCAT(buf, name);

    /*
     *    Resource the string.
     */
    /*
     *    Fuck these stupid Xt resource shit. Why the fuck is
     *    is it so fucking hard to get a string! Like, what,
     *    I'd never want to do that. Dickheads.
     */
    string = resource_motif_string(CONTEXT_DATA(context)->menubar, buf);
    string_cache = string;

    return string;
}

Boolean
fe_EditorCanUndo(MWContext* context, XmString* label_return)
{
    intn command_id = EDT_GetUndoCommandID(context, 0);

    if (label_return) {
	*label_return = fe_editor_undo_get_label(context,
						 command_id,
						 True);
    }
    
    if (command_id != CEDITCOMMAND_ID_NULL)
	return True;
    else
	return FALSE;
}

Boolean
fe_EditorCanRedo(MWContext* context, XmString* label_return)
{
    intn command_id = EDT_GetRedoCommandID(context, 0);
    
    if (label_return) {
	*label_return = fe_editor_undo_get_label(context, 
						 command_id,
						 False);
    }
    
    if (command_id != CEDITCOMMAND_ID_NULL)
	return True;
    else
	return FALSE;
}

void
fe_EditorUndo(MWContext* context)
{
    if (EDT_GetUndoCommandID(context, 0) != CEDITCOMMAND_ID_NULL) {
	EDT_Undo(context);
	fe_EditorUpdateToolbar(context, 0);
    } else {
	fe_Bell(context);
    }
}

void
fe_EditorRedo(MWContext* context)
{
    if (EDT_GetRedoCommandID(context, 0) != CEDITCOMMAND_ID_NULL) {
	EDT_Redo(context);
	fe_EditorUpdateToolbar(context, 0);
    } else {
	fe_Bell(context);
    }
}

Boolean
fe_EditorCanCut(MWContext* context)
{
    return (EDT_COP_OK == EDT_CanCut(context, FALSE));
}

Boolean
fe_EditorCanCopy(MWContext* context)
{
    return (EDT_COP_OK == EDT_CanCopy(context, TRUE));
}

static fe_editor_ccp_clipboard_initialized;

static char fe_editor_html_name[] = "NETSCAPE_HTML";
static char fe_editor_text_name[] = "STRING";
static char fe_editor_app_name[] = "MOZILLA";

#define XFE_CCP_TEXT (fe_editor_text_name)
#define XFE_CCP_HTML (fe_editor_html_name)
#define XFE_CCP_NAME (fe_editor_app_name)

#if 0
/*
 *    Cannot get this to work.
 */
static void
fe_property_notify_eh(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
{
    MWContext* context = (MWContext *)closure;
    Display*   display = XtDisplay(CONTEXT_WIDGET(context));
    Atom       cb_atom = XmInternAtom(display, "CLIPBOARD", False);
    Atom       string_atom = XmInternAtom(display, XFE_CCP_TEXT, False);
    Atom       html_atom = XmInternAtom(display, XFE_CCP_HTML, False);
    Atom       long_string_atom = XmInternAtom(display, "_MOTIF_CLIP_FORMAT_" "STRING", False);
    Atom       long_html_atom = XmInternAtom(display, "_MOTIF_CLIP_FORMAT_" "NETSCAPE_HTML", False);
    fe_EditorContextData* editor;

#if 0
    if (ev->xproperty.atom == XA_CUT_BUFFER0) {
	editor = EDITOR_CONTEXT_DATA(context);
	fe_editor_paste_update_cb(editor->toolbar_paste,
				  (XtPointer)context, NULL);
    }
#endif

#if 0
    if (ev->xproperty.atom ==  XmInternAtom (dpy, "CLIPBOARD", False)XA_CUT_BUFFER0)
#endif
}
#endif

static void
fe_editor_initalize_clipboard(MWContext* context)
{
    Widget   mainw;
    Display* display;

    if (!fe_editor_ccp_clipboard_initialized) {
	mainw = CONTEXT_WIDGET(context);
	display = XtDisplay(mainw);

	XmClipboardRegisterFormat(display, XFE_CCP_HTML, 8);
	fe_editor_ccp_clipboard_initialized = TRUE;
    }
}

Boolean
fe_EditorCanPasteHtml(MWContext* context, int32* r_length)
{
    Widget   mainw = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(mainw);
    Window   window = XtWindow(mainw);
    int      cb_result;
    unsigned long length;

    fe_editor_initalize_clipboard(context);

    cb_result = XmClipboardInquireLength(display, window,
					 XFE_CCP_HTML, &length);

    if (cb_result ==  ClipboardSuccess && length > 0) {
	*r_length = length;
	return TRUE;
    } else {
	*r_length = 0;
	return FALSE;
    }
}

Boolean
fe_EditorCanPasteText(MWContext* context, int32* r_length)
{
    Widget   mainw = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(mainw);
    Window   window = XtWindow(mainw);
    int      cb_result;
    unsigned long length;

    cb_result = XmClipboardInquireLength(display, window,
					 XFE_CCP_TEXT, &length);

    if (cb_result ==  ClipboardSuccess && length > 0) {
	*r_length = length;
	return TRUE;
    } else {
	*r_length = 0;
	return FALSE;
    }
}

Boolean
fe_EditorCanPasteLocal(MWContext* context, int32* r_length)
{
    struct fe_MWContext_cons *rest;

    fe_editor_initalize_clipboard(context);

    /*
     *    The browser maintains it's own internal clipboard.
     *    Clear it.
     */
    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
	if (CONTEXT_DATA(rest->context)->clipboard != NULL) {
	    *r_length = strlen(CONTEXT_DATA(rest->context)->clipboard);
	    return TRUE;
	}
    }
    return FALSE;
}

Boolean
fe_EditorCanPaste(MWContext* context)
{
    int32 len;

    /*
     *    We have to check if we can do a local paste first,
     *    because we can hang the server if we try to re-own
     *    the selection. This is all very hokey. I need to
     *    talk to jamie.
     */
    if (
	fe_EditorCanPasteLocal(context, &len)
	||
	fe_EditorCanPasteHtml(context, &len)
	|| 
	fe_EditorCanPasteText(context, &len))
    {
	return TRUE;
    } else {
	return FALSE;
    }
	
}

static int
fe_clipboard_copy_work(MWContext* context,
		       char* cut_text, unsigned long cut_text_len,
		       char* cut_html, unsigned long cut_html_len,
		       Time timestamp) {
    Widget   mainw = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(mainw);
    Window   window = XtWindow(mainw);
    int      cb_result;
    long     item_id;
    XmString label;
    long private_id = 0;
    long data_id;
    struct fe_MWContext_cons *rest;

    fe_editor_initalize_clipboard(context);

    /*
     *    The browser maintains it's own internal clipboard.
     *    Clear it.
     */
    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
	if (CONTEXT_DATA(rest->context)->clipboard != NULL) {
	    fe_DisownSelection(rest->context, timestamp, TRUE);
	}
    }
    
    label = XmStringCreateLocalized(XFE_CCP_NAME);
    
    cb_result = XmClipboardStartCopy(display, window,
				     label,
				     timestamp,
				     mainw,
				     NULL,
				     &item_id);

    XmStringFree(label);

    if (cb_result != ClipboardSuccess)
	return cb_result;
    
    if (cut_text && cut_text_len > 0) {
	cb_result = XmClipboardCopy(display, window,
				    item_id,
				    XFE_CCP_TEXT,
				    (XtPointer)cut_text,
				    cut_text_len,
				    private_id,
				    &data_id);

	if (cb_result != ClipboardSuccess)
	    return cb_result;
    }
				     
    if (cut_html && cut_html_len > 0) {
	cb_result = XmClipboardCopy(display, window,
				    item_id,
				    XFE_CCP_HTML,
				    (XtPointer)cut_html,
				    cut_html_len,
				    private_id,
				    &data_id);

	if (cb_result != ClipboardSuccess)
	    return cb_result;
    }
				     
    return XmClipboardEndCopy(display, window, item_id);
}

void
fe_editor_report_copy_error(MWContext* context, EDT_ClipboardResult code)
{
    char* msg;
    int id;

    switch (code) {
    case EDT_COP_DOCUMENT_BUSY: /* IDS_DOCUMENT_BUSY */
	id = XFE_EDITOR_COPY_DOCUMENT_BUSY;
	/* Cannot copy or cut at this time, try again later. */
	break;
    case EDT_COP_SELECTION_EMPTY: /* IDS_SELECTION_EMPTY */
	id = XFE_EDITOR_COPY_SELECTION_EMPTY;
	/* Nothing is selected. */
	break;
    case EDT_COP_SELECTION_CROSSES_TABLE_DATA_CELL:
	id = XFE_EDITOR_COPY_SELECTION_CROSSES_TABLE_DATA_CELL;
	/* The selection includes a table cell boundary. */
	/* Deleting and copying are not allowed.         */
	break;
    default:
	return;
    }

    msg = XP_GetString(id);
    FE_Alert(context, msg);
}

Boolean
fe_EditorCut(MWContext* context, Time timestamp)
{
    char* cut_text;
    int32 cut_text_len;
    char* cut_html;
    int32 cut_html_len;
    EDT_ClipboardResult cut_result;
    int cb_result;

    if (!fe_EditorCanCut(context)) {
	fe_Bell(context);
	return FALSE;
    }
    
    /* cut/copy from edt to Xm clipboard */
    cut_result = EDT_CutSelection(context, 
				  &cut_text, &cut_text_len,
				  &cut_html, &cut_html_len);
    if (cut_result != EDT_COP_OK) {
	fe_editor_report_copy_error(context, cut_result);
	return FALSE;
    }

    if ((!cut_text || cut_text_len <= 0) && (!cut_html || cut_html_len <= 0))
	return FALSE;
    
    cb_result = fe_clipboard_copy_work(context,
				       cut_text, cut_text_len,
				       cut_html, cut_html_len,
				       timestamp);

    fe_EditorUpdateToolbar(context, 0);

    if (cb_result != ClipboardSuccess)
	return FALSE;

    return TRUE;
}

Boolean
fe_EditorCopy(MWContext* context, Time timestamp)
{
    char* cut_text;
    int32 cut_text_len;
    char* cut_html;
    int32 cut_html_len;
    EDT_ClipboardResult cut_result;
    int cb_result;

    if (!fe_EditorCanCopy(context)) {
	fe_Bell(context);
	return FALSE;
    }
    
    cut_result = EDT_CopySelection(context, 
				  &cut_text, &cut_text_len,
				  &cut_html, &cut_html_len);
    if (cut_result != EDT_COP_OK) {
	fe_editor_report_copy_error(context, cut_result);
	return FALSE;
    }

    if ((!cut_text || cut_text_len <= 0) && (!cut_html || cut_html_len <= 0))
	return FALSE;
    
    cb_result = fe_clipboard_copy_work(context,
				       cut_text, cut_text_len,
				       cut_html, cut_html_len,
				       timestamp);

    fe_EditorUpdateToolbar(context, 0);

    if (cb_result != ClipboardSuccess)
	return FALSE;
    return TRUE;
}

int
fe_clipboard_retrieve_work(MWContext* context,
			   char* name,
			   char* buf, unsigned long* buf_len_a,
			   Time timestamp) {

    Widget   mainw = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(mainw);
    Window   window = XtWindow(mainw);
    int      cb_result;
    long private_id;

    fe_editor_initalize_clipboard(context);

    cb_result = XmClipboardStartRetrieve(display, window, timestamp);

    if (cb_result != ClipboardSuccess)
	return cb_result;
    
    cb_result = XmClipboardRetrieve(display, window,
				    name,
				    (XtPointer)buf,
				    *buf_len_a,
				    buf_len_a,
				    &private_id);

    if (cb_result != ClipboardSuccess)
	return cb_result;
    
    return XmClipboardEndRetrieve(display, window);
}

static char fe_editor_local_name[] = "LOCAL";

#define XFE_CCP_LOCAL (fe_editor_local_name)

Boolean
fe_EditorPaste(MWContext* context, Time timestamp)
{
    int32   length;
    char*   clip_data = NULL;
    char*   clip_name = NULL; /* none */
    int     cb_result;
    Boolean rv = FALSE;
    Widget   mainw = CONTEXT_WIDGET(context);
    Display* display = XtDisplay(mainw);
    Window   window = XtWindow(mainw);
    struct fe_MWContext_cons *rest;

    /*
     *    The browser maintains it's own internal clipboard.
     *    There seems to be a good reason for it. Anyway,
     *    check on that clipboard list first.
     */
    for (rest = fe_all_MWContexts; rest; rest = rest->next) {
	if ((clip_data = CONTEXT_DATA(rest->context)->clipboard)) {
	    clip_name = XFE_CCP_LOCAL;
	    break;
	}
    }
    
    if (!clip_data) { /* go look on real CLIPBOARD */

	/*
	 *    Need to lock the clipboard while we ask if there is something
	 *    and subsequently get it.
	 */
	XmClipboardLock(display, window);
	
	if (fe_EditorCanPasteHtml(context, &length)) {
	    clip_data = XP_ALLOC(length);
	    clip_name = XFE_CCP_HTML;
	} else if (fe_EditorCanPasteText(context, &length)) {
	    clip_data = XP_ALLOC(length);
	    clip_name = XFE_CCP_TEXT;
	}

	if (clip_data) {

	    cb_result = fe_clipboard_retrieve_work(context,
						   clip_name,
						   clip_data, &length,
						   timestamp);
	    
	    if (cb_result == ClipboardSuccess) {
		clip_data[length] = '\0';
	    } else {
		XP_FREE(clip_data);
		clip_data = NULL;
		clip_name = NULL;
	    }
	}

	XmClipboardUnlock(display, window, TRUE);
    } 

    if (clip_name == XFE_CCP_HTML) {
	rv = (EDT_PasteHTML(context, clip_data) == EDT_COP_OK);
	XP_FREE(clip_data);
    } else if (clip_name == XFE_CCP_TEXT) {
	rv = (EDT_PasteText(context, clip_data) == EDT_COP_OK);
	XP_FREE(clip_data);
    } else if (clip_name == XFE_CCP_LOCAL) {
	rv = (EDT_PasteText(context, clip_data) == EDT_COP_OK);
    } else {
	fe_Bell(context);
    }

    fe_EditorUpdateToolbar(context, 0);

    return rv;
}

void
fe_EditorSelectAll(MWContext* context)
{
    EDT_SelectAll(context);  
    fe_EditorUpdateToolbar(context, 0);
}

void
fe_EditorDeleteItem(MWContext* context)
{
    EDT_ClipboardResult result;

    if ((result = EDT_DeleteChar(context)) != EDT_COP_OK) {
	fe_editor_report_copy_error(context, result);
    }
    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorHorizontalRulePropertiesCanDo(MWContext* context)
{
    ED_ElementType type = EDT_GetCurrentElementType(context);

    if  (type == ED_ELEMENT_HRULE)
	return TRUE;
    else
	return FALSE;
}

void
fe_EditorHorizontalRulePropertiesSet(MWContext* context, EDT_HorizRuleData* data)
{
    if (fe_EditorHorizontalRulePropertiesCanDo(context)) {
	EDT_SetHorizRuleData(context, data);
    } else {
	EDT_InsertHorizRule(context, data);
    }
}

void
fe_EditorHorizontalRulePropertiesGet(MWContext* context, EDT_HorizRuleData* data)
{
    EDT_HorizRuleData* foo;

    if (fe_EditorHorizontalRulePropertiesCanDo(context)) {
	foo = EDT_GetHorizRuleData(context);
	memcpy(data, foo, sizeof(EDT_HorizRuleData));
	EDT_FreeHorizRuleData(foo);
    } else {
	data->size = 2;
	data->iWidth = 100;
	data->bWidthPercent = TRUE;
	data->align = ED_ALIGN_CENTER;
	data->bNoShade = FALSE;
    }
}

void
fe_EditorRefresh(MWContext* context)
{
    EDT_RefreshLayout(context);    
}


CB_STATIC void
fe_editor_refresh_cb(Widget widget, XtPointer closure, XtPointer call_data)
{
    fe_EditorRefresh((MWContext*)closure);
}

void
fe_EditorHrefInsert(MWContext* context, char* p_text, char* p_url)
{
    char* text;
    char* url;

    if (!EDT_CanSetHREF(context)) { /* should not exist already */

	if (EDT_IS_NEW_DOCUMENT(context)) {
	    if (!FE_CheckAndSaveDocument(context))
		return;
	}
	
	if (p_url && p_url[0] != '\0')
	    url = XP_STRDUP(p_url);
	else
	    return; /* no url specified */

	if (p_text && p_text[0] != '\0')
	    text = XP_STRDUP(p_text);
	else
	    text = XP_STRDUP(p_url);

	
	EDT_PasteHREF(context, &url, &text, 1);
    }
}

Boolean
fe_EditorGetHref(MWContext *context) {
    char*         p;

    p = (char *) LO_GetSelectionText(context);
    if (p == NULL) return False;
    else
	return EDT_GetHREF(context);
}


Boolean
fe_EditorHrefGet(MWContext* context, char** text, char** url)
{
    EDT_HREFData* href_data = EDT_GetHREFData(context);
    Bool          is_selected = EDT_IsSelected(context);
    char*         p;

    p = (char *) LO_GetSelectionText(context);

    if (is_selected && p != NULL) {
	*text = XtNewString(p);
	XP_FREE(p);
    } else if (p == NULL) {   /* this fixes #27762 */
	*text = NULL;
    } else if ((p = EDT_GetHREFText(context)) != NULL) { 
			/* charlie's new get the text of a link thing */
	*text = p;
    } else {
	*text = NULL;
    }
    
    if (href_data != NULL && (p = href_data->pURL) != NULL) {
	*url = XtNewString(p);
    } else {
	*url = NULL;
    }

    if (href_data != NULL)
	EDT_FreeHREFData(href_data);

    return EDT_CanSetHREF(context);
}

void
fe_EditorHrefSetUrl(MWContext* context, char* url)
{
    EDT_HREFData* href_data;

    if (EDT_CanSetHREF(context)) {

	href_data = EDT_GetHREFData(context);

	if (!href_data)
	    href_data = EDT_NewHREFData();

	if (href_data->pURL)
	    XP_FREE(href_data->pURL);
	
	if (url)
	    href_data->pURL = XP_STRDUP(url);
	else
	    href_data->pURL = NULL; /* clear the href */

	EDT_SetHREFData(context, href_data);

	EDT_FreeHREFData(href_data);
    }
}

void
fe_EditorHrefClearUrl(MWContext* context)
{
    fe_EditorHrefSetUrl(context, NULL);
}

void
fe_EditorRemoveLinks(MWContext* context)
{
    /*
     *    Remove a single link or all links within selected region.
     */
    if (EDT_CanSetHREF(context)) {
        EDT_SetHREF(context, NULL);
    }
}

static void
fe_editor_link_browse_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext* context = (MWContext*)closure;

    EDT_HREFData* href_data = EDT_GetHREFData(context);
    
    if (href_data != NULL) {
	if (href_data->pURL != NULL) {
	    fe_GetURLInBrowser(context,
			       NET_CreateURLStruct(href_data->pURL, FALSE));
	}
	EDT_FreeHREFData(href_data);
    }
}

static void
fe_editor_link_edit_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext* context = (MWContext*)closure;
    EDT_HREFData* href_data = EDT_GetHREFData(context);
    
    if (href_data != NULL) {
	if (href_data->pURL != NULL) {
	    fe_EditorNew(context, href_data->pURL);
	}
	EDT_FreeHREFData(href_data);
    }
}

static void
fe_editor_link_add_bookmark_cb(Widget widget, XtPointer closure,
			  XtPointer call_data)
{
    MWContext* context = (MWContext*)closure;
    EDT_HREFData* href_data = EDT_GetHREFData(context);
    
    if (href_data != NULL) {
	if (href_data->pURL != NULL) {
	    fe_AddToBookmark(context, 0,
			     NET_CreateURLStruct(href_data->pURL, FALSE), 0);
	}
	EDT_FreeHREFData(href_data);
    }
}

static Time
fe_getTimeFromEvent(MWContext* context, XEvent* event)
{
  Time time;
  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))));
  return time;
}

static void
fe_editor_link_copy_link_cb(Widget widget, XtPointer closure,
			    XtPointer cd)
{
    MWContext* context = (MWContext*)closure;
    EDT_HREFData* href_data = EDT_GetHREFData(context);
    XmPushButtonCallbackStruct* cb_data = (XmPushButtonCallbackStruct*)cd;
    
    if (href_data != NULL) {
	if (href_data->pURL != NULL) {
	    fe_clipboard_copy_work(context,
			   NULL, 0,
			   href_data->pURL, strlen(href_data->pURL),
			   fe_getTimeFromEvent(context, cb_data->event));
	}
	EDT_FreeHREFData(href_data);
    }
}

#if 0
Boolean
fe_EditorImageDataGet(MWContext* context, EDT_ImageData* idata)
{
    EDT_ImageData* foo;

    if (EDT_GetCurrentElementType(context) != ED_ELEMENT_IMAGE)
	return FALSE;

    foo = EDT_GetImageData(context);
    memcpy(idata, foo, sizeof(EDT_ImageData));

    /*
     *    Caller owns this memory.
     */
#define SAFE_DUP(x) if (foo->(x)) idata->(x) = XP_STRDUP(foo->(x))
    SAFE_DUP(pUseMap);
    SAFE_DUP(pSrc);
    SAFE_DUP(pLowSrc);
    SAFE_DUP(pName);
    SAFE_DUP(pAlt);
#undef SAFE_DUP

    EDT_FreeImageData(foo);

    return TRUE;
}

void
fe_EditorImageDataSet(MWContext* context, EDT_ImageData* idata, Boolean copy)
{
    EDT_ImageData* foo;
    EDT_ImageData  save;

    if (EDT_GetCurrentElementType(context) != ED_ELEMENT_IMAGE)
	return;

    foo = EDT_GetImageData(context);
    memcpy(&save, foo, sizeof(EDT_ImageData));

    

}

void
fe_EditorImageInsert(MWContext* context, EDT_ImageData* idata)
{
}
#endif

Boolean
fe_EditorPropertiesDialogCanDo(MWContext* context,
			       fe_EditorPropertiesDialogType d_type)
{
    ED_ElementType e_type;

    if (
	d_type == XFE_EDITOR_PROPERTIES_IMAGE_INSERT
	||
	d_type == XFE_EDITOR_PROPERTIES_LINK_INSERT
	||
	d_type == XFE_EDITOR_PROPERTIES_CHARACTER
	||
	d_type == XFE_EDITOR_PROPERTIES_PARAGRAPH
	||
	d_type == XFE_EDITOR_PROPERTIES_TABLE
	|| 
	d_type == XFE_EDITOR_PROPERTIES_DOCUMENT
    )
	return TRUE; /* always ok to do */

    e_type = EDT_GetCurrentElementType(context);

    if (d_type == XFE_EDITOR_PROPERTIES_IMAGE) {
	if (e_type == ED_ELEMENT_IMAGE)
	    return TRUE;
	else
	    return FALSE;

    } else if (d_type == XFE_EDITOR_PROPERTIES_HRULE) {
	if (e_type == ED_ELEMENT_HRULE)
	    return TRUE;
	else
	    return FALSE;

    } else if (d_type == XFE_EDITOR_PROPERTIES_HTML_TAG) {
	if (e_type == ED_ELEMENT_UNKNOWN_TAG)
	    return TRUE;
	else
	    return FALSE;

    } else if (d_type == XFE_EDITOR_PROPERTIES_TARGET) {
	if (e_type == ED_ELEMENT_TARGET)
	    return TRUE;
	else
	    return FALSE;

    } else if (d_type == XFE_EDITOR_PROPERTIES_LINK) {
	if (EDT_GetHREF(context))
	    return TRUE;
	else
	    return FALSE;
    } 
    return FALSE;
}

/*
 *    This stuff is so fucked.
 *
 */
void
fe_EditorParagraphPropertiesSetAll(MWContext*    context,
				   TagType       paragragh_type,
				   EDT_ListData* list_data,
				   ED_Alignment  align)
{
    TagType       old_paragraph_type = EDT_GetParagraphFormatting(context);
    EDT_ListData* old_list_data = EDT_GetListData(context);
    Boolean       do_set;

    EDT_BeginBatchChanges(context);

    /*
     *    Windows has this kludge, so...
     */
    if (align == ED_ALIGN_LEFT)
	align = ED_ALIGN_DEFAULT;

    EDT_SetParagraphAlign(context, align);

    /*
     *    If there was a list before, and now there is none,
     *    remove the list.
     */
    if (old_list_data && !list_data) {
	for ( ; old_list_data; old_list_data = EDT_GetListData(context)) {
	    EDT_FreeListData(old_list_data);
	    EDT_Outdent(context);
	}
	old_list_data = NULL;
    }
    
    /*
     *    If the style has changed.
     */
    if (paragragh_type != old_paragraph_type)
	EDT_MorphContainer(context, paragragh_type);

    /*
     *    If there was no list, and now there is..
     */
    if (!old_list_data && list_data) {
	EDT_Indent(context);
	old_list_data = EDT_GetListData(context);
    }

    /*
     *    The BE is losing. It cannot deal with lists in selections,
     *    it returns no list data. So, we protect oursleves and
     *    end up doing nothing, just like windows.
     */
    if (old_list_data != NULL) {

	do_set = TRUE;

	/*
	 *    If this is a block quote..
	 */
	if (list_data != NULL && list_data->iTagType == P_BLOCKQUOTE) {
	    old_list_data->iTagType = P_BLOCKQUOTE;
	    old_list_data->eType = ED_LIST_TYPE_DEFAULT;
	}

	/*
	 *    If it's a list.
	 */
	else if (list_data != NULL) {
	    old_list_data->iTagType = list_data->iTagType;
	    old_list_data->eType = ED_LIST_TYPE_DEFAULT;
#if 0
	    /*
	     *    We have no UI for this, but maybe it's in  the author's
	     *    HTML. Don't change it. Actually, I have no idea.
	     */
	    old_list_data->bCompact = FALSE;
#endif
	    switch (list_data->iTagType) {
	    case P_UNUM_LIST:
		if (list_data->eType == ED_LIST_TYPE_DISC   ||
		    list_data->eType == ED_LIST_TYPE_SQUARE	||
		    list_data->eType == ED_LIST_TYPE_CIRCLE)
		    old_list_data->eType = list_data->eType;
		break;
	    case P_NUM_LIST:
		if (list_data->eType == ED_LIST_TYPE_DIGIT       ||
		    list_data->eType == ED_LIST_TYPE_BIG_ROMAN   ||
		    list_data->eType == ED_LIST_TYPE_SMALL_ROMAN ||
		    list_data->eType == ED_LIST_TYPE_BIG_LETTERS ||
		    list_data->eType == ED_LIST_TYPE_SMALL_LETTERS)
		    old_list_data->eType = list_data->eType;
		if (list_data->iStart > 0)
		    old_list_data->iStart = list_data->iStart;
		else
		    old_list_data->iStart = 1;
		break;
	    case P_DIRECTORY:
	    case P_MENU:
	    case P_DESC_LIST:
		break;
	    default: /* garbage, don't set */
		do_set = FALSE;
		break;
	    }
	}

	if (do_set)
	    EDT_SetListData(context, old_list_data);
    }

    /*
     *    Else it's just an ordinary old jo.
     */
    /* nothing to do */
 
    if (old_list_data != NULL)
	EDT_FreeListData(old_list_data);

    EDT_EndBatchChanges(context);
    
    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorParagraphPropertiesGetAll(MWContext*    context,
				   TagType*      paragragh_type,
				   EDT_ListData* list_data,
				   ED_Alignment* align)
{
    TagType       old_paragraph_type = EDT_GetParagraphFormatting(context);
    EDT_ListData* old_list_data = EDT_GetListData(context);

    if (paragragh_type)
	*paragragh_type = old_paragraph_type;

    if (list_data && old_list_data)
	*list_data = *old_list_data;

    if (align)
	*align = EDT_GetParagraphAlign(context);

    if (old_list_data != NULL)
	EDT_FreeListData(old_list_data);

    return (old_list_data != NULL);
}

void
fe_EditorColorSet(MWContext* context, LO_Color* color)
{
    EDT_CharacterData cdata;

    memset(&cdata, 0, sizeof(EDT_CharacterData));

    cdata.mask = TF_FONT_COLOR;
    cdata.pColor = color;

    if (color)
	cdata.values = TF_FONT_COLOR;
    else
	cdata.values = 0;

    EDT_SetCharacterData(context, &cdata);
}

Boolean
fe_EditorColorGet(MWContext* context, LO_Color* color)
{
    EDT_CharacterData* cdata = EDT_GetCharacterData(context);
    Boolean rv;

    if ((cdata->values & TF_FONT_COLOR) != 0) { /* custom */
	*color = *cdata->pColor;
	rv = TRUE;
    } else {
	fe_EditorDocumentGetColors(context,
				   NULL, /* bg image */
				   NULL, /* bg color */
				   color,/* normal color */
				   NULL, /* link color */
				   NULL, /* active color */
				   NULL); /* followed color */
	rv = FALSE;
    }

    EDT_FreeCharacterData(cdata);

    return rv;
}

extern int XFE_COULDNT_FORK;
extern int XFE_EXECVP_FAILED;

void
fe_ExecCommand(MWContext* context, char** argv)
{
    char buf[MAXPATHLEN];
    int forked;

    switch (forked = fork()) {
    case -1:
	fe_perror(context, XP_GetString(XFE_COULDNT_FORK));
	break;
    case 0: {
	Display *dpy = XtDisplay(CONTEXT_WIDGET(context));
	close(ConnectionNumber(dpy));

	execvp(argv[0], argv);

	PR_snprintf(buf, sizeof(buf), XP_GetString(XFE_EXECVP_FAILED),
			fe_progname, argv[0]);
	perror(buf);
	exit(3);	/* Note that this only exits a child fork.  */
	break;
    }
    default:
	/* block */
	break;
    }
}

static char**
fe_editor_parse_argv(char* string, char* filename)
{
    char     buf[MAXPATHLEN];
    unsigned size = 32;
    unsigned argc = 0;
    char**   argv = (char**)malloc(sizeof(char*) * size);
    char*    p;
    char*    q;
    char*    r;

    for (p = string; *p != '\0'; ) {

	q = buf;

	/* skip leading white */
	while (isspace(*p))
	    p++;

	while (!isspace(*p) && *p != '\0' && ((q - buf) < sizeof(buf))) {
	    if (*p == '%' && p[1] == 'f') {
		for (r = filename; *r != '\0'; ) {
		    *q++ = *r++;
		}
		p += 2;
	    } else {
		*q++ = *p++;
	    }
	}
	*q = '\0';

	if (q > buf)
	    argv[argc++] = strdup(buf);

	if (argc == size) {
	    size = argc + 32;
	    argv = (char**)realloc((void*)argv, sizeof(char*) * size);
	}
    }

    if (argc > 0) {
	argv[argc] = NULL;
	return argv;
    } else {
	free(argv);
	return NULL;
    }
}

void
fe_editor_free_argv(char** argv)
{
    unsigned i;
    for (i = 0; argv[i]; i++)
	free(argv[i]);
    free(argv);
}

void
fe_editor_exec_command(MWContext* context, char* command, char* filename)
{
    char** argv;

    if ((argv = fe_editor_parse_argv(command, filename)) != NULL) {

	fe_ExecCommand(context, argv);

	fe_editor_free_argv(argv);
    } else {
	/* Empty command specified! */
	FE_Alert(context, XP_GetString(XFE_COMMAND_EMPTY));
    }
}

void
fe_EditorEditSource(MWContext* context)
{
    char* html_editor;
    char* local_name;
    History_entry* hist_entry;

    if (!FE_CheckAndSaveDocument(context))
	return;
    fe_EditorPreferencesGetEditors(context, &html_editor, NULL);

    if (html_editor == NULL || html_editor[0] == '\0') {
	/*
	 * "Please specify an editor in Editor Preferences.\n"
	 * "Specify the file argument with %f. Netscape will\n"
	 * "replace %f with the correct file name. Examples:\n"
	 * "             xterm -e vi %f\n"
	 * "             xgifedit %f\n"
	 */
	char* msg = XP_GetString(XFE_EDITOR_HTML_EDITOR_COMMAND_EMPTY);
	
      	if (XFE_Confirm(context, msg)) {
	    fe_EditorPreferencesDialogDo(context,
				 XFE_EDITOR_DOCUMENT_PROPERTIES_GENERAL);
	}

	return;
    } 

    hist_entry = SHIST_GetCurrent(&context->hist);
    if (hist_entry && hist_entry->address) {
	if (XP_ConvertUrlToLocalFile(hist_entry->address, &local_name)) {
	    fe_editor_exec_command(context, html_editor, local_name);
	    XP_FREE(local_name);
	}
    }
}

CB_STATIC void
fe_editor_edit_source_cb(Widget widget, XtPointer closure, XtPointer cb)
{
    MWContext* context = (MWContext*)closure;

    fe_UserActivity(context);

    fe_EditorEditSource(context);
}

void
fe_EditorEditImage(MWContext* context, char* file)
{
    char* image_editor;
    char* local_name = NULL;
    char* full_file;
    char* directory;
    char* p;
    History_entry* hist_entry;

    fe_EditorPreferencesGetEditors(context, NULL, &image_editor);

    if (image_editor == NULL || image_editor[0] == '\0') {
	/*
	 * "Please specify an editor in Editor Preferences.\n"
	 * "Specify the file argument with %f. Netscape will\n"
	 * "replace %f with the correct file name. Examples:\n"
	 * "             xterm -e vi %f\n"
	 * "             xgifedit %f\n"
	 */
	char* msg = XP_GetString(XFE_EDITOR_IMAGE_EDITOR_COMMAND_EMPTY);

      	if (XFE_Confirm(context, msg)) {
	    fe_EditorPreferencesDialogDo(context,
				 XFE_EDITOR_DOCUMENT_PROPERTIES_GENERAL);
	}

	return;
    }

    while (isspace(*file))
	file++;

    if (file[0] != '/') { /* not absolute */
	hist_entry = SHIST_GetCurrent(&context->hist);
	if (hist_entry && hist_entry->address) {
	    if (XP_ConvertUrlToLocalFile(hist_entry->address, &local_name)) {
		p = strrchr(local_name, '/'); /* find last slash */
		if (p) {
		    *p = '\0';
		    directory = local_name;
		} else {
		    directory = ".";
		}
		full_file = (char*)XtMalloc(strlen(directory) +
					    strlen(file) + 2);
		sprintf(full_file, "%s/%s", directory, file);
		XP_FREE(local_name);

		fe_editor_exec_command(context, image_editor, full_file);
		XtFree(full_file);
	    }
	}
    } else {
	fe_editor_exec_command(context, image_editor, file);
    }
}

CB_STATIC void
fe_editor_browse_publish_location_cb(Widget widget,
				     XtPointer closure, XtPointer cb_data)
{
    MWContext* context = (MWContext*)closure;
    char*      location = fe_EditorPreferencesGetBrowseLocation(context);

    if (location == NULL || location[0] == '\0') {
	/*
	 *    "The default browse location is not set.\n"
	 *    "Would you like to enter a value in Editor Preferences now?")
	 */
	char* msg = XP_GetString(XFE_EDITOR_BROWSE_LOCATION_EMPTY);
	
      	if (XFE_Confirm(context, msg)) {
	    fe_EditorPreferencesDialogDo(context, 3); /* yea, I know */
	}
	
	return;
    }

    fe_GetURLInBrowser(context, NET_CreateURLStruct(location, FALSE));
}

#define COPY_SAVE_EXTRA(b, a) \
{ char* xx_extra = (b)->pExtra; *(b) = *(a); (b)->pExtra = xx_extra; }

#define COPY_SAVE_BGCOLOR(b, a) \
{ char* xx_bgcolor = (b)->pColorBackground; *(b) = *(a); (b)->pColorBackground = xx_bgcolor; }

static LO_Color*
fe_dup_lo_color(LO_Color* color)
{
    LO_Color* new_color;

    if (color != NULL && (new_color = (LO_Color*)XtNew(LO_Color)) != NULL) {
	*new_color = *color;
	return new_color;
    } else {
	return NULL;
    }
}

#define DUP_BGCOLOR(b, a) \
{ (b)->pColorBackground = fe_dup_lo_color((a)->pColorBackground); }

/*
 *    Table support.
 */
Boolean
fe_EditorTableCanInsert(MWContext* context)
{
    return !EDT_IsJavaScript(context);
}

void
fe_EditorTableInsert(MWContext* context, EDT_AllTableData* all_data)
{
    EDT_TableData* new_data = EDT_NewTableData();
    EDT_TableCaptionData* caption_data;
    
    if (all_data) {
	EDT_TableData* data = &all_data->td;
	COPY_SAVE_EXTRA(new_data, data);
	DUP_BGCOLOR(new_data, data);
    } else {
	new_data->iBorderWidth = 1;
	new_data->iRows = 2;
	new_data->iColumns = 2;
    }
    EDT_InsertTable(context, new_data);
    EDT_FreeTableData(new_data);

    if (all_data != NULL && all_data->has_caption) {
	caption_data = EDT_NewTableCaptionData();
	caption_data->align = all_data->cd.align;
	EDT_InsertTableCaption(context, caption_data);
    }

    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorTableCanDelete(MWContext* context)
{
    return (EDT_IsInsertPointInTable(context) != 0);
}

void
fe_EditorTableDelete(MWContext* context)
{
    if (EDT_IsInsertPointInTable(context) != 0) {
	EDT_DeleteTable(context);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableGetData(MWContext* context, EDT_AllTableData* all_data)
{
    Boolean in_table = FALSE;
    EDT_TableData* table_data = EDT_GetTableData(context);
    EDT_TableData* pt_data = &all_data->td;
    EDT_TableCaptionData* caption_data;

    if (table_data) {
	*pt_data = *table_data;
	if (table_data->pColorBackground != NULL) {
	    static LO_Color color_buf;
	    color_buf = *table_data->pColorBackground;
	    pt_data->pColorBackground = &color_buf; /*caller cannot hang on*/
	}
	
	EDT_FreeTableData(table_data);
	in_table = TRUE;
    } else {
#if 0
	/*
	 *    Set defaults.
	 */
	pt_data->iRows = 0;
	pt_data->iColumns = 0;
	pt_data->BorderWidth = 0;
	pt_data->iCellSpacing = 0;
	pt_data->iCellPadding = 0;
	pt_data->bWidthDefined = FALSE;
	pt_data->bWidthPercent = FALSE;
	pt_data->iWidth = 0;
	pt_data->bHeightDefined = FALSE;
	pt_data->bHeightPercent = FALSE;
	pt_data->iHeight = 0;
	pt_data->pColorBackground = NULL;
#endif
	return FALSE;
    }

    if (EDT_IsInsertPointInTableCaption(context)) {
	caption_data = EDT_GetTableCaptionData(context);
	all_data->has_caption = TRUE;
	all_data->cd.align = caption_data->align;
    } else {
	all_data->has_caption = FALSE;
	all_data->cd.align = ED_ALIGN_TOP;
    }
    return TRUE;
}

void
fe_EditorTableSetData(MWContext* context, EDT_AllTableData* all_data)
{
    Boolean has_caption;
    Boolean wants_caption;
    EDT_TableData* table_data = EDT_GetTableData(context);
    EDT_TableCaptionData* caption_data;

    if (table_data) {
	/* get the basic table data */
	COPY_SAVE_EXTRA(table_data, &all_data->td);
	DUP_BGCOLOR(table_data, &all_data->td);

	EDT_SetTableData(context, table_data);

	caption_data = NULL;
	has_caption = EDT_IsInsertPointInTableCaption(context);
	wants_caption = all_data->has_caption;

	if (has_caption == TRUE && wants_caption == FALSE) {
	    EDT_DeleteTableCaption(context);
	} else if (has_caption == FALSE && wants_caption == TRUE) {
	    caption_data = EDT_NewTableCaptionData();
	    caption_data->align = all_data->cd.align;
	    EDT_InsertTableCaption(context, caption_data);
	} else if (has_caption) {
	    caption_data = EDT_GetTableCaptionData(context);
	    if (caption_data->align != all_data->cd.align) {
		caption_data->align = all_data->cd.align;
		EDT_SetTableCaptionData(context, caption_data);
	    }
	}

	if (caption_data)
	    EDT_FreeTableCaptionData(caption_data);

	EDT_FreeTableData(table_data);

	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableRowCanInsert(MWContext* context)
{
    return (EDT_IsInsertPointInTableRow(context) != 0);
}

void
fe_EditorTableRowInsert(MWContext* context, EDT_TableRowData* data)
{
    EDT_TableRowData* new_data;

    if (!fe_EditorTableRowCanInsert(context)) {
	fe_Bell(context);
	return;
    }

    new_data = EDT_GetTableRowData(context);

    if (data) {
	new_data->align = data->align;
	new_data->valign = data->valign;
	DUP_BGCOLOR(new_data, data);
    } 
    EDT_InsertTableRows(context, new_data, TRUE, 1);
    EDT_FreeTableRowData(new_data);
    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorTableRowCanDelete(MWContext* context)
{
    return (EDT_IsInsertPointInTableRow(context) != 0);
}

void
fe_EditorTableRowDelete(MWContext* context)
{
    if (EDT_IsInsertPointInTable(context) != 0) {
	EDT_DeleteTableRows(context, 1);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableRowGetData(MWContext* context, EDT_TableRowData* p_data)
{
    EDT_TableRowData* row_data = EDT_GetTableRowData(context);

    if (row_data) {
	*p_data = *row_data;
	if (row_data->pColorBackground != NULL) {
	    static LO_Color color_buf;
	    color_buf = *row_data->pColorBackground;
	    p_data->pColorBackground = &color_buf; /*caller cannot hang on*/
	}
	EDT_FreeTableRowData(row_data);
	return TRUE;
    } else {
	return FALSE;
    }
}

void
fe_EditorTableRowSetData(MWContext* context, EDT_TableRowData* p_data)
{
    EDT_TableRowData* row_data = EDT_GetTableRowData(context);

    if (row_data) {
	COPY_SAVE_EXTRA(row_data, p_data);
	DUP_BGCOLOR(row_data, p_data);
	EDT_SetTableRowData(context, row_data);	
	EDT_FreeTableRowData(row_data);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableCaptionCanInsert(MWContext* context)
{
    return (EDT_IsInsertPointInTable(context) != 0);
}

void
fe_EditorTableCaptionInsert(MWContext* context, EDT_TableCaptionData* data)
{
    EDT_TableCaptionData* new_data = EDT_NewTableCaptionData();
    EDT_InsertTableCaption(context, new_data);
    EDT_FreeTableCaptionData(new_data);
    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorTableCaptionCanDelete(MWContext* context)
{
    return (EDT_IsInsertPointInTableCaption(context) != 0);
}

void
fe_EditorTableCaptionDelete(MWContext* context)
{
    if (EDT_IsInsertPointInTableCaption(context) != 0) {
	EDT_DeleteTableCaption(context);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableColumnCanInsert(MWContext* context)
{
    return (EDT_IsInsertPointInTableRow(context) != 0);
}

void
fe_EditorTableColumnInsert(MWContext* context, EDT_TableCellData* data)
{
    EDT_TableCellData* new_data = EDT_NewTableCellData();
    EDT_InsertTableColumns(context, new_data, TRUE, 1);
    EDT_FreeTableCellData(new_data);

    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorTableColumnCanDelete(MWContext* context)
{
    return (EDT_IsInsertPointInTableRow(context) != 0);
}

void
fe_EditorTableColumnDelete(MWContext* context)
{
    if (EDT_IsInsertPointInTableRow(context) != 0) {
	EDT_DeleteTableColumns(context, 1);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableCellCanInsert(MWContext* context)
{
    return (EDT_IsInsertPointInTableRow(context) != 0);
}

void
fe_EditorTableCellInsert(MWContext* context, EDT_TableCellData* data)
{
    EDT_TableCellData* new_data = EDT_NewTableCellData();
    EDT_InsertTableCells(context, new_data, TRUE, 1);
    EDT_FreeTableCellData(new_data);

    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorTableCellCanDelete(MWContext* context)
{
    return (EDT_IsInsertPointInTableCell(context) != 0);
}

void
fe_EditorTableCellDelete(MWContext* context)
{
    if (EDT_IsInsertPointInTableRow(context) != 0) {
	EDT_DeleteTableCells(context, 1);
	fe_EditorUpdateToolbar(context, 0);
    }
}

Boolean
fe_EditorTableCellGetData(MWContext* context, EDT_TableCellData* p_data)
{
    EDT_TableCellData* cell_data = EDT_GetTableCellData(context);
    if (cell_data) {
	*p_data = *cell_data;
	if (cell_data->pColorBackground != NULL) {
	    static LO_Color color_buf;
	    color_buf = *cell_data->pColorBackground;
	    p_data->pColorBackground = &color_buf; /*caller cannot hang on*/
	}
	EDT_FreeTableCellData(cell_data);
	return TRUE;
    } else {
	return FALSE;
    }
}

void
fe_EditorTableCellSetData(MWContext* context, EDT_TableCellData* p_data)
{
    EDT_TableCellData* cell_data = EDT_GetTableCellData(context);
    if (cell_data) {
	COPY_SAVE_EXTRA(cell_data, p_data);
	DUP_BGCOLOR(cell_data, p_data);
	EDT_SetTableCellData(context, cell_data);
	EDT_FreeTableCellData(cell_data);
	fe_EditorUpdateToolbar(context, 0);
    }
}

void
fe_EditorSelectTable(MWContext* context)
{
    EDT_SelectTable(context);  
    fe_EditorUpdateToolbar(context, 0);
}

void
fe_EditorPublish(MWContext* context)
{
    History_entry* hist_entry;

#if 0
    /*FIXME*/
    /* why would we be here anyway??...djw */
    /* Force saving (or don't publish) if new document,*/
    /*  or ask user to save first */
    if ((context->is_new_document &&
	 !FE_SaveNonLocalDocument(context, TRUE)) ||
        !FE_CheckAndSaveDocument(context) ) {
	return;
    }
#endif
    if (context->is_new_document)
        return;

    hist_entry = SHIST_GetCurrent(&context->hist);
    if( !hist_entry || !hist_entry->address ||
	!NET_IsLocalFileURL(hist_entry->address) ){
	return;
    }

    /* Get the destination and what files to include from the user:*/
    fe_EditorPublishDialogDo(context);
}

CB_STATIC void
fe_editor_publish_cb(Widget widget, XtPointer closure, XtPointer cd)
{
    MWContext* context = (MWContext*)closure;

    fe_UserActivity(context);

    if (!FE_CheckAndSaveDocument(context))
	return;

    fe_EditorPublish(context);
}

CB_STATIC void
fe_editor_table_properties_dialog_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;

    fe_EditorTablePropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_TABLE);
}

CB_STATIC void
fe_editor_table_properties_dialog_update_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;

    Boolean sensitive = fe_EditorTableCanDelete(context);

    XtVaSetValues(widget, XtNsensitive, sensitive, 0);
}


CB_STATIC void
fe_editor_table_insert_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
#if 1
    fe_EditorTableCreateDialogDo(context);
#else
    fe_EditorTableInsert(context, NULL);
#endif
}

CB_STATIC void
fe_editor_table_insert_update_cb(Widget widget,
			      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_row_insert_cb(Widget widget,
			      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableRowInsert(context, NULL);
}

CB_STATIC void
fe_editor_table_row_insert_update_cb(Widget widget,
			      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableRowCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_column_insert_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableColumnInsert(context, NULL);
}

CB_STATIC void
fe_editor_table_column_insert_update_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableColumnCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_caption_insert_cb(Widget widget,
				  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableCaptionInsert(context, NULL);
}

CB_STATIC void
fe_editor_table_caption_insert_update_cb(Widget widget,
				  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCaptionCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_cell_insert_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableCellInsert(context, NULL);
}

CB_STATIC void
fe_editor_table_cell_insert_update_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCellCanInsert(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_delete_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableDelete(context);
}

CB_STATIC void
fe_editor_table_delete_update_cb(Widget widget,
			  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCanDelete(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_row_delete_cb(Widget widget,
			      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableRowDelete(context);
}

CB_STATIC void
fe_editor_table_row_delete_update_cb(Widget widget,
			      XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableRowCanDelete(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_column_delete_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableColumnDelete(context);
}

CB_STATIC void
fe_editor_table_column_delete_update_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableColumnCanDelete(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_caption_delete_cb(Widget widget,
				  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableCaptionDelete(context);
}

CB_STATIC void
fe_editor_table_caption_delete_update_cb(Widget widget,
				  XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCaptionCanDelete(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_cell_delete_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    fe_EditorTableCellDelete(context);
}

CB_STATIC void
fe_editor_table_cell_delete_update_cb(Widget widget,
				 XtPointer closure, XtPointer c_data)
{
    MWContext* context = (MWContext*)closure;
    Boolean sensitive = FALSE;
    if (fe_EditorTableCellCanDelete(context))
	sensitive = TRUE;
    XtVaSetValues(widget, XmNsensitive, sensitive, 0);
}

CB_STATIC void
fe_editor_table_menu_cb(Widget cascade, XtPointer closure, XtPointer c_data)
{
    Widget     pulldown;
    WidgetList children;
    Cardinal   nchildren;
    Cardinal i;
    Widget button;
    char* name;

    XtVaGetValues(cascade, XmNsubMenuId, &pulldown, 0);
    XtVaGetValues(pulldown, XmNchildren, &children,
		  XmNnumChildren, &nchildren, 0);

    for (i = 0; i < nchildren; i++) {

	button = children[i];
	name = XtName(button);
	/* update */

	if (strcmp(name, "insertTable") == 0)
	  fe_editor_table_insert_update_cb(button, closure, 0);
	else if (strcmp(name, "deleteTable") == 0)
	  fe_editor_table_delete_update_cb(button, closure, 0);
	else if (strcmp(name, "insertRow") == 0) 
	  fe_editor_table_row_insert_update_cb(button, closure, 0);
	else if (strcmp(name, "deleteRow") == 0) 
	  fe_editor_table_row_delete_update_cb(button, closure, 0);
	else if (strcmp(name, "insertColumn") == 0) 
	  fe_editor_table_column_insert_update_cb(button, closure, 0);
	else if (strcmp(name, "deleteColumn") == 0) 
	  fe_editor_table_column_delete_update_cb(button, closure, 0);
	else if (strcmp(name, "insertCell") == 0) 
	  fe_editor_table_cell_insert_update_cb(button, closure, 0);
	else if (strcmp(name, "deleteCell") == 0) 
	  fe_editor_table_cell_delete_update_cb(button, closure, 0);
	else if (strcmp(name, "insertCaption") == 0) 
	  fe_editor_table_caption_insert_update_cb(button, closure, 0);
	else if (strcmp(name, "deleteCaption") == 0) 
	  fe_editor_table_caption_delete_update_cb(button, closure, 0);
    }
}

void
fe_EditorDisplayTablesSet(MWContext* context, Boolean display)
{
    EDT_SetDisplayTables(context, display == TRUE);
    fe_EditorUpdateToolbar(context, 0);
}

Boolean
fe_EditorDisplayTablesGet(MWContext* context)
{
    return (EDT_GetDisplayTables(context) == TRUE);
}

/*
 *    Popup menu handling.
 */
Widget
fe_CreatePopupMenu(MWContext* context,
		   Widget parent, char* name, Arg* p_args, Cardinal p_n)
{
    Visual *v = 0;
    Colormap cmap = 0;
    Cardinal depth = 0;
    Arg args[8];
    Cardinal n;
    Widget menu;
    Widget mainw = CONTEXT_WIDGET(context);

    XtVaGetValues(mainw, XtNvisual, &v, XtNcolormap, &cmap,
		  XtNdepth, &depth, 0);
    
    n = 0;
    XtSetArg(args[n], XmNvisual, v); n++;
    XtSetArg(args[n], XmNcolormap, cmap); n++;
    XtSetArg(args[n], XmNdepth, depth); n++;
    
    menu = XmCreatePopupMenu(parent, name, args, n);
    
    return menu;
}

Widget
fe_CreatePulldownMenu(MWContext* context,
		   Widget parent, char* name, Arg* p_args, Cardinal p_n)
{
    Visual *v = 0;
    Colormap cmap = 0;
    Cardinal depth = 0;
    Arg args[8];
    Cardinal n;
    Widget menu;
    Widget mainw = CONTEXT_WIDGET(context);

    XtVaGetValues(mainw, XtNvisual, &v, XtNcolormap, &cmap,
		  XtNdepth, &depth, 0);
    
    n = 0;
    XtSetArg(args[n], XmNvisual, v); n++;
    XtSetArg(args[n], XmNcolormap, cmap); n++;
    XtSetArg(args[n], XmNdepth, depth); n++;
    
    menu = XmCreatePulldownMenu(parent, name, args, n);
    
    return menu;
}

typedef Widget (*fe_PopupMenuCreate) (MWContext* context,
				      Widget parent,
				      char*  name,  /* instance name */
				      Arg*   args,
				      Cardinal n);
typedef struct fe_PopupMenuDescription
{
    char*              name;     /* name of the menu item */
    fe_PopupMenuCreate create;
    XtCallbackProc     update;   /* update sensitivity callback */
    XtPointer          user_data;
} fe_PopupMenuDescription;

static void
fe_PopulateMenuFromDescription(MWContext* context, Widget menu,
			       fe_PopupMenuDescription* description)
{
    Arg    args[10];
    Cardinal n;
    unsigned i;
    Widget button;
    char* button_name;
    fe_PopupMenuCreate create;

    for (i = 0; (button_name = description[i].name) != NULL; i++) {
	create = description[i].create;
	if (!create)
	    continue;
	n = 0;
	XtSetArg(args[0], XmNuserData, description[i].user_data); n++;
	button = (create)(context, menu, button_name, args, n);
	if (description[i].update)
	    (description[i].update)(button, context, NULL);

	XtManageChild(button);
    }
}

static Widget
fe_menu_button(MWContext* context, Widget parent, char* name,
	       Arg* p_args, Cardinal p_n)
{
    Arg args[2];
    Cardinal n;
    XtCallbackProc callback = (XtCallbackProc)p_args[0].value;
    Widget widget;

    n = 0;
    widget = XmCreatePushButtonGadget(parent, name, args, n);

    if (callback) {
	XtAddCallback(widget, XmNactivateCallback, callback, context);
	fe_WidgetAddDocumentString(context, widget);
    }
    return widget;
}

#if 0
static void
fe_menu_menu_cascading_cb(Widget widget, XtPointer closure, XtPointer cb)
{
    fe_PopupMenuDescription* description = (fe_PopupMenuDescription*)closure;
    Widget     menu;
    WidgetList children;
    Cardinal   nchildren;
    Cardinal i;
    Widget button;
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    XtVaGetValues(widget, XmNsubMenuId, &menu, 0);
    XtVaGetValues(menu, XmNchildren, &children,
		  menu, &nchildren, 0);

    for (i = 0; i < nchildren && description[i].name != NULL; i++) {
	button = children[i];

	if (description[i].update)
	    (description[i].update)(button, context, NULL);
    }
}
#endif

static Widget
fe_menu_menu(MWContext* context, Widget parent, char* name,
	     Arg* p_args, Cardinal p_n)
{
    Arg args[2];
    Cardinal n;
    fe_PopupMenuDescription* description;
    Widget menu;
    Widget cascade;

    description = (fe_PopupMenuDescription*)p_args[0].value;

    n = 0;
    menu = fe_CreatePulldownMenu(context, parent, name, args, 0);
    fe_PopulateMenuFromDescription(context, menu, description);

    n = 0;
    XtSetArg(args[n], XmNsubMenuId, menu); n++;
    cascade = XmCreateCascadeButtonGadget(parent, name, args, n);

#if 0
    XtAddCallback(cascade, XmNcascadingCallback,
		  fe_menu_menu_cascading_cb, description);
#endif

    return cascade;
}

static Widget
fe_menu_separator(MWContext* context, Widget parent, char* name,
	       Arg* p_args, Cardinal p_n)
{
    Arg args[2];
    Cardinal n;
    Widget widget;
    unsigned s_type = (unsigned)p_args[0].value;

    n = 0;
    XtSetArg(args[n], XmNseparatorType, s_type); n++;
    widget = XmCreateSeparatorGadget(parent, name, args, n);

    return widget;
}

static Widget
fe_menu_label(MWContext* context, Widget parent, char* name,
	       Arg* p_args, Cardinal p_n)
{
    Arg args[2];
    Cardinal n;
    Widget widget;

    n = 0;
    widget = XmCreateLabelGadget(parent, name, args, n);

    return widget;
}

static fe_PopupMenuDescription fe_editor_document_popups[] =
{
    { "documentProperties", fe_menu_button,
      NULL,
      fe_editor_document_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_character_popups[] =
{
    { "textProperties", fe_menu_button,
      NULL,
      fe_editor_character_properties_dialog_cb },
    { "paragraphProperties", fe_menu_button,
      NULL,
      fe_editor_paragraph_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_image_popups[] =
{
    { "imageProperties", fe_menu_button,
      fe_editor_image_properties_dialog_update_cb,
      fe_editor_image_properties_dialog_cb },
    { "paragraphProperties", fe_menu_button,
      NULL,
      fe_editor_paragraph_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_tag_popups[] =
{
    { "tagProperties", fe_menu_button,
      fe_editor_html_properties_dialog_update_cb,
      fe_editor_html_properties_dialog_cb },
    { "paragraphProperties", fe_menu_button,
      NULL,
      fe_editor_paragraph_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_target_popups[] =
{
    { "targetProperties", fe_menu_button,
      fe_editor_target_properties_dialog_update_cb,
      fe_editor_target_properties_dialog_cb },
    { "paragraphProperties", fe_menu_button,
      NULL,
      fe_editor_paragraph_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_hrule_popups[] =
{
    { "hruleProperties", fe_menu_button,
      fe_editor_hrule_properties_dialog_update_cb,
      fe_editor_hrule_properties_dialog_cb },
    { "paragraphProperties", fe_menu_button,
      NULL,
      fe_editor_paragraph_properties_dialog_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_table_insert_popups[] =
{
    { "insertTable", fe_menu_button, fe_editor_table_insert_update_cb,
      fe_editor_table_insert_cb },
    { "insertRow",   fe_menu_button, fe_editor_table_row_insert_update_cb,
      fe_editor_table_row_insert_cb },
    { "insertColumn",fe_menu_button, fe_editor_table_column_insert_update_cb,
      fe_editor_table_column_insert_cb },
    { "insertCell",  fe_menu_button, fe_editor_table_cell_insert_update_cb,
      fe_editor_table_cell_insert_cb },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_table_delete_popups[] =
{
    { "deleteTable",  fe_menu_button, fe_editor_table_delete_update_cb,
      fe_editor_table_delete_cb },
    { "deleteRow",    fe_menu_button, fe_editor_table_row_delete_update_cb,
      fe_editor_table_row_delete_cb },
    { "deleteColumn", fe_menu_button, fe_editor_table_column_delete_update_cb,
      fe_editor_table_column_delete_cb },
    { "deleteCell",   fe_menu_button, fe_editor_table_cell_delete_update_cb,
      fe_editor_table_cell_delete_cb },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_table_popups[] =
{
    { "tableProperties", fe_menu_button,
      fe_editor_table_properties_dialog_update_cb,
      fe_editor_table_properties_dialog_cb },
    { "insert", fe_menu_menu,
      NULL, fe_editor_table_insert_popups },
    { "delete", fe_menu_menu,
      fe_editor_table_delete_update_cb, fe_editor_table_delete_popups },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_link_popups[] =
{
    { "linkProperties", fe_menu_button,
      fe_editor_link_properties_dialog_update_cb,
      fe_editor_link_properties_dialog_cb },
    { "browseLink", fe_menu_button, NULL, fe_editor_link_browse_cb },
    { "editLink", fe_menu_button, NULL, fe_editor_link_edit_cb },
    { "bookmarkLink", fe_menu_button, NULL, fe_editor_link_add_bookmark_cb },
    { "copyLink", fe_menu_button, NULL, fe_editor_link_copy_link_cb },
    { "removeLink", fe_menu_button, NULL, fe_editor_remove_links_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_insert_link_popups[] =
{
    { "insertLink", fe_menu_button, NULL, fe_editor_insert_link_cb },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_edit_popups[] =
{
    { "cut",   fe_menu_button, fe_editor_cut_update_cb, fe_editor_cut_cb },
    { "copy",  fe_menu_button, fe_editor_copy_update_cb, fe_editor_copy_cb },
    { "paste", fe_menu_button, NULL, fe_editor_paste_cb
      /*fe_editor_paste_update_cb*/ },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_title_popups[] =
{
    { "title", fe_menu_label, NULL },
    { "separator", fe_menu_separator, NULL, (XtPointer)XmDOUBLE_LINE },
    { NULL }
};

static fe_PopupMenuDescription fe_editor_show_options_popups[] =
{
    { "separator", fe_menu_separator, NULL, (XtPointer)XmSHADOW_ETCHED_IN },
    { "showMenubar", fe_menu_button, NULL, fe_editor_show_menubar_cb },
    { NULL }
};

static XtPointer
fe_editor_destroy_mappee(Widget widget, XtPointer data)
{
    Widget menu;

    if (XtIsSubclass(widget, xmCascadeButtonGadgetClass)) {
	XtVaGetValues(widget, XmNsubMenuId, &menu, 0);
	if (menu != 0)
	    fe_WidgetTreeWalk(menu, fe_editor_destroy_mappee, NULL);
    }

    XtDestroyWidget(widget);

    return 0;
}

static void
fe_popup_menu_popdown_cb(Widget menu, XtPointer closure, XtPointer cb)
{
    MWContext *context = (MWContext *) closure;

    fe_WidgetTreeWalk(menu, fe_editor_destroy_mappee, NULL);
    fe_SetCursor(context, False);
}

static unsigned
fe_add_menu(fe_PopupMenuDescription* tgt, fe_PopupMenuDescription* src)
{
    unsigned i;

    for (i = 0; src[i].name; i++)
	tgt[i] = src[i];

    return i;
}

static Widget
fe_EditorCreatePopupMenu(MWContext* context)
{
    fe_PopupMenuDescription items[32]; /* better not exceed */
    fe_PopupMenuDescription* list;
    unsigned nitems = 0;
    Widget parent = CONTEXT_WIDGET(context);
    Widget menu;
    ED_ElementType e_type;

    nitems += fe_add_menu(&items[nitems], fe_editor_title_popups);

    e_type = EDT_GetCurrentElementType(context);

    switch (e_type) {
    case ED_ELEMENT_IMAGE:       list = fe_editor_image_popups;  break;
    case ED_ELEMENT_UNKNOWN_TAG: list = fe_editor_tag_popups;    break;
    case ED_ELEMENT_TARGET:      list = fe_editor_target_popups; break;
    case ED_ELEMENT_HRULE:       list = fe_editor_hrule_popups;  break;
    case ED_ELEMENT_TEXT:
    default:                     list = fe_editor_character_popups; break;
    }
    nitems += fe_add_menu(&items[nitems], list);

    /*
     *    Document Props..
     */
    nitems += fe_add_menu(&items[nitems], fe_editor_document_popups);

    /*
     *    Table support.
     */
    if (EDT_IsInsertPointInTable(context) != 0) {
	nitems += fe_add_menu(&items[nitems], fe_editor_table_popups);
    }

    /*
     *    Link stuff.
     */
    if (EDT_GetHREF(context)) { 
	nitems += fe_add_menu(&items[nitems], fe_editor_link_popups);
    } else {
	nitems += fe_add_menu(&items[nitems], fe_editor_insert_link_popups);
    }


    /*
     *    Add cut&paste menu.
     */
    nitems += fe_add_menu(&items[nitems], fe_editor_edit_popups);

    /*
     *    If they accidently deleted the menubar, help them out.
     */
    if (!CONTEXT_DATA(context)->show_menubar_p)
        nitems += fe_add_menu(&items[nitems], fe_editor_show_options_popups);
    
    items[nitems].name = NULL;
    
    menu = fe_CreatePopupMenu(context, parent, "popup", NULL, 0);
    fe_PopulateMenuFromDescription(context, menu, items);
    
    /*
     *    Add destruction callback.
     */
    XtAddCallback(XtParent(menu), XmNpopdownCallback,
		  fe_popup_menu_popdown_cb, context);
    
    return menu;
}

static char*
fe_action_name(char* buf,
	       XtActionsRec* action_list,
	       Cardinal      action_list_size,
	       XtActionProc  action_proc,
	       String *av, Cardinal *ac)
{
    int i;
    char* name = XP_GetString(XFE_UNKNOWN); /* "<unknown>" */

    if (action_list_size == 0)
	action_list_size = 100000; /* look for null termination */

    /*
     *    Look for action name in the action_list.
     */
    for (i = 0; i < action_list_size && action_list[i].string != NULL; i++) {
	if (action_list[i].proc == action_proc) {
	    name = action_list[i].string;
	    break;
	}
    }

    strcpy(buf, name);
    strcat(buf, "(");
    for (i = 0; i < *ac; i++) {
	if (i != 0)
	    strcat(buf, ",");
	strcat(buf, av[i]);
    }
    strcat(buf, ")");

    return buf;
}


/*
 *    Editor specific action handlers.
 */
static void
fe_ActionSyntaxAlert(MWContext*, XtActionProc, String*, Cardinal*);
static void
fe_ActionWrongContextAlert(MWContext*, XtActionProc, String*, Cardinal*);

#define FE_ACTION_VALIDATE(proc)                             \
if (!(context) || (context)->type != MWContextEditor) {      \
    fe_ActionWrongContextAlert(context,                      \
			       (proc),                       \
			       av, ac);                      \
    return;                                                  \
}

#define FE_SYNTAX_ERROR(proc) fe_ActionSyntaxAlert(context, (proc), av, ac)

/*=========================== ACTIONS =================================*/
static void
fe_editor_insert_line_break_action(Widget widget, XEvent *event,
				   String *av, Cardinal *ac)
{
    MWContext*   context = (MWContext *)fe_WidgetToMWContext(widget);
    ED_BreakType type;

    FE_ACTION_VALIDATE(fe_editor_insert_line_break_action);

    if (*ac == 0 || (*ac == 1 && strcmp(av[0], "normal") == 0))
	type = ED_BREAK_NORMAL;
    else if (*ac == 1 && strcmp(av[0], "left") == 0)
	type = ED_BREAK_LEFT;
    else if (*ac == 1 && strcmp(av[0], "right") == 0)
	type = ED_BREAK_RIGHT;
    else if (*ac == 1 && strcmp(av[0], "both") == 0)
	type = ED_BREAK_BOTH;
    else { /* junk */
	FE_SYNTAX_ERROR(fe_editor_insert_line_break_action);
	return;
    }

    fe_EditorLineBreak(context, type);
}

static void /* my entrant for longest function name context..djw */
fe_editor_insert_non_breaking_space_action(Widget widget, XEvent *event,
				   String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_insert_non_breaking_space_action);
    
    fe_EditorNonBreakingSpace(context);
}

struct parse_tokens_st {
    char*    name;
    unsigned bits;
};
static struct parse_tokens_st parse_character_tokens[] = {
    { "bold", TF_BOLD },
    { "italic", TF_ITALIC },
    { "underline", TF_UNDERLINE },
    { "fixed",  TF_FIXED },
    { "super",  TF_SUPER },
    { "sub",    TF_SUB   },
    { "strikethrough", TF_STRIKEOUT },
    { "blink",  TF_BLINK },
    { "none",   TF_NONE } /* probably does nothing, but TF_NONE may change */
};

static ED_TextFormat
parse_character_types(char* s)
{
    ED_TextFormat bits = 0;
    char *p;
    char *q;
    int i;

    s = strdup(s); /* lazy */

    for (p = s; *p; ) {
	q = strchr(p, '|'); /* look for or */

	if (q)
	    *q++ = '\0'; /* punch out or */
	else
	    q = &p[strlen(p)];

	/*
	 *    Check the token.
	 */
	for (i = 0; i < countof(parse_character_tokens); i++) {
	    if (strcmp(p, parse_character_tokens[i].name) == 0) {
		bits |= parse_character_tokens[i].bits;
		break;
	    }
	}
	/* Should generate an error for no match */
	/*FIXME*/ 
	p = q;
    }

    free(s);

    return bits;
}

/*
 *    syntax: set-character-properties(type1|type2|type3....);
 *            set-character-properties(none);
 */
static void
fe_editor_set_character_properties_action(Widget widget, XEvent *event,
					  String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    ED_TextFormat bits;

    FE_ACTION_VALIDATE(fe_editor_set_character_properties_action);

    if (*ac == 1) {
	if (strcmp(av[0], "none") == 0) {
	    fe_EditorDoPoof(context);
	} else {
	    bits = parse_character_types(av[0]);
	    fe_EditorCharacterPropertiesSet(context, bits);
	}
	return;
    }

    FE_SYNTAX_ERROR(fe_editor_set_character_properties_action);
}

static void
fe_editor_toggle_character_properties_action(Widget widget, XEvent *event,
					  String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    ED_TextFormat bits;
    ED_TextFormat value;

    FE_ACTION_VALIDATE(fe_editor_toggle_character_properties_action);

    if (*ac == 1) {
	value = fe_EditorCharacterPropertiesGet(context);

	bits = parse_character_types(av[0]);

	value ^= bits;

	fe_EditorCharacterPropertiesSet(context, value);
	return;
    }

    FE_SYNTAX_ERROR(fe_editor_toggle_character_properties_action);
}

/*
 *    syntax: set-paragraph-properties(type);
 */
static void
fe_editor_set_paragraph_properties_action(Widget widget, XEvent *event,
					  String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    TagType    tag;
    int i;

    FE_ACTION_VALIDATE(fe_editor_set_paragraph_properties_action);

    if (*ac == 1) {

	for (i = 0; fe_paragraph_style_menu_items[i].name; i++) {
	    if (strcmp(av[0], fe_paragraph_style_menu_items[i].name) == 0) {
		tag = (TagType)(unsigned)fe_paragraph_style_menu_items[i].data;
		fe_EditorParagraphPropertiesSet(context, tag);
		return;
	    }
	}
    }

    FE_SYNTAX_ERROR(fe_editor_set_paragraph_properties_action);
}

static void
fe_editor_set_font_size_action(Widget widget, XEvent *event,
			       String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    ED_FontSize size;

    FE_ACTION_VALIDATE(fe_editor_set_font_size_action);

    if (*ac == 1) {
	if (strcmp(av[0], "-2") == 0)
	    size = ED_FONTSIZE_MINUS_TWO;
	else if (strcmp(av[0], "-1") == 0)
	    size = ED_FONTSIZE_MINUS_ONE;
	else if (strcmp(av[0], "+0") == 0)
	    size = ED_FONTSIZE_ZERO;
	else if (strcmp(av[0], "+1") == 0)
	    size = ED_FONTSIZE_PLUS_ONE;
	else if (strcmp(av[0], "+2") == 0)
	    size = ED_FONTSIZE_PLUS_TWO;
	else if (strcmp(av[0], "+3") == 0)
	    size = ED_FONTSIZE_PLUS_THREE;
	else if (strcmp(av[0], "+4") == 0)
	    size = ED_FONTSIZE_PLUS_FOUR;
	else if (strcmp(av[0], "default") == 0)
	    size = ED_FONTSIZE_ZERO;
	else if (strcmp(av[0], "increase") == 0)
	    size = fe_EditorFontSizeGet(context) + 1;
	else if (strcmp(av[0], "decrease") == 0)
	    size = fe_EditorFontSizeGet(context) - 1;
	else { /* junk */
	    FE_SYNTAX_ERROR(fe_editor_set_font_size_action);
	    return;
	}

	fe_EditorFontSizeSet(context, size);
	return;
    }

    FE_SYNTAX_ERROR(fe_editor_set_font_size_action);
}

static void
fe_editor_increase_font_size_action(Widget widget, XEvent *event,
			       String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    String   arg = "increase";
    Cardinal argc = 1;

    FE_ACTION_VALIDATE(fe_editor_increase_font_size_action);

    fe_editor_set_font_size_action(widget, event, &arg, &argc);
}

static void
fe_editor_decrease_font_size_action(Widget widget, XEvent *event,
			       String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    String   arg = "decrease";
    Cardinal argc = 1;

    FE_ACTION_VALIDATE(fe_editor_decrease_font_size_action);

    fe_editor_set_font_size_action(widget, event, &arg, &argc);
}

#define XFE_EDITOR_MAX_INSERT_SIZE 512

static void
fe_editor_key_input_action(Widget widget,
			   XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    char       buf[XFE_EDITOR_MAX_INSERT_SIZE + 1];
    KeySym     keysym;
    Status     status_return;
    int        len;
    int        n;
    EDT_ClipboardResult result;

    FE_ACTION_VALIDATE(fe_editor_key_input_action);

    if (widget != CONTEXT_DATA(context)->drawing_area) {
#ifdef DEBUG
	(void) fprintf(real_stderr,
		"fe_editor_key_input_action got wrong widget (%s)\n",
		XtName(widget));
#endif /* DEBUG */
	XtSetKeyboardFocus(CONTEXT_WIDGET(context),
				CONTEXT_DATA(context)->drawing_area);
	return;
    }
    len = XmImMbLookupString(widget,
			     (XKeyEvent *)event,
			     buf,
			     XFE_EDITOR_MAX_INSERT_SIZE, 
			     &keysym,
			     &status_return);
    
    if (status_return == XBufferOverflow || len > XFE_EDITOR_MAX_INSERT_SIZE)
        return;

    if (len > 0) {
	char *str;

	buf[len] = 0;
	str = (char *) fe_ConvertFromLocaleEncoding(context->win_csid,
		(unsigned char *) buf);
	if (str != buf) {
	    len = strlen(str);
	}
        for (n = 0; n < len; n++) {
	    if ((result = EDT_KeyDown(context, str[n], 0, 0)) != EDT_COP_OK) {
	        fe_editor_report_copy_error(context, result);
	    }
	}
	if (str != buf) {
	    XP_FREE(str);
	}
    }
}

static void
fe_editor_beginning_of_line_action(Widget widget,XEvent *event,String *av,Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

  FE_ACTION_VALIDATE(fe_editor_beginning_of_line_action);

  EDT_BeginOfLine(context, FALSE);
  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_end_of_line_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

  FE_ACTION_VALIDATE(fe_editor_end_of_line_action);

  EDT_EndOfLine(context, FALSE);
  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_backward_character_action(Widget widget, XEvent *event,
				    String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

  FE_ACTION_VALIDATE(fe_editor_backward_character_action);

  EDT_PreviousChar(context, FALSE);
  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_forward_character_action(Widget widget, XEvent *event,
				   String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

  FE_ACTION_VALIDATE(fe_editor_forward_character_action);

  EDT_NextChar(context, FALSE);
  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_delete_previous_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  EDT_ClipboardResult result;
  
  FE_ACTION_VALIDATE(fe_editor_delete_previous_action);

  if ((result = EDT_DeletePreviousChar(context)) != EDT_COP_OK)
      fe_editor_report_copy_error(context, result);

  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_delete_next_action(Widget widget, XEvent *event,
			     String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  
    FE_ACTION_VALIDATE(fe_editor_delete_next_action);

    fe_EditorDeleteItem(context);
}

static void
fe_editor_process_up_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  
  FE_ACTION_VALIDATE(fe_editor_process_up_action);

  EDT_Up(context, FALSE);
  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_process_down_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  
  FE_ACTION_VALIDATE(fe_editor_process_down_action);

  EDT_Down(context, FALSE);

  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_process_return_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  
  FE_ACTION_VALIDATE(fe_editor_process_return_action);

  EDT_ReturnKey(context);

  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_update_toolbar_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
  
  FE_ACTION_VALIDATE(fe_editor_update_toolbar_action);

  fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_grab_focus_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = fe_MotionWidgetToMWContext(widget);
  unsigned long x, y;
  Time time;

  FE_ACTION_VALIDATE(fe_editor_grab_focus_action);

  fe_UserActivity(context); /* tell the app who has focus */

  /* don't do this -- Japanese input method requires focus
  fe_NeutralizeFocus(context);
  */

  time = fe_getTimeFromEvent(context, event); /* get the time */

  fe_EventLOCoords(context, event, &x, &y); /* get the doc co-ords */

  EDT_StartSelection(context, x, y);
}

static void
fe_editor_extend_start_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = fe_MotionWidgetToMWContext (widget);
    unsigned long x, y;
    Time time;

    FE_ACTION_VALIDATE(fe_editor_extend_start_action);

    fe_UserActivity (context);  

    /* don't do this -- Japanese input method requires focus
    fe_NeutralizeFocus (context);
    */

    if (CONTEXT_DATA(context)->editor.ascroll_data.timer_id)
	XtRemoveTimeOut(CONTEXT_DATA(context)->editor.ascroll_data.timer_id);

    if (
	CONTEXT_DATA(context)->clicking_blocked
	||
	CONTEXT_DATA (context)->synchronous_url_dialog)
	{
	    fe_Bell(context);
	    return;
	}

    time = fe_getTimeFromEvent(context, event); /* get the time */
  
    fe_EventLOCoords(context, event, &x, &y);
#if 1
    /* need to impliment disown */
    EDT_StartSelection(context, x, y);
#else
    fe_DisownSelection(context, time, False);
    LO_StartSelection(context, x, y);
#endif
}

static void
fe_editor_extend_adjust_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = fe_MotionWidgetToMWContext(widget);
    Time       time;
    unsigned long x;
    unsigned long y;

    FE_ACTION_VALIDATE(fe_editor_extend_adjust_action);

    time = fe_getTimeFromEvent(context, event); /* get the time */

    fe_UserActivity(context);

    /* don't do this -- Japanese input method requires focus
    fe_NeutralizeFocus(context);
    */

    fe_EventLOCoords (context, event, &x, &y);

    if (CONTEXT_DATA(context)->editor.ascroll_data.timer_id)
	XtRemoveTimeOut(CONTEXT_DATA(context)->editor.ascroll_data.timer_id);
    fe_editor_keep_pointer_visible(context, x, y);

#if 1
    /* need to impliment own */
    EDT_ExtendSelection(context, x, y);
#else
    LO_ExtendSelection (context, x, y);
    fe_OwnSelection (context, time, False);
#endif

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

static void
fe_editor_extend_end_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext *context = fe_MotionWidgetToMWContext (widget);
    Time time;
    unsigned long x;
    unsigned long y;

    FE_ACTION_VALIDATE(fe_editor_extend_end_action);

    fe_UserActivity(context);
    time = fe_getTimeFromEvent(context, event); /* get the time */

    fe_EventLOCoords (context, event, &x, &y);

    if (CONTEXT_DATA(context)->editor.ascroll_data.timer_id)
	XtRemoveTimeOut(CONTEXT_DATA(context)->editor.ascroll_data.timer_id);

#if 1
    /* need to impliment own */
    EDT_EndSelection(context, x, y);
#else
    LO_EndSelection(context);
    fe_OwnSelection(context, time, False);
#endif
    fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_undo_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_undo_action);

    fe_EditorUndo(context);
}

static void
fe_editor_redo_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
  MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

  fe_EditorRedo(context);
}

static void
fe_editor_refresh_action(Widget widget, XEvent *event, String *av,
			 Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_refresh_action);

    fe_EditorRefresh(context);
}

static void
fe_editor_cut_action(Widget widget, XEvent *event, String *av,
			 Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_cut_action);

    fe_EditorCut(context, fe_getTimeFromEvent(context, event));
}

static void
fe_editor_copy_action(Widget widget, XEvent *event, String *av,
			 Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_copy_action);

    fe_EditorCopy(context, fe_getTimeFromEvent(context, event));
}

static void
fe_editor_paste_action(Widget widget, XEvent *event, String *av,
			 Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_paste_action);

    fe_EditorPaste(context, fe_getTimeFromEvent(context, event));
}

static void
fe_editor_find_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_find_action);

    fe_EditorFind(context);
}

static void
fe_editor_find_again_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_find_again_action);

    fe_EditorFindAgain(context);
}

static void
fe_editor_reload_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    Boolean super_reload = FALSE;

    FE_ACTION_VALIDATE(fe_editor_reload_action);

    if (*ac == 1 && strcmp(av[0], "super") == 0)
	super_reload = TRUE;

    fe_EditorReload(context, super_reload);
}

static void
fe_editor_open_file_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_open_file_action);

    fe_EditorOpenFile(context);
}

static void
fe_editor_open_url_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_open_url_action);

    fe_EditorOpenURLDialog(context);
}

static void
fe_editor_new_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_new_action);

    fe_EditorNew(context, NULL);
}

static void
fe_editor_delete_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_delete_action);

    if (!fe_save_file_check(context, FALSE, FALSE))
	return;

    fe_EditorDelete(context);
}

static void
fe_editor_save_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_save_action);

    fe_EditorSave(context);
}

static void
fe_editor_select_all_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_select_all_action);

    fe_EditorSelectAll(context);
}

static void
fe_editor_edit_source_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_edit_source_action);

    fe_EditorEditSource(context);
}

static void
fe_editor_select_word_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    unsigned long x;
    unsigned long y;

    FE_ACTION_VALIDATE(fe_editor_select_word_action);

    fe_EventLOCoords(context, event, &x, &y);

    EDT_DoubleClick(context, x, y); /* this takes screen co-ords?? */
#if 0
    /* can't do EDT_GetCurrentElementType() after EDT_DoubleClick()?? */
    fe_EditorUpdateToolbar(context, 0);
#endif
}

static void
fe_editor_dialog_action(Widget widget, XEvent *event, String *av,
				  Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_dialog_action);
    
    if (*ac == 1) {
	if (strcasecmp(av[0], "text") == 0)
	    fe_EditorPropertiesDialogDo(context,
					XFE_EDITOR_PROPERTIES_CHARACTER);
	else if (strcasecmp(av[0], "link") == 0)
	    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_LINK);
	else if (strcasecmp(av[0], "target") == 0)
	    fe_EditorTargetPropertiesDialogDo(context);
	else if (strcasecmp(av[0], "image") == 0)
	    fe_EditorPropertiesDialogDo(context, XFE_EDITOR_PROPERTIES_IMAGE);
	else if (strcasecmp(av[0], "hrule") == 0)
	    fe_EditorHorizontalRulePropertiesDialogDo(context);
	else if (strcasecmp(av[0], "tag") == 0)
	    fe_EditorHtmlPropertiesDialogDo(context);
	else if (strcasecmp(av[0], "document") == 0)
	    fe_EditorDocumentPropertiesDialogDo(context, 0); /* arg */
	else if (strcasecmp(av[0], "table") == 0)
	    fe_EditorTablePropertiesDialogDo(context, 0);
	else if (strcasecmp(av[0], "table-insert") == 0)
	    fe_EditorTableCreateDialogDo(context);
	else if (strcasecmp(av[0], "color") == 0)
	    fe_EditorSetColorsDialogDo(context);
	else if (strcasecmp(av[0], "publish") == 0)
	    fe_EditorPublishDialogDo(context);
	else
	    FE_SYNTAX_ERROR(fe_editor_dialog_action);

	return;
    }
    FE_SYNTAX_ERROR(fe_editor_dialog_action);
}

static char*
fe_editor_element_type_name(MWContext* context)
{
    ED_ElementType e_type; /* jag */

    e_type = EDT_GetCurrentElementType(context);

    switch (e_type) {
    case ED_ELEMENT_TEXT:        return "text";
    case ED_ELEMENT_IMAGE:       return "image";
    case ED_ELEMENT_UNKNOWN_TAG: return "tag";
    case ED_ELEMENT_TARGET:      return "target";
    case ED_ELEMENT_HRULE:       return "hrule";
    default:                     return "unknown";
    }
}

static fe_editor_click_action_last; /* this is very lazy */

static void
fe_editor_click_action(Widget widget, XEvent *event, String *av,
			    Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    char*      e_type;
    char*      c_type;
    char*      p_name = NULL;
    int        n;
    int        m;
    Time time;
    unsigned n_clicks = 1;
    unsigned c_clicks = 1;

    FE_ACTION_VALIDATE(fe_editor_click_action);

    time = fe_getTimeFromEvent(context, event);

    if (time - fe_editor_click_action_last <
	XtGetMultiClickTime(XtDisplay(widget)))
	n_clicks = 2;

    fe_editor_click_action_last = time;

    e_type = fe_editor_element_type_name(context);

    for (n = 0; n + 1 < *ac; n++) {

	if (strcasecmp(av[n], "double") == 0) {
	    c_clicks = 2;
	    n++;
	} else if (strcasecmp(av[n], "single") == 0) {
	    c_clicks = 1;
	    n++;
	} else {
	    c_clicks = 1;
	}
	    
	c_type = av[n++];
	
	if ((strcmp(c_type, "*") == 0 || strcasecmp(c_type, e_type) == 0)
	    &&
	    c_clicks == n_clicks) {
	    p_name = av[n++]; /* action name */
	}

	/* skip until "" or end */
	for (m = n; m < *ac && av[m] != NULL && av[m][0] != '\0'; m++)
	    ;

	if (p_name != NULL && p_name[0] != '\0') {
	    XtCallActionProc(widget, p_name, event, &av[n], m - n);
	    break;
	}
	n = m;
    }

    fe_EditorUpdateToolbar(context, 0);
}

static void
fe_editor_indent_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_indent_action);
    
    if (*ac == 0)
	fe_EditorIndent(context, FALSE);
    else if (*ac == 1 && strcasecmp(av[0], "in") == 0)
	fe_EditorIndent(context, FALSE);
    else if (*ac == 1 && strcasecmp(av[0], "out") == 0)
	fe_EditorIndent(context, TRUE);
    else
	FE_SYNTAX_ERROR(fe_editor_indent_action);
}

static void
fe_get_selected_text_rect(MWContext* context,
			  LO_TextStruct* text,
			  int32*         rect_x,
			  int32*         rect_y,
			  int32*         rect_width,
			  int32*         rect_height)
{
    PA_Block    text_save = text->text;
    int         len_save = text->text_len;
    LO_TextInfo info;
    
    text->text_len = text->sel_start;
    XFE_GetTextInfo(context, text, &info);

    *rect_x = text->x + text->x_offset + info.max_width;
    *rect_y = text->y + text->y_offset;

    text->text = (PA_Block)((char*)text->text + text->sel_start);
    text->text_len = text->sel_end - text->sel_start;
    
    XFE_GetTextInfo(context, text, &info);

    *rect_width = info.max_width;
    *rect_height = info.ascent + info.descent;

    text->text = text_save;
    text->text_len = len_save;
}

static Boolean
fe_editor_selection_contains_point(MWContext* context, int32 x, int32 y)
{
    int32 start_selection;
    int32 end_selection;
    LO_Element* start_element = NULL;
    LO_Element* end_element = NULL;
    LO_Element* lo_element;
    int32 rect_x;
    int32 rect_y;
    int32 rect_width;
    int32 rect_height;

    if (!EDT_IsSelected(context))
	return FALSE;

    LO_GetSelectionEndpoints(context,
			     &start_element,
			     &end_element,
			     &start_selection,
			     &end_selection);

    if (start_element == NULL)
	return FALSE;

    for (lo_element = start_element;
	 lo_element != NULL;
	 lo_element = ((LO_Any *)lo_element)->next) {

	if (lo_element->type == LO_TEXT &&
	    (lo_element == start_element || lo_element == end_element)) {
	    LO_TextStruct* text = (LO_TextStruct*)lo_element;

	    if (text->text == NULL) {
		if (text->prev != NULL && text->prev->type == LO_TEXT) {
		    text = (LO_TextStruct*)text->prev;
		} else {
		    text = (LO_TextStruct*)text->next;
		}
	    }
	    
	    if (text->text == NULL)
		continue;
	    
	    fe_get_selected_text_rect(context, text,
				      &rect_x,
				      &rect_y,
				      &rect_width,
				      &rect_height);
	    
	} else if (lo_element->type == LO_LINEFEED) {
	    continue;
	    
	} else {
	    LO_Any* lo_any = (LO_Any*)lo_element;
	    rect_x = lo_any->x + lo_any->x_offset;
	    rect_y = lo_any->y + lo_any->y_offset;
	    rect_width = lo_any->width;
	    rect_height = lo_any->height;
	}
	
	if (x > rect_x && y > rect_y &&
	    x < (rect_x + rect_width) && y < (rect_y + rect_height))
	    return TRUE;

	if (lo_element == end_element)
	    break;
    }

    return FALSE;
}

static void
fe_editor_popup_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);
    Widget menu;
    unsigned long x;
    unsigned long y;

    FE_ACTION_VALIDATE(fe_editor_popup_action);

    fe_UserActivity(context); /* tell the app who has focus */

    fe_EventLOCoords(context, event, &x, &y); /* get the doc co-ords */

    if (!fe_editor_selection_contains_point(context, x, y))
	EDT_SelectObject(context, x, y);
    
    menu = fe_EditorCreatePopupMenu(context);

    XmMenuPosition(menu, (XButtonPressedEvent *) event);

    XtManageChild(menu);
}

static char*
fe_lo_image_anchor(LO_ImageStruct* image,
		     long x, long y,
		     MWContext* context)
{
    struct fe_Pixmap *fep = (struct fe_Pixmap *) image->FE_Data;

    if (fep &&
	fep->type == fep_icon &&
	fep->data.icon_number == IL_IMAGE_DELAYED) { /* delayed image */

	long width, height;

	fe_IconSize(IL_IMAGE_DELAYED, &width, &height);

	if (image->alt &&
	    image->alt_len &&
	    (x > image->x + image->x_offset + 1 + 4 + width)) {
	    if (image->anchor_href) {
		return (char *)image->anchor_href->anchor;
	    }
	} /* else fall to end */
    } else if (image->image_attr->attrmask & LO_ATTR_ISFORM) { /* form */
	return "";
    }
    /*
     *    Internal editor image maybe?
     */
    else if ((image->image_attr->attrmask & LO_ATTR_INTERNAL_IMAGE) &&
	       EDT_IS_EDITOR(context) &&
	       image->alt != NULL) {
	return (char*)image->alt;
    }
    /*
     * This would be a client-side usemap image.
     */
    else if (image->image_attr->usemap_name != NULL) {
	
	LO_AnchorData *anchor_href;
	long ix = image->x + image->x_offset;
	long iy = image->y + image->y_offset;
	long mx = x - ix - image->border_width;
	long my = y - iy - image->border_width;
	
	anchor_href = LO_MapXYToAreaAnchor(context,
					   (LO_ImageStruct *)image,
					   mx, my);
	if (anchor_href) {
	    return (char *)anchor_href->anchor;
	} /* else fall to end */
    } else { /* some old random image */
	if (image->anchor_href) {
	    return (char *)image->anchor_href->anchor;
	}
    }

    return NULL;
}

static LO_Element*   fe_editor_motion_action_last_le;
static Boolean       fe_editor_motion_action_last_le_is_image;

static void
fe_editor_motion_action(Widget widget, XEvent *event,
			String *av, Cardinal *ac)
{
    MWContext*    context = fe_MotionWidgetToMWContext(widget);
    LO_Element*   le;
    unsigned long x, y;
    Cursor        cursor = None;
    char*         progress_string = NULL;
    char buf[80];
    char num[32];

    FE_ACTION_VALIDATE(fe_editor_motion_action);

    fe_EventLOCoords(context, event, &x, &y); /* get the doc co-ords */

    le = LO_XYToElement(context, (int32)x, (int32)y);

    if (le == fe_editor_motion_action_last_le &&
	!fe_editor_motion_action_last_le_is_image) {
	return; /* no change */
    }

    fe_editor_motion_action_last_le_is_image = FALSE;
    fe_editor_motion_action_last_le = le;
    
    if (le != NULL) {
	/*
	 *    Are we over text? Special cursor.
	 */
	if (le->type == LO_TEXT) {
	    cursor = CONTEXT_DATA(context)->editable_text_cursor;
	    if (le->lo_text.anchor_href) { /* link */
		progress_string = (char *)le->lo_text.anchor_href->anchor;
	    }
        /*
	 *    Over image, special progress.
	 */
	} else if (le->type == LO_IMAGE) {
	    progress_string = fe_lo_image_anchor(&le->lo_image, /* link ? */
						 x, y,
						 context);
	    if (progress_string != NULL && progress_string[0] == '\0')
		progress_string = NULL;
	    
	    if (progress_string == NULL) {

		if (le->lo_image.image_url) {
		    progress_string = (char*)le->lo_image.image_url;
		} else if (le->lo_image.lowres_image_url) {
		    progress_string = (char*)le->lo_image.lowres_image_url;
		}

		if (progress_string != NULL) {
		    sprintf(num, ",%d,%d",
			    x - le->lo_image.x, y - le->lo_image.y);
		    FE_CondenseURL(buf, progress_string,
				   sizeof(buf) - 1 - strlen(num));
		    strcat(buf, num);
		    progress_string = buf;
		    fe_editor_motion_action_last_le_is_image = TRUE;
		}
	    }
	}
    }

    if (CONTEXT_DATA(context)->clicking_blocked ||
	CONTEXT_DATA(context)->synchronous_url_dialog) {
	cursor = CONTEXT_DATA(context)->busy_cursor;
    }

    if (CONTEXT_DATA(context)->drawing_area) {
	XDefineCursor(XtDisplay(CONTEXT_DATA(context)->drawing_area),
		      XtWindow(CONTEXT_DATA(context)->drawing_area),
		      cursor);
    }

    if (progress_string == NULL)
	progress_string = "";

    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. */
	fe_MidTruncatedProgress(context, progress_string);
    }
}

static void
fe_editor_debug_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

#if 1
    FE_Alert(context, "I see no bugs here!!!");
#endif
}

static void
fe_editor_unimplemented_action(Widget widget, XEvent *event, String *av, Cardinal *ac)
{
    MWContext* context = (MWContext *)fe_WidgetToMWContext(widget);

    FE_ACTION_VALIDATE(fe_editor_unimplemented_action);

    FE_Alert(context, "unimplimented action");
}


#define EA_PREFIX "editor-"

static XtActionsRec fe_editor_actions [] =
{
  { EA_PREFIX "debug",	                 fe_editor_debug_action },
  
  { EA_PREFIX "new",	                 fe_editor_new_action },
  { EA_PREFIX "delete",	                 fe_editor_delete_action },
  { EA_PREFIX "save",	                 fe_editor_save_action },
  { EA_PREFIX "reload",	                 fe_editor_reload_action },
  { EA_PREFIX "open-file",	         fe_editor_open_file_action },
  { EA_PREFIX "open-location",	         fe_editor_open_url_action },
  { EA_PREFIX "edit-source",	         fe_editor_edit_source_action },
  { EA_PREFIX "dialog",	                 fe_editor_dialog_action },
  { EA_PREFIX "popup-menu",	         fe_editor_popup_action },

  { EA_PREFIX "self-insert",	         fe_editor_key_input_action },
  { EA_PREFIX "beginning-of-line",       fe_editor_beginning_of_line_action },
  { EA_PREFIX "end-of-line",             fe_editor_end_of_line_action },
  { EA_PREFIX "backward-character",      fe_editor_backward_character_action },
  { EA_PREFIX "forward-character",       fe_editor_forward_character_action },
  { EA_PREFIX "delete-previous-character", fe_editor_delete_previous_action },
  { EA_PREFIX "delete-next-character",   fe_editor_delete_next_action },
  { EA_PREFIX "process-up",              fe_editor_process_up_action }, 
  { EA_PREFIX "process-down",            fe_editor_process_down_action }, 
  { EA_PREFIX "process-return",          fe_editor_process_return_action },
  { EA_PREFIX "grab-focus",              fe_editor_grab_focus_action },
  { EA_PREFIX "extend-start",            fe_editor_extend_start_action },
  { EA_PREFIX "extend-adjust",           fe_editor_extend_adjust_action },
  { EA_PREFIX "extend-end",              fe_editor_extend_end_action },
  { EA_PREFIX "motion",                  fe_editor_motion_action },

  { EA_PREFIX "undo",                    fe_editor_undo_action },
  { EA_PREFIX "redo",                    fe_editor_redo_action },
  { EA_PREFIX "insert-line-break",       fe_editor_insert_line_break_action },
  { EA_PREFIX "insert-non-breaking-space", 
                            fe_editor_insert_non_breaking_space_action },
  { EA_PREFIX "toggle-character-properties", 
                           fe_editor_toggle_character_properties_action },
  { EA_PREFIX "set-character-properties", 
                              fe_editor_set_character_properties_action },
  { EA_PREFIX "set-paragraph-properties",
                              fe_editor_set_paragraph_properties_action },
  { EA_PREFIX "set-font-size",           fe_editor_set_font_size_action },
  { EA_PREFIX "increase-font-size",      fe_editor_increase_font_size_action },
  { EA_PREFIX "decrease-font-size",      fe_editor_decrease_font_size_action },
  { EA_PREFIX "refresh",                 fe_editor_refresh_action },
  { EA_PREFIX "cut",                     fe_editor_cut_action },
  { EA_PREFIX "copy",                    fe_editor_copy_action },
  { EA_PREFIX "paste",                   fe_editor_paste_action },
  { EA_PREFIX "select-all",              fe_editor_select_all_action },
  { EA_PREFIX "select-word",             fe_editor_select_word_action },
  { EA_PREFIX "click",                   fe_editor_click_action },
  { EA_PREFIX "indent",                  fe_editor_indent_action },

  { EA_PREFIX "process-cancel",          fe_editor_unimplemented_action }, 
  { EA_PREFIX "set-anchor",              fe_editor_unimplemented_action },
  { EA_PREFIX "backward-word",           fe_editor_unimplemented_action },
  { EA_PREFIX "forward-word",            fe_editor_unimplemented_action },
  { EA_PREFIX "copy-clipboard",          fe_editor_unimplemented_action },

  { EA_PREFIX "find",                    fe_editor_find_action },
  { EA_PREFIX "find-again",              fe_editor_find_again_action },

  { EA_PREFIX "update-toolbar",          fe_editor_update_toolbar_action }
};

static void
fe_ActionSyntaxAlert(MWContext*    context,
		     XtActionProc  action_proc,
		     String* av, Cardinal* ac)
{
    char buf[1024];
    char name[1024];

    fe_action_name(name,
		   fe_editor_actions, countof(fe_editor_actions),
		   action_proc, av, ac);
    sprintf(buf, XP_GetString(XFE_ACTION_SYNTAX_ERROR), name);

    FE_Alert(context, buf);
}

static void
fe_ActionWrongContextAlert(MWContext*    context,
			   XtActionProc  action_proc,
			   String* av, Cardinal* ac)
{
    char buf[1024];
    char name[1024];

    fe_action_name(name,
		   fe_editor_actions, countof(fe_editor_actions),
		   action_proc, av, ac);
    sprintf(buf, XP_GetString(XFE_ACTION_WRONG_CONTEXT), name);

    if (context)
	FE_Alert(context, buf);
    else
	fprintf(real_stderr, buf);
}

void
fe_EditorInitActions()
{
  XtAppAddActions(
		  fe_XtAppContext,
		  fe_editor_actions,
		  countof(fe_editor_actions)
		  );
}

#endif /* EDITOR */