#include "ed.h"
#include "d1.h"
#include "rl.h"
#include "ru.h"
#include "assert.h"
#include "ruleset.h"
#include "RSreg.h"
#include "al.h"
#include "rs.h"
#include "verbrgy.h"
#include "MsgType.h"
#include "MTreg.h"
#include "FieldCore.h"
#include "FieldType.h"
#include "useful.h"
#include "ArgPack.h"
#include "bool.h"
#include "rem.h"
#include "childpack.h"
#include "d1Args.h"
#include "a1client.h"
#include "memory.h"
#include "xstuff.h"
#include "rebox.h"
#include "repop.h"

/*--------------------------------------------------*/
void ed_Init(rei)
reid rei;
{
  rei->WidgetKillList=Darray_create();
}
  
/*--------------------------------------------------*/
NORET ed_Done_CB(w,rei,caddb)
Widget w;
reid rei;
caddr_t caddb;
{
  Darray       AlActions;
  AlRuleMember AlMember;
  AlRule       Rule;

  AlActions=rl_TranslateActionsFromRE(rei,rei->Actions);
  AlMember=rl_TranslateFromRE(rei,rei->TopLevelMember);
  Rule=AlRule_create(AlMember,AlActions,0,0,(char*)NULL);
  assert(Rule);
  (rei->SaveFunc)(rei,Rule,rei->UserData);
  rei->Modified=Bool_FALSE;
}

/*--------------------------------------------------*/
NORET ed_Abort_CB(w,rei,caddb)
Widget w;
reid rei;
caddr_t  caddb;
{
  int ret;
  if (rei->Modified==Bool_TRUE) {
    ret=(int)repop_Query(w,"Rule Editor","\
The rule has been modified since it was last saved.\n\
Exit the ruleeditor anyway?",
		    "O.K.,Save then Exit,Cancel");
    if (ret==0) {
      (rei->QuitFunc)(rei,rei->UserData);
      return;
    }
    else if (ret==1) {
      ed_Done_CB(w,rei,caddb);
      (rei->QuitFunc)(rei,rei->UserData);
      return;
    }
    else 
      return;
  }
  else
    (rei->QuitFunc)(rei,rei->UserData);
}  

/*--------------------------------------------------*/
void ed_AddVerb_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  unsigned i,nargs;
  REAction REac;
  Darray Args=Darray_create();
  AlVerb Verb;
  char *buff;
  char *VerbName;
  reid rei;

  VerbName=(char*)RapPack_get(p,0);
  rei=(reid)RapPack_get(p,1);

  ed_ClearWidgetKillList(rei);
  Verb=AlVerbReg_get_obj(VerbName);
  nargs=AlVerb_def_args(Verb);
  for (i=0;i<nargs;i++) {
    buff=Memory_allocate(ARGUMENT_LENGTH+1);
    buff[0]='\0';
    Darray_addh(Args,buff);
  }
  REac=rl_REAction_create(rei,VerbName,Args);
  assert(REac);
  Darray_addh(rei->Actions,REac);
  d1_AddAction(rei,REac);

  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void ed_AddField_CB(w,p,caddb)
Widget w;
RapPack p;
caddr_t caddb;
{
  REMember REm;
  REField  REf;
  REMember TopMember, NewTopMember;
  unsigned i, l1;
  char *ftn;
  reid rei;

  ftn=(char*)RapPack_get(p,0);
  rei=(reid)RapPack_get(p,1);

  ed_ClearWidgetKillList(rei);
  /*
   * !!! This code should be replaces by a flagging this field as not being
   *  formally added to the rule because its not in a box.  For now though
   * we put it in a box on top, if none exists we create one.
   */
  if (rei->TopLevelMember!=NULL) {
    if (rei->TopLevelMember->Type!=ALRMT_ASSOC)  {
      NewTopMember=rl_REMember_create(ALRMT_ASSOC,
				      rl_REAssoc_create(rei,ALRAT_AND),
				      NULL);
      rei->TopLevelMember->father=NewTopMember;
      Darray_addh(NewTopMember->object.Assoc->Contents,rei->TopLevelMember);
      rei->TopLevelMember=NewTopMember;
    }
  }
  else {
    Al_fatal_error("ed.c:ed_AddField_CB: NULL top level member.\n");
  }
  REf=rl_REField_create(rei,ALRFT_MSG,"","",ftn,NULL,REF_FROM_USER);
  REm=rl_REMember_create(ALRMT_FIELD,REf,rei->TopLevelMember);
  if (rei->TopLevelMember==NULL)
    rei->TopLevelMember=REm;
  d1_If_AddField(rei,REm,REF_FROM_USER);

  rei->Modified=Bool_TRUE;
}
/*--------------------------------------------------*/
void ed_ClearWidgetKillList(rei)
reid rei;
{
  unsigned l1, i;
  /*
   *  destroy all widgets in kilkl list
   */
  l1=Darray_len(rei->WidgetKillList);
  if (l1!=0)  {
    for (i=0;i<l1;i++)
      XtDestroyWidget(Darray_remh(rei->WidgetKillList));
  }
}

