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

/* Copyright (C) 1998, 1999, 2000, 2005, 2006 - The VMS Mosaic Project */

#include "../config.h"
#include "mosaic.h"
#include "hotlist.h"
#include "hotfile.h"
#include "gui.h"
#include "mo-www.h"
#include "mailto.h"
#include "gui-documents.h"
#include <time.h>
#include <Xm/List.h>
#include <Xm/TextF.h>
#include <Xm/ToggleBG.h>
#include <Xm/FilesB.h>
#include <sys/types.h>

#include "../libnut/system.h"
#include "gui-dialogs.h"
#include "gui-popup.h"

#if defined(MOTIF1_23) && (MOTIF1_23 == 7)
#undef MOTIF1_23
#endif /* VMS, Motif V1.2-3 bug fixed with ECO 7, so disable workaround, GEC */

#ifndef DISABLE_TRACE
extern int srcTrace;
#endif

extern mo_window *current_win;

static void mo_reinit_hotmenu();

static Boolean init = 0;
static Boolean DisplayURLs;

/* This file provides support for hotlists of interesting
   documents within the browser.
   
   Initially there will be a single hotlist, 'Default'.

   The old hotlist file format look like this:
   
   ncsa-mosaic-hotlist-format-1            [identifying string]
   Default                                 [title]
   url Fri Sep 13 00:00:00 1986            [first word is url;
                                            subsequent words are
                                            last-accessed date (GMT)]
   document title cached here              [cached title for above]
   [2-line sequence for single document repeated as necessary]
   ...
   
   Turns out this format is bad for two reasons:
   (1) Document titles can have embedded carriage returns (usually
       on purpose).
   (2) URL's can have embedded carriage returns (usually on accident).

   Also, we should just be using an HTML-derived format for hotlists.
*/

#define LISTINDIC "-> "

#define FindHotFromPos(hotnode, list, posi)			\
  do for (hotnode = list->nodelist; hotnode != NULL;		\
	  hotnode = hotnode->any.next) {			\
      if (hotnode->any.position == posi)			\
          break;						\
  } while(0)

typedef struct edit_or_insert_hot_info
{
  Widget title_text;
  int pos;
  Widget url_lab;
  Widget url_text;
  Widget tog_url;
  Widget tog_list;
  Widget insert_tog;
} edit_or_insert_hot_info;


static void URL_Include_Set(mo_window *win, int togval, int sensitive)
{
  if (!win) {
      win = current_win;
      if (!win)
	  return;
  }
  XmxSetToggleButton(win->hotlist_rbm_toggle, togval);
  XtVaSetValues(win->hotlist_rbm_toggle,
		XmNsensitive, sensitive,
		NULL);
  return;
}


mo_root_hotlist *default_hotlist = NULL;

/*
 * Given a hotlist and a hotnode, append the node
 * to the hotlist.
 * Change fields nodelist and nodelist_last in the hotlist,
 * and fields next and previous in the hotnode.
 * Also fill in field position in the hotnode.
 * Return nothing.
 */
void mo_append_item_to_hotlist (mo_hotlist *list, mo_hot_item *node)
{
  if (node->type == mo_t_list)
      node->list.parent = list;
  if (list->nodelist == 0) {
      /* Nothing yet. */
      list->nodelist = node;
      list->nodelist_last = node;
      node->any.next = 0;
      node->any.previous = 0;
      node->any.position = 1;
  } else {
      /* The new node becomes nodelist_last. */
      /* But first, set up node. */
      node->any.previous = list->nodelist_last;
      node->any.next = 0;
      node->any.position = node->any.previous->any.position + 1;
      
      /* Now point forward from previous nodelist_last. */
      list->nodelist_last->any.next = node;
      
      /* Now set up new nodelist_last. */
      list->nodelist_last = node;
  }
  
  return;
}

/* Given a hotlist and a hotnode, rip the hotnode out of the hotlist.
   No check is made as to whether the hotnode is actually in the hotlist;
   it better be. */
static void mo_remove_hotnode_from_hotlist (mo_hotlist *list,
					    mo_hot_item *hotnode)
{
  if (hotnode->any.previous == NULL) {
      /* Node was the first member of the list. */
      if (hotnode->any.next != NULL) {
          /* Node was the first member of the list and had
             a next node. */
          /* The next node is now the first node in the list. */
          hotnode->any.next->any.previous = NULL;
          list->nodelist = hotnode->any.next;
      } else {
          /* Node was the first member of the list and
             didn't have a next node. */
          /* The list is now empty. */
          list->nodelist = NULL;
          list->nodelist_last = NULL;
      }
  } else {
      /* Node had a previous. */
      if (hotnode->any.next != NULL) {
          /* Node had a previous and a next. */
          hotnode->any.previous->any.next = hotnode->any.next;
          hotnode->any.next->any.previous = hotnode->any.previous;
      } else {
          /* Node had a previous but no next. */
          hotnode->any.previous->any.next = NULL;
          list->nodelist_last = hotnode->any.previous;
      }
  }

  if (hotnode->type == mo_t_list) {
      mo_hot_item *item, *prev;

      for (item = hotnode->list.nodelist; item; free(prev)) {
	  mo_remove_hotnode_from_hotlist(&(hotnode->list), item);
	  prev = item;
	  item = hotnode->list.nodelist;
      }
  }

  return;
}

/* Go through a hotlist (sequence of hotnodes) and assign position
   numbers for all of 'em. */
static void mo_recalculate_hotlist_positions (mo_hotlist *list)
{
  mo_hot_item *hotnode;
  int count = 1;
  
  for (hotnode = list->nodelist; hotnode; hotnode = hotnode->any.next)
      hotnode->any.position = count++;
  
  return;
}

/* Insert an item in a list at a given position.
   if position is 0, then append it (at end of list). */
static void mo_insert_item_in_hotlist (mo_hotlist *list, mo_hot_item *node,
				       int position)
{
  if (!position) {
      mo_append_item_to_hotlist(list, node);
  } else {
      if (node->type == mo_t_list)
	  node->list.parent = list;
      if (list->nodelist == 0) {
	  /* Nothing yet. */
	  list->nodelist = node;
	  list->nodelist_last = node;
	  node->any.next = 0;
	  node->any.previous = 0;
	  node->any.position = 1;
      } else {
	  mo_hot_item *item;
	  mo_hot_item **prevNextPtr = &list->nodelist;

	  /* Search the item at position 'position' */
	  for (item = list->nodelist; item != NULL; item = item->any.next) {
	      if (item->any.position == position)
		  break;
	      prevNextPtr = &item->any.next;
	  }

	  if (item == NULL) {	/* Item not found */
	      mo_append_item_to_hotlist(list, node);
          } else {
	      *prevNextPtr = node;
	      node->any.previous = item->any.previous;
	      node->any.next = item;
	      item->any.previous = node;
	      mo_recalculate_hotlist_positions(list);
          }
      }
  }
  mo_reinit_hotmenu();
}

/* Go Up The tree to check if a list is the ancestor of an item
 */
static int mo_is_ancestor (mo_hotlist *list, mo_hotlist *item)
{
  while (item && item != list)
      item = item->parent;

  return item == list;
}

/* Recursive function that copy a hierarchy of hotlist */
static mo_hotlist *mo_copy_hot_hier (mo_hotlist *list)
{
  mo_hot_item *item;
  mo_hotnode *hot;
  mo_hotlist *hotlist = (mo_hotlist *)malloc(sizeof(mo_hotlist));

  hotlist->name = strdup(list->name);
  hotlist->type = mo_t_list;
  hotlist->rbm = list->rbm;
  hotlist->nodelist = hotlist->nodelist_last = 0;
  for (item = list->nodelist; item; item = item->any.next) {
      if (item->type == mo_t_url) {
	  hot = (mo_hotnode *)malloc(sizeof(mo_hotnode));
	  hot->type = mo_t_url;
	  hot->title = (item->hot.title ?
		        strdup(item->hot.title) : strdup("Unnamed"));
	  hot->url = strdup(item->hot.url);
	  /* hot->lastdate = strdup(item->hot.lastdate); */
	  hot->lastdate = (char *) 0;
	  hot->rbm = item->hot.rbm;
	  mo_append_item_to_hotlist(hotlist, (mo_hot_item *)hot);
      } else {
	  mo_append_item_to_hotlist(hotlist,
			   (mo_hot_item *)mo_copy_hot_hier((mo_hotlist *)item));
      }
  }
  return hotlist;
}

static char *mo_compute_hot_path (mo_hotlist *curr)
{
  char *str;
  char *prev = curr->parent ? strdup(curr->name) : strdup("/");

  for (str = prev, curr = curr->parent; curr; curr = curr->parent) {
      if (curr->parent)	{
	  str = (char *)malloc(strlen(prev) + strlen(curr->name) + 2);
	  strcat(strcat(strcpy(str, curr->name), "/"), prev);
      } else {
	  str = (char *)malloc(strlen(prev) + 2);
	  strcat(strcpy(str, "/"), prev);
      }
      free(prev);
      prev = str;
  }
  return str;
}

