/*                               -*- Mode: C -*- 
 * $Basename: qcommands.c $
 * $Revision: 1.11.1.3 $
 * Author          : jonathan@Think.COM
 * Created On      : 92/02/17  12:55:38
 * Last Modified By: Ulrich Pfeifer
 * Last Modified On: Mon May  5 09:39:24 1997
 * Language        : C
 * Update Count    : 1
 * Status          : Unknown, Use with caution!
 * 
 * (C) Copyright 1997, Universität Dortmund, all rights reserved.
 * (C) Copyright CNIDR (see ../doc/CNIDR/COPYRIGHT)
 */

#ifndef lint
static char *PRCSid = "$Id: qcommands.c 1.11.1.3 Mon, 05 May 1997 11:54:27 +0200 pfeifer $";
#endif

#define _C_QCOMMANDS

#include "xwais.h"
#include "cat.h"
#include <X11/Xaw/TextP.h>
#include <setjmp.h>

#define WORLD_INTERVAL 100
#define IN_HEADER 1
#define IN_BODY 2

static jmp_buf jbuf;
static XtIntervalId world_timer = NULL;
static SourceList current_sourcelist;
#ifdef ORIGINAL	/* einet */
static DocList resdocs, last_resdocs;
#else			/* einet begin 22jun93 */
/* initialize these values! */
static DocList resdocs = NULL, last_resdocs = NULL;
#endif			/* einet end */
static boolean savep = false;
static Sbuff sb = NULL;

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

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

static XQuestion helpquestion = NULL;
static Textbuff current_text;

static int s_dsb = SENSVAL, s_adb = SENSVAL, s_ddb = SENSVAL;

/* macros */
#define get_selected_type() \
  get_selected_item(typewindow->ListWidget)

#define get_selected_qsource(question) \
  get_selected_item((question)->window->Sources->ListWidget)

#define get_selected_qdoc(question) \
  get_selected_item((question)->window->RelevantDocuments->ListWidget)

#define get_selected_response(question) \
  get_selected_item((question)->window->ResultDocuments->ListWidget)

#define get_question_response(questionwindow) \
  get_selected_item((questionwindow)->ResultDocuments->ListWidget)


/* forward declarations */

static void SetupSearch _AP((Question q));
static void SetupRetrieve _AP((Textbuff textstruct,long page,long size));

static void HandleDoc _AP((Sbuff sb));
static void DoTSaveCB _AP((Widget w,
			   XtPointer closure,
			   XtPointer call_data));

/* Private functions */

