/* WIDE AREA INFORMATION SERVER SOFTWARE:
   No guarantees or restrictions.  See the readme file for the full standard
   disclaimer.

   This is part of the X user-interface for the WAIS software.  Do with it
   as you please.

   jonathan@Think.COM

 *
 * $Log:	qcommands.c,v $
 * Revision 1.3  92/08/05  14:07:47  epeisach
 * ANSI signal handler definition
 * 
 * Revision 1.2  92/08/04  18:29:30  epeisach
 * Prototype corrections
 * 
 * Revision 1.1  92/08/04  18:20:05  epeisach
 * Initial revision
 * 
 * Revision 1.24  92/05/07  14:51:22  jonathan
 * Changed use of setitimer to alarm.  Thanks to
 * steinkel@carlisle-emh2.army.mil.
 * 
 * Revision 1.23  92/04/30  12:22:11  jonathan
 * Modified to understand "quit no save" and desensitivize it when necessary.
 * 
 * Revision 1.22  92/04/28  15:28:24  jonathan
 * Cleaned up use of scrollists.  Fixed bugs in double clicking.  Added
 * ability to view selected sources and relevant documents.
 * 
 * Revision 1.21  92/03/23  16:08:43  jonathan
 * Added file requester, save as button, changed some sensitivity, that kind
 * of stuff.
 * 
 * Revision 1.20  92/03/17  14:24:17  jonathan
 * Added Cursor code, timers, etc.  Really a lot of stuff.
 * 
 * Revision 1.19  92/03/06  14:47:16  jonathan
 * New and Improved source loading!
 * 
 * Revision 1.18  92/03/02  14:34:20  jonathan
 * Added -f to csh call, per blaze@think.com.
 * 
 * Revision 1.17  92/03/01  14:02:08  jonathan
 * Modified all calls to PrintStatus to use PrintStatusW for compatibility
 * with ui PrintStatus.
 * 
 * Revision 1.16  92/02/17  17:48:27  jonathan
 * Moved last_doc to a global, so it can be reset on a new search.
 * 
 * 
 * Revision 1.15  92/02/17  12:55:38  jonathan
 * Added WCAT type to text display.  Added RCSid and $Log too.
 * 
 *
 */

/* this file contains X specific code - it is an integral part of XWAIS */

#ifndef lint
static char *RCSid = "$Header: /afs/athena.mit.edu/astaff/project/wais/src/wais-8-b5/x/RCS/qcommands.c,v 1.3 92/08/05 14:07:47 epeisach Exp $";
#endif

#define _C_QCOMMANDS

#include "xwais.h"
#include <setjmp.h>

static jmp_buf jbuf;

static Boolean editting_new_question, 
  busy = FALSE, searching = FALSE,
  quit = FALSE, save_question = FALSE;

static int last_doc = NO_ITEM_SELECTED, 
  last_qdoc = NO_ITEM_SELECTED,
  last_source = NO_ITEM_SELECTED;

static XQuestion helpquestion = NULL;
static Textbuff current_text;

/* forward declarations */
static void DoTSaveCB _AP((Widget w,
			   XtPointer closure,
			   XtPointer call_data));

static long
GetLineFromPos(text, p)
char *text;
XawTextPosition p;
{
  Textbuff t;
  long i, lines;

  for(lines=0, i=0; (i < p) && (*text != 0); i++, text++)
    if(*text == '\n') lines++;

  return lines;
}

static XawTextPosition
GetPosFromLine(text, line)
char *text;
long line;
{
  Textbuff t;
  long i;
  XawTextPosition pos;

  for(pos=0, i=0; (i < line) && (*text != 0); pos++, text++)
    if(*text == '\n') i++;

  return pos;
}

static int get_selected_type()
{
  return(get_selected_item(typewindow->ListWidget));
}

static int get_selected_qsource(question)
XQuestion question;
{
  return(get_selected_item(question->window->Sources->ListWidget));
}

static int get_selected_qdoc(question)
XQuestion question;
{
  return(get_selected_item(question->window->RelevantDocuments->ListWidget));
}

static int get_selected_response(question)
XQuestion question;
{
  return(get_selected_item(question->window->ResultDocuments->ListWidget));
}

#include <signal.h>

#define TIMEOUT 1

static int new, old; /* timer values */
typedef void (voidfunc)();
static void* alarm_signal;

static void
SetCursors(cursor)
Cursor cursor;
{
  QuestionWindow qw = the_Question->window;
  static Cursor xterm_cursor = 0;

  if(xterm_cursor == 0)
    xterm_cursor = XCreateFontCursor(CurDpy, XC_xterm);

  if(cursor != 0) {
    XDefineCursor(CurDpy, XtWindow(qw->keywordwid), cursor);
    XDefineCursor(CurDpy, XtWindow(qw->shell), cursor);
    XDefineCursor(CurDpy, XtWindow(qw->Sources->ListWidget), cursor);
    XDefineCursor(CurDpy, XtWindow(qw->RelevantDocuments->ListWidget), cursor);
    XDefineCursor(CurDpy, XtWindow(qw->ResultDocuments->ListWidget), cursor);
    XDefineCursor(CurDpy, XtWindow(qw->StatusWindow), cursor);
  }
  else {
    XDefineCursor(CurDpy, XtWindow(qw->keywordwid), xterm_cursor);
    XUndefineCursor(CurDpy, XtWindow(qw->shell));
    XUndefineCursor(CurDpy, XtWindow(qw->Sources->ListWidget));
    XUndefineCursor(CurDpy, XtWindow(qw->RelevantDocuments->ListWidget));
    XUndefineCursor(CurDpy, XtWindow(qw->ResultDocuments->ListWidget));
    XUndefineCursor(CurDpy, XtWindow(qw->StatusWindow));
  }
}