static void mo_copy_hotlist_position (mo_window *win, int position)
{
  mo_hot_item *item;

  for (item = win->current_hotlist->nodelist;
       item != NULL && item->any.position != position;
       item = item->any.next)
      ;
  if (item)
      win->hot_cut_buffer = item;
}

static char *mo_highlight_hotlist (mo_hotlist *list)
{
  char *str = (char *)malloc(strlen(list->name) + strlen(LISTINDIC) + 1);

  return strcat(strcpy(str, LISTINDIC), list->name);
}

static void mo_gui_add_hot_item (mo_hotlist *list, mo_hot_item *item)
{
  mo_window *win = NULL;

  if (!init) {
      DisplayURLs = get_pref_boolean(eDISPLAY_URLS_NOT_TITLES);
      init = 1;
  }

  /* Now we've got to update all active hotlist_list's. */
  while (win = mo_next_window(win)) {
      if (win->hotlist_list && win->current_hotlist == list) {
	  char *highlight = NULL;
	  XmString xmstr = XmxMakeXmstrFromString(
	       		       item->type == mo_t_url ?
	       		       (DisplayURLs ? item->hot.url : item->hot.title) :
	       		       (highlight = mo_highlight_hotlist(&item->list)));

	  if (item->type == mo_t_list && highlight)
	      free(highlight);
	  XmListAddItemUnselected(win->hotlist_list, xmstr, item->any.position);
	  XmStringFree(xmstr);
	  XmListSetBottomPos(win->hotlist_list, 0);
      }
  }
  mo_reinit_hotmenu();
}

mo_status mo_add_item_to_hotlist (mo_hotlist *list, mo_item_type type,
                                  char *title, char *url, int position, int rbm)
{
  mo_hot_item *item;

  if ((!title || title[0] == '\0') && (!url || url[0] == '\0'))
      return mo_fail;

  if (type == mo_t_url) {
      mo_hotnode *hotnode = (mo_hotnode *)malloc(sizeof(mo_hotnode));
      time_t foo = time(NULL);
      char *ts = ctime(&foo);

      item = (mo_hot_item *)hotnode;
      ts[strlen(ts)-1] = '\0';

      hotnode->type = mo_t_url;
      if (title) {
	  hotnode->title = strdup(title);
      } else {
	  hotnode->title = strdup("Unnamed");
      }
      mo_convert_newlines_to_spaces(hotnode->title);

      hotnode->url = strdup(url);
      mo_convert_newlines_to_spaces(hotnode->url);

      hotnode->lastdate = strdup(ts);

      hotnode->rbm = rbm;
  } else {
      mo_hotlist *hotlist = (mo_hotlist *)malloc(sizeof(mo_hotlist));

      item = (mo_hot_item *)hotlist;
      hotlist->type = mo_t_list;
      if (title) {
	  hotlist->name = strdup(title);
      } else {
	  hotlist->name = strdup("Unnamed");
      }
      mo_convert_newlines_to_spaces(hotlist->name);
      hotlist->nodelist = hotlist->nodelist_last = 0;
      hotlist->rbm = rbm;
  }

  if (position) {
      mo_insert_item_in_hotlist(list, item, position);
  } else {
      mo_append_item_to_hotlist(list, item);
  }
  mo_gui_add_hot_item(list, item);
  
  return mo_succeed;
}


/* ------------------------------------------------------------------------ */
/* ------------------------- gui support routines ------------------------- */
/* ------------------------------------------------------------------------ */

/* We've just init'd a new hotlist list widget; look at the default
   hotlist and load 'er up. */
static void mo_load_hotlist_list (mo_window *win, Widget list)
{
  mo_hot_item *node;
  
  if (!init) {
      DisplayURLs = get_pref_boolean(eDISPLAY_URLS_NOT_TITLES);
      init = 1;
  }

  if (win->edithot_win && XtIsManaged(win->edithot_win))
      XtUnmanageChild (win->edithot_win);
  for (node = win->current_hotlist->nodelist; node; node = node->any.next) {
      char *highlight = NULL;
      XmString xmstr = 
        XmxMakeXmstrFromString(node->type == mo_t_url ?
			       (DisplayURLs ? node->hot.url : node->hot.title) :
			       (highlight = mo_highlight_hotlist(&node->list)));

      if (node->type == mo_t_list && highlight)
	  free(highlight);
      XmListAddItemUnselected(list, xmstr, 0);
      XmStringFree(xmstr);
  }

  return;
}

static void mo_visit_hotlist_position (mo_window *win, int position)
{
  mo_hot_item *hotnode;

  for (hotnode = win->current_hotlist->nodelist; hotnode;
       hotnode = hotnode->any.next) {
      if (hotnode->any.position == position) {
	if (hotnode->type == mo_t_url) {
	    mo_access_document(win, hotnode->hot.url);
	} else {
	    char *path = mo_compute_hot_path(&(hotnode->list));

	    win->current_hotlist = &(hotnode->list);
	    XmListDeleteAllItems(win->hotlist_list);
	    XmxTextSetString(win->hotlist_label, path);
	    free(path);
	    mo_load_hotlist_list(win, win->hotlist_list);
	    URL_Include_Set(win, 0, 0);
        }
      }
  }

  return;
}
/* ------------------------------------------------------------------------ */
/* ----------- This part deals with the Edit and Insert features ---------- */
/* ------------------------------------------------------------------------ */

/* ----------------------- edit_or_insert_hot_cb -------------------------- */

static XmxCallback (edit_or_insert_hot_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));
  char *title;
  edit_or_insert_hot_info *eht_info;

  if (!init) {
      DisplayURLs = get_pref_boolean(eDISPLAY_URLS_NOT_TITLES);
      init = 1;
  }

  switch (XmxExtractToken((int)client_data)) {
    case 0:			/* Commit Edit */
      URL_Include_Set(win, 0, 0);
      XmxSetArg(XmNuserData, (XtArgVal)&eht_info);
      XtGetValues(win->edithot_win, Xmx_wargs, Xmx_n);
      Xmx_n = 0;
      XtUnmanageChild(win->edithot_win);
      title = XmxTextGetString(eht_info->title_text);
      
      {
        /* OK, now position is still cached in win->edithot_pos. */
        mo_hotlist *list = win->current_hotlist;
        mo_hot_item *hotnode;
	mo_window *w = NULL;
        
	FindHotFromPos(hotnode, list, eht_info->pos);

        if (hotnode == NULL)
            goto punt;

        /* OK, now we have the hotnode. */

	if (hotnode->type == mo_t_url) {
	  hotnode->hot.url = XmxTextGetString(eht_info->url_text);
	} else if (!strcmp(hotnode->any.name, title)) {
          goto punt;
	}
        hotnode->any.name = title;

        /* Save the hotlist before we screw something up. */
        mo_write_default_hotlist();

        /* Change the extant hotlists. */
	while (w = mo_next_window(w)) {
	    if (w->hotlist_list && w->current_hotlist == win->current_hotlist) {
		char *highlight = NULL;
		XmString xmstr = XmxMakeXmstrFromString(
			   hotnode->type == mo_t_url ?
			    (DisplayURLs ? hotnode->hot.url : title) :
			    (highlight = mo_highlight_hotlist(&hotnode->list)));

		if (hotnode->type == mo_t_list && highlight)
		    free(highlight);
		XmListDeletePos(w->hotlist_list, hotnode->any.position);
#ifndef DISABLE_TRACE
		if (srcTrace)
		    fprintf(stderr, 
			    "hotlist_list 0x%08x, xmstr 0x%08x, position %d\n",
			    w->hotlist_list, xmstr, hotnode->any.position);
#endif
		/* There is what appears to be a Motif UMR here... */
		XmListAddItemUnselected(w->hotlist_list, xmstr, 
				        hotnode->any.position);
		XmStringFree(xmstr);
            }
	    if (w->hotlist_list && hotnode->type == mo_t_list &&
		mo_is_ancestor((mo_hotlist *)hotnode, w->current_hotlist)) {
		char *path = mo_compute_hot_path(w->current_hotlist);

		XmxTextSetString(w->hotlist_label, path);
		free(path);
	    }
	}
        /* That's it! */
      }
     punt:
      break;

    case 3:			/* Commit Insert */
      URL_Include_Set(win, 0, 0);
      XmxSetArg(XmNuserData, (XtArgVal)&eht_info);
      XtGetValues(win->inserthot_win, Xmx_wargs, Xmx_n);
      Xmx_n = 0;
      XtUnmanageChild(win->inserthot_win);
      title = XmxTextGetString(eht_info->title_text);
      {
	Boolean isUrl = XmToggleButtonGadgetGetState(eht_info->tog_url);
	Boolean useIns = XmToggleButtonGadgetGetState(eht_info->insert_tog);
        int *pos_list;
        int pos_cnt, posi = 0;
	mo_status addOk = mo_succeed;

	if (useIns &&
	    XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt) &&
	    pos_cnt) {
	    posi = pos_list[0];
	    XtFree((char *)pos_list); /* DXP */
	}
	if (isUrl) {
	    addOk = mo_add_item_to_hotlist(win->current_hotlist, mo_t_url,
			      title, XmxTextGetString(eht_info->url_text), posi,
			      get_pref_boolean(eADD_HOTLIST_ADDS_RBM));
	} else {
	    if (win->hot_cut_buffer &&
		win->hot_cut_buffer->type == mo_t_list &&
		(!strcmp(title, win->hot_cut_buffer->any.name)) &&
		(!mo_is_ancestor((mo_hotlist *)win->hot_cut_buffer,
				 win->current_hotlist))) {
		mo_insert_item_in_hotlist(win->current_hotlist,
					  (mo_hot_item *)mo_copy_hot_hier(
					     (mo_hotlist *)win->hot_cut_buffer),
					  posi);
		/* Now we've got to update all active hotlist_list's. */
		mo_gui_add_hot_item(win->current_hotlist,
				    win->current_hotlist->nodelist_last);
	    } else {
	        addOk = mo_add_item_to_hotlist(win->current_hotlist, mo_t_list,
				       title, NULL, posi,
				       get_pref_boolean(eADD_HOTLIST_ADDS_RBM));
	    }
	}
	if (addOk == mo_succeed)
	    mo_write_default_hotlist();
      }
      win->hot_cut_buffer = NULL;
      /* Do a redisplay here */
      mo_compute_hot_path(win->current_hotlist);
      XmListDeleteAllItems(win->hotlist_list);
      mo_load_hotlist_list(win, win->hotlist_list);

      break;

    case 1:			/* Dismiss Edit */
      XtUnmanageChild(win->edithot_win);
      /* Do nothing. */
      break;

    case 4:			/* Dismiss Insert */
      XtUnmanageChild(win->inserthot_win);
      win->hot_cut_buffer = NULL;
      /* Do nothing. */
      break;

    case 2:			/* Help... (Edit) */
    case 5:			/* Help... (Insert) */
      mo_open_another_window(win, 
          		     mo_assemble_help_url("help-on-hotlist-view.html"), 
          		     NULL, NULL);
      break;
  }

  return;
}