static long
GetLineFromPos(text, p)
char *text;
XawTextPosition p;
{
  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;
{
  long i;
  XawTextPosition pos;

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

  return pos;
}

#define SetCursor(widget, cursor) \
 XDefineCursor(CurDpy, XtWindow((the_Question->window)->widget), cursor)

#define unSetCursor(widget) \
 XUndefineCursor(CurDpy, XtWindow((the_Question->window)->widget))

static void
SetCursors(cursor)
Cursor cursor;
{
  static Cursor xterm_cursor = NULL;

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

  if(cursor != NULL) {
    SetCursor(keywordwid, cursor);
    SetCursor(shell, cursor);
    SetCursor(Sources->ListWidget, cursor);
    SetCursor(RelevantDocuments->ListWidget, cursor);
    SetCursor(ResultDocuments->ListWidget, cursor);
    SetCursor(StatusWindow, cursor);
  }
  else {
    SetCursor(keywordwid, xterm_cursor);
    unSetCursor(shell);
    unSetCursor(Sources->ListWidget);
    unSetCursor(RelevantDocuments->ListWidget);
    unSetCursor(ResultDocuments->ListWidget);
    unSetCursor(StatusWindow);
  }
}

static void
world(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  static int cursor = 0;

  SetCursors(wais_cursors[cursor]);
  cursor = (cursor+1)%NUM_CURSORS;
  world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
}

#ifdef MOTIF
static void
highlight_words(w)
Widget w;
{
  long b, e, offset;
  char* text = GetString(w);

  e = strlen(text);

  for (offset = 0, e = strlen(text);
       (b = findKeyword(text, &e)) < 999999;
       offset+=e, text+=e, e = strlen(text)) {
    XmTextSetHighlight(w, b+offset, b+offset+e, XmHIGHLIGHT_SELECTED);
  }
}
#endif

static void
cleanup()
{
  Arg args[TWO];
#ifdef MOTIF
  XmString strn = XmStringCreateSimple(" View  ");
#else
  char* strn = " View  ";
#endif

  busy = FALSE;
  fuzzButtons(false);
  XtSetArg(args[ZERO], NLABEL, strn);
  XtSetValues(viewbutton, args, ONE);
#ifdef MOTIF
  highlight_words(the_Question->window->keywordwid, GetKeywordsUsed());
#else
  XtSetSensitive(viewbutton, False);
  XtSetSensitive(saveAsButton, False);
  XtCallActionProc(searchButton, "unset", NULL, NULL, 0);
  XawTextDisplay(the_Question->window->keywordwid);
#endif

  SetCursors(NULL);
  if(world_timer != NULL) {
    XtRemoveTimeOut(world_timer);
    world_timer = NULL;
  }
}

static void
truncate_document_list(dl, n)
DocList dl;
long n;
{
  while(--n > 0)
    if(dl == NULL ||
       (dl = dl->nextDoc) == NULL)
      return;

  freeDocList(dl->nextDoc);
  dl->nextDoc = NULL;
  return;
}

static void
finish_search(sb)
Sbuff sb;
{ /* Done with message, let's interpret it */
  Question q = the_Question->q;
  QuestionWindow qw = the_Question->window;
  char message[255];
  diagnosticRecord **diag;
  WAISSearchResponse *response;

  if(sb != NULL &&
     sb->buffer != NULL) {
    readSearchResponseAPDU(&q->query_response,
			   sb->buffer + HEADER_LENGTH);
    if (q->query_response != NULL)
      if ((response = (WAISSearchResponse *)
	   q->query_response->DatabaseDiagnosticRecords) != NULL) {
	if ((diag = response->Diagnostics)
	    != NULL)
	  showDiags(diag);
	if (q->ResultDocuments != NULL) {
	  char* keys_used = NULL;
	  DocList last = findLast(q->ResultDocuments);

	  last->nextDoc = build_response_list(q->query_response,
					      current_sourcelist->thisSource,
					      &keys_used);
	  if (keys_used != NULL) {
	    if (keys_used[0] != 0) {
	      if(q->keywords_used == NULL)
		q->keywords_used = keys_used;
	      else {
		if(strcmp(q->keywords_used, keys_used) != 0) {
		  q->keywords_used =
		    (char*)s_realloc(q->keywords_used,
				     strlen(q->keywords_used) + strlen(keys_used) +1);
		  strcat(q->keywords_used, " ");
		  strcat(q->keywords_used, keys_used);
		}
	      }
	      s_free(keys_used);
	    }
	  }
	}
	else
	  q->ResultDocuments =
	    build_response_list(q->query_response,
				current_sourcelist->thisSource,
				&q->keywords_used);
	freeWAISSearchResponse(response);
      }
  }
  else q->ResultDocuments = last_resdocs;

  sort_document_list(q->ResultDocuments);

  truncate_document_list(q->ResultDocuments, q->maxresdocs);

  current_sourcelist = current_sourcelist->nextSource;

  if(current_sourcelist == NULL) {
    q->numresdocs =  listlength((List)q->ResultDocuments);
    the_Question->Result_Items = buildDocumentItemList(q->ResultDocuments, TRUE);
    RebuildListWidget(qw->ResultDocuments, the_Question->Result_Items, LIST_TOP);
    if(sb != NULL)
      PrintStatus(STATUS_INFO, STATUS_MEDIUM, FOUND_MESSAGE, q->numresdocs);
    cleanup();
  }
  else
    SetupSearch(q);
}

static void
finish_retrieval(sb)
Sbuff sb;
{
  WAISDocumentText *text;
  diagnosticRecord **diag;
  Question q = the_Question->q;
  char* viewtext;
  long size, numChars, start_byte, end_byte;
  WAISSearchResponse *response;

  if(sb != NULL) {
    numChars = (sb->page+1) * sb->chars_per_page;

    if (sb->textstruct->text == NULL)
      sb->textstruct->text = (char*)s_malloc(sb->chars_per_page);
    else
      sb->textstruct->text = s_realloc(sb->textstruct->text, numChars);

    if(sb->textstruct->text == NULL) {
      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADALLOC_MESSAGE);
      return;
    }

    start_byte = sb->page * sb->chars_per_page;
    end_byte = (sb->doc_size < 0 ?
		(sb->page + 1) * sb->chars_per_page :
		MINIMUM((sb->page + 1) * sb->chars_per_page, sb->doc_size));

    readSearchResponseAPDU(&q->retrieval_response,
			   sb->buffer + HEADER_LENGTH);

    if (q->retrieval_response != NULL)  {
      if ((response = (WAISSearchResponse *)
	   q->retrieval_response->DatabaseDiagnosticRecords) != NULL) {
	diag = response->Diagnostics;

	if(NULL == response->Text) {
	  if (diag != NULL)
	    showDiags(diag);

	  PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
	  HandleDoc(sb);
	  return;
	}
	else {
	  text = response->Text[0];
	  if((sb->textstruct->type == NULL) || (strcmp(sb->textstruct->type, "TEXT") == 0)) {
	    long length = text->DocumentText->size;;

	    delete_seeker_codes(text->DocumentText->bytes, &length);
	    text->DocumentText->size = length;
	    replacecontrolM(text->DocumentText->bytes, &length);
	    text->DocumentText->size = length;
	  }

	  if(text->DocumentText->size > (end_byte - start_byte))
	    text->DocumentText->size = (end_byte - start_byte);

	  size = sb->textstruct->size;
	  viewtext = sb->textstruct->text+size;
	  size+=text->DocumentText->size;
	  sb->textstruct->size = size;
	  if (size <= numChars) { /* got less than we asked for */
	    memcpy(viewtext, text->DocumentText->bytes, text->DocumentText->size);
	    PrintStatus(STATUS_CONT, STATUS_HIGH, RECEIVE_MESSAGE,
			size, sb->s->name);

	    if(diag &&
	       diag[0] &&
	       diag[0]->ADDINFO != NULL &&
	       !strcmp(diag[0]->DIAG, D_PresentRequestOutOfRange)) {
	      PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
	      sb->textstruct->size = size;
	      HandleDoc(sb);
	      freeWAISSearchResponse(response);
	      return;
	    }
	    if((sb->textstruct->type != NULL) &&
	       (strcmp(sb->textstruct->type, "TEXT") != 0) &&
	       (text->DocumentText->size != sb->chars_per_page)) {
	      PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
	      sb->textstruct->size = size;
	      HandleDoc(sb);
	      freeWAISSearchResponse(response);
	      return;
	    }
	  }
	  else {
	    PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
	    sb->textstruct->size = size;
	    HandleDoc(sb);
	    freeWAISSearchResponse(response);
	    return;
	  }
	}

	if (diag != NULL)
	  showDiags(diag);

	freeWAISSearchResponse(response);
      }
    }
    sb->textstruct->size = size;
    if (end_byte == sb->doc_size) {
      PrintStatus(STATUS_INFO, STATUS_HIGH, DONE_MESSAGE);
      HandleDoc(sb);
    }
    else {
      sb->page++;
      SetupRetrieve(sb->textstruct, sb->page, sb->doc_size);
    }
  }
  else HandleDoc(NULL);
}