#ifdef _ANSI_C_SOURCE
static void
alarmhandler(sig)
int sig;
#else
static void
alarmhandler(sig, code, scp, addr)
long sig, code;
struct sigcontext *scp;
char *addr;
#endif
{
  XEvent event;
  static int cursor = 0;

  SetCursors(wais_cursors[cursor]);
  cursor = (cursor+1)%NUM_CURSORS;

  while((XtAppPending(app_context)&XtIMXEvent) != 0) {
    XtAppNextEvent(app_context, &event);
    XtDispatchEvent(&event);
  }
  alarm(new);
  return;
}

static void
fuzzButtons()
{
  Arg args[1];
  XEvent event;

  XtSetArg(args[0], XtNsensitive, False);
  if(searching) {
    XtSetValues(viewbutton, args, ONE);
    XtCallActionProc(searchButton, "set", NULL, NULL, 0);
  }
  else {
    XtSetValues(searchButton, args, ONE);
    XtCallActionProc(viewbutton, "set", NULL, NULL, 0);
  }
  XtSetValues(addSourceButton, args, ONE);
  XtSetValues(delSourceButton, args, ONE);
  XtSetValues(addDocButton, args, ONE);
  XtSetValues(delDocButton, args, ONE);
  XtSetValues(helpButton, args, ONE);
  XtSetValues(saveAsButton, args, ONE);
  XtSetValues(doneButton, args, ONE);
  XtSetArg(args[0], XtNsensitive, True);
  XtSetValues(abortButton, args, ONE);
}

void
unfuzzButtons()
{
  Arg args[1];
  XEvent event;

  XtSetArg(args[0], XtNsensitive, True);
  XtSetValues(searchButton, args, ONE);
  XtSetValues(addSourceButton, args, ONE);
  XtSetValues(delSourceButton, args, ONE);
  XtSetValues(addDocButton, args, ONE);
  XtSetValues(delDocButton, args, ONE);
  XtSetValues(helpButton, args, ONE);
  XtSetValues(saveAsButton, args, ONE);
  XtSetValues(doneButton, args, ONE);
  XtSetValues(viewbutton, args, ONE);
  XtSetArg(args[0], XtNsensitive, False);
  XtSetValues(abortButton, args, ONE);
}

void
Abort(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  if(busy) {
    unfuzzButtons();
    longjmp(jbuf, 1);
  }
}  

/* these are the commands used in the question widget */


/* ARGSUSED */
void
DoSearch(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg args[2];
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  char message[255];
  DocList resdocs = q->ResultDocuments;

  double_click = FALSE;
  LastClicked = w;
  last_doc = -1;

  if(busy) return;

  /* update the info */

  strncpy(q->keywords, GetString(qw->keywordwid), STRINGSIZE);

  new = TIMEOUT;

  busy = TRUE;
  searching = TRUE;

  fuzzButtons();
  if (setjmp(jbuf) != 0) {
    busy = FALSE;
    XtCallActionProc(searchButton, "unset", NULL, NULL, 0);
    q->ResultDocuments = resdocs;
    SetCursors(NULL);
    if(q->Sources != NULL) {
      SourceList slist;
      Source source;
      for(slist = q->Sources;
	  slist != NULL;
	  slist = slist->nextSource) {
	if((source = findsource(slist->thisSource->filename)) != NULL)
	  close_source(source);
      }
    }
    PrintStatus("\nAbort Search.  Question unmodified.");
    return;
  }

  alarm_signal = (void*)signal(SIGALRM, alarmhandler);
  old = alarm(new);
  SearchWais(q);
  alarm(old);
  signal(SIGALRM, alarm_signal);
  SetCursors(NULL);
  freeDocList(resdocs);
  busy = FALSE;
  unfuzzButtons();
  XtSetArg(args[0], XtNsensitive, False);
  XtSetArg(args[1], XtNlabel, " View  ");
  XtSetValues(viewbutton, args, TWO);
  XtSetValues(saveAsButton, args, ONE);
  XtCallActionProc(searchButton, "unset", NULL, NULL, 0);

  the_Question->Result_Items = buildDocumentItemList(q->ResultDocuments, TRUE);
  RebuildListWidget(qw->ResultDocuments, the_Question->Result_Items);
}

void
SaveQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg		args[5];
  Position	x, y;
  Dimension	width, height;
  Cardinal	n;
  static char** files;

  n = 0;
  XtSetArg(args[0], XtNwidth, &width); n++;
  XtSetArg(args[1], XtNheight, &height); n++;
  XtGetValues(top, args, n);
  XtTranslateCoords(top, (Position) (width / 2), (Position) (height / 2),
		    &x, &y);

  n = 0;
  XtSetArg(args[n], XtNx, MAX(x-150, 0));		n++;
  XtSetArg(args[n], XtNy, y);				n++;

  if(savelist == NULL)
    savelist = MakeSaveRequester(top);
  
  XtSetValues(savereq, args, n);

  ReplaceText(dirnamewidget, app_resources.questionDirectory);
  ReplaceText(filenamewidget, the_Question->q->name);
  SetDir(NULL, NULL, NULL);
  save_question = TRUE;

  XtSetArg(args[0], XtNsensitive, True);
  XtSetValues(quitbutton, args, ONE);
  XtSetArg(args[0], XtNlabel, "Save&Quit");
  XtSetValues(savebutton, args, ONE);

  XtAddCallback(savebutton, XtNcallback, DoSave, NULL);
  XtAddCallback(quitbutton, XtNcallback, DontSave, NULL);

  XtPopup(savereq, XtGrabNone);
}

