/****************************************************************************
 * 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, 2004, 2005, 2006, 2007
 * The VMS Mosaic Project
 */

#include "../config.h"
#include "mosaic.h"
#include "gui.h"
#include "mo-www.h"
#include "mailto.h"
#include "gui-popup.h"
#include "gui-documents.h"
#include "globalhist.h"
#include "history.h"
#include <time.h>
#include <sys/types.h>
#include "../libhtmlw/HTMLP.h"
#include <Xm/List.h>

//#ifdef VMS
extern char ftp_type[16];
//#endif /* VMS, BSN */

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

/* For memcpy */
#ifndef VMS
#include <memory.h>
#endif

#ifndef DISABLE_TRACE
extern int srcTrace;
#endif

/* Used by frame_cb in gui.c */
int mo_recorded_visit;

/* ------------------------------------------------------------------------ */
/* ----------------------------- HISTORY LIST ----------------------------- */
/* ------------------------------------------------------------------------ */

/* ---------------------------- kill functions ---------------------------- */

/* Free the data contained in a mo_node and the node itself. */
mo_status mo_free_node(mo_node *node)
{
  if (node->texthead)
      free(node->texthead);
  if (node->title)
      free(node->title);
  if (node->url)
      free(node->url);
  if (node->ref)
      free(node->ref);
  if (node->cached_widgets)
      HTMLFreeWidgetInfo(node->cached_widgets);
  if (node->cached_forms)
      HTMLFreeFormInfo(node->cached_forms);
  if (node->image_file) {
      remove(node->image_file);
      free(node->image_file);
  }
  if (node->last_modified)
      free(node->last_modified);
  if (node->expires)
      free(node->expires);
  if (node->cipher)
      free(node->cipher);
  if (node->cipher_issuer)
      free(node->cipher_issuer);
  if (node->charset)
      free(node->charset);
  if (node->ftp_type)
      free(node->ftp_type);
  if (node->frames) {
      mo_frame *frame = node->frames;
      mo_frame *tmp;

      while (frame) {
	  /* Deaccess its text in cache */
	  mo_deaccess_cached_frame_data(frame->url);
	  if (frame->cached_widgets) {
	      WidgetInfo *wid = (WidgetInfo *) frame->cached_widgets;

	      if (--wid->cache_count < 1) {
		  if (wid->cached_forms)
		      HTMLFreeFormInfo(wid->cached_forms);
	          HTMLFreeWidgetInfo(frame->cached_widgets);
	      }
	  }
	  if (frame->scrolled_win) {
	      HTMLWidget hw = (HTMLWidget) frame->scrolled_win;

	      hw->html.widget_list = NULL;
	      if (--hw->html.node_count < 1)
		  XtDestroyWidget(frame->scrolled_win);
	  }		  
	  tmp = frame;
	  frame = frame->next;
	  free(tmp);
      }
  }
  free(node);

  return mo_succeed;
}

/* Kill a single mo_node associated with a given mo_window; if
 * the history list exists we delete it from that.  In any case
 * we call mo_free_node and return. */
mo_status mo_kill_node(mo_window *win, mo_node *node)
{
  if (win->history_list)
      XmListDeletePos(win->history_list, node->position);
  mo_free_node(node);

  return mo_succeed;
}

/* Iterate through all descendents of an mo_node, but not the given
 * mo_node itself, and kill them.  This is equivalent to calling
 * mo_kill_node on each of those nodes, except this is faster since
 * all the Motif list entries can be killed at once. */
mo_status mo_kill_node_descendents(mo_window *win, mo_node *node)
{
  mo_node *next = node->next;
  mo_node *freeit;
  int count = 0;

  while (next) {
      freeit = next;
      next = next->next;
      mo_free_node(freeit);
      count++;
  }

  /* Free count number of items from the end of the list... */
  if (win->history_list && count)
      XmListDeleteItemsPos(win->history_list, count, node->position + 1);

  return mo_succeed;
}

