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

#include "mosaic.h"
#include "proxy.h"
struct Proxy *noproxy_list = NULL, *proxy_list = NULL, *ReadProxies();

void mo_init_hotmenu();
Widget mo_fill_toolbar(mo_window *win, Widget top, int w, int h);


#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ctype.h>
#include <sys/utsname.h>
#include <pwd.h>

#define __SRC__
#include "../libwww2/HTAAUtil.h"

/*SWP -- 9/7/95*/
char pre_title[80];
int cursorAnimCnt;
int makeBusy=0;

/*SWP -- colormap 3/19/96*/
extern int installed_colormap;
extern Colormap installed_cmap;

/* SWP -- Spoof Agent stuff */
extern int numAgents;
extern int selectedAgent;
extern char **agent;

/* PLB */
extern int newsShowAllGroups;
extern int newsShowAllArticles;
extern int newsShowReadGroups;
extern int newsNoThreadJumping;
extern int ConfigView;

void kill_splash();
int splash_cc=1; /* 1 if we need to free colors, 0 if already popped down */
XtIntervalId splashTimer;
Widget splash=NULL;

/*SWP -- 8/14/95*/
extern int tableSupportEnabled;
extern int securityType;

/*SWP -- 10.27.95 -- No Content Length*/
extern int noLength;

/* doesn't seem to be on all X11R4 systems
#if (XtSpecificationRelease == 4)
extern void _XEditResCheckMessages();
#define EDITRES_SUPPORT
#endif
*/

#if (XtSpecificationRelease > 4)
#define EDITRES_SUPPORT
#endif

/*
 * EDITRES_SUPPORT seems to fail with the HP libraries
 */
#if defined(__hpux) && defined(EDITRES_SUPPORT)
#undef EDITRES_SUPPORT
#endif

#ifdef EDITRES_SUPPORT
#include <X11/Xmu/Editres.h>
#endif

#include <X11/Intrinsic.h>

#include <Xm/LabelG.h>
#include <Xm/PushB.h>
#include <Xm/ScrolledW.h>
#include <Xm/ScrollBar.h>
#include <Xm/List.h>
#include <Xm/ToggleB.h>
#include <Xm/DrawnB.h>
#include <Xm/Text.h>
#include <Xm/TextF.h>
#include <Xm/DrawingA.h>
#include <Xm/MainW.h>
#include <Xm/MenuShell.h>
#include <Xm/Protocols.h>
#include <Xm/Separator.h>
#include <Xm/BulletinB.h>
#include <Xm/RowColumn.h>
#include <Xm/DialogS.h>
#include <Xm/Label.h>
#include <Xm/Form.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>


#include "libhtmlw/HTML.h"
#include "xresources.h"
#include "cci.h"


#include "bitmaps/xmosaic.xbm"
#include "bitmaps/xmosaic_left.xbm"
#include "bitmaps/xmosaic_right.xbm"
#include "bitmaps/xmosaic_down.xbm"
#include "bitmaps/security.xbm"
/*Busy Cursor*/
#include "bitmaps/busy_1.xbm"
#include "bitmaps/busy_2.xbm"
#include "bitmaps/busy_3.xbm"
#include "bitmaps/busy_4.xbm"
#include "bitmaps/busy_5.xbm"
#include "bitmaps/busy_6.xbm"
#include "bitmaps/busy_7.xbm"
#include "bitmaps/busy_8.xbm"
#include "bitmaps/busy_9.xbm"
#include "bitmaps/busy_1_mask.xbm"
#include "bitmaps/busy_2_mask.xbm"
#include "bitmaps/busy_3_mask.xbm"
#include "bitmaps/busy_4_mask.xbm"
#include "bitmaps/busy_5_mask.xbm"
#include "bitmaps/busy_6_mask.xbm"
#include "bitmaps/busy_7_mask.xbm"
#include "bitmaps/busy_8_mask.xbm"
#include "bitmaps/busy_9_mask.xbm"


/* Because Sun cripples the keysym.h file. */
#ifndef XK_KP_Up
#define XK_KP_Home              0xFF95  /* Keypad Home */
#define XK_KP_Left              0xFF96  /* Keypad Left Arrow */
#define XK_KP_Up                0xFF97  /* Keypad Up Arrow */
#define XK_KP_Right             0xFF98  /* Keypad Right Arrow */
#define XK_KP_Down              0xFF99  /* Keypad Down Arrow */
#define XK_KP_Prior             0xFF9A  /* Keypad Page Up */
#define XK_KP_Next              0xFF9B  /* Keypad Page Down */
#define XK_KP_End               0xFF9C  /* Keypad End */
#endif


#define SLAB_MENU 0
#define SLAB_TITLE 1
#define SLAB_URL 2
#define SLAB_TOOLS 3
#define SLAB_STATUS 4
#define SLAB_VIEW 5
#define SLAB_GLOBE 6
#define SLAB_SMALLGLOBE 7
#define SLAB_TEXTTOOLS 8

/*
 * Globals used by the pixmaps for the animated icon.
 * Marc, I imagine you might want to do something cleaner
 * with these?
 *
 * Marc's not here, man.
 */
extern int IconWidth, IconHeight, WindowWidth, WindowHeight;

extern Pixmap *IconPix,*IconPixSmall,*IconPixBig;

extern void MoCCINewConnection();
void mo_gui_update_meter(int level,char *text);

int animateCursor();
void createBusyCursors(Widget bob);
void stopBusyAnimation();

extern int force_dump_to_file;
extern char *HTAppVersion;

/* from cciBindings.c */
extern int cci_event;

/* for the -geometry fix (kludge) - DXP 8/30/95 */
int userSpecifiedGeometry = 0;
Dimension userWidth, userHeight;
Position userX, userY;

char *MakeFilename();
long GetCardCount(char *fname);

/* ------------------------------ variables ------------------------------- */

Display *dsp;
XtAppContext app_context;
Widget toplevel;
Widget view = NULL;  /* HORRIBLE HACK @@@@ */
int Vclass;  /* visual class for 24bit support hack */
/*AppData Rdata;  /* extern'd in mosaic.h */
char *global_xterm_str;  /* required for HTAccess.c now */

char *uncompress_program;
char *gunzip_program;

int use_default_extension_map;
char *global_extension_map;
char *personal_extension_map;
int use_default_type_map;
char *global_type_map;
char *personal_type_map;

int tweak_gopher_types;
int max_wais_responses;
int useAFS;
int have_hdf;
int ftp_timeout_val;
int ftpRedial;
int ftpRedialSleep;
int ftpFilenameLength;
int ftpEllipsisLength;
int ftpEllipsisMode;
int twirl_increment;


extern int sendAgent;
extern int sendReferer;
extern int imageViewInternal;

extern int do_comment;

/* --------------BalloonHelpStuff---------------------------------------- */

static void BalloonHelpMe(Widget w, XEvent *event)
{
    char *info;

    XtVaGetValues(w, XmNuserData, (XtPointer) &info, NULL);    
    mo_gui_notify_progress(info);
}

static void UnBalloonHelpMe(Widget w, XEvent *event)
{
    mo_gui_notify_progress(" ");
}

static char xlattab[] = "<Enter>: BalloonHelpMe()\n<Leave>: UnBalloonHelpMe()";

static XtActionsRec balloon_action[] = {
    {"BalloonHelpMe", (XtActionProc)BalloonHelpMe},
    {"UnBalloonHelpMe", (XtActionProc)UnBalloonHelpMe}
};

/* to use balloon help, add these bits to your widget ...  BJS 2/7/96 
 *    XmNtranslations, XtParseTranslationTable(xlattab),
 *    XmNuserData, (xtpointer) "Balloon Help String!",
 */

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

#ifndef DISABLE_TRACE
extern int httpTrace;
extern int www2Trace;
extern int htmlwTrace;
extern int nutTrace;

int cciTrace=0;
int srcTrace=0;
int cacheTrace=0;
#endif


/* from cciBindings.c */
extern int cci_get;

char *HTReferer = NULL;

/* This is exported to libwww, like altogether too many other
   variables here. */
int binary_transfer;
/* Now we cache the current window right before doing a binary
   transfer, too.  Sheesh, this is not pretty. */
mo_window *current_win;

/* If startup_document is set to anything but NULL, it will be the
   initial document viewed (this is separate from home_document
   below). */
char *startup_document = NULL;
/* If startup_document is NULL home_document will be the initial
   document. */
char *home_document = NULL;
char *machine;
char *shortmachine;
char *machine_with_domain;

XColor fg_color, bg_color;

static Cursor busy_cursor;
static int busy = 0;
static Widget *busylist = NULL;
char *cached_url = NULL;

/* Forward declaration of test predicate. */
int anchor_visited_predicate (Widget, char *);

/* When we first start the application, we call mo_startup()
   after creating the unmapped toplevel widget.  mo_startup()
   either sets the value of this to 1 or 0.  If 0, we don't
   open a starting window. */
int defer_initial_window;

/* Pixmaps for interrupt button. */
static Pixmap xmosaic_up_pix, xmosaic_left_pix, xmosaic_down_pix, xmosaic_right_pix;

#define MAX_BUSY_CURSORS 9
int numCursors=MAX_BUSY_CURSORS;
/* Pixmaps for the busy cursor animation */
static Cursor busyCursor[MAX_BUSY_CURSORS
];

/* Pixmaps for security button. */
static Pixmap security_pix;
extern Pixmap toolbarBack, toolbarForward, toolbarHome, toolbarReload,
    toolbarOpen, toolbarSave, toolbarClone, toolbarNew, toolbarClose,
    toolbarBackGRAY, toolbarForwardGRAY,
    toolbarSearch, toolbarPrint, toolbarPost, toolbarFollow,
    tearv, tearh, toolbarPostGRAY, toolbarFollowGRAY,
    toolbarNewsFwd, toolbarNewsFFwd, toolbarNewsRev, toolbarNewsFRev,
    toolbarNewsIndex, toolbarAddHotlist, toolbarNewsGroups,
    toolbarNewsFwdGRAY, toolbarNewsFFwdGRAY, toolbarNewsRevGRAY, toolbarNewsFRevGRAY,
    toolbarNewsIndexGRAY,
    toolbarFTPput, toolbarFTPmkdir;

extern Pixmap securityKerberos4, securityBasic, securityMd5, securityNone,
    securityUnknown, securityKerberos5, securityDomain, securityLogin,
    enc_not_secure;

extern char *HTDescribeURL (char *);
extern mo_status mo_post_access_document (mo_window *win, char *url,
                                          char *content_type, 
                                          char *post_data);
XmxCallbackPrototype (menubar_cb);

/* from gui-popup.c */
void mo_make_popup();

void mo_gui_check_security_icon_in_win(int type, mo_window *win);
void mo_gui_check_security_icon(int type);

void mo_assemble_controls(mo_window *win, int detach);


struct utsname mo_uname;

/* ----------------------------- WINDOW LIST ------------------------------ */

static mo_window *winlist = NULL;
static int wincount = 0;

/* Return either the first window in the window list or the next
   window after the current window. */
mo_window *mo_next_window (mo_window *win)
{
  if (win == NULL)
    return winlist;
  else
    return win->next;
}

/* Return a window matching a specified uniqid. */
mo_window *mo_fetch_window_by_id (int id)
{
  mo_window *win;

  win = winlist;
  while (win != NULL)
    {
      if (win->id == id)
/*        goto done;*/
	return(win);
      win = win->next;
    }

/* notfound:*/
  return NULL;
/*
 done:
  return win;*/
}

/* Register a window in the window list. */
mo_status mo_add_window_to_list (mo_window *win)
{
  wincount++;

  if (winlist == NULL)
    {
      win->next = NULL;
      winlist = win;
    }
  else
    {
      win->next = winlist;
      winlist = win;
    }

  return mo_succeed;
}

/* Remove a window from the window list. */
mo_status mo_remove_window_from_list (mo_window *win)
{
  mo_window *w = NULL, *prev = NULL;

  while (w = mo_next_window (w))
    {
      if (w == win)
        {
          /* Delete w. */
          if (!prev)
            {
              /* No previous window. */
              winlist = w->next;

              free (w);
              w = NULL;

              wincount--;

              /* Maybe exit. */
              if (!winlist)
                mo_exit ();
            }
          else
            {
              /* Previous window. */
              prev->next = w->next;

              free (w);
              w = NULL;

              wincount--;

              return mo_succeed;
            }
        }
      prev = w;
    }
   
  /* Couldn't find it. */
  return mo_fail;
}

/*
 * THIS IS NOT USED ANYMORE!
 */

/****************************************************************************
 * name:    mo_check_for_proxy
 * purpose: Return the location of the proxy gateway for the passed access
 *	    method.
 * inputs:  
 *   - char *access: access string from the URL (http, gopher, ftp, etc)
 * returns: 
 *   The proxy gateway to use. (http://proxy.ncsa.uiuc.edu:911/)
 * remarks: This should really be open-ended configurable.
 ****************************************************************************/
char *mo_check_for_proxy (char *access)
{
/*
	if (access == NULL)
	{
		return((char *)NULL);
	}
	else if (strcmp(access, "http") == 0)
	{
		return(Rdata.http_proxy);
	}
	else if (strcmp(access, "ftp") == 0)
	{
		return(Rdata.ftp_proxy);
	}
	else if (strcmp(access, "wais") == 0)
	{
		return(Rdata.wais_proxy);
	}
	else if (strcmp(access, "gopher") == 0)
	{
		return(Rdata.gopher_proxy);
	}
	else if (strcmp(access, "news") == 0)
	{
		return(Rdata.news_proxy);
	}
	else if (strcmp(access, "file") == 0)
	{
		return(Rdata.file_proxy);
	}
	else
	{
*/

		return((char *)NULL);
/*
	}
*/
}

/****************************************************************************
 * name:    mo_assemble_help_url
 * purpose: Make a temporary, unique filename.
 * inputs:  
 *   - char *file: Filename to be appended to Rdata.docs_directory.
 * returns: 
 *   The desired help url (a malloc'd string).
 * remarks: 
 ****************************************************************************/
char *mo_assemble_help_url (char *file)
{
  char *tmp;
  char *docs_directory = get_pref_string(eDOCS_DIRECTORY);

  if (!file)
    return strdup ("http://lose.lose/lose");

  tmp = (char *)malloc ((strlen (file) + strlen (docs_directory) + 4) *
                        sizeof (char));

  if (docs_directory[strlen(docs_directory)-1] == '/')
    {
      /* Trailing slash in docs_directory spec. */
      sprintf (tmp, "%s%s", docs_directory, file);
    }
  else
    {
      /* No trailing slash. */
      sprintf (tmp, "%s/%s", docs_directory, file);
    }
  
  return tmp;
}


/* ----------------------------- busy cursor ------------------------------ */

mo_status mo_not_busy (void)
{

	/* This is done from mo_gui_done_with_icon() */

	return mo_succeed;
}


void stopBusyAnimation() {

mo_window *win = NULL;
  
	if (busy) {
		XUndefineCursor (dsp, XtWindow (toplevel));
		while (win = mo_next_window (win)) {
			XUndefineCursor (dsp, XtWindow (win->base));
			if (win->history_win)
				XUndefineCursor (dsp, XtWindow (win->history_win));
			if (win->hotlist_win)
				XUndefineCursor (dsp, XtWindow (win->hotlist_win));
			if (win->searchindex_win)
				XUndefineCursor (dsp, XtWindow (win->searchindex_win));
		}
      
		XFlush (dsp);
		busy = 0;
	}

	return;
}