/* ARGSUSED */
void CloseQuestionEdit(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  char filename[STRINGSIZE];

  strncpy(the_Question->q->keywords,
	  GetString(the_Question->window->keywordwid),
	  STRINGSIZE);

  if(strcmp(the_Question->q->name, "New Question") == 0) {
    quit = TRUE;
    SaveQuestion(NULL, NULL, NULL);
  }
  else {
    sprintf(filename, "%s%s", app_resources.questionDirectory,
	    the_Question->q->name);
    WriteQuestion(filename, the_Question->q);
    exit(0);
  }
}

static int get_question_response(questionwindow)
QuestionWindow questionwindow;
{
  return(get_selected_item(questionwindow->ResultDocuments->ListWidget));
}

/* ARGSUSED */
void
AddResponseToQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int numdocs, document_number, i;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  DocList this, last;
  float top, shown;

  double_click = FALSE;
  LastClicked = w;

  document_number = get_question_response(qw);

  if(document_number == NO_ITEM_SELECTED) {
    PrintStatusW("\nNo selected response.  Select one and try again.",
	   qw->StatusWindow);
    return;
  }

  /* find and add document to question's relevant documents */

  last = findLast(q->RelevantDocuments);

  for(this = q->ResultDocuments, i = 0; i < document_number; i++)
    this = this->nextDoc;

  if(last != NULL) 
    last->nextDoc = makeDocList(copy_docid(this->thisDoc), NULL);
  else q->RelevantDocuments = makeDocList(copy_docid(this->thisDoc), NULL);

  if(the_Question->Relevant_Items != NULL)
    freeItemList(the_Question->Relevant_Items);

  the_Question->Relevant_Items =
    buildDocumentItemList(q->RelevantDocuments, FALSE);

  q->numdocs = charlistlength(the_Question->Relevant_Items);

  RebuildListWidget(qw->RelevantDocuments, the_Question->Relevant_Items);

  q->modified = TRUE;
}

/* ARGSUSED */

void
AddDocToQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int i;
  int SelectedDoc;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  float top, shown;
  DocumentID docid;
  DocList dlist;

  if ((SelectedDoc = get_selected_response(the_Question)) == NO_ITEM_SELECTED) {
    PrintStatusW("\nNo selected ResultDocument - select one and try again.",
	   qw->StatusWindow);
    return;
  }

  docid = (DocumentID)s_malloc(sizeof(_DocumentID));
  docid->doc = (CRetDocument)s_malloc(sizeof(_CRetDocument));

  dlist = makeDocList(docid, NULL);
  
  /* need to get DocID too - that's not as easy. */

  docid->doc->headline = the_Question->Result_Items[SelectedDoc];

  /* append it to the current sourcelist */
  
  if(q->RelevantDocuments != NULL) {
    DocList doc;

    for(doc = q->RelevantDocuments; doc->nextDoc != NULL; doc = doc->nextDoc);
    doc->nextDoc = dlist;
  }
  else
    q->RelevantDocuments = dlist;

  if (the_Question->Relevant_Items != NULL) 
    freeItemList(the_Question->Relevant_Items);

  the_Question->Relevant_Items = 
    buildDocumentItemList(q->RelevantDocuments, FALSE);
  q->numdocs = charlistlength(the_Question->Relevant_Items);

  RebuildListWidget(qw->RelevantDocuments, the_Question->Relevant_Items);
}

/* ARGSUSED */
void
DeleteQuestionDoc(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int i;
  float shown;
  int SelectedDoc;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  DocList doc, last;

  if((SelectedDoc = get_selected_qdoc(the_Question)) == NO_ITEM_SELECTED) {
    double_click = FALSE;
    LastClicked = w;
    PrintStatusW("\nNo Document selected.  Please select one and try again.",
		qw->StatusWindow);
    return;
  }

  /* rip out the bugger */

  last_qdoc = NO_ITEM_SELECTED;

  q->modified = TRUE;

  double_click = FALSE;
  LastClicked = NULL;
  if (SelectedDoc == 0)
    q->RelevantDocuments = q->RelevantDocuments->nextDoc;
  else {
    for (doc = q->RelevantDocuments, i = 0; i < SelectedDoc-1; i++) {
      doc = doc->nextDoc;
    }
    if(doc->nextDoc != NULL)
      doc->nextDoc = doc->nextDoc->nextDoc;
  }
  if(the_Question->Relevant_Items != NULL) freeItemList(the_Question->Relevant_Items);
  the_Question->Relevant_Items = buildDocumentItemList(q->RelevantDocuments, FALSE);

  q->numdocs--;
  RebuildListWidget(qw->RelevantDocuments, the_Question->Relevant_Items);
}