/* This is used to destroy the edit_or_insert_hot_info structure
   called from the "destroyCallback" list. */
static XmxCallback (mo_destroy_hot)
{
  free(client_data);
}

/* Show or hide the url info with respect to the URL toggle */
static XmxCallback (url_or_list_cb)
{
  edit_or_insert_hot_info *eht_info = (edit_or_insert_hot_info *)client_data;

  if (((XmToggleButtonCallbackStruct *)call_data)->set) {
      XtManageChild(eht_info->url_lab);
      XtManageChild(eht_info->url_text);
  } else {
      XtUnmanageChild(eht_info->url_lab);
      XtUnmanageChild(eht_info->url_text);
  }
}


/* If it don't exist, make it...
   If isInsert is True, then we create an Insert Dialog window, otherwise
   we create an Edit dialog window. */
static mo_status mo_create_ed_or_ins_hot_win (mo_window *win, int isInsert)
{
  Widget ed_or_ins_w, dialog_frame;
  Widget dialog_sep, buttons_form;
  Widget eht_form, title_label, url_label, url_val, sep2;
  edit_or_insert_hot_info *eht_info;
  Widget togm, togm2, insert_tog, append_tog;
  
  XmxSetUniqid(win->id);
  eht_info = (edit_or_insert_hot_info *)malloc(sizeof(edit_or_insert_hot_info));
  XmxSetArg(XmNuserData, (XtArgVal)eht_info);
  ed_or_ins_w = XmxMakeFormDialog 
#ifndef MOTIF1_23 
    (win->hotlist_win, isInsert ? "VMS Mosaic: Insert Hotlist Entry" :
#else
    (win->base, isInsert ? "VMS Mosaic: Insert Hotlist Entry" :
#endif /* MOTIF1_23, GEC */
     "VMS Mosaic: Edit Hotlist Entry");
  XtAddCallback(ed_or_ins_w, XmNdestroyCallback, mo_destroy_hot,
		eht_info);

  if (isInsert) {
      win->inserthot_win = ed_or_ins_w;
  } else {
      win->edithot_win = ed_or_ins_w;
  }
  dialog_frame = XmxMakeFrame(ed_or_ins_w, XmxShadowOut);

  /* Constraints for ed_or_ins_w. */
  XmxSetConstraints(dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
		    XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
  
  /* Main form */
  eht_form = XmxMakeForm(dialog_frame);
  
  title_label = XmxMakeLabel(eht_form, "Entry Title:");
  XmxSetArg(XmNwidth, 335);
  eht_info->title_text = XmxMakeTextField(eht_form);
  XmxAddCallbackToText(eht_info->title_text, edit_or_insert_hot_cb, isInsert*3);
  
  eht_info->url_lab = url_label = XmxMakeLabel(eht_form, "URL:");

  XmxSetArg(XmNwidth, 335);
  eht_info->url_text = url_val = XmxMakeTextField(eht_form);

  dialog_sep = XmxMakeHorizontalSeparator(eht_form);

  if (isInsert) {
      togm = XmxMakeRadioBox(eht_form);
      eht_info->tog_url =
	XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass, togm,
				XtVaTypedArg, XmNlabelString,
				XtRString, "URL", strlen("URL") + 1,
				XmNmarginHeight, 0,
				XmNset, True, NULL);
      XtAddCallback(eht_info->tog_url, XmNvalueChangedCallback, url_or_list_cb,
		    (XtPointer)eht_info);
      eht_info->tog_list =
	XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass, togm,
				XtVaTypedArg, XmNlabelString,
				XtRString, "List", strlen("List") + 1,
				XmNmarginHeight, 0, NULL);
      togm2 = XmxMakeRadioBox(eht_form);
      eht_info->insert_tog = insert_tog =
	XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass, togm2,
				XtVaTypedArg, XmNlabelString,
				XtRString, "Insert", strlen("Insert") + 1,
				XmNmarginHeight, 0, NULL);
      append_tog =
	XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass, togm2,
				XtVaTypedArg, XmNlabelString,
				XtRString, "Append", strlen("Append") + 1,
				XmNmarginHeight, 0,
				XmNset, True, NULL);
      sep2 = XmxMakeHorizontalSeparator (eht_form);
  }
  
  buttons_form = XmxMakeFormAndThreeButtons
    (eht_form, edit_or_insert_hot_cb, "Save",
     "Dismiss", "Help...",
     isInsert * 3, isInsert * 3 + 1, isInsert * 3 + 2);
  
  /* Constraints for eht_form. */
  XmxSetOffsets(title_label, 14, 0, 10, 0);
  XmxSetConstraints
    (title_label, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_NONE,
     NULL, NULL, NULL, NULL);
  XmxSetOffsets(eht_info->title_text, 10, 0, 5, 10);
  XmxSetConstraints
    (eht_info->title_text, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_WIDGET,
     XmATTACH_FORM, NULL, NULL, title_label, NULL);
  
  XmxSetOffsets(url_label, 12, 0, 10, 0);
  XmxSetConstraints
    (url_label, XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_NONE,
     title_label, NULL, NULL, NULL);
  XmxSetOffsets(url_val, 8, 10, 5, 10);
  XmxSetConstraints
    (url_val, XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_WIDGET,
     XmATTACH_FORM, title_label, NULL, url_label, NULL);

  XmxSetArg(XmNtopOffset, 10);
  XmxSetConstraints 
    (dialog_sep, XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, 
     XmATTACH_FORM,
     url_val, isInsert ? togm : buttons_form, NULL, NULL);
  if (isInsert) {
      XmxSetConstraints
	(togm, XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM,
	 XmATTACH_NONE,
	 NULL, sep2, NULL, NULL);
      XmxSetPositions
	(togm, XmxNoPosition, XmxNoPosition, XmxNoPosition, 50);
      XmxSetConstraints
	(togm2, XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_NONE,
	 XmATTACH_FORM,
	 NULL, sep2, NULL, NULL);
      XmxSetPositions
	(togm2, XmxNoPosition, XmxNoPosition, 50, XmxNoPosition);
      XmxSetConstraints
	(sep2, XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM,
	 XmATTACH_FORM, NULL, buttons_form, NULL, NULL);
  }
  XmxSetConstraints 
    (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
     XmATTACH_FORM,
     NULL, NULL, NULL, NULL);
  
  return mo_succeed;
}


static mo_status mo_do_edit_hotnode_title_win (mo_window *win, mo_hot_item
					       *item, int position)
{
  edit_or_insert_hot_info *eht_info;

  /* This shouldn't happen. */
  if (!win->hotlist_win)
      return mo_fail;
  
  if (!win->edithot_win)
      mo_create_ed_or_ins_hot_win(win, 0);

  XmxSetArg (XmNuserData, (XtArgVal)&eht_info);
  XtGetValues(win->edithot_win, Xmx_wargs, Xmx_n);
  Xmx_n = 0;
  /* Cache the position. */
  eht_info->pos = position;

  /* Manage the little sucker. */
  XmxManageRemanage(win->edithot_win);
  
  /* Insert this title as a starting point. */
  XmxTextSetString(eht_info->title_text, item->hot.title);

  if (item->type == mo_t_url) {
      /* Insert URL */
      XmxTextSetString(eht_info->url_text, item->hot.url);
      XtManageChild(eht_info->url_lab);
      XtManageChild(eht_info->url_text);
  } else {
      XtUnmanageChild(eht_info->url_lab);
      XtUnmanageChild(eht_info->url_text);
  }

  return mo_succeed;
}


