#include "menupackP.h"
#include "liintrins.h"
#include "ArgPack.h"
#include "al.h"
#include "useful.h"
#include "registry.h"
#include "assert.h"
#include "xstuff.h"
#include "sysdep.h"
#include "memory.h"
#include "alxms.h"
#include "liwidgets.h"
#include "debug.h"

/*+++++++ FILE FUNCTIONS +++++++*/
char * MakeMenuMgrName();

/*-------------------*/
MenuPack MenuPack_create(w, menuName, flags)
Widget w;
char *menuName;
unsigned long flags;
{
  MenuPack cp;
  char mppadd[30];
  cp=(MenuPack)Memory_allocate(sizeof(struct MenuPack_str));
  cp->parentW=w;
  cp->menuName=(char *)Memory_allocate(strlen(menuName)+1);
  strcpy(cp->menuName,menuName);  /* this will not be freed because it will */
  cp->MenuPane=NULL;
  cp->MList=Darray_create();
  cp->Instantiated=Bool_FALSE;
  cp->Flags=flags;
  sprintf(mppadd,"%x",cp);
  cp->MyAddress=strcpy(Memory_allocate(strlen(mppadd)+1),mppadd);
  return(cp);                     /*       be used by the widgets           */
}
/*-------------------*/
MenuPack MenuPack_create_pulldown(menubar,cascade, menuName)
Widget menubar,cascade;
char *menuName;
{
  MenuPack cp;

  cp=(MenuPack)Memory_allocate(sizeof(struct MenuPack_str));
  cp->parentW=cascade;
  cp->menuName=(char *)Memory_allocate(strlen(menuName)+1);
  strcpy(cp->menuName,menuName);  /* this will not be freed because it will */
  cp->MenuPane=NULL;
  cp->MList=Darray_create();
  cp->Instantiated=Bool_FALSE;
  cp->parentMenuBar=menubar;
  cp->Flags=MP_PULLDOWN;
  cp->MyAddress=NULL;
  return(cp);                     /*       be used by the widgets           */
}
/*-------------------*/
MenuPack MenuPack_create_option(parent, menuName)
Widget parent;
char *menuName;
{
  MenuPack cp;
  char mppadd[20];

  cp=(MenuPack)Memory_allocate(sizeof(struct MenuPack_str));
  cp->parentW=parent;
  cp->menuName=(char *)Memory_allocate(strlen(menuName)+1);
  strcpy(cp->menuName,menuName);  /* this will not be freed because it will */
  cp->MenuPane=NULL;
  cp->MList=Darray_create();
  cp->Instantiated=Bool_FALSE;
  cp->parentMenuBar=NULL;
  cp->Flags=MP_OPTION;
  sprintf(mppadd,"%x",cp);
  cp->MyAddress=strcpy(Memory_allocate(strlen(mppadd)+1),mppadd);
  return(cp);                     /*       be used by the widgets           */
}

/*-----------------------*/
void MenuPack_attach_to_widget(mp,w)
MenuPack mp;
Widget w;
{
  if (mp->Instantiated==Bool_TRUE)
    Al_fatal_error1("menupack: attempt to attach menu %s to 2 when its already created.\n", 
		    mp->menuName);
  else
    mp->parentW=w;
}
/*----------------*/
void MenuPack_destroy(m)
MenuPack m;
{
  MenuPackRec mpRec;
  unsigned i;
  if (m==NULL) return;
  for (i=0;i<Darray_len(m->MList);i++) {
    mpRec=(MenuPackRec)Darray_get(m->MList,i);
    if (mpRec->IsWidget==1 && mpRec->sub_menu!=NULL)
      MenuPack_destroy(mpRec->sub_menu);
    safe_free(mpRec->name);
    safe_free(mpRec);
  }
  safe_free(m->menuName);
  safe_free(m->MyAddress);
  Darray_destroy(m->MList);
  safe_free(m);
}

/*----------------------*/
MenuPackRec MenuPack_create_rec(name,CB,data)
char *name;
XtCallbackProc CB;
caddr_t data;
{
  MenuPackRec mpr;
  mpr=(MenuPackRec)Memory_allocate(sizeof(struct MenuPackRec_str));
  if (name==NULL)
      Al_fatal_error("MenuPack: create record: must have either a name or a label\n");
  mpr->name=(char *)Memory_allocate(strlen(name)+1);
  strcpy(mpr->name,name); /* not be deleted because Widgets will use it */
  mpr->callback=CB;
  mpr->callback_data=data;
  mpr->IsWidget=(-1);
  mpr->InstanceW=NULL;
  mpr->sub_menu=NULL;
  mpr->Features=ArgPack_create();
  return(mpr);
}