/* ARGSUSED */
void
PopupSourceMenu(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg		args[5];
  Position	x, y;
  Dimension	width, height;
  Cardinal	n;

  n = 0;
  XtSetArg(args[0], XtNwidth, &width); n++;
  XtSetArg(args[1], XtNheight, &height); n++;
  XtGetValues(w, args, n);
  XtTranslateCoords(w, (Position) 0, (Position)height,
		    &x, &y);

  n = 0;
  XtSetArg(args[n], XtNx, x); n++;
  XtSetArg(args[n], XtNy, y); n++;

  XtSetValues(sshell, args, n);

  XawListUnhighlight(sourcewindow->ListWidget);
  XtPopup(sshell, XtGrabExclusive);
}

  
/* ARGSUSED */
void
AddSourceToQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int i, snum;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  SourceID sid;
  SourceList slist;

  if((snum = get_selected_source()) != NO_ITEM_SELECTED) {
    if(last_source != snum) {
      last_source = snum;
    }
    else {
      XtPopdown(sshell);
      sid = (SourceID)s_malloc(sizeof(_SourceID));
      slist = makeSourceList(sid, NULL);
  
      sid->filename = s_strdup(Source_items[snum]);

      /* append it to the current sourcelist */
  
      if(q->Sources != NULL) {
	SourceList source;

	/* check to see if it's already in the list */

	for(source = q->Sources; source != NULL; source = source->nextSource) {
	  if(strcmp(source->thisSource->filename, sid->filename) == 0) {
	    s_free(sid);
	    s_free(slist);
	    return;
	  }
	  if(source->nextSource == NULL) break;
	}
	source->nextSource = slist;
      }
      else
	q->Sources = slist;

      the_Question->Source_Items = buildSourceItemList(q->Sources);
      q->numsources = charlistlength(the_Question->Source_Items);

      RebuildListWidget(qw->Sources, the_Question->Source_Items);

      q->modified = TRUE;
    }
  }
  else XtPopdown(sshell);
}

/* ARGSUSED */
void
EditQuestionSource(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  SList s;
  int CurrentSource;
  char msg[1000];
  Source edit_source = NULL;

  double_click = FALSE;
  LastClicked = w;

  if ((CurrentSource = get_selected_qsource(the_Question))
      != NO_ITEM_SELECTED) {
    if (last_source == CurrentSource) {
      if((edit_source = 
	  findsource(the_Question->Source_Items[CurrentSource])) == NULL) {
	sprintf(msg, "Cant find source: %s\n", the_Question->Source_Items[CurrentSource]);
	PrintStatus(msg);
	return;
      }
      else {
	sprintf(msg, "Viewing source: %s\n", edit_source->name);
	PrintStatus(msg);
	PopupSource(edit_source);
      }
    }
    else last_source = CurrentSource;
  } 
  else {
    last_source = -1;
    XwaisPrintf("No source selected.\nPlease selected one and try again.\n");
  }
}

/* ARGSUSED */
void
DeleteQuestionSource(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int i;
  float shown;
  int SelectedSource;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  SourceList source, last;

  if((SelectedSource = get_selected_qsource(the_Question)) == NO_ITEM_SELECTED) {
    double_click = FALSE;
    LastClicked = w;
    PrintStatusW("\nNo source selected.  Please select one and try again.",
		qw->StatusWindow);
    return;
  }

  /* rip out the bugger */

  last_source = NO_ITEM_SELECTED;
  q->modified = TRUE;

  double_click = FALSE;
  LastClicked = NULL;
  if (SelectedSource == 0)
    q->Sources = q->Sources->nextSource;
  else {
    for (source = q->Sources, i = 0; i < SelectedSource-1; i++) {
      source = source->nextSource;
    }
    if(source->nextSource != NULL)
      source->nextSource = source->nextSource->nextSource;
  }
  the_Question->Source_Items = buildSourceItemList(q->Sources);

  q->numsources--;
  RebuildListWidget(qw->Sources, the_Question->Source_Items);
}

static char *
findFilter(type)
char *type;
{
  char *p, *i1, *i2, t[80];
  static char result[80];

  /* filters are of the form TYPE,FILTER;... */

  p = app_resources.filters;
  
  while (*p != 0) {
    if((i1 = strchr(p, ',')) == NULL) break;
    strncpy(t, p, i1-p);
    if(!strncmp(t, type, i1-p)) {
      if((i2 = strchr(i1, ';')) != NULL) {
	strncpy(result, i1+1, i2-i1-1);
	result[i2-i1] = 0;
      }
      else strcpy(result, i1+1);
      return result;
    }
    if((p = strchr(i1, ';')) == NULL) break;
    p++;
  }
  return NULL;
}

static Boolean
tryFilter(t, type, filename)
Textbuff t;
char *type;
char *filename;
{
  char fname[STRINGSIZE], command[STRINGSIZE], *text, *viewer;
  FILE *fp;
  long i;

  if((viewer = findFilter(type)) == NULL) return FALSE;

  sprintf(fname, "%s%s",
	  app_resources.documentDirectory,
	  get_filename(t->docid->doc->headline));
  
  if((fp = fopen(fname, "w")) == NULL) {
    sprintf(command, "Error opening file: %s.", fname);
    PrintStatusW(command, the_Question->window->StatusWindow);
    return;
  }

  dumptext(fp, t->text, t->size);
  fclose(fp);

  KillText(t);

  sprintf(command, "\nRunning '%s %s'", viewer, fname);
  PrintStatusW(command, the_Question->window->StatusWindow);
  sprintf(command, "csh -fc '%s %s;/bin/rm %s' &", viewer, fname, fname);
  system(command);

  return TRUE;
}