static void
GetData(data, fid, id)
XtPointer data;
int *fid;
XtInputId *id;
{
  long nbytes;
  Sbuff sb = (Sbuff)data;
  char* b;
  WAISMessage header;

  switch(sb->read_state) {
  case IN_HEADER:
    b = sb->buffer+sb->buffer_offset;
    if((nbytes = read(*fid, b, (HEADER_LENGTH-sb->buffer_offset))) == -1) {
      /* error! unregister XtInput */
      if(sb->xid != NULL)
	XtRemoveInput(sb->xid);
      sb->xid = NULL;
      return;
    }
    sb->buffer_offset+=nbytes;
    if (sb->buffer_offset == HEADER_LENGTH) { /* Done with header, let's interpret it */
      char length_array[11];
      readWAISPacketHeader(sb->buffer, &header);
      strncpy(length_array, header.msg_len, 10);
      length_array[10] = '\0';
      sb->read_state = IN_BODY;
      sb->toRead = atol(length_array);
      sb->buffer_offset = 0;
    }
    break;
  case IN_BODY:
    b = sb->buffer+sb->buffer_offset+HEADER_LENGTH;
    if((nbytes = read(*fid, b, (sb->toRead - sb->buffer_offset))) == -1) {
      /* error! unregister XtInput */
      if(sb->xid != NULL) {
	XtRemoveInput(sb->xid);
	sb->xid = NULL;
      }
      return;
    }
    sb->buffer_offset+=nbytes;
    if (sb->buffer_offset == sb->toRead) {
      sb->read_state = IN_HEADER;
      sb->toRead = HEADER_LENGTH;
      sb->buffer_offset = 0;
      if(sb->xid != NULL) {
	XtRemoveInput(sb->xid);
	sb->xid = NULL;
      }
      if(searching) finish_search(sb);
      else finish_retrieval(sb);
    }
  }
  return;
}

static boolean
  SendWaisMessage(request_message, request_length, connection)
char* request_message;
long request_length;
FILE* connection;
{
  writeWAISPacketHeader(request_message,
			request_length,
			(long)'z', /* Z39.50 */
			"wais      ", /* server name */
			(long)NO_COMPRESSION, /* no compression */
			(long)NO_ENCODING,(long)HEADER_VERSION);

  if( request_length + HEADER_LENGTH
     != fwrite (request_message, 1L, request_length + HEADER_LENGTH, connection))
    return false;

  fflush(connection);
  return true;
}

static void
 makeSBuff(source, page)
Source source;
long page;
{
  if(sb == NULL) sb = (Sbuff) s_malloc(sizeof(_Sbuff));

  sb->s = source;
  if(page == 0) {
    if (sb->buffer != NULL) s_free(sb->buffer);
    sb->buffer = s_malloc(source->buffer_length);
  }
  sb->page = page;
  sb->read_state = IN_HEADER;
  sb->toRead = HEADER_LENGTH;
  sb->buffer_offset = 0;
  if(source->connection != NULL)
    sb->xid = XtAddInput(fileno(source->connection),
			 (XtPointer)XtInputReadMask,
			 (XtInputCallbackProc)GetData, (XtPointer)sb);
  else sb->xid = NULL;
}

static void SetupSearch(q)
Question q;
{
  Source source;
  long request_buffer_length, numdocs;
  DocList dl;
  DocObj **Doc;
  char message[255];

  if(current_sourcelist != NULL) {
    if (last_resdocs) {
      freeDocList(last_resdocs);
      last_resdocs = NULL;
    } else {
      if(q->ResultDocuments != NULL)
        last_resdocs = q->ResultDocuments;
      else last_resdocs = resdocs;
    }
    /* build DocObjs */
    Doc = (DocObj**)s_malloc((q->numdocs+1) * sizeof(char*));

    for(numdocs=0, dl = q->RelevantDocuments;
	dl != NULL;
	dl = dl->nextDoc, numdocs++) {
      if(dl->thisDoc->doc != NULL) {
	char* tmptype = s_strdup((dl->thisDoc->doc->type) ?
				 dl->thisDoc->doc->type[0] : "TEXT");
	if(dl->thisDoc->doc->id != NULL)
	  if(dl->thisDoc->start >= 0)
	    Doc[numdocs] =
	      makeDocObjUsingLines(anyFromDocID(dl->thisDoc->doc->id),
				   tmptype, dl->thisDoc->start, dl->thisDoc->end);
	  else
	    Doc[numdocs] =
	      makeDocObjUsingWholeDocument(anyFromDocID(dl->thisDoc->doc->id),
					   tmptype);

      }
    }
    Doc[numdocs] = NULL;

    if(current_sourcelist == NULL ||
       current_sourcelist->thisSource == NULL) {
      current_sourcelist == NULL;
    }
    else {
      if((source =
	  findsource(current_sourcelist->thisSource->filename,
		     the_Question->q->sourcepath))
	  == NULL) {
	PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
		    current_sourcelist->thisSource->filename);
	finish_search(NULL);
      }
      else {
	PrintStatus(STATUS_INFO, STATUS_HIGH, "\n");

	if(source->initp != TRUE) {
	  PrintStatus(STATUS_INFO, STATUS_HIGH, INITSOURCE_MESSAGE);
	  init_for_source(source, q->request_message, MAX_MESSAGE_LEN,
			  q->response_message);
	}
	if(source->initp != FALSE) {
	  PrintStatus(STATUS_INFO, STATUS_HIGH, SEARCH_MESSAGE, source->name);
	  request_buffer_length = source->buffer_length;
	  if(NULL ==
	     generate_search_apdu(q->request_message + HEADER_LENGTH,
				  &request_buffer_length,
				  q->keywords,
				  (source->database[0]?source->database:NULL),
				  Doc, q->maxresdocs)) {
	    PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
	  }

	  makeSBuff(source, 0);
	  if(source->connection != NULL) {
	    if(SendWaisMessage(q->request_message,
			       (source->buffer_length -
				request_buffer_length),
			       source->connection)) {
	    }
	    else {
	      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADWAIS_MESSAGE);
	    }
	  }
	  else {
	    if(interpret_message(q->request_message,
				 (source->buffer_length -
				  request_buffer_length),
				 sb->buffer,
				 source->buffer_length,
				 source->connection,
				 false
				 ) == 0) {
	      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADCONNECT_MESSAGE);
	      close_source(source);
	    }
	    else {
	      finish_search(sb);
	    }
	  }
	}
	else {
	  PrintStatus(STATUS_INFO, STATUS_HIGH, NOSEARCH_MESSAGE, source->name);
	  if (sb != NULL && sb->buffer != NULL) {
	    s_free(sb->buffer);
	    sb->buffer = NULL;
	  }
	  finish_search(sb);
	}
      }
    }
    if (Doc != NULL) {
      doList((void**)Doc,freeDocObj);
      s_free(Doc);
    }
  }
}