void createBusyCursors(Widget bob) {

int i;
Pixmap pmap,mmap;
XColor ccell1,ccell_fg,ccell_bg;

	if (! get_pref_boolean(eANIMATEBUSYICON) ) {
		numCursors=1;
		busyCursor[0]=busy_cursor;

		return;
	}

	XAllocNamedColor(dsp,(installed_colormap ?
			      installed_cmap :
			      DefaultColormapOfScreen(XtScreen(bob))),
			 "black",&ccell1,&ccell_fg);
	XAllocNamedColor(dsp,(installed_colormap ?
			      installed_cmap :
			      DefaultColormapOfScreen(XtScreen(bob))),
			 "white",&ccell1,&ccell_bg);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_1_mask_bits, busy_1_mask_width, busy_1_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_1_bits, busy_1_width, busy_1_height, 1, 0, 1);

	busyCursor[0]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_1_x_hot,busy_1_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_2_mask_bits, busy_2_mask_width, busy_2_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_2_bits, busy_2_width, busy_2_height, 1, 0, 1);
	busyCursor[1]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_2_x_hot,busy_2_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_3_mask_bits, busy_3_mask_width, busy_3_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_3_bits, busy_3_width, busy_3_height, 1, 0, 1);
	busyCursor[2]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_3_x_hot,busy_3_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_4_mask_bits, busy_4_mask_width, busy_4_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_4_bits, busy_4_width, busy_4_height, 1, 0, 1);
	busyCursor[3]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_4_x_hot,busy_4_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_5_mask_bits, busy_5_mask_width, busy_5_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_5_bits, busy_5_width, busy_5_height, 1, 0, 1);
	busyCursor[4]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_5_x_hot,busy_5_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_6_mask_bits, busy_6_mask_width, busy_6_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_6_bits, busy_6_width, busy_6_height, 1, 0, 1);
	busyCursor[5]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_6_x_hot,busy_6_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_7_mask_bits, busy_7_mask_width, busy_7_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_7_bits, busy_7_width, busy_7_height, 1, 0, 1);
	busyCursor[6]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_7_x_hot,busy_7_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_8_mask_bits, busy_8_mask_width, busy_8_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_8_bits, busy_8_width, busy_8_height, 1, 0, 1);
	busyCursor[7]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_8_x_hot,busy_8_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);

	mmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_9_mask_bits, busy_9_mask_width, busy_9_mask_height, 1, 0, 1);
	pmap = XCreatePixmapFromBitmapData
	(XtDisplay(bob), DefaultRootWindow(XtDisplay(bob)),
	 busy_9_bits, busy_9_width, busy_9_height, 1, 0, 1);
	busyCursor[8]=XCreatePixmapCursor(dsp,pmap,mmap,&ccell_fg,&ccell_bg,busy_9_x_hot,busy_9_y_hot);
	XFreePixmap(dsp,mmap);
	XFreePixmap(dsp,pmap);
}


int animateCursor() {

mo_window *win = NULL;

	if (!makeBusy) {
		mo_not_busy();
		stopBusyAnimation();

		return(0);
	}

	cursorAnimCnt++;
	if (cursorAnimCnt>=numCursors) {
		cursorAnimCnt=0;
	}

	XDefineCursor(dsp, XtWindow(toplevel), busyCursor[cursorAnimCnt]);
	while (win=mo_next_window(win)) {
		XDefineCursor(dsp, XtWindow(win->base),
			      busyCursor[cursorAnimCnt]);
		if (win->history_win) {
			XDefineCursor(dsp, XtWindow(win->history_win),
				      busyCursor[cursorAnimCnt]);
		}
		if (win->hotlist_win) {
			XDefineCursor(dsp, XtWindow(win->hotlist_win),
				      busyCursor[cursorAnimCnt]);
		}
		if (win->searchindex_win) {
			XDefineCursor(dsp, XtWindow(win->searchindex_win),
				      busyCursor[cursorAnimCnt]);
		}
	}

	XFlush(dsp);
	busy=1;

	return(1);
}


mo_status mo_busy (void) {

/* This happens in mo_gui_check_icon */
/*
	if (!busy) {
	}
*/
  
	return mo_succeed;
}


#ifdef HAVE_DTM
/* --------------------- mo_set_dtm_menubar_functions --------------------- */

mo_status mo_set_dtm_menubar_functions (mo_window *win)
{
  if (mo_dtm_out_active_p ())
    {
      /* If we've got an active outport, then we can send a document
         but not open another outport. */
      XmxRSetSensitive 
        (win->menubar, mo_dtm_open_outport, XmxNotSensitive);
      XmxRSetSensitive
        (win->menubar, mo_dtm_send_document, XmxSensitive);
    }
  else
    {
      /* If we don't have an active outport, then we can't send a document
         but we can open an outport. */
      XmxRSetSensitive 
        (win->menubar, mo_dtm_open_outport, XmxSensitive);
      XmxRSetSensitive
        (win->menubar, mo_dtm_send_document, XmxNotSensitive);
    }

  return mo_succeed;
}
#endif


/****************************************************************************
 * name:    mo_redisplay_window
 * purpose: Cause the current window's HTML widget to be refreshed.
 *          This causes the anchors to be reexamined for visited status.
 * inputs:  
 *   - mo_window *win: Current window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   
 ****************************************************************************/
mo_status mo_redisplay_window (mo_window *win)
{
  char *curl = cached_url;
  cached_url = win->cached_url;

  HTMLRetestAnchors (win->scrolled_win, anchor_visited_predicate);

  cached_url = curl;

  return mo_succeed;
}


/* ---------------------- mo_set_current_cached_win ----------------------- */

mo_status mo_set_current_cached_win (mo_window *win)
{
  current_win = win;
  view = win->view;

  return mo_succeed;
}


static connect_interrupt = 0;
extern int sleep_interrupt;

XmxCallback (icon_pressed_cb)
{
  sleep_interrupt = connect_interrupt = 1;
  if (cci_event) MoCCISendEventOutput(MOSAIC_GLOBE);
}


static XmxCallback (security_pressed_cb) {

mo_window *win = current_win;
char buf[BUFSIZ];

	if (!win || !win->current_node || !win->current_node) {
		return;
	}

	if (cci_event)MoCCISendEventOutput(AUTHENTICATION_BUTTON);

	mo_gui_check_security_icon(win->current_node->authType);

	switch(win->current_node->authType) {
		case HTAA_NONE:
			strcpy(buf,"There is no authentication for this URL." );
			break;
		case HTAA_BASIC:
			strcpy(buf,"The authentication method for this URL is\nBasic (uuencode/uudecode)." );
			break;
		case HTAA_KERBEROS_V4:
			strcpy(buf,"The authentication method for this URL is\nKerberos v4." );
			break;
		case HTAA_KERBEROS_V5:
			strcpy(buf,"The authentication method for this URL is\nKerberos v5." );
			break;
		case HTAA_MD5:
			strcpy(buf,"The authentication method for this URL is\nMD5." );
			break;
		case HTAA_DOMAIN:
			strcpy(buf,"This URL is Domain Restricted." );
			break;
		case HTAA_LOGIN:
			strcpy(buf,"This FTP URL is authenticated through logging into the\nFTP server machine." );
			break;
		case HTAA_UNKNOWN:
		default:
			strcpy(buf,"The authentication method for this URL is unknown.\nA default of no authentication was used, which was okayed\nby the server." );
			break;
	}

	application_user_info_wait(buf);

	return;
}


/* ----------------------- editable URL field callback -------------------- */
/* If the user hits return in the URL text field at the top of the display, */
/* then go fetch that URL  -- amb                                           */

static XmxCallback (url_field_cb)
{
  mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  char *url,*xurl;

  /* this won't have any effect if the mouse is in the url field,
     it only is useful when the user tyeps z, or C-[hfn] to warp
     input focus to the url field */
/*
  XtSetKeyboardFocus(XtParent(win->scrolled_win), win->view);
*/

  if (cci_event) MoCCISendEventOutput(MOSAIC_URL_TEXT_FIELD);

  url = XmxTextGetString (win->url_text);
  if (!url || (!strlen(url)))
    return;
  mo_convert_newlines_to_spaces (url);
  xurl=mo_url_prepend_protocol(url);
  mo_load_window_text (win, xurl, NULL);

  if (xurl==url) {
	free(xurl);
  }
  else {
	free(xurl);
	free(url);
  }

  if (cci_event) MoCCISendEventOutput(LINK_LOADED);
  
  return;
}

/****************************************************************************
 * name:    anchor_cb
 * purpose: Callback for triggering anchor in HTML widget.
 * inputs:  
 *   - as per XmxCallback
 * returns: 
 *   nothing
 * remarks: 
 *   This is too complex and should be broken down.
 *   We look at the button event passed in through the callback;
 *   button1 == same window, button2 == new window.
 *   Call mo_open_another_window or mo_load_window_text to get
 *   the actual work done.
 ****************************************************************************/
static XmxCallback (anchor_cb)
{
  char *href, *reftext;
/*  char *access;*/
  static char *referer = NULL;
  mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  XButtonReleasedEvent *event = 
    (XButtonReleasedEvent *)(((WbAnchorCallbackData *)call_data)->event);
  int force_newwin = (event->button == Button2 ? 1 : 0);
  int old_binx_flag;

  if (!win)
    return;

  if (cci_event) MoCCISendEventOutput(MOSAIC_URL_TRIGGER);

  /* if shift was down, make this a Load to Local Disk -- amb */
  old_binx_flag = win->binary_transfer;
  if ( (event->state & ShiftMask) == ShiftMask)
    win->binary_transfer = 1;

  if (get_pref_boolean(eKIOSK) || 
      get_pref_boolean(eKIOSKNOEXIT) || 
      get_pref_boolean(eDISABLEMIDDLEBUTTON)) { 
    /* disable new window if in kiosk mode*/
    force_newwin = 0;
    /* disable load to disk in kiosk */
    win->binary_transfer = 0;
    }

  if (get_pref_boolean(ePROTECT_ME_FROM_MYSELF))
    {
      int answer = XmxModalYesOrNo 
        (win->base, app_context,
	 "BEWARE: Despite our best and most strenuous intentions to the contrary,\nabsolutely anything could be on the other end of this hyperlink,\nincluding -- quite possibly -- pornography, or even nudity.\n\nNCSA disclaims all responsibility regarding your emotional and mental health\nand specifically all responsibility for effects of viewing salacious material via Mosaic.\n\nWith that in mind, are you *sure* you want to follow this hyperlink???" , "Yup, I'm sure, really." ,
         "Ack, no!  Get me outta here." );
      if (!answer)
        return;
    }

  /* amb */
  if (referer!=NULL)
    {
      free(referer);
      referer=NULL;
    }

  /*SWP*/
  if (!strncasecmp(win->current_node->url, "http://", 7))
    {
      /* what if hostname is a partial local? */
      referer = strdup (win->current_node->url);
      HTReferer = referer;
    }
  else
    HTReferer = NULL;
  
  if (((WbAnchorCallbackData *)call_data)->href)
    href = strdup (((WbAnchorCallbackData *)call_data)->href);
  else
    href = strdup ("Unlinked");
  if (((WbAnchorCallbackData *)call_data)->text)
    reftext = strdup (((WbAnchorCallbackData *)call_data)->text);
  else
    reftext = strdup ("Untitled");

  mo_convert_newlines_to_spaces (href);

  if (!force_newwin)
    mo_load_window_text (win, href, reftext);
  else
    {
      char *target = mo_url_extract_anchor (href);
      char *url = 
        mo_url_canonicalize_keep_anchor (href, win->current_node->url);
      /* @@@ should we be keeping the anchor here??? */
      if (strncmp (url, "telnet:", 7) && strncmp (url, "tn3270:", 7) &&
          strncmp (url, "rlogin:", 7))
        {
          /* Not a telnet anchor. */

          /* Open the window (generating a new cached_url). */
          mo_open_another_window (win, url, reftext, target);

          /* Now redisplay this window. */
          mo_redisplay_window (win);
        }
      else
        /* Just do mo_load_window_text go get the xterm forked off. */
        mo_load_window_text (win, url, reftext);
    }

  if (cci_event) MoCCISendEventOutput(LINK_LOADED);

  win->binary_transfer = old_binx_flag;
  free (href);
  return;
}


/****************************************************************************
 * name:    anchor_visited_predicate (PRIVATE)
 * purpose: Called by the HTML widget to determine whether a given URL
 *          has been previously visited.
 * inputs:  
 *   - Widget   w: HTML widget that called this routine.
 *   - char *href: URL to test.
 * returns: 
 *   1 if href has been visited previously; 0 otherwise.
 * remarks: 
 *   All this does is canonicalize the URL and call
 *   mo_been_here_before_huh_dad() to figure out if we've been
 *   there before.
 ****************************************************************************/
int anchor_visited_predicate (Widget w, char *href)
{
  int rv;

  if (!get_pref_boolean(eTRACK_VISITED_ANCHORS) || !href)
    return 0;

  /* This doesn't do special things for data elements inside
     an HDF file, because it's faster this way. */
  href = mo_url_canonicalize (href, cached_url);

  rv = (mo_been_here_before_huh_dad (href) == mo_succeed ? 1 : 0);

  free (href);
  return rv;
}

static void pointer_motion_callback (Widget w, char *href)
{
  mo_window *win = NULL;
  XmString xmstr;
  char *to_free = NULL, *to_free_2 = NULL;

  if (!get_pref_boolean(eTRACK_POINTER_MOTION))
    return;
  
  while (win = mo_next_window (win))
    if (win->scrolled_win == w)
      break;
  if (win == NULL)
    return;
  
  if (href && *href) {
      href = mo_url_canonicalize_keep_anchor (href, win->cached_url);
      to_free = href;

      /* Sigh... */
      mo_convert_newlines_to_spaces (href);

      /* This is now the option wherein the URLs are just spit up there;
         else we put up something more friendly. */
      if (get_pref_boolean(eTRACK_FULL_URL_NAMES)) {
          /* Everything already done... */
      } else {
          /* This is where we go get a good description. */
          href = HTDescribeURL (href);
          to_free_2 = href;
      }
  } else 
    href = " ";

  xmstr = XmStringCreateSimple (href);
  XtVaSetValues
    (win->tracker_label,
     XmNlabelString, (XtArgVal)xmstr,
     NULL);
  XmStringFree (xmstr);

  if (to_free)
    free (to_free);
  if (to_free_2)
    free (to_free_2);
  
  return;
}


XmxCallback (submit_form_callback)
{
  mo_window *win = NULL;
  char *url = NULL, *method = NULL, *enctype = NULL, *query;
  int len, i;
  WbFormCallbackData *cbdata = (WbFormCallbackData *)call_data;
  int do_post_urlencoded = 0;
  int plaintext=0;
  char *entity=NULL;

  if (!cbdata)
    return;

  while (win = mo_next_window (win))
    if (win->scrolled_win == w)
      goto foundit;
  
  /* Shit outta luck. */
  return;
  
 foundit:

  mo_busy ();

  if (cci_event) MoCCISendEventOutput(FORM_SUBMIT);

  /* Initial query: Breathing space. */
  len = 16;

  /* Add up lengths of strings. */
  for (i = 0; i < cbdata->attribute_count; i++)
    {
      if (cbdata->attribute_names[i])
        {
          len += strlen (cbdata->attribute_names[i]) * 3;
          if (cbdata->attribute_values[i])
            len += strlen (cbdata->attribute_values[i]) * 3;
        }
      len += 2;
    }

  /* Get the URL. */
  if (cbdata->href && *(cbdata->href))
    url = cbdata->href;
  else
    url = win->current_node->url;

  if (cbdata->method && *(cbdata->method))
    method = cbdata->method;
  else
    method = strdup ("GET");

  /* Grab enctype if it's there. */
  if (cbdata->enctype && *(cbdata->enctype))
    enctype = cbdata->enctype;
  /* Grab encentity if it's there and we have an enctype. */
  if (enctype && cbdata->enc_entity && *(cbdata->enc_entity))
    entity = cbdata->enc_entity;

#ifndef DISABLE_TRACE
  if (srcTrace) {
	fprintf (stderr, "[submit_form_callback] method is '%s'\n",
		 method);
	fprintf (stderr, "[submit_form_callback] enctype is '%s'\n",
		 enctype);
  }
#endif

  if ((strcasecmp (method, "POST") == 0) ||
 	(strcasecmp (method, "cciPOST") == 0))
    do_post_urlencoded = 1;

  len += strlen (url);

  query = (char *)malloc (sizeof (char) * len);

  if (!do_post_urlencoded)
    {
	strcpy (query, url);
	/* Clip out anchor. */
	strtok (query, "#");
	/* Clip out old query. */
	strtok (query, "?");
	if (query[strlen(query)-1] != '?')
	{
		strcat (query, "?");
	}
	plaintext=0;
    }
  else if (cbdata->format && *cbdata->format && !strcasecmp(cbdata->format,"PLAIN")) {
	/* Get ready for cats below. */
	query[0] = 0;
	plaintext=1;
    }
  else
    {
	/* Get ready for cats below. */
	query[0] = 0;
	plaintext=0;
    }

  /* Take isindex possibility into account. */
  if (cbdata->attribute_count == 1 &&
      strcmp (cbdata->attribute_names[0], "isindex") == 0)
    {
      if (cbdata->attribute_values[0])
        {
          char *c = mo_escape_part (cbdata->attribute_values[0]);
          strcat (query, c);
          free (c);
        }
    }
  else
    {
      for (i = 0; i < cbdata->attribute_count; i++)
        {
          /* For all but the first, lead off with an ampersand. */
          if (i != 0)
            strcat (query, "&");
          /* Rack 'em up. */
          if (cbdata->attribute_names[i])
            {
              char *c = mo_escape_part (cbdata->attribute_names[i]);
              strcat (query, c);
              free (c);
              
              strcat (query, "=");
              
              if (cbdata->attribute_values[i])
                {
                  char *c = mo_escape_part (cbdata->attribute_values[i]);
                  strcat (query, c);
                  free (c);
                }
            }
        }
    }

  if (do_post_urlencoded)
    {
	if (!strcasecmp(method,"cciPOST"))
		MoCCIFormToClient(NULL, NULL, NULL,NULL,1);

      	mo_post_access_document (win, url, 
				 (plaintext?"text/plain":
				  "application/x-www-form-urlencoded"),
				 query);
    }
  else
    {
      mo_access_document (win, query);
    }
  
  if (query) free (query);

  return;
}