static void
DoSource(t)
Textbuff t;
{
  char f[STRINGSIZE], message[STRINGSIZE];
  FILE *fp;

  sprintf(f, "/tmp/src%d", getpid());
  if((fp = fopen(f, "w")) == NULL) {
    sprintf(message, "\nError opening file: %s.", f);
    PrintStatusW(message, the_Question->window->StatusWindow);
    return;
  }

  fprintf(fp, t->text);

  fclose(fp);
  if((fp = fopen(f, "r")) == NULL) {
    sprintf(message, "\nError opening file: %s.", f);
    PrintStatusW(message, the_Question->window->StatusWindow);
    return;
  }

  memset(the_Source, 0, sizeof(_Source));

  ReadSource(the_Source, fp);
  fclose(fp);

  if (the_Source->name != NULL) s_free(the_Source->name);
  the_Source->name = s_strdup(get_filename(t->docid->doc->headline));

  unlink(f);

  PopupSource(the_Source);
}

static void do_other_thing(t, type)
Textbuff t;
char *type;
{
  Arg		args[5];
  Position	x, y;
  Dimension	width, height;
  Cardinal	n;
  char message[STRINGSIZE];

  if (type != NULL && type[0] != 0) {
    sprintf(message,
	    "\nDocument is of type: %s, which is unknown. Using Save routine.",
	    type);
    PrintStatusW(message, the_Question->window->StatusWindow);
  }

  n = 0;
  XtSetArg(args[0], XtNwidth, &width); n++;
  XtSetArg(args[1], XtNheight, &height); n++;
  XtGetValues(the_Question->window->shell, args, n);
  XtTranslateCoords(the_Question->window->shell, (Position) (width / 2), (Position) (height / 2),
		    &x, &y);

  n = 0;
  XtSetArg(args[n], XtNx, MAX(x-150, 0));		n++;
  XtSetArg(args[n], XtNy, y);				n++;

  current_text = t;

  if(savelist == NULL)
    savelist = MakeSaveRequester(top);
  
  XtSetValues(savereq, args, n);

  ReplaceText(filenamewidget, "");
  ReplaceText(dirnamewidget, app_resources.documentDirectory);
  SetDir(NULL, NULL, NULL);
  save_question = FALSE;

  XtPopup(savereq, XtGrabNone);
  clearReqButtons();

  XtRemoveAllCallbacks(savebutton, XtNcallback);
  XtAddCallback(savebutton, XtNcallback, DoTSaveCB, t);
}

static boolean
  GetDoc(doc, type, textstruct)
DocumentID doc;
char* type;
Textbuff textstruct;
{
  Question q = the_Question->q;

  new = TIMEOUT;

  busy = TRUE;
  searching = FALSE;
  fuzzButtons();

  if (setjmp(jbuf) != 0) {
    busy = FALSE;
    XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
    SetCursors(NULL);
    if(q->Sources != NULL) {
      SourceList slist;
      Source source;
      for(slist = q->Sources;
	  slist != NULL;
	  slist = slist->nextSource) {
	if((source = findsource(slist->thisSource->filename)) != NULL)
	  close_source(source);
      }
    }
    KillText(textstruct);
    PrintStatus("\nRetrieval aborted.");
    return(false);
  }

  alarm_signal = (void*)signal(SIGALRM, alarmhandler);
  old = alarm(new);
  textstruct->text = GetWaisDocument(q, doc, type, NULL, &textstruct->size);
  alarm(old);
  signal(SIGALRM, alarm_signal);

  busy = FALSE;
  unfuzzButtons();
  XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
  SetCursors(NULL);
  return(true);
}

static void
  ViewDoc(doc, type, size, savep)
DocumentID doc;
char *type;
long size;
Boolean savep;
{
  Arg args[5];
  Cardinal num_args;
  static long request_length = 0;
  static int document_number;
  WAISDocumentText *text;
  char *viewtext, message[255];
  int i;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  Textbuff textstruct;
  TextList thisText, a_tList;

  if((textstruct = findTextDoc(doc, type)) != NULL) {
    if(textstruct->shell != NULL) {
      XtPopdown(textstruct->shell);
      XtPopup(textstruct->shell, XtGrabNone);
    }
    return;
  }

  if(busy) return;

  if(NULL == (thisText = NewText())) {
    PrintStatus("\nUnable to allocate Text structure.  Bummer.");
    return;
  }

  textstruct = thisText->thisText;
  textstruct->size = size;
  if(savep)
    textstruct->type = s_strdup("WaIsOddBall");
  else
    textstruct->type = s_strdup(type);

  if(GetDoc(doc, type, textstruct) &&
     textstruct->text != NULL) {
    textstruct->docid = doc;

    if(textstruct->size == 0) {
      KillText(textstruct);
      PrintStatus("\nNo data returned");
      sleep(2);
      return;
    }

    if(savep)
     do_other_thing(textstruct, NULL);
    else if(tryFilter(textstruct, type)) 
      return;
    else if (type == NULL ||
	     substrcmp(type, "TEXT") ||
	     !strcmp(type, "WCAT")) {
      textstruct->textwindow = MakeTextPopup(top, textstruct, doc->doc->headline);
      num_args = 0;
      XtSetArg(args[num_args], XtNtype, XawAsciiString); num_args++;
      XtSetArg(args[num_args], XtNstring, textstruct->text); num_args++;
      XtSetValues(textstruct->textwindow, args, num_args);
      if (doc->doc->best > 0)
	XawTextSetInsertionPoint(textstruct->textwindow,
				 GetPosFromLine(textstruct->text, doc->doc->best));
      XawTextDisplay(textstruct->textwindow);
    }
    else if (!strcmp(type, "WSRC")) {
      DoSource(textstruct);
    }
    else do_other_thing(textstruct, type);
  }
}