/*
 * Edit the title of an element of the current hotlist.
 * The element is referenced by its position.
 * Algorithm for edit:
 *   Find hotnode with the position.
 *   Change the title.
 *   Cause redisplay.
 * Return status.
 */
static mo_status mo_edit_current_hotlist_title (mo_window *win, int position)
{
  mo_hotlist *list = win->current_hotlist;
  mo_hot_item *hotnode;
  
  FindHotFromPos(hotnode, list, position);

  /* OK, now we have hotnode loaded. */
  /* hotnode->hot.title is the current title.
     hotnode->hot.position is the current position. */
  return
    ((hotnode != NULL) ?
     mo_do_edit_hotnode_title_win(win, hotnode, position) :
     mo_fail);
}


static void RecursiveSetList(mo_window *win, mo_hotlist *list, int val,
			     int force)
{
  static char *add = "Do you wish to add all items in this list to the RBM?";
  static char *remove =
	    "Do you wish to remove all items in this list from the RBM?";
  mo_hot_item *item;

  if (force || prompt_for_yes_or_no((val ? add : remove))) {
      for (item = list->nodelist; item; item = item->any.next) {
	  if (item->type == mo_t_url) {
	      item->hot.rbm = val;
	  } else {
	      item->list.rbm = val;
	      RecursiveSetList(win, &(item->list), val, 1);
	  }
      }
  }

  return;
}


/*
 * Either add or remove this entry from the RBM.
 * The element is referenced by its position.
 * Algorithm:
 *   Find hotnode with the position.
 *   Change the RBM status.
 *   Cause rebuild of RBM.
 * Return status.
 */
static mo_status mo_rbm_toggle_in_hotlist(mo_window *win, int position)
{
    mo_hotlist *list = win->current_hotlist;
    mo_hot_item *hotnode;
  
    FindHotFromPos(hotnode, list, position);

    if (!hotnode)  /* How did this happen? */
	return(mo_fail);

    /*
     * Set the new rbm value.
     * Redisplay and rebuild the RBM.
     */
    if (hotnode->type == mo_t_url) {
	hotnode->hot.rbm = (!hotnode->hot.rbm);
    } else {
	hotnode->list.rbm = (!hotnode->list.rbm);
	RecursiveSetList(win, &hotnode->list, hotnode->list.rbm, 0);
    }
    XmxSetToggleButton(win->hotlist_rbm_toggle,
		       (hotnode->type == mo_t_url ? hotnode->hot.rbm :
			hotnode->list.rbm));
    mo_reinit_hotmenu();

    return(mo_succeed);
}

static void mo_insert_item_in_current_hotlist(mo_window *win)
{
  if (win->hotlist_win) {
      edit_or_insert_hot_info *eht_info;

      if (!win->inserthot_win)
	  mo_create_ed_or_ins_hot_win(win, 1);

      XmxSetArg(XmNuserData, (XtArgVal)&eht_info);
      XtGetValues(win->inserthot_win, Xmx_wargs, Xmx_n);
      Xmx_n = 0;
      /* Manage the little sucker. */
      XmxManageRemanage(win->inserthot_win);

      if (win->hot_cut_buffer) {
	  /* Insert this title as a starting point. */
	  XmxTextSetString(eht_info->title_text,
			   win->hot_cut_buffer->any.name);
	  if (win->hot_cut_buffer->type == mo_t_url) {
	      /* Insert URL */
	      XmxTextSetString(eht_info->url_text,
			       win->hot_cut_buffer->hot.url);
	      XtManageChild(eht_info->url_lab);
	      XtManageChild(eht_info->url_text);
	      XmToggleButtonGadgetSetState(eht_info->tog_list, False, False);
	      XmToggleButtonGadgetSetState(eht_info->tog_url, True, False);
	  } else {
	      /* Insert a List */
	      XtUnmanageChild(eht_info->url_lab);
	      XtUnmanageChild(eht_info->url_text);
	      XmToggleButtonGadgetSetState(eht_info->tog_list, True, False);
	      XmToggleButtonGadgetSetState(eht_info->tog_url, False, False);
	  }
      } else {
	  XmTextFieldSetString(eht_info->title_text, "");
	  XmTextFieldSetString(eht_info->url_text, "");
      }
  }

  mo_reinit_hotmenu();
}


/*
 * Create a new mo_root_hotlist.
 * Pass in the new filename and the new title.
 */
static mo_root_hotlist *mo_new_root_hotlist (char *filename, char *title)
{
  mo_root_hotlist *list;
  
  list = (mo_root_hotlist *)malloc(sizeof(mo_root_hotlist));
  list->type = mo_t_list;
  list->nodelist = list->nodelist_last = 0;
  list->filename = filename;
  list->modified = 0;
  list->next = list->previous = 0;
  list->parent = 0;
  list->name = title ? strdup(title) : NULL;
  return list;
}

/* --------------------------- mo_read_hotlist ---------------------------- */

/*
 * Read a hotlist from a file.
 * Return pointer to a mo_hotlist structure, fully loaded
 * and ready to go.
 * Return NULL if file does not exist or is not readable.
 */
