/****************************************************************************
 * 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 "../config.h"
#include "../libwww2/HTAABrow.h"
#include "../libwww2/htalert.h"
#include "../libwww2/HTftp.h"
#include "mosaic.h"
#include "gui.h"
#include "gui-documents.h"
#include "main.h"
#include "mo-www.h"
#include "proxy.h"
#include "gui-menubar.h"
#include "pan.h"
#include "pixmaps.h"
#include "../libhtmlw/HTMLp.h"
#include "../libnut/system.h"
#include "../libnut/str-tools.h"
#include "history.h"
#include "gui-dialogs.h"
#include "globalhist.h"
#include "hotlist.h"
#include "ccibindings2.h"
#include "gui-news.h"
#include "annotate.h"
#include "comment.h"
#include <X11/Xatom.h>

#include <signal.h>

#ifndef MULTINET
#ifdef SOCKETSHR /* BGT */
#include "socketshr_files:types.h"
/* DECwindows xresource.h wants __CADDR_T, GEC */
#if defined (__DECC) && defined (CADDR_T)
#define __CADDR_T
#endif
#else
#include <sys/types.h>
#endif /* BGT */
#if defined(VMS) && !defined(__DECC)
#define CADDR_T      /* Used in Motif 1.1, __CADDR_T is Motif 1.2 */
#define __CADDR_T
#endif /* VMS, VAXC include file problem, BSN, PGE, GEC */
#ifndef VMS
#include <sys/socket.h>
#include <netinet/in.h>
#endif /* Cause logical name problems plus already included in tcp.h, GEC */
#if !defined(ultrix)
#include <netdb.h>
#endif
#endif /* MULTINET, BSN*/

#include <ctype.h>
#ifndef VMS
#include <sys/utsname.h>
#include <pwd.h>
#else
#include "vms_pwd.h"
#include <lib$routines.h>
#endif  /* PGE, GEC */

struct Proxy *noproxy_list = NULL, *proxy_list = NULL, *ReadProxies();

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

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

extern Pixmap *tmp_pix;

char pre_title[80];
int cursorAnimCnt;
int makeBusy = 0;

extern int installed_colormap;
extern Colormap installed_cmap;

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

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;
int do_splash;

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

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

extern int tableSupportEnabled;
extern int securityType;

extern int progressiveDisplayEnabled;

/*   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 VMS
#undef EDITRES_SUPPORT
#endif /* VMS Xmu has no Editres, BSN*/

#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 <Xm/Frame.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#if defined(VMS) && !defined(__DECC)
#include <X11/Intrinsicp.h>
#endif /* VAX C needs it to define _WidgetRec, GEC */


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


#include "bitmaps/iconify.xbm"
#include "bitmaps/iconify_mask.xbm"
#include "bitmaps/xmosaic_icon.xbm"
#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"

/* For selective image loading */
char **imagedelay_sites = NULL;
char **imagekill_sites = NULL;
Boolean currently_delaying_images = 0;

/*******************************/

/* 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;

void mo_post_upload_window(mo_window *win);

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

#ifdef VMS
extern unsigned long vms_mosaic_cmdline (int *, char ***);
#endif /* VMS DCL command line interface */
static void paste_init(XtAppContext app);

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 */

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;

char kioskProtocols[50][40]; /* 50 protocols 40 chars long should be plenty */
int max_kiosk_protocols = 0;

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

extern int do_comment;

#ifdef VMS  /* PGE, allows resource file have font lists */ 
/*
** Takes a string "fontname/fontname/.../fontname" and tries loading the fonts
** in order until one succeeds.  If none succeeds then returns fail.  Must be
** loaded with a call to XtSetTypeConverter before resource list is read.
*/
Boolean convertMultiFontStruct (Display   *display,
                                XrmValue  *args,
                                Cardinal  *num_args,
                                XrmValue  *src,
                                XrmValue  *dest,
                                XtPointer *converter_data)
{
   XFontStruct *font = NULL;
   char        *name = (char *)src->addr;
   char        *slash;

   slash = strchr (name, '/');
   if (slash == NULL) {
      font = XLoadQueryFont (display, name);
   }
   else {
      char  temp[200];
      char *slash2;
      int   count;

      while (name && (font == NULL)) {
         if (slash == NULL)
            count = 200;
         else
            count = slash - name + 5;
         strncpy(temp, name, (count > 200 ? 200 : count));
         temp[199] = '\0';
         slash2 = strchr(name, '/');
         if (slash2)
            *slash2 = '\0';

         font = XLoadQueryFont (display, temp);
         if (font == NULL) {
            if (slash) {
               name = slash + 1;
               slash = strchr (name, '/');
            } else {
               name = NULL;
            }
         }
      }
   }

   if (!font) {
      int count = 1;
      name = (char *)src->addr;
      XtAppWarningMsg (app_context, "", "", "",
                       "Cannot convert string\n\"%s\" to type FontStruct\n",
                       &name, (unsigned int *)&count);
      font = XLoadQueryFont (display, "fixed");
   }

   if (font) {
      /* If memory for result not allocated then do it ourselves */
      if (dest->addr == NULL) {
         dest->size = sizeof(XFontStruct *);
         dest->addr = (caddr_t) malloc (sizeof(XFontStruct *));
      }

      /* If memory for result not large enough then return required amount */
      if (dest->size < sizeof(XFontStruct *)) {
         dest->size = sizeof(XFontStruct *);
         return False;
      }

      /* Finally, return the result. */
      *((XFontStruct **) dest->addr) = font;
      return True;
   }
   else {
      return False;
   }
}

/*
** Same as convertMultiFontStruct except it returns an XmFontList instead of an
** XFontStruct.
*/
Boolean convertMultiFontList (Display   *display,
                              XrmValue  *args,
                              Cardinal  *num_args,
                              XrmValue  *src,
                              XrmValue  *dest,
                              XtPointer *converter_data)
{
   XFontStruct *font;
   XmFontList   fontList;
   Boolean      result;
   XrmValue     tempDest;

   tempDest.size = sizeof(XFontStruct *);
   tempDest.addr = (caddr_t)&font;
   result = convertMultiFontStruct (display, args, num_args,
                                    src, &tempDest, converter_data);

   if (result) {
      fontList = XmFontListCreate (font, XmSTRING_DEFAULT_CHARSET);
      if (fontList == NULL) {
         int count = 1;
         char *name = (char *)src->addr;
         XtAppWarningMsg (app_context, "", "", "",
                          "Cannot convert string \"%s\" to type FontList\n",
                          &name, (unsigned int *)&count);
         result = False;
      } else {
         /* If memory for result not allocated then do it ourselves */
         if (dest->addr == NULL) {
            dest->size = sizeof(XmFontList);
            dest->addr = (caddr_t) malloc (sizeof(XmFontList));
         }

         /* If memory for result not large enough then return required amount */
         if (dest->size < sizeof(XmFontList)) {
            dest->size = sizeof(XmFontList);
            return False;
         }

         /* Finally, return the result. */
         *((XmFontList *) dest->addr) = fontList;
      }
   }
   return result;
}
#endif

/* --------------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!",
 */

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

/* emacs bindings to be used in text fields */

static char text_translations[] = "\
           ~Meta ~Alt Ctrl<Key>u:	beginning-of-line()		\
					delete-to-end-of-line()		\n\
           ~Meta ~Alt Ctrl<Key>x:	beginning-of-line()		\
					delete-to-end-of-line()		\n\
           ~Meta ~Alt Ctrl<Key>k:	delete-to-end-of-line()		\n\
           ~Meta ~Alt Ctrl<Key>a:	beginning-of-line()		\n\
           ~Meta ~Alt Ctrl<Key>e:	end-of-line()   		\n\
           ~Meta ~Alt Ctrl<Key>w:	key-select()			\
					delete-selection()		\n\
           ~Meta ~Alt Ctrl<Key>y:	paste-clipboard()		\n\
	 Meta ~Ctrl       <Key>d:	delete-next-word()		\n\
	  Alt ~Ctrl       <Key>d:	delete-next-word()		\n\
           ~Meta ~Alt Ctrl<Key>d:       delete-next-character()         \n\
     Meta ~Ctrl<Key>osfBackSpace:	delete-previous-word()		\n\
      Alt ~Ctrl<Key>osfBackSpace:	delete-previous-word()		\n\
	Meta ~Ctrl<Key>osfDelete:	delete-next-word()		\n\
	 Alt ~Ctrl<Key>osfDelete:	delete-next-word()              \n\
                      <Btn1Down>:       take_focus() grab-focus()";