static void SetupRetrieve(textstruct, page, size)
Textbuff textstruct;
long page;
long size;
{
  long request_length;
  any* docany;
  Source source = NULL;
  DocumentID doc = textstruct->docid;
  char* type = textstruct->type;

  if(doc != NULL &&
     doc->doc != NULL &&
     doc->doc->sourceID != NULL &&
     doc->doc->sourceID->filename != NULL &&
     (source = findsource(doc->doc->sourceID->filename,
			  the_Question->q->sourcepath))
     != NULL) {

    if(page == 0)
      PrintStatus(STATUS_INFO, STATUS_HIGH, "\n");

    if(source->initp == FALSE) {
      PrintStatus(STATUS_INFO, STATUS_HIGH, INITSOURCE_MESSAGE);
      init_for_source(source, the_Question->q->request_message, MAX_MESSAGE_LEN,
		      the_Question->q->response_message);

    }
    if(source->initp == FALSE) {
      PrintStatus(STATUS_INFO, STATUS_HIGH, NOGETDOC_MESSAGE,
		  textstruct->docid->doc->headline, source->name);
      finish_retrieval(NULL);
    }
    else {
/*      prep_retrieval(); */
      makeSBuff(source, page);
      if(page == 0) {
	PrintStatus(STATUS_INFO, STATUS_HIGH, GETDOC_MESSAGE,
		    textstruct->docid->doc->headline, source->name);
	sb->chars_per_page = source->buffer_length - HEADER_LENGTH - 1000; /* ? */
	sb->textstruct = textstruct;
	if(size == 0) sb->doc_size = textstruct->docid->doc->numChars;
	else sb->doc_size = size;
      }
      request_length = source->buffer_length;
      docany = anyFromDocID(doc->doc->id);

      if(NULL ==
	 generate_retrieval_apdu(the_Question->q->request_message + HEADER_LENGTH,
				 &request_length, docany, CT_byte,
				 page * sb->chars_per_page,
				 (sb->doc_size < 0 ?
				  (page + 1) * sb->chars_per_page :
				  MINIMUM((page + 1) * sb->chars_per_page, sb->doc_size)),
				 s_strdup((type ? type : "TEXT")),
				 (source->database[0] ?
				  source->database : NULL))) {
	PrintStatus(STATUS_URGENT, STATUS_HIGH, BUFFOVER_MESSAGE);
      }
      else {
	if(source->connection != NULL) {
	  if(SendWaisMessage(the_Question->q->request_message,
			     (source->buffer_length -
			      request_length),
			     source->connection)) {
	  }
	  else {
	    PrintStatus(STATUS_URGENT, STATUS_HIGH, BADWAIS_MESSAGE);
	  }
	}
	else {
	  if (interpret_message(the_Question->q->request_message,
				(source->buffer_length -
				 request_length),
				sb->buffer,
				source->buffer_length,
				source->connection,
				false)
	      == 0) {
	    PrintStatus(STATUS_URGENT, STATUS_HIGH, BADCONNECT_MESSAGE);
	    close_source(source);
	  }
	  else {
	    finish_retrieval(sb);
	  }
	}
      }
      freeAny(docany);
    }
  }
  else PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
		   doc->doc->sourceID->filename);
}

static void
SetPosition(w, shell, centerp)
Widget w;
Widget shell;
boolean centerp;
{
  Arg		args[2];
  Position	x, y;
  Dimension	width, height;

  XtSetArg(args[ZERO], XtNwidth, &width);
  XtSetArg(args[ONE], XtNheight, &height);
  XtGetValues(w, args, TWO);
  XtTranslateCoords(w,
		    (Position)(centerp?width/2:0),
		    (Position)(centerp?height/2:height),
		    &x, &y);

  XtSetArg(args[ZERO], XtNx, x);
  XtSetArg(args[ONE], XtNy, y);

  XtSetValues(shell, args, TWO);
}

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

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

  memset(result, 0, MAX_FILE_NAME_LEN+1);
  p = app_resources.filters;

  while (*p != 0) {
    if((i1 = (char*)strchr(p, ',')) == NULL) break;
    strncpy(t, p, MINIMUM(i1-p, MAX_FILE_NAME_LEN));
    if(!strncmp(t, type, MINIMUM(i1-p, MAX_FILE_NAME_LEN))) {
      if((i2 = (char*)strchr(i1, ';')) != NULL) {
	strncpy(result, i1+1, MINIMUM(i2-i1-1, MAX_FILE_NAME_LEN));
	result[i2-i1] = 0;
      }
      else strcpy(result, i1+1);
      return result;
    }
    if((p = (char*)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) {
    PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, fname);
    return;
  }

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

  KillText(t);

  PrintStatus(STATUS_INFO, STATUS_HIGH, FILTER_MESSAGE,
	      viewer, fname);
  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) {
    PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, f);
  }
  else {
    fprintf(fp, t->text);

    fclose(fp);
    if((fp = fopen(f, "r")) == NULL) {
      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, f);
    }
    else {
      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);

      KillText(t);
      PopupSource(the_Source);
    }
  }
}