static mo_root_hotlist *mo_read_hotlist (char *filename, char *home,
					 int *rewrite)
{
  mo_root_hotlist *list = NULL;
  FILE *fp;
  char line[MO_LINE_LENGTH];
  char *status, *name;
  int isnew;
  char *oldfilename;
  char *hotname;
  char *tmp = get_pref_string(eDEFAULT_HOT_FILE);
  mo_hotnode *node;

  hotname = (char *)calloc(strlen(home) + strlen(tmp) + 5, sizeof(char));
#ifndef VMS
  sprintf(hotname, "%s/%s", home, tmp);
#else
  sprintf(hotname,"%s%s", home, tmp);
#endif /* VMS, PGE */

  oldfilename = filename;
  if (!(filename = malloc(strlen(filename) + 10)))
      goto screwed_no_file;

  sprintf(filename, "%s.html", oldfilename);
  /* For backward compatibility */

  /* oldfilename: cookie 1
     filename: cookie 2
     hotname: cookie 3 */

  /* cookie 2 and cookie 3 are very similar, but format 2 browsers cannot
	read format 3 without damaging them.
     The cool thing is if we only have format 2, format 3 will automatically
	be written out to the format 3 filename...so we don't have to worry
	about converting anything.  Groovy. --SWP */

  if (!(fp = fopen(hotname, "r"))) {
	*rewrite = 1;		/* Need to write out new format */
	fp = fopen(filename, "r");
	if (!fp) {
#ifdef VMS
		/* Used a different default for first version */
		oldfilename = (char *)malloc((strlen(home) + 30) *
					     sizeof(char));
		sprintf(oldfilename, "%s%s", home, "MOSAIC.HOTLIST-DEFAULT");
#endif /* VMS, GEC */
		fp = fopen(oldfilename, "r");
		if (!fp) {
			goto screwed_no_file;
		} else if (get_pref_boolean(eBACKUP_FILES)) {
			char *tf = NULL, retBuf[BUFSIZ];
        
#ifndef VMS
			tf = (char *)calloc(strlen(oldfilename) +
					   strlen(".backup") + 5, sizeof(char));
			sprintf(tf, "%s.backup", oldfilename);
			if (my_copy(oldfilename, tf, retBuf, BUFSIZ - 1, 1) !=
			    SYS_SUCCESS) {
#else
			tf = (char *)calloc(strlen(oldfilename) +
				strlen("_backup") + 5, sizeof(char));
			sprintf(tf, "%s_backup", oldfilename);
			if (my_copy(oldfilename, tf, retBuf, BUFSIZ - 1,
			    get_pref_int(eBACKUPFILEVERSIONS)) != SYS_SUCCESS) {
#endif /* VMS, GEC */
				fprintf(stderr, "%s\n", retBuf);
			}
			free(tf);
		}
	} else if (get_pref_boolean(eBACKUP_FILES)) {
		char *tf = NULL, retBuf[BUFSIZ];

#ifndef VMS
		tf = (char *)calloc(strlen(filename) + strlen(".backup") + 5,
				    sizeof(char));
		sprintf(tf, "%s.backup", filename);
		if (my_copy(filename, tf, retBuf, BUFSIZ-1, 1) != SYS_SUCCESS) {
#else
		tf = (char *)calloc(strlen(filename) + strlen("_backup") + 5,
				    sizeof(char));
		sprintf(tf, "%s_backup", filename);
		if (my_copy(filename, tf, retBuf, BUFSIZ - 1,
			    get_pref_int(eBACKUPFILEVERSIONS)) != SYS_SUCCESS) {
#endif /* VMS, GEC */
			fprintf(stderr, "%s\n", retBuf);
		}
		free(tf);
	}
  } else if (get_pref_boolean(eBACKUP_FILES)) {
	char *tf = NULL, retBuf[BUFSIZ];

#ifndef VMS
	tf = (char *)calloc(strlen(hotname) + strlen(".backup") + 5,
			    sizeof(char));
	sprintf(tf, "%s.backup", hotname);
	if (my_copy(hotname, tf, retBuf, BUFSIZ - 1, 1) != SYS_SUCCESS) {
#else
	tf = (char *)calloc(strlen(hotname) + strlen("_backup") + 5,
			    sizeof(char));
	sprintf(tf, "%s_backup", hotname);
	if (my_copy(hotname, tf, retBuf, BUFSIZ - 1,
		    get_pref_int(eBACKUPFILEVERSIONS)) != SYS_SUCCESS) {
#endif /* VMS, GEC */
		fprintf(stderr, "%s\n", retBuf);
	}
	free(tf);
  }

  status = fgets(line, MO_LINE_LENGTH, fp);
  if (!status || !*line)
        goto screwed_open_file;

  /* See if it's our format. */
  if (!strncmp(line, NCSA_HOTLIST_FORMAT_COOKIE_ONE,
	       strlen(NCSA_HOTLIST_FORMAT_COOKIE_ONE))) {
        isnew = 0;
  } else {
	status = fgets(line, MO_LINE_LENGTH, fp);
	if (!status || !(*line))
		goto screwed_open_file;

	if (!strncmp(line, NCSA_HOTLIST_FORMAT_COOKIE_TWO,
		     strlen(NCSA_HOTLIST_FORMAT_COOKIE_TWO))) {
		isnew = 1;
	} else if (!strncmp(line, NCSA_HOTLIST_FORMAT_COOKIE_THREE,
			    strlen(NCSA_HOTLIST_FORMAT_COOKIE_THREE))) {
		isnew = 2;
	} else {
		fprintf(stderr,
		    "Unknown hotlist format. Attempting to parse. This\n  could result in damage to this file.\n");
		isnew = 2;
	}

	rewind(fp);
	status = fgets(line, MO_LINE_LENGTH, fp);
	if (!status || !(*line))
		goto screwed_open_file;
  }

  if (isnew) {
      list = mo_new_root_hotlist(hotname, NULL);
      list->name = mo_read_new_hotlist((mo_hotlist *)list, fp);
      if (isnew == 1) {
#ifndef VMS
	fprintf(stderr,
	    "Your hotlist has been updated to a new format!\n  It is now called '.mosaic-hot.html'.\n");
#else
	fprintf(stderr,
	    "Your hotlist has been updated to a new format!\n  It is now called 'mosaic-hot.html'.\n");
#endif /* VMS, GEC */
      }
      goto done;
  }
  /* Go fetch the name on the next line. */
  status = fgets(line, MO_LINE_LENGTH, fp);
  if (!status || !*line)
      goto screwed_open_file;

  name = strtok(line, "\n");
  if (!name)
      goto screwed_open_file;

  /* Display update message for 2.4 users */
  fputs("Your hotlist file has been updated and is now saved as:\n", stderr);
  fputs(hotname, stderr);
  putc('\n', stderr);
  /* Hey, whaddaya know, it is. */
  list = mo_new_root_hotlist(hotname, name);

  /* Start grabbing documents. */
  while (1) {
      status = fgets(line, MO_LINE_LENGTH, fp);
      if (!status || !(*line))
          goto done;
      
      /* We've got a new node. */
      node = (mo_hotnode *)malloc(sizeof(mo_hotnode));
      node->type = mo_t_url;
      node->url = strtok(line, " ");
      if (!node->url)
          goto screwed_open_file;
      node->url = strdup(node->url);
      mo_convert_newlines_to_spaces(node->url);

      node->lastdate = strtok(NULL, "\n");
      if (!node->lastdate)
          goto screwed_open_file;
      node->lastdate = strdup(node->lastdate);

      status = fgets(line, MO_LINE_LENGTH, fp);
      if (!status || !(*line)) {
          /* Oops, something went wrong. */
          free(node->url);
	  if (node->lastdate)
	      free(node->lastdate);
          free(node);
          goto done;
      }
      
      node->title = strtok(line, "\n");
      if (!node->title)
          goto screwed_open_file;
      node->title = strdup(node->title);
      mo_convert_newlines_to_spaces(node->title);
      
      mo_append_item_to_hotlist((mo_hotlist *)list, (mo_hot_item *)node);
  }
  
  done:
  screwed_open_file:
      fclose(fp);
  screwed_no_file:
      return list;
}


#if 0
/*
 * Write a hotlist out to stdout.
 * Return mo_succeed if everything goes OK;
 * mo_fail else.
 */
mo_status mo_dump_hotlist (mo_hotlist *list)
{
  mo_write_hotlist(list, stdout);

  return mo_succeed;
}
#endif

/* ------------------------------------------------------------------------ */
/* ----------------------------- HOTLIST GUI ------------------------------ */
/* ------------------------------------------------------------------------ */

/* Initial GUI support for hotlist will work like this:

   There will be a single hotlist, called 'Default'.
   It will be persistent across all windows.

   Upon program startup an attempt will be made to load it out
   of its file; if this attempt isn't successful, it just plain
   doesn't exist yet.  Bummer.
   
   Upon program exit it will be stored to its file.
*/

/*
 * Called on initialization.  
 * Tries to load the default hotlist.
 */
mo_status mo_setup_default_hotlist (void)
{
  char *home = getenv("HOME");
  char *default_filename = get_pref_string(eDEFAULT_HOTLIST_FILE);
  char *filename;
  int rewrite = 0;
  
  /* This shouldn't happen. */
#ifndef VMS
  if (!home)
      home = "/tmp";
#endif /* VMS, BSN */
  
  filename = (char *)malloc((strlen(home) + strlen(default_filename) + 8) *
			    sizeof(char));
#ifndef VMS
  sprintf(filename, "%s/%s", home, default_filename);
#else
  sprintf(filename, "%s%s", home, default_filename);
#endif /* VMS, BSN */

  /* Try to load the default hotlist. */
  /* Checks for both old and new filenames */
  default_hotlist = mo_read_hotlist(filename, home, &rewrite);

  /* Doesn't exist?  Bummer.  Make a new one. */
  if (!default_hotlist) {
      char *hot_filename = get_pref_string(eDEFAULT_HOT_FILE);

      fprintf(stderr, "Could not find a hotlist.  Creating a new one.\n");
      free(filename);
      filename = (char *)malloc((strlen(home) + strlen(hot_filename) + 8) *
				sizeof(char));
#ifndef VMS
      sprintf(filename, "%s/%s", home, hot_filename);
#else
      sprintf(filename, "%s%s", home, hot_filename);
#endif /* VMS, BSN */
      default_hotlist = mo_new_root_hotlist(filename, "Default");
      rewrite = 1;
  }

  if (rewrite)
      mo_write_default_hotlist();
  
  return mo_succeed;
}

/*
 * Called after mods are made.
 * Tries to write the default hotlist.
 */
mo_status mo_write_default_hotlist (void)
{
  FILE *fp;

  if (!default_hotlist)
      return mo_fail;

#ifdef VMS
  /* Make sure we start a new file like UNIX */
  remove(default_hotlist->filename);
  fp = fopen(default_hotlist->filename, "w", "shr = nil", "rop = WBH",
	     "mbf = 4", "mbc = 32", "deq = 8", "fop = tef");
#else
  fp = fopen(default_hotlist->filename, "w");
#endif

  if (!fp)
      return mo_fail;

  mo_write_hotlist((mo_hotlist *)default_hotlist, fp);
  if (fclose(fp))
      return mo_fail;

  default_hotlist->modified = 0;
  return mo_succeed;
}

static XmxCallback (save_hot_cb)
{
  char *fname = NULL;
  char efname[MO_LINE_LENGTH];
  FILE *fp;
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));

  XtUnmanageChild(win->save_hotlist_win);
  XmStringGetLtoR(((XmFileSelectionBoxCallbackStruct *)call_data)->value,
		   XmSTRING_DEFAULT_CHARSET, &fname);
  pathEval(efname, fname);
#ifdef VMS
  /* Make sure we start a new file like UNIX */
  remove(efname);
#endif /* VMS, BSN */
  fp = fopen(efname, "w");
  if (!fp) {
#ifndef VMS
	char *buf, *final, tmpbuf[80];
	int final_len;

	buf = my_strerror(errno);
	if (!buf || !*buf || !strcmp(buf, "Error 0")) {
	    sprintf(tmpbuf, "Unknown Error");
	    buf = tmpbuf;
	}

	final_len = 30 +((!efname || !*efname ? 3 : strlen(efname)) + 13) + 15 +
		    (strlen(buf) + 13);
	final = (char *)calloc(final_len, sizeof(char));

	sprintf(final,
		"\nUnable to save hotlist:\n   %s\n\nSave Error:\n   %s\n",
		(!efname || !*efname ? " " : efname), buf);

	XmxMakeErrorDialog(win->save_hotlist_win, final, "Save Error");
	XtManageChild(Xmx_w);

	if (final) {
	    free(final);
	    final = NULL;
	}
#else
        char *str;

        str = (char *)calloc(1024, sizeof(char));
        sprintf(str, "Unable to Save Hotlist: %s\nSave Error: %s",
                fname, strerror(errno, vaxc$errno));
#ifndef MOTIF1_23
        XmxMakeErrorDialog(win->save_hotlist_win, str, "Save Error");
#else
        XmxMakeErrorDialog(win->base, str, "Save Error");
#endif /* MOTIF1_23, GEC */
        XtManageChild(Xmx_w);
        free(str);
#endif /* VMS, GEC */
  } else {
        mo_write_hotlist(win->current_hotlist, fp);
        fclose(fp);
  }
  free(fname);
}

static XmxCallback (load_hot_cb)
{
  char *fname = NULL;
  char efname[MO_LINE_LENGTH];
  FILE *fp;
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));

  XtUnmanageChild(win->load_hotlist_win);
  XmStringGetLtoR(((XmFileSelectionBoxCallbackStruct *)call_data)->value,
		  XmSTRING_DEFAULT_CHARSET, &fname);
  pathEval(efname, fname);
  fp = fopen(efname, "r");
  if (!fp) {
#ifndef VMS
	char *buf, *final, tmpbuf[80];
	int final_len;

	buf = my_strerror(errno);
	if (!buf || !*buf || !strcmp(buf, "Error 0")) {
	    sprintf(tmpbuf, "Unknown Error");
	    buf = tmpbuf;
	}

	final_len = 30 + ((!efname || !*efname ? 3 : strlen(efname)) + 13) +
		    15 + (strlen(buf) + 13);
	final = (char *)calloc(final_len, sizeof(char));

	sprintf(final,
		"\nUnable to open hotlist:\n   %s\n\nOpen Error:\n   %s\n",
		(!efname || !*efname ? " " : efname), buf);

	XmxMakeErrorDialog(win->load_hotlist_win, final, "Open Error");
	XtManageChild(Xmx_w);

	if (final) {
	    free(final);
	    final = NULL;
	}
#else
        char *str;

        str = (char *)calloc(1024, sizeof(char));
        sprintf(str, "Unable to Open Hotlist: %s\nOpen Error: %s",
                fname, strerror(errno, vaxc$errno));
#ifndef MOTIF1_23
        XmxMakeErrorDialog(win->save_hotlist_win, str, "Load Error");
#else
        XmxMakeErrorDialog(win->base, str, "Load Error");
#endif /* MOTIF1_23, GEC */
        XtManageChild(Xmx_w);
        free(str);
#endif /* VMS, GEC */
   } else {
      Widget tb;

      XmxSetArg(XmNuserData, (XtArgVal)&tb);
      XtGetValues(win->load_hotlist_win, Xmx_wargs, Xmx_n);
      Xmx_n = 0;
      if (XmToggleButtonGadgetGetState(tb)) {
	  mo_hotlist *list = (mo_hotlist *)malloc(sizeof(mo_hotlist));

	  list->type = mo_t_list;
	  list->nodelist = list->nodelist_last = 0;
	  list->name = mo_read_new_hotlist(list, fp);
	  if (list->name == NULL)
	      list->name = strdup("Unnamed");
	  mo_append_item_to_hotlist(win->current_hotlist, (mo_hot_item *)list);
	  mo_gui_add_hot_item(win->current_hotlist, (mo_hot_item *)list);
      } else {
	  mo_hot_item *item = win->current_hotlist->nodelist_last;

	  mo_read_new_hotlist(win->current_hotlist, fp);
	  if (item == NULL) {
	      item = win->current_hotlist->nodelist;
	  } else {
	      item = item->any.next;
	  }
	  for (; item; item = item->any.next)
	      mo_gui_add_hot_item(win->current_hotlist, item);
      }
      fclose(fp);
      mo_write_default_hotlist();
      URL_Include_Set(win, 0, 0);
  }
  free (fname);
}