/* This only gets called by the widget in the middle of set_text. */
static XmxCallback (link_callback)
{
  mo_window *win = current_win;
  LinkInfo *linfo = (LinkInfo *)call_data;
  extern char *url_base_override;

  if (!linfo)
    return;

  /* Free -nothing- in linfo. */
  if (linfo->href)
    {
      url_base_override = strdup (linfo->href);

      /* Set the URL cache variables to the correct values NOW. */
      cached_url = mo_url_canonicalize (url_base_override, "");
      win->cached_url = cached_url;
    }
  if (linfo->role)
    {
      /* Do nothing with the role crap yet. */
    }

  return;
}


/* Exported to libwww2. */
void mo_gui_notify_progress (char *msg)
{
  XmString xmstr;
  mo_window *win = current_win;

  if (!get_pref_boolean(eTRACK_POINTER_MOTION))
    return;

  if (!msg)
    msg = " ";

  xmstr = XmStringCreateSimple (msg);
  XtVaSetValues
    (win->tracker_label,
     XmNlabelString, (XtArgVal)xmstr,
     NULL);
  XmStringFree (xmstr);

  XmUpdateDisplay (win->base);

  return;
}


void UpdateButtons (Widget w)
{
  XEvent event;
  Display * display = XtDisplay(w);
  
  XSync (display, 0);
  
  while (XCheckMaskEvent(display, (ButtonPressMask|ButtonReleaseMask),
                         &event))
    {
      XButtonEvent *bevent = &(event.xbutton);
      if (bevent->window == XtWindow (current_win->logo))
        {
          XtDispatchEvent(&event);
        }
      /* else just throw it away... users shouldn't be pressing buttons
         in the middle of transfers anyway... */
    }
}


/*SWP 7/3/95*/
void mo_gui_check_security_icon_in_win(int type, mo_window *win) {

int ret;
Pixmap pix;
static int current=HTAA_NONE;

	if (!get_pref_boolean(eSECURITYICON)) {
		return;
	}

	switch (type) {
		case HTAA_UNKNOWN:
			pix=securityUnknown;
			current=type;
			break;

		case HTAA_NONE:
			pix=securityNone;
			current=type;
			break;

		case HTAA_KERBEROS_V4:
			pix=securityKerberos4;
			current=type;
			break;

		case HTAA_KERBEROS_V5:
			pix=securityKerberos5;
			current=type;
			break;
		case HTAA_MD5:
			pix=securityMd5;
			current=type;
			break;
		case HTAA_BASIC:
			pix=securityBasic;
			current=type;
			break;

		case HTAA_DOMAIN:
			pix=securityDomain;
			current=type;
			break;

		case HTAA_LOGIN:
			pix=securityLogin;
			current=type;
			break;

		default:
			pix=securityUnknown;
			current=type;
			break;
	}

	if ((char *)pix != NULL) {
		DrawSecurityPixmap (win->security, pix);
	}
  
	UpdateButtons (win->base);
	XmUpdateDisplay (win->base);

	return;
}


void mo_gui_check_security_icon(int type) {

mo_window *win = current_win;

	if (get_pref_boolean(eSECURITYICON)) {
		mo_gui_check_security_icon_in_win(type,win);
	}

	return;
}


static int logo_count = 0;
static int logo_save = 0;

int mo_gui_check_icon (int twirl)
{
  mo_window *win = current_win;
  int ret;
  static int cnt=0;

  if (twirl!=(-1) && twirl>0) {
	if (!makeBusy) {
		cursorAnimCnt=(-1);
		makeBusy=1;
	}

	cnt++;
	if (cnt==2) {
		animateCursor();
		cnt=0;
	}
  }

  if (twirl>0) {
	if (get_pref_boolean(eTWIRLING_TRANSFER_ICON)) {
		if ((char *)IconPix[logo_count] != NULL) {
			AnimatePixmapInWidget(win->logo, IconPix[logo_count]);
		}
		logo_count++;
		if (logo_count >= get_pref_int(ePIX_COUNT))
            logo_count = 0;
	}
  }
  
  UpdateButtons (win->base);
  XmUpdateDisplay (win->base);

  ret = connect_interrupt;
  connect_interrupt = 0;

  return(ret);
}


void mo_gui_clear_icon (void)
{
  connect_interrupt = 0;
}

void mo_gui_done_with_icon (void)
{
  mo_window *win = current_win;
  extern void ungrab_the_____ing_pointer();

	XClearArea(XtDisplay(win->logo), XtWindow(win->logo), 0, 0, 0, 0, True);
	makeBusy = 0;
	animateCursor();
	logo_count = 0;
	HTMLSetAppSensitive((Widget) win->scrolled_win);
	/* this works dammit (trust me) - TPR */
	XtAppAddTimeOut(app_context, 10,
                    (XtTimerCallbackProc)ungrab_the_____ing_pointer, NULL);
}


/****************************************************************************
 * name:    mo_view_keypress_handler (PRIVATE)
 * purpose: This is the event handler for the HTML widget and associated
 *          scrolled window; it handles keypress events and enables the
 *          hotkey support.
 * inputs:  
 *   - as per XmxEventHandler
 * returns: 
 *   nothing
 * remarks: 
 *   Hotkeys and their actions are currently hardcoded.  This is probably
 *   a bad idea, and Eric hates it.
 ****************************************************************************/
static XmxEventHandler (mo_view_keypress_handler)
{
/*  char url[128]; /* buffer for news io */
  mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  int _bufsize = 3, _count;
  char _buffer[3];
  KeySym _key;
  XComposeStatus _cs;
  Widget sb;
  String params[1];

  if (!win)
    return;

  /* Go get ascii translation. */
  _count = XLookupString (&(event->xkey), _buffer, _bufsize, 
                          &_key, &_cs);

  /* I don't know why this is necessary but for some reason the rbm was making
     the above function return 0 as the _key, this fixes it -- TPR */
  if(!_key)
    _key = XKeycodeToKeysym(XtDisplay(win->view), event->xkey.keycode, 0);
  
  /* Insert trailing Nil. */
  _buffer[_count] = '\0';

  params[0] = "0";
  
  switch(_key){
  case XK_Prior: /* Page up. */
      if(!get_pref_boolean(eCATCH_PRIOR_AND_NEXT)) break;
  case XK_KP_Prior:
  case XK_BackSpace:
  case XK_Delete:
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)) {
          XtCallActionProc (sb, "PageUpOrLeft", event, params, 1);
      }
      break;

  case XK_Next:  /* Page down. */
      if(!get_pref_boolean(eCATCH_PRIOR_AND_NEXT)) break;
  case XK_KP_Next:
  case XK_Return:
  case XK_space:
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)) {
          XtCallActionProc (sb, "PageDownOrRight", event, params, 1);
      }
      break;

/*
  case XK_Tab:
      HTMLTraverseTabGroups(win->scrolled_win, XmTRAVERSE_NEXT_TAB_GROUP);
      break;
*/
      
  case XK_Home: /* Home -- Top */
      HTMLGotoId(win->scrolled_win, 0);
      break;

  case XK_End: /* End -- Bottom */
      HTMLGotoId(win->scrolled_win, HTMLLastId(win->scrolled_win));
      break;

  case XK_Down:
  case XK_KP_Down:
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)) {
          XtCallActionProc (sb, "IncrementDownOrRight", event, params, 1);
      }
      break;

  case XK_Right:
  case XK_KP_Right:
      params[0] = "1";
      XtVaGetValues (win->scrolled_win, XmNhorizontalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)) {
          XtCallActionProc (sb, "IncrementDownOrRight", event, params, 1);
      }
      break;

  case XK_Up:
  case XK_KP_Up:
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)){
          XtCallActionProc (sb, "IncrementUpOrLeft", event, params, 1);
      }
      break;

  case XK_Left:
  case XK_KP_Left:
      params[0] = "1";
      
      XtVaGetValues (win->scrolled_win, XmNhorizontalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb)) {
          XtCallActionProc (sb, "IncrementUpOrLeft", event, params, 1);
      }
      break;
  }
  

  if (!(get_pref_boolean(eKIOSK) || get_pref_boolean(eKIOSKNOEXIT))) {
      switch(_key){
              /* News Hotkeys ...
                 < > = prev/next thread  , . = prev/next message */
      case XK_less:
          gui_news_prevt(win);
          break;

      case XK_greater:
          gui_news_nextt(win);
          break;

      case XK_comma:
          gui_news_prev(win);

      case XK_period:
          gui_news_next(win);
          break;

      case XK_A: /* Annotate. */
      case XK_a: 
          mo_post_annotate_win (win, 0, 0, NULL, NULL, NULL, NULL);
          break;
          
      case XK_B: /* Back. */
      case XK_b:
          mo_back_node (win);
          break;

      case XK_C: /* Clone */
      case XK_c:
          mo_duplicate_window (win);
          break;

      case XK_D: /* Document source. */
      case XK_d:
          mo_post_source_window (win);
          break;

      case XK_E: /* Edit */
      case XK_e:
          mo_edit_source (win);
          break;

      case XK_F:
      case XK_f:
          mo_forward_node (win);
          break;
  
      case XK_H: /* Hotlist */
          mo_post_hotlist_win (win);
          break;

      case XK_h: /* History */
          mo_post_history_win (win);
          break;

      case XK_L: /* Open Local */
      case XK_l:
          mo_post_open_local_window (win);
          break;
  
      case XK_M: /* Mailto */
      case XK_m:
          mo_post_mail_window (win);
          break;

      case XK_N: /* New */
      case XK_n:
          mo_open_another_window (win, home_document, NULL, NULL);
          break;
  
      case XK_O: /* Open */
      case XK_o:
          mo_post_open_window (win);
          break;

      case XK_P: /* Print */
      case XK_p:
          mo_post_print_window (win);
          break;

      case XK_r: /* reload */
          mo_reload_window_text (win, 0);
          break;
   
      case XK_R: /* Refresh */
          mo_refresh_window_text (win);
          break;
          
      case XK_S: /* Search. */
      case XK_s:
          mo_post_search_window (win);
          break;
          
	/* Tag 'n Bag */
              /*
      case XK_T:
      case XK_t:
	  mo_tagnbag_url (win);
          break;
          */

	/* the z key (warps cursor to url bar) */
/*
      case XK_Z:
      case XK_z:
	    if(XtIsManaged(win->slab[SLAB_URL]))
	      {
		char *str;

		XtSetKeyboardFocus(XtParent(win->slab[SLAB_URL]), 
				   win->url_text);

		str = XmTextFieldGetString(win->url_text);
		XmTextFieldSetSelection(win->url_text, 0, strlen(str), 
					event->xkey.time);
		XtFree(str);
	      }
              break;
*/

      case XK_Escape:
          if (!get_pref_boolean(eKIOSKNOEXIT)) {
              mo_delete_window (win);
          }
          break;
      default:
          break;
      }
  }
  
  
  return;
}


/* Callback to redraw the meter -bjs */
static void ResizeMeter(Widget meter, XtPointer client, XtPointer call);


static void DrawMeter(Widget meter, XtPointer client, XtPointer call)
{
    mo_window *win = (mo_window *) client;
    GC gc;
    long mask = 0;
    XGCValues values;
    int l;
    char *ss;
    char s[256];
    static int last_len=0;
    int current_len;
    int resize=0;
    static char *finished="100%";

    if (!get_pref_boolean(eMETER)) {
	return;
    }

    gc = XtGetGC( meter,0,NULL);

    if(win->meter_width== -1) {
	resize=1;
	ResizeMeter(meter,(XtPointer)win,NULL);
    }

    if(win->meter_width== -1) ResizeMeter(meter,(XtPointer)win,NULL);
    current_len=(win->meter_width * win->meter_level)/100;

    XSetForeground( XtDisplay(meter),gc,win->meter_bg);
    XFillRectangle(XtDisplay(meter),XtWindow(meter),gc,
		   0,0,win->meter_width,win->meter_height);

    XSetForeground( XtDisplay(meter),gc,win->meter_fg);
    XFillRectangle(XtDisplay(meter),XtWindow(meter),gc,
		   0,0,current_len,
                   win->meter_height);

    if (win->meter_level==100 && !win->meter_text) {
	win->meter_text=finished;
    }

    if(!win->meter_notext && (win->meter_level<=100 || win->meter_text)){

	if (!win->meter_font) {
		XtVaGetValues(win->scrolled_win,
			      WbNmeterFont,
			      &(win->meter_font),
			      NULL);
		if (!win->meter_font) {
/*
 = XLoadQueryFont(XtDisplay(win->base),ftext))) {
*/
			puts("METER Cannot Get Font -- Please set 'Mosaic*MeterFont: NEW_FONT'\n  Where NEW_FONT is a 14 point font on your system.");
			win->meter_notext=1;
		}
		else {
			win->meter_notext=0;
			win->meter_fontW = win->meter_font->max_bounds.rbearing;
			win->meter_fontH = win->meter_font->max_bounds.ascent;

/* +			win->meter_font->max_bounds.descent;*/
		}
	}

	if (!win->meter_notext) {
		if(!win->meter_text) {
			l = 3;
			ss = s;
			s[0]=win->meter_level>9 ? '0'+(win->meter_level/10) : ' ';
			s[1]='0'+(win->meter_level%10);
			s[2]='%';
			s[3]=0;
		} else {
			ss = win->meter_text;
			l = strlen(ss);
		}

		values.font = win->meter_font->fid;
/*
		values.function = GXxor;
		values.background = win->meter_bg;
		mask |= GCFont | GCFunction | GCBackground;
*/
		mask = GCFont;
		XChangeGC(XtDisplay(meter), gc,  mask, &values);
		XSetForeground( XtDisplay(meter),gc,win->meter_font_fg);
		XDrawString(XtDisplay(meter), XtWindow(meter), gc, 
			    win->meter_width/2-(win->meter_fontW*l)/2,
			    ((win->meter_height/2)+(win->meter_fontH/2)), 
			    ss, l);
	}
    }

    last_len=current_len;

    XtReleaseGC(meter,gc);
}

static void ResizeMeter(Widget meter, XtPointer client, XtPointer call)
{
    XWindowAttributes wattr;
    mo_window *win = (mo_window *) client;

    if (!get_pref_boolean(eMETER)) {
	return;
    }

    if(!XtWindow(meter)) return;
    
    XGetWindowAttributes(XtDisplay(meter),XtWindow(meter),&wattr);

    win->meter_width = wattr.width;
    win->meter_height = wattr.height;

}


/* Exported to libwww2 */
void mo_gui_update_meter(int level, char *text)
{

    if (!get_pref_boolean(eMETER)) {
	return;
    }

    current_win->meter_text = text;

    if(current_win->meter_level == -1) return;
    if(level<0) level = 0;
    if(level>100) level = 100;
    current_win->meter_level = level;
                
    DrawMeter(current_win->meter,(XtPointer) current_win, NULL);
}

char *slab_words[] =
{"MENU","TITLE","URL","TOOLS","STATUS","VIEW","GLOBE","SMALLGLOBE","TEXTTOOLS",NULL};

int sarg[7],scount=-1,smalllogo=0,stexttools=0;


/* take a text string of the form "MENU,URL,VIEW,STATUS" and set the
   layout slab stuff from it */

/* WARNING:  The code to draw the interface expects the rules enforced herein
   to be followed... just taking out the safety checks here could cause some
   major headaches. BJS */