/* ------------------------ Widget data caching ------------------------ */

static mo_status mo_cache_node_widget_data(mo_node *node)
{
  if (node->cached_widgets)
      HTMLCacheWidgetInfo(node->cached_widgets);
  if (node->frames) {
      mo_frame *frame = node->frames;

      while (frame) {
          if (frame->cached_widgets) {
              WidgetInfo *wid = (WidgetInfo *) frame->cached_widgets;

              /* Must only be in use by this node */
              if (wid->cache_count == 1)
                  HTMLCacheWidgetInfo(wid);
          }
          frame = frame->next;
      }
  }
  return mo_succeed;
}

/* ------------------------ mo_add_node_to_history ------------------------ */

/* Called from mo_record_visit to insert a mo_node into the history
 * list of an mo_window. */
static mo_status mo_add_node_to_history(mo_window *win, mo_node *node)
{
  static int displayURLs;
  static int init = 0;

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

  /* If there is no current node, this is our first time through. */
  if (!win->history) {
      win->history = node;
      node->previous = node->next = NULL;
      node->position = 1;
      win->current_node = node;
  } else {
      /* Node becomes end of history list.
       * Point back at current node. */
      node->previous = win->current_node;
      /* Point forward to nothing. */
      node->next = NULL;
      node->position = node->previous->position + 1;
      /* Kill descendents of current node, since we'll never
       * be able to go forward to them again. */
      mo_kill_node_descendents(win, win->current_node);
      /* Cache widget data of current node after killing descendents */
      mo_cache_node_widget_data(win->current_node);
      /* Current node points forward to this. */
      win->current_node->next = node;
      /* Current node now becomes new node. */
      win->current_node = node;
  }

  if (win->history_list) {
      XmString xmstr = XmxMakeXmstrFromString(displayURLs ?
			                      node->url : node->title);

      XmListAddItemUnselected(win->history_list, xmstr, node->position);
      XmStringFree(xmstr);
  }

  return mo_succeed;
}

/* ---------------------------- mo_grok_title ----------------------------- */

/* Make up an appropriate title for a document that does not otherwise
 * have one associated with it. */