/*--------------------------------------------------*/
void ed_AddMT_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  AlMsgType MT;
  Darray Keys=Darray_create();
  AlFieldCore FC;
  AlFieldType FT;
  unsigned i,l;
  REMember REm;
  REField  REf;
  REMember TopMember, NewTopMember; 
  char *FTN;
  const char *Key;
  char *MTN;
  reid rei;

  MTN=(char*)RapPack_get(p,0);
  rei=(reid)RapPack_get(p,1);

  MT=AlMTreg_get_obj_with_name(MTN);
  if (MT==NULL) {
    Al_warning1("Trying to add Message Type: %s which doesn't exist\n",MTN);
    Al_warning("  Probably because of nonexistant message type directory");
    Al_warning("  or bad path name in lens profile");
  }
  else {
    AlMsgType_all_fields(MT,Keys);
    l=Darray_len(Keys);
    for (i=0;i<l;i++) {
      FC=(AlFieldCore)Darray_get(Keys,i);
      assert(FC);
      Key=AlFieldCore_get_key(FC);
      if (ed_FieldExists(rei,Key)==Bool_FALSE) {
	FTN=AlFieldCore_get_field_type_name(FC);
	if (rei->TopLevelMember!=NULL) {
	  if (rei->TopLevelMember->Type!=ALRMT_ASSOC)  {
	    NewTopMember=rl_REMember_create(ALRMT_ASSOC,
					    rl_REAssoc_create(rei,ALRAT_AND),
					    NULL);
	    rei->TopLevelMember->father=NewTopMember;
	    Darray_addh(NewTopMember->object.Assoc->Contents,rei->TopLevelMember);
	    rei->TopLevelMember=NewTopMember;
	  }
	}
	REf=rl_REField_create(rei,
			      ALRFT_MSG,
			      AlFieldCore_get_key(FC),
			      "",
			      FTN,
			      NULL,
			      REF_FROM_TEMPLATE);
	REm=rl_REMember_create(ALRMT_FIELD,REf,rei->TopLevelMember);
	if (rei->TopLevelMember==NULL)
	  rei->TopLevelMember=REm;
	d1_If_AddField(rei,REm,REF_FROM_TEMPLATE);
	rei->Modified=Bool_TRUE;
      }
    }
  }
} 

/*--------------------------------------------------*/
Bool ed_FieldExists(rei,Key)
reid rei;
char *Key;
{
  return(ed_FE(rei->TopLevelMember,Key));
}
/*--------------------------------------------------*/
Bool ed_FE(rem,key)
REMember rem;
char *key;
{
  if (rem->Type==ALRMT_ASSOC) {
    int i,l;
    REAssoc rea=rem->object.Assoc;
    l=Darray_len(rea->Contents);
    for (i=0;i<l;i++) {
      if (ed_FE((REMember)Darray_get(rea->Contents,i),key)==Bool_TRUE)
	return(Bool_TRUE);
    }
    return(Bool_FALSE);
  }
  else {
    if (strcmp(rem->object.Field->Key,key)==0)
      return(Bool_TRUE);
    else
      return(Bool_FALSE);
  }
}

