/*   e2trmlst.c
* ===========================================================================
*
*                            PUBLIC DOMAIN NOTICE
*            National Center for Biotechnology Information (NCBI)
*
*  This software/database is a "United States Government Work" under the
*  terms of the United States Copyright Act.  It was written as part of
*  the author's official duties as a United States Government employee and
*  thus cannot be copyrighted.  This software/database is freely available
*  to the public for use. The National Library of Medicine and the U.S.
*  Government do not place any restriction on its use or reproduction.
*  We would, however, appreciate having the NCBI and the author cited in
*  any work or product based on this material
*
*  Although all reasonable efforts have been taken to ensure the accuracy
*  and reliability of the software and data, the NLM and the U.S.
*  Government do not and cannot warrant the performance or results that
*  may be obtained by using this software or data. The NLM and the U.S.
*  Government disclaim all warranties, express or implied, including
*  warranties of performance, merchantability or fitness for any particular
*  purpose.
*
* ===========================================================================
*
* File Name:  e2trmlst.c
*
* Author:  Jonathan Kans, Greg Schuler, Jonathan Epstein, Tim Ford
*
* Version Creation Date:   10/30/01
*
* $Revision: 6.48 $
*
* File Description: 
*
* Modifications:  
* --------------------------------------------------------------------------
*
* ==========================================================================
*/

#include <vibrant.h>
#include <document.h>
#include <asn.h>
#include <ent2api.h>
#include <urlquery.h>
#include <dlogutil.h>
#include <medview.h>
#include <bspview.h>
#include <accutils.h>

#include <entrez2.h>

/*-------------------*/
/* Defined constants */
/*-------------------*/

#define MAX_TAXONOMY_TREE_DEPTH  1024
#define TL_TERM_DELIMITER        ':'
#define E2_STR_BUFF_SIZE         256
#define E2_EXPLODE               FALSE
#define E2_DO_NOT_EXPLODE        TRUE
#define E2_TRANSLATE             FALSE
#define E2_DO_NOT_TRANSLATE      TRUE
#define E2_TERM_COL              1
#define E2_COUNT_COL             2
#define E2_LIST_MODIFIED         TRUE
#define E2_LIST_NOT_MODIFIED     FALSE

#define MAX_MODES         11

#define STATE_OFF         0
#define STATE_ON          1

#define E2_RANGE_FROM     1
#define E2_RANGE_TO       2

#define GROUP_NONE        0
#define GROUP_SINGLE      1
#define GROUP_FIRST       2
#define GROUP_MIDDLE      3
#define GROUP_LAST        4

#define TERMLIST_OPERATOR             2
#define TERMLIST_TERM                 3

#define ADV_QUERY_INVALID_STATE       0
#define ADV_QUERY_EVALUATE_STATE      1
#define ADV_QUERY_RETRIEVE_STATE      2

#define AVAIL_WINDOW_ROWS             7
#define ADV_TEXT_MAX_LENGTH           8192
#define ADV_TEXT_WIDTH                20
#define ADV_TEXT_HEIGHT               7

#define LEXCHAR_LPAREN                1
#define LEXCHAR_RPAREN                2
#define LEXCHAR_LBRACKET              3
#define LEXCHAR_RBRACKET              4
#define LEXCHAR_QUOTE                 5
#define LEXCHAR_AND                   6
#define LEXCHAR_OR                    7
#define LEXCHAR_NOT                   8
#define LEXCHAR_COMMA                 9
#define LEXCHAR_ATSIGN               10
#define LEXCHAR_BACKSLASH            11
#define LEXCHAR_WHITESPACE           12
#define LEXCHAR_SEMICOLON            13
#define LEXCHAR_COLON                14
#define LEXCHAR_EOL                  15
#define LEXCHAR_NULL                 16
#define LEXCHAR_OTHER                17

#define LEXSTATE_IDLE                 0
#define LEXSTATE_BACKSLASHED          1
#define LEXSTATE_INQUOTE              2
#define LEXSTATE_INQUOTE_AFTERBSLASH  3
#define LEXSTATE_INSTRING             4
#define LEXSTATE_ERROR                5

#define LEXTOK_LPAREN                 1
#define LEXTOK_RPAREN                 2
#define LEXTOK_LBRACKET               3
#define LEXTOK_RBRACKET               4
#define LEXTOK_AND                    5
#define LEXTOK_OR                     6
#define LEXTOK_NOT                    7
#define LEXTOK_COMMA                  8
#define LEXTOK_ATSIGN                 9
#define LEXTOK_STRING                10
#define LEXTOK_RANGE                 11

#define ERR_CD_LEX                   17

/*------------------------------*/
/* Defines for enumerated lists */
/*------------------------------*/

typedef enum {
  SELECTION_MODE = 1,
  AUTOMATIC_MODE,
  WILD_CARD_MODE,
  MESH_TREE_MODE,
  TAXONOMY_MODE,
  RANGE_MODE,
  LOOKUP_ACCN_MODE,
  VIEW_ACCN_MODE,
  LOOKUP_UID_MODE,
  VIEW_UID_MODE,
  TRANSLATE_MODE
} ModeChoice;

static ENUM_ALIST(mult_alist)
  {"Multiple",            AUTOMATIC_MODE},
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(auto_alist)
  {"Automatic",           TRANSLATE_MODE},
  {"Multiple",            AUTOMATIC_MODE},
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(accn_alist)
  {"Lookup",              LOOKUP_ACCN_MODE},
  {"Range",               RANGE_MODE},
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
  {"View",                VIEW_ACCN_MODE},
END_ENUM_ALIST