static void
do_other_thing(t, type)
Textbuff t;
char *type;
{
  char message[STRINGSIZE];

  if (type != NULL && type[0] != 0) {
    PrintStatus(STATUS_URGENT, STATUS_HIGH, UNKNOWNTYPE_MESSAGE, type);
  }

  if(savelist == NULL)
    savelist = MakeSaveRequester(top);

  SetPosition(the_Question->window->shell, savereq, true);

  current_text = t;

  ReplaceText(filenamewidget, "");
  ReplaceText(dirnamewidget, app_resources.documentDirectory);

  XtPopup(savereq, XtGrabNone);
  SetDir(NULL, NULL, NULL);
  SetReqButtons(false);

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

static boolean
GetDoc(textstruct, size)
Textbuff textstruct;
long size;
{
  busy = TRUE;
  searching = FALSE;
  fuzzButtons(true);

  world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
  SetupRetrieve(textstruct, 0, size);
}

static void
HandleDoc(sb)
Sbuff sb;
{
#ifndef MOTIF
  XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
#endif
  if(world_timer != NULL) {
    XtRemoveTimeOut(world_timer);
    world_timer = NULL;
  }
  fuzzButtons(false);
  SetCursors(NULL);
  busy = false;

  if(sb != NULL) {
    if(sb->textstruct->size == 0) {
      KillText(sb->textstruct);
      PrintStatus(STATUS_URGENT, STATUS_HIGH, NODATA_MESSAGE);
      return;
    }

    if(savep)
      do_other_thing(sb->textstruct, NULL);
    else if(tryFilter(sb->textstruct, sb->textstruct->type))
      return;

    else if (substrcmp(sb->textstruct->docid->doc->headline, 
		       "Catalog for database:") ||
	     substrcmp(sb->textstruct->docid->doc->headline, ">") || 
	     strcmp(sb->textstruct->type,"WCAT") == 0 ) {
      Catbuff cat = build_cat(sb->textstruct->text,
			      sb->textstruct->docid->doc->sourceID);

      if(cat != NULL) {
	sb->textstruct->textwindow =
	  MakeCatPopup(top, cat, sb->textstruct->docid->doc->headline);
	XtPopup(sb->textstruct->textwindow, XtGrabNone);
      }
    }
    else if (sb->textstruct->type == NULL ||
	     substrcmp(sb->textstruct->type, "TEXT") ||
	     !strcmp(sb->textstruct->type, "WCAT")) {
      Arg args[2];

      sb->textstruct->textwindow =
	MakeTextPopup(top, sb->textstruct, sb->textstruct->docid->doc->headline);

      XtSetArg(args[ZERO], XtNtype, XawAsciiString);
      XtSetArg(args[ONE], XtNstring, sb->textstruct->text);
      XtSetValues(sb->textstruct->textwindow, args, TWO);

      if (sb->textstruct->docid->doc->best > 0) {
	XawTextPosition pos = GetPosFromLine(sb->textstruct->text,
					     sb->textstruct->docid->doc->best);
	XawTextSetInsertionPoint(sb->textstruct->textwindow, pos);
	XtSetArg(args[ZERO], XtNdisplayPosition, pos);
	XtSetValues(sb->textstruct->textwindow, args, ONE);
      }
    }
    else if (!strcmp(sb->textstruct->type, "WSRC")) {
      DoSource(sb->textstruct);
    }
    else do_other_thing(sb->textstruct, sb->textstruct->type);
  }
}

static void PopupTypeMenu(w, doc)
Widget w;
DocumentID doc;
{
  char** types = doc->doc->type;
  int i;

  SetPosition(w, typeshell, false);

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

  RebuildListWidget(typewindow, Type_items, LIST_TOP);

  XtPopup(typeshell, XtGrabExclusive);
}

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;
{
  Textbuff t;
  static char msg[STRINGSIZE], str[STRINGSIZE], minstr[STRINGSIZE], *keys;
  Widget tw;
  XawTextPosition cpos, minpos;

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

  tw = t->textwindow;

  messwidget = t->status;
  PrintStatus(STATUS_CONT, STATUS_MEDIUM, "\nSearching for next keyword...");
  PrintStatusW(msg, t->status,false);

  cpos = XawTextGetInsertionPoint(tw);
  minpos = findKeyword(t->text+cpos, NULL);
  if (minpos == 999999) {
    PrintStatus(STATUS_INFO, STATUS_MEDIUM, "\nCould not find any more keywords.");
  }
  else {
    Arg args[ONE];

    minpos += cpos+1;
    XawTextSetInsertionPoint(tw, minpos + 1);
    /*XtSetArg(args[ZERO], XtNdisplayPosition, minpos);
    XtSetValues(tw, args, ONE);*/
    PrintStatus(STATUS_CONT, STATUS_MEDIUM, "\nSearching for next keyword...done");
  }
  messwidget = the_Question->window->StatusWindow;
}

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) {
    PrintStatus(STATUS_INFO, STATUS_HIGH, ENTERFNAME_MESSAGE);
  }
  else {
    XtPopdown(savereq);
    SetReqButtons(false);

    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) {
      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADFOPEN_MESSAGE, filename);
    }
    else {
      dumptext(fp, t->text, t->size);
      fclose(fp);
    }
    if(t->type != NULL && strcmp(t->type, "TEXT") != 0)
      KillText(t);
  }
}

/* Public Functions */

void
fuzzButtons(fuzz)
boolean fuzz;
{
  if(fuzz) {
    s_dsb = XtIsSensitive(delSourceButton);
    s_adb = XtIsSensitive(addDocButton);
    s_ddb = XtIsSensitive(delDocButton);
  }

#ifndef MOTIF
  if(searching) {
    XtSetSensitive(viewbutton, !fuzz);
    XtCallActionProc(searchButton, (fuzz?"set":"unset"), NULL, NULL, 0);
  }
  else {
    XtSetSensitive(viewbutton, !fuzz);
    XtCallActionProc(viewbutton, (fuzz?"set":"unset"), NULL, NULL, 0);
  }
#endif

  XtSetSensitive(viewbutton, !fuzz);
  XtSetSensitive(saveAsButton, !fuzz);
  XtSetSensitive(addSourceButton, !fuzz);
  XtSetSensitive(delSourceButton, (fuzz?False:s_dsb));
  XtSetSensitive(addDocButton, (fuzz?False:s_adb));
  XtSetSensitive(delDocButton, (fuzz?False:s_ddb));

  XtSetSensitive(abortButton, fuzz);
  XtSetSensitive(helpButton, !fuzz);
  XtSetSensitive(doneButton, !fuzz);
}