/*--------------------------------------------------*/
NORET ed_Destroy_all_empty_parent_boxes(rei,Parent)
reid rei;
REMember Parent;
{
  if (Parent==NULL)
    return;
  assert(Parent->Type==ALRMT_ASSOC);
  if (Darray_len(Parent->object.Assoc->Contents)==0 &&
      Parent!=rei->TopLevelMember) {
    REAssoc rea=Parent->object.Assoc;
    Widget PrevW, NextW, ParentW;
    rl_Get_surrounding_widgets(rei,rea->container,
			       &PrevW,&NextW,&ParentW);
    if (NextW!=NULL)
      Alxt_SetArg(NextW,XtNfromVert,(XtArgVal)PrevW);
    if (rea->ContainerW!=NULL)
      XtDestroyWidget(rea->ContainerW);
    ed_rl_Assoc_remove(rei,rea);
    ed_Destroy_all_empty_parent_boxes(rei,Parent->father);
  }
}

/*--------------------------------------------------*/
void ed_Field_delete_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  REField Current, Previous;
  Widget PrevW, NextW, CurrW, ParentW;
  REMember Prev,Next, Parent;
  unsigned i, l1, l2;
  Bool Found=Bool_FALSE;
  ArgPack AP;
  REField tf;

  REField f;
  REMember Crem, Prem, Nrem;
  reid rei;

  f=(REField)RapPack_get(p,0);
  rei=(reid)RapPack_get(p,1);

  assert(f);

  rl_Get_surrounding_members(rei,f->container,
			     &Prev,&Next,&Parent);
  rl_Get_surrounding_widgets(rei,f->container,
			     &PrevW,&NextW,&ParentW);
    
  if (NextW!=NULL)
    Alxt_SetArg(NextW,XtNfromVert,(XtArgVal)PrevW);
  Alxt_SetArg(f->ContainerW,XtNfromVert,(XtArgVal)NULL);
  
  XtUnmanageChild(f->ContainerW);
  Darray_addh(rei->WidgetKillList,f->ContainerW);
  ed_rl_Field_remove(rei,f);

  assert(f->container->father==Parent);
  /* check to see if the containing box has no children in it */
  ed_Destroy_all_empty_parent_boxes(rei,Parent);
  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void ed_rl_Field_remove(rei,f)
reid rei;
REField f;
{
  REMember rem=f->container;
  REMember Frem=rem->father;
  unsigned i,l1,l2;

  REField tf;

  if (Frem==NULL) {
    rei->TopLevelMember=NULL;
  }
  else {
    if (Frem->Type!=ALRMT_ASSOC) 
     Al_fatal_error("ruleeditor: in ed_rl_Field_remove: bad rule language.\n");
    rl_Darray_remove_ptr(Frem->object.Assoc->Contents,rem);
  }
  
  rl_REField_remove_address(f);
  rl_REMember_destroy(rem);
}

/*--------------------------------------------------*/
NORET ed_rl_Assoc_remove(rei,rea)
reid rei;
REAssoc rea;
{
  REMember rem;
  REMember Prem;

  rem=rea->container;
  Prem=rem->father;
  if (Prem==NULL) 
    rei->TopLevelMember=NULL;
  else 
    rl_Darray_remove_ptr(Prem->object.Assoc->Contents,rem);
  rl_REMember_destroy(rem);
}