/* This will have to be handled dynamically when we go to preferences */

static char url_translations[] = "Ctrl<Key>z:         set_focus_to_view()";

static char logo_translations[] = "<Btn2Down>:\n <Btn2Up>: URLPaste()";

void set_focus_to_view();
void take_focus();
static XtActionsRec url_actions[] = {
    {"set_focus_to_view", (XtActionProc)set_focus_to_view},
    {"take_focus", (XtActionProc)take_focus}
};

/* This stuff is so we can properly update the current_win variable
   eliminating alot of problems with cloned windows (We love globals!)

   Globals? Where? There are no globals here! */

static char toplevel_translations[] = "\
                      <Enter>:            set_current_win() \n\
                      <Leave>:            set_current_win()";

void set_current_win();

static XtActionsRec toplevel_actions[] = {
    {"set_current_win", (XtActionProc)set_current_win}
};

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

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

int cciTrace;
int srcTrace;
int cacheTrace;
int reportBugs;
#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;

#ifdef VMS
/*
** Need to distinguish between commandline -home argument and Xresource file
** homeDocument setting so that commandline takes precedence over WWW_HOME
** environment variable.
*/
char *cmdline_homeDocument = NULL;
#endif /* VMS, LLL */

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);

#ifndef VMS
struct utsname mo_uname;
#endif /* VMS, GEC */

#ifdef VMS
extern unsigned long mbx_event_flag;
extern unsigned short mbx_iosb[4];
static char mbx_name[64];
static XtInputId mo_InputId;
static int use_mbx = 0;
static int grp_mbx = 0;
#endif /* VMS, BSN, TJA */


int kioskSafe(char *url)
{
    int i, psize;
    char *protocol, *ptr;

    if (!max_kiosk_protocols) {
	return(1); /* not supposed to check anything if 0 */
    }

    if (!(ptr = strchr(url, ':'))) {
	return(1); /* return safe...not a valid URL */
    }

    psize = ptr - url;
    protocol = (char *)malloc((psize+1)*sizeof(char));
    strncpy(protocol, url, psize);
    protocol[psize] = '\0';

    for (i=0; i < max_kiosk_protocols; i++) {
	if (!my_strcasecmp(protocol, kioskProtocols[i])) {
	    free(protocol);

	    return(1);
	}
    }

    free(protocol);

    return(0);
}

/* ----------------------------- 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)
    {
      if (win->id == id)
	return(win);
      win = win->next;
    }

  return NULL;
}

/* 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;
}

char *mo_assemble_help_url_vms (char *file)
{
  char *tmp;
  char *docs_directory = DOCS_DIRECTORY_VMS;

  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;
}


/* For lack of a better place, we do the iconify icon stuff here as well...
    --SWP */

void createBusyCursors(Widget bob) {

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

/*
XWMHints *whints = XAllocWMHints();

	imap = XCreatePixmapFromBitmapData
	(XtDisplay(toplevel), XtWindow(toplevel),
	 iconify_bits, iconify_width, iconify_height, 1, 0, 1);
	imaskmap = XCreatePixmapFromBitmapData
	(XtDisplay(toplevel), XtWindow(toplevel),
	 iconify_mask_bits, iconify_mask_width, iconify_mask_height, 1, 0, 1);

	whints->flags = IconPixmapHint|IconMaskHint;
	whints->icon_pixmap = imap;
	whints->icon_mask = imaskmap;
	XSetWMHints(XtDisplay(toplevel), XtWindow(toplevel), whints);
*/

	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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)), (char *)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)
{
  mo_window *win = mo_fetch_window_by_id (XmxExtractUniqid ((int)client_data));
  HTMLWidget hw = (HTMLWidget) win->scrolled_win;

  sleep_interrupt = connect_interrupt = 1;
  if (cci_event)
	MoCCISendEventOutput(MOSAIC_GLOBE);
  /* Stop animations unless we are still drawing the window */
  if (!hw->html.drawing)
	hw->html.draw_count++;
}


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;
  XmTextVerifyCallbackStruct *cbs = (XmTextVerifyCallbackStruct *) call_data;

  if (!get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
    XtSetKeyboardFocus(win->base, 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);

  if (max_kiosk_protocols && !kioskSafe(url)) {
    fprintf(stderr, "kioskMode: Protocol not specified as 'safe'.\n");
    application_user_info_wait("kioskMode: Protocol not specified as 'safe'.");

    return;
  } else {  /* V2.7b6 fix */
    mo_load_window_text(win, xurl, NULL);
  }

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

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


/* public anchor load to disk function -- hack for RBM ltd support */
void pub_anchor_ltd(char *linkurl) {

  char *href, *reftext;
  static char *referer = NULL;
  mo_window *win = current_win;
  int old_binx_flag;

  if (!win)
    return;

  old_binx_flag = win->binary_transfer;
  win->binary_transfer = 1;

  if (get_pref_boolean(eKIOSK) || 
      get_pref_boolean(eKIOSKNOEXIT) || 
      get_pref_boolean(eDISABLEMIDDLEBUTTON)) { 
    /* 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,
/*
 * VAX C has a line length limitation
 */
         "BEWARE: Despite our best and most strenuous intentions to the contrary,\n\
absolutely anything could be on the other end of this hyperlink,\nincluding -- quite possibly -- pornography, or even nudity.\n\n\
NCSA disclaims all responsibility regarding your emotional and mental health\nand specifically all responsibility for effects of viewing salacious material via Mosaic.\n\n\
With that in mind, are you *sure* you want to follow this hyperlink???",
         "Yup, I'm sure, really.", "Ack, no!  Get me outta here.");
/* The VAX C source line length limitation cannot be ifdefed around, GEC */
      if (!answer)
        return;
    }

  /* Free previous one, if any */
  if (referer)
    {
      free(referer);
      referer = NULL;
    }

  if (!my_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 (linkurl)
    href = strdup(linkurl);
  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);

  mo_load_window_text (win, href, reftext);

  win->binary_transfer = old_binx_flag;
  free (href);
  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;
  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;
  char *tmp;

  if (!win)
    return;

  /* Don't do refresh if user doesn't want them */
  if (((WbAnchorCallbackData *)call_data)->refresh && !win->refresh_url)
	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,
/*
 * VAX C has a line length limitation
 */
	"BEWARE: Despite our best and most strenuous intentions to the contrary,\n\
absolutely anything could be on the other end of this hyperlink,\nincluding -- quite possibly -- pornography, or even nudity.\n\n\
Mosaic disclaims all responsibility regarding your emotional and mental health\nand specifically all responsibility for effects of viewing salacious material via Mosaic.\n\n\
With that in mind, are you *sure* you want to follow this hyperlink???",
         "Yup, I'm sure, really.", "Ack, no!  Get me outta here.");