int parse_slabinfo(char *s)
{
    int k,j,i,done;
    char *p;

    for(p=s,i=0,done=0;!done;p++){
        if(!*p) done = 1;
        
        if(!*p || (*p==',')){
            *p=0;

            if(i==7){
                fprintf(stderr,"layout: too many slabs\n");
                return 0;
            }
            for(j=0;slab_words[j];j++){
                if((strlen(slab_words[j])==strlen(s)) &&
                   !strcmp(slab_words[j],s)){
                    if(j==SLAB_TEXTTOOLS){
                        j = SLAB_TOOLS;
                        stexttools=1;
                    }
                    if(j==SLAB_SMALLGLOBE) {
                        j = SLAB_GLOBE;
                        smalllogo = 1;
                    }
                    sarg[i++] = j;
                    goto next1;
                }
            }
            fprintf(stderr,"layout: bad slab name \"%s\"\n",s);
            return 0;
            
          next1:          
            s = p+1;
            continue;
        }
        if(isalpha(*p)){
            *p = toupper(*p);
        } else {
            fprintf(stderr,"layout: bad character '%c'\n",*p);
            return 0;
        }
    }

        /* do some idiot-proofing */
    for(done=0,j=0;j<i;j++){
        if(sarg[j] == SLAB_VIEW) done = 1;
        if(sarg[j] == SLAB_GLOBE) {
            if(smalllogo){
                if(j+1 >= i){
                    fprintf(stderr,"layout: SMALLGLOBE requires one normal slab\n");
                    return 0;
                }
                if(sarg[j+1]==SLAB_VIEW || sarg[j+1]==SLAB_TOOLS){
                    fprintf(stderr,"layout: SMALLGLOBE may not be next to %s\n",
                            slab_words[sarg[j+1]]);
                    return 0;
                }
            } else {
                if(j+2 >= i){
                    fprintf(stderr,"layout: GLOBE requires two normal slabs\n");
                    return 0;
                }
                if((sarg[j+1]==SLAB_VIEW)||(sarg[j+2]==SLAB_VIEW)){
                    fprintf(stderr,"layout: GLOBE requires two normal slabs\n");
                    return 0;
                }
            }
            
        }
    }
    if(!done){
        fprintf(stderr,"layout: one VIEW slab required\n");
        return 0;
    }

        /* check for duplicate slabs */
    for(j=0;j<i;j++){
        for(k=0;k<i;k++){
            if((k!=j) && (sarg[j]==sarg[k])){
                fprintf(stderr,"layout: only one %s slab allowed\n",
                        slab_words[j]);
                return 0;
            }
        }
    }

        /* whew. made it. */
    scount = i;
    return 1;
}

    

struct tool mo_tools[] = { 
    {"<-","Back","Previous page",mo_back,&toolbarBack, &toolbarBackGRAY, moMODE_ALL, 1, NULL},
    {"->","Forward","Next page",mo_forward,&toolbarForward, &toolbarForwardGRAY, moMODE_ALL, 1, NULL},
    {"Rel","Reload","Reload this page",mo_reload_document,&toolbarReload,NULL, moMODE_ALL, 0, NULL},
    {"Home","Home","Go Home!",mo_home_document,&toolbarHome,NULL, moMODE_ALL, 1, NULL},
    {"Open","Open","Open a new URL",mo_open_document,&toolbarOpen,NULL, moMODE_ALL, 0, NULL},
    {"Save","Save","Save current page as ...",mo_save_document,&toolbarSave,NULL, moMODE_ALL, 0, NULL},
    {"New","New","Create a new Mosaic window",mo_new_window,&toolbarNew,NULL, moMODE_ALL, 0, NULL},
    {"Clone","Clone","Clone this Mosaic window",mo_clone_window,&toolbarClone,NULL, moMODE_ALL, 0, NULL},
    {"Close","Close","Destroy this Mosaic window",mo_close_window,&toolbarClose,NULL, moMODE_ALL, 0, NULL},
    {"+ Hot","Add To Hotlist","Add current page to hotlist",mo_register_node_in_default_hotlist,&toolbarAddHotlist,NULL, moMODE_PLAIN, 0, NULL},
    {"Find","Find","Search this document", mo_search, &toolbarSearch, NULL, moMODE_ALL, 0, NULL},
    {"Prt","Print","Print this document", mo_print_document, &toolbarPrint, NULL, moMODE_ALL, 0, NULL},
    {"Grps","Groups","Newsgroups index",mo_news_groups,&toolbarNewsGroups, NULL, moMODE_ALL, 0, NULL},
/* News Mode */
    {"Idx","Index","Newsgroup article index",mo_news_index,&toolbarNewsIndex, NULL, moMODE_NEWS, 1, NULL},
    {"<Thr","< Thread","Go to previous thread",mo_news_prevt,&toolbarNewsFRev, &toolbarNewsFRevGRAY, moMODE_NEWS, 1, NULL},
    {"<Art","< Article","Go to previous article",mo_news_prev,&toolbarNewsRev, &toolbarNewsRevGRAY, moMODE_NEWS, 1, NULL},
    {"Art>","Article >","Go to next article",mo_news_next,&toolbarNewsFwd, &toolbarNewsFwdGRAY, moMODE_NEWS, 1, NULL},
    {"Thr>","Thread >","Go to next thread",mo_news_nextt,&toolbarNewsFFwd, &toolbarNewsFFwdGRAY, moMODE_NEWS, 1, NULL},
    {"Post","Post","Post a UseNet Article",mo_news_post,&toolbarPost, &toolbarPostGRAY, moMODE_NEWS, 1, NULL},
    {"Foll","Followup","Follow-up to UseNet Article",mo_news_follow,&toolbarFollow, &toolbarFollowGRAY, moMODE_NEWS, 1, NULL},
/* FTP Mode */
    {"Put","Put","Send file to remote host",mo_ftp_put,&toolbarFTPput, NULL, moMODE_FTP, 1, NULL},
    {"Mkdir","Mkdir","Make remote directory",mo_ftp_mkdir,&toolbarFTPmkdir, NULL, moMODE_FTP, 1, NULL},
    {NULL, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}
};

/* NOTE: THESE MUST COINCIDE EXACTLY WITH mo_tools!!! */
char *tool_names[] = {
	"BACK",
	"FORWARD",
	"RELOAD",
	"HOME",
	"OPEN",
	"SAVE",
	"NEW",
	"CLONE",
	"CLOSE",
	"ADD_TO_HOTLIST",
	"FIND",
	"PRINT",
	"GROUPS",
	"INDEX",
	"PREVIOUS_THREAD",
	"PREVIOUS_ARTICLE",
	"NEXT_ARTICLE",
	"NEXT_THREAD",
	"POST",
	"FOLLOW_UP",
	"PUT",
	"MKDIR",
	NULL
};
int use_tool[BTN_COUNT];


void mo_get_tools_from_res() {

int i;
char *tools,*ptr,*start,*end;

	if (!(ptr=get_pref_string(eTOOLBAR_LAYOUT))) {
		ptr="BACK,FORWARD,RELOAD,HOME,OPEN,SAVE,CLONE,CLOSE,FIND,PRINT,GROUPS,INDEX,PREVIOUS_THREAD,PREVIOUS_ARTICLE,NEXT_ARTICLE,NEXT_THREAD,POST,FOLLOW_UP,PUT,MKDIR";
	}
	tools=strdup(ptr);

	for (i=0; tool_names[i]; i++) {
		use_tool[i]=0;
	}

	for (start=tools; start && *start; ) {
		ptr=start;
		for (; *ptr && isspace(*ptr); ptr++);
		if (*ptr==',') {
			ptr++;
		}
		end=strchr(ptr,',');
		if (end) {
			start=end+1;
			*end='\0';
		}
		else {
			start=NULL;
		}
		for (i=0; tool_names[i]; i++) {
			if (!strncmp(tool_names[i],ptr,strlen(tool_names[i]))) {
				use_tool[i]=1;
			}
		}
	}

	free(tools);

	return;
}


void mo_make_globe(mo_window *win, Widget parent, int small);

void mo_tool_detach_cb(Widget wx, XtPointer cli, XtPointer call)
{
    Atom WM_DELETE_WINDOW;
    mo_window *win = (mo_window *) cli;
    int h,w;

        /* Xmx sucks */
    XmxSetUniqid(win->id);

    XtUnmanageChild(XtParent(win->scrolled_win));
    
    if(win->toolbardetached){
        win->toolbardetached = 0;
        XtUnmanageChild(win->toolbarwin);
        XtDestroyWidget(win->toolbarwin);
        win->toolbarwin = NULL;
        win->topform = win->slab[SLAB_TOOLS];
        mo_fill_toolbar(win,win->topform,0,0);        
        if(win->biglogo && !win->smalllogo){
            mo_make_globe(win, win->slab[SLAB_GLOBE], 0);
        }
    } else {
        win->toolbardetached = 1;
        XtUnmanageChild(win->button_rc);
        XtUnmanageChild(win->button2_rc);
        
        XtDestroyWidget(win->button_rc);
        XtDestroyWidget(win->button2_rc);

        if(win->biglogo && !win->smalllogo){
            XtUnmanageChild(win->logo);
            XtDestroyWidget(win->logo);
            XtUnmanageChild(win->security);
            XtDestroyWidget(win->security);
            XtUnmanageChild(win->encrypt);
            XtDestroyWidget(win->encrypt);            
        }
        win->toolbarwin = XtVaCreatePopupShell
            ("ToolBox",
/*             topLevelShellWidgetClass,*/
             xmDialogShellWidgetClass,
             win->base,
             XmNminHeight, h = win->toolbarorientation?640:40,
             XmNminWidth, w = win->toolbarorientation?40:640,
             XmNmaxHeight, h,
             XmNmaxWidth, w,
             XmNheight, h,
             XmNwidth, w,
             XmNallowShellResize, FALSE,
             NULL);
        XtManageChild(win->toolbarwin);
        win->topform = XtVaCreateWidget
            ("slab_tools",
             xmFormWidgetClass, win->toolbarwin, 
             XmNminHeight, h,
             XmNminWidth, w,
             XmNmaxHeight, h,
             XmNmaxWidth, w,
             XmNheight, h,
             XmNwidth, w,
             NULL);
        mo_fill_toolbar(win,win->topform,0,0);
        XtManageChild(win->topform);
        WM_DELETE_WINDOW = XmInternAtom(dsp, "WM_DELETE_WINDOW", False);
        XmAddWMProtocolCallback(win->toolbarwin, WM_DELETE_WINDOW,
                                mo_tool_detach_cb, (XtPointer)win);
        
        XtPopup(win->toolbarwin, XtGrabNone);
    }    

    XtManageChild(XtParent(win->scrolled_win));

}

void mo_switch_mode(mo_window *win)
{
    int i;
    for(i=0;mo_tools[i].label;i++){
        if(use_tool[i] && win->tools[i].w){
            if(!(mo_tools[i].toolset & win->mode)) {
                if(XtIsManaged(win->tools[i].w))
                    XtUnmanageChild(win->tools[i].w);
            } else {   
                if(!XtIsManaged(win->tools[i].w))
                    XtManageChild(win->tools[i].w);
            }
        }
    }
}


    
mo_tool_state(struct toolbar *t,int state,int index)
{
    if (use_tool[index]) {
	XmxSetSensitive (t->w, t->gray = state);
    }
}


void mo_extra_buttons(mo_window *win, Widget top)
{
    win->security = XmxMakeNamedPushButton (top, NULL, "sec",
                                            security_pressed_cb, 0);
    XmxApplyPixmapToLabelWidget (win->security, securityUnknown);

    XtVaSetValues(win->security,
                  XmNmarginWidth, 0,
                  XmNmarginHeight, 0,
                  XmNmarginTop, 0,
                  XmNmarginBottom, 0,
                  XmNmarginLeft, 0,
                  XmNmarginRight, 0,
                  XmNuserData, (XtPointer) "Security Stats Information",
                  NULL);
    
    XtOverrideTranslations(win->security,
                            XtParseTranslationTable(xlattab));
    
    win->encrypt = XtVaCreateManagedWidget
        (" ", xmPushButtonWidgetClass,
         top,
         XmNmarginWidth, 0,
         XmNmarginHeight, 0,
         XmNmarginTop, 0,
         XmNmarginBottom, 0,
         XmNmarginLeft, 0,
         XmNmarginRight, 0,
         XmNuserData, (XtPointer) "Encryption Status (not in this release)",
         NULL);

    XmxApplyPixmapToLabelWidget (win->encrypt, enc_not_secure);
    
    XtOverrideTranslations(win->encrypt,XtParseTranslationTable(xlattab));
    
            /* insure we set the security icon! */
    if (win->current_node) {
        mo_gui_check_security_icon_in_win(win->current_node->authType,win);
    }
    
}


void mo_make_globe(mo_window *win, Widget parent, int small)
{
    int tmp = 25;
    
    if(!small){
      IconPix = IconPixBig;
      IconWidth = IconHeight = 64;
      WindowWidth = WindowHeight = 0;
      logo_count = 0;
      set_pref(ePIX_COUNT, (void *)&logo_save);
    } else {
        IconPix = IconPixSmall;
        IconWidth = IconHeight = 32;
        logo_count = 0;
        set_pref(ePIX_COUNT, (void *)&tmp);
        WindowWidth = WindowHeight = 0;
    }

    win->logo = XmxMakeNamedPushButton
        (parent, NULL, "logo", icon_pressed_cb, 0);
    XmxApplyPixmapToLabelWidget(win->logo, IconPix[0]);
    XtVaSetValues(win->logo,
                  XmNmarginWidth, 0,
                  XmNmarginHeight, 0,
                  XmNmarginTop, 0,
                  XmNmarginBottom, 0,
                  XmNmarginLeft, 0,
                  XmNmarginRight, 0,
                  XmNtopAttachment, XmATTACH_FORM,
                  XmNbottomAttachment, XmATTACH_FORM,
                  XmNleftAttachment, !win->biglogo || (!win->smalllogo && win->toolbardetached) ? XmATTACH_FORM : XmATTACH_NONE,
                  XmNrightAttachment, XmATTACH_FORM,
                  NULL);

    if(win->biglogo){
        if(win->smalllogo){
            mo_extra_buttons(win,win->slab[SLAB_GLOBE]);
            XtVaSetValues(win->security,
                          XmNtopAttachment, XmATTACH_FORM,
                          XmNbottomAttachment, XmATTACH_FORM,
                          XmNleftAttachment, XmATTACH_FORM,
                          XmNrightAttachment, XmATTACH_NONE,
                          NULL);
            XtVaSetValues(win->encrypt,
                          XmNtopAttachment, XmATTACH_FORM,
                          XmNbottomAttachment, XmATTACH_FORM,
                          XmNleftAttachment, XmATTACH_WIDGET,
                          XmNleftWidget, win->security,
                          XmNrightAttachment, XmATTACH_WIDGET,
                          XmNrightWidget, win->logo,
                          NULL);
        } else {
            if(!win->toolbardetached){
                mo_extra_buttons(win,win->slab[SLAB_GLOBE]);
                XtVaSetValues(win->security,
                              XmNtopAttachment, XmATTACH_FORM,
                              XmNbottomAttachment, XmATTACH_NONE,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_WIDGET,
                              XmNrightWidget, win->logo,
                              NULL);
                XtVaSetValues(win->encrypt,
                              XmNtopAttachment, XmATTACH_WIDGET,
                              XmNtopWidget, win->security,
                              XmNbottomAttachment, XmATTACH_FORM,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_WIDGET,
                              XmNrightWidget, win->logo,
                              NULL);
            }           
        }
    }
    XtVaSetValues(win->logo,
                  XmNuserData, (XtPointer) "Logo Button - Abort a Transaction",
                  NULL);

    XtOverrideTranslations(win->logo,
                          XtParseTranslationTable(xlattab));
}