/* --------------- mo_delete_position_from_current_hotlist ---------------- */
/*
 * Delete an element of the default hotlist.
 * The element is referenced by its position.
 * Algorithm for removal:
 *   Find hotnode with the position.
 *   If it is a list, change the current list of the windows that has hotnode
 *	as an ancestor.
 *   Remove the hotnode from the hotlist data structure.
 *   Recalculate positions of the hotlist.
 *   Remove the element in the position in the list widgets.
 * Return status.
 */
static void delete_hot_from_list (mo_hotlist *list, mo_hot_item *hotnode,
				  int position)
{
  mo_window *win = NULL;

  if (hotnode == NULL)
      return;
  if (hotnode->type == mo_t_list) {
      while (win = mo_next_window(win)) {
	  if (win->hotlist_list &&
	      mo_is_ancestor(&(hotnode->list), win->current_hotlist)) {
	      char *path = mo_compute_hot_path(list);

	      XmListDeleteAllItems(win->hotlist_list);
	      win->current_hotlist = list;
	      XmxTextSetString(win->hotlist_label, path);
	      free(path);
	      mo_load_hotlist_list(win, win->hotlist_list);
	  }
      }
  }
  /* Pull the hotnode out of the hotlist. */
  mo_remove_hotnode_from_hotlist(list, hotnode);
  free(hotnode);
  /* Recalculate positions in this hotlist. */
  mo_recalculate_hotlist_positions(list);
  
  /* Do the GUI stuff. */
  while (win = mo_next_window(win)) {
      if (win->hotlist_list && win->current_hotlist == list)
          XmListDeletePos(win->hotlist_list, position);
      if (win->hot_cut_buffer == hotnode)
	  win->hot_cut_buffer = NULL;
  }

  mo_reinit_hotmenu();
  mo_write_default_hotlist();
}

static XmxCallback (remove_confirm_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));
  int position = XmxExtractToken((int)client_data);

  if (position) {
      mo_hot_item *hotnode;

      FindHotFromPos(hotnode, win->current_hotlist, position);
      delete_hot_from_list(win->current_hotlist, hotnode, position);
      URL_Include_Set(win, 0, 0);
  }
  XtDestroyWidget(w);
}

static mo_status mo_delete_position_from_current_hotlist (mo_window *win,
							  int position)
{
  mo_hotlist *list = win->current_hotlist;
  mo_hot_item *hotnode;
  
  FindHotFromPos(hotnode, list, position);

  if (hotnode == NULL)
      return mo_fail;
  
  /* OK, now we have hotnode loaded. */

  if (hotnode->type == mo_t_list) {
      char *question;
      char *endquestion;
      char *buff;

      question = strdup("Are you sure you want to remove the \"" );
      endquestion = strdup("\" list?" );
      buff = (char *)malloc
	(strlen(question) + strlen(hotnode->list.name) + strlen(endquestion)+1);

      strcat(strcat(strcpy(buff, question), hotnode->list.name), endquestion);
      XmxSetUniqid(win->id);
      XmxMakeQuestionDialog
#ifndef MOTIF1_23
	(win->hotlist_win, buff, "VMS Mosaic: Remove list",
#else
	(win->base, buff, "VMS Mosaic: Remove list",
#endif /* MOTIF1_23, GEC */
	 remove_confirm_cb, position, 0);
      free(buff);
      free(question);
      free(endquestion);
      XtManageChild(Xmx_w);
  } else {
      delete_hot_from_list(list, hotnode, position);
      URL_Include_Set(win, 0, 0);
  }

  mo_reinit_hotmenu();
  return mo_succeed;
}


/* ----------------------------- mail hotlist ----------------------------- */

static XmxCallback (mailhot_win_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));
  char *to, *subj;
  FILE *fp;

  switch (XmxExtractToken((int)client_data)) {
    case 0:
      XtUnmanageChild(win->mailhot_win);

      to = XmxTextGetString(win->mailhot_to_text);
      if (!to || !*to)
          return;

      subj = XmxTextGetString(win->mailhot_subj_text);

      /* Open a file descriptor to sendmail. */
      fp = mo_start_sending_mail_message(to, subj, "text/x-html", NULL);
      if (fp) {
          mo_write_hotlist(win->current_hotlist, fp);
          mo_finish_sending_mail_message();
      } else {
	  application_error("Unable to mail Hotlist.", "Mail Error");
      }
      free(to);
      free(subj);

      break;
    case 1:
      XtUnmanageChild(win->mailhot_win);
      /* Do nothing. */
      break;
    case 2:
      mo_open_another_window(win, 
         		     mo_assemble_help_url("help-on-hotlist-view.html"), 
         		     NULL, NULL);
      break;
  }

  return;
}