/* The VAX C source line length limitation cannot be ifdefed around, GEC */
      if (!answer)
        return;
    }

  if (referer)
    {
      free(referer);
      referer = NULL;
    }

  if (!my_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)
    if (((WbAnchorCallbackData *)call_data)->refresh)
      href = mo_url_canonicalize(((WbAnchorCallbackData *)call_data)->href,
				win->current_node->url);
    else
      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) {
    if (max_kiosk_protocols && !kioskSafe(href)) {
      fprintf(stderr, "kioskMode: Protocol not specified as 'safe'.\n");
      application_user_info_wait
        ("kioskMode: Protocol not specified as 'safe'.");
      if (cci_event) MoCCISendEventOutput(LINK_LOADED);
      win->binary_transfer = old_binx_flag;
      free (href);
      free (reftext);

      return;
    }
    /* Just reload current window if called by refresh timer and URL
     * is same as current one */
    if (((WbAnchorCallbackData *)call_data)->refresh &&
	!my_strcasecmp(href, win->current_node->url)) {
      mo_reload_window_text(win, 0);
      free (reftext);
    } else {
      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);

      if (max_kiosk_protocols && !kioskSafe(url)) {
        fprintf(stderr, "kioskMode: Protocol not specified as 'safe'.\n");
        application_user_info_wait(
		"kioskMode: Protocol not specified as 'safe'.");
	if (cci_event) MoCCISendEventOutput(LINK_LOADED);
	win->binary_transfer = old_binx_flag;
	free (href);
        free (reftext);
	free (url);
	free (target);

        return;
      }

      /* @@@ should we be keeping the anchor here??? */
      if (my_strncasecmp(url, "telnet:", 7) &&
          my_strncasecmp(url, "tn3270:", 7) &&
          my_strncasecmp(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);
	free(target);
      }
      free(url);
    }

  if (cci_event) MoCCISendEventOutput(LINK_LOADED);

  win->binary_transfer = old_binx_flag;
  free(href);	/* Note: reftext should not be freed here */
  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;

  if (get_pref_boolean(eTRACK_TARGET_ANCHORS))
    href = mo_url_canonicalize_keep_anchor(href, cached_url);
  else
    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, XtPointer clid, XtPointer calld)
{
  char *href = (char*) calld;
  mo_window * win = (mo_window*) clid;
  XmString xmstr;
  char *to_free = NULL, *to_free_2 = NULL;

  if (!get_pref_boolean(eTRACK_POINTER_MOTION))
    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;
/* Removed in mMosaic */
  /* 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);
  }
#endif

  if ((my_strcasecmp (method, "POST") == 0) ||
 	(my_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
    {
	/* 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 (!my_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 HTMLSettext. */
static XmxCallback (base_callback)
{
  mo_window *win = current_win;
  char *href = (char *)call_data;
  extern char *url_base_override;

  if (!*href)
    return;

  url_base_override = strdup(href);

  /* Set the URL cache variables to the correct values NOW. */
  cached_url = mo_url_canonicalize(url_base_override, "");
  win->cached_url = cached_url;

  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;
}


int logo_count = 0;
int logo_save = 0;

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

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

	cnt++;
	if (cnt == 2) {
		animateCursor();
		cnt = 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))
#ifdef VMS
			logo_count = 1;
#else
			logo_count = 0;
#endif /* VMS, GEC */
	}
  }
  
  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_apply_default_icon(void) {

mo_window *win = current_win;

	XmxApplyPixmapToLabelWidget(win->logo, IconPix[0]);
}


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

	if (tmp_pix) {
		IconPix = tmp_pix;
		logo_count = 0;
		set_pref(ePIX_COUNT, (void *)&logo_save);
		tmp_pix = NULL;
	}

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


void mo_presentation_mode(mo_window *win) {

	if (!pres) {
		pres = 1;
		scount = (-1);
		mo_duplicate_window(win);
		mo_delete_window(win);
	}
	else {
		pres = 0;
		scount = (-1);
		mo_duplicate_window(win);
		mo_delete_window(win);
	}
}


/****************************************************************************
 * 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 void mo_view_keypress_handler(Widget w, XtPointer clid,
	XEvent *event, Boolean *cont)
{
  mo_window *win = (mo_window*) clid;
  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. */
  case XK_KP_Prior:
      if (!get_pref_boolean(eCATCH_PRIOR_AND_NEXT)) break;
  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. */
  case XK_KP_Next:
      if (!get_pref_boolean(eCATCH_PRIOR_AND_NEXT)) break;
  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:
    if (!get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
       {
	 if (event->xkey.state & ControlMask)
	   {
	     XtSetKeyboardFocus(win->base, win->view);
	     HTMLTraverseTabGroups(win->view, XmTRAVERSE_HOME);
	   }
	 else
	   HTMLTraverseTabGroups(win->scrolled_win, XmTRAVERSE_NEXT_TAB_GROUP);
       }
    break;
      
  case XK_Home: /* Home -- Top */
      HTMLGotoId(win->scrolled_win, 0, 0);
      break;

  case XK_End: /* End -- Bottom */
      HTMLGotoId(win->scrolled_win, HTMLLastId(win->scrolled_win), 0);
      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);
	  break;

      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:
	if (event->xkey.state & ControlMask)
	  {
	    if (XtIsManaged(win->slab[SLAB_URL]) && 
	       !get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
	      {
		XmTextFieldSetString(win->url_text, "ftp://");
		XtSetKeyboardFocus(win->base, win->url_text);
		XmTextSetInsertionPosition(win->url_text, 7);
	      }
	  }
	else
          mo_forward_node (win);
          break;
  
      case XK_H: /* Hotlist */
          mo_post_hotlist_win (win);
          break;

      case XK_h: /* History */
	if (event->xkey.state & ControlMask)
	  {
	    if (XtIsManaged(win->slab[SLAB_URL]) && 
	       !get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
	      {
		XmTextFieldSetString(win->url_text, "http://");
		XtSetKeyboardFocus(win->base, win->url_text);
		XmTextSetInsertionPosition(win->url_text, 8);
	      }
	  }
	else
          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:
	if (event->xkey.state & ControlMask)
	  {
	    if (XtIsManaged(win->slab[SLAB_URL]) && 
	       !get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
	      {
		XtSetKeyboardFocus(win->base, win->url_text);
		
		XmTextFieldSetString(win->url_text, "news:");
		XmTextSetInsertionPosition(win->url_text, 8);
	      }
	  }        
	else
	  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:
/*
	  if (event->xkey.state & ControlMask) {
		mo_presentation_mode(win);
	  }
	  else
*/
	  {
		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;
          */

/* Not active */
#ifdef SWP_NOT_DONE
      case XK_U: /* Upload a file (method of put) */
      case XK_u:
	  mo_post_upload_window(win);
	  break;
#endif

      case XK_Z:
      case XK_z:
	if (XtIsManaged(win->slab[SLAB_URL]) && 
	   !get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
	  {
	    char *str;
	    
	    str = XmTextFieldGetString(win->url_text);
	    XmTextFieldSetSelection(win->url_text, 0, strlen(str), 
				    event->xkey.time);

	    XtSetKeyboardFocus(win->base, win->url_text);
	    XtSetKeyboardFocus(win->base, win->url_text);
	    XtFree(str);
	  }
	break;


      case XK_Escape:
            mo_delete_window (win);
            break;

      default:
          break;
      }
  }
  else { /* Kiosk */
      switch (_key) {
	case XK_B: /* Back. */
	case XK_b:
          mo_back_node (win);
          break;

	case XK_F: /* Forward */
	case XK_f:
          mo_forward_node (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) {
			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;

		}
	}

	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_bg);
		XDrawString(XtDisplay(meter), XtWindow(meter), gc, 
			    (win->meter_width/2-(win->meter_fontW*l)/2)+2,
			    ((win->meter_height/2)+(win->meter_fontH/2)), 
			    ss, l);
		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))-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);
}