static char *mo_grok_alternate_title(char *url, char *ref, mo_node *node)
{
  char *title, *foo1, *foo2;

  if (!strncmp(url, "gopher:", 7)) {
      /* It's a gopher server.
       * Do we have a ref? */
      if (ref) {
          char *tmp = ref;

          while (*tmp && (*tmp == ' ' || *tmp == '\t'))
              tmp++;
          title = strdup(tmp);
          goto done;
      } else {
          /* Nope, no ref.  Make up a title. */
          foo1 = url + 9;
          foo2 = strstr(foo1, ":");
          /* If there's a trailing colon (always should be.. ??)... */
          if (foo2) {
              char *server = (char *) malloc(foo2 - foo1 + 2);
              
              memcpy(server, foo1, foo2 - foo1);
              server[foo2 - foo1] = '\0';
              
              title = (char *) malloc((strlen(server) + 32) * sizeof(char));
              sprintf(title, "%s %s", "Gopher server at" , server);
              
              /* OK, we got a title... */
              free(server);

              goto done;
          } else {
              /* Aw hell... */
              title = strdup("Gopher server");
              goto done;
          }
      }
  }

  /* If get here, assume we should use 'ref' if possible for the WAIS title. */
  if (!strncmp(url, "wais:", 5)) {
      /* It's a WAIS server.
       * Do we have a ref? */
      if (ref) {
          title = strdup(ref);
          goto done;
      } else {
          /* Nope, no ref.  Make up a title. */
          foo1 = url + 7;
          foo2 = strstr(foo1, ":");
          /* If there's a trailing colon (always should be.. ??)... */
          if (foo2) {
              char *server = (char *) malloc(foo2 - foo1 + 2);
              
              memcpy(server, foo1, foo2 - foo1);
              server[foo2 - foo1] = '\0';
              
              title = (char *) malloc((strlen(server) + 32) * sizeof(char));
              sprintf(title, "%s %s", "WAIS server at", server);
              
              /* OK, we got a title... */
              free(server);

              goto done;
          } else {
              /* Aw hell... */
              title = strdup("WAIS server");
              goto done;
          }
      }
  }

  if (!strncmp(url, "news:", 5)) {
      /* It's a news source. */
      if (strstr(url, "@")) {
          /* It's a news article. */
          foo1 = url + 5;
          
          title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
          sprintf(title, "%s %s", "USENET article", foo1);

          goto done;
      } else {
          /* It's a newsgroup. */
          foo1 = url + 5;
          
          title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
          sprintf(title, "%s %s", "USENET newsgroup", foo1);

          goto done;
      }
  }

  if (!strncmp(url, "file:", 5)) {
      /* It's a file. */
      if (!strncmp(url, "file:///", 8)) {
          /* It's a local file. */
          foo1 = url + 7;
          
          title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
          sprintf(title, "%s %s", "Local file", foo1);
          
          goto done;
      } else if (!strncmp(url, "file://localhost/", 17)) {
          /* It's a local file. */
          foo1 = url + 16;
          
          title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
          sprintf(title, "%s %s", "Local file", foo1);
          
          goto done;
      } else {
          /* It's a remote file. */
	  int flen = 0;

          foo1 = url + 7;
#ifndef VMS
          title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
          sprintf(title, "%s %s", "Remote file", foo1);
#else
	  if (node->ftp_type)
	      flen = strlen(node->ftp_type);
          title = (char *)malloc((strlen(foo1) + flen + 32) * sizeof(char));
          if (flen) {
              sprintf(title, "%s %s %s %s", "Remote", node->ftp_type,
		      "file", foo1);
          } else {
              sprintf(title, "%s %s", "Remote file", foo1);
	  }
#endif /* Display ftp server type, BSN */
          
          goto done;
      }
  }
  
  if (!strncmp(url, "ftp:", 4)) {
      /* It's a remote file. */
      int flen = 0;

      foo1 = url + 6;
#ifndef VMS
      title = (char *)malloc((strlen(foo1) + 32) * sizeof(char));
      sprintf(title, "%s %s", "Remote file", foo1);
#else
      if (node->ftp_type)
	  flen = strlen(node->ftp_type);
      title = (char *)malloc((strlen(foo1) + flen + 32) * sizeof(char));
      if (flen) {
          sprintf(title, "%s %s %s %s", "Remote", node->ftp_type, "file", foo1);
      } else {
          sprintf(title, "%s %s", "Remote file", foo1);
      }
#endif /* Display ftp server type, BSN */
        
      goto done;
  }
  
  /* Punt... */
  title = (char *) malloc((strlen(url) + 24) * sizeof(char));
  sprintf(title, "%s %s", "Untitled", url);
  
 done:
  if (strstr(title, "%20")) {
      /* URLs (FTP in particular) may have escaped spaces */
      char *freeit = title;

      title = mo_unescape_spaces(title);
      free(freeit);
  }
  return title;
}

/* Figure out a title for the given URL.  'ref', if it exists,
 * was the text used for the anchor that pointed us to this URL;
 * it is not required to exist. */
char *mo_grok_title(mo_window *win, char *url, char *ref, mo_node *node)
{
  char *title = NULL;
  char *t;

  XtVaGetValues(win->scrolled_win,
		WbNtitleText, &title,
		NULL);
  if (!title || !strcmp(title, "Document")) {
      t = mo_grok_alternate_title(url, ref, node);
  } else {
      char *tmp = title;

      while (*tmp && (*tmp == ' ' || *tmp == '\t'))
          tmp++;

      if (*tmp) {
	  t = strdup(tmp);
      } else {
	  t = mo_grok_alternate_title(url, ref, node);
      }
  }
  mo_convert_newlines_to_spaces(t);

  return t;
}


/* --------------------------- mo_record_visit ---------------------------- */