static void PopupTypeMenu(w, doc)
Widget w;
DocumentID doc;
{
  char** types = doc->doc->type;
  int i;
  Arg		args[5];
  Position	x, y;
  Dimension	width, height;
  Cardinal	n;

  n = 0;
  XtSetArg(args[0], XtNwidth, &width); n++;
  XtSetArg(args[1], XtNheight, &height); n++;
  XtGetValues(w, args, n);
  XtTranslateCoords(w, (Position) 0, (Position)height,
		    &x, &y);

  n = 0;
  XtSetArg(args[n], XtNx, x); n++;
  XtSetArg(args[n], XtNy, y); n++;

  XtSetValues(typeshell, args, n);

  for(i = 0; types[i] != NULL; i++)
    Type_items[i] = types[i];

  RebuildListWidget(typewindow, Type_items);

  XtPopup(typeshell, XtGrabExclusive);
}  

void doType(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  long tnum, dnum, size = -1;
  DocumentID doc;
  
  if((tnum = get_selected_type()) != NO_ITEM_SELECTED) {
    XtPopdown(typeshell);
    dnum = get_selected_response(the_Question);
    if((doc = findDoc(the_Question->q->ResultDocuments, dnum)) != NULL)
      ViewDoc(doc, Type_items[tnum], size, false);
  }
  else
    XtPopdown(typeshell);
}

/* ARGSUSED */
void
ViewResponse(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg args[2];
  static int document_number;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  DocumentID doc;
  Boolean savep;

  double_click = FALSE;
  LastClicked = w;

  if(busy) return;

  if(w == saveAsButton) 
    savep = true;
  else
    savep = false;

  document_number = get_selected_response(the_Question);

  if(document_number == NO_ITEM_SELECTED) {
    PrintStatusW("\nNo selected response.  Select one and try again.",
		qw->StatusWindow);
    return;
  }

  if((doc = findDoc(q->ResultDocuments, document_number)) == NULL) {
    PrintStatusW("\nUnable to find document.  This should not happen.",
		qw->StatusWindow);
    return;
  }

  if (document_number != last_doc) {
    XtSetArg(args[0], XtNsensitive, True);
    if((w != viewbutton) && (doc->doc->type[1] != NULL)) {
      XtSetArg(args[1], XtNlabel, "View...");
    }
    else {
      XtSetArg(args[1], XtNlabel, " View  ");
    }
    XtSetValues(viewbutton, args, TWO);
    XtSetValues(saveAsButton, args, ONE);
    last_doc = document_number;
    return;
  }

  if((w == viewbutton) && (doc->doc->type[1] != NULL)) {
    PopupTypeMenu(w, doc);
  }
  else ViewDoc(doc, doc->doc->type[0], 0, savep);
}

/* ARGSUSED */
void
ViewRelevant(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int document_number;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  DocumentID doc;

  double_click = FALSE;
  LastClicked = w;

  if(busy) return;

  document_number = get_selected_qdoc(the_Question);

  if(document_number == NO_ITEM_SELECTED) {
    PrintStatusW("\nNo selected response.  Select one and try again.",
		qw->StatusWindow);
    return;
  }

  if((doc = findDoc(q->RelevantDocuments, document_number)) == NULL) {
    PrintStatusW("\nUnable to find document.  This should not happen.",
		qw->StatusWindow);
    return;
  }

  if (document_number != last_qdoc) {
    last_qdoc = document_number;
    return;
  }

  ViewDoc(doc, doc->doc->type[0], 0, false);
}

void
EndText(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Textbuff t;

  t = findText(w);

  if(t != NULL) {
    XtPopdown(t->shell);
    KillText(t);
  }
}
static Widget helpwindow = NULL;

void EndHelp(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(helpwindow);
}

void XwaisHelp(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Widget textwindow, frame, button;
  Arg arglist[10];
  Cardinal num_args;
  static String items[] = {NULL};

  if (helpwindow == NULL) {
    num_args = 0;
    XtSetArg(arglist[num_args], XtNtitle, "X WAIS Help"); num_args++;
    XtSetArg(arglist[num_args], XtNiconName, "X WAIS Help"); num_args++;
    helpwindow = XtCreatePopupShell("textpopup", applicationShellWidgetClass,
				    XtParent(w), arglist, num_args);
    frame =
      XtCreateManagedWidget("helppopupform", formWidgetClass,
			    helpwindow, NULL, ZERO);
    num_args = 0;
    XtSetArg(arglist[num_args], XtNtype, XawAsciiFile); num_args++;
    XtSetArg(arglist[num_args], XtNstring, app_resources.helpFile); num_args++;
    XtSetArg(arglist[num_args], XtNeditType, XawtextRead); num_args++;
    textwindow =
      XtCreateManagedWidget("textWindow", asciiTextWidgetClass, frame, arglist, num_args);
    button = MakeCommandButton(frame, "tdone", EndHelp,
			       textwindow, NULL, NULL);
    SettIcon(helpwindow);
  }
  XtPopup(helpwindow, XtGrabNone);
}

#include <X11/Xaw/TextP.h>


static XawTextPosition
findstring(text, string, casesensitive)
char *text, *string;
Boolean casesensitive;
{
  char *t, *t2, *t3;

  if (casesensitive) {
    for (t = text; *t != 0; t++) {
      if (*t == *string) {
	t2 = t;
	t3 = string;
	do {
	  t2++;
	  t3++;
	  if(*t3 == 0) return((XawTextPosition)(t-text));
	}
	while(*t2 == *t3);
      }
    }
    return -1;
  }
  else {
    for (t = text; *t != 0; t++) {
      if (tolower(*t) == tolower(*string)) {
	t2 = t;
	t3 = string;
	do {
	  t2++;
	  t3++;
	  if(*t3 == 0) return((XawTextPosition)(t-text));
	}
	while(tolower(*t2) == tolower(*t3));
      }
    }
    return -1;
  }
}