static mo_status mo_post_mailhot_win (mo_window *win)
{
  /* This shouldn't happen. */
  if (!win->hotlist_win)
      return mo_fail;

  if (!win->mailhot_win) {
      Widget dialog_frame;
      Widget dialog_sep, buttons_form;
      Widget mailhot_form, to_label, subj_label;
      
      /* Create it for the first time. */
      XmxSetUniqid(win->id);
      win->mailhot_win = XmxMakeFormDialog 
#ifndef MOTIF1_23
        (win->hotlist_win, "VMS Mosaic: Mail Hotlist");
#else
        (win->base, "VMS Mosaic: Mail Hotlist");
#endif /* MOTIF1_23, GEC */
      dialog_frame = XmxMakeFrame(win->mailhot_win, XmxShadowOut);

      /* Constraints for base. */
      XmxSetConstraints 
        (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
         XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
      
      /* Main form. */
      mailhot_form = XmxMakeForm(dialog_frame);
      
      to_label = XmxMakeLabel(mailhot_form, "Mail To:");
      XmxSetArg(XmNwidth, 335);
      win->mailhot_to_text = XmxMakeTextField(mailhot_form);
      
      subj_label = XmxMakeLabel(mailhot_form, "Subject:");
      win->mailhot_subj_text = XmxMakeTextField(mailhot_form);

      dialog_sep = XmxMakeHorizontalSeparator(mailhot_form);
      
      buttons_form = XmxMakeFormAndThreeButtons
        (mailhot_form, mailhot_win_cb, "Mail", 
	 "Dismiss", "Help...", 0, 1, 2);

      /* Constraints for mailhot_form. */
      XmxSetOffsets(to_label, 14, 0, 10, 0);
      XmxSetConstraints
        (to_label, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_NONE,
         NULL, NULL, NULL, NULL);
      XmxSetOffsets(win->mailhot_to_text, 10, 0, 5, 10);
      XmxSetConstraints
        (win->mailhot_to_text, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_WIDGET,
         XmATTACH_FORM, NULL, NULL, to_label, NULL);

      XmxSetOffsets(subj_label, 14, 0, 10, 0);
      XmxSetConstraints
        (subj_label, XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_FORM, 
         XmATTACH_NONE,
         win->mailhot_to_text, NULL, NULL, NULL);
      XmxSetOffsets (win->mailhot_subj_text, 10, 0, 5, 10);
      XmxSetConstraints
        (win->mailhot_subj_text, XmATTACH_WIDGET, 
         XmATTACH_NONE, XmATTACH_WIDGET,
         XmATTACH_FORM, win->mailhot_to_text, NULL, subj_label, NULL);

      XmxSetArg(XmNtopOffset, 10);
      XmxSetConstraints 
        (dialog_sep, XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, 
         XmATTACH_FORM,
         win->mailhot_subj_text, buttons_form, NULL, NULL);
      XmxSetConstraints 
        (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
         XmATTACH_FORM,
         NULL, NULL, NULL, NULL);
  }
  
  XtManageChild(win->mailhot_win);
  
  return mo_succeed;
}


static XmxCallback(hotlist_rbm_toggle_cb)
{
  mo_window *win;
  Boolean rv;
  int *pos_list;
  int pos_cnt;
  mo_hotlist *list;
  mo_hot_item *hotnode;

#ifndef VMS
  win = current_win;
#else
  win = (mo_window *) client_data;
#endif /* VMS, GEC */
  if (!win || !win->current_hotlist || !win->hotlist_list)
      return;

  list = win->current_hotlist;
  rv = XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt);
  if (rv && pos_cnt) {
      FindHotFromPos(hotnode, list, pos_list[0]);
      if (!hotnode)
	  return;
      URL_Include_Set(win, (hotnode->type == mo_t_url ?
			    hotnode->hot.rbm :
			    hotnode->list.rbm),	1);
  } else {
      XmxMakeErrorDialog
#ifndef MOTIF1_23
	 (win->hotlist_win,
#else
	 (win->base,
#endif /* MOTIF1_23, GEC */
	  "No entry in the hotlist is currently selected.\n\nTo go to an entry in the hotlist,\nselect it with a single mouse click\nand press the Go To button again.",
          "Error: Nothing Selected");
      XtManageChild(Xmx_w);
  }

  return;
}

/* ---------------------------- hotlist_win_cb ---------------------------- */

static XmxCallback (hotlist_win_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));

  switch (XmxExtractToken((int)client_data)) {
    case 0:
      XtUnmanageChild(win->hotlist_win);
      /* Dismissed -- do nothing. */
      break;
    case 1:
      mo_post_mailhot_win(win);
      break;
    case 2:
      mo_open_another_window(win, 
         		     mo_assemble_help_url("help-on-hotlist-view.html"),
         		     NULL, NULL);
      break;
    case 3:
      /* Add current. */
      if (win->current_node) {
          mo_add_node_to_current_hotlist(win);
          mo_write_default_hotlist();
	  URL_Include_Set(win, 0, 0);
      }
      break;
    case 4:
      /* Goto selected. */
      {
        Boolean rv;
        int *pos_list;
        int pos_cnt;

        rv = XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt);
        if (rv && pos_cnt) {
            mo_visit_hotlist_position(win, pos_list[0]);
        } else {
            XmxMakeErrorDialog
#ifndef MOTIF1_23
              (win->hotlist_win,
#else
              (win->base,
#endif /* MOTIF1_23, GEC */
	       "No entry in the hotlist is currently selected.\n\nTo go to an entry in the hotlist,\nselect it with a single mouse click\nand press the Go To button again.",
	       "Error: Nothing Selected");
            XtManageChild(Xmx_w);
        }
      }
      break;
    case 5:
      /* Remove selected. */
      {
        Boolean rv;
        int *pos_list;
        int pos_cnt;

        rv = XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt);
        if (rv && pos_cnt) {
            mo_delete_position_from_current_hotlist(win, pos_list[0]);
            mo_write_default_hotlist();
        } else {
            XmxMakeErrorDialog
#ifndef MOTIF1_23
              (win->hotlist_win,
#else
              (win->base,
#endif /* MOTIF1_23, GEC */
	       "No entry in the hotlist is currently selected.\n\nTo remove an entry in the hotlist,\nselect it with a single mouse click\nand press the Remove button again.",
	       "Error: Nothing Selected");
            XtManageChild(Xmx_w);
        }
      }
      break;
    case 6:
      /* Edit title of selected. */
      {
        Boolean rv;
        int *pos_list;
        int pos_cnt;

        rv = XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt);
        if (rv && pos_cnt) {
            mo_edit_current_hotlist_title(win, pos_list[0]);
	    XtFree((char *)pos_list);
            /* Writing the default hotlist takes place in the callback. */
        } else {
            XmxMakeErrorDialog
#ifndef MOTIF1_23
              (win->hotlist_win,
#else
              (win->base,
#endif /* MOTIF1_23, GEC */
	       "No entry in the hotlist is currently selected.\n\nTo edit an entry in the hotlist,\nselect it with a single mouse click\nand press the Edit button again.",
	       "Error: Nothing Selected");
            XtManageChild(Xmx_w);
        }
      }
      break;
    case 7:
      /* Copy item to cut buffer */
      {
        int *pos_list;
        int pos_cnt;

        if (XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt) &&
	    pos_cnt) {
	    mo_copy_hotlist_position(win, pos_list[0]);
	    XtFree((char *)pos_list); /* DXP */
	} else {
            XmxMakeErrorDialog
#ifndef MOTIF1_23
              (win->hotlist_win,
#else
              (win->base,
#endif /* MOTIF1_23, GEC */
               "No entry in the hotlist is currently selected.\n\nTo copy an entry in the hotlist,\nselect it with a single mouse click\nand press the Copy button again.",
	       "Error: Nothing Selected");
            XtManageChild(Xmx_w);
        }
      }
      break;
    case 8:
      /* Insert an Item in the current hotlist */
      mo_insert_item_in_current_hotlist(win);
      break;
    case 9:
      /* Go Up one level */
      if (win->current_hotlist->parent != 0) {
	  char *path = mo_compute_hot_path(win->current_hotlist->parent);

	  XmListDeleteAllItems(win->hotlist_list);
	  win->current_hotlist = win->current_hotlist->parent;
	  XmxTextSetString(win->hotlist_label, path);
	  free(path);
	  mo_load_hotlist_list(win, win->hotlist_list);
	  URL_Include_Set(win, 0, 0);
      }
      break;
    case 10:
      /* Save in a file */
      XmxSetUniqid(win->id);
      if (!win->save_hotlist_win) {
	  win->save_hotlist_win = XmxMakeFileSBDialog
#ifndef MOTIF1_23
	    (win->hotlist_win,
#else
	    (win->base,
#endif /* MOTIF1_23, GEC */
	     "VMS Mosaic: Save Current hotlist",
	     "Name for saved hotlist", save_hot_cb, 0);
      } else {
	  XmFileSelectionDoSearch(win->save_hotlist_win, NULL);
      }
      XmxManageRemanage(win->save_hotlist_win);
      break;
    case 11:
      /* Load a hotlist file */
      if (!win->load_hotlist_win) {
	  Widget frame, workarea, tb;

	  XmxSetUniqid(win->id);
	  win->load_hotlist_win = XmxMakeFileSBDialog
#ifndef MOTIF1_23
	    (win->hotlist_win,
#else
	    (win->base,
#endif /* MOTIF1_23, GEC */
	     "VMS Mosaic: Load in Current hotlist",
	     "Name of file to open" , load_hot_cb, 0);
	  /* This makes a frame as a work area for the dialog box. */
	  XmxSetArg(XmNmarginWidth, 5);
	  XmxSetArg (XmNmarginHeight, 5);
	  frame = XmxMakeFrame(win->load_hotlist_win, XmxShadowEtchedIn);
	  XmxSetArg(XmNorientation, XmHORIZONTAL);
	  workarea = XmxMakeRadioBox(frame);
	  tb = XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass,
				       workarea,
				       XtVaTypedArg, XmNlabelString,
				       XtRString, "Create new hotlist",
				       strlen("Create new hotlist" ) + 1,
				       XmNmarginHeight, 0,
				       XmNset, True, NULL);
	  XmxSetArg(XmNuserData, (XtArgVal)tb);
	  XmxSetValues(win->load_hotlist_win);
	  XtVaCreateManagedWidget("toggle", xmToggleButtonGadgetClass,
				  workarea,
				  XtVaTypedArg, XmNlabelString,
				  XtRString, "Load in current hotlist",
				  strlen("Load in current hotlist" ) + 1,
				  XmNmarginHeight, 0,
				  NULL);
      } else {
	  XmFileSelectionDoSearch(win->load_hotlist_win, NULL);
      }
      XmxManageRemanage(win->load_hotlist_win);
      break;
    case 12:
      /* Add selected to the RBM or take it away... */
      {
        Boolean rv;
        int *pos_list;
        int pos_cnt;

        rv = XmListGetSelectedPos(win->hotlist_list, &pos_list, &pos_cnt);
        if (rv && pos_cnt) {
	    mo_rbm_toggle_in_hotlist(win, pos_list[0]);
	    XtFree((char *)pos_list);
	    mo_write_default_hotlist();
	} else {
            XmxMakeErrorDialog
#ifndef MOTIF1_23
              (win->hotlist_win,
#else
              (win->base,
#endif /* MOTIF1_23, GEC */
	       "No entry in the hotlist is currently selected.\n\nTo edit an entry in the hotlist,\nselect it with a single mouse click\nand press the Edit button again.",
	       "Error: Nothing Selected");
            XtManageChild(Xmx_w);
        }
      }
      break;
  }

  return;
}

static XmxCallback (hotlist_list_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));
  XmListCallbackStruct *cs = (XmListCallbackStruct *)call_data;

  URL_Include_Set(win, 0, 0);

  mo_visit_hotlist_position(win, cs->item_position);
  
  /* Don't unmanage the list. */
  
  return;
}