/* create topform and fill it with toolbar bits'n'pieces */
Widget mo_fill_toolbar(mo_window *win, Widget top, int w, int h)
{
    int tmp = 25;
    Widget rightform, tearbutton, btn;
    int i,vert = win->toolbarorientation && win->toolbardetached;
    int textbuttons = win->texttools;
    int long_text = get_pref_boolean(eUSE_LONG_TEXT_NAMES);
    static XFontStruct *tmpFont=NULL;
    static XmFontList tmpFontList;

    if (!tmpFont) {
	XtVaGetValues(win->scrolled_win,
		      WbNtoolbarFont,
		      &tmpFont,
		      NULL);
	if (!tmpFont) {
		fprintf(stderr,"Toolbar Font: Could not load! The X Resource is Mosaic*ToolbarFont\n  Default font is: -adobe-times-bold-r-normal-*-12-*-*-*-*-*-iso8859-1\n\Exiting Mosaic.");

		exit(1);
	}
	tmpFontList = XmFontListCreate(tmpFont,XmSTRING_DEFAULT_CHARSET);
    }

    /* Which tools to show */
    mo_get_tools_from_res();

    win->topform = top;

        /* Xmx sucks */
    XmxSetUniqid(win->id);

    win->button2_rc = XtVaCreateWidget
        ("buttonrc2", xmRowColumnWidgetClass,
         win->topform,
         XmNorientation, vert?XmVERTICAL:XmHORIZONTAL,
         XmNmarginWidth, 0,
         XmNmarginHeight, 0,
         XmNspacing, 0,
         XmNleftOffset, 0,
         XmNrightOffset, 0,
         XmNtopOffset, 0,
         XmNbottomOffset, 0,
         XmNleftAttachment, XmATTACH_NONE,
         XmNrightAttachment, XmATTACH_FORM,
         XmNtopAttachment, XmATTACH_FORM,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);
    

    win->button_rc = XtVaCreateWidget
        ("buttonrc", xmRowColumnWidgetClass,
         win->topform,
         XmNorientation, vert?XmVERTICAL:XmHORIZONTAL,
/*         XmNpacking, XmPACK_TIGHT,*/
         XmNpacking, XmPACK_TIGHT,
/*
         XmNmarginWidth, 1,
*/
         XmNmarginWidth, 0,

         XmNmarginHeight, 1,
         XmNspacing, 0,
         XmNleftOffset, 0,
         XmNrightOffset, 0,
         XmNtopOffset, 2,
         XmNbottomOffset, 2,
         XmNleftAttachment, XmATTACH_FORM,
         XmNrightAttachment, XmATTACH_WIDGET,
         XmNrightWidget, win->button2_rc,
         XmNtopAttachment, XmATTACH_FORM,
         XmNbottomAttachment, XmATTACH_FORM,
         NULL);

    tearbutton = XtVaCreateManagedWidget
        ("|",xmPushButtonWidgetClass,
         win->button_rc,
         XmNuserData, (XtPointer) "Toolbar Tearoff Control",
         XmNlabelType, textbuttons ? XmSTRING : XmPIXMAP,
         XmNlabelPixmap, vert?tearh:tearv,

	 XmNfontList, tmpFontList,

         NULL);
    
    XtOverrideTranslations(tearbutton,XtParseTranslationTable(xlattab));

    XtAddCallback(tearbutton,
                  XmNactivateCallback, mo_tool_detach_cb,
                  (XtPointer) win);

    for(i=0;mo_tools[i].label;i++) {
        if(mo_tools[i].action>0){
	    if (use_tool[i]) {
              win->tools[i].w = XtVaCreateManagedWidget
                ((long_text?mo_tools[i].long_text:mo_tools[i].text)
		 ,xmPushButtonWidgetClass,
                 win->button_rc,
                 XmNuserData, (XtPointer) mo_tools[i].label,
                 XmNmarginWidth, 0,
                 XmNmarginHeight, 0,
                 XmNmarginTop, 0,
                 XmNmarginBottom, 0,
/*
                 XmNmarginLeft, textbuttons ? 2 : 0,
                 XmNmarginRight, textbuttons ? 2 : 0,
*/
                 XmNmarginLeft, 0,
                 XmNmarginRight, 0,

                 XmNalignment, XmALIGNMENT_CENTER,
                 XmNlabelType, textbuttons ? XmSTRING : XmPIXMAP,
                 XmNlabelPixmap, *(mo_tools[i].image),

		 XmNfontList, tmpFontList,

                 NULL);

              XtOverrideTranslations(win->tools[i].w,
                                  XtParseTranslationTable(xlattab));
              if(mo_tools[i].greyimage != NULL)
                XtVaSetValues(win->tools[i].w,
                              XmNlabelInsensitivePixmap,
                              *(mo_tools[i].greyimage),
                              NULL);
              XmxSetSensitive(win->tools[i].w,win->tools[i].gray);
              XmxAddCallback(win->tools[i].w,
                           XmNactivateCallback, menubar_cb,
                           mo_tools[i].action);
            
              if(!(mo_tools[i].toolset & win->mode))
                XtUnmanageChild(win->tools[i].w);                    
	    }
	} else {
            win->tools[i].w=NULL;
            XtVaCreateManagedWidget
                (" ",xmSeparatorWidgetClass,
                 win->button_rc,
                 XmNorientation, vert?XmHORIZONTAL:XmVERTICAL, 
                 vert?XmNheight:XmNwidth, vert?3:4,
                 XmNseparatorType, XmNO_LINE,
                 NULL);
	}
    }
        

    if(!win->biglogo || (!win->smalllogo && win->toolbardetached)){
        mo_extra_buttons(win, win->button2_rc);
        mo_make_globe(win, win->button2_rc, 1);
    }  

    XtManageChild(win->button2_rc);
    XtManageChild(win->button_rc);
    
    return (win->topform);
}



/****************************************************************************
 * name:    mo_fill_window (PRIVATE)
 * purpose: Take a new (empty) mo_window struct and fill in all the 
 *          GUI elements.
 * inputs:  
 *   - mo_window *win: The window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   
 ****************************************************************************/