/* ARGSUSED */
void
showKeyword(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  Textbuff t;
  static char msg[STRINGSIZE], str[STRINGSIZE], minstr[STRINGSIZE], *keys;
  Widget tw;
  XawTextPosition minpos, pos, pos2, offset;
  XawTextBlock text;
  int i, j, k;
  char c1, c2;

  if((t = findText(w)) == NULL) {
    XwaisPrintf("couldn't find text.\n");
    return;
  }

  keys = q->keywords;
  tw = t->textwindow;

  minpos = 999999;
  minstr[0] = 0;

  sprintf(msg, "\nSearching for next keyword...");
  PrintStatusW(msg, t->status);

  /* parse the keywords into individual words */
  for(j = 0, i = 0; i <= strlen(keys); i++) {
    str[j] = keys[i];
    if ((keys[i] == 0) || (keys[i] == ' ') || (keys[i] == '\n')) {
      str[j] = 0;
      j = 0;

      text.ptr = str;
      text.length = strlen(str);
      text.firstPos = 0;
      text.format = FMT8BIT;
      
      for(offset = XawTextGetInsertionPoint(tw), 
	  pos = findstring(t->text+offset, str, FALSE);
	  pos > 0;
	  offset=pos+1, 
	  pos = findstring(t->text+offset, str, FALSE)) {
	pos+=offset;
	c1 = t->text[pos-1];
	c2 = t->text[pos+text.length];
	if((isspace(c1) || ispunct(c1)) &&
	   (isspace(c2) || ispunct(c2))) {
	  if (pos < minpos) {
	    minpos = pos;
	    strcpy(minstr, str);
	    break;
	  }
	}
      }
    }
    else
      j++;
  }

  if (minpos == 999999) {
    sprintf( msg, "\nCould not find any more keywords.");
    XawTextUnsetSelection(tw);
    PrintStatusW(msg, t->status);
    return;
  }
  else {
    XawTextSetInsertionPoint( tw, minpos + strlen(minstr));
    sprintf(msg, "\nSearching for next keyword...done");
    PrintStatusW(msg, t->status);
    XawTextSetSelection( tw, minpos, minpos + strlen(minstr));
    return;
  }
}

void
SaveText(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg		args[5];
  Position	x, y;
  Dimension	width, height;
  Cardinal	n;
  Textbuff t;

  if((t = findText(w)) == NULL) {
    XwaisPrintf("couldn't find text.\n");
    return;
  }

  n = 0;
  XtSetArg(args[0], XtNwidth, &width); n++;
  XtSetArg(args[1], XtNheight, &height); n++;
  XtGetValues(t->textwindow, args, n);
  XtTranslateCoords(t->textwindow, (Position) (width / 2), (Position) (height / 2),
		    &x, &y);

  n = 0;
  XtSetArg(args[n], XtNx, MAX(x-150, 0));		n++;
  XtSetArg(args[n], XtNy, y);				n++;

  current_text = t;

  if(savelist == NULL)
    savelist = MakeSaveRequester(top);
  
  XtSetValues(savereq, args, n);

  ReplaceText(filenamewidget, "");
  ReplaceText(dirnamewidget, app_resources.documentDirectory);
  SetDir(NULL, NULL, NULL);
  save_question = FALSE;

  XtPopup(savereq, XtGrabNone);
  clearReqButtons();

  XtRemoveAllCallbacks(savebutton, XtNcallback);
  XtAddCallback(savebutton, XtNcallback, DoTSaveCB, t);
}

void
DoSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  FILE *fp;
  char message[STRINGSIZE], filename[STRINGSIZE];
  Arg arglist[10];
  Cardinal num_args;

  XtPopdown(savereq);
  clearReqButtons();

  if(save_question) {
    strncpy(the_Question->q->name, GetString(filenamewidget), STRINGSIZE);
    sprintf(filename, "%s/%s",
	    GetString(dirnamewidget), the_Question->q->name);

    WriteQuestion(filename, the_Question->q);
    if(quit)
      exit(0);
    else {
      char name[STRINGSIZE];
      num_args = 0;
      sprintf(name, "X WAIS Question: %s",  the_Question->q->name);
      XtSetArg(arglist[num_args], XtNtitle, name); num_args++;
      XtSetArg(arglist[num_args], XtNiconName, the_Question->q->name); num_args++;
      XtSetValues(top, arglist, num_args);
    }
  }
  else {
    DoTSave(w, closure, call_data);
  }
}

void
DontSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  clearReqButtons();

  if(quit)
    exit(0);
}

void
DoTSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  DoTSaveCB(w, (XtPointer)current_text, NULL);
}
 
static void
DoTSaveCB(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  FILE *fp;
  char message[STRINGSIZE], filename[STRINGSIZE];

  Textbuff t = (Textbuff) closure;

  strncpy(filename, GetString(filenamewidget), STRINGSIZE);

  if(filename[0]==0) {
    PrintStatusW("\nYou have to enter a filename.",
		the_Question->window->StatusWindow);
    return;
  }
  
  XtPopdown(savereq);
  clearReqButtons();

  if (filename[0] != '/') {
    if(t->type != NULL &&
       !strcmp(t->type, "WSRC"))
       sprintf(message, "%s%s", app_resources.userSourceDirectory, filename);
    else	
      sprintf(message, "%s%s", GetString(dirnamewidget), filename);
    strcpy(filename, message);
  }

  if((fp = fopen(filename, "w")) == NULL) {
    sprintf(message, "\nUnable to save %s.", filename);
    PrintStatusW(message, the_Question->window->StatusWindow);
  }
  else {
    dumptext(fp, t->text, t->size);
    fclose(fp);
  }
  if(t->type != NULL && strcmp(t->type, "TEXT") != 0)
    KillText(t);
}