/* ------------------------- mo_post_hotlist_win -------------------------- */

/*
 * Pop up a hotlist window for a mo_window.
 */
mo_status mo_post_hotlist_win (mo_window *win)
{
  if (!win->hotlist_win) {
      Widget dialog_frame;
      Widget dialog_sep, buttons_form, buttons1_form, buttons2_form;
      Widget hotlist_form;
      XtTranslations listTable;
      static char listTranslations[] =
	"~Shift ~Ctrl ~Meta ~Alt <Btn2Down>: ListBeginSelect() \n\
	 Button2<Motion>:		ListButtonMotion()\n\
	 ~Shift ~Ctrl ~Meta ~Alt <Btn2Up>:  ListBeginSelect() ListEndSelect()";

      listTable = XtParseTranslationTable(listTranslations);

      /* Create it for the first time. */
      XmxSetUniqid(win->id);
      XmxSetArg(XmNwidth, get_pref_int(eHOTLIST_MENU_WIDTH));
      XmxSetArg(XmNheight, get_pref_int(eHOTLIST_MENU_HEIGHT));
      win->hotlist_win = XmxMakeFormDialog 
        (win->base, "VMS Mosaic: Hotlist View");
      dialog_frame = XmxMakeFrame(win->hotlist_win, XmxShadowOut);
      
      /* Constraints for base. */
      XmxSetConstraints 
        (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
         XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
      
      /* Main form. */
      hotlist_form = XmxMakeForm(dialog_frame);

      win->current_hotlist = (mo_hotlist *)default_hotlist;
      XmxSetArg(XmNcursorPositionVisible, False);
      XmxSetArg(XmNeditable, False);
      XmxSetArg(XmNvalue, (XtArgVal)"/");
      win->hotlist_label = XmxMakeTextField(hotlist_form);
      XmxAddClue(win->hotlist_label, "Displays current Hotlist level");

      buttons1_form = XmxMakeFormAndFourButtons
        (hotlist_form, hotlist_win_cb, "Add Current",
	 "Goto URL", "Remove", 
	 "Edit", 3, 4, 5, 6);
      XmxSetButtonClue("Add current URL to Hotlist", "Open selected entry",
		       "Delete selected entry", "Edit selected entry", NULL);

      buttons2_form = XmxMakeFormAndThreeButtons
        (hotlist_form, hotlist_win_cb, "Copy", "Insert", "Up", 7, 8, 9);
      XmxSetButtonClue("Copy entry to cut buffer",
		       "Insert entry into Hotlist",
		       "Move up one level in Hotlist", NULL, NULL);
      XmxSetArg(XmNfractionBase, (XtArgVal)4);
      XmxSetArg(XmNverticalSpacing, (XtArgVal)0);
      XmxSetValues(buttons2_form);

      /* Hotlist list itself. */
      XmxSetArg(XmNresizable, False);
      XmxSetArg(XmNscrollBarDisplayPolicy, XmSTATIC);
      XmxSetArg(XmNlistSizePolicy, XmCONSTANT);
      win->hotlist_list = XmxMakeScrolledList(hotlist_form, hotlist_list_cb, 0);
      XtAugmentTranslations(win->hotlist_list, listTable);
      XtAddCallback(win->hotlist_list,
		    XmNbrowseSelectionCallback, hotlist_rbm_toggle_cb,
		    (XtPointer) win);
      XmxAddClue(win->hotlist_list, "Double click on entry to visit it");

      win->hotlist_rbm_toggle = XmxMakeToggleButton(hotlist_form,
						 "Include On Right Button Menu",
						 hotlist_win_cb, 12);
      XmxAddClue(win->hotlist_rbm_toggle,
		 "Include selected entry on the mouse Hotlist menu");

      URL_Include_Set(win, 0, 0);

      dialog_sep = XmxMakeHorizontalSeparator(hotlist_form);
      
      buttons_form = XmxMakeFormAndFiveButtons(hotlist_form, hotlist_win_cb,
					       "Mail To...",
					       "Save",
					       "Load",
					       "Dismiss",
					       "Help...",
					       1, 10, 11, 0, 2);
      XmxSetButtonClue("Send Hotlist via Mail", "Save Hotlist to file",
		       "Load Hotlist from file", "Close this menu",
		       "Open help in new Mosaic window");
      /* Constraints for hotlist_form. */
      /* buttons1_form: top to nothing, bottom to hotlist_list,
       * left to form, right to form. */
      XmxSetOffsets(win->hotlist_label,  4, 0, 2, 2);
      XmxSetConstraints
	(win->hotlist_label, XmATTACH_FORM,  XmATTACH_NONE, XmATTACH_FORM,
	 XmATTACH_FORM, NULL, NULL, NULL, NULL);
      XmxSetOffsets(buttons1_form, 0, 0, 0, 0);
      XmxSetConstraints
        (buttons1_form, 
         XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
         win->hotlist_label, NULL, NULL, NULL);
      XmxSetOffsets(buttons2_form, 0, 2, 0, 0);
      XmxSetConstraints
        (buttons2_form, 
         XmATTACH_WIDGET, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
	 buttons1_form, NULL, NULL, NULL);
      /* list: top to form, bottom to rbm_toggle,
         etc... */
      XmxSetOffsets(XtParent (win->hotlist_list), 10, 10, 8, 8);
      XmxSetConstraints
        (XtParent(win->hotlist_list), 
         XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_FORM, 
         buttons2_form, win->hotlist_rbm_toggle, NULL, NULL);
      XmxSetOffsets(win->hotlist_rbm_toggle, 0, 10, 6, 6);
      XmxSetConstraints
        (win->hotlist_rbm_toggle, 
         XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_NONE, 
         NULL, dialog_sep, NULL, NULL);
      XmxSetConstraints 
        (dialog_sep, 
         XmATTACH_NONE, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_FORM,
         NULL, buttons_form, NULL, NULL);
      XmxSetConstraints 
        (buttons_form, XmATTACH_NONE, XmATTACH_FORM, XmATTACH_FORM, 
         XmATTACH_FORM,
         NULL, NULL, NULL, NULL);
      win->save_hotlist_win = win->load_hotlist_win = NULL;
      win->hot_cut_buffer = NULL;
      /* Go get the hotlist up to this point set up... */
      mo_load_hotlist_list(win, win->hotlist_list);
  }
  
  XmxManageRemanage(win->hotlist_win);
  
  return mo_succeed;
}

/* -------------------- mo_add_node_to_current_hotlist -------------------- */

mo_status mo_add_node_to_current_hotlist (mo_window *win)
{
  if (!win->hotlist_win)
      win->current_hotlist = (mo_hotlist *)default_hotlist;

  return mo_add_item_to_hotlist(win->current_hotlist, mo_t_url,
				win->current_node->title,
				win->current_node->url, 0,
				get_pref_boolean(eADD_HOTLIST_ADDS_RBM));
}

void mo_init_hotmenu()
{
    mo_init_hotlist_menu((mo_hotlist *) default_hotlist);
}

static void mo_reinit_hotmenu()
{
    mo_reinit_hotlist_menu((mo_hotlist *) default_hotlist);
}

void mo_rbm_myself_to_death(mo_window *win, int val)
{
    RecursiveSetList(win, (mo_hotlist *)default_hotlist, val, 1);
    mo_reinit_hotmenu();
}