/*--------------------------------------------------*/
void ed_Action_delete_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  REAction Prev, Next, Curr;
  Widget Prev1, Next1, Curr1;
  ArgPack AP;
  unsigned i, l1, l2;
  REArgument REarg;
  Bool Found=Bool_FALSE;

  REAction ta;
  REAction REac;
  reid rei;

  rei=(reid)RapPack_get(p,0);
  REac=(REAction)RapPack_get(p,1);
  
  l1=Darray_len(rei->Then_Contents);
  for (i=0;i<l1;i++) {
    if ((Curr=(REAction)Darray_get(rei->Then_Contents,i))==REac) {
      if (i==0)
	Prev1=NULL;
      else {
	Prev=(REAction)Darray_get(rei->Then_Contents,i-1);
	Prev1=Prev->ContainerW;
      }
      if (i==(l1-1))
	Next1=NULL;
      else {
	Next=(REAction)Darray_get(rei->Then_Contents,i+1);
	Next1=Next->ContainerW;
      }
      Found=Bool_TRUE;
      break;
    }
  }
  Curr1=Curr->ContainerW;
  if (Found==Bool_FALSE)
    Al_fatal_error((const char *)"ed_Action_delete_CB:couldn't find field");
  if (Next1!=NULL) {
    AP=ArgPack_create();
    ArgPack_add_arg(AP,XtNfromVert,(XtArgVal)Prev1);
    XtSetValues(Next1,ArgPack_the_args(AP),ArgPack_num_args(AP));
    ArgPack_delete(AP);
  }
  AP=ArgPack_create();
  ArgPack_add_arg(AP,XtNfromVert,(XtArgVal)NULL);
  XtSetValues(Curr1,ArgPack_the_args(AP),ArgPack_num_args(AP));
  ArgPack_delete(AP);

  XtUnmanageChild(Curr1);
  Darray_addh(rei->WidgetKillList,Curr1);

  rei->Then_Contents=rl_Darray_remove_ptr(rei->Then_Contents,REac);
  rei->Actions=rl_Darray_remove_ptr(rei->Actions,REac);
  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void ed_ChangeVerb_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  ArgPack AP;
  Widget w2;
  AlFieldCore FC;
  Bool Ret;
  char *currentVerbName, *string;
  AlVerb Verb;
  unsigned exists, goal, i;
  reid rei;
  REAction Action;
  char* name;

  rei=(reid)RapPack_get(p,0);
  name=RapPack_get(p,1);
  Action=(REAction)RapPack_get(p,2);
  
  assert (Action);
  Alxt_SetArg(w, XtNsetMark, (XtArgVal) TRUE);
  currentVerbName=Action->VerbName;

  w2=MenuPack_get_widget(Action->Menu,currentVerbName);
  if (w2!=NULL)
    Alxt_SetArg(w2, XtNsetMark, (XtArgVal) FALSE);
  else 
    printf("add routine to handle bad field type data\n");

  Memory_free(Action->VerbName);
  Action->VerbName=(char *)strcpy(Memory_allocate(strlen(name)+1),name);
  Action->Verb=AlVerbReg_get_obj(name);

  /* change the display name of the verb */
  Alxt_SetArg(Action->VerbW, XtNlabel, (XtArgVal) name);

  /*  reset the actual verb object and find out what the differences in the */
  /*  numver of arguments we have as compared to the number of arguments we */
  /*  want is */
  /*  Not that Verbs are not destroyed. */
  Verb=AlVerbReg_get_obj(name);
  assert(Verb);
  Action->Verb=Verb;
  exists=Darray_len(Action->Arguments);
  goal=AlVerb_def_args(Verb);

  if (goal<exists) {
    for (i=0;i<exists;i++)
      ed_BlankArgument(Action,i);
    ed_RemoveArgumentsToEnd(Action,goal);
  }
  else {
    for (i=0;i<exists;i++)
      ed_BlankArgument(Action,i);
    for (;i<goal;i++)
      ed_AddArgument(rei,Action,i);
  } 
  ed_UpdateAltsMenuSensitivity(Action);
  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void ed_RemoveArgumentsToEnd(REac,goal)
REAction REac;
unsigned goal;
{
  unsigned i;
  REArgument arg;
  for (i=Darray_len(REac->Arguments);i>goal;i--) {
    arg=(REArgument)Darray_remh(REac->Arguments);
    XtDestroyWidget(arg->MenuW);
    XtDestroyWidget(arg->ArgW);
    MenuPack_destroy(arg->Menu);
    Memory_free(arg);
  }  
}
  
/*--------------------------------------------------*/
void ed_BlankArgument(REac,index)
REAction REac;
unsigned index;
{
  REArgument REarg;
  XtTextBlock TextB;
  ArgPack AP;
/*  
  REarg=(REArgument)Darray_get(REac->Arguments,index);
  AP=ArgPack_create();
  ArgPack_add_arg(AP,XtNinsertPosition,(XtArgVal)0);
  XtSetValues(REarg->ArgW,ArgPack_the_args(AP),ArgPack_num_args(AP));
  ArgPack_delete(AP);
  TextB.firstPos=0;
  TextB.lastPos=ARGUMENT_LENGTH+1;
  TextB.ptr=Memory_allocate(ARGUMENT_LENGTH+1);
  TextB.ptr[0]='\0';
  XtTextReplace(REarg->ArgW,0,ARGUMENT_LENGTH+1,&TextB);
*/
}