void
Prefs(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Arg arglist[TWO];
  char maxdocstring[STRINGSIZE];

  if(prefpopup == NULL)
    prefpopup = MakePrefPopup(top);

  XtPopup(prefpopup, XtGrabNone);
  XtSetArg(arglist[ZERO], XtNtitle, "Xwais Preferences");
  XtSetArg(arglist[ONE], XtNiconName, "Xwais Preferences");
  XtSetValues(prefpopup, arglist, TWO);
  sprintf(maxdocstring, "%d", the_Question->q->maxresdocs);
  ReplaceText(maxdocwid, maxdocstring);
  ReplaceText(sourcepathwid, the_Question->q->sourcepath);
  ReplaceText(filterwid, app_resources.filters);
}

void
DoPSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  the_Question->q->maxresdocs = atoi(GetString(maxdocwid));
  if(the_Question->q->sourcepath != NULL) s_free(the_Question->q->sourcepath);
  the_Question->q->sourcepath = s_strdup(GetString(sourcepathwid));
  if(app_resources.filters != NULL) s_free(app_resources.filters);
  app_resources.filters = s_strdup(GetString(filterwid));
  XtPopdown(prefpopup);
}

void
DontPSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(prefpopup);
}

void
Abort(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  if(busy) {
    fuzzButtons(false);
    busy = FALSE;
    SetCursors(NULL);

    if (sb != NULL && sb->xid != NULL) {
      XtRemoveInput(sb->xid);
      sb->xid = NULL;
    }
    if(world_timer != NULL) {
      XtRemoveTimeOut(world_timer);
      world_timer = NULL;
    }

    PrintStatus(STATUS_URGENT, STATUS_HIGH, ABORT_MESSAGE);
    if(searching) {
      if(the_Question->q->ResultDocuments != NULL)
	freeDocList(the_Question->q->ResultDocuments);
      the_Question->q->ResultDocuments = resdocs;
      the_Question->q->numresdocs =
	listlength((List)the_Question->q->ResultDocuments);
      the_Question->Result_Items =
	buildDocumentItemList(the_Question->q->ResultDocuments, TRUE);
      RebuildListWidget(the_Question->window->ResultDocuments,
			the_Question->Result_Items, LIST_TOP);
#ifndef MOTIF
      XtCallActionProc(searchButton, "unset", NULL, NULL, 0);
#endif
    }
    else {
      freeDocList(resdocs);
#ifndef MOTIF
      XtCallActionProc(viewbutton, "unset", NULL, NULL, 0);
#endif
      if(sb != NULL && sb->textstruct != NULL) {
	KillText(sb->textstruct);
	sb->textstruct = NULL;
      }
      sb->page = 0; sb->chars_per_page = 0;
    }
    /* close out the connection to clear buffers */

    if(the_Question->q->Sources != NULL) {
      SourceList slist;
      Source source;
      for(slist = the_Question->q->Sources;
	  slist != NULL;
	  slist = slist->nextSource) {
	if((source = findsource(slist->thisSource->filename,
				the_Question->q->sourcepath)) != NULL)
	  close_source(source);
      }
    }
  }
}

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


/* ARGSUSED */
void
DoSearch(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  double_click = FALSE;
  LastClicked = w;
  last_doc = -1;

  if(!busy) {
    /* update the info */

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

    busy = TRUE;
    searching = TRUE;

    fuzzButtons(true);
    if((current_sourcelist = the_Question->q->Sources) == NULL) {
      SourceID sid;

      sid = (SourceID)s_malloc(sizeof(_SourceID));
      sid->filename = s_strdup("directory-of-servers.src");
      the_Question->q->Sources = makeSourceList(sid, NULL);
      current_sourcelist = the_Question->q->Sources;
      the_Question->Source_Items =
	buildSourceItemList(the_Question->q->Sources);
      the_Question->q->numsources =
	charlistlength(the_Question->Source_Items);

      RebuildListWidget(the_Question->window->Sources,
			the_Question->Source_Items, LIST_BOTTOM);
    }
    world_timer = XtAddTimeOut(WORLD_INTERVAL, world, (Opaque) world);
    resdocs = the_Question->q->ResultDocuments;
    the_Question->q->ResultDocuments = NULL;
    SetupSearch(the_Question->q);
  }
}

/* ARGSUSED */
void
DoSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
#ifndef MOTIF
  XtCallActionProc(savebutton, "set", NULL, NULL, 0);
  XtCallActionProc(savebutton, "notify", NULL, NULL, 0);
  XtCallActionProc(savebutton, "unset", NULL, NULL, 0);
#endif
}

/* ARGSUSED */
static void
DoSaveQ(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  FILE *fp;
  char message[STRINGSIZE], filename[STRINGSIZE];

  XtPopdown(savereq);
  SetReqButtons(false);

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

  WriteQuestion(filename, the_Question->q);
  exit(0);
}

/* ARGSUSED */
static void
DontSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  exit(0);
}

static void
SaveQuestion()
{
  Arg args[ONE];
  if(savelist == NULL)
    savelist = MakeSaveRequester(top);

  SetPosition(the_Question->window->shell, savereq, true);

  XtPopup(savereq, XtGrabNone);
  ReplaceText(dirnamewidget, app_resources.questionDirectory);
  ReplaceText(filenamewidget, the_Question->q->name);
  SetDir(NULL, NULL, NULL);

  SetReqButtons(true);

  XtAddCallback(savebutton, COMMANDCALLBACK, DoSaveQ, NULL);
  XtAddCallback(quitbutton, COMMANDCALLBACK, DontSave, NULL);

}