static ENUM_ALIST(mesh_alist)
  {"MeSH Tree",           MESH_TREE_MODE},
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(tax_alist)
  {"Selection",           SELECTION_MODE},
  {"Taxonomy",            TAXONOMY_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(default_alist)
  {"Range",               RANGE_MODE},
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(range_alist)
  {"Range",               RANGE_MODE},
  {"Selection",           SELECTION_MODE},
END_ENUM_ALIST

static ENUM_ALIST(trunc_alist)
  {"Selection",           SELECTION_MODE},
  {"Wild Card",           WILD_CARD_MODE},
END_ENUM_ALIST

static ENUM_ALIST(uid_alist)
  {"Lookup",              LOOKUP_UID_MODE},
  {"View",                VIEW_UID_MODE},
END_ENUM_ALIST

static EnumFieldAssocPtr mode_alists [] = {
  mult_alist,
  auto_alist,
  accn_alist,
  mesh_alist,
  tax_alist,
  default_alist,
  range_alist,
  trunc_alist,
  uid_alist,
  NULL
};

typedef enum {
  POPUP_MULT = 0,
  POPUP_AUTO,
  POPUP_ACCN,
  POPUP_MESH,
  POPUP_TAX,
  POPUP_DEFAULT,
  POPUP_RANGE,
  POPUP_TRUNC,
  POPUP_UID
} ModeIndex;

/*-----------------------*/
/* Structure definitions */
/*-----------------------*/

typedef struct trmstatedata {
  CharPtr                   term;
  CharPtr                   field;
  Int4                      count;
  Int2                      db;
  Int2                      fld;
  Int2                      group;
  Int2                      above;
  Int2                      below;
  Int2                      state;
  Int4Ptr                   uids;
  CharPtr                   key;
  struct trmstatedata PNTR  prev;
  struct trmstatedata PNTR  next;
} StateData, PNTR StateDataPtr;

typedef struct {
  FORM_MESSAGE_BLOCK
  E2RetrieveDocsProc  retrieveDocsProc;
  E2RetrieveUidProc   retrieveUidProc;
  GrouP               stdQueryGroup;
  GrouP               advQueryGroup;
  ButtoN              advEvaluateButton;
  ButtoN              advRetrieveButton;
  Int2                advQueryState;
  ButtoN              advResetButton;
  TexT                advQueryText;
  Int2                advQueryLexPos;
  CharPtr             advQueryLexStr;
  Int2                advQueryLexState;
  ValNodePtr          advQueryNextToken;
  ValNode             advQueryNextRealNode;
  Boolean             advQueryNewGroup;
  PopuP PNTR          fieldsPopup;
  PopuP               modesPopup [MAX_MODES];
  GrouP               termGroup;
  GrouP               rangeGroup;
  IteM                advancedQueryItem;
  IteM                explodeItem;
  ModeChoice          currMode;
  Int2                currField;
  Int2                currDb;
  DoC                 availDoc;
  Int2                availItem;
  Int2                availRow;
  Int2                availClickItem;
  Int2                availClickRow;
  Int4                availNumTerms;
  Int4                availPageSize;
  Int2                availNumPages;
  Int2                availCurrentPage;
  Boolean             textChanged;
  Int2                okayToAccept;
  TexT                termText;
  TexT                fromText;
  TexT                toText;
  Boolean             isValidFrom;
  Boolean             isValidTo;
  Boolean             isFromTextChanged;
  Boolean             isToTextChanged;
  Int2                currRangeField;
  ButtoN              acceptButton;
  PopuP               taxLineagePopup;
  ValNodePtr          taxStrings;
  PopuP               databasePopup;
  ButtoN              retrieveButton;
  ButtoN              resetButton;
  Boolean             wasDoubleClick;
  DoC                 chosenDoc;
  Int2                chosenItem;
  Int2                chosenClickItem;
  Int2                chosenClickRow;
  Int2                chosenClickCol;
  Boolean             chosenInAboveBox;
  Boolean             chosenInBelowBox;
  Int2                chosenNumLines;
  RecT                trackRect;
  PoinT               trackPt;
  StateDataPtr        termList;
} FormInfo, PNTR FormInfoPtr;

/*----------------------------*/
/* File-wide static variables */
/*----------------------------*/

static ChoicE         s_showASNItem;
static Entrez2InfoPtr s_masterE2ip = NULL;

static ParData  availParFmt = { FALSE, FALSE, FALSE, FALSE, FALSE, 0, 0 };
static ColData  availColFmt [] = {
  {0, 5, 70, 0, NULL, 'l', FALSE, FALSE, FALSE, FALSE, FALSE},  /* term  */
  {0, 5, 10, 0, NULL, 'r', FALSE, TRUE, FALSE, FALSE, FALSE},   /* count */
  {0, 5, 10, 0, NULL, 'r', FALSE, FALSE, FALSE, FALSE, TRUE}    /* leaf  */
};

static ParData  chosenParFmt = { FALSE, FALSE, FALSE, FALSE, FALSE, 0, 0 };
static ColData  chosenColFmt [] = {
  {0, 23, 70, 0, NULL, 'l', FALSE, FALSE, FALSE, FALSE, FALSE}, /* term  */
  {0, 5, 10, 0, NULL, 'l', FALSE, FALSE, FALSE, FALSE, FALSE},  /* field */
  {0, 5, 10, 0, NULL, 'r', FALSE, TRUE, FALSE, FALSE, TRUE}     /* count */
};

/*---------------------*/
/* Function prototpyes */
/*---------------------*/

static Boolean RemoveTermFromList (
  FormInfoPtr pFormInfo,
  StateDataPtr sdp,
  Boolean goingDown,
  Boolean lastDraggedDown
);
static Boolean Query_TranslateAndAddBoolTerm (
  ForM f,
  Int2 currDb,
  Int2 currFld,
  CharPtr strs,
  Int2 state,
  Int4 num
);

static Boolean LoadAvailList (FormInfoPtr pFormInfo, CharPtr str);

/*==================================================================*/
/*                                                                  */
/*  ShowASN () -  Returns the current setting of the s_showASN      */
/*               static variable.                                   */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Boolean ShowASN (void)

{
  Int2  val;

  /*
  return GetStatus (s_showASNItem);
  */
  val = GetValue (s_showASNItem);
  if (val >= 11) return TRUE;
  if (val < 2) return FALSE;
  SetValue (s_showASNItem, val - 1);
  if (val < 2) return FALSE;
  return TRUE;
}

static void LogOrLaunch (CharPtr path, CharPtr title)

{
  Char    buf [257];
  size_t  ct;
  FILE    *ifp;
  FILE    *ofp;
  Int2    val;

  val = GetValue (s_showASNItem);
  if (val == 12) {
    ifp = FileOpen (path, "r");
    ofp = FileOpen ("entrez2.log", "a");
    if (ifp != NULL && ofp != NULL) {
      while ((ct = FileRead (buf, 1, sizeof (buf) - 1, ifp)) > 0) {
        FileWrite (buf, 1, ct, ofp);
      }
      fprintf (ofp, "\n");
    }
    FileClose (ofp);
    FileClose (ifp);
  } else {
    LaunchGeneralTextViewer (path, title);
  }
}

/*==================================================================*/
/*                                                                  */
/*  DisplayEntrezRequest () - Displays an Entrez2 request in a      */
/*                           pop-up window.                         */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN void DisplayEntrezRequest (Entrez2RequestPtr e2rq)

{
  AsnIoPtr  aip;
  Char      path [PATH_MAX];

  if (e2rq == NULL) return;
  TmpNam (path);
  aip = AsnIoOpen (path, "w");
  if (aip == NULL) return;
  Entrez2RequestAsnWrite (e2rq, aip, NULL);
  AsnIoClose (aip);
  LogOrLaunch (path, "Entrez2RequestAsnWrite");
  FileRemove (path);
}

/*==================================================================*/
/*                                                                  */
/*  DisplayEntrezReply () - Displays an Entrez2 reply in a pop-up   */
/*                         window.                                  */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN void DisplayEntrezReply (Entrez2ReplyPtr e2ry)

{
  AsnIoPtr  aip;
  Char      path [PATH_MAX];

  if (e2ry == NULL) return;
  TmpNam (path);
  aip = AsnIoOpen (path, "w");
  if (aip == NULL) return;
  Entrez2ReplyAsnWrite (e2ry, aip, NULL);
  AsnIoClose (aip);
  LogOrLaunch (path, "Entrez2ReplyAsnWrite");
  FileRemove (path);
}

/* text version of entrez2 query for debugging purposes */

static Entrez2ReplyPtr EntrezTextWaitForReply (
  CONN conn
)

{
  AsnIoConnPtr     aicp;
  AsnIoPtr         aip;
  Char             buffer [1025];
  time_t           currtime, starttime;
  Entrez2ReplyPtr  e2ry = NULL;
  Boolean          first;
  FILE *           fp;
  time_t           max = 0;
  size_t           n_read;
  Boolean          special_read = FALSE;
  EIO_Status       status;
  STimeout         timeout;
  Char             tmp [PATH_MAX];

  if (conn == NULL) return NULL;

  timeout.sec = 100;
  timeout.usec = 0;

  starttime = GetSecs ();
  while ((status = CONN_Wait (conn, eIO_Read, &timeout)) == eIO_Timeout && max < 300) {
    currtime = GetSecs ();
    max = currtime - starttime;
  }
  if (status == eIO_Success) {
#ifdef OS_UNIX
    if (getenv ("ENTREZ2_RECORD_QUERY") != 0) {
      special_read = TRUE;
    }
#endif
    if (special_read) {
      TmpNam (tmp);
#ifdef OS_UNIX
printf ("TmpNam '%s'\n", tmp);
#endif
      fp = FileOpen (tmp, "w");
      if (fp != NULL) {

        first = TRUE;
        do {
          status = CONN_Read (conn, buffer, sizeof (buffer) -1, &n_read,
                              first ? eIO_ReadPersist : eIO_ReadPlain);
          buffer [n_read] = '\0';
          if (first) {
            if (StringStr (buffer, "Entrez2-reply") == NULL) {
              ErrPostEx (SEV_ERROR, 0, 0, "EntrezReadReply failed on '%s'", buffer);
              FileClose (fp);
              /*
              FileRemove (tmp);
              */
              return NULL;
            }
            first = FALSE;
          }
          fprintf (fp, "%s", buffer);
        } while (status == eIO_Success);
        FileClose (fp);

        aip = AsnIoOpen (tmp, "r");
        e2ry = Entrez2ReplyAsnRead (aip, NULL);
        AsnIoClose (aip);
        /*
        FileRemove (tmp);
        */
      }
    } else {
      aicp = QUERY_AsnIoConnOpen ("r", conn);
      e2ry = Entrez2ReplyAsnRead (aicp->aip, NULL);
      QUERY_AsnIoConnClose (aicp);
    }
  }
  CONN_Close (conn);

  return e2ry;
}

static Entrez2ReplyPtr EntrezSpecialSynchronousQuery (
  Entrez2RequestPtr e2rq,
  Boolean textmode
)

{
  AsnIoConnPtr     aicp;
  CONN             conn;
  Entrez2ReplyPtr  e2ry;
  CharPtr          tempcookie = NULL;
  CharPtr          host_machine = NULL;
  Uint2            host_port = 0;
  CharPtr          host_path = NULL;
  CharPtr          env_machine = NULL;
  CharPtr          env_path = NULL;
  CharPtr          env_port = NULL;
  int              val = 0;

  if (e2rq == NULL) return NULL;

#ifdef OS_UNIX
  env_machine = (CharPtr) getenv ("NCBI_ENTREZ2_HOST_MACHINE");
  if (! StringHasNoText (env_machine)) {
    host_machine = env_machine;
  }
#endif
  if (StringHasNoText (host_machine)) {
    host_machine = "www.ncbi.nlm.nih.gov";
  }

#ifdef OS_UNIX
  env_port = (CharPtr) getenv ("NCBI_ENTREZ2_HOST_PORT");
  if (! StringHasNoText (env_port)) {
    if (sscanf (env_port, "%d", &val) == 1) {
      host_port = (Uint2) val;
    }
  }
#endif
  if (host_port == 0) {
    host_port = 80;
  }

#ifdef OS_UNIX
  env_path = (CharPtr) getenv ("NCBI_ENTREZ2_HOST_PATH");
  if (! StringHasNoText (env_path)) {
    host_path = env_path;
  }
#endif
  if (StringHasNoText (host_path)) {
    host_path = "/entrez/eutils/entrez2server.fcgi";
  }

  if (textmode) {
    conn = QUERY_OpenUrlQuery (host_machine, host_port, host_path, NULL,
                               "Entrez2Text", 30, eMIME_T_NcbiData,
                               eMIME_AsnText, eENCOD_None, 0);

    aicp = QUERY_AsnIoConnOpen ("w", conn);
  } else {
    conn = QUERY_OpenUrlQuery (host_machine, host_port, host_path, NULL,
                               "Entrez2Text", 30, eMIME_T_NcbiData,
                               eMIME_AsnBinary, eENCOD_None, 0);

    aicp = QUERY_AsnIoConnOpen ("wb", conn);
  }

  tempcookie = e2rq->cookie;

  Entrez2RequestAsnWrite (e2rq, aicp->aip, NULL);

  e2rq->cookie = tempcookie;

  AsnIoFlush (aicp->aip);
  QUERY_AsnIoConnClose (aicp);

  QUERY_SendQuery (conn);

  if (textmode) {
    e2ry = EntrezTextWaitForReply (conn);
  } else {
    e2ry = EntrezWaitForReply (conn);
  }

  return e2ry;
}

extern Entrez2ReplyPtr SpecialEntrezSynchronousQuery (
  Entrez2RequestPtr e2rq
);

extern Entrez2ReplyPtr SpecialEntrezSynchronousQuery (
  Entrez2RequestPtr e2rq
)

{
#ifdef OS_UNIX
  if (getenv ("ENTREZ2_TEXT_QUERY") != 0) {
    return EntrezSpecialSynchronousQuery (e2rq, TRUE);
  }
  if (getenv ("ENTREZ2_BINARY_QUERY") != 0) {
    return EntrezSpecialSynchronousQuery (e2rq, FALSE);
  }
#endif
  return EntrezSynchronousQuery (e2rq);
}

/*==================================================================*/
/*                                                                  */
/*  DBGetInfo () - Given a database ID, return the DB pointer.      */
/*                                                                  */
/*==================================================================*/

static Entrez2DbInfoPtr DBGetInfo (Int2 dbId)

{
  Int2              count;
  Entrez2InfoPtr    e2ip;
  Entrez2DbInfoPtr  e2db;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (dbId < 0) return NULL;

  /*----------------------------------*/
  /* Get information on the databases */
  /*----------------------------------*/

  e2ip = Query_GetInfo ();

  /*-----------------------*/
  /* Find the requested DB */
  /*-----------------------*/

  for (e2db = e2ip->db_info, count = 0; e2db != NULL; e2db = e2db->next, count++) {
    if (count == dbId) return e2db;
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/*  DBGetNameFromID () - Given a DB ID, returns its name.           */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN CharPtr DBGetNameFromID (Int2 dbId)

{
  Int2              count;
  Entrez2InfoPtr    e2ip;
  Entrez2DbInfoPtr  e2db;

  /*-----------------*/
  /* Check parameter */
  /*-----------------*/

  if (dbId < 0) return NULL;

  /*----------------------------------*/
  /* Get information on the databases */
  /*----------------------------------*/

  e2ip = Query_GetInfo ();

  /*--------------------------*/
  /* Find to the requested DB */
  /*--------------------------*/

  for (e2db = e2ip->db_info, count = 0; e2db != NULL; e2db = e2db->next, count++) {
    if (count != dbId) continue;
    if (StringHasNoText (e2db->db_name)) continue;
    return e2db->db_name;
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/*  DBGetIDFromName () - Given a DB name, returns its unique ID.    */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Int2 DBGetIDFromName (CharPtr dbName)

{
  Int2              count;
  Entrez2InfoPtr    e2ip;
  Entrez2DbInfoPtr  e2db;

  /*-----------------*/
  /* Check parameter */
  /*-----------------*/

  if (dbName == NULL) return -1;

  /*----------------------------------*/
  /* Get information on the databases */
  /*----------------------------------*/

  e2ip = Query_GetInfo ();

  /*---------------------------*/
  /* Look for the requested DB */
  /*---------------------------*/

  for (e2db = e2ip->db_info, count = 0; e2db != NULL; e2db = e2db->next, count++) {
    if (StrICmp (e2db->db_name, dbName) == 0) return count;
  }

  return -1;
}

/*==================================================================*/
/*                                                                  */
/*  FieldGetInfo () - Given a database ID and a field ID, return    */
/*                    the field name.                               */
/*                                                                  */
/*==================================================================*/

static Entrez2FieldInfoPtr FieldGetInfo (Int2 dbId, Int2 fieldId)

{
  Int2                 count;
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (dbId < 0 || fieldId < 0) return NULL;

  /*----------------------------------*/
  /* Find the requested DB            */
  /*----------------------------------*/

  e2db = DBGetInfo (dbId);
  if (e2db == NULL) return NULL;

  /*----------------*/
  /* Get field info */
  /*----------------*/

  for (e2fd = e2db->fields, count = 0; e2fd != NULL; e2fd = e2fd->next, count++) {
    if (count == fieldId) return e2fd;
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/*  IsValidFieldName () - Given a database ID and a field name,     */
/*                        check to see if it is as valid name.      */
/*                                                                  */
/*==================================================================*/

static Boolean IsValidFieldName (Int2 dbId, CharPtr fieldName)

{
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (dbId < 0 || fieldName == NULL) return FALSE;

  /*-----------------------*/
  /* Find the requested DB */
  /*-----------------------*/

  e2db = DBGetInfo (dbId);
  if (e2db == NULL) return FALSE;

  /*----------------*/
  /* Get field info */
  /*----------------*/

  for (e2fd = e2db->fields; e2fd != NULL; e2fd = e2fd->next) {
    if (StrICmp (e2fd->field_name, fieldName) == 0)
      return TRUE;
  }

  return FALSE;
}

/*===================================================================*/
/*                                                                   */
/*  FieldGetNameFromMenuName () - Given a field menu name and a      */
/*                               database ID, return the field name. */
/*                                                                   */
/*===================================================================*/

static CharPtr FieldGetNameFromMenuName (Int2 dbId, CharPtr menuName)

{
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (menuName == NULL || dbId < 0) return NULL;

  /*-----------------------*/
  /* Find the requested DB */
  /*-----------------------*/

  e2db = DBGetInfo (dbId);
  if (e2db == NULL) return NULL;

  /*--------------------------*/
  /* Find the requested field */
  /*--------------------------*/

  for (e2fd = e2db->fields; e2fd != NULL; e2fd = e2fd->next) {
    if (StrICmp (e2fd->field_menu, menuName) == 0) return e2fd->field_name;
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/*  FieldGetIDFromName () - Given a field name and a database ID,   */
/*                         return the field ID.                     */
/*                                                                  */
/*==================================================================*/

static Int2 FieldGetIDFromName (Int2 dbId, CharPtr fieldName)

{
  Int2                 count;
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (fieldName == NULL || dbId < 0) return -1;

  /*-----------------------*/
  /* Find the requested DB */
  /*-----------------------*/

  e2db = DBGetInfo (dbId);
  if (e2db == NULL) return -1;

  /*--------------------------*/
  /* Find the requested field */
  /*--------------------------*/

  for (e2fd = e2db->fields, count = 0; e2fd != NULL; e2fd = e2fd->next, count++) {
    if (StrICmp (e2fd->field_name, fieldName) == 0) return count;
  }

  return -1;
}

/*==================================================================*/
/*                                                                  */
/*  FieldGetInfoFromName () - This is desigend to get field info    */
/*                           given only a field name. It is useful  */
/*                           for a getting information on a field   */
/*                           in a sorted alist of fields.           */
/*                                                                  */
/*==================================================================*/

static Entrez2FieldInfoPtr FieldGetInfoFromName (CharPtr fieldName)

{
  Int2                 count;
  Entrez2InfoPtr       e2ip;
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;

  /*------------------*/
  /* Check parameters */
  /*------------------*/

  if (fieldName == NULL) return NULL;

  /*----------------------------------*/
  /* Get information on the databases */
  /*----------------------------------*/

  e2ip = Query_GetInfo ();

  /*-------------------------------*/
  /* Find the requested field info */
  /*-------------------------------*/

  for (e2db = e2ip->db_info, count = 0; e2db != NULL; e2db = e2db->next, count++) {
    for (e2fd = e2db->fields; e2fd != NULL; e2fd = e2fd->next) {
      if (StrICmp (e2fd->field_name, fieldName) == 0) return e2fd;
    }
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/* FieldGetModePopup () -                                           */
/*                                                                  */
/*==================================================================*/

static Int2 FieldGetModePopup (Int2 dbId, Int2 fieldId, EnumFieldAssocPtr fieldAList, FormInfoPtr pFormInfo)

{
  Entrez2DbInfoPtr     dbInfo;
  Entrez2FieldInfoPtr  fieldInfo;
  Boolean              is_rangable;
  Boolean              is_truncatable;
  ModeIndex            modeId = POPUP_DEFAULT;

  /*-----------------------------------------*/
  /* Find the requested field, and determine */
  /* its corresponding mode list popup.      */
  /*-----------------------------------------*/

  dbInfo = DBGetInfo (dbId);
  if (dbInfo == NULL) return -1;

  fieldInfo = FieldGetInfo (dbId, fieldId);
  if (fieldInfo == NULL) return -1;

  /* is_rangable = fieldInfo->is_rangable; */
  is_rangable = (Boolean) (fieldInfo->is_date || fieldInfo->is_numerical);
  /* is_truncatable = fieldInfo->is_truncatable; */
  is_truncatable = (Boolean) (! is_rangable);

  if (StringICmp (dbInfo->db_name, "PubMed") == 0 &&
      StringICmp (fieldInfo->field_name, "ALL") == 0) {
    modeId = POPUP_AUTO;
  } else if (StringICmp (fieldInfo->field_name, "ALL") == 0) {
    modeId = POPUP_MULT;
  } else if ((StringICmp (fieldInfo->field_name, "TITL") == 0) ||
             (StringICmp (fieldInfo->field_name, "WORD") == 0) ||
             (StringICmp (fieldInfo->field_name, "TIAB") == 0)) {
    modeId = POPUP_MULT;
  } else if (StringICmp (fieldInfo->field_name, "ACCN") == 0 ||
             StringICmp (fieldInfo->field_name, "PACC") == 0) {
    modeId = POPUP_ACCN;
  } else if (StringICmp (fieldInfo->field_name, "ORGN") == 0) {
    modeId = POPUP_TAX;
  } else if (StringICmp (fieldInfo->field_name, "MESH") == 0 ||
             StringICmp (fieldInfo->field_name, "MAJR") == 0) {
    modeId = POPUP_MESH;
  } else if (StringICmp (fieldInfo->field_name, "UID") == 0) {
    modeId = POPUP_UID;
  } else if (is_rangable && is_truncatable) {
    modeId = POPUP_DEFAULT;
  } else if (is_rangable) {
    modeId = POPUP_RANGE;
  } else if (is_truncatable) {
    modeId = POPUP_TRUNC;
  } else {
    modeId = POPUP_DEFAULT;
  }

  pFormInfo->currField = FieldGetIDFromName (dbId, fieldInfo->field_name);
  return modeId;
}

/*==================================================================*/
/*                                                                  */
/*  CreateDatabaseAlist () - Returns alist of database names        */
/*                                                                  */
/*==================================================================*/

EnumFieldAssocPtr CreateDatabaseAlist (Entrez2InfoPtr e2ip)

{
  EnumFieldAssocPtr  alist;
  Uint1              choice;
  Entrez2DbInfoPtr   e2db;
  ValNodePtr         head = NULL;

  if (e2ip == NULL || e2ip->db_count < 1) return NULL;

  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    if (StringHasNoText (e2db->db_menu)) continue;
    choice = (Uint1) DBGetIDFromName (e2db->db_name);
    ValNodeCopyStr (&head, choice, e2db->db_menu);
  }

  alist = MakeEnumFieldAlistFromValNodeList (head);
  ValNodeFreeData (head);

  return alist;
}

/*==================================================================*/
/*                                                                  */
/*  CreateFieldAlist () - Returns sorted alist of field names for   */
/*                       the given database.                        */
/*                                                                  */
/*==================================================================*/

EnumFieldAssocPtr CreateFieldAlist (Entrez2DbInfoPtr e2db)

{
  EnumFieldAssocPtr    alist;
  Uint1                choice;
  Entrez2FieldInfoPtr  fieldInfo;
  Int2                 dbId;
  ValNodePtr           head = NULL;

  if (e2db == NULL || e2db->field_count < 1) return NULL;

  dbId = DBGetIDFromName (e2db->db_name);
  for (fieldInfo = e2db->fields; fieldInfo != NULL; fieldInfo = fieldInfo->next) {
    if (StringHasNoText (fieldInfo->field_name)) continue;
    choice = (Uint1) FieldGetIDFromName (dbId, fieldInfo->field_name);
    ValNodeCopyStr (&head, choice, fieldInfo->field_menu);
  }

  head = ValNodeSort (head, SortVnpByString);

  alist = MakeEnumFieldAlistFromValNodeList (head);
  ValNodeFreeData (head);

  return alist;
}

/*==================================================================*/
/*                                                                  */
/*  CreateAllFieldsAlist () - Returns sorted uniqued alist of all   */
/*                           field names.                           */
/*                                                                  */
/*==================================================================*/

static EnumFieldAssocPtr CreateAllFieldsAlist (Entrez2InfoPtr e2ip)

{
  EnumFieldAssocPtr    alist;
  Uint1                choice;
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  fieldInfo;
  Int2                 dbId;
  ValNodePtr           head = NULL;

  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    dbId = DBGetIDFromName (e2db->db_name);
    for (fieldInfo = e2db->fields; fieldInfo != NULL; fieldInfo = fieldInfo->next) {
      if (StringHasNoText (fieldInfo->field_name)) continue;
      choice = (Uint1) FieldGetIDFromName (dbId, fieldInfo->field_name);
      ValNodeCopyStr (&head, choice, fieldInfo->field_name);
    }
  }

  head = ValNodeSort (head, SortVnpByString);
  head = UniqueValNode (head);

  alist = MakeEnumFieldAlistFromValNodeList (head);
  ValNodeFreeData (head);

  return alist;
}

/*==================================================================*/
/*                                                                  */
/*  GetUidFromAccn () -                                             */
/*                                                                  */
/*==================================================================*/

static Uint4 GetUidFromAccn (CharPtr dbName, CharPtr accn)

{
  Entrez2BooleanReplyPtr  e2br;
  Entrez2IdListPtr        e2id;
  Entrez2RequestPtr       e2rq;
  Entrez2ReplyPtr         e2ry;
  Char                    ch;
  CharPtr                 ptr;
  Char                    str [61];
  Uint4                   uid = 0;

  if (StringHasNoText (dbName) || StringHasNoText (accn)) return 0;

  StringNCpy_0 (str, accn, sizeof (str));
  ptr = str;
  ch = *ptr;
  while (ch != '\0') {
    if (ch == '|' || ch == '.') {
      *ptr = ' ';
    }
    ptr++;
    ch = *ptr;
  }
  TrimSpacesAroundString (str);
  if (StringStr (str, "[ACCN]") == NULL) {
    StringCat (str, "[ACCN]");
  }
  e2rq = EntrezCreateBooleanRequest (TRUE, FALSE, dbName, str, 0, 0, NULL, 1, 0);

  if (e2rq == NULL) return 0;
  e2ry = SpecialEntrezSynchronousQuery (e2rq);
  e2rq = Entrez2RequestFree (e2rq);
  if (e2ry == NULL) return 0;
  e2br = EntrezExtractBooleanReply (e2ry);
  if (e2br == NULL) return 0;

  if (e2br->count > 0) {
    e2id = e2br->uids;
    if (e2id != NULL && e2id->num > 0 && e2id->uids != NULL) {
      BSSeek (e2id->uids, 0, SEEK_SET);
      uid = Nlm_BSGetUint4 (e2id->uids);
    }
  }

  Entrez2BooleanReplyFree (e2br);

  return uid;
}

/*==================================================================*/
/*                                                                  */
/*  ProcessDirectLookup () -                                        */
/*                                                                  */
/*==================================================================*/

static void ProcessDirectLookup (FormInfoPtr pFormInfo, CharPtr str)

{
  CharPtr  dbName;
  Int4     uid;

  uid = 0;

  /*----------------------------------------*/
  /* Convert the given string to a UID and */
  /* check to see if it is a valid one.    */
  /*---------------------------------------*/

  if ((pFormInfo->currMode == LOOKUP_ACCN_MODE) || (pFormInfo->currMode == VIEW_ACCN_MODE)) {
    if (StringLen (str) > 0) {
      dbName = DBGetNameFromID (pFormInfo->currDb);
      uid = GetUidFromAccn (dbName, str);
    }
  } else if ((pFormInfo->currMode == LOOKUP_UID_MODE) || (pFormInfo->currMode == VIEW_UID_MODE)) {
    if (! StrToLong (str, &uid)) {
      Message (MSG_OK, "You must enter an integer");
      return;
    }
  } else
    return;

  if (uid <= 0) {
    if (pFormInfo->currMode == LOOKUP_ACCN_MODE || pFormInfo->currMode == VIEW_ACCN_MODE) {
      Message (MSG_OK, "This accession is not present in the database");
    } else {
      Message (MSG_OK, "This UID is not present in the database");
    }
    return;
  }

  /*-------------------------------------*/
  /* Lookup the record for the given UID */
  /* and display it in a DocSum window.  */
  /*-------------------------------------*/

  if (pFormInfo->currMode == LOOKUP_ACCN_MODE || pFormInfo->currMode == LOOKUP_UID_MODE) {
    dbName = DBGetNameFromID (pFormInfo->currDb);
    pFormInfo->retrieveUidProc (pFormInfo->form, uid, dbName);
  } else {
    LaunchRecViewer (pFormInfo->form, uid, 1, &uid, pFormInfo->currDb, 1);
  }
}

/*==================================================================*/
/*                                                                  */
/*  DoResetAvail () - Clears out the Term List object.              */
/*                                                                  */
/*==================================================================*/

static void DoResetAvail (FormInfoPtr pFormInfo, Boolean clearTerm)

{
  Int2  i;

  if (clearTerm) {
    SafeSetTitle (pFormInfo->termText, "");
    SafeSetTitle (pFormInfo->toText, "");
    SafeSetTitle (pFormInfo->fromText, "");
    SafeDisable (pFormInfo->acceptButton);
  } else if (pFormInfo->currMode == RANGE_MODE) {
    if (TextHasNoText (pFormInfo->fromText) && TextHasNoText (pFormInfo->toText))
      SafeDisable (pFormInfo->acceptButton);
    else
      SafeEnable (pFormInfo->acceptButton);
  } else {
    if (TextHasNoText (pFormInfo->termText))
      SafeDisable (pFormInfo->acceptButton);
    else
      SafeEnable (pFormInfo->acceptButton);
  }

  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  Reset (pFormInfo->availDoc);
  SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);

  for (i = 0; i < 7; i++)
    AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt, systemFont);

  InvalDocument (pFormInfo->availDoc);
  SafeHide (pFormInfo->taxLineagePopup);
  Reset (pFormInfo->taxLineagePopup);
  pFormInfo->taxStrings = ValNodeFreeData (pFormInfo->taxStrings);
  pFormInfo->availNumTerms = 0;
  pFormInfo->availPageSize = 0;
  pFormInfo->availNumPages = 0;
}

/*==================================================================*/
/*                                                                  */
/* FreeTerm () - Frees a given term from the term list.             */
/*                                                                  */
/*==================================================================*/

static void FreeTerm (StateDataPtr termPtr)

{
  MemFree (termPtr->term);
  MemFree (termPtr->field);
  MemFree (termPtr);
}

/*==================================================================*/
/*                                                                  */
/* FreeTermList () - Frees all the terms in the current query       */
/*                    term list.                                    */
/*                                                                  */
/*==================================================================*/

static StateDataPtr FreeTermList (FormInfoPtr pFormInfo)

{
  StateDataPtr  next;

  while (pFormInfo->termList != NULL) {
    next = pFormInfo->termList->next;
    FreeTerm (pFormInfo->termList);
    pFormInfo->termList = next;
  }

  return NULL;
}

/*==================================================================*/
/*                                                                  */
/*  ResetChosen () - Clears out the Query Refinement object.        */
/*                                                                  */
/*==================================================================*/

static void ResetChosen (FormInfoPtr pFormInfo)

{
  /*--------------*/
  /* Clear it out */
  /*--------------*/

  Reset (pFormInfo->chosenDoc);
  FreeTermList (pFormInfo);
  SetDocCache (pFormInfo->chosenDoc, NULL, NULL, NULL);

  InvalDocument (pFormInfo->chosenDoc);
  pFormInfo->chosenNumLines = 0;

  SafeSetTitle (pFormInfo->retrieveButton, "Retrieve 0 Documents");
  SafeDisable (pFormInfo->retrieveButton);
}

/*==================================================================*/
/*                                                                  */
/*  Reset_Callback () - Called when reset button is pushed. Clears  */
/*                     the Term List and Query Refinement windows.  */
/*                                                                  */
/*==================================================================*/

static void Reset_Callback (ButtoN b)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (b);
  DoResetAvail (pFormInfo, TRUE);
  ResetChosen (pFormInfo);
  Reset (pFormInfo->advQueryText);
  pFormInfo->isValidFrom = FALSE;
  pFormInfo->isValidTo = FALSE;
}

/*==================================================================*/
/*                                                                  */
/*  StateDataNew() - Creates a new Query term structure and adds    */
/*                     it to the query term list.                   */
/*                                                                  */
/*==================================================================*/

static StateDataPtr StateDataNew (FormInfoPtr pFormInfo)

{
  StateDataPtr  newNode;
  StateDataPtr  tempNode;

  /*-------------------*/
  /* Create a new node */
  /*-------------------*/

  newNode = (StateDataPtr) MemNew (sizeof (StateData));
  if (newNode == NULL) return NULL;

  /*-------------------------------*/
  /* Add it to the end of the list */
  /*-------------------------------*/

  tempNode = pFormInfo->termList;
  if (tempNode != NULL) {
    while (tempNode->next != NULL)
      tempNode = tempNode->next;
    tempNode->next = newNode;
    newNode->prev = tempNode;
  } else {                      /* Empty list -- this is first term */

    newNode->prev = NULL;
    newNode->next = NULL;
    pFormInfo->termList = newNode;
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return newNode;
}

/*===================================================================*/
/*                                                                   */
/*  CreateTerm () - Creates a new boolean term using the given       */
/*                       data and adds it to the linked list of      */
/*                       terms.                                      */
/*                                                                   */
/*===================================================================*/

static StateDataPtr CreateTerm (
  ForM    f,
  Int2    currDb,
  Int2    currFld,
  CharPtr strs,
  Int2    state,
  Int4    num,
  Boolean allowDuplicates
)

{
  StateDataPtr         prev;
  StateDataPtr         sdp;
  Entrez2FieldInfoPtr  fieldInfo;
  FormInfoPtr          pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*----------------------------------*/
  /* Make sure we're don't try to add */
  /* the same string twice.           */
  /*----------------------------------*/

  if (! allowDuplicates) {
    for (sdp = pFormInfo->termList; sdp != NULL; sdp = sdp->next) {
      if (MeshStringICmp (sdp->term, strs) == 0 &&
          sdp->db == currDb && sdp->fld == currFld) return NULL;
    }
  }

  /*------------------------------*/
  /* Create a term for the string */
  /* on the end of the list.      */
  /*------------------------------*/

  sdp = StateDataNew (pFormInfo);
  if (pFormInfo->termList == NULL)
    pFormInfo->termList = sdp;

  if (sdp == NULL) return NULL;

  (pFormInfo->chosenNumLines)++;

  sdp->term = StringSave (strs);
  sdp->count = num;
  sdp->db = currDb;
  sdp->fld = currFld;
  sdp->uids = NULL;
  sdp->key = NULL;
  fieldInfo = FieldGetInfo (currDb, currFld);
  if (fieldInfo == NULL) return NULL;
  sdp->field = StringSave (fieldInfo->field_name);
  if (sdp->field == NULL)
    sdp->field = StringSave ("----");
  sdp->group = GROUP_SINGLE;
  sdp->above = ENTREZ_OP_NONE;

  /*-------------------------------------*/
  /* Add the term to the current Query's */
  /* linked list of terms.               */
  /*-------------------------------------*/

  prev = sdp->prev;
  if (prev != NULL) {
    sdp->above = ENTREZ_OP_AND;
    prev->below = ENTREZ_OP_AND;
  }
  sdp->below = ENTREZ_OP_NONE;
  sdp->state = state;

  /*------------------------------*/
  /* Return a ptr to the new term */
  /*------------------------------*/

  return sdp;
}

/*==================================================================*/
/*                                                                  */
/*  DisplayTerm () -                                                */
/*                                                                  */
/*==================================================================*/

static void DisplayTerm (FormInfoPtr pFormInfo, CharPtr term, CharPtr fieldName, Int4 total)

{
  RecT  r;
  BaR   sb;
  Char  strn [256];

  /*------------------------------------------*/
  /* Create a display string for the term and */
  /* add it to the 'chosen' window.           */
  /*------------------------------------------*/

  sprintf (strn, "%s\t [%s]\t%ld\n", term, fieldName, (long) total);
  AppendText (pFormInfo->chosenDoc, strn, &chosenParFmt, chosenColFmt, systemFont);

  /*---------------------------------*/
  /* Display our new query string in */
  /* Query Refinement window.        */
  /*---------------------------------*/

  InvalDocRows (pFormInfo->chosenDoc, pFormInfo->chosenNumLines, 1, 1);
  AdjustDocScroll (pFormInfo->chosenDoc);
  sb = GetSlateVScrollBar ((SlatE) pFormInfo->chosenDoc);
  ResetClip ();
  SetBarValue (sb, MAX (pFormInfo->chosenNumLines - 7, 0));
  ObjectRect (pFormInfo->chosenDoc, &r);
  InsetRect (&r, 4, 4);
  r.right = r.left + 16;
  InsetRect (&r, -1, -1);
  Select (pFormInfo->chosenDoc);
  InvalRect (&r);

  Update ();
}

/*==================================================================*/
/*                                                                  */
/*  Query_AddBoolTerm () - Adds a boolean term to the linked list   */
/*                        of terms that form the query.             */
/*                                                                  */
/*==================================================================*/

static StateDataPtr Query_AddBoolTerm (ForM f, Int2 currDb, Int2 currFld, CharPtr strs, Int2 state, Int4 num)

{
  StateDataPtr  sdp;
  FormInfoPtr   pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*-----------------------------*/
  /* Add the term and display it */
  /*-----------------------------*/

  sdp = CreateTerm (f, currDb, currFld, strs, state, num, FALSE);
  if (sdp != NULL) {
    DisplayTerm (pFormInfo, strs, sdp->field, num);
  }

  /*------------------------------*/
  /* Return a ptr to the new term */
  /*------------------------------*/

  return sdp;
}

/*==================================================================*/
/*                                                                  */
/*  ProcessMeshTerm () -                                            */
/*                                                                  */
/*==================================================================*/

static void ProcessMeshTerm (FormInfoPtr pFormInfo, CharPtr str)

{
  Int4     iTermCount;
  CharPtr  ptr;
  CharPtr  text;

  /*---------------------------*/
  /* Trim off extra spaces and */
  /* curly-bracketed info      */
  /*---------------------------*/

  ptr = str;
  while (*ptr != '\0' && *ptr != '{') {
    ptr++;
  }
  *ptr = '\0';

  /*-----------------------------*/
  /* Get the term count from the */
  /* term selection window.      */
  /*-----------------------------*/

  text = GetDocText (pFormInfo->availDoc, pFormInfo->availItem, pFormInfo->availRow, E2_COUNT_COL);
  TrimSpacesAroundString (text);
  StrToLong (text, &iTermCount);
  MemFree (text);

  /*-------------------------------*/
  /* Load the chosen term into the */
  /* query refinement window.      */
  /*-------------------------------*/

  Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb, pFormInfo->currField, str, STATE_ON, iTermCount);
}

/*==================================================================*/
/*                                                                  */
/*  ValNodeMerge () -                                               */
/*                                                                  */
/*==================================================================*/

static CharPtr ValNodeMerge (ValNodePtr head)

{
  size_t      len;
  CharPtr     str;
  CharPtr     to;
  ValNodePtr  vnp;

  if (head == NULL) return NULL;

  for (vnp = head, len = 0; vnp != NULL; vnp = vnp->next)
    len += StringLen ((CharPtr) vnp->data.ptrvalue);

  str = (CharPtr) MemNew (len + 4);
  if (str == NULL) return NULL;

  for (vnp = head, to = str; vnp != NULL; vnp = vnp->next)
    to = StringMove (to, (CharPtr) vnp->data.ptrvalue);

  return str;
}

/*==================================================================*/
/*                                                                  */
/*  FetchFromTermList () -                                          */
/*                                                                  */
/*==================================================================*/

static CharPtr FetchFromTermList (DoC d, Int2 item, Pointer ptr)

{
  FormInfoPtr          pFormInfo;
  CharPtr              db, field;
  Entrez2RequestPtr    e2rq;
  Entrez2ReplyPtr      e2ry;
  Entrez2TermListPtr   e2tl;
  Entrez2TermPtr       e2tp;
  Entrez2FieldInfoPtr  fieldInfo;
  Int4                 firstPos, numTerms, page;
  ValNodePtr           head = NULL;
  CharPtr              rsult = NULL;
  Char                 str [256], tmp [16];

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  if (item < 1) return NULL;

  page = item - 1;

  db = DBGetNameFromID (pFormInfo->currDb);
  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  field = fieldInfo->field_name;
  if (StringHasNoText (db) || StringHasNoText (field)) return NULL;

  firstPos = pFormInfo->availPageSize * page;
  numTerms = pFormInfo->availPageSize;
  if (firstPos + numTerms > pFormInfo->availNumTerms)
    numTerms = pFormInfo->availNumTerms - firstPos;

  e2rq = EntrezCreateGetTermListRequest (db, field, firstPos, numTerms);
  if (e2rq == NULL) return NULL;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2rq);

  e2ry = SpecialEntrezSynchronousQuery (e2rq);
  if (e2ry != NULL) {
    if (ShowASN () == TRUE)
      DisplayEntrezReply (e2ry);

    e2tl = EntrezExtractTermListReply (e2ry);
    if (e2tl != NULL)
      for (e2tp = e2tl->list; e2tp != NULL; e2tp = e2tp->next) {
        StringNCpy_0 (str, e2tp->term, sizeof (str) - 16);
        sprintf (tmp, "\t%ld\n", (long) e2tp->count);
        StringCat (str, tmp);
        ValNodeCopyStr (&head, 0, str);
      }
  }
  Entrez2RequestFree (e2rq);

  rsult = ValNodeMerge (head);
  ValNodeFreeData (head);

  return rsult;
}

/*==================================================================*/
/*                                                                  */
/*  GetTermPage () -                                                */
/*                                                                  */
/*==================================================================*/

static Int4 GetTermPage (FormInfoPtr pFormInfo, CharPtr str)

{
  CharPtr              db;
  CharPtr              field;
  Entrez2RequestPtr    e2rq;
  Entrez2ReplyPtr      e2ry;
  Int4                 pagesize = -1;
  Int4                 pos = -1;
  Int4                 rsult = -1;
  Entrez2FieldInfoPtr  fieldInfo;

  db = DBGetNameFromID (pFormInfo->currDb);
  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  field = fieldInfo->field_name;

  if (StringHasNoText (db) || StringHasNoText (field)) return rsult;

  e2rq = EntrezCreateGetTermPositionRequest (db, field, str);
  if (e2rq == NULL) return rsult;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2rq);

  e2ry = SpecialEntrezSynchronousQuery (e2rq);
  if (e2ry != NULL) {
    if (ShowASN () == TRUE)
      DisplayEntrezReply (e2ry);

    pos = EntrezExtractTermPosReply (e2ry);
    pagesize = pFormInfo->availPageSize;
    if (pagesize > 0)
      rsult = (pos - 1) / pagesize;
  }
  Entrez2RequestFree (e2rq);

  if (pos == -1) return -1;

  return rsult;
}

/*==================================================================*/
/*                                                                  */
/*  ChangeUnderscoreToSpace () -                                    */
/*                                                                  */
/*==================================================================*/

static void ChangeUnderscoreToSpace (CharPtr str)

{
  Char     ch;
  CharPtr  ptr;

  if (str == NULL) return;
  ptr = str;
  ch = *ptr;
  while (ch != '\0') {
    if (ch == '_')
      *ptr = ' ';
    ptr++;
    ch = *ptr;
  }
}

/*==================================================================*/
/*                                                                  */
/*  ChangePeriodToSpace () -                                    */
/*                                                                  */
/*==================================================================*/

static void ChangePeriodToSpace (CharPtr str)

{
  Char     ch;
  CharPtr  ptr;

  if (str == NULL) return;
  ptr = str;
  ch = *ptr;
  while (ch != '\0') {
    if (ch == '.')
      *ptr = ' ';
    ptr++;
    ch = *ptr;
  }
}

/*==================================================================*/
/*                                                                  */
/*  ChangeMeshSlashSymbol () -                                      */
/*                                                                  */
/*==================================================================*/

static void ChangeMeshSlashSymbol (CharPtr str)

{
  Char     ch;
  CharPtr  ptr;

  if (str == NULL) return;

  ptr = str;
  ch = *ptr;
  while (ch != '\0') {
    if (ch == '/')
      *ptr = '\31';
    ptr++;
    ch = *ptr;
  }
}

/*==================================================================*/
/*                                                                  */
/*  FindLineOfText () - Assumes a string of text separated by       */
/*                     newline, characters, and terminated by a     */
/*                     newline                                      */
/*                                                                  */
/*==================================================================*/

static Int2 FindLineOfText (CharPtr text, CharPtr str)

{
  Char          ch;
  Int2          compare, idx, left, mid, numLines, right;
  CharPtr PNTR  index;
  CharPtr       lookfor, ptr;
  size_t        len;

  /* Check parameters */

  if (StringLen (text) == 0 || StringLen (str) == 0) return 0;

  /* Count the number of lines to be searched */

  mid = 0;
  lookfor = StringSave (str);
  ChangeMeshSlashSymbol (lookfor);
  numLines = 0;
  ptr = text;
  ch = *ptr;
  while (ch != '\0') {
    if (ch == '\n')
      numLines++;
    ptr++;
    ch = *ptr;
  }

  if (numLines <= 0) {
    MemFree (lookfor);
    return 0;
  }

  /* Create an array of pointers to the lines */

  index = MemNew (sizeof (CharPtr) * (size_t) (numLines + 3));
  if (index != NULL) {
    idx = 0;
    ptr = text;
    ch = *ptr;
    index [idx] = ptr;
    while (ch != '\0') {
      if (ch == '\n') {
        idx++;
        *ptr = '\0';
        ptr++;
        ch = *ptr;
        index [idx] = ptr;
      } else {
        ptr++;
        ch = *ptr;
      }
    }
  }

  /* Do a binary search for the search string */

  if (index != NULL) {
    left = 1;
    right = numLines;
    while (left <= right) {
      mid = (left + right) / 2;
      compare = StringICmp (lookfor, index [mid - 1]);
      if (compare <= 0)
        right = mid - 1;
      if (compare >= 0)
        left = mid + 1;
    }

    if (left <= right + 1) {
      len = StringLen (lookfor);
      compare = StringNICmp (lookfor, index [mid - 1], len);
      if (compare > 0) {
        mid++;
        /*
        if (mid <= numLines) {
      compare = StringNICmp (lookfor, index [mid - 1], len);
      if (compare > 0)
        mid = 0;
        } else
      mid = 0;
        */
      }
      /*
      else
        mid = 0;
      */
    }
  }
  
  /* Clean up and return */

  MemFree (index);
  MemFree (lookfor);
  return mid;
}

/*==================================================================*/
/*                                                                  */
/*  ScrollToText () -                                               */
/*                                                                  */
/*==================================================================*/

static void ScrollToText (FormInfoPtr pFormInfo,
              CharPtr     str,
              Int2        page,
              Boolean     hard,
              Boolean     exact)

{
  Int2     compare, oldItem, oldRow, perfect, row;
  size_t   len;
  Int4     numLines, startsAt;
  BaR      sb;
  Char     temp [256];
  CharPtr  text;

  /* Check parameters */

  if (StringHasNoText (str) || page == 0) return;

  /* Clean up the text string */

  StringNCpy_0 (temp, str, sizeof (temp));
  ChangeUnderscoreToSpace (temp);
  ChangePeriodToSpace (temp);
  ChangeMeshSlashSymbol (temp);

  /* Get the information on the current page of data */

  text = GetDocText (pFormInfo->availDoc, page, 0, 1);
  GetItemParams4 (pFormInfo->availDoc, page, &startsAt, NULL, NULL, NULL, NULL);
  GetDocParams4 (pFormInfo->availDoc, NULL, &numLines);
  ChangeMeshSlashSymbol (text);

  /* Search for the text string in the current page of data */

  row = FindLineOfText (text, temp);
  MemFree (text);

  /* If the text string is not on the page */
  /* then go to the top of the page.       */

  if (row < 1 || row > numLines)
    /*
    return;
    */
    row = 1;

  /* If the term is not on the current page then */
  /* we have to load and search the new page.    */

  /*
  if (row <= 1 || row >= numLines) {
    LoadAvailList (pFormInfo, str);
    return;
  }
  */

  /* Redisplay the page with the text string highlighted */
  
  startsAt += row - 1;
  sb = GetSlateVScrollBar ((SlatE) pFormInfo->availDoc);
  CorrectBarMax (sb, numLines - 7);
  if (!RowIsVisible (pFormInfo->availDoc, page, row, NULL, NULL)) {
    ForceFormat (pFormInfo->availDoc, page);    /* forces UpdateLineStarts */
    GetItemParams4 (pFormInfo->availDoc, page, &startsAt, NULL, NULL,
            NULL, NULL);
    GetDocParams4 (pFormInfo->availDoc, NULL, &numLines);
    startsAt += row - 1;
    sb = GetSlateVScrollBar ((SlatE) pFormInfo->availDoc);
    CorrectBarMax (sb, numLines - 7);
    if (hard)
      SetBarValue (sb, startsAt);
    else
      CorrectBarValue (sb, startsAt);
  }
  text = GetDocText (pFormInfo->availDoc, page, row, 1);
  ChangeMeshSlashSymbol (text);
  perfect = StringICmp (text, temp);
  len = StringLen (temp);
  compare = StringNICmp (text, temp, len);
  MemFree (text);
  if (compare == 0) {
    oldItem = pFormInfo->availItem;
    oldRow = pFormInfo->availRow;
    if (oldItem != page || oldRow != row) {
      pFormInfo->availItem = page;
      pFormInfo->availRow = row;
      InvalDocRows (pFormInfo->availDoc, oldItem, oldRow, oldRow);
      InvalDocRows (pFormInfo->availDoc, pFormInfo->availItem,
            pFormInfo->availRow, pFormInfo->availRow);
    }
    if (exact) {
      if (perfect == 0)
        pFormInfo->textChanged = FALSE;
    } else
      pFormInfo->textChanged = FALSE;
  } else {
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
  }
}

/*==================================================================*/
/*                                                                  */
/*  LoadAvailList () -                                              */
/*                                                                  */
/*==================================================================*/

static Boolean LoadAvailList (FormInfoPtr pFormInfo, CharPtr str)

{
  Int4                 count;
  Entrez2FieldInfoPtr  fieldInfo;
  Int4                 pagesize;
  Int2                 numpages = 0;
  Int2                 page = 0;

  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  Reset (pFormInfo->availDoc);
  SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);
  Update ();
  if (str [0] == '\0') return FALSE;

  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  count = fieldInfo->term_count;
  if (count == 0) return FALSE;

  pFormInfo->availNumTerms = count;
  pagesize = 1;
  while (count > pagesize) {
    count /= 2;
    pagesize *= 2;
  }

  while (count < 16000 && pagesize > 128) {
    count *= 2;
    pagesize /= 2;
  }
  pFormInfo->availPageSize = pagesize;
  numpages = (Int2) ((pFormInfo->availNumTerms + pagesize - 1) / pagesize);
  pFormInfo->availNumPages = numpages;

  BulkAppendItem (pFormInfo->availDoc, numpages, FetchFromTermList, pagesize, &availParFmt, availColFmt, systemFont);
  AppendText (pFormInfo->availDoc, "\n\n\n\n\n\n\n\n\n\n\n\n \n", &availParFmt, availColFmt, systemFont);

  SetDocCache (pFormInfo->availDoc, StdPutDocCache, StdGetDocCache, StdResetDocCache);

  page = GetTermPage (pFormInfo, str);
  pFormInfo->availCurrentPage = page + 1;
  if (page != -1) {
    ScrollToText (pFormInfo, str, pFormInfo->availCurrentPage, FALSE, FALSE);
  }

  InvalDocument (pFormInfo->availDoc);
  Update ();
  AdjustDocScroll (pFormInfo->availDoc);

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  ProcessSelectionTerm () -                                       */
/*                                                                  */
/*==================================================================*/

static void ProcessSelectionTerm (FormInfoPtr pFormInfo, CharPtr str)

{
  Int4     iTermCount;
  Char     termText [E2_STR_BUFF_SIZE];
  CharPtr  text;

  /* If a new term has been typed into the */
  /* term text entry box, then do a lookup */
  /* of that term.                         */

  if (pFormInfo->textChanged) {
    LoadAvailList (pFormInfo, str);
    pFormInfo->textChanged = FALSE;
    pFormInfo->okayToAccept = FALSE;
  }

  /* Process the highlighted term from */
  /* the term selection box            */

  else if (pFormInfo->okayToAccept &&
       pFormInfo->availItem > 0 &&
       pFormInfo->availRow > 0) {

    text = GetDocText (pFormInfo->availDoc, pFormInfo->availItem,
               pFormInfo->availRow, E2_TERM_COL);
    if (NULL == text)
      return;

    GetTitle (pFormInfo->termText, termText, E2_STR_BUFF_SIZE);

    /* If the higlighted term is the same as */
    /* in the text entry box, then add the   */
    /* term to the Query Refinement list.    */

    if (StringICmp (termText, text) == 0) {
      if (StringICmp (str, text) == 0) {
    MemFree (text);
    text = GetDocText (pFormInfo->availDoc, pFormInfo->availItem,
               pFormInfo->availRow, E2_COUNT_COL);
    TrimSpacesAroundString (text);
    StrToLong (text, &iTermCount);
    MemFree (text);
    Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb,
               pFormInfo->currField, str, STATE_ON,
               iTermCount);
    Select (pFormInfo->termText);
    pFormInfo->okayToAccept = FALSE;
      }
    }

    /* Otherwise, just move the higlighted */
    /* term to the text box.               */

    else {
      TrimSpacesAroundString (text);
      SafeSetTitle (pFormInfo->termText, text);
      Select (pFormInfo->termText);
      Update ();
      StringNCpy_0 (str, text, E2_STR_BUFF_SIZE);
    }

  }
}

/*==================================================================*/
/*                                                                  */
/*  ProcessTaxonomyTerm () -                                        */
/*                                                                  */
/*==================================================================*/

static void ProcessTaxonomyTerm (FormInfoPtr pFormInfo, CharPtr str)

{
  Int4     iTermCount;
  CharPtr  text;

  /*----------------------*/
  /* Get the term's count */
  /*----------------------*/

  text = GetDocText (pFormInfo->availDoc, pFormInfo->availItem, pFormInfo->availRow, E2_COUNT_COL);
  TrimSpacesAroundString (text);
  StrToLong (text, &iTermCount);
  MemFree (text);

  /*-----------------------------------*/
  /* Add the term to the current query */
  /*-----------------------------------*/

  Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb, pFormInfo->currField, str, STATE_ON, iTermCount);
  Select (pFormInfo->termText);
  pFormInfo->okayToAccept = FALSE;
}

/*===================================================================*/
/*                                                                   */
/*  ReadAFew () - Fetches the count for a given term and for several */
/*               following terms and displays them in the term list  */
/*               panel.                                              */
/*                                                                   */
/*===================================================================*/

static Boolean ReadAFew (FormInfoPtr pFormInfo, CharPtr str, CharPtr actual, size_t maxsize, Int4Ptr count)

{
  Char                 displayStr [256];
  Boolean              found;
  Entrez2TermListPtr   e2TermListPtr;
  Entrez2FieldInfoPtr  fieldInfo;
  CharPtr              dbName;
  CharPtr              fieldName;
  Int2                 row;
  Entrez2TermPtr       termPtr;

  /*------------------------------*/
  /* Get the names of the current */
  /* db and the current field.    */
  /*------------------------------*/

  found = FALSE;
  dbName = DBGetNameFromID (pFormInfo->currDb);
  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  fieldName = fieldInfo->field_name;
  if (StringHasNoText (dbName) || StringHasNoText (fieldName)) return FALSE;

  /*----------------------------------------*/
  /* Fetch a count for the requested string */
  /* and for several strings after it.      */
  /*----------------------------------------*/

  e2TermListPtr = Query_FetchSeveralCounts (dbName, fieldName, str, AVAIL_WINDOW_ROWS);

  if (e2TermListPtr == NULL) return FALSE;

  /*-------------------------------*/
  /* Did we find the desired term? */
  /*-------------------------------*/

  if (StringICmp (e2TermListPtr->list->term, str) == 0) {
    found = TRUE;
  } else {
    found = FALSE;
    StringCpy (actual, e2TermListPtr->list->term);
  }

  /*----------------------------*/
  /* Send the count back to the */
  /* calling function.          */
  /*----------------------------*/

  *count = e2TermListPtr->list->count;

  /*-------------------------------------*/
  /* Display the term and it's neighbors */
  /* in the term list window.            */
  /*-------------------------------------*/

  termPtr = e2TermListPtr->list;

  for (row = 1, termPtr = e2TermListPtr->list; row <= *count, termPtr != NULL; row++, termPtr = termPtr->next) {
    sprintf (displayStr, "%s\t%ld\t%d\n", termPtr->term, (long) (termPtr->count), (int) (termPtr->is_leaf_node ? 1 : 0));
    AppendText (pFormInfo->availDoc, displayStr, &availParFmt, availColFmt, systemFont);
  }

  if (found) {
    pFormInfo->availItem = 1;
    pFormInfo->availRow = 1;
  }
  InvalDocument (pFormInfo->availDoc);

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return found;
}

/*==================================================================*/
/*                                                                  */
/*  ProcessMultipleTerms () -                                       */
/*                                                                  */
/*==================================================================*/

static void ProcessMultipleTerms (FormInfoPtr pFormInfo, CharPtr strs)

{
  Char     actual [256];
  Char     ch;
  Int4     count = 0;
  Boolean  found;
  Int2     i;
  Int2     j;
  Int2     k;
  Char     str [256];

  i = 0;
  while (StringLen (strs + i) > 0) {

    /*--------------------------------*/
    /* Parse a term out of the string */
    /*--------------------------------*/

    StringNCpy_0 (str, strs + i, sizeof (str));
    k = 0;
    ch = str [k];
    while (ch == ' ') {
      k++;
      ch = str [k];
    }
    j = 0;
    if (ch == '"') {
      k++;
      ch = str [j + k];
      while (ch != '\0' && ch != '"') {
        j++;
        ch = str [j + k];
      }
      if (ch == '"') {
        str [j + k] = '\0';
        i += j + k + 1;
      } else
        i += j + k;
    } else {
      while (ch != '\0' && ch != ' ') {
        j++;
        ch = str [j + k];
      }
      if (ch == ' ') {
        str [j + k] = '\0';
        i += j + k + 1;
      } else
        i += j + k;
    }

    /*-----------------------------------*/
    /* If we successfully parsed a term, */
    /* look it up and then add it to the */
    /* boolean query if found.           */
    /*-----------------------------------*/

    if (StringLen (str + k) > 0) {
      pFormInfo->availItem = 0;
      pFormInfo->availRow = 0;
      Reset (pFormInfo->availDoc);
      actual [0] = '\0';
      found = ReadAFew (pFormInfo, str + k, actual, sizeof (actual), &count);
      Update ();
      if (found) {
        Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb, pFormInfo->currField, str + k, STATE_ON, count);
      } else {
        Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb, pFormInfo->currField, actual, STATE_OFF, count);
      }
    }
  }

  /*-------------------------------------*/
  /* Clear out the term selection window */
  /* and the term entry fields.          */
  /*-------------------------------------*/

  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  Reset (pFormInfo->availDoc);

  InvalDocument (pFormInfo->availDoc);
  SafeSetTitle (pFormInfo->termText, "");
  SafeSetTitle (pFormInfo->fromText, "");
  SafeSetTitle (pFormInfo->toText, "");
  SafeDisable (pFormInfo->acceptButton);
  if (Visible (pFormInfo->termText)) {
    Select (pFormInfo->termText);
  }
}