/*----------------------*/
void MenuPack_add_option(name,mpp,CB,data)
char *name;
MenuPack mpp;
XtCallbackProc CB;
caddr_t data;
{
  MenuPackRec mpRec;
  mpRec=MenuPack_create_rec(name,CB,data);
  mpRec->IsWidget=1;
  mpRec->sub_menu=NULL;
  Darray_addh(mpp->MList,mpRec);
}
  
/*--------------------------------*/
void MenuPack_add_sub_menu(name,mpp,SubMenu)
char *name;
MenuPack mpp, SubMenu;
{
  MenuPackRec mpRec;
  if (SubMenu->Flags & MP_SUBMENU) {
    mpRec=MenuPack_create_rec(name,NULL,NULL);
    mpRec->IsWidget=0;
    mpRec->sub_menu=SubMenu;
    Darray_addh(mpp->MList,mpRec);
  }
  else {
    Al_fatal_error2("menupack: add_sub_menu: child of wrong type. Parent is %s Child is %s\n",mpp->menuName,SubMenu->menuName);
  }
}



static Arg MenuShellArgs[]= {
  { NULL, (XtArgVal) NULL },
};

Arg MenuRCArgs[]={
  { XmNorientation, (XtArgVal) XmVERTICAL }, 
  { XmNadjustMargin, (XtArgVal) FALSE },
  { XmNisHomogeneous, (XtArgVal) FALSE },
  { NULL, (XtArgVal) NULL },
};

Arg MenuButtonArgs[]={
  { XmNlabelType, (XtArgVal) XmSTRING },
  { NULL, (XtArgVal) NULL },
};

Arg CascadeOnArgs[]={
  { NULL, NULL },
};

/*----------------------*/
void FormMenu(mpp,TopMenuPane)
MenuPack mpp;
Widget TopMenuPane;
{
  Widget SubMenuCascadeW, PopShell2W;
  MenuPackRec mpRec;
  MenuPack SubMpp;
  unsigned i,l;
  ArgPack AP;
 
  l=Darray_len(mpp->MList);
  for (i=0;i<l;i++) {
    mpRec=(MenuPackRec)Darray_get(mpp->MList,i);
    if (mpRec->IsWidget==1) {
      AP=ArgPack_duplicate_args(MenuButtonArgs);
      ArgPack_add_arg(AP,XmNlabelString, (XtArgVal) AlXms_create(mpRec->name));
      AP=ArgPack_copy_and_append(AP,mpRec->Features);
      mpRec->InstanceW=XtCreateManagedWidget(mpRec->name,
					     xmPushButtonWidgetClass,
					     mpp->MenuPane,
					     ArgPack_the_args(AP), 
					     ArgPack_num_args(AP));
      if (mpRec->callback!=NULL)
	XtAddCallback(mpRec->InstanceW,
		      XmNactivateCallback,
		      mpRec->callback,
		      mpRec->callback_data);
      ArgPack_delete(AP);
    }
    else {
      AP=ArgPack_create();
      mpRec->sub_menu->MenuPane=XmCreatePulldownMenu(TopMenuPane,
						     (char*)NULL,
						     ArgPack_the_args(AP), 
						     ArgPack_num_args(AP));
      ArgPack_delete(AP);

      AP=ArgPack_duplicate_args(MenuButtonArgs);
      ArgPack_add_arg(AP,XmNlabelString, (XtArgVal) AlXms_create(mpRec->name));
      ArgPack_add_arg(AP,XmNsubMenuId, (XtArgVal) mpRec->sub_menu->MenuPane);
      AP=ArgPack_copy_and_append(AP,mpRec->Features);
      mpRec->InstanceW=XtCreateManagedWidget(mpRec->name,
					     xmCascadeButtonWidgetClass,
					     mpp->MenuPane,
					     ArgPack_the_args(AP), 
					     ArgPack_num_args(AP));
      ArgPack_delete(AP);
      FormMenu(mpRec->sub_menu,TopMenuPane);
    }
  }
}

/*--------------------------------------------------*/
void MenuPost(w,xe,parms,nps)
Widget w;
XEvent *xe;
char **parms;
int    *nps;
{
  MenuPack mpp;
  XButtonPressedEvent*  bpe=(XButtonPressedEvent*)xe;
   /** !!! Make sure that this type of event matches the event defined by **/
   /** AL_MENU_POST !!!                                                   **/
  if (*nps<1)
    Al_fatal_error("MenuPost: called without one parameter\n");
  mpp=(MenuPack)AlMonitor_get_obj_from_string(parms[0]);
  assert(mpp);
  Alxt_SetArg(mpp->MenuPane,XmNx,(XtArgVal)bpe->x_root);
  Alxt_SetArg(mpp->MenuPane,XmNy,(XtArgVal)bpe->y_root);
  XtManageChild(mpp->MenuPane);
}


