/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                             B O N D F O R M . C                              *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: bondform.c,v 1.4 2003/11/07 10:58:06 jrh Exp $
* $Log: bondform.c,v $
* Revision 1.4  2003/11/07 10:58:06  jrh
* *** empty log message ***
*
* Revision 1.3  2000/12/10 15:01:55  jrh
* Release 2.3
*
* Revision 1.2  1999/05/24 01:24:45  jrh
* Release 2.2.1
*
* Revision 1.1  1999/02/07 21:44:32  jrh
* Initial revision
*
*
*/
#include<math.h>
#include<stdlib.h>
#include<string.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<Xm/Form.h>
#include<Xm/Label.h>
#include<Xm/LabelG.h>
#include<Xm/PanedW.h>
#include<Xm/PushB.h>
#include<Xm/PushBG.h>
#include<Xm/RowColumn.h>
#include<Xm/Scale.h>
#include<Xm/Separator.h>
#include<Xm/Text.h>
#include<Xm/ToggleB.h>
#include "viewmol.h"
#include "dialog.h"

void bondDialog(Widget, caddr_t, XmAnyCallbackStruct *);
void bondDialogExit(Widget, caddr_t, XmPushButtonCallbackStruct *);
void GetHBond(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void GetHBondThreshold(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
struct MenuItem *makeElementMenu(void);
void GetRadiusScale(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void GetScaleFactor(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
static int compareElements(const void *s1, const void *s2);

extern void MapBox(Widget, caddr_t, XmAnyCallbackStruct *);
extern Widget CreateToggleBox(Widget, struct PushButtonRow *, int, int,
                              int, int, int);
extern void CreatePushButtonRow(Widget, struct PushButtonRow *, int);
extern char *getStringResource(Widget, char *);
extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern int  messgb(Widget, int, char *, struct PushButtonRow *, int);
extern void setBondType(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern void GetSlider(Widget, struct SLIDER *, XmScrollBarCallbackStruct *);
extern void redraw(int);
extern int makeConnectivity(struct MOLECULE *, int, int);
extern Widget initShell(Widget, char *, Widget *, Widget *);
extern Widget makeMenu(Widget, int, char *, struct MenuItem *);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);

extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern Widget topShell;
extern double hbondThreshold, scaleRadius;
extern int nmolecule, bondType, showHBond;

static Widget dialog, slider1, slider2;
static double hbondThreshold_save, scaleFactor;
static double scaleRadius_save, *radScale_save;
static struct ELEMENT *scaleElement=NULL;
static int bondType_save, showHBond_save, mol;
static char *labels;

void bondDialog(Widget widget, caddr_t which, XmAnyCallbackStruct *data)
{
  Widget board, form, form1, sep, hb, element;
  Widget radiobox;
  int na;
  short decimals;
  struct MenuItem *menu;
  static struct SLIDER threshold;
  static struct SLIDER scaleRad;
  static struct PushButtonRow buttons[] = {
    { "ok", bondDialogExit, (XtPointer)TRUE, NULL },
    { "cancel", bondDialogExit, (XtPointer)FALSE, NULL }
  };
  static struct PushButtonRow radiobox_buttons[] = {
    { "single", setBondType, (XtPointer)SINGLE_BONDS, NULL },
    { "multiple", setBondType, (XtPointer)MULTIPLE_BONDS, NULL },
    { "conjugated", setBondType, (XtPointer)CONJUGATION, NULL },
  };
  register int i, j, k;

  /* This function creates the bond order dialog */

  bondType_save=bondType;
  showHBond_save=showHBond;
  hbondThreshold_save=hbondThreshold;
  scaleRadius_save=scaleRadius;
  mol=windows[VIEWER].set;
  if (mol < 0)
  {
    na=0;
    for (i=0; i<nmolecule; i++)
      na+=molecules[i].na;
  }
  else
    na=molecules[mol].na;
  radScale_save=(double *)getmem((size_t)na, sizeof(double));
  k=0;
  if (mol < 0)
  {
    for (i=0; i<nmolecule; i++)
    {
      for (j=0; j<molecules[i].na; j++)
  	  radScale_save[k++]=molecules[i].atoms[j].radScale;
    }
  }
  else
  {
    for (j=0; j<molecules[mol].na; j++)
      radScale_save[k++]=molecules[mol].atoms[j].radScale;
  }
  dialog=initShell(windows[VIEWER].widget, "bondForm", &board, &form);
  radiobox=CreateToggleBox(form, radiobox_buttons, XtNumber(radiobox_buttons),
                           XmVERTICAL, 1, True, bondType-1);
  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                              XmNorientation, XmHORIZONTAL,
                              XmNtraversalOn, False,
                              NULL);
  hb=XtVaCreateManagedWidget("showHydrogenBonds", xmToggleButtonWidgetClass, form,
                             NULL);
  if (showHBond) XtVaSetValues(hb, XmNset, True, NULL); 
  XtAddCallback(hb, XmNvalueChangedCallback, (XtCallbackProc)GetHBond, &showHBond);

  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                              XmNorientation, XmHORIZONTAL,
                              XmNtraversalOn, False,
                              NULL);
  (void)XtVaCreateManagedWidget("HydrogenBondLabel", xmLabelWidgetClass, form, NULL);
  slider1=XtVaCreateManagedWidget("thresholdSlider", xmScaleWidgetClass, form,
                                   XmNorientation, XmHORIZONTAL,
                                   XmNshowValue, True,
                                   XmNsensitive, True,
                                   NULL);
  XtVaGetValues(slider1, XmNdecimalPoints, &decimals, NULL);
  XtVaSetValues(slider1, XmNvalue, (int)(hbondThreshold*pow(10.0, (double)decimals)), NULL);
  threshold.number=&hbondThreshold;
  threshold.decimals=decimals;
  threshold.draw=NULL;
  XtAddCallback(slider1, XmNvalueChangedCallback, (XtCallbackProc)GetHBondThreshold, &threshold);
  XtAddCallback(slider1, XmNdragCallback, (XtCallbackProc)GetHBondThreshold, &threshold);
  if (!showHBond) XtVaSetValues(slider1, XmNsensitive, False, NULL);
  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                              XmNorientation, XmHORIZONTAL,
                              XmNtraversalOn, False,
                              NULL);
  form1=XtVaCreateWidget("rowcolumn", xmRowColumnWidgetClass, form,
                         XmNorientation, XmHORIZONTAL,
                         XmNtopAttachment, XmATTACH_FORM,
                         XmNbottomAttachment, XmATTACH_FORM,
                         XmNleftAttachment, XmATTACH_FORM,
                         XmNrightAttachment, XmATTACH_FORM,
                         NULL);    
  (void)XtVaCreateManagedWidget("select", xmLabelWidgetClass, form1, NULL);
  menu=makeElementMenu();
  element=makeMenu(form1, XmMENU_OPTION, "element", menu);
  fremem((void **)&labels);
  if (scaleElement != NULL)
  {
    i=0;
    while (menu[i].label != NULL)
    {
	if (!strcmp(menu[i].label, scaleElement->symbol))
	{
	  XtVaSetValues(element, XmNmenuHistory, menu[i].widget, NULL);
	  break;
	}
	i++;
    }
  }
  (void)XtVaCreateManagedWidget("atoms", xmLabelWidgetClass, form1, NULL);
  slider2=XtVaCreateManagedWidget("scaleRadius", xmScaleWidgetClass, form,
                                   XmNorientation, XmHORIZONTAL,
                                   XmNshowValue, True,
                                   XmNsensitive, True,
                                   NULL);
  XtVaGetValues(slider2, XmNdecimalPoints, &decimals, NULL);
  if (scaleElement != NULL)
    XtVaSetValues(slider2, XmNvalue, (int)(scaleElement->radScale*pow(10.0, (double)decimals)),
			NULL);
  else
    XtVaSetValues(slider2, XmNvalue, (int)(scaleRadius*pow(10.0, (double)decimals)), NULL);
  scaleRad.number=&scaleFactor;
  scaleRad.decimals=decimals;
  scaleRad.draw=NULL;
  XtAddCallback(slider2, XmNvalueChangedCallback, (XtCallbackProc)GetScaleFactor, &scaleRad);
  XtAddCallback(slider2, XmNdragCallback, (XtCallbackProc)GetScaleFactor, &scaleRad);
  XtManageChild(element);
  sep=XtVaCreateManagedWidget("sep", xmSeparatorWidgetClass, form,
                              XmNorientation, XmHORIZONTAL,
                              XmNtraversalOn, False,
                              NULL);

  CreatePushButtonRow(form, buttons, XtNumber(buttons));
  XtAddCallback(dialog, XmNpopupCallback, (XtCallbackProc)MapBox, (XmAnyCallbackStruct *)NULL);
  XtManageChild(form1);
  XtManageChild(form);
  XtManageChild(board);
}

void bondDialogExit(Widget button, caddr_t which, XmPushButtonCallbackStruct *data)
{
  register int i, j, k;

  if (!(int)which)
  {
    bondType=bondType_save;
    showHBond=showHBond_save;
    hbondThreshold=hbondThreshold_save;
    scaleRadius=scaleRadius_save;
    k=0;
    if (mol < 0)
    {
      for (i=0; i<nmolecule; i++)
      {
        for (j=0; j<molecules[i].na; j++)
   	    molecules[i].atoms[j].radScale=radScale_save[k++];
        makeConnectivity(&molecules[i], FALSE, TRUE);
      }
    }
    else
    {
      for (j=0; j<molecules[mol].na; j++)
        molecules[mol].atoms[j].radScale=radScale_save[k++];
      makeConnectivity(&molecules[mol], FALSE, TRUE);
    }
    fremem((void **)&radScale_save);
    redraw(VIEWER);
  }
  XtDestroyWidget(dialog);
}

void GetHBond(Widget w, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  register int i;

  if (data->set)
  {
    *(int *)which=TRUE;
    XtVaSetValues(slider1, XmNsensitive, True, NULL);
  }
  else
  {
    *(int *)which=FALSE;
    XtVaSetValues(slider1, XmNsensitive, False, NULL);
  }

  for (i=0; i<nmolecule; i++)
    makeConnectivity(&molecules[i], FALSE, TRUE);
  redraw(VIEWER);
}

void GetHBondThreshold(Widget w, struct SLIDER *slider, XmScrollBarCallbackStruct *data)
{
  register int i;

  GetSlider(w, slider, data);
  for (i=0; i<nmolecule; i++)
    makeConnectivity(&molecules[i], FALSE, TRUE);
  redraw(VIEWER);
}

struct MenuItem *makeElementMenu(void)
{
  struct MOLECULE *mol;
  struct MenuItem *menu;
  int *done;
  static char all[]="all";
  char *word;
  register int i, j, k;

  if (windows[VIEWER].set < 0)
    mol=&molecules[0];
  else
    mol=&molecules[windows[VIEWER].set];

  done=(int *)getmem((size_t)mol->na, sizeof(int));
  menu=(struct MenuItem *)getmem((size_t)(mol->na+2), sizeof(struct MenuItem));
  labels=(char *)getmem((size_t)mol->na+2, MAXLENLINE*sizeof(char));
  menu[0].label=all;
  menu[0].class=&xmPushButtonGadgetClass;
  menu[0].callback=GetRadiusScale;
  menu[0].callback_data=0;
  menu[0].widget=NULL;
  menu[0].subitems=NULL;
  word=getStringResource(topShell, "elementMenuPrefix");
  j=1;
  for (i=0; i<mol->na; i++)
  {
    if (!done[i])
    {
	if (strcmp(mol->atoms[i].element->symbol, "Uc"))
	{
	  strncpy(&labels[MAXLENLINE*j], word, MAXLENLINE-4);
	  strcat(&labels[MAXLENLINE*j], " ");
	  strcat(&labels[MAXLENLINE*j], mol->atoms[i].element->symbol);
        menu[j].label=&labels[MAXLENLINE*j];
	  menu[j].class=&xmPushButtonGadgetClass;
	  menu[j].callback=GetRadiusScale;
	  menu[j].callback_data=(XtPointer)mol->atoms[i].element;
	  menu[j].widget=NULL;
	  menu[j].subitems=NULL;
        j++;
	}
	for (k=i; k<mol->na; k++)
	{
	  if (!strcmp(mol->atoms[i].element->symbol, mol->atoms[k].element->symbol))
	    done[k]=TRUE;
      }
    }
  }
  menu[j++].label=NULL;
  fremem((void **)&done);
  menu=(struct MenuItem *)expmem((void *)menu, (size_t)j, sizeof(struct MenuItem));
  qsort((void *)(&menu[1]), (size_t)(j-2), sizeof(struct MenuItem), compareElements);
  return(menu);
}

void GetRadiusScale(Widget button, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  short decimals;

  scaleElement=(struct ELEMENT *)which;
  XtVaGetValues(slider2, XmNdecimalPoints, &decimals, NULL);
  if (scaleElement == NULL)
    XtVaSetValues(slider2, XmNvalue, (int)(scaleRadius*pow((double)10.0, (double)decimals)), NULL);
  else
    XtVaSetValues(slider2, XmNvalue, (int)(scaleElement->radScale*pow((double)10.0, (double)decimals)), NULL);
}

void GetScaleFactor(Widget widget, struct SLIDER *slider, XmScrollBarCallbackStruct *data)
{
  register int i, j;

  *(slider->number)=(double)(data->value)/pow((double)10.0, (double)slider->decimals);
  if (scaleElement != NULL)
    scaleElement->radScale=*(slider->number);
  else
    scaleRadius=*(slider->number);

  for (i=0; i<nmolecule; i++)
  {
    for (j=0; j<molecules[i].na; j++)
      molecules[i].atoms[j].radScale=scaleRadius*molecules[i].atoms[j].element->radScale;
    makeConnectivity(&molecules[i], FALSE, TRUE);
  }
  redraw(VIEWER);
}

int compareElements(const void *s1, const void *s2)
{
  struct MenuItem *p1, *p2;
  register int i;

  p1=(struct MenuItem *)s1;
  p2=(struct MenuItem *)s2;

  i=strcmp(p1->label, p2->label);
  return(i);
}