/*==================================================================*/
/*                                                                  */
/*  RecalculateChosen () -                                          */
/*                                                                  */
/*==================================================================*/

static void RecalculateChosen (FormInfoPtr pFormInfo)

{
  Int4  count;
  Char  tmp [32];

  /*-------------------------------*/
  /* Get a count of documents that */
  /* satisfy the current query.    */
  /*-------------------------------*/

  count = Query_FetchCount (pFormInfo->form);

  /*-----------------------------------*/
  /* Update the retrieve message count */
  /*-----------------------------------*/

  if (count < 1)
    sprintf (tmp, "Retrieve 0 Documents");
  else if (count > 1)
    sprintf (tmp, "Retrieve %ld Documents", (long) count);
  else
    sprintf (tmp, "Retrieve %ld Document", (long) count);

  SafeSetTitle (pFormInfo->retrieveButton, tmp);
  SafeSetTitle (pFormInfo->advRetrieveButton, tmp);

  if (count < 1 || count > 32000) {
    SafeDisable (pFormInfo->retrieveButton);
    SafeDisable (pFormInfo->advRetrieveButton);
  } else {
    SafeEnable (pFormInfo->retrieveButton);
    SafeEnable (pFormInfo->advRetrieveButton);
  }
}

/*==================================================================*/
/*                                                                  */
/*  Query_TruncateCount () -                                        */
/*                                                                  */
/*==================================================================*/

static Int4 Query_TruncateCount (FormInfoPtr pFormInfo, CharPtr str)

{
  CharPtr                 dbName;
  Boolean                 doNotExplode;
  Boolean                 doNotTranslate;
  Int4                    count;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  Entrez2FieldInfoPtr     fieldInfo;

  if (pFormInfo == NULL || StringHasNoText (str)) return 0;

  /*--------------------------------*/
  /* Get the name of the current DB */
  /*--------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  if (StringHasNoText (dbName)) return 0;

  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  if (fieldInfo == NULL) return 0;

  /*---------------------------------*/
  /* Create an empty Boolean request */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateBooleanRequest (FALSE, FALSE, dbName, NULL, 0, 0, NULL, 0, 0);
  if (e2RequestPtr == NULL) return 0;

  /*--------------------------------------------------*/
  /* Send truncated term count request to the server. */
  /*--------------------------------------------------*/

  doNotTranslate = FALSE;
  doNotExplode = !GetStatus (pFormInfo->explodeItem);
  EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, fieldInfo->field_name,
                             str, NULL, 0, 0, NULL, NULL,
                             doNotExplode, doNotTranslate);

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return 0;

  /*----------------------------------*/
  /* Parse the count out of the reply */
  /*----------------------------------*/

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);
  count = e2BooleanPtr->count;

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  Entrez2BooleanReplyFree (e2BooleanPtr);
  Entrez2RequestFree (e2RequestPtr);

  return count;
}

/*==================================================================*/
/*                                                                  */
/*  Query_FetchRangeCount () -                                      */
/*                                                                  */
/*==================================================================*/

static Int4 Query_FetchRangeCount (FormInfoPtr pFormInfo, CharPtr str)

{
  CharPtr                 dbName;
  Int4                    count;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  Entrez2FieldInfoPtr     fieldInfo;
  Boolean                 doNotTranslate;

  if (pFormInfo == NULL || StringHasNoText (str)) return 0;

  /*--------------------------------*/
  /* Get the name of the current DB */
  /*--------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  if (StringHasNoText (dbName)) return 0;

  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  if (fieldInfo == NULL) return 0;

  /*---------------------------------*/
  /* Create an empty Boolean request */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateBooleanRequest (FALSE, FALSE, dbName, NULL, 0, 0, NULL, 0, 0);
  if (e2RequestPtr == NULL) return 0;

  /*--------------------------------------------------*/
  /* Send truncated term count request to the server. */
  /*--------------------------------------------------*/

  doNotTranslate = FALSE;
  EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, fieldInfo->field_name, str, NULL, 0, 0, NULL, NULL, FALSE, doNotTranslate);

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return 0;

  /*----------------------------------*/
  /* Parse the count out of the reply */
  /*----------------------------------*/

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);
  if (e2BooleanPtr != NULL)
    count = e2BooleanPtr->count;
  else {
    Entrez2RequestFree (e2RequestPtr);
    return 0;
  }
    

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  Entrez2BooleanReplyFree (e2BooleanPtr);
  Entrez2RequestFree (e2RequestPtr);

  return count;
}

/*===================================================================*/
/*                                                                   */
/* ProcessRangeTerms ()                                              */
/*                                                                   */
/*===================================================================*/

static void ProcessRangeTerms (FormInfoPtr pFormInfo)
{
  Char          fromStr [E2_STR_BUFF_SIZE];
  Char          toStr [E2_STR_BUFF_SIZE];
  Char          rangeStr [2 * E2_STR_BUFF_SIZE + 1];
  StateDataPtr  sdp;
  Int4          count;
  CharPtr       text;

  /* If a new term has been typed into the */
  /* 'To' text box, then do a lookup of    */
  /* that term.                            */

  if (pFormInfo->isToTextChanged) {
    GetTitle (pFormInfo->toText, toStr, E2_STR_BUFF_SIZE);
    pFormInfo->isValidTo = LoadAvailList (pFormInfo, toStr);
    pFormInfo->isToTextChanged = FALSE;
  }
  /* If we have valid 'From' and 'To' terms then */
  /* create a range term in the query window.    */

  else if ((pFormInfo->isValidFrom) && (pFormInfo->isValidTo)) {

    /* Get the 'from' and 'to' strings */

    GetTitle (pFormInfo->toText, toStr, E2_STR_BUFF_SIZE);
    GetTitle (pFormInfo->fromText, fromStr, E2_STR_BUFF_SIZE);
    sprintf(rangeStr, "%s:%s", fromStr, toStr);
    
    /* Get a count for the range */
    
    count = Query_FetchRangeCount (pFormInfo, rangeStr);
    
    /*  Add a term to the linked list */
    
    sdp = CreateTerm (pFormInfo->form, pFormInfo->currDb,
                      pFormInfo->currField, rangeStr, STATE_ON, count,
                      FALSE);
    if (NULL == sdp)
      return;
    
    /* Display the range as one term */
    
    DisplayTerm (pFormInfo, rangeStr, sdp->field, sdp->count);
    
    Select (pFormInfo->fromText);
    Reset (pFormInfo->availDoc);
    pFormInfo->okayToAccept = FALSE;
  }

  /* If a new term has been typed into the */
  /* 'From' text box, then do a lookup of  */
  /* that term.                            */

  else if (pFormInfo->isFromTextChanged) {
    GetTitle (pFormInfo->fromText, fromStr, E2_STR_BUFF_SIZE);
    pFormInfo->isValidFrom = LoadAvailList (pFormInfo, fromStr);
    pFormInfo->isFromTextChanged = FALSE;
  }
    
  else if (pFormInfo->okayToAccept &&
           pFormInfo->availItem > 0 &&
           pFormInfo->availRow > 0) {
    text = GetDocText (pFormInfo->availDoc, pFormInfo->availItem,
                       pFormInfo->availRow, E2_TERM_COL);
    if (text != NULL) {
      TrimSpacesAroundString (text);
      if (pFormInfo->currRangeField == E2_RANGE_FROM) {
        SafeSetTitle (pFormInfo->fromText, text);
        Select (pFormInfo->fromText);
        pFormInfo->isValidFrom = TRUE;
        pFormInfo->isFromTextChanged = FALSE;
      }
      else {
        SafeSetTitle (pFormInfo->toText, text);
        Select (pFormInfo->toText);
        pFormInfo->isValidTo = TRUE;
        pFormInfo->isToTextChanged = FALSE;
      }
      Update ();
    }
    MemFree (text);
  }

  /* Return successfully */
  
  return;
}

/*===================================================================*/
/*                                                                   */
/*  Accept_Callback () - Called when the accept button is pressed,   */
/*                      if fetches a term list or adds term to query */
/*                      list.                                        */
/*                                                                   */
/*===================================================================*/

static void Accept_Callback (ButtoN b)

{
  Int4          count;
  FormInfoPtr   pFormInfo;
  StateDataPtr  sdp;
  Char          str [E2_STR_BUFF_SIZE];

  pFormInfo = (FormInfoPtr) GetObjectExtra (b);

  str [0] = '\0';
  if (pFormInfo->currMode == RANGE_MODE)
    GetTitle (CurrentText (), str, E2_STR_BUFF_SIZE);
  else
    GetTitle (pFormInfo->termText, str, E2_STR_BUFF_SIZE);
  if (str [0] == '\0') return;

  WatchCursor ();
  switch (pFormInfo->currMode) {
    case TRANSLATE_MODE:
      Query_TranslateAndAddBoolTerm (pFormInfo->form, pFormInfo->currDb,
                     pFormInfo->currField, str, STATE_ON, 0);
      SafeSetTitle (pFormInfo->termText, "");
      if (Visible (pFormInfo->termText))
        Select (pFormInfo->termText);
      pFormInfo->availItem = 0;
      pFormInfo->availRow = 0;
      Reset (pFormInfo->availDoc);
      break;
    case SELECTION_MODE:
      ProcessSelectionTerm (pFormInfo, str);
      /*
      pFormInfo->availCurrentPage = 0;
      */
      break;
    case AUTOMATIC_MODE:
      ProcessMultipleTerms (pFormInfo, str);
      break;
    case WILD_CARD_MODE:
      StringCat (str, "*");
      count = Query_TruncateCount (pFormInfo, str);
      sdp = CreateTerm (pFormInfo->form, pFormInfo->currDb,
                        pFormInfo->currField, str, STATE_ON, count, FALSE);
      if (sdp != NULL) {
        DisplayTerm (pFormInfo, str, sdp->field, count);
      }
      SafeSetTitle (pFormInfo->termText, "");
      if (Visible (pFormInfo->termText))
        Select (pFormInfo->termText);
      pFormInfo->availItem = 0;
      pFormInfo->availRow = 0;
      Reset (pFormInfo->availDoc);
      break;
    case MESH_TREE_MODE:
      ProcessMeshTerm (pFormInfo, str);
      break;
    case TAXONOMY_MODE:
      ProcessTaxonomyTerm (pFormInfo, str);
      break;
    case RANGE_MODE:
      ProcessRangeTerms (pFormInfo);
      break;
    case LOOKUP_ACCN_MODE:
    case LOOKUP_UID_MODE:
    case VIEW_ACCN_MODE:
    case VIEW_UID_MODE:
      ProcessDirectLookup (pFormInfo, str);
      ArrowCursor ();
      Update ();
      return;
    default:
      break;
  }
  Update ();

  RecalculateChosen (pFormInfo);

  ArrowCursor ();
  Update ();
}

/*===================================================================*/
/*                                                                   */
/*  EvaluateRetrieve_Callback () - This is the callback for the      */
/*                                Evaluate/Retrieve button in        */
/*                                advanced query mode.               */
/*                                                                   */
/*===================================================================*/

static void EvaluateRetrieve_Callback (ButtoN evaluateRetrieveButton)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (evaluateRetrieveButton);

  if (pFormInfo->advQueryState == ADV_QUERY_EVALUATE_STATE) {
    WatchCursor ();
    Update ();
    RecalculateChosen (pFormInfo);
    pFormInfo->advQueryState = ADV_QUERY_RETRIEVE_STATE;
    ArrowCursor ();
  } else if (pFormInfo->advQueryState == ADV_QUERY_RETRIEVE_STATE) {
    pFormInfo->retrieveDocsProc (evaluateRetrieveButton);
    pFormInfo->advQueryState = ADV_QUERY_INVALID_STATE;
  }
}

/*===================================================================*/
/*                                                                   */
/*  DatabaseView_Callback () and FieldView_Callback () -             */
/*     Callback functions for items in Help menu.                    */
/*                                                                   */
/*===================================================================*/

static void DatabaseView_Callback (IteM i)

{
  Entrez2DbInfoPtr  e2db;
  Entrez2InfoPtr    e2ip;
  FILE              *fp;
  Int2              len;
  Int2              max_menu = 0;
  Int2              max_name = 0;
  Char              path [PATH_MAX];

  e2ip = Query_GetInfo ();
  if (e2ip == NULL) return;
  TmpNam (path);
  fp = FileOpen (path, "w");
  if (fp == NULL) return;
  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    len = (Int2) StringLen (e2db->db_name);
    max_name = MAX (len, max_name);
    len = (Int2) StringLen (e2db->db_menu);
    max_menu = MAX (len, max_menu);
  }
  max_name += 2;
  max_menu += 2;
  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    len = (Int2) StringLen (e2db->db_name);
    max_name = MAX (len, max_name);
    len = (Int2) StringLen (e2db->db_menu);
    max_menu = MAX (len, max_menu);
  }
  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    len = (Int2) StringLen (e2db->db_name);
    fprintf (fp, "%s", e2db->db_name);
    while (len < max_name) {
      fprintf (fp, " ");
      len++;
    }
    len = (Int2) StringLen (e2db->db_menu);
    fprintf (fp, "%s", e2db->db_menu);
    while (len < max_menu) {
      fprintf (fp, " ");
      len++;
    }
    fprintf (fp, "%s", e2db->db_descr);
    fprintf (fp, "\n");
  }
  FileClose (fp);
  LaunchGeneralTextViewer (path, "Database Summary");
  FileRemove (path);
}

static void FieldView_Callback (IteM i)

{
  Entrez2InfoPtr       e2ip;
  EnumFieldAssocPtr    fieldAlist;
  Entrez2FieldInfoPtr  fieldInfo;
  FILE                 *fp;
  Int2                 j;
  Int2                 len;
  Int2                 max_menu = 0;
  Int2                 max_name = 0;
  Char                 path [PATH_MAX];

  e2ip = Query_GetInfo ();
  if (e2ip == NULL) return;
  fieldAlist = CreateAllFieldsAlist (e2ip);
  if (fieldAlist == NULL) return;
  TmpNam (path);
  fp = FileOpen (path, "w");
  if (fp == NULL) return;
  for (j = 0; fieldAlist [j].name != NULL; j++) {
    fieldInfo = FieldGetInfoFromName (fieldAlist [j].name);
    len = (Int2) StringLen (fieldInfo->field_name);
    max_name = MAX (len, max_name);
    len = (Int2) StringLen (fieldInfo->field_menu);
    max_menu = MAX (len, max_menu);
  }
  max_name += 2;
  max_menu += 2;
  for (j = 0; fieldAlist [j].name != NULL; j++) {
    fieldInfo = FieldGetInfoFromName (fieldAlist [j].name);
    len = (Int2) StringLen (fieldInfo->field_name);
    fprintf (fp, "%s", fieldInfo->field_name);
    while (len < max_name) {
      fprintf (fp, " ");
      len++;
    }
    len = (Int2) StringLen (fieldInfo->field_menu);
    fprintf (fp, "%s", fieldInfo->field_menu);
    while (len < max_menu) {
      fprintf (fp, " ");
      len++;
    }
    fprintf (fp, "%s", fieldInfo->field_descr);
    fprintf (fp, "\n");
  }
  FileClose (fp);
  LaunchGeneralTextViewer (path, "Field Summary");
  FileRemove (path);
  FreeEnumFieldAlist (fieldAlist);
}

static CharPtr modeSummary [] = {
  "Automatic  Terms are processed through PubMed query engine",
  "Lookup     Accession looked up directly into Document window",
  "MeSH Tree  MeSH hierarchy above current level displayed in popup",
  "Multiple   Terms extracted and processed one at a time",
  "Range      From and To values entered as a composite term",
  "Selection  Available terms are displayed in Term Selection box",
  "Taxonomy   Organism hierarchy above current level displayed in popup",
  "View       Accession looked up directly into Viewer window",
  "Wild Card  Term appended with asterisk and processed",
  NULL
};

static void ModeView_Callback (IteM i)

{
  FILE  *fp;
  Int2  j;
  Char  path [PATH_MAX];

  TmpNam (path);
  fp = FileOpen (path, "w");
  if (fp == NULL) return;
  for (j = 0; modeSummary [j] != NULL; j++) {
    fprintf (fp, "%s\n", modeSummary [j]);
  }
  FileClose (fp);
  LaunchGeneralTextViewer (path, "Mode Summary");
  FileRemove (path);
}

/*==================================================================*/
/*                                                                  */
/*  AddPopupItem () - Add an item to a popup list. This is used to  */
/*                   create the lineage popup list in taxonomy      */
/*                   mode.                                          */
/*                                                                  */
/*==================================================================*/