/* 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 (get_pref_boolean(eKIOSK)) {
		if (ptr = get_pref_string(eTOOLBAR_LAYOUT)) {
			fprintf(stderr,
			  "Toolbar Resource is Overiding the Kiosk Toolbar.\n");
		}
		else if (get_pref_boolean(eKIOSKPRINT)) {
			ptr = strdup("BACK,FORWARD,HOME,CLOSE,PRINT");
		}
		else {
			ptr = strdup("BACK,FORWARD,HOME,CLOSE");
		}
	}
	else if (get_pref_boolean(eKIOSKNOEXIT)) {
		if (ptr = get_pref_string(eTOOLBAR_LAYOUT)) {
			fprintf(stderr,
			  "Toolbar Resource is Overiding the Kiosk Toolbar.\n");
		}
		else if (get_pref_boolean(eKIOSKPRINT)) {
			ptr = strdup("BACK,FORWARD,HOME,PRINT");
		}
		else {
			ptr = strdup("BACK,FORWARD,HOME");
		}
	}
	else 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);            
        }
        h = win->toolbarorientation?640:40;
        w = win->toolbarorientation?40:640;
        win->toolbarwin = XtVaCreatePopupShell
            ("ToolBox",
/*             topLevelShellWidgetClass,*/
             xmDialogShellWidgetClass,
             win->base,
             XmNminHeight, h,
             XmNminWidth, w,
             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",
		  XmNtraversalOn, False,
		  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)",
	 XmNtraversalOn, False,
         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,
		  XmNtraversalOn, False,
                  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);
            }           
        }
    }
    if (get_pref_boolean(eKIOSK) || 
        get_pref_boolean(eKIOSKNOEXIT) || 
        get_pref_boolean(eDISABLEMIDDLEBUTTON)) { 
        XtVaSetValues(win->logo,
		XmNuserData,
		(XtPointer) "Logo Button - Abort current transfer or animations",
                NULL);
    } else {
        XtVaSetValues(win->logo,
                XmNuserData,
		(XtPointer) "Abort current transfer or animations (button 1) or Paste URL (middle button)",
                NULL);
        XtOverrideTranslations(win->logo,
                XtParseTranslationTable(logo_translations));
    }

    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\nDefault font is: -adobe-times-bold-r-normal-*-12-*-*-*-*-*-iso8859-1\nExiting 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,
	 XmNtraversalOn, False,
         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,
		 XmNtraversalOn, False,
                 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,
		 XmNtraversalOn, False,
                 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;
  static char *pres_slab = NULL;
  static char *kiosk_slab = NULL;
  
  form = XtVaCreateManagedWidget("form0", xmFormWidgetClass, win->base, NULL);

  if (scount<0 && pres) {
	if (!pres_slab) {
		pres_slab = strdup("VIEW");
	}
	s = pres_slab;
  }
  else {
	if (s = get_pref_string(eGUI_LAYOUT)) {
		if (get_pref_boolean(eKIOSK) || get_pref_boolean(eKIOSKNOEXIT)) {
			fprintf(stderr, "The Gui Layout Resource is Overiding the Kiosk Resource.\n");
		}
	}
	else if (get_pref_boolean(eKIOSK) || get_pref_boolean(eKIOSKNOEXIT)) { /*we be kiosking*/
		if (!kiosk_slab) {
			kiosk_slab = strdup("TOOLS,STATUS,VIEW");
		}
		s = kiosk_slab;
	}
  }

  if ((scount < 0) && s) {
      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,
					  XmNtraversalOn, False,
                                          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);
  }

  XtOverrideTranslations(win->url_text,
			 XtParseTranslationTable(text_translations));
  XtOverrideTranslations(win->title_text,
			 XtParseTranslationTable(text_translations));
  XtOverrideTranslations(win->url_text,
			 XtParseTranslationTable(url_translations));


      /*********************** SLAB_VIEW ****************************/
  win->slab[SLAB_VIEW] = win->scrolled_win = XtVaCreateManagedWidget
      ("view", htmlWidgetClass, form,
       WbNtext, 0,
       XmNresizePolicy, XmRESIZE_ANY,
       WbNpreviouslyVisitedTestFunction, anchor_visited_predicate,
       WbNfancySelections, win->pretty ? True : False,
       WbNdelayImageLoads, win->delay_image_loads ? True : False,
       XmNshadowThickness, 2,
       NULL);
  XtAddCallback(win->scrolled_win, WbNimageCallback, ImageResolve, win);
  XtAddCallback(win->scrolled_win, WbNpointerMotionCallback,
		  pointer_motion_callback, win);
  XmxAddCallback (win->scrolled_win, WbNanchorCallback, anchor_cb, 0);
  XmxAddCallback (win->scrolled_win, WbNbaseCallback, base_callback, 0);
  XmxAddCallback (win->scrolled_win, WbNsubmitFormCallback,
                  submit_form_callback, 0);
  XtVaGetValues(win->scrolled_win, WbNview, (long)(&win->view), NULL);
  XtAddEventHandler(win->view, KeyPressMask, False,
		 mo_view_keypress_handler, win);
 /* 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);

  if (get_pref_boolean(eKIOSK) || get_pref_boolean(eKIOSKNOEXIT)) {
	set_pref_boolean(eMETER, False);
  }

  /* 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;
          XAllocNamedColor(dsp, (installed_colormap ?
                                installed_cmap :
                                DefaultColormapOfScreen(XtScreen(win->base))),
                           get_pref_string(eMETER_FONT_BACKGROUND),
                           &ccell1, &ccell2);
          win->meter_font_bg = 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_WIDGET : XmATTACH_NONE,
       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;
  HTMLWidget hw;

  if (!win)
    return mo_fail;

  hw = (HTMLWidget) win->scrolled_win;
  /* Stop animations */
  hw->html.draw_count++;

  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);
  win->base = NULL;

  /* Free up some of the HTML Widget's state */

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

  /* 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;
}
                  

void mo_set_agents(mo_window *win, int which);

void mo_sync_windows(mo_window *win, mo_window *parent)
{

    win->font_size = parent->font_size;
    mo_set_fonts(win, parent->font_size);

    win->underlines_state = parent->underlines_state;
    mo_set_underlines (win, parent->underlines_state);

    win->agent_state = parent->agent_state;
    mo_set_agents(win, win->agent_state);

    imageViewInternal = win->image_view_internal = parent->image_view_internal;
    XmxRSetToggleState (win->menubar, mo_image_view_internal,
                      (win->image_view_internal ? XmxSet : XmxNotSet));
    
    win->preferences = parent->preferences;
    XmxRSetToggleState (win->menubar, mo_preferences,
                        win->preferences ? XmxSet : XmxNotSet);

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

    win->refresh_url = parent->refresh_url;
    XmxRSetToggleState (win->menubar, mo_refresh_url,
                        win->refresh_url ? XmxSet : XmxNotSet);

    progressiveDisplayEnabled = win->progressive_loads =
			parent->progressive_loads;
    XmxRSetToggleState (win->menubar, mo_progressive_loads,
                        win->progressive_loads ? XmxSet : XmxNotSet);

    win->image_animation = parent->image_animation;
    XtVaSetValues(win->scrolled_win,
                  WbNimageAnimation,
                  win->image_animation,
                  NULL);
    XmxRSetToggleState (win->menubar, mo_animate_images,
                        win->image_animation ? XmxSet : XmxNotSet);

    win->min_animation_delay = parent->min_animation_delay;

    win->body_color = parent->body_color;
    XtVaSetValues(win->scrolled_win,
                  WbNbodyColors,
                  win->body_color,
                  NULL);
    XmxRSetToggleState (win->menubar, mo_body_color,
                        win->body_color ? XmxSet : XmxNotSet);

    win->body_images = parent->body_images;
    XtVaSetValues(win->scrolled_win,
                  WbNbodyImages,
                  win->body_images,
                  NULL);
    XmxRSetToggleState (win->menubar, mo_body_images,
                        win->body_images ? XmxSet : XmxNotSet);

    win->font_color = parent->font_color;
    XtVaSetValues(win->scrolled_win,
                  WbNfontColors,
                  win->font_color,
                  NULL);
    XmxRSetToggleState (win->menubar, mo_font_color,
                        win->font_color ? XmxSet : XmxNotSet);

    win->font_sizes = parent->font_sizes;
    XtVaSetValues(win->scrolled_win,
                  WbNfontSizes,
                  win->font_sizes,
                  NULL);
    XmxRSetToggleState (win->menubar, mo_font_sizes,
                        win->font_sizes ? XmxSet : XmxNotSet);

    XtVaSetValues(win->scrolled_win,
                  WbNfontBase,
                  get_pref_int(eFONTBASESIZE),
                  NULL);

    win->delay_image_loads = parent->delay_image_loads;
    XmxSetArg (WbNdelayImageLoads, win->delay_image_loads ? True : False);
    XmxSetValues (win->scrolled_win);
    XmxRSetSensitive (win->menubar, mo_expand_images_current,
                      win->delay_image_loads ? XmxSensitive : XmxNotSensitive);
    XmxRSetToggleState (win->menubar, mo_delay_image_loads,
                        win->delay_image_loads ? XmxSet : XmxNotSet);

#ifndef DISABLE_TRACE
    if (get_pref_boolean(eDEBUG_MENU)) {
	XmxRSetToggleState (win->menubar, mo_trace_cache,
                        cacheTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_cci,
                        cciTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_html,
                        htmlwTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_http,
                        httpTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_nut,
                        nutTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_src,
                        srcTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_table,
                        tableTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_www2,
                        www2Trace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_refresh,
                        refreshTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_report_bugs,
                        reportBugs ? XmxSet : XmxNotSet);
    }
#endif
}

/****************************************************************************
 * 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.).
 ****************************************************************************/