void
DontTSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  clearReqButtons();
}

void
setSourceMenu()
{
  /*
  SList s;
  Widget entry;

  if(sourcemenu) 
    XtDestroyWidget(sourcemenu);

  sourcemenu = XtCreatePopupShell("menu", simpleMenuWidgetClass, sourcebutton, 
				  NULL, ZERO);
    
  for (s = Sources; s != NULL; s = s->nextSource) {
    char * item = s->thisSource->name;
	
    entry = XtCreateManagedWidget(item, smeBSBObjectClass, sourcemenu,
				      NULL, ZERO);
    XtAddCallback(entry, XtNcallback, AddSourceToQuestion, NULL);
  }
  */
}

void
addSection(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int i;
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  float top, shown;
  DocList dlist;
  DocumentID doc;
  XawTextPosition p1, p2;
  long l1, l2;
  Textbuff t;

  t = findText(w);
  
  XawTextGetSelectionPos(t->textwindow, &p1, &p2);

  if(p1 >= 0 && p2 > 0) {
    /* find the line positions */
    l1 = GetLineFromPos(t->text, p1);
    l2 = GetLineFromPos(t->text, p2);

    doc = copy_docid(t->docid);
    doc->start = l1;
    doc->end = l2;
    dlist = makeDocList(doc, NULL);
    /* append it to the current rellist */
  
    if(q->RelevantDocuments != NULL) {
      DocList doc;

      for(doc = q->RelevantDocuments; doc->nextDoc != NULL; doc = doc->nextDoc);
      doc->nextDoc = dlist;
    }
    else
      q->RelevantDocuments = dlist;

    if(the_Question->Relevant_Items != NULL)
      freeItemList(the_Question->Relevant_Items);
    the_Question->Relevant_Items = 
      buildDocumentItemList(q->RelevantDocuments, FALSE);
    q->numdocs = charlistlength(the_Question->Relevant_Items);

    RebuildListWidget(qw->RelevantDocuments, the_Question->Relevant_Items);
  }
}

void
DoSSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  FILE *fp;
  char name[STRINGSIZE];
  Source source;
  SList current_sources;

  source = the_Source;

  XtPopdown(sourcepopup);

  strcpy(name, GetString(snamewid));

  if(!((strlen(name) > 4) && 
       strstr(name, ".src") &&
       (!strcmp(".src", strstr(name, ".src")))))
    strcat(name, ".src");

  if(source->name != NULL) s_free(source->name);
  source->name = s_strdup(name);

  if (source->maintainer != NULL) s_free(source->maintainer);
  source->maintainer = s_strdup(GetString(maintainerwid));

  if (source->description != NULL) s_free(source->description);
  source->description = s_strdup(GetString(descwid));

  strncpy(source->server, GetString(serverwid), STRINGSIZE);
  strncpy(source->service, GetString(servicewid), STRINGSIZE);
  strncpy(source->database, GetString(dbwid), STRINGSIZE);
  strncpy(source->cost, GetString(costwid), STRINGSIZE);
  strncpy(source->units, GetString(unitwid), STRINGSIZE);

  WriteSource(app_resources.userSourceDirectory, source, TRUE);

  NumSources = 0;

  GetSourceNames(app_resources.userSourceDirectory);
  if(app_resources.commonSourceDirectory[0] != 0)
    GetSourceNames(app_resources.commonSourceDirectory);

  RebuildListWidget(sourcewindow, Source_items);
}

void
DontSSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(sourcepopup);
}

void showNext(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Textbuff t;
  DocumentID d;
  Source source;
  Question q = the_Question->q;

  t = findText(w);
  if(t != NULL) {
    if(t->docid->doc != NULL &&
       t->docid->doc->sourceID != NULL &&
       t->docid->doc->sourceID->filename != NULL)
      source = findsource(t->docid->doc->sourceID->filename);
    
    if (source == NULL) {
      PrintStatusW("\nCould not find Source for this document!",
		   t->status);
      return;
    }

    if((d = getNextorPrevDoc(q, source, t->docid, TRUE)) != NULL)
      ViewDoc(d, d->doc->type[0], 0, false);
    else
      PrintStatusW("\nCould not get next document.", t->status);
  }
}

void showPrevious(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Textbuff t;
  DocumentID d;
  Source source;

  t = findText(w);
  if(t != NULL) {
    if(t->docid->doc != NULL &&
       t->docid->doc->sourceID != NULL &&
       t->docid->doc->sourceID->filename != NULL)
      source = findsource(t->docid->doc->sourceID->filename);
    
    if (source == NULL) {
      PrintStatusW("\nCould not find Source for this document!",
		   t->status);
      return;
    }

    if((d = getNextorPrevDoc(the_Question->q, source, t->docid, FALSE))
       != NULL)
      ViewDoc(d, d->doc->type[0], 0, false);
    else
      PrintStatusW("\nCould not get previous document.", t->status);
  }
}

void setFile(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  clearReqButtons();
}

void quitFile(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  clearReqButtons();
}