/*--------------------------------------------------*/
void AddCursorToTop(w,xe,parms,nps)
Widget w;
XEvent *xe;
char **parms;
int    *nps;
{
  static int Inited=0;
  static Registry AccountedFor;
  if (Inited==0) {
    Inited=1;
    AccountedFor=Registry_create(Registry_ptrcmp,Registry_ptrhash);
  }
  if (Registry_get(AccountedFor,w)==NULL) {
    AlUI_AttachCursor(w,MENU_BUTTON_CURSOR);
    Registry_add(AccountedFor,(VOIDP)w,(VOIDP)0xffff);
  }
}

static XtActionsRec MenuActions[] = {
  { "AddCursorToTop", AddCursorToTop },
  { "MenuPost", MenuPost },
};

static char * MenuTranslations = 
  "<Enter>: AddCursorToTop()";

#define AL_MENU_POST "<Btn2Down>"

/*--------------------------*/
void MenuPack_make_menu(mpp)
MenuPack mpp;
{
  ArgPack AP;
  static int AddedActions=0;

  assert(mpp->parentW);
  assert((mpp->Flags & MP_POPUP) || (mpp->Flags & MP_PULLDOWN) || (mpp->Flags & MP_PULLDOWN) || (mpp->Flags & MP_OPTION) );
  if ((mpp->Flags & MP_POPUP) || (mpp->Flags & MP_OPTION) ) {
    if (AddedActions==0) {
      AddedActions=1;
      XtAddActions(MenuActions,2);
    }
    XtOverrideTranslations(mpp->parentW,
			   XtParseTranslationTable(MenuTranslations));
    AlMonitor_register_address(mpp,mpp->MyAddress);
    XtOverrideTranslations(mpp->parentW,
      XtParseTranslationTable(
        AlMonitor_make_translation(AL_MENU_POST,"MenuPost",mpp->MyAddress)));
    
    AP=ArgPack_duplicate_args(MenuRCArgs);
    mpp->MenuPane=XmCreatePopupMenu(mpp->parentW,
				    mpp->menuName,
				    ArgPack_the_args(AP),
				    ArgPack_num_args(AP));
    ArgPack_delete(AP);
  }
  else if (mpp->Flags & MP_PULLDOWN) {
    AP=ArgPack_duplicate_args(MenuRCArgs);
    mpp->MenuPane=XmCreatePulldownMenu(mpp->parentMenuBar,
				       mpp->menuName,
				       ArgPack_the_args(AP),
				       ArgPack_num_args(AP));
    ArgPack_delete(AP);
    /* check that parent is a cascade button in this case */
    Alxt_SetArg(mpp->parentW,XmNsubMenuId,(XtArgVal)mpp->MenuPane);
  }
  
  FormMenu(mpp,mpp->MenuPane);
  mpp->Instantiated=Bool_TRUE;
  
  if (mpp->Flags & MP_OPTION) {
    AP=ArgPack_create();
    ArgPack_add_arg(AP,XmNsubMenuId,(XtArgVal)mpp->MenuPane);
    ArgPack_add_arg(AP,XmNstring,(XtArgVal)AlXms_create(mpp->menuName));
    mpp->OptionW=XmCreateOptionMenu(mpp->parentW,
				    mpp->menuName,
				    ArgPack_the_args(AP),
				    ArgPack_num_args(AP));
    ArgPack_delete(AP);
  }
}

/*---------------------*/
Widget MenuPack_get_widget(tmp,name)
     MenuPack tmp;
     char *name;
{
  unsigned i;
  MenuPackRec mpr;
  Widget W;

  if (strcmp(name,tmp->menuName)==0)
    return(tmp->MenuPane);
  for (i=0;i<Darray_len(tmp->MList);i++) {
    mpr=(MenuPackRec)Darray_get(tmp->MList,i);
    if (strcmp(name,mpr->name)==0)
      return(mpr->InstanceW);
    if (mpr->IsWidget==0) {
      if ((W=MenuPack_get_widget(mpr->sub_menu,name))!=NULL)
	return(W);
    }
  }
  return(NULL);
}