/* 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) {
    SaveQuestion(NULL, NULL, NULL);
  }
  else {
    sprintf(filename, "%s%s", app_resources.questionDirectory,
	    the_Question->q->name);
    WriteQuestion(filename, the_Question->q);
    exit(0);
  }
}

/* ARGSUSED */
void
AddResponseToQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  long document_number;
  DocList this, last;

  double_click = FALSE;
  LastClicked = w;

  document_number = get_question_response(the_Question->window);

  if(document_number != NO_ITEM_SELECTED) {
    XtSetSensitive(delDocButton, False);
    last_qdoc = NO_ITEM_SELECTED;
    /* find and add document to question's relevant documents */
    last = findLast(the_Question->q->RelevantDocuments);

    for(this = the_Question->q->ResultDocuments;document_number--;)
      this = this->nextDoc;

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

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

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

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

    RebuildListWidget(the_Question->window->RelevantDocuments,
		      the_Question->Relevant_Items, LIST_BOTTOM);

    the_Question->q->modified = TRUE;
  }
}

/* ARGSUSED */
void
DeleteQuestionDoc(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int SelectedDoc;
  DocList doc, last;

  if((SelectedDoc = get_selected_qdoc(the_Question)) == NO_ITEM_SELECTED) {
    double_click = FALSE;
    LastClicked = w;
    PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSELECT_MESSAGE);
  }
  else {
    /* rip out the bugger */

    XtSetSensitive(delDocButton, False);
    last_qdoc = NO_ITEM_SELECTED;

    the_Question->q->modified = TRUE;

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

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

/* ARGSUSED */
void
PopupSourceMenu(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  SetPosition(w, sshell, false);
#ifndef MOTIF
  XawListUnhighlight(sourcewindow->ListWidget);
#endif
  XtPopup(sshell, XtGrabExclusive);
}

/* ARGSUSED */
void
AddSourceToQuestion(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  long snum = get_selected_source();

  if(snum == NO_ITEM_SELECTED) XtPopdown(sshell);
  else {
#ifdef MOTIF
    last_source = snum;
#endif
    if(last_source != snum) last_source = snum;
    else {
      SourceID sid;
      SourceList slist, source = NULL;
      char *name = Source_items[snum];

      XtSetSensitive(delSourceButton, SENSVAL);
      last_qsource = NO_ITEM_SELECTED;
      XtPopdown(sshell);
      /* append it to the current sourcelist */

      for(source = the_Question->q->Sources;
	  source != NULL && source->nextSource != NULL;
	  source = source->nextSource)

	if(strcmp(source->thisSource->filename, name) == 0)
	  break;

      if(source == NULL || source->nextSource == NULL) {
	sid = (SourceID)s_malloc(sizeof(_SourceID));
	sid->filename = s_strdup(name);
	slist = makeSourceList(sid, NULL);

	if (source != NULL) source->nextSource = slist;
	else the_Question->q->Sources = slist;

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

	RebuildListWidget(the_Question->window->Sources,
			  the_Question->Source_Items, LIST_BOTTOM);
	the_Question->q->modified = TRUE;
      }
    }
  }
}

/* ARGSUSED */
void
EditQuestionSource(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int CurrentSource;
  Source edit_source = NULL;

  double_click = FALSE;
  LastClicked = w;

  if ((CurrentSource = get_selected_qsource(the_Question))
      == NO_ITEM_SELECTED) {
    last_qsource = NO_ITEM_SELECTED;
  }
  else {
#ifdef MOTIF
    last_qsource = CurrentSource;
#endif
    if (last_qsource == CurrentSource) {
      if((edit_source =
	  findsource(the_Question->Source_Items[CurrentSource],
		     the_Question->q->sourcepath)) == NULL) {
	PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
		    the_Question->Source_Items[CurrentSource]);
      }
      else {
	PrintStatus(STATUS_INFO, STATUS_MEDIUM, VIEWSOURCE_MESSAGE,
		    edit_source->name);
	PopupSource(edit_source);
      }
    }
    else last_qsource = CurrentSource;
  }
}

/* ARGSUSED */
void
DeleteQuestionSource(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  int SelectedSource;
  SourceList source, last;

  if((SelectedSource = get_selected_qsource(the_Question)) == NO_ITEM_SELECTED) {
    double_click = FALSE;
    LastClicked = w;
    PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSELECT_MESSAGE);
  }
  else {
    /* rip out the bugger */
    XtSetSensitive(delSourceButton, SENSVAL);
    last_qsource = NO_ITEM_SELECTED;
    the_Question->q->modified = TRUE;

    double_click = FALSE;
    LastClicked = NULL;
    if (SelectedSource == 0) {
      last = the_Question->q->Sources;
      the_Question->q->Sources = the_Question->q->Sources->nextSource;
      s_free(last);
    }
    else {
      for (source = the_Question->q->Sources; --SelectedSource;) {
	source = source->nextSource;
      }
      if(source->nextSource != NULL) {
	last = source->nextSource;
	source->nextSource = source->nextSource->nextSource;
	s_free(last);
      }
    }
    the_Question->Source_Items =
      buildSourceItemList(the_Question->q->Sources);

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

/* ARGSUSED */
void doType(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  long tnum, dnum;
  DocumentID doc;

  XtPopdown(typeshell);
  if((tnum = get_selected_type()) != NO_ITEM_SELECTED) {
    dnum = get_selected_response(the_Question);
    if((doc = findDoc(the_Question->q->ResultDocuments, dnum)) != NULL) {
      messwidget = the_Question->window->StatusWindow;
      ViewDoc(doc, Type_items[tnum], -1, false);
    }
  }
}

void SensitizeDelSource(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XawListReturnStruct *data = (XawListReturnStruct *)call_data;

  XtSetSensitive(delSourceButton,!!data->string);
}

void SensitizeAddDoc(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XawListReturnStruct *data = (XawListReturnStruct *)call_data;
  XtSetSensitive(addDocButton, !!data->string);
  XtSetSensitive(viewbutton, !!data->string);
  XtSetSensitive(saveAsButton, !!data->string);
}

void SensitizeDelDoc(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XawListReturnStruct *data = (XawListReturnStruct *)call_data;

  XtSetSensitive(delDocButton, !!data->string);
}

void
  ViewDoc(doc, type, size, saveit)
DocumentID doc;
char *type;
long size;
Boolean saveit;
{
  Textbuff textstruct;
  TextList thisText;

  savep = saveit;

  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(STATUS_URGENT, STATUS_HIGH, BADALLOC_MESSAGE);
    return;
  }

  textstruct = thisText->thisText;
  textstruct->docid = doc;

  textstruct->size = 0;

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

  GetDoc(textstruct, size);
}