extern int securityType;

/* Called when we visit a new node (as opposed to backing up or
 * going forward).  Create an mo_node entry, call mo_grok_title
 * to figure out what the title is, and call mo_node_to_history
 * to add the new mo_node to both the window's data structures and
 * to its Motif history list. */
mo_status mo_record_visit(mo_window *win, char *url, char *newtext, 
                          char *newtexthead, char *ref, char *last_modified,
			  char *expires, char *cipher, int cipher_bits,
			  char *cipher_issuer, int cipher_status, char *charset)
{
  mo_node *node = (mo_node *)calloc(1, sizeof(mo_node));

  node->url = strdup(url);
  node->text = newtext;
  node->texthead = newtexthead;
  if (ref)
      node->ref = strdup(ref);

  /** Set by calloc
  node->image_file = NULL;
  node->cached_widgets = NULL;
  node->cached_forms = NULL;
  **/

  mo_recorded_visit = 1;

  /* Figure out what the title is... */
  if (*ftp_type && !strncmp(url, "ftp:", 4))
      node->ftp_type = strdup(ftp_type);
  node->title = mo_grok_title(win, url, ref, node);

  node->authType = securityType;
  securityType = HTAA_NONE;
  mo_gui_check_win_security_icon(node->authType, win);

  if (cipher)
      node->cipher = strdup(cipher);
  if (cipher_issuer)
      node->cipher_issuer = strdup(cipher_issuer);
  node->cipher_bits = cipher_bits;
  node->cipher_status = cipher_status;

  /* This will be recalc'd when we leave this node. */
  node->docid = 1;

  /* This may or may not be filled in later! */
  if (last_modified)
      node->last_modified = strdup(last_modified);
  if (expires)
      node->expires = strdup(expires);
  if (charset)
      node->charset = strdup(charset);

  mo_add_node_to_history(win, node);
  mo_add_to_rbm_history(win, node->url, node->title);

  if (win->frames)
      node->frames = mo_cache_frames(win->frames, 1);

  return mo_succeed;
}

/* ------------------------- Navigation functions ------------------------- */

/* Back up a node */
mo_status mo_back_node(mo_window *win)
{
  mo_node *old_node = win->current_node;

  /* If there is no previous node, choke. */
  if (!old_node || !old_node->previous)
      return mo_fail;

  mo_set_win_current_node(win, old_node->previous);
  win->refreshable = False;	/* Don't do refresh URLs when moving back */
  mo_cache_node_widget_data(old_node);

  return mo_succeed;
}

/* Go forward a node */
mo_status mo_forward_node(mo_window *win)
{
  mo_node *old_node = win->current_node;

  /* If there is no next node, choke. */
  if (!old_node || !old_node->next)
      return mo_fail;

  mo_set_win_current_node(win, old_node->next);
  mo_cache_node_widget_data(old_node);

  return mo_succeed;
}

/* Visit an arbitrary position.  This is called when a history
 * list entry is double-clicked upon.
 *
 * Iterate through the window history; find the mo_node associated
 * with the given position.  Call mo_set_win_current_node.
 */
static mo_status mo_visit_position(mo_window *win, int pos)
{
  mo_node *node;
  
  for (node = win->history; node; node = node->next) {
      if (node->position == pos) {
	  mo_node *old_node = win->current_node;;

          mo_set_win_current_node(win, node);
	  if (old_node)
	      mo_cache_node_widget_data(old_node);
	  return mo_succeed;
      }
  }
  return mo_fail;
}

/* ---------------------------- misc functions ---------------------------- */

#ifdef dump_history
static mo_status mo_dump_history(mo_window *win)
{
#ifndef DISABLE_TRACE
  if (srcTrace) {
      mo_node *node;

      fprintf(stderr, "----------------- history -------------- \n");
      fprintf(stderr, "HISTORY is 0x%08x\n", win->history);
      for (node = win->history; node; node = node->next) {
          fprintf(stderr, "NODE %d %s\n", node->position, node->url);
          fprintf(stderr, "     TITLE %s\n", node->title);
      }
      fprintf(stderr, "CURRENT NODE %d %s\n", win->current_node->position,
              win->current_node->url);
      fprintf(stderr, "----------------- history -------------- \n");
  }
#endif

  return mo_succeed;
}  
#endif