static mo_status mo_fill_window (mo_window *win)
{
  Widget up, dn;
  int linkup, topatt, botatt;
  Widget form,topform,botform;
  Widget rightform,title_label,url_label;
  int i,globe,height;
  char *s;
  
  form = XtVaCreateManagedWidget("form0", xmFormWidgetClass, win->base, NULL);


  if((scount < 0)  && (s = get_pref_string(eGUI_LAYOUT))){
      parse_slabinfo(s);
  }
  if(scount < 0){
          /* go with the default layout */
      win->smalllogo = 0;
      win->texttools = 0;
      
      win->slabcount = 6;
      win->slabpart[0] = SLAB_MENU;
      win->slabpart[1] = SLAB_GLOBE;
      win->slabpart[2] = SLAB_TOOLS;
      win->slabpart[3] = SLAB_URL;
      win->slabpart[4] = SLAB_VIEW;
      win->slabpart[5] = SLAB_STATUS;
  } else {
      win->texttools = stexttools;
      win->smalllogo = smalllogo;
      win->slabcount = scount;
      for(i=0;i<scount;i++){
          win->slabpart[i] = sarg[i];
      }
  }

  win->biglogo=0;
  for(i=0;i<win->slabcount;i++){
      if(win->slabpart[i]==SLAB_GLOBE) win->biglogo=1;
  }
  
      /* no active toolset, horiz, not detached */
  win->toolset = 0;
  win->toolbarorientation = 0;
  win->toolbardetached = 0;
  win->toolbarwin = NULL;


    
      /*********************** SLAB_GLOBE ****************************/
  if(win->biglogo){
    win->slab[SLAB_GLOBE] = XtVaCreateWidget("slab_globe",
                                             xmFormWidgetClass, form, NULL);

    mo_make_globe(win,win->slab[SLAB_GLOBE],win->smalllogo);
  } else {
      win->slab[SLAB_GLOBE] = NULL;
  }
  
  
      /*********************** SLAB_MENU ****************************/
  win->menubar = mo_make_document_view_menubar (form);
  win->slab[SLAB_MENU] = win->menubar->base;
  XtUnmanageChild(win->slab[SLAB_MENU]);

      /*********************** SLAB_TITLE ****************************/
  win->slab[SLAB_TITLE] = XtVaCreateWidget("slab_title",
                                           xmFormWidgetClass, form,
                                           XmNheight, 36, NULL);
  title_label = XtVaCreateManagedWidget("Title:",xmLabelWidgetClass,
                                        win->slab[SLAB_TITLE],
                                        XmNleftOffset, 3,
                                        XmNleftAttachment, XmATTACH_FORM,
                                        XmNrightAttachment, XmATTACH_NONE,
                                        XmNtopAttachment, XmATTACH_FORM,
                                        XmNbottomAttachment, XmATTACH_FORM,
                                        NULL);
  win->title_text = XtVaCreateManagedWidget("title",xmTextFieldWidgetClass,
                                            win->slab[SLAB_TITLE],
                                            XmNrightOffset, 3,
                                            XmNleftOffset, 3,
                                            XmNtopOffset, 3,
                                            XmNleftAttachment, XmATTACH_WIDGET,
                                            XmNleftWidget, title_label,
                                            XmNrightAttachment, XmATTACH_FORM,
                                            XmNtopAttachment, XmATTACH_FORM,
                                            XmNbottomAttachment, XmATTACH_NONE,
                                            XmNeditable, False,
                                            XmNcursorPositionVisible, False,
                                            NULL);
  

      /*********************** SLAB_URL ****************************/
  win->slab[SLAB_URL] = XtVaCreateWidget("slab_url",
                                         xmFormWidgetClass, form,
                                         XmNheight, 36, NULL);
  url_label = XtVaCreateManagedWidget("URL:",xmLabelWidgetClass,
                                      win->slab[SLAB_URL],
                                      XmNleftOffset, 3,
                                      XmNleftAttachment, XmATTACH_FORM,
                                      XmNrightAttachment, XmATTACH_NONE,
                                      XmNtopAttachment, XmATTACH_FORM,
                                      XmNbottomAttachment, XmATTACH_FORM,
                                      NULL);
  win->url_text = XtVaCreateManagedWidget("text",xmTextFieldWidgetClass,
                                          win->slab[SLAB_URL],
                                          XmNrightOffset, 3,
                                          XmNleftOffset, 3,
                                          XmNtopOffset, 3,
                                          XmNleftAttachment, XmATTACH_WIDGET,
                                          XmNleftWidget, url_label,
                                          XmNrightAttachment, XmATTACH_FORM,
                                          XmNtopAttachment, XmATTACH_FORM,
                                          XmNbottomAttachment, XmATTACH_NONE,
                                          XmNcursorPositionVisible, True,
                                          XmNeditable, True,
                                          NULL);
      /* DO THIS WITH THE SLAB MANAGER - BJS */
  if (!(get_pref_boolean(eKIOSK) || get_pref_boolean(eKIOSKNOEXIT))) {
      XmxAddCallbackToText (win->url_text, url_field_cb, 0);
  } else {
      XtUnmanageChild(url_label);
      XtUnmanageChild(win->url_text);
  }

  
      /*********************** SLAB_VIEW ****************************/
  win->slab[SLAB_VIEW] = win->scrolled_win = XtVaCreateManagedWidget
      ("view", htmlWidgetClass, form,
       WbNtext, 0,
       XmNresizePolicy, XmRESIZE_ANY,
       WbNpreviouslyVisitedTestFunction, anchor_visited_predicate,
       WbNpointerMotionCallback, pointer_motion_callback,
       WbNfancySelections, win->pretty ? True : False,
       WbNdelayImageLoads, win->delay_image_loads ? True : False,
       XmNshadowThickness, 2,
       NULL);
  mo_register_image_resolution_function (win);
  XmxAddCallback (win->scrolled_win, WbNanchorCallback, anchor_cb, 0);
  XmxAddCallback (win->scrolled_win, WbNlinkCallback, link_callback, 0);
  XmxAddCallback (win->scrolled_win, WbNsubmitFormCallback,
                  submit_form_callback, 0);
  XtVaGetValues(win->scrolled_win, WbNview, (long)(&win->view), NULL);
  XmxAddEventHandler
    (win->view, KeyPressMask, mo_view_keypress_handler, 0);
 /* now that the htmlWidget is created we can do this  */
  mo_make_popup(win->view);

  
      /*********************** SLAB_STATUS ****************************/
  win->slab[SLAB_STATUS] = XtVaCreateWidget("slab_status",
                                            xmFormWidgetClass, form, NULL);

  
  /* meter */
  if (get_pref_boolean(eMETER)) {
      win->meter_text = NULL;
      win->meter_notext = 0;
      win->meter_font = 0;
      win->meter_frame = XmxMakeFrame(win->slab[SLAB_STATUS], XmxShadowIn);
      XtVaSetValues(win->meter_frame,
                    XmNrightOffset, 3,
                    XmNtopOffset, 2,
                    XmNbottomOffset, 2,
                    XmNleftAttachment, XmATTACH_NONE,
                    XmNrightAttachment, XmATTACH_FORM,
                    XmNtopAttachment, XmATTACH_FORM,
                    XmNbottomAttachment, XmATTACH_FORM,
                    NULL);
        
      win->meter = XtVaCreateManagedWidget
          ("meter", xmDrawingAreaWidgetClass,
           win->meter_frame,
           XmNuserData, (XtPointer) "Progress Meter",
           XmNheight, 16,
           XmNwidth, 96,
           NULL);
      XtOverrideTranslations(win->meter,
                            XtParseTranslationTable(xlattab));
      
      win->meter_level = 0;
      win->meter_width = -1;
      
      XtAddCallback(win->meter, XmNexposeCallback,
                    DrawMeter, (XtPointer) win);
      XtAddCallback(win->meter, XmNresizeCallback,
                    ResizeMeter, (XtPointer) win);
      
          /* grab some colors */
      {
          XColor ccell1,ccell2;
          
          XAllocNamedColor(dsp,(installed_colormap ?
                                installed_cmap :
                                DefaultColormapOfScreen(XtScreen(win->base))),
                           get_pref_string(eMETER_FOREGROUND),
                           &ccell1,&ccell2);
          win->meter_fg = ccell2.pixel;
          XAllocNamedColor(dsp,(installed_colormap ?
                                installed_cmap :
                                DefaultColormapOfScreen(XtScreen(win->base))),
                           get_pref_string(eMETER_BACKGROUND),
                           &ccell1,&ccell2);
          win->meter_bg = ccell2.pixel;
          XAllocNamedColor(dsp,(installed_colormap ?
                                installed_cmap :
                                DefaultColormapOfScreen(XtScreen(win->base))),
                           get_pref_string(eMETER_FONT_FOREGROUND),
                           &ccell1,&ccell2);
          win->meter_font_fg = ccell2.pixel;
      }
  } else {
      win->meter_frame = NULL;
      win->meter = NULL;
  }

  win->tracker_label = XtVaCreateManagedWidget
      (" ",xmLabelWidgetClass,
       win->slab[SLAB_STATUS],
       XmNalignment, XmALIGNMENT_BEGINNING,
       XmNleftAttachment, XmATTACH_FORM,
       XmNrightAttachment, get_pref_boolean(eMETER) ? XmATTACH_NONE : XmATTACH_WIDGET,
       XmNrightWidget, get_pref_boolean(eMETER) ? win->meter_frame : NULL,
       XmNtopAttachment, XmATTACH_FORM,
       XmNbottomAttachment, XmATTACH_NONE,
                                               NULL);


      /*********************** SLAB_TOOLS ****************************/
  win->slab[SLAB_TOOLS] = XtVaCreateWidget("slab_tools",
                                            xmFormWidgetClass, form,
                                            NULL);

  mo_fill_toolbar(win,win->slab[SLAB_TOOLS],640,32);

      /* chain those slabs together 'n stuff */
  for(globe=0,linkup=1,i=0;i<win->slabcount;i++){
      if(win->slabpart[i] == SLAB_GLOBE){
/* next two slabs have to attach to the globe */
          globe=2-win->smalllogo;
          if(linkup){
              XtVaSetValues
                  (win->slab[SLAB_GLOBE],
                   XmNleftAttachment, XmATTACH_NONE,
                   XmNrightAttachment, XmATTACH_FORM,
                   XmNtopAttachment, i ? XmATTACH_WIDGET : XmATTACH_FORM,
                   XmNtopWidget, i ?win->slab[win->slabpart[i-1]] : NULL,
                   XmNbottomAttachment, XmATTACH_NONE,
                   NULL);
          } else {
              XtVaSetValues
                  (win->slab[SLAB_GLOBE],
                   XmNleftAttachment, XmATTACH_NONE,
                   XmNrightAttachment, XmATTACH_FORM,
                   XmNbottomAttachment, i+globe+1==win->slabcount ? XmATTACH_FORM : XmATTACH_WIDGET,
                   XmNbottomWidget, i+globe+1==win->slabcount ? NULL : win->slab[win->slabpart[i+globe+1]],
                   XmNtopAttachment, XmATTACH_NONE,
                   NULL);
          }
          
      } else {
          if(win->slabpart[i] == SLAB_VIEW){
                  /* we change link dir here AND link this slab both ways*/
              linkup = 0;
              if(!i || ((i==1) && (win->slabpart[i-1]==SLAB_GLOBE))){
                  up = NULL;
                  topatt = XmATTACH_FORM;
              } else {
                  up = win->slab[win->slabpart[(i-1) - (globe==2 ? 1 : 0)]];
                  topatt = XmATTACH_WIDGET;
              }
              if(i==win->slabcount-1){
                  dn = NULL;
                  botatt = XmATTACH_FORM;
              } else {
                  dn = win->slab[win->slabpart[i+1]];
                  botatt = XmATTACH_WIDGET;
              }
          } else {
              if(linkup){
                  if(!i || ((i==1) && (win->slabpart[i-1]==SLAB_GLOBE))){
                      up = NULL;
                      topatt = XmATTACH_FORM;
                  } else {
                      if(globe==1 && win->smalllogo){
                          up = win->slab[win->slabpart[i-2]];
                      } else {
                          up = win->slab[win->slabpart[(i-1)-(globe==2 ? 1 : 0)]];
                      }
                      
                      topatt = XmATTACH_WIDGET;
                  }
                  dn = NULL;
                  botatt = XmATTACH_NONE;
              } else {
                  if(i==win->slabcount-1){
                      dn = NULL;
                      botatt = XmATTACH_FORM;
                  } else {
                      dn = win->slab[win->slabpart[i+1]];
                      botatt = XmATTACH_WIDGET;
                  }
                  up = NULL;
                  topatt = XmATTACH_NONE;
              }    
          }

              /*
          fprintf(stderr,"%s (0x%08X): up=0x%08X dn=0x%08X ta=%d ba=%d\n",
                  slab_words[win->slabpart[i]], win->slab[win->slabpart[i]],
                  up, dn, topatt, botatt);
                  */  

          XtVaSetValues(win->slab[win->slabpart[i]],
                        XmNleftOffset, 0,
                        XmNrightOffset, 0,
                        XmNtopOffset, 0,
                        XmNbottomOffset, 0,
                        XmNtopAttachment, topatt,
                        XmNtopWidget, up,
                        XmNbottomAttachment, botatt,
                        XmNbottomWidget, dn,
                        XmNleftAttachment, XmATTACH_FORM,
                        XmNrightAttachment, globe ? XmATTACH_WIDGET : XmATTACH_FORM,
                        XmNrightWidget, win->slab[SLAB_GLOBE],
                        NULL);
          if(globe) globe--;
      }
  }
  for(i=0;i<win->slabcount;i++)
      XtManageChild(win->slab[win->slabpart[i]]);

  XtManageChild(form);

  /* Can't go back or forward if we haven't gone anywhere yet... */
  mo_back_impossible (win);
  mo_forward_impossible (win);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_delete_window
 * purpose: Shut down a window.
 * inputs:  
 *   - mo_window *win: The window.
 * returns: 
 *   mo_succeed
 * remarks: 
 *   This can be called, among other places, from the WM_DELETE_WINDOW
 *   handler.  By the time we get here, we must assume the window is already
 *   in the middle of being shut down.
 *   We must explicitly close every dialog that be open as a child of
 *   the window, because window managers too stupid to do that themselves
 *   will otherwise allow them to stay up.
 ****************************************************************************/
#define POPDOWN(x) \
  if (win->x) XtUnmanageChild (win->x)

mo_status mo_delete_window (mo_window *win)
{
  mo_node *node;

  if (!win)
    return mo_fail;

  node = win->history;

  POPDOWN (source_win);
  POPDOWN (save_win);
  POPDOWN (savebinary_win);
  POPDOWN (open_win);
  POPDOWN (mail_win);
  POPDOWN (mailhist_win);
  POPDOWN (print_win);
  POPDOWN (history_win);
  POPDOWN (open_local_win);
  if (win->hotlist_win)
    XtDestroyWidget(win->hotlist_win);
  POPDOWN (techsupport_win);
  POPDOWN (annotate_win);
  POPDOWN (search_win);
  POPDOWN (searchindex_win);
  POPDOWN (mailto_win);
  POPDOWN (mailto_form_win);
  POPDOWN (news_win);
  POPDOWN (links_win);
#ifdef HAVE_DTM
  POPDOWN (dtmout_win);
#endif
#ifdef HAVE_AUDIO_ANNOTATIONS
  POPDOWN (audio_annotate_win);
#endif
  XtPopdown (win->base);

      /* we really should be doing this :-) BJS */
  XtDestroyWidget(win->base);

  /* Free up some of the HTML Widget's state */
  HTMLFreeImageInfo (win->scrolled_win);

  while (node)
    {
      mo_node *tofree = node;
      node = node->next;
      mo_free_node_data (tofree);
      free (tofree);
    }
  
  free (win->search_start);
  free (win->search_end);

  /* This will free the win structure (but none of its elements
     individually) and exit if this is the last window in the list. */
  mo_remove_window_from_list (win);

  /* Go get another current_win. */
  mo_set_current_cached_win (mo_next_window (NULL));

  return mo_succeed;
}


int mo_get_font_size_from_res(char *userfontstr,int *fontfamily)
{
  char *lowerfontstr = strdup(userfontstr);
  int   x;

  for (x=0; x<strlen(userfontstr); x++)
    lowerfontstr[x]=tolower(userfontstr[x]);
  
  *fontfamily = 0;
  if (strstr(lowerfontstr, "times")!=NULL)
    {
      if (strstr(lowerfontstr, "large")!=NULL)
	return mo_large_fonts;
      if (strstr(lowerfontstr, "regular")!=NULL)
	return mo_regular_fonts;
      if (strstr(lowerfontstr, "small")!=NULL)
	return mo_small_fonts;
      return mo_regular_fonts;
    }
  if (strstr(lowerfontstr, "helvetica")!=NULL)
    {
      *fontfamily = 1;
      if (strstr(lowerfontstr, "large")!=NULL)
	return mo_large_helvetica;
      if (strstr(lowerfontstr, "regular")!=NULL)
	return mo_regular_helvetica;
      if (strstr(lowerfontstr, "small")!=NULL)
	return mo_small_helvetica;
      return mo_regular_helvetica;
    }
  if (strstr(lowerfontstr, "century")!=NULL)
    {
      *fontfamily = 2;
      if (strstr(lowerfontstr, "large")!=NULL)
	return mo_large_newcentury;
      if (strstr(lowerfontstr, "regular")!=NULL)
	return mo_regular_newcentury;
      if (strstr(lowerfontstr, "small")!=NULL)
	return mo_small_newcentury;
      return mo_regular_newcentury;
    }
  if (strstr(lowerfontstr, "lucida")!=NULL)
    {
      *fontfamily = 3;
      if (strstr(lowerfontstr, "large")!=NULL)
	return mo_large_lucidabright;
      if (strstr(lowerfontstr, "regular")!=NULL)
	return mo_regular_lucidabright;
      if (strstr(lowerfontstr, "small")!=NULL)
	return mo_small_lucidabright;
      return mo_regular_lucidabright;
    }
  return mo_regular_fonts;
}  


void kill_splash()
{
    if(splash_cc) {
        ReleaseSplashColors(splash);
    } else {
	XtPopdown(splash);
    }
    XtDestroyWidget(splash);
    splash=NULL;
}


extern gui_news_updateprefs (mo_window *win);
void mo_set_agents(mo_window *win, int which);

/****************************************************************************
 * name:    mo_open_window_internal (PRIVATE)
 * purpose: Make a mo_window struct and fill up the GUI.
 * inputs:  
 *   - Widget       base: The dialog widget on which this window is 
 *                        to be based.
 *   - mo_window *parent: The parent mo_window struct for this window,
 *                        if one exists; this can be NULL.
 * returns: 
 *   The new window (mo_window *).
 * remarks: 
 *   This routine must set to 0 all elements in the mo_window struct
 *   that can be tested by various routines to see if various things
 *   have been done yet (popup windows created, etc.).
 ****************************************************************************/
static mo_window *mo_open_window_internal (Widget base, mo_window *parent)
{
  mo_window *win;
  Widget dialog_pixmap;
  int i;

  win = (mo_window *)malloc (sizeof (mo_window));
  win->id = XmxMakeNewUniqid ();
  XmxSetUniqid (win->id);

  win->base = base;
  win->mode = moMODE_PLAIN;
  
  win->source_win = 0;
  win->save_win = 0;
  win->savebinary_win = 0;
  win->ftpput_win = win->ftpremove_win = win->ftpmkdir_win = 0;
/*
  win->tag_win = win->tag_list = 0;
  win->urlUnderPointer = NULL;
*/
  for(i=0;i<BTN_COUNT;i++) win->tools[i].gray = XmxSensitive;

  win->open_win = win->open_text = win->open_local_win = 0;
  win->mail_win = win->mailhot_win = win->edithot_win = win->mailhist_win =
    win->inserthot_win = 0;
  win->print_win = 0;
  win->history_win = win->history_list = 0;
  win->hotlist_win = win->hotlist_list = 0;
  win->techsupport_win = win->techsupport_text = 0;
  win->mailto_win = win->mailto_text = 0;
  win->mailto_form_win = win->mailto_form_text = 0;
  win->post_data=0;
  win->news_win = 0;
  win->links_win = 0;
  win->news_fsb_win = 0;
  win->mail_fsb_win = 0;
  win->annotate_win = 0;
  win->search_win = win->search_win_text = 0;
  win->searchindex_win = win->searchindex_win_label = win->searchindex_win_text = 0;
  win->cci_win = win->cci_win_text = (Widget) 0;
  win->cci_accept_toggle = win->cci_off_toggle = (Widget) 0;
#ifdef HAVE_DTM
  win->dtmout_win = win->dtmout_text = 0;
#endif
#ifdef HAVE_AUDIO_ANNOTATIONS
  win->audio_annotate_win = 0;
#endif

  win->history = NULL;
  win->current_node = 0;
  win->reloading = 0;
  win->source_text = 0;
  win->format_optmenu = 0;
  win->save_format = 0;
  if (!parent) {
    win->font_size = mo_get_font_size_from_res(get_pref_string(eDEFAULT_FONT_CHOICE),
			&(win->font_family));
    /*win->font_size = mo_regular_fonts;*/
    /*win->font_family = 0;*/
  } else {
    win->font_size = parent->font_size;
    win->font_family = parent->font_family;
  }

  win->agent_state=selectedAgent+mo_last_entry;

  win->underlines_snarfed = 0;
  if (!parent)
    win->underlines_state = mo_default_underlines;
  else
    win->underlines_state = parent->underlines_state;

  win->pretty = get_pref_boolean(eDEFAULT_FANCY_SELECTIONS);

  win->mail_format = 0;

#ifdef HAVE_AUDIO_ANNOTATIONS
  win->record_fnam = 0;
  win->record_pid = 0;
#endif
  
  win->print_text = 0;
  win->print_format = 0;

  win->target_anchor = 0;
  /* Create search_start and search_end. */
  win->search_start = (void *)malloc (sizeof (ElementRef));
  win->search_end = (void *)malloc (sizeof (ElementRef));

#ifdef ISINDEX
  /* We don't know yet. */
  win->keyword_search_possible = -1;
#endif
  
/*SWP 7/3/95*/
  if (get_pref_boolean(eSECURITYICON)) {
        if (win->current_node) {
                mo_gui_check_security_icon_in_win(win->current_node->authType,win);
	}
  }
  win->delay_image_loads = get_pref_boolean(eDELAY_IMAGE_LOADS);
/* Install all the GUI bits & pieces. */
  mo_fill_window (win);

  XmxRSetToggleState (win->menubar, win->font_size, XmxSet);

  /* setup news default states */
   ConfigView = !get_pref_boolean (eUSETHREADVIEW);
   newsShowAllGroups = get_pref_boolean (eSHOWALLGROUPS);
   newsShowReadGroups = get_pref_boolean (eSHOWREADGROUPS);
   newsShowAllArticles = get_pref_boolean (eSHOWALLARTICLES);
   newsNoThreadJumping = get_pref_boolean (eNOTHREADJUMPING);
   gui_news_updateprefs (win);

  win->binary_transfer = 0;
  XmxRSetToggleState (win->menubar, mo_binary_transfer,
                      (win->binary_transfer ? XmxSet : XmxNotSet));
  XmxRSetToggleState (win->menubar, mo_delay_image_loads,
                      (win->delay_image_loads ? XmxSet : XmxNotSet));
  XmxRSetSensitive (win->menubar, mo_expand_images_current,
                    win->delay_image_loads ? XmxSensitive : XmxNotSensitive);
  XmxRSetSensitive (win->menubar, mo_annotate, XmxSensitive);
  XmxRSetSensitive (win->menubar, mo_annotate_edit, XmxNotSensitive);
  XmxRSetSensitive (win->menubar, mo_annotate_delete, XmxNotSensitive);

  tableSupportEnabled = win->table_support = get_pref_boolean(eENABLE_TABLES);
  XmxRSetToggleState (win->menubar, mo_table_support,
                      (win->table_support ? XmxSet : XmxNotSet));

  imageViewInternal = win->image_view_internal = get_pref_boolean(eIMAGEVIEWINTERNAL);
  XmxRSetToggleState (win->menubar, mo_image_view_internal,
                      (win->image_view_internal ? XmxSet : XmxNotSet));


  /* Pop the window up. */
  XtPopup (win->base, XtGrabNone);
  XFlush (dsp);
  XSync (dsp, False);
  
  /* Register win with internal window list. */
  mo_add_window_to_list (win);

      /* Set the font size. */
  if (win->font_size != mo_regular_fonts)
    mo_set_fonts (win, win->font_size);

  /* Set the underline state. */
  mo_set_underlines (win, win->underlines_state);

  mo_set_agents(win, win->agent_state);

  /* Set the fancy selections toggle to the starting value. */
  mo_set_fancy_selections_toggle (win);

  return win;
}


/****************************************************************************
 * name:    delete_cb (PRIVATE)
 * purpose: Callback for the WM_DELETE_WINDOW protocol.
 * inputs:  
 *   - as per XmxCallback
 * returns: 
 *   nothing
 * remarks: 
 *   By the time we get called here, the window has already been popped
 *   down.  Just call mo_delete_window to clean up.
 ****************************************************************************/
static XmxCallback (delete_cb)
{
  mo_window *win = (mo_window *)client_data;
  mo_delete_window (win);
  return;
}


/****************************************************************************
 * name:    mo_make_window (PRIVATE)
 * purpose: Make a new window from scratch.
 * inputs:  
 *   - Widget      parent: Parent for the new window shell.
 *   - mo_window *parentw: Parent window, if one exists (may be NULL).
 * returns: 
 *   The new window (mo_window *).
 * remarks: 
 *   The 'parent window' parentw is the window being cloned, or the
 *   window in which the 'new window' command was triggered, etc.
 *   Some GUI properties are inherited from it, if it exists (fonts,
 *   anchor appearance, etc.).
 ****************************************************************************/
static mo_window *mo_make_window (Widget parent, mo_window *parentw)
{
  Widget base;
  mo_window *win;
  Atom WM_DELETE_WINDOW;
  char buf[80];

  sprintf(pre_title,"NCSA X Mosaic %s",MO_VERSION_STRING);
  sprintf(buf,"%s: ",pre_title);
  XmxSetArg (XmNtitle, (long)buf);
  XmxSetArg (XmNiconName, (long)"Mosaic");
  XmxSetArg (XmNallowShellResize, False);
  if (installed_colormap) {
	XmxSetArg(XmNcolormap,installed_cmap);
  }
  base = XtCreatePopupShell ("shell", topLevelShellWidgetClass,
                             toplevel, Xmx_wargs, Xmx_n);
  Xmx_n = 0;

#ifdef EDITRES_SUPPORT  
  XtAddEventHandler(base, (EventMask) 0, TRUE,
                    (XtEventHandler) _XEditResCheckMessages, NULL);
#endif

  win = mo_open_window_internal (base, parentw);

  WM_DELETE_WINDOW = XmInternAtom(dsp, "WM_DELETE_WINDOW", False);
  XmAddWMProtocolCallback(base, WM_DELETE_WINDOW, delete_cb, (XtPointer)win);

  return win;
}


/****************************************************************************
 * name:    mo_open_another_window_internal (PRIVATE)
 * purpose: Open a new window from an existing window.
 * inputs:  
 *   - mo_window *win: The old window.
 * returns: 
 *   The new window (mo_window *).
 * remarks: 
 *   This routine handles (optional) autoplace of new windows.
 ****************************************************************************/
static mo_window *mo_open_another_window_internal (mo_window *win)
{
  Dimension oldx, oldy;
  Dimension scrx = WidthOfScreen (XtScreen (win->base));
  Dimension scry = HeightOfScreen (XtScreen (win->base));
  Dimension x, y;
  Dimension width, height;
  mo_window *newwin;

  XtVaGetValues (win->base, XmNx, &oldx, XmNy, &oldy,
                 XmNwidth, &width, XmNheight, &height, NULL);

  /* Ideally we open down and over 40 pixels... is this possible? */
  /* If not, deal with it... */

  /* Bug fix, thanks to Ken Shores <kss1376@pop.draper.com> */
  
  /* the original test did not handle the case where the old window
   * was exactly the same size as the screen.  Also, it used a looping
   * algorithm which would infinite loop under such a case. */
  
  if ((oldx+width) > (scrx-40))
    x = (scrx - (oldx + width));
  else
    x = oldx + 40;
  
  if ((oldy+height) > (scry-40))
    y = (scry - (oldy + height));
  else
    y = oldy + 40;

  if (x > scrx) x = 0;
  if (y > scry) y = 0;
  
  XmxSetArg (XmNdefaultPosition, False);
  if (get_pref_boolean(eAUTO_PLACE_WINDOWS))
    {
      char geom[20];
      sprintf (geom, "+%d+%d\0", x, y);
      XmxSetArg (XmNgeometry, (long)geom);
    }
  XmxSetArg (XmNwidth, width);
  XmxSetArg (XmNheight, height);

  newwin = mo_make_window (toplevel, win);
  mo_set_current_cached_win (newwin);
  return newwin;
}


/****************************************************************************
 * name:    mo_open_window
 * purpose: Open a new window to view a given URL.
 * inputs:  
 *   - Widget      parent: The parent Widget for the new window's shell.
 *   - char          *url: The URL to view in the new window.
 *   - mo_window *parentw: The (optional) parent window of the new window.
 * returns: 
 *   The new window.
 * remarks: 
 *   
 ****************************************************************************/
mo_window *mo_open_window (Widget parent, char *url, mo_window *parentw)
{
  mo_window *win;

  win = mo_make_window (parent, parentw);

  mo_set_current_cached_win (win);

  mo_load_window_text (win, url, NULL);

  return win;
}


/****************************************************************************
 * name:    mo_duplicate_window
 * purpose: Clone a existing window as intelligently as possible.
 * inputs:  
 *   - mo_window *win: The existing window.
 * returns: 
 *   The new window.
 * remarks: 
 *   
 ****************************************************************************/
mo_window *mo_duplicate_window (mo_window *win) {

mo_window *neww;

	if (win && win->current_node) {
		securityType=win->current_node->authType;
	}

	neww = mo_open_another_window_internal (win);
  
	mo_duplicate_window_text (win, neww);

	mo_gui_update_meter(100,NULL);

	return neww;
}



/****************************************************************************
 * name:    mo_open_another_window
 * purpose: Open another window to view a given URL, unless the URL
 *          indicates that it's pointless to do so
 * inputs:  
 *   - mo_window      *win: The existing window.
 *   - char           *url: The URL to view in the new window.
 *   - char           *ref: The reference (hyperlink text contents) for this
 *                          URL; can be NULL.
 *   - char *target_anchor: The target anchor to view open opening the
 *                          window, if any.
 * returns: 
 *   The new window.
 * remarks: 
 *   
 ****************************************************************************/
mo_window *mo_open_another_window (mo_window *win, char *url, char *ref,
                                   char *target_anchor)
{
  mo_window *neww;
  mo_status return_stat = mo_succeed;

  /* Check for reference to telnet.  Never open another window
     if reference to telnet exists; instead, call mo_load_window_text,
     which knows how to manage current window's access to telnet. */
  if (!strncmp (url, "telnet:", 7) || !strncmp (url, "tn3270:", 7) ||
      !strncmp (url, "rlogin:", 7))
    {
      mo_load_window_text (win, url, NULL);
      return NULL;
    }

  mo_busy ();

  neww = mo_open_another_window_internal (win);
  /* Set it here; hope it gets handled in mo_load_window_text_first
     (it probably won't, now. */
  neww->target_anchor = target_anchor;

  return_stat = mo_load_window_text (neww, url, ref); 

  if ((cci_get) && (return_stat == mo_fail))
	return (mo_window *) NULL;

  return neww;
}


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

char **gargv;
int gargc;

#ifndef VMS
extern MO_SIGHANDLER_RETURNTYPE ProcessExternalDirective (MO_SIGHANDLER_ARGS);
#endif

#ifdef HAVE_DTM
static XmxCallback (blip)
{
  mo_dtm_poll_and_read ();

  XtAppAddTimeOut (app_context, 100, (XtTimerCallbackProc)blip, (XtPointer)True);
  
  return;
}
#endif

#ifdef HAVE_DTM
mo_status mo_register_dtm_blip (void)
{
  /* Set a timer that will poll DTM regularly. */
  XtAppAddTimeOut (app_context, 100, (XtTimerCallbackProc)blip, (XtPointer)True);
  
  return mo_succeed;
}
#endif


/****************************************************************************
 * name:    fire_er_up (PRIVATE)
 * purpose: Callback from timer that actually starts up the application,
 *          i.e., opens the first window.
 * inputs:  
 *   - as per XmxCallback
 * returns: 
 *   Nothing.
 * remarks: 
 *   This routine figures out what the home document should be
 *   and then calls mo_open_window().
 ****************************************************************************/
static XmxCallback (fire_er_up)
{
  char *home_opt;
  mo_window *win;
  char *init_document;
  char *fname=NULL;
  int cnt=0;

  /* Pick up default or overridden value out of X resources. */
  home_document = get_pref_string(eHOME_DOCUMENT);;

  /* Value of environment variable WWW_HOME overrides that. */
  if ((home_opt = getenv ("WWW_HOME")) != NULL)
    home_document = home_opt;

#ifdef PRERELEASE
  /*
   * If this is a pre-release, go to the help-on-version doc for three
   *   start ups. Then, go to the Pre-Release warning page for three
   *   start ups. Then go to their defined page or the NCSA home page.
   */
  if ((cnt=GetCardCount((fname=MakeFilename())))<=MO_GO_NCSA_COUNT) {
	init_document = strdup (MO_HELP_ON_VERSION_DOCUMENT);
  }
  else if (cnt<=(MO_GO_NCSA_COUNT*2)) {
	init_document = strdup ("http://www.ncsa.uiuc.edu/SDG/Software/Mosaic/NewPrereleaseWarningPage.html");
  }
  else {
	init_document=strdup(home_document);
  }
#else
  /*
   * If this is not a pre-release, go to the help-on-version doc for three
   *   start ups. Then go to their defined page or the NCSA home page.
   */
  if (GetCardCount((fname=MakeFilename()))<=MO_GO_NCSA_COUNT) {
	init_document = strdup (MO_HELP_ON_VERSION_DOCUMENT);
  }
  else {
	init_document=strdup(home_document);
  }
#endif

  if (fname) {
	free(fname);
  }

  /* Value of argv[1], if it exists, sets startup_document.
     (All other command-line flags will have been picked up by
     the X resource mechanism.) */
  /* Unless they are bogus options - then they will break... DXP */
  if (gargc > 1 && gargv[1] && *gargv[1])
    startup_document = mo_url_prepend_protocol(gargv[1]);

  /* Check for proper home document URL construction. */
  if (!strstr (home_document, ":"))
    home_document = mo_url_canonicalize_local (home_document);

  /* Check for proper init document URL construction. */
  if (!strstr (init_document, ":"))
    init_document = mo_url_canonicalize_local (init_document);

/* SWP -- Done in mo_url_prepend_protcol
  if (startup_document && !strstr (startup_document, ":"))
    startup_document = mo_url_canonicalize_local (startup_document);
*/

/* set the geometry values - dxp */

  if(!userSpecifiedGeometry) {
      /* then no -geometry was specified on the command line,
	   so we just use the default values from the resources */
      XmxSetArg (XmNwidth, get_pref_int(eDEFAULT_WIDTH));
      XmxSetArg (XmNheight, get_pref_int(eDEFAULT_HEIGHT));
  }
  else {
      /* the they DID specify a -geometry, so we use that */
      XmxSetArg (XmNwidth, userWidth);
      XmxSetArg (XmNheight, userHeight);

      XmxSetArg (XmNx, userX);
      XmxSetArg (XmNx, userY);
  }

  if (get_pref_boolean(eINITIAL_WINDOW_ICONIC))
    XmxSetArg (XmNiconic, True);

  win = mo_open_window 
    (toplevel, startup_document ? startup_document : init_document, NULL);

  /* Check the Comment Card */
#ifdef PRERELEASE
  do_comment=0; /* Don't actually display the cc if we aren't in final release */
#endif
  CommentCard(win);

  XtVaGetValues(win->scrolled_win,
		WbNbodyColors,
		&(win->body_color),
		NULL);

  XtVaGetValues(win->scrolled_win,
		WbNbodyImages,
		&(win->body_images),
		NULL);

  XmxRSetToggleState (win->menubar, mo_body_color,
                      (win->body_color ? XmxSet : XmxNotSet));

  XmxRSetToggleState (win->menubar, mo_body_images,
                      (win->body_images ? XmxSet : XmxNotSet));

  return;
}


/****************************************************************************
 * name:    mo_open_initial_window
 * purpose: This routine is called when we know we want to open the
 *          initial Document View window.
 * inputs:  
 *   none
 * returns: 
 *   mo_succeed
 * remarks: This routine is simply a stub that sets a timeout that
 *          calls fire_er_up() after 10 milliseconds, which does the
 *          actual work.
 ****************************************************************************/
mo_status mo_open_initial_window (void)
{
  /* Set a timer that will actually cause the window to open. */
  XtAppAddTimeOut (app_context, 10, 
                   (XtTimerCallbackProc)fire_er_up, (XtPointer)True);

  return mo_succeed;
}


/****************************************************************************
 * name:    mo_error_handler (PRIVATE)
 * purpose: Handle X errors.
 * inputs:  
 *   - Display       *dsp: The X display.
 *   - XErrorEvent *event: The error event to handle.
 * returns: 
 *   0, if it doesn't force an exit.
 * remarks: 
 *   The main reason for this handler is to keep the application
 *   from crashing on BadAccess errors during calls to XFreeColors().
 ****************************************************************************/
static int mo_error_handler (Display *dsp, XErrorEvent *event)
{
  char buf[128];

  XUngrabPointer (dsp, CurrentTime);   /* in case error occurred in Grab */

  /* BadAlloc errors (on a XCreatePixmap() call)
     and BadAccess errors on XFreeColors are 'ignoreable' errors */
  if (event->error_code == BadAlloc ||
      (event->error_code == BadAccess && event->request_code == 88)) 
    return 0;
  else 
    {
{char ans[10]; printf("Press RETURN\n"); gets(ans);}
      /* All other errors are 'fatal'. */
      XGetErrorText (dsp, event->error_code, buf, 128);
      fprintf (stderr, "X Error: %s\n", buf);
      fprintf (stderr, "  Major Opcode:  %d\n", event->request_code);

      /* Try to close down gracefully. */
      mo_exit ();
    }
 return 0; /* never makes it here.... */
}

/* exported from HTTP.c */
void HT_SetExtraHeaders(char **headers);

/****************************************************************************
 * name:    mo_do_gui
 * purpose: This is basically the real main routine of the application.
 * inputs:  
 *   - int    argc: Number of arguments.
 *   - char **argv: The argument vector.
 * returns: 
 *   nothing
 * remarks: 
 *   
 ****************************************************************************/
void mo_do_gui (int argc, char **argv)
{
#ifdef MONO_DEFAULT
    int use_color = 0;
#else
    int use_color = 1;
#endif
    int no_defaults = 0;
    int color_set = 0;
    char* display_name = getenv("DISPLAY");
    Display* dpy;
    XrmDatabase intDB,appDB;
    Widget intWidget;
    int i;

        /* for prefs - DXP */
    Boolean successful;
    prefsStructP thePrefsStructP;

        /* Loop through the args before passing them off to
     XtAppInitialize() in case we need to catch something first. */
    for (i = 1; i < argc; i++)
    {
        if (!strcmp (argv[i], "-mono"))
        {
            use_color = 0;
            color_set = 1;
	    continue;
        }
        if (!strcmp (argv[i], "-color"))
        {
            use_color = 1;
            color_set = 1;
	    continue;
        }
        if (!strcmp (argv[i], "-nd"))
        {
            no_defaults = 1;
	    continue;
        }
        if (!strcmp (argv[i], "-display"))
        {
            display_name = argv[i + 1];
            i++;
	    continue;
        }
        if(!strcmp(argv[i], "-geometry")) {
            userSpecifiedGeometry = 1;
	    continue;
	}
	if (!strcmp(argv[i],"-install")) {
		installed_colormap=1;
		continue;
	}
	if (!strcmp(argv[i],"-iconic")) {
		splash_cc=0;
		continue;
	}
    }


        /* Motif setup. */
    XmxStartup ();
    XmxSetArg (XmNwidth,1);
    XmxSetArg (XmNheight,1);
    XmxSetArg (XmNmappedWhenManaged, False);
    /*
     * Awful expensive to open and close the display just to find
     * rhe depth information.
     */
    if ((dpy=XOpenDisplay(display_name))!=NULL) {
	if (!color_set) {
		use_color = DisplayPlanes(dpy, DefaultScreen(dpy)) > 1;
	}
	XCloseDisplay(dpy);
    }
    else {
	fprintf(stderr,"Couldn't open display: %s\n",(!display_name?"(NULL)":display_name));
    }

    if (no_defaults)
    {
        toplevel = XtAppInitialize 
            (&app_context, "Mosaic", options, XtNumber (options),
             &argc, argv, NULL, Xmx_wargs, Xmx_n);
    }
    else
    {
        if (use_color)
        {
            toplevel = XtAppInitialize 
                (&app_context, "Mosaic", options, XtNumber (options),
                 &argc, argv, color_resources, Xmx_wargs, Xmx_n);
        }
        else
        {
            toplevel = XtAppInitialize 
                (&app_context, "Mosaic", options, XtNumber (options),
                 &argc, argv, mono_resources, Xmx_wargs, Xmx_n);
        }
    }

    Xmx_n=0;

    dsp = XtDisplay (toplevel);

        /* initialize the preferences stuff */  
    successful = preferences_genesis();
    if(!successful) { /* I should probably be generating an error here... */
        signal (SIGBUS, 0);
        signal (SIGSEGV, 0);
        signal (SIGILL, 0);
        abort ();
    }

    thePrefsStructP = get_ptr_to_preferences();

      /* First for the regular resources */
    XtVaGetApplicationResources(
        toplevel,
        (XtPointer)thePrefsStructP->RdataP,
        resources,
        XtNumber (resources), NULL);

/*
  appDB=XrmGetDatabase(dsp);

  if (Rdata.internationalFilename!=NULL) {
	if ((intDB=XrmGetFileDatabase(Rdata.internationalFilename))!=NULL) {
		XrmMergeDatabases(intDB,&appDB);
		XrmSetDatabase(dsp,appDB);
	}
	else {
		fprintf(stderr,"There was no language file called:\n  [%s]\n",Rdata.internationalFilename);
	}
  }

  intWidget=XtVaCreateWidget("international",xmRowColumnWidgetClass,toplevel,
			     NULL);
  XtVaGetApplicationResources(intWidget, (XtPointer)&Idata, intResources,
			      XtNumber (intResources), NULL);
*/

        /* read the preferences file now */
    successful = read_preferences_file(NULL);
    if(!successful) {
        signal (SIGBUS, 0);
        signal (SIGSEGV, 0);
        signal (SIGILL, 0);
        abort ();
    }

    if (get_pref_boolean(eINSTALL_COLORMAP)) {
	installed_colormap=1;
    }

    if (installed_colormap) {
	XColor bcolr;

	installed_cmap=XCreateColormap(dsp,
				       RootWindow(dsp,DefaultScreen(dsp)),
				       DefaultVisual(dsp,DefaultScreen(dsp)),
				       AllocNone);

	XtVaGetValues(toplevel,
		      XtNbackground,
		      &(bcolr.pixel),
		      NULL);
	XQueryColor(dsp,
		    DefaultColormap(dsp,
				    DefaultScreen(dsp)),
		    &bcolr);

	XtVaSetValues(toplevel,
		      XmNcolormap, installed_cmap,
		      NULL);

        XAllocColor(dsp,
		    installed_cmap,
		    &bcolr);
	XtVaSetValues(toplevel,
		      XmNbackground,
		      bcolr.pixel,
		      NULL);
    }

  /* Needed for picread.c, right now. */
    {
        XVisualInfo vinfo, *vptr;
        int cnt;
    
        vinfo.visualid = 
            XVisualIDFromVisual
            (DefaultVisual (dsp,
                            DefaultScreen (dsp)));
        vptr = XGetVisualInfo (dsp, VisualIDMask, &vinfo, &cnt);
        Vclass = vptr->class;
        XFree((char *)vptr);
    }
  
        /* First get the hostname. */
    machine = (char *)malloc (sizeof (char) * 64);
    gethostname (machine, 64);

    uname(&mo_uname);
    HTAppVersion = 
        (char *)malloc (sizeof(char) * (
            strlen(MO_VERSION_STRING) +
            strlen(mo_uname.sysname) + 
            strlen(mo_uname.release) +
            strlen(mo_uname.machine) + 20));
    sprintf(HTAppVersion, "%s (X11;%s %s %s)", 
            MO_VERSION_STRING,
            mo_uname.sysname,
            mo_uname.release,
            mo_uname.machine);

    XSetErrorHandler (mo_error_handler);

    /* Transient shell cannot be focussed, so no point in splash screen as it
       will be complete psychadelic */
    if (installed_colormap || !splash_cc) {
	set_pref_boolean(eSPLASHSCREEN,False);
	if (splash_cc) {
		fprintf(stderr,"Warning: Splash Screen has been aborted.\n  Reason: User Installed Private Colormap.\n");
	}
    }

splash_goto:

    if (get_pref_boolean(eSPLASHSCREEN)) {
        Pixmap splashpix;
        GC gc;
        XGCValues values;
        XColor ccell1, ccell_fg;
      
        int x,y;
        XWindowAttributes war;
        Widget sform, spixwid;
        XFontStruct *font;
        char s[64];
      
        int l;
        int fontW, fontH;

        if (!XGetWindowAttributes(dsp,DefaultRootWindow(dsp),&war)) {
            fprintf(stderr,"Warning: Could not obtain your root window attributes.\n  Splash screen will not be centered.\n");
            x=y=100;
        }
        else {
            x=(war.width/2)-(320/2);
            y=(war.height/2)-(320/2);
        }

            /* GO GO MOSAIC SPLASH SCREEN - WHOOMP! */
        if (!(font =
              XLoadQueryFont(dsp,
                             "-adobe-helvetica-medium-o-normal-*-*-180-*-*-p-*-iso8859-*"))) {
		fprintf(stderr,"Warning: Cannot Get Font -adobe-helvetica-medium-o-normal-*-*-180-*-*-p-*-iso8859-*");
		fprintf(stderr,"Warning: Splash Screen has been aborted.\n  Reason: Could not load version font.\n");
		set_pref_boolean(eSPLASHSCREEN,False);
		goto splash_goto;
	}

        fontW = font->max_bounds.rbearing;
        fontH = font->max_bounds.ascent + font->max_bounds.descent;
      
        splash = XtVaCreatePopupShell
            ("Hello, World!",
             xmMenuShellWidgetClass,
             toplevel,
             XmNheight, 320,
             XmNwidth, 320,
             XmNx, x,
             XmNy, y,
             XmNallowShellResize, FALSE,
             NULL);
      
        splash_cc=180;
        splashpix = LoadSplashXPM(splash,&splash_cc);
	if (!splash_cc) {
		XtDestroyWidget(splash);
		set_pref_boolean(eSPLASHSCREEN,False);
		goto splash_goto;
	}

        sprintf(s,"version %s",MO_VERSION_STRING);
      
        l = strlen(s);
      
        sform = XtVaCreateManagedWidget("sform", xmRowColumnWidgetClass,
                                        splash,
                                        XmNheight, 320,
                                        XmNwidth, 320,
                                        XmNx, x,
                                        XmNy, y,
                                        NULL);

        XtPopup(splash, XtGrabNone);

        ccell_fg.flags = DoRed | DoGreen | DoBlue;
        ccell_fg.red = 0xF9F9;
        ccell_fg.blue = 0x0404;
        ccell_fg.green = 0x0404;

          /* we use red so we don't bother freeing it */
        if(!XAllocColor(dsp,(installed_colormap ?
			     installed_cmap :
			     DefaultColormapOfScreen(XtScreen(splash))),
                        &ccell_fg))
            ccell_fg.pixel = WhitePixelOfScreen(XtScreen(splash));
      
        
        gc = XtGetGC( splash,0,NULL);
        values.font = font->fid;
        values.foreground = ccell_fg.pixel;
      
        XChangeGC(dsp, gc, GCFont | GCForeground, &values);
      
        XDrawString(dsp, splashpix, gc, 
                    320-(fontW*l/2),
                    320-fontH/2, 
                    s, l);
      
      
        spixwid = XtVaCreateManagedWidget(" ", xmLabelWidgetClass,
                                          sform,
                                          XmNlabelType, XmPIXMAP,
                                          XmNlabelPixmap, splashpix,
                                          XmNalignment, XmALIGNMENT_CENTER,
                                          XmNx, x,
                                          XmNy, y,
                                          NULL);
      
        XFlush (dsp);
        XmUpdateDisplay (splash);
        XFlush (dsp);
        XSync (dsp, False);
        XtReleaseGC(splash,gc);
    }
        
    XtAppAddActions(app_context, balloon_action, 2);

    mo_init_menubar();

#ifdef __sgi
        /* Turn on debugging malloc if necessary. */
    if (get_pref_boolean(eDEBUGGING_MALLOC))
        mallopt (M_DEBUG, 1);
#endif

    if(get_pref_string(eACCEPT_LANGUAGE_STR)) {
        char **extras;

        extras = malloc(sizeof(char *) * 2);

        extras[0] = malloc(strlen(get_pref_string(eACCEPT_LANGUAGE_STR))+19);
        sprintf(extras[0],
                "Accept-Language: %s",
                get_pref_string(eACCEPT_LANGUAGE_STR));
        extras[1] = NULL;
      
        HT_SetExtraHeaders(extras);
    }
          
    global_xterm_str = get_pref_string(eXTERM_COMMAND);

    uncompress_program = get_pref_string(eUNCOMPRESS_COMMAND);
    gunzip_program = get_pref_string(eGUNZIP_COMMAND);

    tweak_gopher_types = get_pref_boolean(eTWEAK_GOPHER_TYPES);
    max_wais_responses = get_pref_int(eMAX_WAIS_RESPONSES);
    ftp_timeout_val = get_pref_int(eFTP_TIMEOUT_VAL);
    ftpRedial=get_pref_int(eFTP_REDIAL);
    ftpRedialSleep=get_pref_int(eFTP_REDIAL_SLEEP);
    ftpFilenameLength=get_pref_int(eFTP_FILENAME_LENGTH);
    ftpEllipsisLength=get_pref_int(eFTP_ELLIPSIS_LENGTH);
    ftpEllipsisMode=get_pref_int(eFTP_ELLIPSIS_MODE);

    sendReferer=get_pref_boolean(eSEND_REFERER);
    sendAgent=get_pref_boolean(eSEND_AGENT);

#ifndef DISABLE_TRACE
    httpTrace=get_pref_boolean(eHTTPTRACE);
    www2Trace=get_pref_boolean(eWWW2TRACE);
    htmlwTrace=get_pref_boolean(eHTMLWTRACE);
    cciTrace=get_pref_boolean(eCCITRACE);
    srcTrace=get_pref_boolean(eSRCTRACE);
    cacheTrace=get_pref_boolean(eCACHETRACE);
    nutTrace=get_pref_boolean(eNUTTRACE);
#else
    if (get_pref_boolean(eHTTPTRACE) ||
        get_pref_boolean(eWWW2TRACE) ||
        get_pref_boolean(eHTMLWTRACE) ||
        get_pref_boolean(eCCITRACE) ||
        get_pref_boolean(eSRCTRACE) ||
        get_pref_boolean(eCACHETRACE) ||
        get_pref_boolean(eNUTTRACE)) {
        fprintf(stderr,"Tracing has been compiled out of this binary.\n");
    }
#endif

    useAFS = get_pref_boolean(eUSEAFSKLOG);

    proxy_list = ReadProxies(get_pref_string(ePROXY_SPECFILE));
    noproxy_list = ReadNoProxies(get_pref_string(eNOPROXY_SPECFILE));

    use_default_extension_map = get_pref_boolean(eUSE_DEFAULT_EXTENSION_MAP);
    global_extension_map = get_pref_string(eGLOBAL_EXTENSION_MAP);

    if (get_pref_string(ePERSONAL_EXTENSION_MAP))
    {
        char *home = getenv ("HOME");
      
        if (!home)
            home = "/tmp";
      
        personal_extension_map = (char *)malloc 
            (strlen (home) +
             strlen (get_pref_string(ePERSONAL_EXTENSION_MAP)) +
             8);
        sprintf (personal_extension_map, "%s/%s\0", home, 
                 get_pref_string(ePERSONAL_EXTENSION_MAP));
    }
    else
        personal_extension_map = "\0";

    use_default_type_map = get_pref_boolean(eUSE_DEFAULT_TYPE_MAP);
    global_type_map = get_pref_string(eGLOBAL_TYPE_MAP);
    if (get_pref_string(ePERSONAL_TYPE_MAP))
    {
        char *home = getenv ("HOME");
      
        if (!home)
            home = "/tmp";
      
        personal_type_map = (char *)malloc 
            (strlen (home) +
             strlen (get_pref_string(ePERSONAL_TYPE_MAP)) +
             8);
        sprintf (personal_type_map, "%s/%s\0", home, 
                 get_pref_string(ePERSONAL_TYPE_MAP));
    }
    else
        personal_type_map = "\0";

#ifdef HAVE_HDF
    have_hdf = 1;
#else
    have_hdf = 0;
#endif

    twirl_increment = get_pref_int(eTWIRL_INCREMENT);
  
  /* Then make a copy of the hostname for shortmachine.
     Don't even ask. */
    shortmachine = strdup (machine);
  
        /* Then find out the full name, if possible. */
    if (get_pref_string(eFULL_HOSTNAME))
    {
        free (machine);
        machine = get_pref_string(eFULL_HOSTNAME);
    }
    else if (!get_pref_boolean(eGETHOSTBYNAME_IS_EVIL))
    {
        struct hostent *phe;
      
        phe = gethostbyname (machine);
        if (phe && phe->h_name)
        {
            free (machine);
            machine = strdup (phe->h_name);
        }
    }
        /* (Otherwise machine just remains whatever gethostname returned.) */
  
    machine_with_domain = (strlen (machine) > strlen (shortmachine) ?
                           machine : shortmachine);

    {/* Author Name & Email init.  - bjs */
        struct passwd *pw = getpwuid (getuid ());
        char *cc;
        char *default_author_name = get_pref_string(eDEFAULT_AUTHOR_NAME);
        char *default_author_email = get_pref_string(eDEFAULT_AUTHOR_EMAIL);
      
        if(!default_author_name) {
            default_author_name = strdup(pw->pw_gecos);
            strcpy(default_author_name,pw->pw_gecos);
            for(cc = default_author_name;*cc;cc++)
                if(*cc==',') {
                    *cc=0;
                    break;
                }
        }
        if(!default_author_email) {
            default_author_email =
                (char *) malloc(strlen(pw->pw_name)+strlen(machine)+2);
            sprintf(default_author_email,"%s@%s",pw->pw_name,machine);
        }    
        set_pref(eDEFAULT_AUTHOR_NAME, (void *)default_author_name);
        set_pref(eDEFAULT_AUTHOR_EMAIL, (void *)default_author_email);
    }
  
        /* If there's no tmp directory assigned by the X resource, then
     look at TMPDIR. */
    {
        char *tmp_dir = get_pref_string(eTMP_DIRECTORY);
          
        if (!tmp_dir)
        {
            tmp_dir = getenv ("TMPDIR");
                /* It can still be NULL when we leave here -- then we'll just
                   let tmpnam() do what it does best. */
            set_pref(eTMP_DIRECTORY, (void *)tmp_dir);
        }
    }

        /* If there's no docs directory assigned by the X resource,
     then look at MOSAIC_DOCS_DIRECTORY environment variable
     and then at hardcoded default. */
    {
        char *docs_dir = get_pref_string(eDOCS_DIRECTORY);
              
        if (!docs_dir)
        {
            docs_dir = getenv ("MOSAIC_DOCS_DIRECTORY");
            if (!docs_dir)
                docs_dir = DOCS_DIRECTORY_DEFAULT;
            if (!docs_dir || !*(docs_dir))
            {
                fprintf (stderr, "fatal error: nonexistent docs directory\n");
                exit (-1);
            }
            set_pref(eDOCS_DIRECTORY, (void *)docs_dir);
        }
    }

  if (get_pref_boolean(eUSE_GLOBAL_HISTORY))
    mo_setup_global_history ();
  else
    mo_init_global_history ();

  mo_setup_default_hotlist ();
  mo_write_default_hotlist (); /* amb */
  mo_setup_pan_list ();

  mo_init_hotmenu();

  /* Write pid into "~/.mosaicpid". */
  {
    char *home = getenv ("HOME"), *fnam;
    FILE *fp;
    
        if (!home)
            home = "/tmp";
    
        fnam = (char *)malloc (strlen (home) + 32);
        sprintf (fnam, "%s/.mosaicpid", home);
    
        fp = fopen (fnam, "w");
        if (fp)
        {
            fprintf (fp, "%d\n", getpid());
            fclose (fp);
        }
    
        free (fnam);
    }
  
    busy_cursor = XCreateFontCursor (dsp, XC_watch);

    XtRealizeWidget (toplevel);

        /* get the current geometry values */
    XtVaGetValues(toplevel, 
                  XmNwidth, &userWidth, 
                  XmNheight, &userHeight, 
                  XmNx, &userX,
                  XmNy, &userY,
                  NULL);
  

    gargv = argv;
    gargc = argc;

#ifndef VMS
    signal (SIGUSR1, (void *)ProcessExternalDirective);
#endif

    if(get_pref_boolean(eSPLASHSCREEN) && splash) {
      /*Wait 3 secs, then popdown*/
      if(splash_cc) {
          splashTimer =
              XtAppAddTimeOut(app_context, 3000,
                              (XtTimerCallbackProc)kill_splash, NULL);
      } else {
          kill_splash();
      }
    }

    createBusyCursors(toplevel);
    MakePixmaps(toplevel);
    logo_save = get_pref_int(ePIX_COUNT);
    logo_count = 0;

    mo_open_initial_window ();

#ifndef DISABLE_TRACE
    if (srcTrace) {
        fprintf(stderr,"cciPort resourced to %d\n",get_pref_int(eCCIPORT));
    }
#endif

    if ((get_pref_int(eCCIPORT) > 1023 ) &&  (get_pref_int(eCCIPORT) < 65536))
    {	
        MoCCIStartListening(toplevel,get_pref_int(eCCIPORT));
    }

    XtAppMainLoop (app_context);
}


/****************************************************************************
 * name:    mo_process_external_directive
 * purpose: Handle an external directive given to the application via
 *          a config file read in response to a SIGUSR1.
 * inputs:  
 *   - char *directive: The directive; either "goto" or "newwin".
 *   - char       *url: The URL corresponding to the directive.
 * returns: 
 *   nothing
 * remarks: 
 *   
 ****************************************************************************/
#define CLIP_TRAILING_NEWLINE(url) \
  if (url[strlen (url) - 1] == '\n') \
    url[strlen (url) - 1] = '\0';

static XEvent *mo_manufacture_dummy_event (Widget foo)
{
  /* This is fucking hilarious. */
  XAnyEvent *a = (XAnyEvent *)malloc (sizeof (XAnyEvent));
  a->type = 1; /* HAHA! */
  a->serial = 1; /* HAHA AGAIN! */
  a->send_event = False;
  a->display = XtDisplay (foo);
  a->window = XtWindow (foo);
  return (XEvent *)a;
}

void mo_process_external_directive (char *directive, char *url)
{
  /* Process a directive that we received externally. */
  mo_window *win = current_win;

  /* Make sure we have a window. */
  if (!win)
    win = mo_next_window (NULL);

  if (!strncmp (directive, "goto", 4))
    {
      CLIP_TRAILING_NEWLINE(url);

      mo_access_document (win, url);

      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "newwin", 6))
    {
      CLIP_TRAILING_NEWLINE(url);

      /* Force a new window to open. */ 
      mo_open_another_window (win, url, NULL, NULL);

      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "pagedown", 8))
    {
      Widget sb;
      String params[1];
      
      params[0] = "0";
      
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb))
        {
          XEvent *event = mo_manufacture_dummy_event (sb);
          XtCallActionProc (sb, "PageDownOrRight", event, params, 1);
        }

      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "pageup", 6))
    {
      Widget sb;
      String params[1];

      params[0] = "0";

      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb))
        {
          XEvent *event = mo_manufacture_dummy_event (sb);
          XtCallActionProc (sb, "PageUpOrLeft", event, params, 1);
        }
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "scrolldown", 9))
    {
      Widget sb;
      String params[1];
      
      params[0] = "0";
      
      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb))
        {
          XEvent *event = mo_manufacture_dummy_event (sb);
          XtCallActionProc (sb, "IncrementDownOrRight", event, params, 1);
        }
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "scrollup", 7))
    {
      Widget sb;
      String params[1];

      params[0] = "0";

      XtVaGetValues (win->scrolled_win, XmNverticalScrollBar, 
                     (long)(&sb), NULL);
      if (sb && XtIsManaged (sb))
        {
          XEvent *event = mo_manufacture_dummy_event (sb);
          XtCallActionProc (sb, "IncrementUpOrLeft", event, params, 1);
        }
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "flushimagecache", 15))
    {
      mo_flush_image_cache (win);
    }
  else if (!strncmp (directive, "backnode", 8))
    {
      mo_back_node (win);
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "forwardnode", 11))
    {
      mo_forward_node (win);
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "reloaddocument", 14))
    {
      mo_reload_window_text (win, 0);
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "reloadimages", 12))
    {
      mo_reload_window_text (win, 1);
      XmUpdateDisplay (win->base);
    }
  else if (!strncmp (directive, "refresh", 7))
    {
      mo_refresh_window_text (win);
      XmUpdateDisplay (win->base);
    }

  return;
}