/* FOO */
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->upload_win = 0;
  win->savebinary_win = 0;
  win->ftpput_win = win->ftpremove_win = win->ftpmkdir_win = 0;

  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->src_search_win = 0;
  win->src_search_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));
  } 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)
    if (!get_pref_boolean(eUSE_PREFERENCES)) {
      win->underlines_state = mo_default_underlines;
    }
    else if (!my_strcasecmp(get_pref_string(eDEFAULTUNDERLINES),"Default")) {
      win->underlines_state = mo_default_underlines;      
    }
    else if (!my_strcasecmp(get_pref_string(eDEFAULTUNDERLINES),"Light")) {
      win->underlines_state = mo_l1_underlines;      
    }
    else if (!my_strcasecmp(get_pref_string(eDEFAULTUNDERLINES),"Medium")) {
      win->underlines_state = mo_l2_underlines;      
    }
    else if (!my_strcasecmp(get_pref_string(eDEFAULTUNDERLINES),"Heavy")) {
      win->underlines_state = mo_l3_underlines;      
    }
    else if (!my_strcasecmp(get_pref_string(eDEFAULTUNDERLINES), "No")) {
      win->underlines_state = mo_no_underlines;      
    }
    else {
      fprintf(stderr,
	"Error: Preference DEFAULTUNDERLINES has invalid value '%s'\n",
	get_pref_string(eDEFAULTUNDERLINES)); 
      fprintf(stderr, "       Using default underlines\n"); 
      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));
  win->src_search_pos = 0;

#ifdef ISINDEX
  /* We don't know yet. */
  win->keyword_search_possible = -1;
#endif
  
  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);

  win->image_animation = get_pref_boolean(eIMAGE_ANIMATION);
  XtVaSetValues(win->scrolled_win,
                  WbNimageAnimation,
                  win->image_animation,
                  NULL);
  win->min_animation_delay = get_pref_int(eMIN_ANIMATION_DELAY);

  win->body_color = get_pref_boolean(eBODYCOLORS);
  XtVaSetValues(win->scrolled_win,
                  WbNbodyColors,
                  win->body_color,
                  NULL);

  win->body_images = get_pref_boolean(eBODYIMAGES);
  XtVaSetValues(win->scrolled_win,
                  WbNbodyImages,
                  win->body_images,
                  NULL);

  win->font_color = get_pref_boolean(eFONTCOLORS);
  XtVaSetValues(win->scrolled_win,
                  WbNfontColors,
                  win->font_color,
                  NULL);

  win->font_sizes = get_pref_boolean(eFONTSIZES);
  XtVaSetValues(win->scrolled_win,
                  WbNfontSizes,
                  win->font_sizes,
                  NULL);
  XtVaSetValues(win->scrolled_win,
                  WbNfontBase,
                  get_pref_int(eFONTBASESIZE),
                  NULL);
  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->have_focus = False;

  win->binary_transfer = 0;
  XmxRSetToggleState (win->menubar, mo_binary_transfer,
                      (win->binary_transfer ? XmxSet : XmxNotSet));
  win->binary_ftp_mode = 1;
  set_pref_boolean(eFTP_BINARY_MODE, win->binary_ftp_mode);
  XmxRSetToggleState (win->menubar, mo_binary_ftp_mode,
                      (win->binary_ftp_mode ? 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);

  win->preferences = get_pref_boolean(eUSE_PREFERENCES);
  XmxRSetToggleState (win->menubar, mo_preferences,
                      (win->preferences ? XmxSet : XmxNotSet));

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

  win->refresh_url = get_pref_boolean(eREFRESH_URL);
  XmxRSetToggleState (win->menubar, mo_refresh_url,
                      (win->refresh_url ? XmxSet : XmxNotSet));

  progressiveDisplayEnabled = win->progressive_loads =
			get_pref_boolean(ePROGRESSIVE_DISPLAY);
  XmxRSetToggleState (win->menubar, mo_progressive_loads,
                      (win->progressive_loads ? XmxSet : XmxNotSet));

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

#ifndef DISABLE_TRACE
    if (get_pref_boolean(eDEBUG_MENU)) {
	XmxRSetToggleState (win->menubar, mo_trace_cache,
                        cacheTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_cci,
                        cciTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_html,
                        htmlwTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_http,
                        httpTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_nut,
                        nutTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_src,
                        srcTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_table,
                        tableTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_www2,
                        www2Trace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_trace_refresh,
                        refreshTrace ? XmxSet : XmxNotSet);
	XmxRSetToggleState (win->menubar, mo_report_bugs,
                        reportBugs ? XmxSet : XmxNotSet);
    }
#endif

  /* Take care of session history for rbm */

  if (get_pref_boolean(eSESSION_HISTORY_ON_RBM)) {
      win->session_menu = NULL;
      win->num_session_items = 0;
      win->session_items = malloc(sizeof(Widget) * 
		get_pref_int(eNUMBER_OF_ITEMS_IN_RBM_HISTORY));
  }

  /* 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. */
  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);

  if (parent) {
#ifndef DISABLE_TRACE
    if (srcTrace) {
      fprintf(stderr, "Window SYNCing\n");
    }
#endif
      mo_sync_windows(win, parent);
  }
  
  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];
  Pixmap icon_pixmap;

#ifndef VMS
  sprintf(pre_title, "NCSA X Mosaic %s", MO_VERSION_STRING);
#else
  sprintf(pre_title, "VMS Mosaic %s", MO_VERSION_STRING);
#endif /* VMS, GEC */
  sprintf(buf, "%s: ", pre_title);
  XmxSetArg (XmNtitle, (long)buf);
  XmxSetArg (XmNiconName, (long)"Mosaic");
  XmxSetArg (XmNallowShellResize, False);

  icon_pixmap = XCreateBitmapFromData(dsp, XDefaultRootWindow(dsp),
    xmosaic_icon_bits, xmosaic_icon_width, xmosaic_icon_height);
  XmxSetArg (XmNiconPixmap, icon_pixmap);

  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

  XtOverrideTranslations(base, XtParseTranslationTable(toplevel_translations));


  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", 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 = NULL;

  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 (!my_strncasecmp (url, "telnet:", 7) ||
      !my_strncasecmp (url, "tn3270:", 7) ||
      !my_strncasecmp (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);
#else /* BSN */
extern void ProcessExternalDirective();
extern void InitExternalDirective();
#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;
  char *tmp;

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

  /* Value of environment variable WWW_HOME overrides that. */
  if (home_opt = getenv("WWW_HOME"))
    home_document = strdup(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);
  }

#ifdef VMS
  /* Value specified on command line overrides everything. */
  if (cmdline_homeDocument) {
    free(home_document);
    home_document = cmdline_homeDocument;
  }
#endif /* VMS, LLL */

  /* 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, ":")) {
    tmp = home_document;
    home_document = mo_url_canonicalize_local(home_document);
    free(tmp);
  }

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

  /* 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);

  /* Get rid of it */
  if (do_splash && splash) {
          kill_splash();
  }

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

  free(init_document);

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

  XmxRSetToggleState (win->menubar, mo_animate_images,
                      (win->image_animation ? XmxSet : XmxNotSet));

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

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

  XmxRSetToggleState (win->menubar, mo_font_color,
                      (win->font_color ? XmxSet : XmxNotSet));

  XmxRSetToggleState (win->menubar, mo_font_sizes,
                      (win->font_sizes ? XmxSet : XmxNotSet));

  /* Set focus policy of HTMLWidget according to preferences */
  HTMLSetFocusPolicy(win->scrolled_win, get_pref_boolean(eFOCUS_FOLLOWS_MOUSE));

  if (get_pref_boolean(eFOCUS_FOLLOWS_MOUSE))
    XtVaSetValues(toplevel, XmNkeyboardFocusPolicy, XmPOINTER, NULL);

/*
 * Setup remote control.  Since a remote control command can come in
 * any time afterwards, we must have everything else ready.
 */
#ifndef VMS
    signal (SIGUSR1, (void *)ProcessExternalDirective);
#else
  if (use_mbx) {
    int i;
/*
 * Reserve event flag and initalize mailbox on VMS for remote control.
 */
    i = lib$free_ef(&mbx_event_flag);
    i = lib$reserve_ef(&mbx_event_flag);
    if (i & 1) {
      mo_InputId = XtAppAddInput(app_context, mbx_event_flag, mbx_iosb,
        ProcessExternalDirective, NULL);
      InitExternalDirective(grp_mbx, mbx_name);
    }
  }