/* ------------------------------------------------------------------------ */
/* ----------------------------- HISTORY GUI ------------------------------ */
/* ------------------------------------------------------------------------ */

/* We've just init'd a new history list widget; look at the window's
 * history and load 'er up. */
static void mo_load_history_list(mo_window *win, Widget list)
{
  mo_node *node;
  static int urls = -1;

  if (urls == -1)
      urls = get_pref_boolean(eDISPLAY_URLS_NOT_TITLES);
  
  for (node = win->history; node; node = node->next) {
      XmString xmstr = XmxMakeXmstrFromString(urls ? node->url : node->title);

      XmListAddItemUnselected(list, xmstr, 0);
      XmStringFree(xmstr);
  }
  XmListSetBottomPos(list, 0);
  if (win->current_node)
      XmListSelectPos(win->history_list, win->current_node->position, False);
  return;
}

/* ----------------------------- mail history ----------------------------- */

static XmxCallback(mailhist_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->mailhist_win);

      to = XmxTextGetString(win->mailhist_to_text);
      if (!to || !*to) {
	  if (to)
	      XtFree(to);
          return;
      }
      subj = XmxTextGetString(win->mailhist_subj_text);

      /* Open a file descriptor to sendmail. */
      if (!(fp = mo_start_sending_mail_message(to, subj, "text/html", NULL))) {
	  application_error("Unable to mail History.", "Mail Error");
          goto oops;
      }
      {
        mo_node *node;
        
        fprintf(fp, "<HTML>\n<H1>History Path From %s</H1>\n<DL>\n",
                get_pref_string(eDEFAULT_AUTHOR_NAME));
        for (node = win->history; node; node = node->next)
            fprintf(fp, "<DT>%s\n<DD><A HREF=\"%s\">%s</A>\n", 
                    node->title, node->url, node->url);
        fprintf(fp, "</DL>\n</HTML>\n");
      }
      mo_finish_sending_mail_message();

     oops:
      XtFree(to);
      XtFree(subj);

      break;
    case 1:
      XtUnmanageChild(win->mailhist_win);
      /* Do nothing. */
      break;
    case 2:
      mo_open_another_window(win, 
         	  mo_assemble_help_url("docview-menubar-navigate.html#history"),
         	  NULL, NULL);
      break;
  }
  return;
}

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

  if (!win->mailhist_win) {
      Widget dialog_frame, dialog_sep, buttons_form;
      Widget mailhist_form, to_label, subj_label;
      
      /* Create it for the first time. */
      XmxSetUniqid(win->id);
      win->mailhist_win = XmxMakeFormDialog(win->history_win,
					    "VMS Mosaic: Mail Window History");
      dialog_frame = XmxMakeFrame(win->mailhist_win, XmxShadowOut);

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

      dialog_sep = XmxMakeHorizontalSeparator(mailhist_form);
      
      buttons_form = XmxMakeFormAndThreeButtons(mailhist_form, mailhist_win_cb,
						"Mail",	"Dismiss", "Help...",
						0, 1, 2);
      /* Constraints for mailhist_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->mailhist_to_text, 10, 0, 5, 10);
      XmxSetConstraints
        (win->mailhist_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->mailhist_to_text, NULL, NULL, NULL);
      XmxSetOffsets(win->mailhist_subj_text, 10, 0, 5, 10);
      XmxSetConstraints
        (win->mailhist_subj_text, XmATTACH_WIDGET, XmATTACH_NONE, 
         XmATTACH_WIDGET,
         XmATTACH_FORM, win->mailhist_to_text, NULL, subj_label, NULL);

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

/* ---------------------------- history_win_cb ---------------------------- */

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

  switch (XmxExtractToken((int)client_data)) {
    case 0:
      XtUnmanageChild(win->history_win);
      /* Dismissed -- do nothing. */
      break;
    case 1:
      mo_post_mailhist_win(win);
      break;
    case 2:
      mo_open_another_window(win, 
                  mo_assemble_help_url("docview-menubar-navigate.html#history"),
                  NULL, NULL);
      break;
  }
  return;
}