/* ARGSUSED */
void
ViewResponse(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  static int document_number;

  double_click = FALSE;
  LastClicked = w;

  if(!busy)
    if((document_number = get_selected_response(the_Question)) ==
       NO_ITEM_SELECTED) {
      last_doc = document_number;
    }
    else {
      DocumentID doc;

      if((doc = findDoc(the_Question->q->ResultDocuments, document_number)) == NULL)
	PrintStatus(STATUS_URGENT, STATUS_HIGH, NODOC_MESSAGE);
      else {
#ifdef MOTIF
	last_doc = document_number;
#endif
	if (document_number != last_doc) {
	  Arg args[2];

	  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;
	}
	else if((w == viewbutton) && (doc->doc->type[1] != NULL)) {
	  PopupTypeMenu(w, doc);
	}
	else {
	  messwidget = the_Question->window->StatusWindow;
	  ViewDoc(doc, doc->doc->type[0], 0, (w == saveAsButton));
	}
      }
    }
}

/* ARGSUSED */
void
ViewRelevant(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  double_click = FALSE;
  LastClicked = w;

  if(!busy) {
    long document_number = get_selected_qdoc(the_Question);

    if(document_number == NO_ITEM_SELECTED) {
      last_qdoc = document_number;
    }
    else {
      DocumentID doc;
#ifdef MOTIF
      last_qdoc = document_number;
#endif
      if((doc = findDoc(the_Question->q->RelevantDocuments, document_number))
	 == NULL) {
	PrintStatus(STATUS_URGENT, STATUS_HIGH, NODOC_MESSAGE);
      }
      else if (document_number != last_qdoc) {
	last_qdoc = document_number;
      }
      else {
	messwidget = the_Question->window->StatusWindow;
	ViewDoc(doc, doc->doc->type[0], 0, false);
      }
    }
  }
}

/* ARGSUSED */
void
EndText(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  Textbuff t = findText(w);

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

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

  if(savelist == NULL)
    savelist = MakeSaveRequester(top);

  SetPosition(t->textwindow, savereq, true);

  current_text = t;

  ReplaceText(filenamewidget, "");
  ReplaceText(dirnamewidget, app_resources.documentDirectory);

  XtPopup(savereq, XtGrabNone);
  SetDir(NULL, NULL, NULL);
  SetReqButtons(false);

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

/* ARGSUSED */
void
DoTSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  DoTSaveCB(w, (XtPointer)current_text, NULL);
}

/* ARGSUSED */
void
DontTSave(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  SetReqButtons(false);
}

/* ARGSUSED */
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, LIST_BOTTOM);
  }
}

/* ARGSUSED */
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, LIST_NONE);
}

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

/* ARGSUSED */
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,
			  the_Question->q->sourcepath);
    messwidget = t->status;
    if (source == NULL) {
      PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
		  t->docid->doc->sourceID->filename);
      return;
    }

    if((d = getNextorPrevDoc(q, source, t->docid, TRUE)) != NULL)
      ViewDoc(d, d->doc->type[0], 0, false);
    else
      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADNEXT_MESSAGE);
  }
}

/* ARGSUSED */
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,
			  the_Question->q->sourcepath);
    messwidget = t->status;
    if (source == NULL) {
      PrintStatus(STATUS_URGENT, STATUS_HIGH, NOSOURCE_MESSAGE,
		  t->docid->doc->sourceID->filename);
      return;
    }

    if((d = getNextorPrevDoc(the_Question->q, source, t->docid, FALSE))
       != NULL)
      ViewDoc(d, d->doc->type[0], 0, false);
    else
      PrintStatus(STATUS_URGENT, STATUS_HIGH, BADPREV_MESSAGE);
  }
}

/* ARGSUSED */
void setFile(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  SetReqButtons(false);
}

/* ARGSUSED */
void quitFile(w, closure, call_data)
Widget w;
XtPointer closure, call_data;
{
  XtPopdown(savereq);
  SetReqButtons(false);
}

char* GetKeywordsUsed() {
  char* result = the_Question->q->keywords_used;

#ifdef BOOLEANS
  static char* nobool_question;
  int i, j;
  char savec;
#endif
    
#ifndef BOOLEANS
  if (result == NULL) result = the_Question->q->keywords;
#else
  if (result == NULL) {
    result = the_Question->q->keywords;
    if (nobool_question != NULL) {
       XtFree(nobool_question);
       nobool_question = NULL;
    }
  } 
#endif
#ifdef BOOLEANS
  if (nobool_question == NULL) {
    nobool_question = XtMalloc(sizeof(unsigned char) * (strlen(result)+1));
    *nobool_question = '\0';
    for(j = 0, i = 0; i <= strlen(result); i++) {	/* step thru keys */
      if ((result[i] == 0) || isspace(result[i]) || ispunct(result[i])) {
        savec = result[i];
        result[i] = '\0';
        /* toss booleans, need code here for partial matches as well? */
        if(! (0 == strcasecmp(&result[j], "and") || 
              0 == strcasecmp(&result[j], "or" ) || 
              0 == strcasecmp(&result[j], "not") )) {
          if(nobool_question[0] != '\0')		/* delimit keys */
	    strcat(nobool_question, " ");
          strcat(nobool_question, &result[j]);
  
        }
        result[i] = savec;
        j = i + 1;
      }
    }
  }
  if(strlen(nobool_question) > 0) /* make sure we didn't strip everything ...*/
    result = nobool_question;
#endif

  return(result);
}



syntax highlighted by Code2HTML, v. 0.9.1