#endif /* VMS, BSN, TJA */

  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) ||
      /* VMS: BadWindow error on XGetWindowAttributes is also ignored.
       * D. Potterveld, Argonne National Lab.
       */
      (event->error_code == BadWindow && event->request_code == 3) ||
      (event->error_code == BadAccess && event->request_code == 88))
    return 0;
  else 
    {
      /* 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);
      {char ans[10]; printf("Caught X Error -- Press RETURN\n"); gets(ans);}

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


/****************************************************************************
 * name:    setup_imagekill
 * purpose: Read the imagekill file if it exists and fill in the
 *          imagekill_sites array
 * 
 * returns: 
 *   nothing
 * remarks: 
 *   
 ****************************************************************************/
#ifndef VMS
#define IMAGESELECT_FILENAME ".mosaic-imageselect-sites"
#else
#define IMAGESELECT_FILENAME "mosaic-imageselect-sites."
#endif   /* VMS file name, PGE */

void setup_imagekill(void) {

    char *home_ptr, *home;
    struct passwd *pwdent;
    char imageselect_file_pathname[512];
    FILE *fp;
    long i, j, cnt, num_delay_sites = 0, num_kill_sites = 0;
    char buf[512];


    if (get_home(&home) != 0 || !home) {
		fprintf(stderr, "home: Could not get your home directory.\n");
		return;
	}
    
#ifndef VMS
    sprintf(imageselect_file_pathname, "%s/%s",
#else
    sprintf(imageselect_file_pathname, "%s%s",
#endif   /* VMS file name, PGE */
            home, IMAGESELECT_FILENAME);

    free(home);

    /* Check to see if the file exists. If it doesn't, then exit */

    if (!file_exists(imageselect_file_pathname))
        return;

    /* try to open it */
    if (!(fp = fopen(imageselect_file_pathname, "r"))) {

        fprintf(stderr,
#ifndef VMS
                "Error: Can't open .mosaic-imageselect-sites file for reading\n");
#else
                "Error: Can't open mosaic-imageselect-sites. file for reading\n");
#endif
        return;
    }

        /* read it */

    while (!(fgets(buf, 512, fp) == NULL)) {
        if (buf[0] == '#' || buf[0] == '\n')
            continue;
        else if (buf[0] == 'd' || buf[0] == 'D')
            num_delay_sites++;
        else if (buf[0] == 'k' || buf[0] == 'K')
            num_kill_sites++;
    }
    
    rewind(fp);

    imagekill_sites = (char **)malloc((num_kill_sites+1) * sizeof(char *));
    imagedelay_sites = (char **)malloc((num_delay_sites+1) * sizeof(char *));

    if (imagekill_sites == NULL)
        return;
    if (imagedelay_sites == NULL) {
        free(imagekill_sites);
        return;
    }

    i=j=0;
    
    while (!(fgets(buf, 512, fp) == NULL)) {

        int len;
        
        
        if (buf[0] == '#' || buf[0] == '\n')
            continue;
        
        else if (buf[0] == 'd' || buf[0] == 'D') {
            len = strlen(buf) - 6 - 1; /* 6 == strlen("delay ") */
            imagedelay_sites[i] = NULL;
            imagedelay_sites[i] = (char *)malloc((len+1) * sizeof(char));
            strncpy(imagedelay_sites[i], buf+6, len);
            imagedelay_sites[i][len] = '\0';
            i++;
        }
        
        else if (buf[0] == 'k' || buf[0] == 'K') {
            
            len = strlen(buf) - 5 - 1; /* 5 == strlen("kill ") */
            imagekill_sites[j] = NULL;
            imagekill_sites[j] = (char *)malloc((len+1) * sizeof(char));
            strncpy(imagekill_sites[j], buf+5, len);
            imagekill_sites[j][len] = '\0';
            j++;
        }
    }
        
    imagedelay_sites[i] = NULL;
    imagekill_sites[j] = NULL;

    
    fclose(fp);
    
    return;
    
}



/* 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 VMS
  int j;

#define SYI$_HW_NAME 4362
#define SYI$_VERSION 4096

  int syi_hw_name = SYI$_HW_NAME;
  int syi_version = SYI$_VERSION;
  char hardware[32], VMS_version[16], *cp;
  int status;
  unsigned short l_hardware, l_version;

struct  dsc$descriptor_s
{
  unsigned short  dsc$w_length;
  unsigned char   dsc$b_dtype;
  unsigned char   dsc$b_class;
  char            *dsc$a_pointer;
} hardware_desc = {sizeof(hardware), 14, 1, NULL},
  VMS_version_desc = {sizeof(VMS_version), 14, 1, NULL};
  int no_preferences = 0;
#endif /* VMS, BSN, PGE, GEC */
#ifdef MONO_DEFAULT
    int use_color = 0;
#else
    int use_color = 1;
#endif
    int no_defaults = 0;
    int color_set = 0;
#ifndef VMS   /* PGE */
    char* display_name = getenv("DISPLAY");
#else
    char* display_name = "\0";
#endif
    Display* dpy;
    XrmDatabase intDB, appDB;
    Widget intWidget;
    int i;
    char *tptr, *ptr, *eptr;
        /* for prefs - DXP */
    Boolean successful;
    prefsStructP thePrefsStructP;

#ifdef VMS
    hardware_desc.dsc$a_pointer = hardware;
    VMS_version_desc.dsc$a_pointer = VMS_version;
    mbx_name[0] = '\0';

    /* Handle DCL style command line, GEC */
    status = vms_mosaic_cmdline(&argc, &argv);
    if (!(status & 1)) exit (1);
#endif /* VMS */

  /* Loop through the args before passing them off to
     XtAppInitialize() in case we need to catch something first. */
    for (i = 1; i < argc; i++)
    {
#ifndef VMS
        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;
	}
#else
       if (!strcmp (argv[i], "-mono")) {
        use_color = 0;
        color_set = 1;
      } else if (!strcmp (argv[i], "-color")) {
        use_color = 1;
        color_set = 1;
      } else if (!strcmp (argv[i], "-nd")) {
        no_defaults = 1;
      } else if (!strcmp (argv[i], "-display")) {
        if (argv[i + 1] == NULL) {
	  fprintf(stderr, "-display argument missing.\n");
	  return;
        }
        display_name = argv[i + 1];
        i++;
      } else if(!strcmp(argv[i], "-geometry")) {
        userSpecifiedGeometry = 1;
      } else if (!strcmp (argv[i], "-install")) {
	installed_colormap = 1;
        argv[i] = NULL;
      } else if (!strcmp (argv[i], "-iconic")) {
	splash_cc = 0;
      } else if (!strcmp (argv[i], "-nopref")) {
        no_preferences = 1;
        argv[i] = NULL;
      } else if (!strcmp (argv[i], "-mbx")) {
        use_mbx = 1;
        argv[i] = NULL;
      } else if (!strcmp (argv[i], "-mbx_grp")) {
        use_mbx = 1;
        grp_mbx = 1;
        argv[i] = NULL;
      } else if (!strcmp (argv[i], "-mbx_name")) {
        if (argv[i + 1] == NULL) {
	  fprintf(stderr, "-mbx_name argument missing.\n");
	  return;
        }
        use_mbx = 1;
        strcpy (mbx_name, argv[i + 1]);
        argv[i] = NULL;
        argv[i+1] = NULL;
        i++;
/*
 *    Need to distinguish between Xresource homepage and
 *    commandline homepage; set a pointer to the commandline
 *    argument if present.  LLL
 */
      } else if (!strcmp (argv[i], "-home")) {
        if (argv[i + 1] == NULL) {
	  fprintf(stderr, "-home argument missing.\n");
	  return;
        }
        cmdline_homeDocument = strdup (argv[i + 1]);   
        argv[i] = NULL;                                   
        argv[i+1] = NULL;
        i++;                                  
      }                                    
#endif /* VMS, BSN, GEC, ML */
    }
#ifdef VMS
/*
 * Remove used arguments.
 */
  j = 0;
  for (i = 1; i < argc; i++) {
    if (argv[i] == NULL) continue;
    j++;
    argv[j] = argv[i];
  }
  argc = j + 1;