static XmxCallback(history_list_cb)
{
  mo_window *win = mo_fetch_window_by_id(XmxExtractUniqid((int)client_data));
  XmListCallbackStruct *cs = (XmListCallbackStruct *)call_data;
  
  mo_visit_position(win, cs->item_position);
  return;
}

mo_status mo_post_history_win(mo_window *win)
{
  if (!win->history_win) {
      Widget dialog_frame, dialog_sep, buttons_form;
      Widget history_label, history_form;
      XtTranslations listTable;
      static char listTranslations[] =
	"~Shift ~Ctrl ~Meta ~Alt <Btn2Down>: ListKbdSelectAll() ListBeginSelect() \n\
	 ~Shift ~Ctrl ~Meta ~Alt <Btn2Up>:   ListEndSelect() ListKbdActivate()";

      listTable = XtParseTranslationTable(listTranslations);
      
      /* Create it for the first time. */
      XmxSetUniqid(win->id);
      win->history_win = XmxMakeFormDialog(win->base,
					   "VMS Mosaic: Window History");
      dialog_frame = XmxMakeFrame(win->history_win, XmxShadowOut);

      /* Constraints for base. */
      XmxSetConstraints 
        (dialog_frame, XmATTACH_FORM, XmATTACH_FORM, 
         XmATTACH_FORM, XmATTACH_FORM, NULL, NULL, NULL, NULL);
      
      /* Main form. */
      history_form = XmxMakeForm(dialog_frame);

      XmxSetArg(XmNalignment, XmALIGNMENT_BEGINNING);
      history_label = XmxMakeLabel(history_form, "Where you've been:");

      /* History list itself. */
      XmxSetArg(XmNresizable, False);
      XmxSetArg(XmNscrollBarDisplayPolicy, XmSTATIC);
      XmxSetArg(XmNlistSizePolicy, XmCONSTANT);
      XmxSetArg(XmNwidth, 380);
      XmxSetArg(XmNheight, 184);
      win->history_list = XmxMakeScrolledList(history_form, history_list_cb, 0);
      XtAugmentTranslations(win->history_list, listTable);
      XmxAddClue(win->history_list, "Double click on entry to visit it");

      dialog_sep = XmxMakeHorizontalSeparator(history_form);
      
      buttons_form = XmxMakeFormAndThreeButtons(history_form, history_win_cb,
						"Mail To...", "Dismiss", 
						"Help...", 1, 0, 2);
      XmxSetButtonClue("Send history via Mail", "Close this menu",
		       "Open help in new Mosaic window", NULL, NULL);

      /* Constraints for history_form. */
      XmxSetOffsets(history_label, 8, 0, 10, 10);
      XmxSetConstraints
        (history_label, XmATTACH_FORM, XmATTACH_NONE, XmATTACH_FORM,
         XmATTACH_NONE, NULL, NULL, NULL, NULL);
      /* History list is stretchable. */
      XmxSetOffsets(XtParent(win->history_list), 0, 10, 10, 10);
      XmxSetConstraints
        (XtParent(win->history_list), 
         XmATTACH_WIDGET, XmATTACH_WIDGET, XmATTACH_FORM, XmATTACH_FORM, 
         history_label, dialog_sep, NULL, NULL);
      XmxSetArg(XmNtopOffset, 10);
      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);

      /* Go get the history up to this point set up... */
      mo_load_history_list(win, win->history_list);
  }
  XmxManageRemanage(win->history_win);
  
  return mo_succeed;
}