static void AddPopupItem (PopuP p, CharPtr str, Int2 maxwid)

{
  Char     ch;
  Int2     hbounds;
  Int2     i;
  CharPtr  ptr;
  Char     title [256];
  Int2     wid;

  if (p != NULL && str != NULL) {
    StringNCpy_0 (title, str, sizeof (title));
    i = StringLen (title);
    title [i] = '\0';
    ptr = title;
    ch = *ptr;
    while (ch != '\0') {
      if (ch == '/') {
        *ptr = '-';
      }
      ptr++;
      ch = *ptr;
    }
#ifdef WIN_MAC
    hbounds = 13;
#endif
#ifdef WIN_MSWIN
    hbounds = 13;
#endif
#ifdef WIN_MOTIF
    hbounds = 24;
#endif
    maxwid -= StringWidth ("...") + hbounds * 2 + StringWidth (" ");
    wid = 0;
    i = 0;
    ch = title [i];
    while (ch != '\0' && wid <= maxwid) {
      wid += CharWidth (ch);
      i++;
      ch = title [i];
    }
    title [i] = '\0';
    if (ch != '\0' && i <= (Int2) StringLen (str)) {
      StringCat (title, "...");
    }
    PopupItem (p, title);
  }
}

/*==================================================================*/
/*                                                                  */
/*  AvailGetColWidth () - Returns the pixel width of the given      */
/*                       column.                                    */
/*                                                                  */
/*==================================================================*/

static Int2 AvailGetColWidth (Int2 columnNum)

{
  return availColFmt [columnNum].pixWidth;
}

/*==================================================================*/
/*                                                                  */
/*  RepopulateTaxonomy () - Given a term and a database, will       */
/*                         look up the term in the taxonomy tree    */
/*                         and populate the Term Selection window   */
/*                         with the term's 'children' in the tree.  */
/*                                                                  */
/*==================================================================*/

static Boolean RepopulateTaxonomy (FormInfoPtr pFormInfo, CharPtr taxterm)

{
  CharPtr              aLineage [MAX_TAXONOMY_TREE_DEPTH];
  Int2                 delta;
  CharPtr              dbName;
  CharPtr              fieldName;
  Int2                 i;
  Int2                 maxwidth;
  RecT                 r;
  RecT                 s;
  Char                 str [256];
  Int4                 taxId;
  Int2                 wid;
  Entrez2TermPtr       childPtr;
  Entrez2RequestPtr    e2Request;
  Entrez2ReplyPtr      e2Reply;
  Entrez2HierNodePtr   e2TermNode;
  Entrez2TermPtr       currentLineage = NULL;
  Entrez2FieldInfoPtr  fieldInfo;

  /*--------------------------*/
  /* Build an Entrez2 request */
  /*--------------------------*/

  taxId = 0;
  dbName = DBGetNameFromID (pFormInfo->currDb);
  fieldInfo = FieldGetInfo (pFormInfo->currDb, pFormInfo->currField);
  fieldName = fieldInfo->field_name;
  if (StringHasNoText (dbName) || StringHasNoText (fieldName)) return FALSE;

  e2Request = EntrezCreateGetTermHierarchyRequest (dbName, fieldName, taxterm, taxId);

  /*----------------------------------*/
  /* Send off the request and extract */
  /* the resulting reply.             */
  /*----------------------------------*/

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2Request);

  e2Reply = SpecialEntrezSynchronousQuery (e2Request);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2Reply);

  if (e2Reply == NULL) {
    Show (pFormInfo->taxLineagePopup);
    Update ();
    return FALSE;
  }

  e2TermNode = EntrezExtractHierNodeReply (e2Reply);
  if (e2TermNode == NULL) {
    Show (pFormInfo->taxLineagePopup);
    Update ();
    return FALSE;
  }

  Entrez2RequestFree (e2Request);

  /*-------------------------*/
  /* Clear any old terms out */
  /* of the Avail window.    */
  /*-------------------------*/

  SafeSetTitle (pFormInfo->termText, taxterm);
  Hide (pFormInfo->taxLineagePopup);
  Reset (pFormInfo->taxLineagePopup);
  pFormInfo->taxStrings = ValNodeFreeData (pFormInfo->taxStrings);
  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  Reset (pFormInfo->availDoc);
  SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);
  Update ();
  maxwidth = AvailGetColWidth (0);

  /*------------------------------------------------*/
  /* Create a selection list of a term's "lineage"  */
  /*    NOTE: The lineage is provided in the        */
  /*          reverse order from how we want to     */
  /*          add it to the list box, so they must  */
  /*          be put into a random-access structure */
  /*          and then reverse added.               */
  /*------------------------------------------------*/

  for (i = 0; i < e2TermNode->lineage_count; i++) {
    if (i == 0)
      currentLineage = e2TermNode->lineage;
    else
      currentLineage = currentLineage->next;

    if (currentLineage == NULL)
      break;

    aLineage [i] = currentLineage->term;
  }

  for (i = e2TermNode->lineage_count - 1; i >= 0; i--) {
    AddPopupItem (pFormInfo->taxLineagePopup, aLineage [i], maxwidth);
    ValNodeCopyStr (&(pFormInfo->taxStrings), 0, aLineage [i]);
  }

  /*----------------------------------------------*/
  /* Adjust the size of the Pull Down list box to */
  /* accomodate the lineage list just created.    */
  /*----------------------------------------------*/

  SetValue (pFormInfo->taxLineagePopup, e2TermNode->lineage_count);
  ObjectRect (pFormInfo->taxLineagePopup, &r);
  ObjectRect (pFormInfo->availDoc, &s);
  InsetRect (&s, 4, 4);
  wid = r.right - r.left;
  delta = (maxwidth - wid) / 2;
  r.left = s.left + delta;
  r.right = r.left + wid;
  SetPosition (pFormInfo->taxLineagePopup, &r);

  /*---------------------------------*/
  /* Add the children of the current */
  /* term to the Selection Box.      */
  /*---------------------------------*/

  childPtr = e2TermNode->children;

  for (i = 0; i < e2TermNode->child_count; i++) {
    sprintf (str, "%s\t%ld\t%d\n", childPtr->term, (long) (childPtr->count), (int) (childPtr->is_leaf_node ? 1 : 0));
    AppendText (pFormInfo->availDoc, str, &availParFmt, availColFmt, systemFont);
    childPtr = childPtr->next;
  }

  for (i = e2TermNode->child_count; i < 7; i++)
    AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt, systemFont);

  /*-------------------------*/
  /* Redraw the avail window */
  /* with the new terms.     */
  /*-------------------------*/

  InvalDocument (pFormInfo->availDoc);
  Show (pFormInfo->taxLineagePopup);
  Update ();
  AdjustDocScroll (pFormInfo->availDoc);

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  RepopulateTaxonomyRoot () -                                     */
/*                                                                  */
/*==================================================================*/

static void RepopulateTaxonomyRoot (FormInfoPtr pFormInfo)

{
  CharPtr  dbName;

  dbName = DBGetNameFromID (pFormInfo->currDb);

  if (StringICmp (dbName, "PubMed") == 0) {
    RepopulateTaxonomy (pFormInfo, "All MeSH Categories");
  } else {
    RepopulateTaxonomy (pFormInfo, "root");
  }
}

/*===================================================================*/
/*                                                                   */
/*  ChangeTaxParents_Callback () - Called when a 'Parent' term is    */
/*                                selected from the taxonomy lineage */
/*                                popup list.                        */
/*                                                                   */
/*                                Makes the selected term the active */
/*                                one and re-queries for it.         */
/*                                                                   */
/*===================================================================*/

static void ChangeTaxParents_Callback (PopuP p)

{
  FormInfoPtr  pFormInfo;
  Int2         val;
  ValNodePtr   vnp;

  pFormInfo = (FormInfoPtr) GetObjectExtra (p);

  val = GetValue (p);
  if (val <= 0) return;

  vnp = pFormInfo->taxStrings;
  while (val > 1 && vnp != NULL) {
    val--;
    vnp = vnp->next;
  }
  if (vnp != NULL) {
    WatchCursor ();
    RepopulateTaxonomy (pFormInfo, (CharPtr) vnp->data.ptrvalue);
    ArrowCursor ();
  }
}

/*===================================================================*/
/*                                                                   */
/*  TextAction () - Called when a key is type into the "Term:" text  */
/*                  entry box.  Enables/disables the accept button   */
/*                  and scroll to text.                              */
/*                                                                   */
/*===================================================================*/

static void TextAction (TexT t)

{
  Int2         i;
  FormInfoPtr  pFormInfo;
  Char         str [256];

  pFormInfo = (FormInfoPtr) GetObjectExtra (t);

  /* Enable or disable the 'Accept' button based */
  /* on the mode and the current text.           */

  if (pFormInfo->currMode == RANGE_MODE) {
    if (TextHasNoText (pFormInfo->fromText) &&
        TextHasNoText (pFormInfo->toText))
      SafeDisable (pFormInfo->acceptButton);
    else
      SafeEnable (pFormInfo->acceptButton);
  } else {
    GetTitle (pFormInfo->termText, str, sizeof (str));
    if (StringHasNoText (str)) {
      if (pFormInfo->currMode == SELECTION_MODE && str [0] == ' ')
        SafeEnable (pFormInfo->acceptButton);
      else
        SafeDisable (pFormInfo->acceptButton);
    } else
      SafeEnable (pFormInfo->acceptButton);
  }

  /* Get the current text */

  GetTitle (t, str, sizeof (str));

  /* If there is no text then clear */
  /* the 'Term Selection' window.   */

  if (str [0] == '\0') {
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
    Reset (pFormInfo->availDoc);
    for (i = 0; i < 7; i++)
      AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt,
          systemFont);
    InvalDocument (pFormInfo->availDoc);
    pFormInfo->textChanged = FALSE;
    pFormInfo->okayToAccept = FALSE;
    pFormInfo->availCurrentPage = 0;
  }

  /* If there is text, then attempt to scroll */
  /* to it in the 'Term Selection' window.    */

  else {
    pFormInfo->textChanged = TRUE;
    pFormInfo->okayToAccept = FALSE;
    if (pFormInfo->availCurrentPage > 0 &&
    pFormInfo->currMode == SELECTION_MODE)
      ScrollToText (pFormInfo, str, pFormInfo->availCurrentPage, TRUE, TRUE);
    Update ();
  }
}

/*==================================================================*/
/*                                                                   */
/*  ToTextAction () -                                                */
/*                                                                   */
/*===================================================================*/

static void ToTextAction (TexT t)

{
  Int2         i;
  FormInfoPtr  pFormInfo;
  Char         str [256];

  pFormInfo = (FormInfoPtr) GetObjectExtra (t);

  pFormInfo->currRangeField = E2_RANGE_TO;

  if (TextHasNoText (pFormInfo->fromText) &&
      TextHasNoText (pFormInfo->toText))
    SafeDisable (pFormInfo->acceptButton);
  else
    SafeEnable (pFormInfo->acceptButton);
  
  GetTitle (t, str, sizeof (str));
  if (str [0] == '\0') {
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
    Reset (pFormInfo->availDoc);
    for (i = 0; i < 7; i++)
      AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt, systemFont);
    InvalDocument (pFormInfo->availDoc);
    pFormInfo->isToTextChanged = FALSE;
    pFormInfo->isValidTo = FALSE;
    pFormInfo->availCurrentPage = 0;
  } else {
    pFormInfo->isToTextChanged = TRUE;
    pFormInfo->isValidTo = TRUE;
    if (pFormInfo->availCurrentPage > 0)
      ScrollToText (pFormInfo, str, pFormInfo->availCurrentPage, TRUE, TRUE);
    Update ();
  }
}

/*==================================================================*/
/*                                                                   */
/*  FromTextAction () -                                              */
/*                                                                   */
/*===================================================================*/

static void FromTextAction (TexT t)

{
  Int2         i;
  FormInfoPtr  pFormInfo;
  Char         str [256];

  pFormInfo = (FormInfoPtr) GetObjectExtra (t);

  pFormInfo->currRangeField = E2_RANGE_FROM;

  if (TextHasNoText (pFormInfo->fromText) &&
      TextHasNoText (pFormInfo->toText))
    SafeDisable (pFormInfo->acceptButton);
  else
    SafeEnable (pFormInfo->acceptButton);

  GetTitle (t, str, sizeof (str));
  if (str [0] == '\0') {
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
    Reset (pFormInfo->availDoc);
    for (i = 0; i < 7; i++)
      AppendText (pFormInfo->availDoc, " \n", &availParFmt,
                  availColFmt, systemFont);
    InvalDocument (pFormInfo->availDoc);
    pFormInfo->isFromTextChanged = FALSE;
    pFormInfo->isValidFrom = FALSE;
    pFormInfo->availCurrentPage = 0;
  } else {
    pFormInfo->isFromTextChanged = TRUE;
    pFormInfo->isValidFrom = TRUE;
    if (pFormInfo->availCurrentPage > 0)
      ScrollToText (pFormInfo, str, pFormInfo->availCurrentPage, TRUE, TRUE);
    Update ();
  }
}

/*==================================================================*/
/*                                                                  */
/*  ChangeMode () -                                                 */
/*                                                                  */
/*==================================================================*/

static void ChangeMode (PopuP p)

{
  AlistDialogPtr     adp;
  Char               ch;
  Int2               i;
  EnumFieldAssocPtr  modeSet;
  ModeChoice         menuMode;
  ModeChoice         selectedMode;
  CharPtr            ptr, text;
  FormInfoPtr        pFormInfo;


  /*------------------------------------------*/
  /* Convert the menu choice into a mode type */
  /*------------------------------------------*/

  adp = (AlistDialogPtr) GetObjectExtra (p);
  pFormInfo = adp->userdata;
  modeSet = adp->alist;
  menuMode = (ModeChoice) (GetValue (p) - 1);
  selectedMode = (ModeChoice) modeSet [menuMode].value;

  pFormInfo->currMode = selectedMode;

  /* handle special modes, populate termlist */
  if (selectedMode == TAXONOMY_MODE || selectedMode == MESH_TREE_MODE) {
    SafeHide (pFormInfo->termGroup);
    SafeHide (pFormInfo->rangeGroup);
    text = SaveStringFromText (pFormInfo->termText);
    WatchCursor ();
    Update ();
    if (text != NULL) {
      ptr = text;
      ch = *ptr;
      while (ch != '\0' && ch >= ' ') {
        ptr++;
        ch = *ptr;
      }
      *ptr = '\0';
      if ((*text == '\0') || (! RepopulateTaxonomy (pFormInfo, text))) {
        RepopulateTaxonomyRoot (pFormInfo);
      }
    } else {
      RepopulateTaxonomyRoot (pFormInfo);
    }
    ArrowCursor ();
    MemFree (text);
    SafeShow (pFormInfo->taxLineagePopup);
    pFormInfo->textChanged = FALSE;
    pFormInfo->okayToAccept = TRUE;
  } else if (selectedMode == RANGE_MODE) {
    SafeHide (pFormInfo->termGroup);
    SafeHide (pFormInfo->taxLineagePopup);
    SafeShow (pFormInfo->rangeGroup);
    TextAction (pFormInfo->fromText);
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
    Reset (pFormInfo->availDoc);
    SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);
    for (i = 0; i < 7; i++) {
      AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt, systemFont);
    }
    InvalDocument (pFormInfo->availDoc);
    Select (pFormInfo->fromText);
  } else {
    SafeHide (pFormInfo->rangeGroup);
    SafeHide (pFormInfo->taxLineagePopup);
    SafeShow (pFormInfo->termGroup);
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
    pFormInfo->availCurrentPage = 0;
    TextAction (pFormInfo->termText);
    Reset (pFormInfo->availDoc);
    SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);
    for (i = 0; i < 7; i++) {
      AppendText (pFormInfo->availDoc, " \n", &availParFmt, availColFmt, systemFont);
    }
    InvalDocument (pFormInfo->availDoc);
    Select (pFormInfo->termText);
  }

  Update ();
}

/*==================================================================*/
/*                                                                  */
/*  ChangeMode_Callback () -                                        */
/*                                                                  */
/*==================================================================*/

static void ChangeMode_Callback (PopuP p)

{
  ChangeMode (p);
}

/*==================================================================*/
/*                                                                  */
/*  ChangeField () - Sets the current field to the one selected in  */
/*                  the given PopuP object, then selects the        */
/*                  corresponding modes PopuP that contains the     */
/*                  possible modes for this field.                  */
/*                                                                  */
/*==================================================================*/

static void ChangeField (PopuP p)

{
  AlistDialogPtr     adp;
  EnumFieldAssocPtr  fieldAList;
  Int2               fieldId;
  ModeIndex          md, mode;
  Int2               menuChoice;
  FormInfoPtr        pFormInfo;

  /*------------------------*/
  /* Get the selected field */
  /*------------------------*/

  adp = (AlistDialogPtr) GetObjectExtra (p);
  pFormInfo = adp->userdata;
  fieldAList = adp->alist;

  menuChoice = GetValue (p) - 1;
  if (menuChoice < 0) return;

  fieldId = fieldAList [menuChoice].value;

  /*--------------------------------*/
  /* Select and make active the set */
  /* of modes for this field.       */
  /*--------------------------------*/

  mode = (ModeIndex) FieldGetModePopup (pFormInfo->currDb, fieldId, fieldAList, pFormInfo);
  if (mode < 0) return;

  for (md = POPUP_MULT; md <= POPUP_UID; md++)
    if (md != mode)
      SafeHide (pFormInfo->modesPopup [md]);

  SafeShow (pFormInfo->modesPopup [mode]);
  ChangeMode (pFormInfo->modesPopup [mode]);
}

/*==================================================================*/
/*                                                                  */
/*  ChangeField_Callback () -                                       */
/*                                                                  */
/*==================================================================*/

static void ChangeField_Callback (PopuP p)

{
  ChangeField (p);
}

/*==================================================================*/
/*                                                                  */
/*  ChangeDatabase () -                                             */
/*                                                                  */
/*==================================================================*/

static void ChangeDatabase (PopuP p)

{
  FormInfoPtr     pFormInfo;
  Int2            db;
  Int2            dbase;
  UIEnum          val;
  Entrez2InfoPtr  e2ip;

  pFormInfo = (FormInfoPtr) GetObjectExtra (p);
  if (pFormInfo == NULL || pFormInfo->fieldsPopup == NULL) return;

  e2ip = Query_GetInfo ();

  val = GetValue (p) - 1;

  for (db = 0; db < e2ip->db_count; db++) {
    if (db != (Int2) val) {
      SafeHide (pFormInfo->fieldsPopup [db]);
    }
  }

  dbase = (Int2) val;
  pFormInfo->currDb = dbase;
  SafeShow (pFormInfo->fieldsPopup [dbase]);
  ChangeField (pFormInfo->fieldsPopup [dbase]);

  DoResetAvail (pFormInfo, FALSE);
  ResetChosen (pFormInfo);

  if (pFormInfo->currMode == TAXONOMY_MODE || pFormInfo->currMode == MESH_TREE_MODE) {
    SafeHide (pFormInfo->termGroup);
    SafeHide (pFormInfo->rangeGroup);
    SafeShow (pFormInfo->taxLineagePopup);
  } else if (pFormInfo->currMode == RANGE_MODE) {
    SafeHide (pFormInfo->termGroup);
    SafeHide (pFormInfo->taxLineagePopup);
    SafeShow (pFormInfo->rangeGroup);
    Select (pFormInfo->fromText);
  } else {
    SafeHide (pFormInfo->rangeGroup);
    SafeHide (pFormInfo->taxLineagePopup);
    SafeShow (pFormInfo->termGroup);
    Select (pFormInfo->termText);
  }

  Reset (pFormInfo->advQueryText);
}

/*==================================================================*/
/*                                                                  */
/*  ChangeDatabase_Callback () -                                    */
/*                                                                  */
/*==================================================================*/

static void ChangeDatabase_Callback (PopuP p)

{
  ChangeDatabase (p);
}

/*==================================================================*/
/*                                                                  */
/*  EditMessage_Callback () -                                       */
/*                                                                  */
/*==================================================================*/

static void EditMessage_Callback (ForM f, Int2 mssg)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  switch (mssg) {
    case VIB_MSG_CUT:
      StdCutTextProc (NULL);
      break;
    case VIB_MSG_COPY:
      StdCopyTextProc (NULL);
      break;
    case VIB_MSG_PASTE:
      StdPasteTextProc (NULL);
      break;
    case VIB_MSG_DELETE:
      StdDeleteTextProc (NULL);
      break;
    default:
      if (pFormInfo->appmessage != NULL) {
        pFormInfo->appmessage (f, mssg);
      }
      break;
  }
}

/*==================================================================*/
/*                                                                  */
/*  TermListActivate_Callback () -                                  */
/*                                                                  */
/*==================================================================*/

static void TermListActivate_Callback (WindoW w)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (w);

  if (pFormInfo->activate != NULL)
    pFormInfo->activate (w);
}

/*==================================================================*/
/*                                                                  */
/*  TermListCleanup_Callback () -                                   */
/*                                                                  */
/*==================================================================*/

static void TermListCleanup_Callback (GraphiC g, VoidPtr data)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (g);

  MemFree (pFormInfo->fieldsPopup);
  StdCleanupFormProc (g, data);
}

/*==================================================================*/
/*                                                                  */
/*  Quit_Callback () -                                              */
/*                                                                  */
/*==================================================================*/

static void Quit_Callback (IteM i)

{
  QuitProgram ();
}

/*==================================================================*/
/*                                                                  */
/*  StripNewLine () - Strip the newline character from the end of   */
/*                   a string.                                      */
/*                                                                  */
/*==================================================================*/

static void StripNewLine (CharPtr str)

{
  CharPtr  chptr;

  if (StringHasNoText (str))
    return;
  chptr = StringRChr (str, '\n');
  if (chptr != NULL) {
    *chptr = '\0';
  }
  chptr = StringRChr (str, '\r');
  if (chptr != NULL) {
    *chptr = '\0';
  }
}

/*==================================================================*/
/*                                                                  */
/*  Query_AddUidsTerm () -                                          */
/*                                                                  */
/*==================================================================*/

static Boolean Query_AddUidsTerm (ForM f, CharPtr uidListName, Int4 iUidCount, Int4Ptr uids, Int2 db)

{
  CharPtr            dbName;
  Entrez2RequestPtr  e2rq;
  Entrez2ReplyPtr    e2ry;
  StateDataPtr       sdp;
  StateDataPtr       prev;
  Char               displayStr [256];
  RecT               r;
  BaR                sb;
  FormInfoPtr        pFormInfo;
  Boolean            doNotTranslate;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*-----------------------------------------*/
  /* Make sure we don't add a Uid list twice */
  /*-----------------------------------------*/

  for (sdp = pFormInfo->termList; sdp != NULL; sdp = sdp->next)
    if (MeshStringICmp (sdp->term, uidListName) == 0) return FALSE;

  /*--------------------------------*/
  /* Create a term for the UID list */
  /*--------------------------------*/

  sdp = StateDataNew (pFormInfo);
  if (pFormInfo->termList == NULL)
    pFormInfo->termList = sdp;

  if (sdp == NULL) return FALSE;

  (pFormInfo->chosenNumLines)++;

  sdp->field = StringSave ("----");
  /* sdp->term = StringSave (uidListName); */
  sdp->count = iUidCount;
  /* sdp->uids = uids; */
  sdp->group = GROUP_SINGLE;
  sdp->above = ENTREZ_OP_NONE;

  dbName = DBGetNameFromID (pFormInfo->currDb);
  e2rq = EntrezCreateBooleanRequest (FALSE, FALSE, dbName, NULL, 0, 0, NULL, 0, 0);
  if (e2rq == NULL) return FALSE;

  doNotTranslate = FALSE;
  EntrezAddToBooleanRequest (e2rq, NULL, 0, NULL, NULL, NULL, 0, iUidCount, uids, NULL, FALSE, doNotTranslate);
  EntrezSetUseHistoryFlag (e2rq);

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2rq);

  e2ry = SpecialEntrezSynchronousQuery (e2rq);
  if (e2ry == NULL) return FALSE;

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ry);

  e2rq = Entrez2RequestFree (e2rq);
  sdp->key = StringSave (e2ry->key);
  sdp->term = StringSave (e2ry->key);
  Entrez2ReplyFree (e2ry);

  /*-------------------------------------*/
  /* Add the term to the current Query's */
  /* linked list of terms.               */
  /*-------------------------------------*/

  prev = sdp->prev;
  if (prev != NULL) {
    sdp->above = ENTREZ_OP_AND;
    prev->below = ENTREZ_OP_AND;
  }
  sdp->below = ENTREZ_OP_NONE;
  sdp->state = STATE_ON;

  /*---------------------------------------------*/
  /* Add the term to the query refinement window */
  /*---------------------------------------------*/

  sprintf (displayStr, "%s\t [%s]\t%ld\n", sdp->term, sdp->field, (long) iUidCount);
  AppendText (pFormInfo->chosenDoc, displayStr, &chosenParFmt, chosenColFmt, systemFont);

  /*---------------------------------*/
  /* Display our new query string in */
  /* Query Refinement window.        */
  /*---------------------------------*/

  InvalDocRows (pFormInfo->chosenDoc, pFormInfo->chosenNumLines, 1, 1);
  AdjustDocScroll (pFormInfo->chosenDoc);
  sb = GetSlateVScrollBar ((SlatE) pFormInfo->chosenDoc);
  ResetClip ();
  SetBarValue (sb, MAX (pFormInfo->chosenNumLines - 7, 0));
  ObjectRect (pFormInfo->chosenDoc, &r);
  InsetRect (&r, 4, 4);
  r.right = r.left + 16;
  InsetRect (&r, -1, -1);
  Select (pFormInfo->chosenDoc);
  InvalRect (&r);

  Update ();

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  ImportUIDs_Callback () -                                        */
/*                                                                  */
/*==================================================================*/

static void ImportUIDs_Callback (IteM i)

{
  Entrez2InfoPtr    e2ip;
  Entrez2DbInfoPtr  e2dbInfo;
  FILE              *fp;
  FormInfoPtr       pFormInfo;
  ByteStorePtr      bsp;
  Int4              iUidCount;
  Char              fullName [PATH_MAX];
  CharPtr           baseName;
  Char              str [32];
  Int4              uid;
  Int4Ptr           uids = NULL;
  WindoW            w;

  /*----------------------------*/
  /* Get the Term Form info for */
  /* the parent window.         */
  /*----------------------------*/

#ifdef WIN_MAC
  w = FrontWindow ();
#else
  w = ParentWindow (i);
#endif

  pFormInfo = (FormInfoPtr) GetObjectExtra (w);

  /*------------------------------*/
  /* Get the name of the UID file */
  /* and then open it.            */
  /*------------------------------*/

  if (! GetInputFileName (fullName, sizeof (fullName), "", "TEXT")) return;

  fp = FileOpen (fullName, "r");
  if (fp == NULL) return;

  /*-------------------------------*/
  /* Create a ByteStore Pointer to */
  /* store the UIDs in.            */
  /*-------------------------------*/

  bsp = BSNew (128);
  if (bsp == NULL) {
    FileClose (fp);
    return;
  }

  /*-------------------------------------*/
  /* Read the first line of the file and */
  /* verify that it contains a valid     */
  /* database name.                      */
  /*                                     */
  /* NOTE - Special case for medline     */
  /*        needs to me removed once     */
  /*        it is fixed on the server.   */
  /*                                     */
  /*-------------------------------------*/

  if (!FileGets (str, sizeof (str), fp)) {
    BSFree (bsp);
    FileClose (fp);
    return;
  }

  StripNewLine (str);

  e2ip = Query_GetInfo ();

  for (e2dbInfo = e2ip->db_info; e2dbInfo != NULL; e2dbInfo = e2dbInfo->next)
    if (StringICmp (e2dbInfo->db_menu, &str [1]) == 0) break;

  if (e2dbInfo == NULL) {
    Message (MSG_POSTERR, "First line must be in the form >DbName");
    BSFree (bsp);
    FileClose (fp);
    return;
  }

  /*-----------------------------------*/
  /* Read the rest of the lines in and */
  /* add each UID to the byteStore.    */
  /*-----------------------------------*/

  while (FileGets (str, sizeof (str), fp)) {
    StripNewLine (str);
    if (str [0] != '\0' && StrToLong (str, &uid))
      BSWrite (bsp, &uid, sizeof (DocUid));
  }

  BSSeek (bsp, 0L, 0);
  iUidCount = (Int4) ((BSLen (bsp)) / sizeof (Int4));
  uids = (Int4Ptr) BSMerge (bsp, NULL);

  /*--------------------------------*/
  /* Add the list of UIDs as a term */
  /* in the current query.          */
  /*--------------------------------*/

  baseName = FileNameFind (fullName);
  Query_AddUidsTerm (pFormInfo->form, baseName, iUidCount, uids, pFormInfo->currDb);
  Select (pFormInfo->termText);

  /*---------------------------*/
  /* Get a count of terms that */
  /* satisfy this query.       */
  /*---------------------------*/

  Update ();
  RecalculateChosen (pFormInfo);
  ArrowCursor ();
  Update ();

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  BSFree (bsp);
  MemFree (uids);
  FileClose (fp);
}