#endif /* VMS, BSN */


#ifdef VMS  /* PGE, register to allow multiple fonts in resource file */
    /* For some reason it has to be here and later, after XtAppInitialize */
    XtSetTypeConverter (XtRString, XtRFontStruct,
                        &convertMultiFontStruct,
                        NULL, 0, XtCacheAll, NULL);
    XtSetTypeConverter (XtRString, XmRFontList,
                        &convertMultiFontList,
                        NULL, 0, XtCacheAll, NULL);
#endif
        /* Motif setup. */
    XmxStartup ();
    XmxSetArg (XmNwidth,1);
    XmxSetArg (XmNheight,1);
    XmxSetArg (XmNmappedWhenManaged, False);
    /*
     * Awful expensive to open and close the display just to find
     * the 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));

	return;
    }

    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);
        }
    }

#ifdef VMS  /* PGE, register to allow multiple fonts in resource file */
    paste_init(app_context); /* Paste URL, GEC */
    XtAppSetTypeConverter (app_context, XtRString, XtRFontStruct,
                           &convertMultiFontStruct,
                           NULL, 0, XtCacheAll, NULL);
    XtAppSetTypeConverter (app_context, XtRString, XmRFontList,
                           &convertMultiFontList,
                           NULL, 0, XtCacheAll, NULL);
#endif

    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);
#ifndef VMS
        abort ();
#else
	exit (44);
#endif /* VMS, GEC */
    }

    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 */
    if (!no_preferences) {
        successful = read_preferences_file(NULL);
        if (!successful) {
            signal(SIGBUS, 0);
            signal(SIGSEGV, 0);
            signal(SIGILL, 0);
#ifndef VMS
            abort ();
#else
	    exit (44);
#endif /* VMS, GEC */
        }
    }

  {
	char *serverName;

        serverName = ServerVendor(dsp);
        /* Force enable of max_clip_transitions if a VAX X-server */
        if ((strstr(serverName, "DECWINDOWS") &&
               strstr(serverName, "VAX"))
        /* Force enable of max_clip_transitions if a Multia X-server */
           || (strstr(serverName, "DECWINDOWS") &&
               strstr(serverName, "eXcursion"))
        /* Force enable of max_clip_transitions if a VXT2000 X-server */
           || (strstr(serverName, "DECWINDOWS") &&
               strstr(serverName, "VXT 2000"))
           || ((strcmp(serverName, "DECWINDOWS DigitalEquipmentCorp.") == 0) &&
               (VendorRelease(dsp) == 11))
           )
        {
                int tmp;
                tmp = get_pref_int(eMAX_CLIP_TRANSITIONS);
                if (tmp < 0) {
                        tmp = 2048;
                        set_pref(eMAX_CLIP_TRANSITIONS, (void *)&tmp);
                }
        }
  }

    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, pixmaps.c, etc. */
    {
        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);

#ifndef VMS
    uname(&mo_uname);
#else
    status = lib$getsyi((void *)&syi_hw_name, 0, &hardware_desc, &l_hardware, 0, 0);
    status = lib$getsyi((void *)&syi_version, 0, &VMS_version_desc, &l_version, 0, 0);
    hardware[l_hardware] = '\0';
    VMS_version[l_version] = '\0';
    for (cp = &VMS_version[l_version-1]; VMS_version; cp--) {
      if (*cp != ' ') break;
      *cp = '\0';
    }
#endif /* VMS, GEC */
    HTAppVersion = 
        (char *)malloc (sizeof(char) * (
            strlen(MO_VERSION_STRING) +
#ifndef VMS
            strlen(mo_uname.sysname) + 
            strlen(mo_uname.release) +
            strlen(mo_uname.machine) + 20));
#else
            strlen("OpenVMS") +
            strlen(VMS_version) +
            strlen(hardware) + 20));
#endif /* VMS, GEC */
    sprintf(HTAppVersion, "%s (X11;%s %s %s)", 
            MO_VERSION_STRING,
#ifndef VMS
            mo_uname.sysname,
            mo_uname.release,
            mo_uname.machine);
#else
            "OpenVMS",
            VMS_version,
            hardware);
#endif /* VMS, GEC */

    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) {
	do_splash = 0;
    } else {
	do_splash = get_pref_boolean(eSPLASHSCREEN);
    }

splash_goto:

    if (do_splash) {
        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 {
#if defined(VMS) && !defined(__DECC) && !defined(__GNUC__)
            x = (war.width/2) - (126/2);
            y = (war.height/2) - (126/2);
#else
            x = (war.width/2) - (320/2);
            y = (war.height/2) - (320/2);
#endif /* VAX C one is smaller, GEC */
        }

            /* 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");
		do_splash = 0;
		goto splash_goto;
	}

        fontW = font->max_bounds.rbearing;
        fontH = font->max_bounds.ascent + font->max_bounds.descent;
      
        splash = XtVaCreatePopupShell
            ("Hello, World!",
             xmMenuShellWidgetClass,
             toplevel,
#if defined(VMS) && !defined(__DECC) && !defined(__GNUC__)
             XmNheight, 126,
             XmNwidth, 126,
#else
             XmNheight, 320,
             XmNwidth, 320,
#endif /* VAX C one is smaller, GEC */
             XmNx, x,
             XmNy, y,
             XmNallowShellResize, FALSE,
             NULL);
      
        splash_cc = 180;
        splashpix = LoadSplashXPM(splash, &splash_cc);
	if (!splash_cc) {
		XtDestroyWidget(splash);
		do_splash = 0;
		goto splash_goto;
	}

        sprintf(s, "version %s", MO_VERSION_STRING);
      
        l = strlen(s);
      
        sform = XtVaCreateManagedWidget("sform", xmRowColumnWidgetClass,
                                        splash,
#if defined(VMS) && !defined(__DECC) && !defined(__GNUC__)
                                        XmNheight, 126,
                                        XmNwidth, 126,
#else
                                        XmNheight, 320,
                                        XmNwidth, 320,
#endif /* VAX C  one is smaller, GEC */
                                        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);
      
#if !defined(VMS) || defined(__DECC) || defined(__GNUC__)
        XDrawString(dsp, splashpix, gc, 
                    320 - (fontW * l/2),
                    320 - fontH/2, 
                    s, l);
#endif /* Don't do for VAX C, GEC */
      
        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);
    XtAppAddActions(app_context, toplevel_actions, 1);
    XtAppAddActions(app_context, url_actions, 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);

    max_kiosk_protocols = 0;
    if (get_pref_boolean(eKIOSK)) {
	tptr = get_pref_string(eKIOSKPROTOCOLS);
	if (!tptr || !*tptr) {
	    /* do nothing */
	} else {
	    ptr = tptr;
	    while (1) {
		tptr = ptr;
		if (!(ptr = strchr(ptr, ','))) {
		    for (; *tptr && isspace(*tptr); tptr++);
		    for (eptr = tptr + (strlen(tptr)-1);
			 tptr <= eptr && (isspace(*eptr) || !isprint(*eptr));
			 eptr--) {
			*eptr = '\0';
		    }
		    if (*tptr) {
			strcpy(kioskProtocols[max_kiosk_protocols++], tptr);
		    }
		    break;
		} else {
		    *ptr = '\0';
		    ptr++;
		    strcpy(kioskProtocols[max_kiosk_protocols++], tptr);
		}
	    }
	}
    } else {
	max_kiosk_protocols = 0;
    }

    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);
    tableTrace = get_pref_boolean(eTABLETRACE);
    refreshTrace = get_pref_boolean(eREFRESHTRACE);
    reportBugs = get_pref_boolean(eREPORTBUGS);
