/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                          M O U S E A C T I O N . C                           *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: mouseaction.c,v 1.7 2004/08/29 14:58:01 jrh Exp $
* $Log: mouseaction.c,v $
* Revision 1.7  2004/08/29 14:58:01  jrh
* Release 2.4.1
*
* Revision 1.6  2003/11/07 11:07:48  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:13:02  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:26:45  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:53:35  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:48:50  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:42:28  jrh
* Initial revision
*
*/
#include<fcntl.h> 
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<X11/X.h>
#include<X11/Xlib.h>
#include<X11/Intrinsic.h>
#include<Xm/Xm.h>
#include<Xm/PushBG.h>
#include<GL/glu.h>
#include "viewmol.h"
#include "dialog.h"

#define BUFFERSIZE 100

void viewerMouseAction(XEvent *, int);
void clearGeometry(Widget, caddr_t, XmAnyCallbackStruct *);
void spectrumMouseAction(XmDrawingAreaCallbackStruct *, int);
void historyMouseAction(unsigned int, int, int, int);
void MODiagramMouseAction(XmDrawingAreaCallbackStruct *, int);
void setGeometry(int);
void restoreGeometry(struct SAVE *, int);
void ringBell(void);
GLint doPicking(int, int, void draw(), int *);
void getBasisfunction(Widget, caddr_t, caddr_t);
void setAnimation(int);
void selectMolecule(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void setWindow(int);
void deleteSelection(void);
void trim(char *);

extern void drawMolecule(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern void redraw(int);
extern void drawSpectrum(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern void drawMODiagram(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern void processInput(Widget, caddr_t, XmDrawingAreaCallbackStruct *);
extern Boolean normalMode(XtPointer);
extern Boolean animateHistory(XtPointer);
extern Widget makeMenu(Widget, int, char *, struct MenuItem *);
extern void makeINSIntensity(void);
extern char *getStringResource(Widget, char *);
extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern void basisDialog(int);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern int  calcInternal(struct MOLECULE *, int);
extern void redraw(int);
extern int  messgb(Widget, int, char *, struct PushButtonRow *, int);
extern void initZoom(Widget, XmDrawingAreaCallbackStruct *);
extern void endZoom(Widget, int, int);
extern void pixelToWorld(int, double *, double *);
extern int  makeConnectivity(struct MOLECULE *, int, int);
extern void setMenuItem(int, int);
extern void distortGeometry(double);
extern int  makeAnnotation(int, int, float, float, float, int, const GLfloat *,
                           int, int, char *);
extern void deleteAnnotation(int *);
extern void annotateWavefunction(void);
extern void modifyAnnotation(int, int, int);
extern void setWindowTitle(Widget, char *);
extern void buildMolecule(struct MOLECULE *, int, int, int);
extern void setupProjection(int);
extern void setupModelview(int);
extern struct MOLECULE *initMolecule(int);
extern Widget makeViewerMenu(Widget);
extern int checkZoom(int);
extern void setMenu(struct MOLECULE *);
extern void updateMOs(int);
extern void expandCell(int, int);
extern void getScreenCoordinates(double, double, double, double *, double *,
                                 double *);
extern void getWorldCoordinates(double, double, double, double *, double *,
                                double *);
extern void annotateNormalMode(struct MOLECULE *);

extern XtAppContext app;
extern struct WINDOW windows[];
extern struct MOLECULE *molecules;
extern struct ANNOTATION *annotation;
extern double *grid, forceScale, amplitude;
extern double weight, *cnm, wnScale;
extern int picking, swapBuffers, setins, projectionMode;
extern int bfatom, annotate;
extern int rotateXY, rotateZ, moveItem;
extern int debug;
extern int nmolecule, nAnnotations;
extern int historyAnnotation, normalModeAnnotation;
extern int modifyMolecule, bondOrderDefault;
extern XtAppContext app;
extern Widget topShell;
extern int showMenu, animate, selectAtom;
extern Pixel stdcol[9];
extern char *formBondLength, *formBondAngle, *formTorsionAngle;

static int naSaved;
static int clicked[4] = {-1, -1, -1, -1}, intcoord;

void viewerMouseAction(XEvent *event, int state)
{
  static struct PushButtonRow buttons[] = {{"ok", GetMessageBoxButton,
                                            (XtPointer)0, NULL}};
  struct MOLECULE *mol;
  Dimension width, height;
  GLint picked;
  double xs, ys, zs, xpix, ypix;
  float black[4]={0.0, 0.0, 0.0, 0.0};
  static int new=TRUE, moveItemSave;
  static int molclicked[4], ic=(-1);
  int imol;
  char line[20], *form=NULL;
  register int i, j, k, l;

  if (!(moveItem & 0x80000000)) moveItemSave=moveItem;
  if (event->xbutton.button == Button1 && state == TRUE)
  {
    picking=TRUE;
    swapBuffers=FALSE;
    picked=doPicking(event->xbutton.x, event->xbutton.y, drawMolecule, &imol);
    picking=FALSE;
    swapBuffers=TRUE;
    if (picked != -1)
    {
      ringBell();
      moveItem=imol+MOLECULES;
      windows[VIEWER].set=imol;
      setWindow(imol);
      if (selectAtom)
      {
        basisDialog(picked);
        bfatom=picked;
        return;
      }
      if (setins)
      {
        molecules[imol].atoms[picked].neutronScatterfac=weight;
        makeINSIntensity();
        return;
      }
      if (modifyMolecule > NOTHING && modifyMolecule < CREATE_BOND)
      {
        buildMolecule(&molecules[imol], picked, modifyMolecule, FALSE);
        return;
      }
      if (picked & 0x80000000)
      {
        if (!(event->xmotion.state & ShiftMask))
        {
	  l=picked & 0x3fffffff;
          modifyAnnotation(l, event->xbutton.x, event->xbutton.y);
        }
        else
        {
          moveItem=picked;
          rotateXY=(-1);
        }
        return;
      }
      if (picked & 0x40000000 && imol != 0x80000000)
      {
        l=picked & 0x3fffffff;
        switch (molecules[imol].internals[l].type)
        {
          case BONDAVERAGE: return;
          case BONDLENGTH:  form=formBondLength;
                            break;
          case ANGLE:       form=formBondAngle;
                            break;
          case TORSION:     form=formTorsionAngle;
                            break;
        }
	ic=(-1);
	for (i=0; i<nAnnotations; i++)
	{
	  if (annotation[i].userdata == l)
	  {
	    ic=i;
	    break;
	  }
	}
        if (ic != -1)
        {
          molecules[imol].internals[l].type=(-molecules[imol].internals[l].type);
          deleteAnnotation(&ic);
        }
        sprintf(line, form, molecules[imol].internals[l].value);
        trim(line);
        glXMakeCurrent(XtDisplay(windows[VIEWER].widget), XtWindow(windows[VIEWER].widget),
                                 windows[VIEWER].context);
        setupProjection(picking);
        setupModelview(imol);
	getScreenCoordinates(molecules[imol].internals[l].x, molecules[imol].internals[l].y, 
                             molecules[imol].internals[l].z, &xs, &ys, &zs);
        XtVaGetValues(windows[VIEWER].widget, XtNwidth, &width, XtNheight, &height, NULL);
	pixelToWorld(VIEWER, &xpix, &ypix);
        ic=makeAnnotation((-1), COORDINATES, (float)(xpix*(xs-0.5*(double)width)), (float)(ypix*(0.5*(double)height-ys)),
  	                  (float)zs, stdcol[BLACK], black, GEOMETRY | EDITABLE, l, line);
        modifyAnnotation(ic, event->xbutton.x, event->xbutton.y);
        molecules[imol].internals[l].type=(-molecules[imol].internals[l].type);
        return;
      }
      if (picked & 0x20000000)
      {
        l=(picked & 0x1fffffff);
        molecules[imol].unitcell[0].corners[l].flags^=SELECTED;
/*      return; */
      }
      if (!(event->xmotion.state & ShiftMask))
      {
        if (new)
        {
          new=FALSE;
          intcoord=0;
        }
	  if (!(picked & 0x20000000))
	    molecules[imol].atoms[picked].flags^=SELECTED;
        /* check whether atom has already been clicked on */
        j=FALSE;
        for (i=0; i<intcoord; i++)
        {
          if (imol == molclicked[i] && picked == clicked[i])
          {
            /* yes, delete it */
            for (k=i; k<intcoord; k++)
            {
              molclicked[k]=molclicked[k+1];
              clicked[k]=clicked[k+1];
            }
            intcoord--;
            j=TRUE;
            break;
          }
        }
        if (intcoord < TORSION)
        {
          /* no, add it */
          if (!j)
          {
            molclicked[intcoord]=imol;
            clicked[intcoord++]=picked;
          }
          if (modifyMolecule > REPLACE_ATOM && intcoord == BONDLENGTH)
          {
            switch (modifyMolecule)
            {
              case CREATE_BOND: if (molecules[imol].addedBonds == NULL)
                                  molecules[imol].addedBonds=(int *)getmem((size_t)3, sizeof(int));
                                else
                                  molecules[imol].addedBonds=(int *)expmem((void *)molecules[imol].addedBonds,
                                    (size_t)(3*(molecules[imol].nbAdded+1)), sizeof(int));
                                molecules[imol].addedBonds[3*molecules[imol].nbAdded]=clicked[0];
                                molecules[imol].addedBonds[3*molecules[imol].nbAdded+1]=clicked[1];
                                molecules[imol].addedBonds[3*molecules[imol].nbAdded+2]=bondOrderDefault;
                                molecules[imol].nbAdded++;
                                makeConnectivity(&molecules[imol], FALSE, TRUE);
                                break;
              case REMOVE_BOND: k=FALSE;
                                for (i=0; i<molecules[imol].nbAdded; i++)
                                {
                                  if ((molecules[imol].addedBonds[3*i] == clicked[0] &&
                                       molecules[imol].addedBonds[3*i+1] == clicked[1]) ||
                                      (molecules[imol].addedBonds[3*i] == clicked[1] &&
                                       molecules[imol].addedBonds[3*i+1] == clicked[0]))
                                  {
                                    for (j=i+1; j<molecules[imol].nbAdded; j++)
                                    {
                                      molecules[imol].addedBonds[3*(j-1)]=molecules[imol].addedBonds[3*j];
                                      molecules[imol].addedBonds[3*(j-1)+1]=molecules[imol].addedBonds[3*j+1];
                                      molecules[imol].addedBonds[3*(j-1)+2]=molecules[imol].addedBonds[3*j+2];
                                    }
                                    molecules[imol].nbAdded--;
                                    if (molecules[imol].nbAdded)
                                    {
                                      molecules[imol].addedBonds=(int *)expmem((void *)molecules[imol].addedBonds,
                                        (size_t)(3*(molecules[imol].nbAdded)), sizeof(int));
                                    }
                                    else
                                      fremem((void **)&molecules[imol].addedBonds);
                                    k=TRUE;
                                    break;
                                  }
                                }
                                if (!k)
                                {
                                  if (molecules[imol].deletedBonds == NULL)
                                    molecules[imol].deletedBonds=(int *)getmem((size_t)2, sizeof(int));
                                  else
                                    molecules[imol].deletedBonds=(int *)expmem((void *)molecules[imol].deletedBonds,
                                        (size_t)(2*(molecules[imol].nbDeleted+1)), sizeof(int));
                                  molecules[imol].deletedBonds[2*molecules[imol].nbDeleted]=clicked[0];
                                  molecules[imol].deletedBonds[2*molecules[imol].nbDeleted+1]=clicked[1];
                                  molecules[imol].nbDeleted++;
                                }
                                makeConnectivity(&molecules[imol], FALSE, TRUE);
                                break;
              case BOND_ORDER:  j=FALSE;
                                if (molecules[imol].addedBonds != NULL)
                                {
                                  for (i=0; i<molecules[imol].nbAdded; i++)
                                  {
                                    if ((molecules[imol].addedBonds[3*i] == clicked[0] &&
                                         molecules[imol].addedBonds[3*i+1] == clicked[1]) ||
                                        (molecules[imol].addedBonds[3*i] == clicked[1] &&
                                         molecules[imol].addedBonds[3*i+1] == clicked[0]))
                                    {
                                      molecules[imol].addedBonds[3*i+2]=bondOrderDefault;
                                      makeConnectivity(&molecules[imol], FALSE, TRUE);
                                      j=TRUE;
                                      break;
                                    }
                                  }
                                }
                                if (!j)
                                {
                                  if (molecules[imol].addedBonds == NULL)
                                    molecules[imol].addedBonds=(int *)getmem((size_t)3, sizeof(int));
                                  else
                                    molecules[imol].addedBonds=(int *)expmem((void *)molecules[imol].addedBonds,
                                      (size_t)(3*(molecules[imol].nbAdded+1)), sizeof(int));
                                  molecules[imol].addedBonds[3*molecules[imol].nbAdded]=clicked[0];
                                  molecules[imol].addedBonds[3*molecules[imol].nbAdded+1]=clicked[1];
                                  molecules[imol].addedBonds[3*molecules[imol].nbAdded+2]=bondOrderDefault;
                                  molecules[imol].nbAdded++;
                                  makeConnectivity(&molecules[imol], FALSE, TRUE);
                                }
                                break;
            }
            new=TRUE;
		deleteSelection();
          }
          else
            showMenu=FALSE;
          return;
        }
        else
        {
          if (molecules[imol].internals != NULL)
          {
            if (molecules[imol].internals == NULL)
              molecules[imol].internals=(struct INTERNAL *)getmem(molecules[imol].ninternal+1,
                                                                  sizeof(struct INTERNAL));
            else
              molecules[imol].internals=(struct INTERNAL *)expmem((void *)molecules[imol].internals,
                                        molecules[imol].ninternal+1, sizeof(struct INTERNAL));
            molecules[imol].internals[molecules[imol].ninternal].atoms[0]=clicked[0];
            molecules[imol].internals[molecules[imol].ninternal].atoms[1]=clicked[1];
            molecules[imol].internals[molecules[imol].ninternal].atoms[2]=clicked[2];
            molecules[imol].internals[molecules[imol].ninternal].atoms[3]=clicked[3];
            molecules[imol].internals[molecules[imol].ninternal].type=TORSION;
            if (calcInternal(&molecules[imol], molecules[imol].ninternal))
            {
              molecules[imol].ninternal++;
              setMenuItem(VIEWER2_ALL, True);
              setMenuItem(VIEWER2_LAST, True);
            }
            else
            {
              form=getStringResource(topShell, "notDefined");           
              messgb(topShell, 1, form, buttons, 1);
              if (molecules[imol].ninternal)
                molecules[imol].internals=(struct INTERNAL *)expmem((void *)molecules[imol].internals,
                                    molecules[imol].ninternal, sizeof(struct INTERNAL));
              else
                fremem((void *)&(molecules[imol].internals));
            }
          }
          new=FALSE;
          intcoord=0;
          ringBell();
          molclicked[intcoord]=imol;
          clicked[intcoord++]=picked;
          return;
        }
      }
      else
        ringBell();
    }
    else /* picked != -1 */
    {
      if (modifyMolecule == NEW_MOLECULE ||
          ((event->xmotion.state & ShiftMask) && (modifyMolecule == ADD_ATOM)))
      {
        modifyMolecule=ADD_ATOM;
/*      mol=&molecules[nmolecule];
        if (mol == NULL)*/ mol=initMolecule(20);
        ringBell();
        if (nmolecule == 0)
          buildMolecule(mol, picked, modifyMolecule, TRUE);
        else
          buildMolecule(mol, picked, modifyMolecule, FALSE);
        nmolecule++;
        windows[VIEWER].menu=makeViewerMenu(windows[VIEWER].widget);
        setWindowTitle(windows[VIEWER].widget, mol->title);
        setMenuItem(VIEWER1_SAVE, True);
        setMenuItem(VIEWER1_DELETE, True);
        setMenuItem(VIEWER1_BUILD, True);
      }
    }
  }

  switch (event->xbutton.button)
  {
    case Button1:   if (state == TRUE)
                    {
                      if (annotate)
                        modifyAnnotation((-1), event->xbutton.x, event->xbutton.y);
                      rotateXY=(-1);
                    }
                    else
                    {
                      rotateXY=0;
                      moveItem=moveItemSave;
                    }
                    break;
    case Button2:   if (state == TRUE) rotateZ=(-1);
                    else               rotateZ=0;
                    break;
    case Button3:   if (state == TRUE)
                    {
                      if (!(event->xmotion.state & ShiftMask))
                      {
                        new=TRUE;
                        if (windows[VIEWER].set >= 0)
                          mol=&molecules[windows[VIEWER].set];
                        else
                          mol=&molecules[0];
                        /* Is the internal coordinate in clicked[] already defined ?
                           If yes, delete it. */
                        for (i=0; i<mol->ninternal; i++)
                        {
                          if (mol->internals[i].type == intcoord)
                          {
                            k=l=0;
                            for (j=0; j<intcoord; j++)
                            {
                              if (mol->internals[i].atoms[j] == clicked[j]) k++;
                              if (mol->internals[i].atoms[j] == clicked[intcoord-j-1]) l++;
                            }
                            if (k == intcoord || l == intcoord)
                            {
                              if (i == mol->ninternal-1)
                                clearGeometry((Widget)0, (caddr_t)LAST, (XmAnyCallbackStruct *)0);
                              else
                                clearGeometry((Widget)0, (caddr_t)i, (XmAnyCallbackStruct *)0);
                              clicked[0]=(-1);
                              break;
                            }
                          }
                        } /* end of i-loop */
                        if (clicked[0] >= 0)
                        {
                          j=FALSE;
                          switch (intcoord)
                          {
                            case 2: if (molclicked[0] != molclicked[1]) j=TRUE;
                                    break;
                            case 3: if (molclicked[0] != molclicked[1] ||
                                        molclicked[0] != molclicked[2] ||
                                        molclicked[1] != molclicked[2]) j=TRUE;
                                    break;
                            case 4: if (molclicked[0] != molclicked[1] ||
                                        molclicked[0] != molclicked[2] ||
                                        molclicked[0] != molclicked[3] ||
                                        molclicked[1] != molclicked[2] ||
                                        molclicked[1] != molclicked[3] ||
                                        molclicked[2] != molclicked[3]) j=TRUE;
                                    break;
                          }
                          if (j)
                          {
                            form=getStringResource(topShell, "differentMolecules");           
                            messgb(topShell, 1, form, buttons, 1);
                            break;
                          }
                          if (mol->internals == NULL)
                            mol->internals=(struct INTERNAL *)getmem(mol->ninternal+1,
                                                                     sizeof(struct INTERNAL));
                          else
                            mol->internals=(struct INTERNAL *)expmem((void *)mol->internals,
                                                      mol->ninternal+1, sizeof(struct INTERNAL));
                          mol->internals[mol->ninternal].atoms[0]=clicked[0];
                          mol->internals[mol->ninternal].atoms[1]=clicked[1];
                          mol->internals[mol->ninternal].atoms[2]=clicked[2];
                          mol->internals[mol->ninternal].atoms[3]=clicked[3];
                          mol->internals[mol->ninternal].type=intcoord;
                          intcoord=0;
                          if (calcInternal(mol, mol->ninternal))
                          {
                            mol->ninternal++;
                            setMenuItem(VIEWER2_ALL, True);
                            setMenuItem(VIEWER2_LAST, True);
                          }
                          else
                          {
                            form=getStringResource(topShell, "notDefined");           
                            messgb(topShell, 1, form, buttons, 1);
                            if (mol->ninternal)
                              mol->internals=(struct INTERNAL *)expmem((void *)mol->internals,
                                                  mol->ninternal, sizeof(struct INTERNAL));
                            else
                              fremem((void *)&(mol->internals));
                          }
                        }
			    }
			    deleteSelection();
                    }
                    break;
  }
}

void clearGeometry(Widget widget, caddr_t which, XmAnyCallbackStruct *data)
{
  struct MOLECULE *mol;
  register int i;

  if (windows[VIEWER].set >= 0)
    mol=&molecules[windows[VIEWER].set];
  else
    mol=&molecules[0];
  if ((int)which == ALL)
  {
    fremem((void **)&(mol->internals));
    mol->ninternal=0;
  }
  else if ((int)which == LAST)
  {
    mol->ninternal--;
    if (mol->ninternal > 0)
      mol->internals=(struct INTERNAL *)expmem(mol->internals, mol->ninternal,
                                               sizeof(struct INTERNAL));
    else
    {
      fremem((void **)&(mol->internals));
      mol->ninternal=0;
    }
  }
  else
  {
    for (i=(int)which; i<mol->ninternal-1; i++)
    {
      mol->internals[i].value=mol->internals[i+1].value;
      mol->internals[i].x=mol->internals[i+1].x;
      mol->internals[i].y=mol->internals[i+1].y;
      mol->internals[i].z=mol->internals[i+1].z;
      mol->internals[i].atoms[0]=mol->internals[i+1].atoms[0];
      mol->internals[i].atoms[1]=mol->internals[i+1].atoms[1];
      mol->internals[i].atoms[2]=mol->internals[i+1].atoms[2];
      mol->internals[i].atoms[3]=mol->internals[i+1].atoms[3];
      mol->internals[i].type=mol->internals[i+1].type;
    }
    mol->ninternal--;
    if (mol->ninternal > 0)
      mol->internals=(struct INTERNAL *)expmem(mol->internals, mol->ninternal,
                                               sizeof(struct INTERNAL));
    else
    {
      fremem((void **)&(mol->internals));
      mol->ninternal=0;
    }
  }
  if (mol->ninternal == 0)
  {
    setMenuItem(VIEWER2_ALL, False);
    setMenuItem(VIEWER2_LAST, False);
  }
  redraw(VIEWER);
}

void spectrumMouseAction(XmDrawingAreaCallbackStruct *data, int state)
{
/*const GLfloat black[4] = {0.0, 0.0, 0.0, 0.0};*/
  struct MOLECULE *mol;
  static struct PushButtonRow button[] = {{"continue", GetMessageBoxButton, (XtPointer)0, NULL}};
  int mode, imol;
  char /*line[MAXLENLINE],*/ *word;
  register int i;

  mol=&molecules[windows[SPECTRUM].set];
  if (data->event->xbutton.button == Button1 && state == TRUE)
  {
    picking=TRUE;
    swapBuffers=FALSE;
    mode=doPicking(data->event->xbutton.x, data->event->xbutton.y, drawSpectrum,
                   &imol);
    mol->mode=mode;
    picking=FALSE;
    swapBuffers=TRUE;

    if (mol->cnm == NULL)
    {
      word=getStringResource(topShell, "noNormalModes");
      messgb(topShell, 3, word, button, 1);
    }
    else
    {
      if (mode != -1)
      {
        ringBell();
        if (animate == ANIMATE) setAnimation(animate);
        else
        {
          restoreGeometry(mol->coord, windows[SPECTRUM].set);
          if (animate == DISTORT) distortGeometry(amplitude);
        }
	annotateNormalMode(mol);
      }
      else
      {
        if (animate == ANIMATE) setAnimation(animate);
        else
        {
          for (i=0; i<nmolecule; i++)
            restoreGeometry(molecules[i].coord, i);
        }
      }
      annotateNormalMode(mol);
    }
  } 
  else if (data->event->xbutton.button == Button2)
  {
    if (state == TRUE)
      initZoom(windows[SPECTRUM].widget, data);
    else
      endZoom(windows[SPECTRUM].widget, data->event->xbutton.x, data->event->xbutton.y);
  }
}

void getImaginaryMode(Widget widget, caddr_t which, caddr_t data)
{
  struct MOLECULE *mol;

  mol=&molecules[windows[SPECTRUM].set];
  mol->mode=(int)which;
  if (animate == ANIMATE) setAnimation(animate);
  else
  {
    restoreGeometry(mol->coord, windows[SPECTRUM].set);
    redraw(VIEWER);
  }
}

void selectMolecule(Widget widget, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  struct MENUDATA {struct MenuItem *submenu;
                   int window;
                   int which;} *menudata;
  char t[7], *string, line[MAXLENLINE];
  int offset=0;
  register int i;

  if (data->set)
  {
    menudata=(struct MENUDATA *)which;
    windows[menudata->window].set=menudata->which;
    if (menudata->window == VIEWER)
    {
      XtVaSetValues(menudata->submenu[0].widget, XmNset, False, NULL);
      offset=2;
    }
    for (i=0; i<nmolecule; i++)
    {
      if (i != menudata->which)
        XtVaSetValues(menudata->submenu[i+offset].widget, XmNset, False, NULL);
    }
    if (menudata->which >= 0)
      XtVaSetValues(menudata->submenu[menudata->which+offset].widget, XmNset,
			  True, NULL);
    else
      XtVaSetValues(menudata->submenu[0].widget, XmNset, True, NULL);
    switch (menudata->window)
    {
      case VIEWER:   if (menudata->which >= 0)
                     {
                       moveItem=menudata->which+MOLECULES;
                       setWindowTitle(windows[VIEWER].widget,
						  molecules[menudata->which].title);
                       setMenu(&molecules[menudata->which]);
                       annotateWavefunction();
                     }
                     else
                     {
                       moveItem=WORLD;
                       string=getStringResource(menudata->submenu[0].widget, "labelString");
                       setWindowTitle(windows[VIEWER].widget, string);
                       setMenu(&molecules[0]);
                     }
                     break;
      case SPECTRUM: sprintf(t, "title%.1d", windows[SPECTRUM].mode);
                     string=getStringResource(XtParent(windows[SPECTRUM].widget), t);
                     sprintf(line, string, molecules[windows[SPECTRUM].set].title);
                     setWindowTitle(windows[SPECTRUM].widget, line);
                     redraw(SPECTRUM);
                     break;
      case MO:       if (!checkZoom(MO))
                     {
                       if ((string=getStringResource(topShell, "MO-energy")) == NULL)
                       {
                         windows[MO].bottom=1.05*molecules[menudata->which].orbitals[0].energy;
                         windows[MO].top=1.05*molecules[menudata->which].orbitals[molecules[menudata->which].nbasfu-1].energy;
                       }
                       else
                       {
                         windows[MO].bottom=atof(strtok(string, ":"));
                         windows[MO].top=atof(strtok(NULL, ":"));
                       }
                     }
                     string=getStringResource(XtParent(windows[MO].widget), "title");
                     sprintf(line, string, molecules[windows[MO].set].title);
                     setWindowTitle(windows[MO].widget, line);
                     redraw(MO);
                     break;
      case HISTORY:  string=getStringResource(XtParent(windows[HISTORY].widget), "title");
                     sprintf(line, string, molecules[windows[HISTORY].set].title);
                     setWindowTitle(windows[HISTORY].widget, line);
                     redraw(HISTORY);
                     break;
    }
  }
}

void historyMouseAction(unsigned int button, int state, int x, int y)
{
  struct MOLECULE *mol;
  double xw, yw, zw;
  int i;

  mol=&molecules[windows[HISTORY].set];
  windows[HISTORY].mouseX=x;
  windows[HISTORY].mouseY=y;
  if (button == Button1 && state == TRUE)
  {
    getWorldCoordinates(x, y, 0.0, &xw, &yw, &zw);
    i=(int)floor(xw);
    mol->cycle=xw-(double)i < (double)(i+1)-xw ? i : i+1;
    if (xw < 1.0)
    {
      mol->cycle=1;
      getScreenCoordinates(1.0, 0.0, 0.0, &xw, &yw, &zw);
      windows[HISTORY].mouseX=(int)xw;
    }
    else if (xw > (double)(mol->nhist))
    {
      mol->cycle=mol->nhist;
      getScreenCoordinates((double)(mol->nhist), 0.0, 0.0, &xw, &yw, &zw);
      windows[HISTORY].mouseX=(int)xw;
    }
  }

  if (button == Button1 && state == FALSE)
  {
    getScreenCoordinates((double)(mol->cycle), 0.0, 0.0, &xw, &yw, &zw);
    windows[HISTORY].mouseX=(int)xw;
  }
  setGeometry(TRUE);
}

void MODiagramMouseAction(XmDrawingAreaCallbackStruct *data, int state)
{
  struct MOLECULE *mol;
  int imoSave, imol;

  mol=&molecules[windows[MO].set];
  imoSave=mol->imo;
  if (data->event->xbutton.button == Button1 && state == TRUE)
  {
    picking=TRUE;
    swapBuffers=FALSE;
    mol->imo=doPicking(data->event->xbutton.x, data->event->xbutton.y,
                       drawMODiagram, &imol);
    picking=FALSE;
    swapBuffers=TRUE;
    if (mol->imo != -1)
    {
      windows[MO].set=imol;
      ringBell();
      updateMOs(imoSave);
      setMenuItem(MO_TRANSITION, True);
    }
    else
      setMenuItem(MO_TRANSITION, False);
    annotateWavefunction();
  }
  else if (data->event->xbutton.button == Button2)
  {
    if (state == TRUE)
      initZoom(windows[MO].widget, data);
    else
      endZoom(windows[MO].widget, data->event->xbutton.x, data->event->xbutton.y);
  }
}

GLint doPicking(int x, int y, void draw(), int *molecule)
{
  GLfloat picksize=3.0;
  GLuint buffer[BUFFERSIZE], n, *p, z, zmax;
  GLint viewport[4], picked=(-1);
  GLint hits;
  register int i;

  *molecule=(-1);
  glGetIntegerv(GL_VIEWPORT, viewport);
  glSelectBuffer(BUFFERSIZE, buffer);
  glRenderMode(GL_SELECT);
  glInitNames();
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPickMatrix((GLdouble)x, (GLdouble)(viewport[3]-y), picksize, picksize,
                viewport);
  (*draw)((Widget)0, (caddr_t)0, (XmDrawingAreaCallbackStruct *)0);
  hits=glRenderMode(GL_RENDER);
  p=buffer;
  zmax=0;
  if (debug) printf("%d hits\n", hits);

/* Buffer overrun, but the data in the buffer is still good */

  if (hits == -1) hits=1;
  for (i=0; i<hits; i++)
  {
    n=*p++;
    z=*p++;
    if (debug) printf("  z1 = %u, z2 = %u, names = ", z, *p);
    p++;
    while (n-- > 0)
    {
      if (debug) printf("%u, ", *p);
      *molecule=*p;
      p++;
      if (--n) break;
      if (*p && (int)(*p) != (-1) && z >= zmax)
      {
        picked=(GLint)(*p)-1;
        zmax=z;
      }
      p++;
    }
    if (debug) printf("\n");
  }
  if (debug)
    printf("picked: molecule: %d, item: %d\n", *molecule, picked);
  return(picked);
}

void setGeometry(int doConnectivity)
{
  struct MOLECULE *mol;
  const GLfloat black[4] = {0.0, 0.0, 0.0, 0.0};
  double top, bottom, right, left, force;
  char line[MAXLENLINE], *word;
  int cycle;
  register int i, j;

  mol=&molecules[windows[HISTORY].set];
  naSaved=mol->na;
/*cycle=((int)ceil(mol->cycle-0.5)-1);
  if (cycle < 0) cycle=0; */
  cycle=mol->cycle-1;
/*if (cycle > 0) mol->na=mol->optimization[j].natoms; */
  i=mol->optimization[cycle].coords;
  for (j=0; j<mol->na; j++)
  {
    if (mol->atoms[j].flags & ORIGINAL)
    {
      mol->atoms[j].x=mol->history[i].x;
      mol->atoms[j].y=mol->history[i].y;
      mol->atoms[j].z=mol->history[i].z;
    }
    i++;
  }
  if (mol->unitcell)
  {
    i=(int)ceil(mol->cycle-0.5);
    for (j=0; j<mol->unitcell->nc; j++)
    {
      mol->unitcell[0].corners[j].x=mol->unitcell[i].corners[j].x;
      mol->unitcell[0].corners[j].y=mol->unitcell[i].corners[j].y;
      mol->unitcell[0].corners[j].z=mol->unitcell[i].corners[j].z;
    }
    expandCell(TRUE, FALSE);
  }
  word=getStringResource(topShell, "historyTitle");
/*sprintf(line, word, (int)mol->cycle, mol->optimization[(int)mol->cycle-1].energy,
          mol->optimization[(int)mol->cycle-1].gnorm);*/
  sprintf(line, word, mol->cycle, mol->optimization[cycle].energy,
          mol->optimization[cycle].gnorm);
  historyAnnotation=makeAnnotation(historyAnnotation, CENTERED, 0.0, 1.0, 0.0,
                                   stdcol[BLACK], black, 0, 0, line);

  if (!doConnectivity) return;
  top=windows[VIEWER].top;
  bottom=windows[VIEWER].bottom;
  right=windows[VIEWER].right;
  left=windows[VIEWER].left;
  force=forceScale;
  makeConnectivity(mol, FALSE, FALSE);
  windows[VIEWER].top=top;
  windows[VIEWER].bottom=bottom;
  windows[VIEWER].right=right;
  windows[VIEWER].left=left;
  forceScale=force;
  for (i=0; i<mol->ninternal; i++)
    (void)calcInternal(mol, i);
}

void restoreGeometry(struct SAVE *c, int imol)
{
  register int i, j;

/*molecules[imol].na=naSaved;*/
  j=0;
  for (i=0; i<molecules[imol].na; i++)
  {
    molecules[imol].atoms[i].x=c[i].x;
    molecules[imol].atoms[i].y=c[i].y;
    molecules[imol].atoms[i].z=c[i].z;
    strcpy(molecules[imol].atoms[i].name, c[i].name);
  }
  if (c != molecules[imol].coord) fremem((void *)&c);
}

void ringBell(void)
{
  int tty;
  char *command, bell='\007';

  if ((command=getStringResource(topShell, "Bell")) == NULL)
  {
    tty=open("/dev/tty", O_WRONLY | O_NOCTTY);
    write(tty, &bell, 1);
  }
  else
    system(command);
}

void setAnimation(int animate)
{
  static XtWorkProcId id=0;
  int all=TRUE;
  register int i;

  for (i=0; i<nmolecule; i++)
  {
    restoreGeometry(molecules[i].coord, i);
    if (molecules[i].mode != (-1)) all=FALSE;
  }
  if ((animate == ANIMATE) && !id)
    id=XtAppAddWorkProc(app, normalMode, (XtPointer)NULL);
  if (((animate != ANIMATE) || all) && id)
  {
    XtRemoveWorkProc(id);
    id=0;
  }
}

void setHistoryAnimation(Widget widget, caddr_t mode, XmAnyCallbackStruct *data)
{
  static XtWorkProcId id=0;
  int start=FALSE;

  switch ((int)mode)
  {
    case 0: if (!id) start=TRUE;
            else     start=FALSE;
	      break;
    case 1: if (!id) start=TRUE;
            else     return;
     	      break;
    case 2: if (!id) return;
            else     start=FALSE;
	      break;
  }

  if (start)
  {
    animate=TRAJECTORY;
    id=XtAppAddWorkProc(app, animateHistory, (XtPointer)0);
  }
  else
  {
    XtRemoveWorkProc(id);
    id=0;
    animate=0;
  }
}

void setWindow(int imol)
{
  XmToggleButtonCallbackStruct data;

  setWindowTitle(windows[VIEWER].widget, molecules[imol].title);
  if (nmolecule > 1)
  {
    data.set=TRUE;
    (void)selectMolecule((Widget)0, (XtPointer)&windows[VIEWER].selectMenu[imol+2], &data);
  }
}

void deleteSelection()
{
  register int i, j;

  if (windows[VIEWER].set < 0)
  {
    for (i=0; i<nmolecule; i++)
    {
      for (j=0; j<molecules[i].na; j++)
        molecules[i].atoms[j].flags&=~SELECTED;
      if (molecules[i].unitcell)
      {
        for (j=0; j<molecules[i].unitcell[0].nc; j++)
          molecules[i].unitcell[0].corners[j].flags&=~SELECTED;
      }
    }
  }
  else
  {
    i=windows[VIEWER].set;
    for (j=0; j<molecules[i].na; j++)
      molecules[i].atoms[j].flags&=~SELECTED;
    if (molecules[i].unitcell)
    {
      for (j=0; j<molecules[i].unitcell[0].nc; j++)
        molecules[i].unitcell[0].corners[j].flags&=~SELECTED;
    }
  }
  intcoord=0;
  clicked[0]=clicked[1]=clicked[2]=clicked[3]=(-1);
  showMenu=TRUE;
}

void trim(char *str)
{
  char *p;

  p=str;
  while (*p == ' ' || *p == '\t') p++;
  if (p != str)
  {
    memmove(str, p, strlen(p));
    str[strlen(p)]='\0';
  }
}