/*--------------------------------------------------*/
void ed_AddArgument(rei,REac,index)
reid rei;
REAction REac;
unsigned index;
{
  REArgument Prev, REarg;
  
  if (Darray_len(REac->Arguments)==0 || index==0)
    Prev=NULL;
  else 
    Prev=(REArgument)Darray_get(REac->Arguments,index-1);

  REarg=rl_REArgument_create(rei,(char*)NULL,(REAction)NULL);
  d1_Argument_create(rei,REarg,Prev,REac);
  Darray_addh(REac->Arguments,REarg);
}

/*--------------------------------------------------*/
void ed_AddArg_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  ArgPack AP;
  unsigned nargs;
  REArgument REArg, PrevArg;
  Widget A2W, A3W;
  reid rei;
  REAction   REac;

  rei=(reid)RapPack_get(p,0);
  REac=(REAction)RapPack_get(p,2);

  nargs=Darray_len(REac->Arguments);
  if (nargs>=AlVerb_max_args(REac->Verb)) {
    Al_warning("Rule Editor:unable to add argument: already at max args.\n");
    return;
  }

  REArg=rl_REArgument_create(rei,(char*)NULL,REac);
  Darray_addh(REac->Arguments,REArg);

  if (nargs==0) 
    PrevArg=NULL;
  else 
    PrevArg=(REArgument)Darray_get(REac->Arguments,nargs-1);

  d1_Argument_create(rei,REArg,PrevArg,REac);
  ed_UpdateAltsMenuSensitivity(REac);
  rei->Modified=Bool_TRUE;
}

/*--------------------------------------------------*/
void ed_DeleteArg_CB(w,p,cadda)
Widget w;
RapPack p;
caddr_t cadda;
{
  REArgument CurrArg; 
  REArgument PrevArg, NextArg;
  Widget A2W, A3W, Next2W, Next3W;
  unsigned i,l, EditMenuOffset;
  int index;
  ArgPack AP;
  reid rei;
  REAction   REac;

  rei=(reid)RapPack_get(p,0);
  CurrArg=(REArgument)RapPack_get(p,1);
  REac=(REAction)RapPack_get(p,2);

  index=(-1);
  l=Darray_len(REac->Arguments);
  if (l<=AlVerb_min_args(REac->Verb)) {
   Al_warning("Rule Editor:unable to remove argument: already at min args.\n");
    return;
  }

  for (i=0;i<l;i++) {
    if (CurrArg==(REArgument)Darray_get(REac->Arguments,i)) {
      index=i;
      break;
    }
  }
  if (index==(-1))
    Al_fatal_error("ed.c:DeleteArg_CB: couldn't find the current REArgument.\n");
  else {
    if (index+1<l) {  /* is a next argument */
      if (index==0) {
	A2W=NULL;
	A3W=NULL;
	EditMenuOffset=2;
      }
      else {
	PrevArg=(REArgument)Darray_get(REac->Arguments,index-1);
	A2W=PrevArg->MenuW;
	A3W=PrevArg->ArgW;
	EditMenuOffset=8;
      }
      NextArg=(REArgument)Darray_get(REac->Arguments,index+1);
      Next2W=NextArg->MenuW;
      Next3W=NextArg->ArgW;

      AP=ArgPack_create();
      ArgPack_add_arg(AP,XtNfromVert,(XtArgVal) A2W);
      ArgPack_add_arg(AP,XtNfromHoriz,(XtArgVal) REac->VerbW);
      ArgPack_add_arg(AP,XtNvertDistance,(XtArgVal) EditMenuOffset);
      ArgPack_add_arg(AP,XtNhorizDistance, (XtArgVal) 4);
      XtSetValues(Next2W,ArgPack_the_args(AP),ArgPack_num_args(AP));
      ArgPack_delete(AP);

      AP=ArgPack_create();
      ArgPack_add_arg(AP,XtNfromVert,(XtArgVal) A3W);
      ArgPack_add_arg(AP,XtNfromHoriz,(XtArgVal) NextArg->MenuW); 
      if (PrevArg!=NULL)
	ArgPack_add_arg(AP,XtNvertDistance,(XtArgVal) 2);
      XtSetValues(Next3W,ArgPack_the_args(AP),ArgPack_num_args(AP));
      ArgPack_delete(AP);
    }
    XtUnmanageChild(CurrArg->MenuW);
    Darray_addh(rei->WidgetKillList,CurrArg->MenuW);
    XtUnmanageChild(CurrArg->ArgW);
    Darray_addh(rei->WidgetKillList,CurrArg->ArgW);
    REac->Arguments=rl_Darray_remove_ptr(REac->Arguments,CurrArg);
    ed_UpdateAltsMenuSensitivity(REac);
    rei->Modified=Bool_TRUE;
    /* !! should destroy the REArgument here */
  }
}