/*---------------------*/
MenuPackRec MenuPack_get_option(tmp,name)
MenuPack tmp;
char *name;
{
  unsigned i;
  MenuPackRec mpr,mpr2;

  if (strcmp(name,tmp->menuName)==0)
    return(NULL);
  for (i=0;i<Darray_len(tmp->MList);i++) {
    mpr=(MenuPackRec)Darray_get(tmp->MList,i);
    if (strcmp(name,mpr->name)==0)
      return(mpr);
    if (mpr->IsWidget==0) {
      if ((mpr2=MenuPack_get_option(mpr->sub_menu,name))!=NULL)
	return(mpr2);
    }
  }
  return(NULL);
}
      

  
  
/*--------------------------------------------------*/
Widget MenuPack_get_top_level_manager(mp)
MenuPack mp;
{
  if (mp->Flags & MP_OPTION)
    return(mp->OptionW);
  else
    return(mp->MenuPane);
}

/*--------------------------------------------------*/
NORET    MenuPack_add_arg_to_all(mp,arg,val)
MenuPack mp;
char* arg;
XtArgVal val;
{
  unsigned i;
  MenuPackRec mpr;
  
  for (i=0;i<Darray_len(mp->MList);i++) {
    mpr=(MenuPackRec)Darray_get(mp->MList,i);
    if (mpr->IsWidget==1) 
      Alxt_SetArg(mpr->InstanceW,arg,val);
    else
      MenuPack_add_arg_to_all(mpr->sub_menu,arg,val);
  }
}
/*--------------------------------------------------*/
void MenuPack_do_nothing(w,xe,p,c)
Widget w;
XEvent *xe;
char*p;
Cardinal *c;
{
}

static XtActionsRec MPDoNothingActions[]={
  {"MenuPack_do_nothing", MenuPack_do_nothing}
};

static String MenuPackDoNothingTT =
  "<Btn1Down>:MenuPack_do_nothing()";
/*--------------------------------------------------*/
NORET MenuPack_detach_menu(mp)
     MenuPack mp;
{
  XtAddActions(MPDoNothingActions,1);
  XtOverrideTranslations(mp->parentW,
			 XtParseTranslationTable(MenuPackDoNothingTT));
}

/*
NORET MenuPack_reattach_menu(mp)
     MenuPack mp;
{
  assert(mp);
  Alxt_SetArg(mp->PopMgrW,XtNmenuPost, (XtArgVal) AL_MENU_POST);
}

*/

/*--------------------------------------------------*/
Bool MenuPack_set_feature(mpp,item_name,arg,argval)
MenuPack mpp;
char* item_name;
char* arg;
XtArgVal argval;
{
  MenuPackRec mpr;
  mpr=MenuPack_get_option(mpp,item_name);
  if (mpr==NULL) {  /* !!! this warning should be changed to a debug option */
    Al_warning2("menupack: set_feature: attempt to set option %s in %s when option does not exist.\n",item_name,mpp->menuName);
    return(Bool_FALSE);
  }
  if (mpp->Instantiated==Bool_TRUE) {
    Alxt_SetArg(mpr->InstanceW,arg,argval);
    return(Bool_TRUE);
  }
  else {
    ArgPack_add_arg(mpr->Features,arg,argval);
    return(Bool_TRUE);
  }
}


/*--------------------------------------------------*/
Bool Alxt_set_menu_item_with_arg(mp,item_name,arg,value)
MenuPack mp;
char *item_name, *arg;
XtArgVal value;
{
  Widget SW;
				  
  if ((SW=MenuPack_get_widget(mp,item_name))!=NULL) {
    Alxt_SetArg(SW,arg,value);
    return(Bool_TRUE);
  }
  else
    return(Bool_FALSE);
}

/*--------------------------------------------------*/
NORET MenuPack_set_as_default(mp,name)
MenuPack mp;
char* name;
{
  Pixel fgv;
  Widget w;
  w=MenuPack_get_widget(mp,name);

  _debug1(1,"foreground=%d\n",fgv);
  if (mp->Instantiated==Bool_TRUE) {
    if (w==NULL) return;
    fgv=(Pixel)Alxt_get_widget_feature(w,XmNforeground);
    Alxt_SetArg(w,XmNborderColor,(XtArgVal) fgv);
    Alxt_SetArg(w,XmNborderWidth, (XtArgVal)3);
  }
  else {
    MenuPackRec mprec;
    mprec=MenuPack_get_option(mp,name);
    if (mprec==NULL) return;
    ArgPack_add_arg(mprec->Features,XmNborderWidth, (XtArgVal) 6);
  }
}