/*==================================================================*/
/*                                                                  */
/*  ExportUIDs_Callback () -                                        */
/*                                                                  */
/*==================================================================*/

static void ExportUIDs_Callback (IteM i)

{
  CharPtr                 dbName;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  Entrez2IdListPtr        e2UidList;
  FILE                    *fp;
  FormInfoPtr             pFormInfo;
  Char                    path [PATH_MAX];
  Char                    headerStr [32];
  Int4Ptr                 uids;
  Int4                    uidNum;
  Char                    uidStr [32];
  WindoW                  w;

  /*----------------------------*/
  /* Get the Term Form info for */
  /* the parent window.         */
  /*----------------------------*/

#ifdef WIN_MAC
  w = FrontWindow ();
#else
  w = ParentWindow (i);
#endif

  pFormInfo = (FormInfoPtr) GetObjectExtra (w);

  /*-------------------------------*/
  /* Get a file name from the user */
  /* and open that file.           */
  /*-------------------------------*/

  if (! GetOutputFileName (path, sizeof (path), NULL)) return;

#ifdef WIN_MAC
  FileCreate (path, "TEXT", "ttxt");
#endif

  fp = FileOpen (path, "w");
  if (fp == NULL) return;

  WatchCursor ();

  /*-----------------------------------*/
  /* Write the header line to the file */
  /*-----------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  sprintf (headerStr, ">%s\n", dbName);
  FilePuts (headerStr, fp);

  /*-----------------------*/
  /* Create a list of UIDS */
  /*-----------------------*/

  e2BooleanPtr = Query_FetchUIDs (pFormInfo->form);
  e2UidList = e2BooleanPtr->uids;

  uids = (Int4Ptr) BSMerge (e2UidList->uids, NULL);

  /*----------------------------*/
  /* Write the uids to the file */
  /*----------------------------*/

  for (uidNum = 0; uidNum < e2UidList->num; uidNum++) {
    sprintf (uidStr, "%ld\n", (long) uids [uidNum]);
    FilePuts (uidStr, fp);
  }

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  FileClose (fp);
  free (uids);
  ArrowCursor ();
  Entrez2BooleanReplyFree (e2BooleanPtr);
}

/*==================================================================*/
/*                                                                  */
/*  AdvQueryInit () - Initialize the lexical static variables for   */
/*                     parsing a new string.                        */
/*                                                                  */
/*==================================================================*/

static Int2 AdvQueryInit (FormInfoPtr pFormInfo, CharPtr newStr)

{
  /*-----------------*/
  /* Check parameter */
  /*-----------------*/

  if (newStr == NULL) return -1;

  /*-----------------------------------------*/
  /* Initialize the lexical static variables */
  /* using the new string.                   */
  /*-----------------------------------------*/

  MemFree (pFormInfo->advQueryLexStr);
  pFormInfo->advQueryLexStr = StringSave (newStr);
  pFormInfo->advQueryLexPos = 0;
  pFormInfo->advQueryLexState = LEXSTATE_IDLE;

  /*-------------------------------------*/
  /* Free the linked list of query terms */
  /* so that we can build a new one with */
  /* the given string.                   */
  /*-------------------------------------*/

  ResetChosen (pFormInfo);

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return 0;
}

/*==================================================================*/
/*                                                                  */
/*  LexClassifyChar () -                                            */
/*                                                                  */
/*     NOTE -- Taken from LexClassifyChar() in accutils.c           */
/*                                                                  */
/*==================================================================*/

static Int2 LexClassifyChar (Char c)

{
  Int2  retval;

  switch (c) {
    case '(':
      retval = LEXCHAR_LPAREN;
      break;
    case ')':
      retval = LEXCHAR_RPAREN;
      break;
    case '[':
      retval = LEXCHAR_LBRACKET;
      break;
    case ']':
      retval = LEXCHAR_RBRACKET;
      break;
    case '"':
      retval = LEXCHAR_QUOTE;
      break;
    case '&':
      retval = LEXCHAR_AND;
      break;
    case '|':
      retval = LEXCHAR_OR;
      break;
    case '-':
      retval = LEXCHAR_NOT;
      break;
    case ',':
      retval = LEXCHAR_COMMA;
      break;
    case '@':
      retval = LEXCHAR_ATSIGN;
      break;
    case '\\':
      retval = LEXCHAR_BACKSLASH;
      break;
    case ' ':
    case '\t':
      retval = LEXCHAR_WHITESPACE;
      break;
    case ';':
      retval = LEXCHAR_SEMICOLON;
      break;
    case ':':
      retval = LEXCHAR_COLON;
      break;
    case '\0':
      retval = LEXCHAR_NULL;
      break;
    case '\r':
    case '\n':
      retval = LEXCHAR_EOL;
      break;
    default:
      retval = LEXCHAR_OTHER;
      break;
  }

  return retval;
}

/*==================================================================*/
/*                                                                  */
/*  AdvQueryGetNextToken() -                                        */
/*                                                                  */
/*        Returns:                                                  */
/*                                                                  */
/*            StartPos - >0 : The position in the static string     */
/*                            pFormInfo->advQueryLexStr where the   */
/*                            token starts.                         */
/*                                                                  */
/*                       -1 : No token found.                       */
/*                                                                  */
/*        Sets:                                                     */
/*                                                                  */
/*            pFormInfo->advQueryNextToken : Contains an            */
/*                            identifier specifying the type of     */
/*                            the token, and if the token is a      */
/*                            string also contains a pointer to     */
/*                            the string.                           */
/*                                                                  */
/*                            Set to NULL if no token found.        */
/*                                                                  */
/*==================================================================*/

static Int2 AdvQueryGetNextToken (FormInfoPtr pFormInfo)

{
  Int2            startPos;
  Int2            classChar;
  Int2            token = 0;
  Boolean         done;
  Char            c;
  CharPtr         lexToken = NULL;
  CharPtr         lexTokenStart;
  Int2            len;
  Int2            pos;

  /*------------------*/
  /* Check conditions */
  /*------------------*/

  if (pFormInfo->advQueryLexStr == NULL) {
    pFormInfo->advQueryNextToken = NULL;
    return -1;
  }

  pFormInfo->advQueryNextToken = &pFormInfo->advQueryNextRealNode;

  /*--------------------------------------*/
  /* Find the beginning of the next token */
  /* and malloc space for it.             */
  /*--------------------------------------*/

  len = StringLen (pFormInfo->advQueryLexStr);
  startPos = pFormInfo->advQueryLexPos;

  if (pFormInfo->advQueryLexPos >= len) {
    pFormInfo->advQueryLexState = LEXSTATE_ERROR;
    token = -1;
    lexToken = MemNew (1);
  } else {
    pos = pFormInfo->advQueryLexPos;
    lexToken = MemNew (StringLen (&pFormInfo->advQueryLexStr [pos]) + 1);
  }

  lexTokenStart = lexToken;

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

  for (done = FALSE; !done && pFormInfo->advQueryLexPos <= len; pFormInfo->advQueryLexPos++) {
    c = pFormInfo->advQueryLexStr [pFormInfo->advQueryLexPos];
    classChar = LexClassifyChar (c);
    switch (pFormInfo->advQueryLexState) {
      case LEXSTATE_IDLE:
        switch (classChar) {
          case LEXCHAR_LPAREN:
            token = LEXTOK_LPAREN;
            done = TRUE;
            break;
          case LEXCHAR_RPAREN:
            token = LEXTOK_RPAREN;
            done = TRUE;
            break;
          case LEXCHAR_LBRACKET:
            token = LEXTOK_LBRACKET;
            done = TRUE;
            break;
          case LEXCHAR_RBRACKET:
            token = LEXTOK_RBRACKET;
            done = TRUE;
            break;
          case LEXCHAR_AND:
            token = LEXTOK_AND;
            done = TRUE;
            break;
          case LEXCHAR_OR:
            token = LEXTOK_OR;
            done = TRUE;
            break;
          case LEXCHAR_NOT:
            token = LEXTOK_NOT;
            done = TRUE;
            break;
          case LEXCHAR_COMMA:
            token = LEXTOK_COMMA;
            done = TRUE;
            break;
          case LEXCHAR_ATSIGN:
            token = LEXTOK_ATSIGN;
            done = TRUE;
            break;
          case LEXCHAR_COLON:
            token = LEXTOK_RANGE;
            done = TRUE;
            break;
          case LEXCHAR_QUOTE:
            pFormInfo->advQueryLexState = LEXSTATE_INQUOTE;
            break;
          case LEXCHAR_BACKSLASH:
            pFormInfo->advQueryLexState = LEXSTATE_BACKSLASHED;
            break;
          case LEXCHAR_EOL:
          case LEXCHAR_WHITESPACE:
            startPos = pFormInfo->advQueryLexPos + 1;
            break;
          case LEXCHAR_SEMICOLON:
          case LEXCHAR_NULL:
            pFormInfo->advQueryLexState = LEXSTATE_ERROR;
            done = TRUE;
            break;
          case LEXCHAR_OTHER:
          default:
            pFormInfo->advQueryLexState = LEXSTATE_INSTRING;
            *lexToken++ = c;
            break;
        }
        break;
      case LEXSTATE_BACKSLASHED:
        switch (classChar) {
          case LEXCHAR_NULL:
          case LEXCHAR_EOL:
            *lexToken++ = '\0';
            done = TRUE;
            pFormInfo->advQueryLexState = LEXSTATE_IDLE;
            break;
          case LEXCHAR_LPAREN:
          case LEXCHAR_RPAREN:
          case LEXCHAR_LBRACKET:
          case LEXCHAR_RBRACKET:
          case LEXCHAR_QUOTE:
          case LEXCHAR_AND:
          case LEXCHAR_OR:
          case LEXCHAR_NOT:
          case LEXCHAR_COMMA:
          case LEXCHAR_ATSIGN:
          case LEXCHAR_BACKSLASH:
          case LEXCHAR_WHITESPACE:
          case LEXCHAR_SEMICOLON:
          case LEXCHAR_COLON:
          case LEXCHAR_OTHER:
          default:
            pFormInfo->advQueryLexState = LEXSTATE_INSTRING;
            *lexToken++ = c;
            break;
        }
        break;
      case LEXSTATE_INQUOTE:
        switch (classChar) {
          case LEXCHAR_QUOTE:
            token = LEXTOK_STRING;
            *lexToken++ = '\0';
            done = TRUE;
            pFormInfo->advQueryLexState = LEXSTATE_IDLE;
            break;
          case LEXCHAR_BACKSLASH:
            pFormInfo->advQueryLexState = LEXSTATE_INQUOTE_AFTERBSLASH;
            break;
          case LEXCHAR_NULL:
          case LEXCHAR_EOL:
            pFormInfo->advQueryLexState = LEXSTATE_ERROR;
            done = TRUE;
            break;
          default:
            *lexToken++ = c;
            break;
        }
        break;
      case LEXSTATE_INQUOTE_AFTERBSLASH:
        switch (classChar) {
          case LEXCHAR_NULL:
          case LEXCHAR_EOL:
            pFormInfo->advQueryLexState = LEXSTATE_ERROR;
            done = TRUE;
            break;
          default:
            pFormInfo->advQueryLexState = LEXSTATE_INQUOTE;
            *lexToken++ = c;
            break;
        }
        break;
      case LEXSTATE_INSTRING:
        switch (classChar) {
          case LEXCHAR_WHITESPACE:
          case LEXCHAR_SEMICOLON:
          case LEXCHAR_NULL:
          case LEXCHAR_EOL:
            token = LEXTOK_STRING;
            *lexToken++ = '\0';
            done = TRUE;
            pFormInfo->advQueryLexState = LEXSTATE_IDLE;
            break;
          case LEXCHAR_BACKSLASH:
            pFormInfo->advQueryLexState = LEXSTATE_BACKSLASHED;
            break;
          case LEXCHAR_QUOTE:
            pFormInfo->advQueryLexState = LEXSTATE_INQUOTE;
            break;
          case LEXCHAR_OTHER:
            *lexToken++ = c;
            break;
          default:
            token = LEXTOK_STRING;
            *lexToken++ = '\0';
            done = TRUE;
            pFormInfo->advQueryLexState = LEXSTATE_IDLE;
            pFormInfo->advQueryLexPos--;        /* push back last character */
            break;
        }
        break;
      case LEXSTATE_ERROR:
        done = TRUE;
        break;
    }
  }

  pFormInfo->advQueryNextToken->choice = (Uint1) token;
  pFormInfo->advQueryNextToken->data.ptrvalue = NULL;

  if (token == LEXTOK_STRING)
    pFormInfo->advQueryNextToken->data.ptrvalue = lexTokenStart;
  else
    MemFree (lexTokenStart);

  if (pFormInfo->advQueryLexState == LEXSTATE_ERROR) {
    pFormInfo->advQueryNextToken = NULL;
    return -1;
  } else
    return startPos;
}

/*===================================================================*/
/*                                                                   */
/*  CloseGroup () -                                                  */
/*                                                                   */
/*===================================================================*/

static Boolean CloseGroup (FormInfoPtr pFormInfo)

{
  StateDataPtr  pLastTerm;

  /*--------------*/
  /* Sanity check */
  /*--------------*/

  if (pFormInfo == NULL || pFormInfo->termList == NULL) return FALSE;

  /*--------------------------------*/
  /* Find the last term in the list */
  /*--------------------------------*/

  pLastTerm = pFormInfo->termList;
  while (pLastTerm->next != NULL)
    pLastTerm = pLastTerm->next;

  /*----------------------*/
  /* Set its group status */
  /*----------------------*/

  switch (pLastTerm->group) {
    case GROUP_SINGLE:
    case GROUP_LAST:
      break;
    case GROUP_FIRST:
      pLastTerm->group = GROUP_SINGLE;
      break;
    case GROUP_MIDDLE:
      pLastTerm->group = GROUP_LAST;
      break;
    default:
      break;
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return TRUE;
}

/*===================================================================*/
/*                                                                   */
/*  ParseANDedTerms () -                                             */
/*                                                                   */
/*     NOTE -- Brought over from accutils.c                          */
/*                                                                   */
/*===================================================================*/

/* prototype for recursive function */

static Boolean ParseTermList (FormInfoPtr pFormInfo, Int2 db, Int2 fld, Int2 currOp);

static Boolean ParseANDedTerms (FormInfoPtr pFormInfo, Int2 db, Int2 fld, Int2 currOp)

{
  if (! ParseTermList (pFormInfo, db, fld, currOp)) return FALSE;

  while (pFormInfo->advQueryNextToken != NULL && pFormInfo->advQueryNextToken->choice == LEXTOK_AND) {
    AdvQueryGetNextToken (pFormInfo);
    if (! ParseTermList (pFormInfo, db, fld, LEXCHAR_AND)) return FALSE;
  }

  return TRUE;
}

/*===================================================================*/
/*                                                                   */
/*  ParseORedTerms () -                                              */
/*                                                                   */
/*     NOTE -- Brought over from accutils.c                          */
/*                                                                   */
/*===================================================================*/

static Boolean ParseORedTerms (FormInfoPtr pFormInfo, Int2 db, Int2 fld, Int2 currOp)

{
  if (! ParseANDedTerms (pFormInfo, db, fld, currOp)) return FALSE;

  while (pFormInfo->advQueryNextToken != NULL && pFormInfo->advQueryNextToken->choice == LEXTOK_OR) {
    AdvQueryGetNextToken (pFormInfo);
    if (! ParseANDedTerms (pFormInfo, db, fld, LEXCHAR_OR)) return FALSE;
  }

  return TRUE;
}

/*===================================================================*/
/*                                                                   */
/*  ParseNOTedTerms () -                                             */
/*                                                                   */
/*     NOTE -- This function brought over from accutils.c            */
/*                                                                   */
/*===================================================================*/

static Boolean ParseNOTedTerms (FormInfoPtr pFormInfo, Int2 db, Int2 fld, Int2 currOp)

{
  if (! ParseORedTerms (pFormInfo, db, fld, currOp)) return FALSE;

  while ((pFormInfo->advQueryNextToken != NULL) && (pFormInfo->advQueryNextToken->choice == LEXTOK_NOT)) {
    AdvQueryGetNextToken (pFormInfo);
    if (! ParseORedTerms (pFormInfo, db, fld, LEXCHAR_NOT)) return FALSE;
  }

  return TRUE;
}

/*===================================================================*/
/*                                                                   */
/*  ParseTermList () -                                               */
/*                                                                   */
/*===================================================================*/

static Boolean ParseTermList (FormInfoPtr pFormInfo, Int2 db, Int2 fld, Int2 currOp)

{
  CharPtr       term;
  CharPtr       fldStr;
  CharPtr       highRangeStr = NULL;
  StateDataPtr  newTermPtr;
  Int2          nOperator;

  /*---------------------------------------------*/
  /* Check for proper conditions to run function */
  /*---------------------------------------------*/

  if (pFormInfo->advQueryNextToken == NULL) {
    ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "Null factor");
    return FALSE;
  }

  /*------------------------------------------*/
  /* If we have a left paren, then recursivly */
  /* parse what follows it as a term list.    */
  /*------------------------------------------*/

  if (pFormInfo->advQueryNextToken->choice == LEXTOK_LPAREN) {
    pFormInfo->advQueryNewGroup = TRUE;
    AdvQueryGetNextToken (pFormInfo);
    if (! ParseNOTedTerms (pFormInfo, db, fld, currOp))
      return FALSE;

      /*---------------------------*/
    /* If we have a right paren, */
    /* close out group.          */
      /*---------------------------*/

    if (pFormInfo->advQueryNextToken != NULL && pFormInfo->advQueryNextToken->choice == LEXTOK_RPAREN) {
      CloseGroup (pFormInfo);
      AdvQueryGetNextToken (pFormInfo);
    } else {
      ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "Missing right paren");
      return FALSE;
    }

    return TRUE;
  }

  /*--------------------------*/
  /* Otherwise, grab the term */
  /*--------------------------*/

  if (pFormInfo->advQueryNextToken->choice != LEXTOK_STRING) {
    ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "invalid token");
    return FALSE;
  }

  term = (CharPtr) (pFormInfo->advQueryNextToken->data.ptrvalue);
  AdvQueryGetNextToken (pFormInfo);

  /*---------------------------------------*/
  /* If it's a range, grab the second term */
  /*---------------------------------------*/

  if ((pFormInfo->advQueryNextToken != NULL) && (pFormInfo->advQueryNextToken->choice == LEXTOK_RANGE)) {
    AdvQueryGetNextToken (pFormInfo);
    if ((pFormInfo->advQueryNextToken == NULL) || (pFormInfo->advQueryNextToken->choice != LEXTOK_STRING)) {
      ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "missing second half of range");
      MemFree (term);
      return FALSE;
    }
    highRangeStr = (CharPtr) (pFormInfo->advQueryNextToken->data.ptrvalue);
    AdvQueryGetNextToken (pFormInfo);
  }

  /*----------------------*/
  /* Parse the field name */
  /*----------------------*/

  if ((pFormInfo->advQueryNextToken != NULL) && (pFormInfo->advQueryNextToken->choice == LEXTOK_LBRACKET)) {

      /*----------------------*/
    /* Get the field string */
      /*----------------------*/

    AdvQueryGetNextToken (pFormInfo);
    if ((pFormInfo->advQueryNextToken == NULL) || (pFormInfo->advQueryNextToken->choice != LEXTOK_STRING)) {
      ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "missing field id after bracket");
      MemFree (term);
      MemFree (highRangeStr);
      return FALSE;
    }

    fldStr = (CharPtr) (pFormInfo->advQueryNextToken->data.ptrvalue);

      /*-------------------------------------*/
    /* Convert string to field ID, mapping */
    /* wildcard '*' to 'ALL'.              */
      /*-------------------------------------*/

    if (fldStr != NULL && StrCmp (fldStr, "*") == 0)
      fld = FieldGetIDFromName (db, "ALL");
    else
      fld = FieldGetIDFromName (db, fldStr);

    MemFree (pFormInfo->advQueryNextToken->data.ptrvalue);
    if (fld < 0) {
      ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "bad field identifier");
      MemFree (term);
      MemFree (highRangeStr);
      return FALSE;
    }

      /*----------------------------------*/
    /* Check to see if the fields are a */
    /* comma-seperated range.           */
      /*----------------------------------*/

    AdvQueryGetNextToken (pFormInfo);
    if ((pFormInfo->advQueryNextToken == NULL) ||
        (pFormInfo->advQueryNextToken->choice != LEXTOK_COMMA && pFormInfo->advQueryNextToken->choice != LEXTOK_RBRACKET)) {
      ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "missing right bracket");
      MemFree (term);
      MemFree (highRangeStr);
      return FALSE;
    }
    if (pFormInfo->advQueryNextToken->choice == LEXTOK_COMMA) {
      AdvQueryGetNextToken (pFormInfo);
      if ((pFormInfo->advQueryNextToken == NULL) ||
          (pFormInfo->advQueryNextToken->choice != LEXTOK_STRING) || StringCmp (pFormInfo->advQueryNextToken->data.ptrvalue, "S") != 0) {
        ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "field qualifier error");
        MemFree (term);
        MemFree (highRangeStr);
        return FALSE;
      }
      MemFree (pFormInfo->advQueryNextToken->data.ptrvalue);
      AdvQueryGetNextToken (pFormInfo);
      if ((pFormInfo->advQueryNextToken == NULL) || (pFormInfo->advQueryNextToken->choice != LEXTOK_RBRACKET)) {
        ErrPostEx (SEV_WARNING, ERR_CD_LEX, 0, "missing right bracket");
        MemFree (term);
        MemFree (highRangeStr);
        return FALSE;
      }
    }
    AdvQueryGetNextToken (pFormInfo);
  }

  /*-------------------------------------*/
  /* Add in the high range, if it exists */
  /*-------------------------------------*/

  if (highRangeStr != NULL) {
    StringCat (term, ":");
    StringCat (term, highRangeStr);
  }

  /*-----------------------------------------------*/
  /* Create a new term and add it to the term list */
  /*-----------------------------------------------*/

  newTermPtr = Query_AddBoolTerm (pFormInfo->form, db, fld, term, STATE_ON, 0);

  if (newTermPtr == NULL) return FALSE;

  /*---------------------------------------*/
  /* Select the proper operator with which */
  /* to connect it to the previous term.   */
  /*---------------------------------------*/

  switch (currOp) {
    case LEXCHAR_OR:
      nOperator = ENTREZ_OP_OR;
      break;
    case LEXCHAR_AND:
      nOperator = ENTREZ_OP_AND;
      break;
    case LEXCHAR_NOT:
      nOperator = ENTREZ_OP_BUTNOT;
      break;
    default:
      nOperator = ENTREZ_OP_NONE;
      break;
  }
  newTermPtr->above = nOperator;
  if (newTermPtr->prev != NULL)
    newTermPtr->prev->below = nOperator;

  /*-------------------------------*/
  /* Set its group status properly */
  /*-------------------------------*/

  if (pFormInfo->advQueryNewGroup == TRUE) {
    newTermPtr->group = GROUP_FIRST;
    pFormInfo->advQueryNewGroup = FALSE;
  } else if (newTermPtr->prev != NULL) {
    switch (newTermPtr->prev->group) {
      case GROUP_SINGLE:
        newTermPtr->group = GROUP_SINGLE;
        break;
      case GROUP_FIRST:
      case GROUP_MIDDLE:
        newTermPtr->group = GROUP_MIDDLE;
        break;
      default:
        newTermPtr->group = GROUP_SINGLE;
        break;
    }
  }


/*----------------------------------*/
/* Clean up and return successfully */
/*----------------------------------*/

  MemFree (term);
  MemFree (highRangeStr);

  return TRUE;
}

/*===================================================================*/
/*                                                                   */
/*  Query_ParseString () -                                           */
/*                                                                   */
/*     NOTE -- This function is based on EntrezPMTLParseString()     */
/*             from accutil.c                                        */
/*                                                                   */
/*===================================================================*/

static Boolean Query_ParseString (ForM f, CharPtr str, Int2 db, Int2 fld)