/*--------------------------------------------------*/
void ed_UpdateAltsMenuSensitivity(action)
REAction action;
{
  unsigned i,l;
  REArgument arg;
  int Add, Delete;
  AlVerb Verb;
  Widget w2;
  ArgPack AP;

  l=Darray_len(action->Arguments);
  Verb=action->Verb;
  if (l<AlVerb_max_args(Verb))
    Add=TRUE;
  else
    Add=FALSE;
  if (l>AlVerb_min_args(Verb))
    Delete=TRUE;
  else
    Delete=FALSE;

  for (i=0;i<l;i++) {
    arg=(REArgument)Darray_get(action->Arguments,i);
    w2=MenuPack_get_widget(arg->Menu,ARGUMENT_ADD_FUNCTION_MENU_NAME); 
    if (w2!=NULL) {
      AP=ArgPack_create();
      ArgPack_add_arg(AP,XtNsensitive,(XtArgVal) Add);
      XtSetValues(w2, ArgPack_the_args(AP), ArgPack_num_args(AP));
      ArgPack_delete(AP);
    }
    else 
      Al_warning("ed_UpdateAltsMenuSensitivity: couldn't find add widget.");

    w2=MenuPack_get_widget(arg->Menu,ARGUMENT_DEL_FUNCTION_MENU_NAME); 
    if (w2!=NULL) {
      AP=ArgPack_create();
      ArgPack_add_arg(AP,XtNsensitive,(XtArgVal) Delete);
      XtSetValues(w2, ArgPack_the_args(AP), ArgPack_num_args(AP));
      ArgPack_delete(AP);
    }
    else
      Al_warning("ed_UpdateAltsMenuSensitivity: couldn't find add widget.");
  }
}

/*                                                                         *
  *                                                                       *
  *=======================================================================*
  *                                                                       *
  *     The Boxer Routines                                                *
  *                                                                       *
  *=======================================================================*
  *                                                                       *
 *                                                                         */

/*--------------------------------------------------*/
NORET ed_AddBox_CB(w,rp,cadda)
Widget w;
RapPack rp;
caddr_t cadda;
{
  reid rei;

  rei=(reid)RapPack_get(rp,1);

  rebox_Set_callback_data(rei->OuterW,(VOIDP)rp);
  rebox_Activate(rei->OuterW);
  AlUI_AttachCursor(rei->OuterW,BOXER_LEFT_CURSOR); 
}

/*--------------------------------------------------*/
Bool ed_Boxer_get_members(the_fields,Started,rem,Parent,ParentIndex,
			  dis,DestWin,
			  sx,sy,fx,fy)
Darray the_fields;
Bool *Started;
REMember rem, *Parent;
int *ParentIndex;       /* the member index which is previous to the new box */
Display *dis;
Window DestWin;
int sx,sy,fx,fy;
{
  int i,l;
  REAssoc rea;
  REMember Nrem;

  if (*Started==Bool_FALSE) {
    if (point_contains_member(dis,DestWin,rem,sx,sy,fx,fy)) {
      *ParentIndex=rl_REMember_get_index(rem);
      RE_debug1("ParentIndex=%d\n",*ParentIndex);
      Darray_addh(the_fields,rem);
      *Parent=rem->father;
      *Started=Bool_TRUE;
    }

  /* once we get started we will simply get all members at one level.  Here */
  /* we do a depth first search of the tree untill we come across a member */
  /* in the box, then we switch over to the following else which just looks */
  /* for the end of the box. */

    if (rem->Type==ALRMT_ASSOC) {
      rea=rem->object.Assoc;
      l=Darray_len(rea->Contents);
      for (i=0;i<l;i++) {
	Nrem=(REMember)Darray_get(rea->Contents,i);
	if (ed_Boxer_get_members(the_fields,Started,Nrem,
				 Parent,ParentIndex,
				 dis,DestWin,sx,sy,fx,fy)==Bool_FALSE)
	  return(Bool_FALSE);
      }
    }
    return(Bool_TRUE);
  }
  else {
    if (point_contains_member(dis,DestWin,rem,sx,sy,fx,fy)) {
      if (*Parent==rem->father) {
	Darray_addh(the_fields,rem);
	return(Bool_TRUE);
      }
      else {
	*Started=Bool_FALSE;
	return(Bool_FALSE);
      }
    }
    else
      return(Bool_FALSE);
  }
}