#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) ||
        get_pref_boolean(eREFRESHTRACE) ||
        get_pref_boolean(eREPORTBUGS) ||
        get_pref_boolean(eTABLETRACE)) {
        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");
      
#ifndef VMS
        if (!home)
            home = "/tmp";
#endif /* VMS, BSN */
      
        personal_extension_map = (char *)malloc 
            (strlen (home) +
             strlen (get_pref_string(ePERSONAL_EXTENSION_MAP)) + 8);
#ifndef VMS
        sprintf (personal_extension_map, "%s/%s", home, 
#else
        sprintf (personal_extension_map, "%s%s", home, 
#endif /* VMS, BSN */
                 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");
      
#ifndef VMS
        if (!home)
            home = "/tmp";
#endif /* VMS, BSN */
      
        personal_type_map = (char *)malloc 
            (strlen (home) + strlen (get_pref_string(ePERSONAL_TYPE_MAP)) + 8);
#ifndef VMS
        sprintf (personal_type_map, "%s/%s", home, 
#else
        sprintf (personal_type_map, "%s%s", home, 
#endif /* VMS, BSN */
                 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 */
#ifndef VMS
        struct passwd *pw = getpwuid(getuid());
#else
        struct passwd *pw = getpwuid();
        char *vms_mail_prefix = get_pref_string(eVMS_MAIL_PREFIX);
#endif /* VMS, BSN */
        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) {
	    if (!pw || !pw->pw_gecos) {
		default_author_name = strdup("Unknown");
	    }
	    else {
		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) {
	    if (!pw || !pw->pw_name) {
		default_author_email =
			(char *) malloc(strlen("UNKNOWN") + strlen(machine)+2);
		sprintf(default_author_email, "UNKNOWN@%s", machine);
	    }
	    else {
		default_author_email =
			(char *) malloc(strlen(pw->pw_name)+strlen(machine)+2);
		sprintf(default_author_email, "%s@%s", pw->pw_name, machine);
	    }
        }    

        /* Check again just to make absolutely sure something is there */
        if (!default_author_name || !*default_author_name) {
                default_author_name = strdup("Unknown");
        }
        if (!default_author_email || !*default_author_email) {
                default_author_email =
                        (char *) malloc(strlen("UNKNOWN") + strlen(machine)+2);
                sprintf(default_author_email, "UNKNOWN@%s", machine);
        }

        set_pref(eDEFAULT_AUTHOR_NAME, (void *)default_author_name);
        set_pref(eDEFAULT_AUTHOR_EMAIL, (void *)default_author_email);
#ifdef VMS /* Drop blanks off the mail prefix string. GEC */
        if (vms_mail_prefix) {
            for (cc = vms_mail_prefix; *cc; cc++) {
                if (*cc == ' ') {
                    *cc = 0;
                    break;
                }
            }
        }
        else {
            vms_mail_prefix = " "; /* But make sure it is not a NULL string */
        }
        set_pref(eVMS_MAIL_PREFIX, (void *)vms_mail_prefix);
#endif /* VMS, GEC */
    }
  
        /* 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");
#ifdef VMS /* Make temp files in SYS$SCRATCH if TMPDIR not defined. PGE */
            if (!tmp_dir)
                tmp_dir = getenv("SYS$SCRATCH");
#endif /* VMS, GEC for PGE */
                /* 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_int(eCOLORS_PER_INLINED_IMAGE) > 256) {
	fprintf(stderr,"WARNING: Colors per inline image specification > 256.\n  Auto-Setting to 256.\n");
	set_pref_int(eCOLORS_PER_INLINED_IMAGE, 256);
  }

  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();

#ifndef VMS
  /* Write pid into "~/.mosaicpid". */
#else
  /* Write pid into "~/mosaicpid.". and delete a possible previous file */
  /* Changed now, just delete a possible previous file */
#endif /* VMS, BSN */
  {
    char *home = getenv("HOME"), *fnam;
    FILE *fp;
    
#ifndef VMS
        if (!home)
            home = "/tmp";
#endif /* VMS, BSN */
    
        fnam = (char *)malloc (strlen (home) + 32);
#ifndef VMS
        sprintf (fnam, "%s/.mosaicpid", home);
#else
        sprintf (fnam, "%smosaicpid.", home);
        remove (fnam);
#endif /* VMS, BSN */
    
#ifndef VMS
        fp = fopen (fnam, "w");
        if (fp)
        {
#ifndef VMS
            fprintf (fp, "%d\n", getpid());
#else
            fprintf (fp, "%x\n", getpid());
#endif /* VMS, BSN */
            fclose (fp);
        }
#endif /* Not needed, BSN */
    
        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;

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

    setup_imagekill();

    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 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);
    }

/*
 * The following commands were added by Mark Donszelmann, duns@vxdeop.cern.ch
 */
  else if (!strncmp(directive, "iconify", 7))
    {
      /* This was broken for some time, GEC */
      XIconifyWindow(dsp, XtWindow(win->base), DefaultScreen(dsp));
    }
  else if (!strncmp(directive, "deiconify", 9))
    {
      XMapWindow(dsp, XtWindow(win->base));
    }
  else if (!strncmp(directive, "raise", 5))
    {
      XMapRaised(dsp, XtWindow(win->base));
    }
  else if (!strncmp(directive, "move", 4))
    {
       char *status;
       if ((status = strchr(url, '|')) != NULL)
       {
          *status = '\0';
          status++;
       } else
	  status = url;  /* Prevent crash if 2nd parameter missing, TJA */
       XMoveWindow(dsp, XtWindow(win->base), atoi(url), atoi(status));
    }
  else if (!strncmp(directive, "resize", 6))
    {
       char *status;
       if ((status = strchr(url, '|')) != NULL)
       {
          *status = '\0';
          status++;
       } else
	  status = url;  /* Prevent crash if 2nd parameter missing, TJA */
       XResizeWindow(dsp, XtWindow(win->base), atoi(url), atoi(status));
    }

  return;
}


void set_current_win(Widget w, XEvent *event,
	       String *params, Cardinal *num_params)
{
  Widget toplevel = w;
  mo_window *ptr = winlist;
  int i;

  while (!XtIsTopLevelShell(toplevel))
    toplevel = XtParent(toplevel);

  for (i=0; ptr && (i < wincount); i++)
    {
      if (ptr->base == toplevel)
	{
	  if (event->xany.type == EnterNotify)
	    {
	      current_win = ptr;
	      ptr->have_focus = True;
	    }
	  else if (event->xany.type == LeaveNotify)
	    ptr->have_focus = False;
	  break;
	}
      else
	ptr = ptr->next;
    }
  if (!ptr)
	fprintf(stderr, "Couldn't find current window.  Mosaic will be crashing soon.\n");
}

void set_focus_to_view(Widget w, XEvent *event,
	       String *params, Cardinal *num_params)
{
  XtSetKeyboardFocus(current_win->base, current_win->view);
}

void take_focus(Widget w, XEvent *event,
	       String *params, Cardinal *num_params)
{
  XtSetKeyboardFocus(current_win->base, w);
}


void mo_flush_passwd_cache (mo_window *win)
{
  
  HTFTPClearCache ();
  HTAAServer_clear ();
  HTProgress ("Password cache flushed");
}

/* --------------------------- PASTE CODE ----------------------------------*/

static void paste_action(Widget w, XEvent *event, String *params,
  Cardinal *num_params);


static XtActionsRec actions[] =
  {
    {"URLPaste",    (XtActionProc)paste_action}
  };


static void paste_init(XtAppContext app)
{
  XtAppAddActions(app, actions, XtNumber(actions));
}


static mo_window *mo_fetch_window_by_widget (Widget w)
{
  /* Given any widget in the hierarchy, find the associated window structure */
  mo_window *win = winlist;
  Widget shell = w;

  /* Find Xt's Shell Widget */
  while (shell && !XtIsTopLevelShell(shell)) {
    shell = XtParent(shell);
  }

  while (win) {
    if (win->base == shell)
      return(win);
    win = win->next;
  }

  return NULL;
}


static void selection_requestor_cb (Widget w, XtPointer client_data,
	Atom *selection, Atom *type,
	XtPointer value, unsigned long *length, int *format)
{
  char *text = (char *) value;
  mo_window *win;

  win = mo_fetch_window_by_widget(w);

  if (win &&
      type && *type == XA_STRING &&
      text && strlen(text) &&
      length && *length > 0) {
    mo_convert_newlines_to_spaces(text);
    mo_load_window_text(win, text, NULL);
  }

}


static void paste_action(Widget w, XEvent *event, String *params,
  Cardinal *num_params)
{
  XButtonPressedEvent *mouse_event = (XButtonPressedEvent *) event;

  XtGetSelectionValue(w, XA_PRIMARY, XA_STRING, selection_requestor_cb,
                      NULL, mouse_event->time);

}