{
  Boolean      retval;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*---------------------------*/
  /* Check parameters, setting */
  /* defaults if necessary.    */
  /*---------------------------*/

  if (str == NULL || *str == '\0') return FALSE;

  /*------------------------------------*/
  /* Clear out the existing query terms */
  /*------------------------------------*/

  AdvQueryInit (pFormInfo, str);

  /*------------------------------------*/
  /* Recursively parse the query string */
  /*------------------------------------*/

  AdvQueryGetNextToken (pFormInfo);

  if ((ParseNOTedTerms (pFormInfo, db, fld, 0) == TRUE) && (pFormInfo->advQueryNextToken == NULL))
    retval = TRUE;
  else {
    ResetChosen (pFormInfo);
    retval = FALSE;
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return retval;
}

/*==================================================================*/
/*                                                                  */
/*  AdvancedQueryText_Callback () -                                 */
/*                                                                  */
/*==================================================================*/

static void AdvancedQueryText_Callback (TexT queryTextBox)

{
  CharPtr      curstr;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (queryTextBox);

  curstr = SaveStringFromText (pFormInfo->advQueryText);

  if (Query_ParseString (pFormInfo->form, curstr, pFormInfo->currDb, -1)) {

    SafeEnable (pFormInfo->advRetrieveButton);
    pFormInfo->advQueryState = ADV_QUERY_EVALUATE_STATE;
    SafeSetTitle (pFormInfo->advRetrieveButton, "Evaluate");
  } else {
    SafeDisable (pFormInfo->advRetrieveButton);
    pFormInfo->advQueryState = ADV_QUERY_INVALID_STATE;
  }

  MemFree (curstr);
}

/*==================================================================*/
/*                                                                  */
/*  SetAdvancedText () -                                            */
/*                                                                  */
/*==================================================================*/

static void SetAdvancedText (TexT advancedText, CharPtr str)

{
  SetTitle (advancedText, str);
}

/*==================================================================*/
/*                                                                  */
/*  RepopulateChosen () -                                           */
/*                                                                  */
/*==================================================================*/

static void RepopulateChosen (FormInfoPtr pFormInfo)

{
  Int4          off;
  BaR           sb;
  StateDataPtr  sdp;
  Char          strn [256];

  sb = GetSlateVScrollBar ((SlatE) pFormInfo->chosenDoc);
  off = GetBarValue (sb);
  Reset (pFormInfo->chosenDoc);

  InvalDocument (pFormInfo->chosenDoc);
  pFormInfo->chosenNumLines = 0;
  for (sdp = pFormInfo->termList; sdp != NULL; sdp = sdp->next) {
    (pFormInfo->chosenNumLines)++;
    if (sdp->field == NULL)
      sprintf (strn, "%s\t [----]\t%ld\n", sdp->term, (long) sdp->count);
    else
      sprintf (strn, "%s\t [%s]\t%ld\n", sdp->term, sdp->field, (long) sdp->count);
    AppendText (pFormInfo->chosenDoc, strn, &chosenParFmt, chosenColFmt, systemFont);
  }
  InvalDocument (pFormInfo->chosenDoc);
  AdjustDocScroll (pFormInfo->chosenDoc);
  CorrectBarValue (sb, off);
  Update ();
}

/*==================================================================*/
/*                                                                  */
/*  Query_ConvertToString () - Convert the current linked list of   */
/*                            terms into a string that can be       */
/*                            displayed in the advanced query win.  */
/*                                                                  */
/*==================================================================*/

static CharPtr Query_ConvertToString (ForM f)

{
  Entrez2FieldInfoPtr  fieldInfo;
  Int2                 group;
  Int2                 last;
  CharPtr              ptr;
  StateDataPtr         sdp;
  CharPtr              tmp;
  CharPtr              advQueryString;
  FormInfoPtr          pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  if (pFormInfo->termList == NULL) return NULL;

  if (pFormInfo->chosenNumLines < 1) return NULL;

  advQueryString = MemNew (1000);
  advQueryString [0] = '\0';
  group = 0;
  last = 0;
  for (sdp = pFormInfo->termList; sdp != NULL; sdp = sdp->next) {
    if (sdp->group == GROUP_SINGLE || sdp->group == GROUP_FIRST)
      group++;

    if (sdp->state == STATE_ON) {
      if (last == 0) {
        StrCat (advQueryString, "( ");
        StrCat (advQueryString, "( ");
      } else if (last == group) {
        StrCat (advQueryString, " | ");
      } else if (sdp->above == ENTREZ_OP_BUTNOT) {
        StrCat (advQueryString, " )");
        StrCat (advQueryString, " )");
        StrCat (advQueryString, " - ");
        StrCat (advQueryString, "( ");
        StrCat (advQueryString, "( ");
      } else {
        StrCat (advQueryString, " )");
        StrCat (advQueryString, " & ");
        StrCat (advQueryString, "( ");
      }
      StringCat (advQueryString, "\"");
      tmp = StringSave (sdp->term);
      ptr = StringChr (tmp, ':');
      if (ptr != NULL) {
        *ptr = '\0';
        ptr++;
        TrimSpacesAroundString (tmp);
        TrimSpacesAroundString (ptr);
        StringCat (advQueryString, tmp);
        StringCat (advQueryString, "\" : \"");
        StringCat (advQueryString, ptr);
      } else
        StringCat (advQueryString, tmp);
      MemFree (tmp);
      StringCat (advQueryString, "\"[");
      fieldInfo = FieldGetInfo (sdp->db, sdp->fld);
      StringCat (advQueryString, fieldInfo->field_name);
      StringCat (advQueryString, "]");
      last = group;
    }
  }

  if (group > 0 && last > 0) {
    StrCat (advQueryString, " )");
    StrCat (advQueryString, " )");
  }

  return advQueryString;
}

/*==================================================================*/
/*                                                                  */
/*  AdvancedQueryToggle_Callback () - Called when the "Advanced     */
/*                                   Queries" menu option is        */
/*                                   selected.                      */
/*                                                                  */
/*==================================================================*/

static void AdvancedQueryToggle_Callback (IteM i)

{
  FormInfoPtr  pFormInfo;
  CharPtr      str;

  pFormInfo = (FormInfoPtr) GetObjectExtra (i);

  /*-----------------------*/
  /* Advanced ==> Standard */
  /*-----------------------*/

  if (! GetStatus (pFormInfo->advancedQueryItem)) {
    SafeHide (pFormInfo->advQueryGroup);
    Reset (pFormInfo->advQueryText);
    RepopulateChosen (pFormInfo);
    SafeShow (pFormInfo->stdQueryGroup);
  }

  /*-----------------------*/
  /* Standard ==> Advanced */
  /*-----------------------*/

  else {
    str = Query_ConvertToString (pFormInfo->form);
    SetAdvancedText (pFormInfo->advQueryText, str);
    MemFree (str);
    SafeHide (pFormInfo->stdQueryGroup);
    SafeShow (pFormInfo->advQueryGroup);
  }
}

/*==================================================================*/
/*                                                                  */
/*  ExplodeTermsToggle_Callback () - Called when the "Explode       */
/*                                   Terms" menu option is          */
/*                                   selected.                      */
/*                                                                  */
/*==================================================================*/

static void ExplodeTermsToggle_Callback (IteM i)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (i);
  if (pFormInfo == NULL) return;

  WatchCursor ();
  Update ();
  RecalculateChosen (pFormInfo);
  ArrowCursor ();
  Update ();
}

/*===================================================================*/
/*                                                                   */
/*  TermList_UnselectAll () -- Marks all terms in the term list as   */
/*                            unselected.                            */
/*                                                                   */
/*===================================================================*/

NLM_EXTERN Boolean TermList_UnselectAll (ForM f)

{
  Int2          count = 0;
  Int2          item;
  FormInfoPtr   pFormInfo;
  StateDataPtr  sdp;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  sdp = pFormInfo->termList;
  while (sdp != NULL) {
    sdp->state = STATE_OFF;
    sdp = sdp->next;
    count++;
  }

  ResetClip ();
  WatchCursor ();
  for (item = 1; item <= count; item++) {
    InvalDocCols (pFormInfo->chosenDoc, item, 3, 3);
  }
  Update ();
  RecalculateChosen (pFormInfo);
  ArrowCursor ();

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  AlphabetizeGroups () -                                          */
/*                                                                  */
/*==================================================================*/

static void AlphabetizeGroups (FormInfoPtr pFormInfo)

{
  Int2          compare;
  Boolean       keepGoing;
  StateDataPtr  next;
  StateDataPtr  sdp;
  StateData     tmp;

  if (pFormInfo->termList == NULL) return;

  keepGoing = TRUE;
  while (keepGoing) {
    keepGoing = FALSE;
    for (sdp = pFormInfo->termList; sdp->next != NULL; sdp = sdp->next) {
      next = sdp->next;
      if (sdp->group == GROUP_FIRST || sdp->group == GROUP_MIDDLE) {
        compare = MeshStringICmp (sdp->term, next->term);
        if (compare > 0) {
          tmp.term = next->term;
          tmp.field = next->field;
          tmp.count = next->count;
          tmp.db = next->db;
          tmp.fld = next->fld;
          next->term = sdp->term;
          next->field = sdp->field;
          next->count = sdp->count;
          next->db = sdp->db;
          next->fld = sdp->fld;
          sdp->term = tmp.term;
          sdp->field = tmp.field;
          sdp->count = tmp.count;
          sdp->db = tmp.db;
          sdp->fld = tmp.fld;
          keepGoing = TRUE;
        }
      }
    }
  }
}

/*==================================================================*/
/*                                                                  */
/*  Query_ClearUnusedTerms () - Remove any terms that have been     */
/*                              turned off.                         */
/*                                                                  */
/*==================================================================*/

static void Query_ClearUnusedTerms (IteM i)

{
  StateDataPtr       next;
  StateDataPtr       sdp;
  FormInfoPtr        pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (i);

  WatchCursor ();
  Update ();

  /* Iterate through the list, removing all */
  /* terms that are marked as unused.       */

  sdp = pFormInfo->termList;

  while (sdp != NULL) {
    next = sdp->next;
    if (sdp->state == STATE_OFF) {
      RemoveTermFromList (pFormInfo, sdp, FALSE, FALSE);
      FreeTerm (sdp);
    }
    sdp = next;
  }

  /* Adjust the display accordingly */

  AlphabetizeGroups (pFormInfo);
  RepopulateChosen (pFormInfo);
  RecalculateChosen (pFormInfo);
  ArrowCursor ();
}

/*==================================================================*/
/*                                                                  */
/*  SetupMenus () - Create and initialize the pulldown menus.       */
/*                                                                  */
/*==================================================================*/

static void SetupMenus (WindoW w, FormInfoPtr pFormInfo, Boolean explodeToggle, Boolean advancedQueryToggle)

{
  IteM  clearUnusedItem;
  MenU  m;
  MenU  s;

  /*-----------*/
  /* File menu */
  /*-----------*/

  m = PulldownMenu (w, "File");
  CommandItem (m, "Import UID List.../I", ImportUIDs_Callback);
  CommandItem (m, "Export UID List.../E", ExportUIDs_Callback);
  SeparatorItem (m);
  CommandItem (m, "Quit/Q", Quit_Callback);

  /*-----------*/
  /* Edit menu */
  /*-----------*/

  m = PulldownMenu (w, "Edit");
  FormCommandItem (m, CUT_MENU_ITEM, (BaseFormPtr) pFormInfo, VIB_MSG_CUT);
  FormCommandItem (m, COPY_MENU_ITEM, (BaseFormPtr) pFormInfo, VIB_MSG_COPY);
  FormCommandItem (m, PASTE_MENU_ITEM, (BaseFormPtr) pFormInfo, VIB_MSG_PASTE);
  FormCommandItem (m, CLEAR_MENU_ITEM, (BaseFormPtr) pFormInfo, VIB_MSG_DELETE);

  /*--------------*/
  /* Options menu */
  /*--------------*/

  m = PulldownMenu (w, "Options");

  clearUnusedItem = CommandItem (m, "Clear Unused Query Terms", Query_ClearUnusedTerms);
  SetObjectExtra (clearUnusedItem, pFormInfo, NULL);
  pFormInfo->advancedQueryItem = StatusItem (m, "Advanced Queries", AdvancedQueryToggle_Callback);
  SetStatus (pFormInfo->advancedQueryItem, advancedQueryToggle);
  SetObjectExtra (pFormInfo->advancedQueryItem, pFormInfo, NULL);

  pFormInfo->explodeItem = StatusItem (m, "Explode Terms", ExplodeTermsToggle_Callback);
  SetStatus (pFormInfo->explodeItem, explodeToggle);
  SetObjectExtra (pFormInfo->explodeItem, pFormInfo, NULL);

  s = SubMenu (m, "Show ASN.1");
  s_showASNItem = ChoiceGroup (s, NULL);
  ChoiceItem (s_showASNItem, "OFF");
  ChoiceItem (s_showASNItem, "1");
  ChoiceItem (s_showASNItem, "2");
  ChoiceItem (s_showASNItem, "3");
  ChoiceItem (s_showASNItem, "4");
  ChoiceItem (s_showASNItem, "5");
  ChoiceItem (s_showASNItem, "6");
  ChoiceItem (s_showASNItem, "7");
  ChoiceItem (s_showASNItem, "8");
  ChoiceItem (s_showASNItem, "9");
  ChoiceItem (s_showASNItem, "ON");
  ChoiceItem (s_showASNItem, "LOG");
  SetValue (s_showASNItem, 1);

  /*------------*/
  /* Help menu */
  /*------------*/

  m = PulldownMenu (w, "Help");
  CommandItem (m, "Databases...", DatabaseView_Callback);
  CommandItem (m, "Fields...", FieldView_Callback);
  CommandItem (m, "Modes...", ModeView_Callback);
}

/*==================================================================*/
/*                                                                  */
/*  SetupAdvQueryGroup () - Create and initialize the Advanced      */
/*                          Query Form.                             */
/*                                                                  */
/*==================================================================*/

static GrouP SetupAdvQueryGroup (FormInfoPtr pFormInfo, GrouP mainGroup)

{
  GrouP   advQueryGroup;
  GrouP   buttonGroup;
  GrouP   instructionGroup;
  Char    tempStr [256];

  advQueryGroup = HiddenGroup (mainGroup, -1, 0, NULL);
  SetGroupSpacing (advQueryGroup, 5, 5);

  /*----------------------------------*/
  /* Set up alternative boolean query */
  /* refinement text edit box.        */
  /*----------------------------------*/

  pFormInfo->advQueryText = ScrollText (advQueryGroup, ADV_TEXT_WIDTH, ADV_TEXT_HEIGHT, programFont, TRUE, AdvancedQueryText_Callback);
  SetObjectExtra (pFormInfo->advQueryText, pFormInfo, NULL);

  /*------------------------------------------*/
  /* Create a group for the Retrieve/Evaluate */
  /* combined button and the Reset button.    */
  /*------------------------------------------*/

  buttonGroup = HiddenGroup (advQueryGroup, 5, 0, NULL);
  SetGroupSpacing (buttonGroup, 10, 10);

  pFormInfo->advRetrieveButton = PushButton (buttonGroup, "Retrieve 000000000 Documents", EvaluateRetrieve_Callback);
  SetObjectExtra (pFormInfo->advRetrieveButton, pFormInfo, NULL);
  SetTitle (pFormInfo->advRetrieveButton, "Evaluate");
  Disable (pFormInfo->advRetrieveButton);
  pFormInfo->advQueryState = ADV_QUERY_INVALID_STATE;

  pFormInfo->advResetButton = PushButton (buttonGroup, "Reset", Reset_Callback);
  SetObjectExtra (pFormInfo->advResetButton, pFormInfo, NULL);

  /*----------------------------------------------*/
  /* Create instructions for using advanced query */
  /*----------------------------------------------*/

  instructionGroup = HiddenGroup (advQueryGroup, 1, 0, NULL);
  StaticPrompt (instructionGroup, "Operators:", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "    & (and)", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "    | (or)", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "    - (butnot)", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "    : (range)", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "Example:", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "", 0, 0, programFont, 'l');
  tempStr [0] = '\0';
  StringCat (tempStr, "    ((\"glucagon\" [WORD] | \"insulin\" ");
  StringCat (tempStr, "[MESH]) & (\"1995\" : \"1996\" [PDAT]))");
  StaticPrompt (instructionGroup, tempStr, 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "", 0, 0, programFont, 'l');
  StaticPrompt (instructionGroup, "Use [*] to search all fields.", 0, 0, programFont, 'l');

  /*--------------------------------*/
  /* Return the newly created group */
  /*--------------------------------*/

  return advQueryGroup;
}

/*==================================================================*/
/*                                                                  */
/*  GetValueFromField () -                                          */
/*                                                                  */
/*==================================================================*/

static Int4 GetValueFromField (DoC d, Int2 item, Int2 row, Int2 col)

{
  CharPtr  str;
  Int4     value;

  value = -1;
  str = GetDocText (d, item, row, col);
  if (str != NULL) {
    if (! StrToLong (str, &value)) {
      value = -1;
    }
  }

  MemFree (str);
  return value;
}

/*==================================================================*/
/*                                                                  */
/*  DrawAvailLeaf () -                                              */
/*                                                                  */
/*==================================================================*/

static void DrawAvailLeaf (DoC d, RectPtr r, Int2 item, Int2 frst)

{
  RecT  q;
  Int2  value;

  if (r == NULL || frst != 0) return;

  value = GetValueFromField (d, item, 1, 3);
  if (value != 1) return;

  q = *r;
  q.left++;
  q.right = q.left + 4;
  q.top += stdLineHeight / 2 - 2;
  q.bottom = q.top + 4;
  value = GetValueFromField (d, item, 1, 3);
  if (value == STATE_ON) {
    /*
       InvertColors ();
    */
    EraseRect (&q);
    PaintRect (&q);
    /*
       InvertColors ();
    */
  } else
    PaintRect (&q);
}

/*==================================================================*/
/*                                                                  */
/*  HighlightAvail () -                                             */
/*                                                                  */
/*==================================================================*/

static Boolean HighlightAvail (DoC d, Int2 item, Int2 row, Int2 col)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  if (item == pFormInfo->availItem && row == pFormInfo->availRow) return TRUE;

  return FALSE;
}

/*==================================================================*/
/*                                                                  */
/*  ClickAvail () -                                                 */
/*                                                                  */
/*==================================================================*/

static void ClickAvail (DoC d, PoinT pt)

{
  Int2         item;
  Int2         row;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  MapDocPoint (d, pt, &item, &row, NULL, NULL);
  pFormInfo->availClickItem = item;
  pFormInfo->availClickRow = row;
  pFormInfo->wasDoubleClick = dblClick;

  SafeEnable (pFormInfo->acceptButton);
}

/*==================================================================*/
/*                                                                  */
/*  ReleaseAvail () - This is called whenever the mouse button is   */
/*                   realeased in the term selection window.        */
/*                                                                  */
/*==================================================================*/

static void ReleaseAvail (DoC d, PoinT pt)

{
  Char         ch;
  Int2         item;
  Int2         olditem;
  Int2         oldrow;
  CharPtr      ptr;
  Int2         row;
  CharPtr      text;
  Int4         iTermCount;
  CharPtr      sTermCount;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  /*------------------------------------*/
  /* Convert the screen location of the */
  /* release into an item and a row.    */
  /*------------------------------------*/

  MapDocPoint (d, pt, &item, &row, NULL, NULL);

  /* ---------------------------------------- */
  /* There's no dragging in the avail window */
  /* ---------------------------------------- */

  if (pFormInfo->availClickItem != item || pFormInfo->availClickRow != row) return;

  text = GetDocText (pFormInfo->availDoc, item, row, 1);
  if (StringHasNoText (text)) {
    text = MemFree (text);
  }

  olditem = pFormInfo->availItem;
  oldrow = pFormInfo->availRow;
  if (text != NULL) {
    pFormInfo->availItem = item;
    pFormInfo->availRow = row;
  } else {
    pFormInfo->availItem = 0;
    pFormInfo->availRow = 0;
  }
  if (olditem > 0 && oldrow > 0)
    InvalDocRows (pFormInfo->availDoc, olditem, oldrow, oldrow);
  if (text != NULL && item > 0 && row > 0)
    InvalDocRows (pFormInfo->availDoc, item, row, row);

  if (text != NULL) {
    ptr = text;
    ch = *ptr;
    while (ch != '\0' && ch >= ' ') {
      ptr++;
      ch = *ptr;
    }
    *ptr = '\0';
    if (pFormInfo->currMode == RANGE_MODE) {
      if (CurrentText () == pFormInfo->fromText) {
        SafeSetTitle (pFormInfo->fromText, text);
        Select (pFormInfo->fromText);
      } else if (CurrentText () == pFormInfo->toText) {
        SafeSetTitle (pFormInfo->toText, text);
        Select (pFormInfo->toText);
      }
    } else {
      SafeSetTitle (pFormInfo->termText, text);
      Select (pFormInfo->termText);
    }
    Update ();
  } else {
    if (pFormInfo->currMode == RANGE_MODE) {
      SafeSetTitle (pFormInfo->fromText, "");
      SafeSetTitle (pFormInfo->toText, "");
      Select (pFormInfo->fromText);
    } else {
      SafeSetTitle (pFormInfo->termText, "");
      Select (pFormInfo->termText);
    }
    Update ();
  }

  if (text != NULL && pFormInfo->wasDoubleClick) {
    WatchCursor ();
    Update ();

    if ((pFormInfo->currMode == TAXONOMY_MODE) || (pFormInfo->currMode == MESH_TREE_MODE)) {
      Update ();
      ResetClip ();             /* clipped to panel, need to update popup */
      RepopulateTaxonomy (pFormInfo, text);
    } else if (pFormInfo->currMode == RANGE_MODE) {
      ResetClip ();
      sTermCount = GetDocText (pFormInfo->availDoc, pFormInfo->availItem,
                               pFormInfo->availRow, E2_COUNT_COL);
      TrimSpacesAroundString (sTermCount);
      iTermCount = atoi (sTermCount);
      MemFree (sTermCount);
      Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb,
                         pFormInfo->currField, text, STATE_ON, iTermCount);
      Update ();
      RecalculateChosen (pFormInfo);
    } else {
      ResetClip ();
      sTermCount = GetDocText (pFormInfo->availDoc, pFormInfo->availItem,
                               pFormInfo->availRow, E2_COUNT_COL);
      TrimSpacesAroundString (sTermCount);
      iTermCount = atoi (sTermCount);
      MemFree (sTermCount);
      Query_AddBoolTerm (pFormInfo->form, pFormInfo->currDb,
                         pFormInfo->currField, text, STATE_ON, iTermCount);
      Update ();
      RecalculateChosen (pFormInfo);
    }

    ArrowCursor ();
  }
  MemFree (text);
}

/*==================================================================*/
/*                                                                  */
/*  AvailTimerProc () -                                             */
/*                                                                  */
/*==================================================================*/

static void AvailTimerProc (WindoW w)

{
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (w);
  pFormInfo->okayToAccept = TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  InitAvailPanel () - Set up the Term List Panel.                 */
/*                                                                  */
/*==================================================================*/

static void InitAvailPanel (FormInfoPtr pFormInfo)

{
  RecT  r;

  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  SetDocShade (pFormInfo->availDoc, DrawAvailLeaf, NULL, HighlightAvail, NULL);
  SetDocProcs (pFormInfo->availDoc, ClickAvail, NULL, ReleaseAvail, NULL);
  SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);

  ObjectRect (pFormInfo->availDoc, &r);
  InsetRect (&r, 4, 4);
  SelectFont (systemFont);

  availColFmt [1].pixWidth = StringWidth ("0000000") + 10;
  availColFmt [0].pixWidth = (r.right - r.left) - availColFmt [1].pixWidth;
  availColFmt [2].pixWidth = 0;

  SetDocAutoAdjust (pFormInfo->availDoc, FALSE);

  InvalDocument (pFormInfo->availDoc);
}

/*==================================================================*/
/*                                                                  */
/*  DrawChosenBrackets () - This is the draw callback for the       */
/*                         Chosen document panel. It gets called    */
/*                         every time the panel is drawn.           */
/*                                                                  */
/*==================================================================*/

static void DrawChosenBrackets (DoC d, RectPtr r, Int2 item, Int2 frst)

{
  FormInfoPtr   pFormInfo;
  RecT          s;
  StateDataPtr  sdp;
  static Uint1  andsign [] = { 0x30, 0x48, 0x50, 0x20, 0x50, 0x8A, 0x84, 0x8A, 0x71, 0x00 };
  static Uint1  notsign [] = { 0x00, 0x00, 0xFF, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00 };

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  sdp = pFormInfo->termList;
  while (sdp != NULL && item > 1) {
    sdp = sdp->next;
    item--;
  }
  if (sdp == NULL) return;

  switch (sdp->group) {
    case GROUP_SINGLE:
      break;
    case GROUP_FIRST:
      MoveTo (r->left + 16 + 1, r->top + 1);
      LineTo (r->left + 16 + 3, r->top + 1);
      MoveTo (r->left + 16 + 1, r->top + 2);
      LineTo (r->left + 16 + 3, r->top + 2);
      MoveTo (r->left + 16 + 1, r->top + 1);
      LineTo (r->left + 16 + 1, r->bottom);
      break;
    case GROUP_MIDDLE:
      MoveTo (r->left + 16 + 1, r->top);
      LineTo (r->left + 16 + 1, r->bottom);
      break;
    case GROUP_LAST:
      MoveTo (r->left + 16 + 1, r->top);
      LineTo (r->left + 16 + 1, r->bottom - 1);
      MoveTo (r->left + 16 + 1, r->bottom - 2);
      LineTo (r->left + 16 + 3, r->bottom - 2);
      MoveTo (r->left + 16 + 1, r->bottom - 1);
      LineTo (r->left + 16 + 3, r->bottom - 1);
      break;
    default:
      break;
  }

  switch (sdp->above) {
    case ENTREZ_OP_NONE:
      break;
    case ENTREZ_OP_AND:
      LoadRect (&s, r->left + 6, r->top, r->left + 14, r->top + 5);
      CopyBits (&s, andsign + 5);
      break;
    case ENTREZ_OP_BUTNOT:
      LoadRect (&s, r->left, r->top, r->left + 8, r->top + 5);
      CopyBits (&s, notsign + 5);
      break;
    default:
      break;
  }

  switch (sdp->below) {
    case ENTREZ_OP_NONE:
      break;
    case ENTREZ_OP_AND:
      LoadRect (&s, r->left + 6, r->bottom - 5, r->left + 14, r->bottom);
      CopyBits (&s, andsign);
      break;
    case ENTREZ_OP_BUTNOT:
      LoadRect (&s, r->left, r->bottom - 5, r->left + 8, r->bottom);
      CopyBits (&s, notsign);
      break;
    default:
      break;
  }
}

/*==================================================================*/
/*                                                                  */
/*  HighlightChosen () -                                            */
/*                                                                  */
/*==================================================================*/

static Boolean HighlightChosen (DoC d, Int2 item, Int2 row, Int2 col)

{
  StateDataPtr  sdp;
  FormInfoPtr   pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  if (col == 1 || col == 2) {
    if (pFormInfo->chosenInAboveBox || pFormInfo->chosenInBelowBox) return FALSE;
    /* if (item == pFormInfo->chosenItem) return TRUE; */
    return FALSE;
  } else if (col == 3) {
    sdp = pFormInfo->termList;
    while (sdp != NULL && item > 1) {
      sdp = sdp->next;
      item--;
    }
    if (sdp == NULL) return FALSE;
    return (Boolean) (sdp->state == STATE_ON);
  } else
    return FALSE;
}

/*==================================================================*/
/*                                                                  */
/*  FrameChosen () -                                                */
/*                                                                  */
/*==================================================================*/

static void FrameChosen (FormInfoPtr pFormInfo)

{
  RecT  sr;

  ObjectRect (pFormInfo->chosenDoc, &sr);
  InsetRect (&sr, 4, 4);

  if (RectInRect (&(pFormInfo->trackRect), &sr)) {
    Dotted ();
    FrameRect (&(pFormInfo->trackRect));
  }
}

/*==================================================================*/
/*                                                                  */
/*  ClickChosen () -                                                */
/*                                                                  */
/*==================================================================*/

static void ClickChosen (DoC d, PoinT pt)

{
  Int2         col;
  Int2         item;
  RecT         r;
  Int2         row;
  RecT         s;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  MapDocPoint (d, pt, &item, &row, &col, &r);
  if (item > 0 && row == 0 && col > 0)
    row = 1;

  pFormInfo->chosenClickItem = item;
  pFormInfo->chosenClickRow = row;
  pFormInfo->chosenClickCol = col;
  pFormInfo->wasDoubleClick = dblClick;
  pFormInfo->chosenInAboveBox = FALSE;
  pFormInfo->chosenInBelowBox = FALSE;

  if ((col == 1 || col == 2) && (item > 0) && (item <= pFormInfo->chosenNumLines) && (row > 0)) {
    pFormInfo->chosenItem = item;
    LoadRect (&s, r.left, r.top, r.left + 16, r.top + 5);
    if (PtInRect (pt, &s))
      pFormInfo->chosenInAboveBox = TRUE;
    LoadRect (&s, r.left, r.bottom - 5, r.left + 16, r.bottom);
    if (PtInRect (pt, &s))
      pFormInfo->chosenInBelowBox = TRUE;
    if (pFormInfo->chosenInAboveBox || pFormInfo->chosenInBelowBox)
      return;
    InvalDocCols (pFormInfo->chosenDoc, item, 1, 2);
    Update ();
    r.right = r.left + chosenColFmt [0].pixWidth + chosenColFmt [1].pixWidth;
    LoadRect (&(pFormInfo->trackRect), r.left + 22, r.top, r.right - 2, r.bottom);
    pFormInfo->trackPt = pt;
    InvertMode ();
    FrameChosen (pFormInfo);
  } else
    pFormInfo->chosenItem = 0;
}