/*--------------------------------------------------*/
NORET ed_Boxer_CB(rp,x,y,x2,y2)
RapPack rp;
int x,y,x2,y2;
{
  reid rei;
  AlRAType rat;
  int sx,sy,fx,fy,i,l,ParentIndex;
  Darray the_fields=Darray_create();
  Window DestWin;
  Display *dis;
  REMember rem, ParentMember, StartMember;
  REField ref;
  REAssoc rea;
  Bool Started;

  /* this rap pack is made in d1_FillBTBar */
  rat=(AlRAType)RapPack_get(rp,0);
  rei=(reid)RapPack_get(rp,1);

  dis=XtDisplay(rei->ShellW);
  sx=ed_min(x,x2);
  fx=ed_max(x,x2);
  sy=ed_min(y,y2);
  fy=ed_max(y,y2);

  DestWin=XtWindow(rei->OuterW);

  /* find the members contained by the box drawn on the rule editor */
  Started=Bool_FALSE;

  ParentMember=NULL;
  ed_Boxer_get_members(the_fields,&Started,rei->TopLevelMember,
		       &ParentMember,&ParentIndex,dis,DestWin,sx,sy,fx,fy);

  if (Started==Bool_TRUE && Darray_len(the_fields)!=0) {
    REAssoc Nrea;
    REMember Nrem, rem;
    Widget PrevW, NextW, ParentW, d1,d2;
    Darray OldWidgets=Darray_create();
    int l,i;
    ArgPack AP;
    
    rei->Modified=Bool_TRUE;
    rl_Get_surrounding_widgets(rei,(REMember)Darray_get(the_fields,0),
			       &PrevW,&NextW,&ParentW);
    if ((l=Darray_len(the_fields))!=1) {
      rl_Get_surrounding_widgets(rei,(REMember)Darray_get(the_fields,l-1),
				 &d1,&NextW,&d2);
      assert (d2==ParentW);
    }

#ifdef RE_DEBUG
    printf("\n ADDING A NEW BOX, parent index=%d \n",ParentIndex);
    for (i=0;i<l;i++) {
      rem=(REMember)Darray_get(the_fields,i);
      if (rem->Type==ALRMT_ASSOC)
	printf("adding box\n");
      else
	printf("adding field  %s\n",rem->object.Field->Key);
    }
#endif    

    Nrea=rl_REAssoc_create(rei,rat);
    Nrem=rl_REMember_create_at_index(ALRMT_ASSOC,Nrea,ParentMember,
				     ParentIndex);

    AP=ArgPack_duplicate_args(d1_AssocArgs);
    ArgPack_add_arg(AP,XtNfromVert,(XtArgVal) PrevW);
    ArgPack_add_arg(AP,XtNheight,(XtArgVal) fy-sy);
    Nrea->ContainerW=XtCreateWidget("assoc_container",
				    formWidgetClass,
				    ParentW,
				    ArgPack_the_args(AP),
				    ArgPack_num_args(AP));
    ArgPack_delete(AP);
    {
      AlRAType t=Nrea->Type;
      AP=ArgPack_duplicate_args(d1_AssocLabelArgs); 
      ArgPack_add_arg(AP,XtNlabel,
		      (XtArgVal) ((t==ALRAT_AND) ? "AND" :
				  ((t==ALRAT_OR) ? "OR" : "NOT")));
      ArgPack_add_arg(AP,XtNfont, (XtArgVal) 
		      AlUI_GetFont(ParentW,
				   (int)CONTROL_BUTTONS_FONT));
      Nrea->LabelW=XtCreateManagedWidget("assoc_labeel",
					 labelWidgetClass,
					 Nrea->ContainerW,
					 ArgPack_the_args(AP),
					 ArgPack_num_args(AP));
      ArgPack_delete(AP);
    }
    
    /* modify the actual rule to reflect the changes */
    ed_Boxer_modify_rule(rei,the_fields,ParentMember,ParentIndex,Nrem);

    /* replace all the old widgets with new ones, keeping track of the old */
    /* widgets to be killed */
    ed_Boxer_modify_screen(rei,the_fields,ParentMember,Nrem,OldWidgets);

    /* manage the new box, it should appear in front of the old members */
    XtManageChild(Nrea->ContainerW);

    /* the next widget should be linked to the new box */
    if (NextW!=NULL)
      Alxt_SetArg(NextW,XtNfromVert,(XtArgVal) Nrea->ContainerW);

    /* destroy all the old widgets (now behind the new box */
    l=Darray_len(OldWidgets);
    for (i=0;i<l;i++) 
      XtDestroyWidget((Widget)Darray_get(OldWidgets,i));
    
  }
}

/*--------------------------------------------------*/
Bool point_contains_member(dis,DestWin,rem,psx,psy,pfx,pfy)
Display *dis;
Window DestWin;
int psx,psy,pfx,pfy;
REMember rem;
{
  Window SrcWin, ChildWin;
  Widget container;
  int msx,msy,mfx,mfy;    /* the coordintes of the members container window */
			  /* with respect to the DestWin */
  int memberW, memberH;

  if (rem->Type==ALRMT_FIELD)  {
    REField ref=rem->object.Field;
    container=ref->ContainerW;
  }
  else{
    REAssoc rea=rem->object.Assoc;
    container=rea->ContainerW;
  }

  if (container==NULL) {
    RE_debug("in point_above_member: containerW==NULL\n");
    return(Bool_FALSE);
  }
  SrcWin=XtWindow(container);
  if (XTranslateCoordinates(dis,SrcWin,DestWin,0,0,
			    &msx,&msy,&ChildWin)==0) 
    Al_fatal_error("in point_above_member: fatal error in the extreme\n");
  memberW=(int)Alxt_get_widget_feature(container,XtNwidth);
  memberH=(int)Alxt_get_widget_feature(container,XtNheight);
  if (XTranslateCoordinates(dis,SrcWin,DestWin,memberW,memberH,
			    &mfx,&mfy,&ChildWin)==0) 
    Al_fatal_error("in point_above_member: fatal error in the extreme-2\n");

  /* give them some horizontal leaway */
  msx=msx+20;
  mfx=mfx-20; if (mfx<0) mfx=0;
  msy=msy+2;
  mfy=mfy-2;

  if (msx>psx && msy>psy && mfx<pfx && mfy<pfy )
    return(Bool_TRUE);
  else
    return(Bool_FALSE);
}


/*--------------------------------------------------*/
NORET ed_Boxer_modify_rule(rei,the_members,OldParent,OldIndex,NewParent)
reid rei;
Darray the_members;
REMember OldParent, NewParent;
int OldIndex;
{
  REAssoc Orea, Nrea;
  REMember Crem;
  int i,l;
  assert(OldParent->Type==ALRMT_ASSOC);
  assert(NewParent->Type==ALRMT_ASSOC);
  Orea=OldParent->object.Assoc;
  Nrea=NewParent->object.Assoc;

  l=Darray_len(the_members);
  for (i=0;i<l;i++) {
    Crem=(REMember)Darray_get(the_members,i);
    rl_Darray_remove_ptr(Orea->Contents,Crem);
    Crem->father=NewParent;
    Darray_addh(Nrea->Contents,Crem);
  }
}

/*--------------------------------------------------*/
NORET ed_Boxer_modify_screen(rei,the_members,OldParent,NewParent,ToKill)
reid rei;
Darray the_members, ToKill;
REMember OldParent, NewParent;
{
  int i,l;
  REMember rem;
  
  l=Darray_len(the_members);
  for (i=0;i<l;i++) {
    rem=(REMember)Darray_get(the_members,i);
    if (rem->Type==ALRMT_ASSOC) {
      RE_debug("adding assoc.\n");
      Darray_addh(ToKill,rem->object.Assoc->ContainerW);
      d1_If_AddAssoc(rei,rem);
    }
    else if (rem->Type==ALRMT_FIELD) {
      RE_debug1("adding field=%s\n",rem->object.Field->Key);
      Darray_addh(ToKill,rem->object.Field->ContainerW);
      d1_If_AddField(rei,rem,rem->object.Field->FromWhere);
    }
    else
      Al_fatal_error("file=ed.c ^^^7: bad REMember\n");
  }
}
  