/*==================================================================*/
/*                                                                  */
/*  DragChosen () -                                                 */
/*                                                                  */
/*==================================================================*/

static void DragChosen (DoC d, PoinT pt)

{
  Int4         off;
  BaR          sb;
  RecT         sr;
  FormInfoPtr  pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  if (pFormInfo->chosenItem == 0) return;
  if (pFormInfo->chosenInAboveBox || pFormInfo->chosenInBelowBox) return;
  InvertMode ();
  FrameChosen (pFormInfo);
  Update ();
  ObjectRect (pFormInfo->chosenDoc, &sr);
  InsetRect (&sr, 4, 4);
  if (PtInRect (pt, &sr)) {
    OffsetRect (&(pFormInfo->trackRect), 0, pt.y - pFormInfo->trackPt.y);
    pFormInfo->trackPt = pt;
  }
  sb = GetSlateVScrollBar ((SlatE) pFormInfo->chosenDoc);
  off = GetBarValue (sb);
  ResetClip ();
  if (pt.y < sr.top)
    SetBarValue (sb, off - 1);
  else if (pt.y > sr.bottom)
    SetBarValue (sb, off + 1);
  Update ();
  InvertMode ();
  FrameChosen (pFormInfo);
  Update ();
}

/*==================================================================*/
/*                                                                  */
/*  FlipOperatorBelow () -                                          */
/*                                                                  */
/*==================================================================*/

static void FlipOperatorBelow (FormInfoPtr pFormInfo, Int2 item)

{
  StateDataPtr  next;
  RecT          r;
  StateDataPtr  sdp;

  sdp = pFormInfo->termList;
  while (item > 1 && sdp != NULL) {
    item--;
    sdp = sdp->next;
  }
  if (sdp != NULL) {
    next = sdp->next;
    if (next != NULL) {
      if (sdp->below != next->above)
        Beep ();
      if (sdp->below == ENTREZ_OP_AND) {
        sdp->below = ENTREZ_OP_BUTNOT;
        next->above = ENTREZ_OP_BUTNOT;
      } else if (sdp->below == ENTREZ_OP_BUTNOT) {
        sdp->below = ENTREZ_OP_AND;
        next->above = ENTREZ_OP_AND;
      }
      ObjectRect (pFormInfo->chosenDoc, &r);
      InsetRect (&r, 4, 4);
      r.right = r.left + 16;
      Select (pFormInfo->chosenDoc);
      InvalRect (&r);
      ResetClip ();
      WatchCursor ();
      Update ();
      RecalculateChosen (pFormInfo);
      ArrowCursor ();
    }
  }
}

/*==================================================================*/
/*                                                                  */
/*  RemoveTermFromList () - Removes the given term from the given   */
/*                           doubly-linked list of terms, and       */
/*                           adjusts the surrounding terms          */
/*                           accordingly.                           */
/*                                                                  */
/*==================================================================*/

static Boolean RemoveTermFromList (FormInfoPtr pFormInfo, StateDataPtr sdp, Boolean goingDown, Boolean lastDraggedDown)

{
  StateDataPtr  prev;
  StateDataPtr  next;
  Int2          op;

  /*-----------------------------*/
  /* Sanity check - can't remove */
  /* term from an empty list.    */
  /*-----------------------------*/

  if (sdp == NULL) return E2_LIST_NOT_MODIFIED;

  /*------------------------------------*/
  /* Detach term from surrounding terms */
  /*------------------------------------*/

  next = sdp->next;
  prev = sdp->prev;
  switch (sdp->group) {
    case GROUP_SINGLE:
      if (lastDraggedDown)
        return E2_LIST_NOT_MODIFIED;
      break;
    case GROUP_FIRST:
      if (next != NULL) {
        if (next->group == GROUP_MIDDLE)
          next->group = GROUP_FIRST;
        else if (next->group == GROUP_LAST)
          next->group = GROUP_SINGLE;
      }
      break;
    case GROUP_MIDDLE:
      break;
    case GROUP_LAST:
      if (prev != NULL) {
        if (prev->group == GROUP_MIDDLE)
          prev->group = GROUP_LAST;
        else if (prev->group == GROUP_FIRST)
          prev->group = GROUP_SINGLE;
      }
      break;
    default:
      break;
  }

  /*-------------------------*/
  /* Adjust the state of the */
  /* surrounding terms.      */
  /*-------------------------*/

  sdp->prev = NULL;
  sdp->next = NULL;
  if (prev != NULL)
    prev->next = next;
  if (next != NULL)
    next->prev = prev;
  if (prev == NULL)
    pFormInfo->termList = next;
  op = ENTREZ_OP_NONE;
  switch (sdp->group) {
    case GROUP_SINGLE:
      if (goingDown) {
        if (prev != NULL)
          op = prev->below;
      } else {
        if (next != NULL)
          op = next->above;
      }
      if (prev != NULL)
        prev->below = op;
      if (next != NULL)
        next->above = op;
      break;
    case GROUP_FIRST:
      if (prev != NULL)
        op = prev->below;
      if (next != NULL)
        next->above = op;
      break;
    case GROUP_MIDDLE:
      break;
    case GROUP_LAST:
      if (next != NULL)
        op = next->above;
      if (prev != NULL)
        prev->below = op;
      break;
    default:
      break;
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return E2_LIST_MODIFIED;
}

/*====================================================================*/
/*                                                                    */
/*  AddTermToList () - Adds a given term to the given doubly-linked   */
/*                      list of terms, and adjusts the surrounding    */
/*                      terms accordingly.                            */
/*                                                                    */
/*====================================================================*/

static void AddTermToList (FormInfoPtr pFormInfo, StateDataPtr newTerm, Int2 item, Boolean merge)

{
  StateDataPtr  prev;
  StateDataPtr  next;
  StateDataPtr  currTerm;
  Int2          op;

  /*-------------------------------------*/
  /* Add the new term into the term list */
  /*-------------------------------------*/

  currTerm = pFormInfo->termList;
  if (currTerm == NULL)
    pFormInfo->termList = newTerm;
  else {
    while (item > 1 && currTerm->next != NULL) {
      item--;
      currTerm = currTerm->next;
    }
    next = currTerm->next;
    prev = currTerm->prev;
    currTerm->next = newTerm;
    newTerm->next = next;
    newTerm->prev = currTerm;
    if (next != NULL)
      next->prev = newTerm;
    op = ENTREZ_OP_NONE;
    if (prev != NULL)
      op = prev->below;
    currTerm->above = op;
    newTerm->below = currTerm->below;
    currTerm->below = ENTREZ_OP_NONE;
    newTerm->above = ENTREZ_OP_NONE;
  }

  /*----------------------------------*/
  /* Do adding to group, if necessary */
  /*----------------------------------*/

  newTerm->group = GROUP_SINGLE;
  if (merge && currTerm != NULL) {
    switch (currTerm->group) {
      case GROUP_SINGLE:
        currTerm->group = GROUP_FIRST;
        newTerm->group = GROUP_LAST;
        break;
      case GROUP_FIRST:
        newTerm->group = GROUP_MIDDLE;
        break;
      case GROUP_MIDDLE:
        newTerm->group = GROUP_MIDDLE;
        break;
      case GROUP_LAST:
        currTerm->group = GROUP_MIDDLE;
        newTerm->group = GROUP_LAST;
        break;
      default:
        break;
    }
  }

  /*-----------------------------------------*/
  /* If it's not in a group, then by default */
  /* it's AND'd with the neighboring terms.  */
  /*-----------------------------------------*/

  else {
    prev = newTerm->prev;
    if (prev != NULL) {
      prev->below = ENTREZ_OP_AND;
      newTerm->above = ENTREZ_OP_AND;
    }
  }
}

/*==================================================================*/
/*                                                                  */
/*  ReleaseChosen () - This is the callback function that gets      */
/*                    when the mouse button is released after       */
/*                    dragging a term in the Query Refinement       */
/*                    panel.                                        */
/*                                                                  */
/*==================================================================*/

static void ReleaseChosen (DoC d, PoinT pt)

{
  Int2          col;
  Boolean       goingDown;
  Int2          inval;
  Int2          item;
  Boolean       lastDraggedDown;
  Boolean       merge;
  Int2          oldItem;
  RecT          r;
  Int2          row;
  RecT          s;
  StateDataPtr  sdp;
  RecT          sr;
  FormInfoPtr   pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (d);

  /*---------------------------------------*/
  /* Map the current cursor position to an */
  /* item in the Query Refinement panel.   */
  /*---------------------------------------*/

  MapDocPoint (d, pt, &item, &row, &col, &r);
  if (item > 0 && row == 0 && col > 0)
    row = 1;

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

  if (pFormInfo->chosenInAboveBox || pFormInfo->chosenInBelowBox) {
    LoadRect (&s, r.left, r.top, r.left + 16, r.top + 5);
    if (PtInRect (pt, &s)) {
      if ((pFormInfo->chosenInAboveBox) && (item == pFormInfo->chosenItem))
        FlipOperatorBelow (pFormInfo, pFormInfo->chosenItem - 1);
      else if ((pFormInfo->chosenInBelowBox) && (item == pFormInfo->chosenItem - 1))
        FlipOperatorBelow (pFormInfo, pFormInfo->chosenItem - 1);
    }
    LoadRect (&s, r.left, r.bottom - 5, r.left + 16, r.bottom);
    if (PtInRect (pt, &s)) {
      if ((pFormInfo->chosenInBelowBox) && (item == pFormInfo->chosenItem))
        FlipOperatorBelow (pFormInfo, pFormInfo->chosenItem);
      else if ((pFormInfo->chosenInAboveBox) && (item == pFormInfo->chosenItem + 1))
        FlipOperatorBelow (pFormInfo, pFormInfo->chosenItem);
    }
    return;
  }

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

  if (pFormInfo->chosenItem > 0) {

    oldItem = pFormInfo->chosenItem;
    pFormInfo->chosenItem = 0;
    InvertMode ();
    FrameChosen (pFormInfo);
    Update ();
    InvalDocCols (pFormInfo->chosenDoc, oldItem, 1, 2);
    Update ();
    if (item == oldItem) return;
    if (pFormInfo->chosenNumLines < 2) return;

      /*-----------------------------------------------*/
    /* Are we dragging the last item 'past' the end? */
      /*-----------------------------------------------*/

    lastDraggedDown = FALSE;
    if (oldItem == pFormInfo->chosenNumLines && item > pFormInfo->chosenNumLines)
      lastDraggedDown = TRUE;

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

    merge = TRUE;
    ObjectRect (pFormInfo->chosenDoc, &sr);
    InsetRect (&sr, 4, 4);
    /*
       if (item > pFormInfo->chosenNumLines && PtInRect (pt, &sr))
    */
    if (item == 0 && PtInRect (pt, &sr)) {
      merge = FALSE;
      item = INT2_MAX;
    }

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

    if ((item <= 0) || (pFormInfo->termList == NULL))
      return;

      /*--------------------------------------*/
    /* Are we moving an item down the list? */
      /*--------------------------------------*/

    goingDown = FALSE;
    if (item > oldItem) {
      item--;
      goingDown = TRUE;
    }

      /*--------------------------------*/
    /* Find the item being dragged in */
    /* the state list.                */
      /*--------------------------------*/

    sdp = pFormInfo->termList;
    while (oldItem > 1 && sdp != NULL) {
      oldItem--;
      sdp = sdp->next;
    }

    /*----------------------------------------*/
    /* Remove the term from the term list and */
    /* then re-add it in it's new position.   */
    /*----------------------------------------*/

    if (RemoveTermFromList (pFormInfo, sdp, goingDown, lastDraggedDown) ==
        E2_LIST_NOT_MODIFIED) return;

    AddTermToList (pFormInfo, sdp, item, merge);

    /*---------------------------*/
    /* Recalculate the new query */
    /* and redisplay it.         */
    /*---------------------------*/

    ResetClip ();
    WatchCursor ();
    Update ();
    AlphabetizeGroups (pFormInfo);
    RepopulateChosen (pFormInfo);
    RecalculateChosen (pFormInfo);
    ArrowCursor ();
  }

  /*------------------------------------*/
  /* If the count column was clicked on */
  /* then toggle rows state on or off.  */
  /*------------------------------------*/

  if ((pFormInfo->chosenClickItem == item) &&
      (pFormInfo->chosenClickRow == row)   &&
      (pFormInfo->chosenClickCol == col)   &&
      (col == 3)) {
    inval = item;
    sdp = pFormInfo->termList;
    while (sdp != NULL && item > 1) {
      sdp = sdp->next;
      item--;
    }
    if (sdp == NULL) return;
    switch (sdp->state) {
      case STATE_OFF:
        sdp->state = STATE_ON;
        break;
      case STATE_ON:
        sdp->state = STATE_OFF;
        break;
      default:
        sdp->state = STATE_OFF;
        break;
    }
    ResetClip ();
    WatchCursor ();
    InvalDocCols (pFormInfo->chosenDoc, inval, 3, 3);
    Update ();
    RecalculateChosen (pFormInfo);
    ArrowCursor ();
  }
}

/*==================================================================*/
/*                                                                  */
/*  InitChosenPanel () -                                            */
/*                                                                  */
/*==================================================================*/

static void InitChosenPanel (FormInfoPtr pFormInfo)

{
  Entrez2InfoPtr       e2ip;
  Entrez2DbInfoPtr     e2db;
  Entrez2FieldInfoPtr  e2fd;
  Int2                 maxWidth = 0;
  RecT                 r;
  Char                 str [32];

  SetDocProcs (pFormInfo->chosenDoc, ClickChosen, DragChosen, ReleaseChosen, NULL);
  SetDocShade (pFormInfo->chosenDoc, DrawChosenBrackets, NULL, HighlightChosen, NULL);
  pFormInfo->chosenNumLines = 0;

  ObjectRect (pFormInfo->chosenDoc, &r);
  InsetRect (&r, 4, 4);

  SelectFont (systemFont);
  chosenColFmt [2].pixWidth = StringWidth ("0000000") + 10;
  chosenColFmt [1].pixWidth = StringWidth ("0000000")  + 10;

  /* more accurate calculation of chosenColFmt [1].pixWidth */

  e2ip = Query_GetInfo ();
  if (e2ip != NULL) {
    for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
      for (e2fd = e2db->fields; e2fd != NULL; e2fd = e2fd->next) {
        if (e2fd->field_name == NULL || StringLen (e2fd->field_name) > 20) continue;
        sprintf (str, " [%s]", e2fd->field_name);
        maxWidth = MAX (maxWidth, StringWidth (str));
      }
    }
    chosenColFmt [1].pixWidth = maxWidth  + 10;
  }

  chosenColFmt [0].pixWidth = (r.right - r.left) - chosenColFmt [1].pixWidth - chosenColFmt [2].pixWidth;
  SetDocAutoAdjust (pFormInfo->chosenDoc, FALSE);

  InvalDocument (pFormInfo->chosenDoc);
}

/*==================================================================*/
/*                                                                  */
/*  SetupStdQueryGroup () - Create and initialize the Standard      */
/*                          Query Form.                             */
/*                                                                  */
/*==================================================================*/

static GrouP SetupStdQueryGroup (FormInfoPtr pFormInfo, GrouP mainGroup)

{
  Entrez2DbInfoPtr   e2db;
  Entrez2InfoPtr     e2ip;
  GrouP              stdQueryGroup;
  GrouP              enumlistGroup;
  EnumFieldAssocPtr  alist;
  Int2               idx;
  Int2               db;
  Int2               wid;
  GrouP              c;
  GrouP              j;
  GrouP              q1;
  GrouP              q2;
  GrouP              q3;
  PrompT             ppt1;
  PrompT             ppt2;
  ModeChoice         mc;
  ModeIndex          md;
  RecT               r;

  /*---------------------------------*/
  /* Get info about the DB server(s) */
  /*---------------------------------*/

  e2ip = Query_GetInfo ();
  if (e2ip == NULL) return NULL;

  /*---------------------------------*/
  /* Create the Standard Query Group */
  /*---------------------------------*/

  stdQueryGroup = HiddenGroup (mainGroup, -1, 0, NULL);
  SetGroupSpacing (stdQueryGroup, 5, 5);

  /*---------------------------------*/
  /* Create a group for the pulldown */
  /* lists and the accept button.    */
  /*---------------------------------*/

  enumlistGroup = HiddenGroup (stdQueryGroup, 0, 2, NULL);
  SetGroupSpacing (enumlistGroup, 15, 2);

  /*-------------------------------*/
  /* Set up database pulldown list */
  /*-------------------------------*/

  StaticPrompt (enumlistGroup, "Database:", 0, 0, programFont, 'l');
  q1 = HiddenGroup (enumlistGroup, 0, 0, NULL);
  alist = CreateDatabaseAlist (e2ip);
  idx = DBGetIDFromName ("PubMed");

  pFormInfo->databasePopup = CreateEnumPopupDialog (q1, TRUE, ChangeDatabase_Callback, alist, (UIEnum) idx, NULL);
  SetObjectExtra (pFormInfo->databasePopup, pFormInfo, NULL);
  FreeEnumFieldAlist (alist);
  pFormInfo->currDb = idx;

  /*-------------------------------*/
  /* Set up field pulldown list(s) */
  /*-------------------------------*/

  StaticPrompt (enumlistGroup, "Field:", 0, 0, programFont, 'l');
  q2 = HiddenGroup (enumlistGroup, 0, 0, NULL);
  pFormInfo->fieldsPopup = MemNew (sizeof (PopuP) * (e2ip->db_count + 2));
  idx = FieldGetIDFromName (pFormInfo->currDb, "ALL");
  for (e2db = e2ip->db_info; e2db != NULL; e2db = e2db->next) {
    db = DBGetIDFromName (e2db->db_name);
    alist = CreateFieldAlist (e2db);
    pFormInfo->fieldsPopup [db] = CreateEnumPopupDialog (q2, TRUE, ChangeField_Callback, alist, (UIEnum) idx, pFormInfo);
    FreeEnumFieldAlist (alist);
    Hide (pFormInfo->fieldsPopup [db]);
  }
  pFormInfo->currField = idx;
  db = DBGetIDFromName ("PubMed");
  Show (pFormInfo->fieldsPopup [db]);

  /*---------------------------*/
  /* Set up mode pulldown list */
  /*---------------------------*/

  StaticPrompt (enumlistGroup, "Mode:", 0, 0, programFont, 'l');
  q3 = HiddenGroup (enumlistGroup, 0, 0, NULL);
  for (md = POPUP_MULT; md <= POPUP_UID; md++) {
    alist = mode_alists [md];
    mc = SELECTION_MODE;
    if (md == POPUP_MULT)
      mc = AUTOMATIC_MODE;
    else if (md == POPUP_AUTO)
      mc = TRANSLATE_MODE;
    else if (md == POPUP_UID)
      mc = LOOKUP_UID_MODE;
    pFormInfo->modesPopup [md] = CreateEnumPopupDialog (q3, TRUE, ChangeMode_Callback, alist, (UIEnum) mc, pFormInfo);
    Hide (pFormInfo->modesPopup [md]);
  }
  pFormInfo->currMode = AUTOMATIC_MODE;
  md = POPUP_MULT;
  Show (pFormInfo->modesPopup [md]);

  /*----------------------*/
  /* Set up accept button */
  /*----------------------*/

  StaticPrompt (enumlistGroup, "", 0, 0, programFont, 'l');
  pFormInfo->acceptButton = DefaultButton (enumlistGroup, "Accept", Accept_Callback);
  SetObjectExtra (pFormInfo->acceptButton, pFormInfo, NULL);
  Disable (pFormInfo->acceptButton);

  AlignObjects (ALIGN_MIDDLE, (HANDLE) q1, (HANDLE) q2, (HANDLE) q3, (HANDLE) pFormInfo->acceptButton, NULL);

  /*------------------------------*/
  /* Create a group to contain:   */
  /*                              */
  /*    Term entry                */
  /*    From/To Range entry       */
  /*    Taxonomy 'Lineage' popup  */
  /*                              */
  /* These are mutually exclusive */
  /* and only one of these will   */
  /* be visible at a time.        */
  /*------------------------------*/

  c = HiddenGroup (stdQueryGroup, 0, 0, NULL);

  GetPosition (pFormInfo->acceptButton, &r);
  SelectFont (programFont);
  wid = r.right - (StringWidth ("From:") + StringWidth ("To:")) - 30;
  SelectFont (systemFont);
  wid /= 2 * stdCharWidth;

  pFormInfo->termGroup = HiddenGroup (c, 2, 0, NULL);
  StaticPrompt (pFormInfo->termGroup, "Term:", 0, dialogTextHeight, programFont, 'l');
  pFormInfo->termText = DialogText (pFormInfo->termGroup, "", 20, TextAction);
  SetObjectExtra (pFormInfo->termText, pFormInfo, NULL);

  pFormInfo->rangeGroup = HiddenGroup (c, 4, 0, NULL);
  StaticPrompt (pFormInfo->rangeGroup, "From:", 0, dialogTextHeight, programFont, 'l');
  pFormInfo->fromText = DialogText (pFormInfo->rangeGroup, "", wid, FromTextAction);
  pFormInfo->isValidFrom = FALSE;
  SetObjectExtra (pFormInfo->fromText, pFormInfo, NULL);

  StaticPrompt (pFormInfo->rangeGroup, "To:", 0, dialogTextHeight, programFont, 'l');
  pFormInfo->toText = DialogText (pFormInfo->rangeGroup, "", wid, ToTextAction);
  pFormInfo->isValidTo = FALSE;
  SetObjectExtra (pFormInfo->toText, pFormInfo, NULL);
  Hide (pFormInfo->rangeGroup);

  pFormInfo->taxLineagePopup = PopupList (c, TRUE, ChangeTaxParents_Callback);
  Hide (pFormInfo->taxLineagePopup);
  SetObjectExtra (pFormInfo->taxLineagePopup, pFormInfo, NULL);
  pFormInfo->taxStrings = NULL;

  /*------------------------------------*/
  /* Create the Avail and Chosen panels */
  /*------------------------------------*/

  c = HiddenGroup (stdQueryGroup, -1, 0, NULL);
  ppt1 = StaticPrompt (c, "Term Selection", 0, 0, programFont, 'c');
  pFormInfo->availDoc = DocumentPanel (c, 10 * stdCharWidth, 7 * stdLineHeight);
  SetObjectExtra (pFormInfo->availDoc, pFormInfo, NULL);

  ppt2 = StaticPrompt (c, "Query Refinement", 0, 0, programFont, 'c');
  j = HiddenGroup (c, 0, 0, NULL);
  pFormInfo->chosenDoc = DocumentPanel (j, 10 * stdCharWidth, 7 * stdLineHeight);
  SetObjectExtra (pFormInfo->chosenDoc, pFormInfo, NULL);

  /*-------------------*/
  /* Align the objects */
  /*-------------------*/

  AlignObjects (ALIGN_CENTER, (HANDLE) pFormInfo->taxLineagePopup, (HANDLE) pFormInfo->termGroup, NULL);
  AlignObjects (ALIGN_MIDDLE, (HANDLE) pFormInfo->taxLineagePopup, (HANDLE) pFormInfo->termGroup, (HANDLE) pFormInfo->rangeGroup, NULL);
  AlignObjects (ALIGN_RIGHT,
                (HANDLE) pFormInfo->acceptButton,
                (HANDLE) pFormInfo->termText,
                (HANDLE) pFormInfo->availDoc, (HANDLE) pFormInfo->chosenDoc, (HANDLE) pFormInfo->toText, (HANDLE) ppt1, (HANDLE) ppt2, NULL);

  /*---------------------------------*/
  /* Create a group for the Retrieve */
  /* and Reset buttons.              */
  /*---------------------------------*/

  c = HiddenGroup (stdQueryGroup, 5, 0, NULL);
  SetGroupSpacing (c, 10, 10);

  pFormInfo->retrieveButton = PushButton (c, "Retrieve 000000000 Documents", pFormInfo->retrieveDocsProc);
  SetObjectExtra (pFormInfo->retrieveButton, pFormInfo, NULL);
  SetTitle (pFormInfo->retrieveButton, "Retrieve 0 Documents");
  Disable (pFormInfo->retrieveButton);

  pFormInfo->resetButton = PushButton (c, "Reset", Reset_Callback);
  SetObjectExtra (pFormInfo->resetButton, pFormInfo, NULL);

  /*----------------------------------------*/
  /* Initialize the Avail and Chosen panels */
  /*----------------------------------------*/

  InitChosenPanel (pFormInfo);
  InitAvailPanel (pFormInfo);

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return stdQueryGroup;
}

/*==================================================================*/
/*                                                                  */
/*  CreateTermlistForm() -                                          */
/*                                                                  */
/*     The object hierarchy is:                                     */
/*                                                                  */
/*        MainWindow                                                */
/*           MainGroup                                              */
/*                                                                  */
/*              PulldownMenus                                       */
/*                                                                  */
/*              StdQueryGroup                                       */
/*                 EnumListGroup                                    */
/*                    DbPopup                                       */
/*                    FieldPopup                                    */
/*                    ModePopup(s)                                  */
/*                    AcceptButton                                  */
/*                 MiscGroup                                        */
/*                    TermDialogText                                */
/*                    FromDialogText                                */
/*                    ToDialogText                                  */
/*                    TaxLineagePopup                               */
/*                 AvailGroup                                       */
/*                    AvailPrompt                                   */
/*                    AvailPanel                                    */
/*                 ChosenGroup                                      */
/*                    ChosenPrompt                                  */
/*                    ChosenPanel                                   */
/*                 ButtonGroup                                      */
/*                    RetrieveButton                                */
/*                    ResetButton                                   */
/*                                                                  */
/*              AdvQueryGroup                                       */
/*                 AdvQueryTextBox                                  */
/*                 ButtonGroup                                      */
/*                    EvaluateButton                                */
/*                    RetrieveButton                                */
/*                    ResetButton                                   */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN ForM CreateTermlistForm (
  Int2 left,
  Int2 top,
  CharPtr title,
  WndActnProc activateCallback,
  FormMessageFunc messagesCallback,
  E2RetrieveDocsProc retrieveCallback,
  E2RetrieveUidProc retrieveUidCallback,
  Boolean explodeToggle,
  Boolean advancedQueryToggle
)

{
  WindoW       mainWindow;
  GrouP        mainGroup;
  FormInfoPtr  pFormInfo;

  /*-----------------------------------*/
  /* Create a formInfo structure to be */
  /* filled in by this function.       */
  /*-----------------------------------*/

  pFormInfo = (FormInfoPtr) MemNew (sizeof (FormInfo));
  if (pFormInfo == NULL) return NULL;

  /*-----------------------------*/
  /* Fill in some default values */
  /*-----------------------------*/

  pFormInfo->advQueryLexPos = 0;
  pFormInfo->advQueryLexStr = NULL;
  pFormInfo->advQueryLexState = LEXSTATE_IDLE;
  pFormInfo->advQueryNextToken = NULL;
  pFormInfo->form = NULL;
  pFormInfo->termList = NULL;

  /*----------------------------*/
  /* Create the Main Window and */
  /* set up basic callbacks.    */
  /*----------------------------*/

  mainWindow = FixedWindow (left, top, -10, -10, title, StdSendCloseWindowMessageProc);
  if (mainWindow == NULL) return NULL;

  pFormInfo->formmessage = EditMessage_Callback;
  pFormInfo->appmessage = messagesCallback;
  pFormInfo->activate = activateCallback;
  pFormInfo->retrieveDocsProc = retrieveCallback;
  pFormInfo->retrieveUidProc = retrieveUidCallback;
  SetActivate (mainWindow, TermListActivate_Callback);

  /*---------------------------------------*/
  /* Set up the menus and an overall group */
  /* for everything in the termlist window */
  /*---------------------------------------*/

  SetupMenus (mainWindow, pFormInfo, explodeToggle, advancedQueryToggle);

  mainGroup = HiddenGroup (mainWindow, 0, 0, NULL);

  pFormInfo->stdQueryGroup = SetupStdQueryGroup (pFormInfo, mainGroup);
  pFormInfo->advQueryGroup = SetupAdvQueryGroup (pFormInfo, mainGroup);

  AlignObjects (ALIGN_RIGHT, (HANDLE) pFormInfo->availDoc, (HANDLE) pFormInfo->advQueryText, NULL);

  /*----------------------------------*/
  /* Show the advanced query group if */
  /* we're in advanced query mode.    */
  /*----------------------------------*/

  if (advancedQueryToggle == TRUE)
    SafeHide (pFormInfo->stdQueryGroup);
  else
    SafeHide (pFormInfo->advQueryGroup);

  /*--------------------------------------*/
  /* Select the default starting database */
  /*--------------------------------------*/

  ChangeDatabase (pFormInfo->databasePopup);

  /*--------------------------------*/
  /* Fill in the FormInfo structure */
  /*--------------------------------*/

  pFormInfo->form = (ForM) mainWindow;

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  SetObjectExtra ((ForM) mainWindow, (BaseFormPtr) pFormInfo, TermListCleanup_Callback);

  RealizeWindow (mainWindow);
  SetWindowTimer (mainWindow, AvailTimerProc);

  return pFormInfo->form;
}

/*==================================================================*/
/*                                                                  */
/* Query_GetInfo () - Connect to the Entrez2 server and get DB      */
/*                   info -- db names, field names, etc.            */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Entrez2InfoPtr Query_GetInfo (void)

{
  Entrez2RequestPtr  e2rq;
  Entrez2ReplyPtr    e2ry;
  FILE               *fp;
  ValNodePtr         head = NULL;
  Char               path [PATH_MAX];
  CharPtr            str;
  ValNodePtr         vnp;

  /*---------------------------------*/
  /* Only query the server once, use */
  /* stored version afterwards.      */
  /*---------------------------------*/

  if (s_masterE2ip != NULL) return s_masterE2ip;

  /*----------------------------------*/
  /* Request the data from the server */
  /*----------------------------------*/

  e2rq = EntrezCreateGetInfoRequest ();
  if (e2rq == NULL) return NULL;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2rq);

  e2ry = SpecialEntrezSynchronousQuery (e2rq);
  if (e2ry == NULL) return NULL;

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ry);

  s_masterE2ip = EntrezExtractInfoReply (e2ry);

  Entrez2RequestFree (e2rq);

  /*------------------------------*/
  /* Validate EntrezInfo contents */
  /*------------------------------*/

  if (! ValidateEntrez2InfoPtrExEx (s_masterE2ip, &head, TRUE, TRUE)) {
    TmpNam (path);
    fp = FileOpen (path, "w");
    if (fp != NULL) {
      fprintf (fp, "Entrez2Info Validation Errors\n\n");
      for (vnp = head; vnp != NULL; vnp = vnp->next) {
        str = (CharPtr) vnp->data.ptrvalue;
        if (str == NULL) continue;
        fprintf (fp, "%s\n", str);
      }
      FileClose (fp);
      LaunchGeneralTextViewer (path, "ValidateEntrez2InfoPtr");
    }
    FileRemove (path);
    ValNodeFreeData (head);
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return s_masterE2ip;
}

/*==================================================================*/
/*                                                                  */
/*  TermlistToRequestCloseGroup () - Builds a boolean request from  */
/*                           the info in the given TermFormPtr.     */
/*                                                                  */
/*==================================================================*/

static Boolean TermlistToRequestCloseGroup (FormInfoPtr pFormInfo, Entrez2RequestPtr e2RequestPtr)

{
  Int2                 group;
  Int2                 last;
  StateDataPtr         sdp;
  Boolean              isEmpty = TRUE;
  Boolean              doNotExplode;
  Boolean              doNotTranslate = FALSE;
  CharPtr              rangeStr;
  CharPtr              fromTerm;
  CharPtr              toTerm;
  Entrez2FieldInfoPtr  fieldInfo;
  CharPtr              dbName;

  group = 0;
  last = 0;
  doNotExplode = ! GetStatus (pFormInfo->explodeItem);
  sdp = pFormInfo->termList;

  /* Loop through all the terms in the linked list */

  for (sdp = pFormInfo->termList; sdp != NULL; sdp = sdp->next) {

    /* Do not translate if DB is PubMed */
    /* and the field is ALL.            */

    fieldInfo = FieldGetInfo (sdp->db, sdp->fld);
    dbName = DBGetNameFromID (sdp->db);
    
    if ((StrICmp(dbName, "PubMed") == 0) && 
         (StrICmp(fieldInfo->field_name, "ALL") == 0))
      doNotTranslate = doNotExplode; /* doNotTranslate = TRUE; */
    else
      doNotTranslate = doNotExplode;

    if (sdp->group == GROUP_SINGLE || sdp->group == GROUP_FIRST)
      group++;
    if (sdp->state == STATE_ON) {
      isEmpty = FALSE;

      /* Add opening parens at beginning */

      if (last == 0) {
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_LEFT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_LEFT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
      }

      /* Put an 'OR' operator between groups */

      else if (last == group) {
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_OR,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
      }

      /* Put 'BUTNOT' operator where requested */

      else if (sdp->above == ENTREZ_OP_BUTNOT) {
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RIGHT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RIGHT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_BUTNOT,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_LEFT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_LEFT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
      }

      /* Otherwise default operator is 'AND' */

      else {
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RIGHT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_AND,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_LEFT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
      }

      /* Add in the term itself */

      if (sdp->key == NULL) {

        /* If it is a range, then split out the to and from */

        if (StringChr (sdp->term, ':') != 0) {
          rangeStr = (CharPtr) MemNew (strlen (sdp->term) + 1);
          StringCpy (rangeStr, sdp->term);
          fromTerm = StringTokMT(rangeStr, ":", &rangeStr);
          toTerm = StringTokMT(rangeStr, ":", &rangeStr);
          if ((fromTerm != NULL) && (toTerm != NULL)) {
            EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, sdp->field,
                                       fromTerm, NULL, 0, 0, NULL, NULL,
                                       doNotExplode, doNotTranslate);
            EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RANGE,
                                       NULL, NULL, NULL, 0, 0, NULL, NULL,
                                       doNotExplode, doNotTranslate);
            EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, sdp->field,
                                       toTerm, NULL, 0, 0, NULL, NULL,
                                       doNotExplode, doNotTranslate);
          }
          MemFree (rangeStr);
        }
        else
          EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, sdp->field,
                                     sdp->term, NULL, 0, 0, NULL, NULL,
                                     doNotExplode, doNotTranslate);
      } else {
        EntrezAddToBooleanRequest (e2RequestPtr, NULL, 0, NULL, NULL,
                                   sdp->key, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
        EntrezSetUseHistoryFlag (e2RequestPtr);
      }
      last = group;
    }
  }

  if (isEmpty == TRUE) return FALSE;

  /* Add on the closing parens */

  if (group > 0 && last > 0) {
    EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RIGHT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
    EntrezAddToBooleanRequest (e2RequestPtr, NULL, ENTREZ_OP_RIGHT_PAREN,
                                   NULL, NULL, NULL, 0, 0, NULL, NULL,
                                   doNotExplode, doNotTranslate);
  }

  /* Return successfully */

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  Query_FetchUIDs () - Use the linked list of terms stored in     */
/*                      the TermFormPtr to generate a query that    */
/*                      returns all matching UIDs.                  */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Entrez2BooleanReplyPtr Query_FetchUIDs (ForM f)

{
  CharPtr                 dbName;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  FormInfoPtr             pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*------------------------------------*/
  /* Make sure that we have at hand all */
  /* the ingredients for the query.     */
  /*------------------------------------*/

  if (pFormInfo->termList == NULL || pFormInfo->chosenNumLines < 1 || pFormInfo->termList->db < 0) return 0;

  /*--------------------------------*/
  /* Get the name of the current DB */
  /*--------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  if (StringHasNoText (dbName)) return 0;

  /*---------------------------------*/
  /* Create an empty Boolean request */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateBooleanRequest (TRUE, FALSE, dbName, NULL, 0, 0, NULL, 0, 0);
  if (e2RequestPtr == NULL) return 0;

  /*------------------------------------*/
  /* Convert the linked list of boolean */
  /* terms into a boolean request and   */
  /* send the request to the server.    */
  /*------------------------------------*/

  if (! TermlistToRequestCloseGroup (pFormInfo, e2RequestPtr)) return 0;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);
  if (e2ReplyPtr == NULL) return 0;

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  /*----------------------------------*/
  /* Parse the count out of the reply */
  /*----------------------------------*/

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  Entrez2RequestFree (e2RequestPtr);

  return e2BooleanPtr;
}

/*==================================================================*/
/*                                                                  */
/*  Query_FetchSeveralCounts () -                                   */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Entrez2TermListPtr Query_FetchSeveralCounts (CharPtr dbName, CharPtr fieldName, CharPtr searchStr, Int2 count)

{
  Entrez2RequestPtr   e2RequestPtr;
  Entrez2ReplyPtr     e2ReplyPtr;
  Entrez2TermListPtr  e2TermListPtr;
  Int4                termPos;

  /*-----------------------------------------*/
  /* Find the position of the requested term */
  /*-----------------------------------------*/

  e2RequestPtr = EntrezCreateGetTermPositionRequest (dbName, fieldName, searchStr);
  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return NULL;

  termPos = EntrezExtractTermPosReply (e2ReplyPtr);

  /*---------------------------------*/
  /* Retrieve the requested term and */
  /* several following terms.        */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateGetTermListRequest (dbName, fieldName, termPos, count);
  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return NULL;

  e2TermListPtr = EntrezExtractTermListReply (e2ReplyPtr);

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return e2TermListPtr;
}

/*==================================================================*/
/*                                                                  */
/*  Query_FetchCount () -                                           */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Int4 Query_FetchCount (ForM f)

{
  CharPtr                 dbName;
  Int4                    count;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  FormInfoPtr             pFormInfo;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*------------------------------------*/
  /* Make sure that we have at hand all */
  /* the ingredients for the query.     */
  /*------------------------------------*/

  if (pFormInfo->termList == NULL || pFormInfo->chosenNumLines < 1 || pFormInfo->termList->db < 0) return 0;

  /*--------------------------------*/
  /* Get the name of the current DB */
  /*--------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  if (StringHasNoText (dbName)) return 0;

  /*---------------------------------*/
  /* Create an empty Boolean request */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateBooleanRequest (FALSE, FALSE, dbName, NULL, 0, 0, NULL, 0, 0);
  if (e2RequestPtr == NULL) return 0;

  /*------------------------------------*/
  /* Convert the linked list of boolean */
  /* terms into a boolean request and   */
  /* send the request to the server.    */
  /*------------------------------------*/

  if (! TermlistToRequestCloseGroup (pFormInfo, e2RequestPtr)) return 0;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return 0;

  /*----------------------------------*/
  /* Parse the count out of the reply */
  /*----------------------------------*/

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);
  if (e2BooleanPtr == NULL) return 0;

  count = e2BooleanPtr->count;

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  Entrez2BooleanReplyFree (e2BooleanPtr);
  Entrez2RequestFree (e2RequestPtr);

  return count;
}

/*==================================================================*/
/*                                                                  */
/*  RemoveExtraQuotes () -                                          */
/*                                                                  */
/*==================================================================*/

static CharPtr RemoveExtraQuotes (CharPtr origString)

{
  Int2     charNum = 0;
  Int2     length;
  CharPtr  newString;
  Int2     newCharNum = 0;

  length = StringLen (origString);
  newString = MemNew (length + 1);
  for (charNum = 0; charNum < length; charNum++) {
    if (origString [charNum] != '"') {
      newString [newCharNum] = origString [charNum];
      newCharNum++;
    }
  }

  return newString;
}

/*==================================================================*/
/*                                                                  */
/*  Query_FetchParsedCount () -                                     */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Int4 Query_FetchParsedCount (ForM f)

{
  CharPtr                 dbName;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Int4                    count;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  FormInfoPtr             pFormInfo;
  Entrez2BooleanTermPtr   tmpTerm;
  StateDataPtr            currentTerm;
  Boolean                 found;
  ValNodePtr              valNodeTermList;
  CharPtr                 cleanedUpTerm;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*------------------------------------*/
  /* Make sure that we have at hand all */
  /* the ingredients for the query.     */
  /*------------------------------------*/

  if (pFormInfo->termList == NULL ||
      pFormInfo->chosenNumLines < 1 ||
      pFormInfo->termList->db < 0)
    return -1;

  /*--------------------------------*/
  /* Get the name of the current DB */
  /*--------------------------------*/

  dbName = DBGetNameFromID (pFormInfo->currDb);
  if (StringHasNoText (dbName)) return -1;

  /*---------------------------------*/
  /* Create an empty Boolean request */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateBooleanRequest (FALSE, TRUE, dbName, NULL,
                                             0, 0, NULL, 0, 0);
  if (e2RequestPtr == NULL) return -1;

  /*------------------------------------*/
  /* Convert the linked list of boolean */
  /* terms into a boolean request and   */
  /* send the request to the server.    */
  /*------------------------------------*/

  if (! TermlistToRequestCloseGroup (pFormInfo, e2RequestPtr)) return -1;

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return -1;

  /*--------------------------------*/
  /* Parse the counts for each term */
  /* out of the reply.              */
  /*--------------------------------*/

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);
  if (e2BooleanPtr == NULL) return -1;

  count = e2BooleanPtr->count;

  if (e2BooleanPtr->query != NULL) {
    valNodeTermList = e2BooleanPtr->query->exp;

    while (valNodeTermList != NULL) {
      if (valNodeTermList->choice == 3) {
        tmpTerm = (Entrez2BooleanTermPtr) valNodeTermList->data.ptrvalue;
        cleanedUpTerm = RemoveExtraQuotes (tmpTerm->term);
        StringCpy (tmpTerm->term, cleanedUpTerm);
        currentTerm = pFormInfo->termList;
        found = FALSE;
        while ((currentTerm != NULL) && (! found)) {
          if ((StrICmp (currentTerm->field, tmpTerm->field) == 0) &&
              (StrICmp (currentTerm->term, tmpTerm->term) == 0)) {
            currentTerm->count = tmpTerm->term_count;
            found = TRUE;
          }
          currentTerm = currentTerm->next;
        }
      }
      valNodeTermList = valNodeTermList->next;
    }
  }

  /*----------------------------------*/
  /* Clean up and return successfully */
  /*----------------------------------*/

  Entrez2BooleanReplyFree (e2BooleanPtr);
  Entrez2RequestFree (e2RequestPtr);

  return count;
}

/*==================================================================*/
/*                                                                  */
/*  Query_GetTranslatedCount () -                                   */
/*                                                                  */
/*==================================================================*/

static Int4 Query_GetTranslatedTermCount (FormInfoPtr pFormInfo, CharPtr dbName, CharPtr fieldName, CharPtr term)

{
  Char                displayStr [256];
  Entrez2RequestPtr   e2RequestPtr;
  Entrez2ReplyPtr     e2ReplyPtr;
  Entrez2TermListPtr  e2TermListPtr;
  Int2                row;
  Int4                termPos;
  Entrez2TermPtr      termPtr;

  /*-----------------------------------------*/
  /* Find the position of the requested term */
  /*-----------------------------------------*/

  e2RequestPtr = EntrezCreateGetTermPositionRequest (dbName, fieldName, term);

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return -1;

  termPos = EntrezExtractTermPosReply (e2ReplyPtr);

  /*---------------------------------*/
  /* Retrieve the requested term and */
  /* several following terms.        */
  /*---------------------------------*/

  e2RequestPtr = EntrezCreateGetTermListRequest (dbName, fieldName, termPos, AVAIL_WINDOW_ROWS);

  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return -1;

  e2TermListPtr = EntrezExtractTermListReply (e2ReplyPtr);

  if (e2TermListPtr == NULL) return -1;

  pFormInfo->availItem = 0;
  pFormInfo->availRow = 0;
  Reset (pFormInfo->availDoc);
  SetDocCache (pFormInfo->availDoc, NULL, NULL, NULL);
  Update ();

  for (row = 1, termPtr = e2TermListPtr->list; row <= AVAIL_WINDOW_ROWS && termPtr != NULL; row++, termPtr = termPtr->next) {
    sprintf (displayStr, "%s\t%ld\t%d\n", termPtr->term, (long) (termPtr->count), (int) (termPtr->is_leaf_node ? 1 : 0));
    AppendText (pFormInfo->availDoc, displayStr, &availParFmt, availColFmt, systemFont);
  }

  InvalDocument (pFormInfo->availDoc);
  Update ();

  if (e2TermListPtr != NULL &&  e2TermListPtr->list != NULL) {
    if (StringICmp (e2TermListPtr->list->term, term) == 0) return e2TermListPtr->list->count;
  }

  return -1;
}

/*==================================================================*/
/*                                                                  */
/*  Query_TranslateAndAddBoolTerm () -                              */
/*                      Have the server translate a single term     */
/*                      into multiple terms, each with their own    */
/*                      count. Then add each of these terms to the  */
/*                      the termlist (in an OR'd group) and display */
/*                      them.                                       */
/*                                                                  */
/*==================================================================*/

static Boolean Query_TranslateAndAddBoolTerm (
  ForM    f,
  Int2    currDb,
  Int2    currFld,
  CharPtr strs,
  Int2    state,
  Int4    num
)

{
  StateDataPtr            sdp = NULL;
  Entrez2RequestPtr       e2RequestPtr;
  Entrez2ReplyPtr         e2ReplyPtr;
  Entrez2BooleanReplyPtr  e2BooleanPtr;
  CharPtr                 dbName;
  Entrez2BooleanTermPtr   tmpTerm;
  ValNodePtr              valNodeTermList;
  CharPtr                 cleanedUpTerm;
  Boolean                 firstTerm;
  StateDataPtr            prev = NULL;
  CharPtr                 fieldName;
  Int2                    fieldId;
  Entrez2FieldInfoPtr     fieldInfo;
  FormInfoPtr             pFormInfo;
  Int2                    nextOperator = ENTREZ_OP_NONE;
  Int2                    nextGroup;
  Int2                    tmpOp;
  Boolean                 allowDuplicates;
  Boolean                 doNotTranslate;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*-------------------------------------------*/
  /* Get the names of the current db and field */
  /*-------------------------------------------*/

  dbName = DBGetNameFromID (currDb);
  if (StringHasNoText (dbName)) return FALSE;

  fieldInfo = FieldGetInfo (currDb, currFld);
  if (fieldInfo == NULL) return FALSE;

  /*------------------------------------------*/
  /* Allow duplicates if the DB is PubMed and */
  /* the field is All.                        */
  /*------------------------------------------*/

  if ((StringICmp (dbName, "PubMed") == 0) &&
      (StringICmp (fieldInfo->field_name, "All") == 0))
    allowDuplicates = TRUE;
  else
    allowDuplicates = FALSE;

  /*-----------------------------------*/
  /* Create and send a boolean request */
  /* to return a translated list       */
  /* of terms for the given term.      */
  /*-----------------------------------*/

  doNotTranslate = TRUE;
  e2RequestPtr = EntrezCreateBooleanRequest (FALSE, TRUE, dbName, NULL, 0, 0, NULL, 0, 0);
  EntrezAddToBooleanRequest (e2RequestPtr, strs, 0, NULL, NULL, NULL, 0, 0, NULL, NULL, TRUE, doNotTranslate);
  if (ShowASN () == TRUE)
    DisplayEntrezRequest (e2RequestPtr);

  e2ReplyPtr = SpecialEntrezSynchronousQuery (e2RequestPtr);

  if (ShowASN () == TRUE)
    DisplayEntrezReply (e2ReplyPtr);

  if (e2ReplyPtr == NULL) return FALSE;

  e2BooleanPtr = EntrezExtractBooleanReply (e2ReplyPtr);
  if (e2BooleanPtr == NULL) return FALSE;

  if (e2BooleanPtr->query == NULL) return FALSE;

  /*------------------------------------------------*/
  /* Parse the resulting terms out of the query and */
  /* add them one at a time to the current list of  */
  /* terms and to the chosen window.                */
  /*------------------------------------------------*/

  valNodeTermList = e2BooleanPtr->query->exp;

  firstTerm = TRUE;
  nextGroup = GROUP_SINGLE;
  while (valNodeTermList != NULL) {
    if (valNodeTermList->choice == TERMLIST_OPERATOR) {
      tmpOp = (Int2) valNodeTermList->data.intvalue;
      switch (tmpOp) {
        case ENTREZ_OP_AND:
          if (sdp != NULL) {
            if (sdp->group == GROUP_MIDDLE) {
              sdp->group = GROUP_LAST;
            } else if (sdp->group == GROUP_FIRST) {
              sdp->group = GROUP_SINGLE;
            }
          }
          nextOperator = tmpOp;
          nextGroup = GROUP_SINGLE;
          break;
        case ENTREZ_OP_OR:
        case ENTREZ_OP_BUTNOT:
          nextOperator = tmpOp;
          break;
        case ENTREZ_OP_LEFT_PAREN:
          nextGroup = GROUP_FIRST;
          break;
        case ENTREZ_OP_RIGHT_PAREN:
          break;
      }
    } else if (valNodeTermList->choice == TERMLIST_TERM) {

      /*---------------------*/
      /* Parse the term info */
      /*---------------------*/

      tmpTerm = (Entrez2BooleanTermPtr) valNodeTermList->data.ptrvalue;
      cleanedUpTerm = RemoveExtraQuotes (tmpTerm->term);
      StringCpy (tmpTerm->term, cleanedUpTerm);
      if (IsValidFieldName (currDb, tmpTerm->field) == FALSE)
        fieldName = FieldGetNameFromMenuName (currDb, tmpTerm->field);
      else
        fieldName = tmpTerm->field;

      fieldId = -1;
      if (StringDoesHaveText (fieldName)) {
        fieldId = FieldGetIDFromName (currDb, fieldName);
      }

      if (fieldId != -1) {

        /*-----------------------------------*/
        /* Replace -1 with actual term count */
        /*  Note -- this also creates the    */
        /*    desired 'flashing' effect in   */
        /*    the termlist window.           */
        /*-----------------------------------*/

        tmpTerm->term_count = Query_GetTranslatedTermCount (pFormInfo, dbName, fieldName, tmpTerm->term);

        /*-------------------------------*/
        /* Add the term to the term list */
        /*-------------------------------*/

        if (NULL != sdp)
          prev = sdp;
        sdp = CreateTerm (f, currDb, fieldId, tmpTerm->term, state,
                          tmpTerm->term_count, allowDuplicates);
        if (NULL == sdp) {
          valNodeTermList = valNodeTermList->next;
          sdp = prev;
          continue;
        }

        /*-------------------------------*/
        /* Make all the translated terms */
        /* an or'd group of terms.       */
        /*-------------------------------*/

        switch (nextGroup) {
          case GROUP_SINGLE:
            sdp->group = GROUP_SINGLE;
            break;
          case GROUP_FIRST:
            sdp->group = GROUP_FIRST;
            nextGroup = GROUP_MIDDLE;
            break;
          case GROUP_MIDDLE:
            sdp->group = GROUP_MIDDLE;
            break;
          case GROUP_LAST:
            sdp->group = GROUP_LAST;
            break;
        }

        if (firstTerm == TRUE) {
          prev = sdp->prev;
          if (prev != NULL) {
            sdp->above = ENTREZ_OP_AND;
            prev->below = ENTREZ_OP_AND;
          }
          firstTerm = FALSE;
        } else {
          prev = sdp->prev;
          sdp->above = nextOperator;
          prev->below = nextOperator;
        }

        /*--------------------------------------*/
        /* Display the term in the chosen panel */
        /*--------------------------------------*/

        DisplayTerm (pFormInfo, tmpTerm->term, fieldName, tmpTerm->term_count);

      } else {
        if (StringDoesHaveText (tmpTerm->field)) {
          Message (MSG_POSTERR, "Bad field name %s ignored in expanded query", tmpTerm->field);
        } else {
          Message (MSG_POSTERR, "Empty field name ignored in expanded query");
        }
      }
    }
    valNodeTermList = valNodeTermList->next;
  }

  /*---------------------------*/
  /* Mark the last term as the */
  /* last in the group.        */
  /*---------------------------*/

  if (NULL != sdp)
    if (sdp->group != GROUP_SINGLE) {
      if (sdp->group == GROUP_FIRST)
        sdp->group = GROUP_SINGLE;
      else
        sdp->group = GROUP_LAST;
    }

  /*--------------------------------*/
  /* Flatten out nested 'OR' groups */
  /*       (A OR (B OR C))          */
  /*   becomes                      */
  /*       (A OR B OR C)            */
  /*--------------------------------*/

  sdp = pFormInfo->termList;
  while (sdp != NULL) {
    if ((sdp->group == GROUP_FIRST) &&
        (sdp->above == ENTREZ_OP_OR) &&
        (sdp->below == ENTREZ_OP_OR))
      sdp->group = GROUP_MIDDLE;
    sdp = sdp->next;
  }

  /*------------------------*/
  /* Recalculate and redraw */
  /*------------------------*/

  AlphabetizeGroups (pFormInfo);
  RepopulateChosen (pFormInfo);
  RecalculateChosen (pFormInfo);

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return TRUE;
}

/*==================================================================*/
/*                                                                  */
/*  RefineUIDs () -                                                 */
/*                                                                  */
/*==================================================================*/

NLM_EXTERN Boolean RefineUIDs (ForM f, CharPtr term, Int4 num, Int4Ptr uids, Int2 db)

{
  CharPtr            advQueryStr;
  EnumFieldAssocPtr  alist;
  Entrez2InfoPtr     e2ip;
  FormInfoPtr        pFormInfo;
  Char               str [64];
  WindoW             tempPort;

  pFormInfo = (FormInfoPtr) GetObjectExtra (f);

  /*-------------------------------*/
  /* Set the query window settings */
  /*-------------------------------*/

  e2ip = Query_GetInfo ();
  if (e2ip == NULL) return FALSE;

  alist = CreateDatabaseAlist (e2ip);
  if (pFormInfo->currDb != db) {
    SetEnumPopup (pFormInfo->databasePopup, alist, (UIEnum) db);
    ChangeDatabase (pFormInfo->databasePopup);
  } else
    DoResetAvail (pFormInfo, TRUE);

  SafeSetTitle (pFormInfo->termText, "");
  SafeSetTitle (pFormInfo->fromText, "");
  SafeSetTitle (pFormInfo->toText, "");
  SafeDisable (pFormInfo->acceptButton);
  if (StringHasNoText (term))
    term = "*Current_Documents";
  if (term [0] == '*')
    term++;
  StringCpy (str, "*");
  StringNCpy (str + 1, term, sizeof (str) - 3);

  /*-----------------------------------*/
  /* Add the term to the current query */
  /* in the query window.              */
  /*-----------------------------------*/

  tempPort = SavePort (pFormInfo->chosenDoc);
  Query_AddUidsTerm (pFormInfo->form, str, num, uids, db);
  RecalculateChosen (pFormInfo);
  RestorePort (tempPort);

  if (! GetStatus (pFormInfo->advancedQueryItem)) {
    Show (pFormInfo->termText);
    Select (pFormInfo->termText);
  } else {
    advQueryStr = Query_ConvertToString (pFormInfo->form);
    SetAdvancedText (pFormInfo->advQueryText, advQueryStr);
    MemFree (advQueryStr);
    SafeShow (pFormInfo->advQueryGroup);
  }

  /*---------------------*/
  /* Return successfully */
  /*---------------------*/

  return TRUE;
}

