/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                               D R A W S P . C                                *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: drawsp.c,v 1.6 2003/11/07 11:00:03 jrh Exp $
* $Log: drawsp.c,v $
* Revision 1.6  2003/11/07 11:00:03  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:05:27  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:25:18  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:47:45  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:36  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:40:41  jrh
* Initial revision
*
*/
#include<ctype.h>
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include<X11/Xlib.h>
#include<X11/Intrinsic.h>
#include<X11/StringDefs.h>
#include<X11/keysym.h>
#include<Xm/Xm.h>
#include "viewmol.h"

#define max(a, b) (a) > (b) ? (a) : (b)
#define min(a, b) (a) < (b) ? (a) : (b)

void setWindowColor(int, Pixel, const float *);
void paper(Dimension, Dimension);
void pixelToWorld(int, double *, double *);
void annotateNormalMode(struct MOLECULE *);

extern int StringWidth(XFontStruct *, char *);
extern int StringHeight(XFontStruct *);
extern int StringAscent(XFontStruct *);
extern void (*drawColor4fv)(const GLfloat *);
extern void (*drawBegin)(GLenum), (*drawEnd)(void), (*drawVertex2d)(double, double);
extern void (*drawString)(char *, double, double, double, double, GLuint, char);
extern void (*drawLineStipple)(GLint, GLushort), (*drawDisable)(GLenum);
extern void (*drawLineWidth)(GLfloat);
extern void (*drawClearColor)(GLclampf, GLclampf, GLclampf, GLclampf);
extern void printDialog(Widget, caddr_t, XmAnyCallbackStruct *);
extern char *getStringResource(Widget, char *);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern void drawBackground(int, Pixel, double);
extern void setAnimation(int);
extern void restoreGeometry(struct SAVE *, int);
extern void redraw(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 char *selectFile(char *, char *, int);
extern void redraw(int);
extern void selectMolecule(Widget, caddr_t, XmToggleButtonCallbackStruct *);
extern double makeTics(double, double, double *, char *);

extern struct MOLECULE *molecules;
extern struct WINDOW windows[];
extern Widget topShell;
extern int nmolecule;
extern int lines, animate, normalModeAnnotation, xIsTop, showGrid;
extern double temp, wnScale, amplitude, spectrumLineWidth;
extern int rgbMode, picking, swapBuffers;
extern Pixel stdcol[9];

static double *expwn, *expint;
static int expwnc;

void drawSpectrum(Widget w, caddr_t client_data, XmDrawingAreaCallbackStruct *data)
{
  const double h=6.626176e-34, c=2.99792458e10, kb=1.380662e-23;
  const float red[4] = {1.0, 0.0, 0.0, 0.0};
  const float blue[4] = {0.0, 0.0, 1.0, 0.0};
/*FILE *file;*/
  Dimension width, height;
  double *scan, *s;
  double f, zz, wmax, frq, frqinc, shape, a, b;
  int renderMode, imol;
  register int i, j;

  if (temp > 0.0)
    f=-h*c/(kb*temp);
  else
    f=-h*c/(kb*0.001);
/*a=512.0*(atan(1.0))**3*6.022045e23/(h*c*3000.0*log(10.0)); */

  imol=windows[SPECTRUM].set;
  glGetIntegerv(GL_RENDER_MODE, &renderMode);
  if (renderMode == GL_RENDER)
    glXMakeCurrent(XtDisplay(windows[SPECTRUM].widget), XtWindow(windows[SPECTRUM].widget),
                             windows[SPECTRUM].context);
  if (!picking)
  {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
  }
  glShadeModel(GL_FLAT);
  XtVaGetValues(windows[SPECTRUM].widget, XtNwidth, &width, XtNheight, &height, NULL);
  paper(width, height);

/* Draw line spectrum */

  (*drawLineWidth)((GLfloat)spectrumLineWidth);
  glPushName(imol);
  if (lines)
  {
/* Loop over wave numbers   */

    for (i=0; i<molecules[imol].nmodes; i++)
    {
      if (i == molecules[imol].mode) continue;
      frq=wnScale*molecules[imol].normal_modes[i].wavenumber;
	if (frq >= windows[SPECTRUM].left && frq <= windows[SPECTRUM].right)
	{
        glPushName(i+1);
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)(frq, max(0.0, windows[SPECTRUM].top));
        if (windows[SPECTRUM].mode == SPECTRUM_ALL)
          (*drawVertex2d)(frq, windows[SPECTRUM].bottom);
        if (windows[SPECTRUM].mode == SPECTRUM_IR &&
            molecules[imol].normal_modes[i].rel_ir_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[i].rel_ir_intensity));
        if (windows[SPECTRUM].mode == SPECTRUM_RAMAN &&
            molecules[imol].normal_modes[i].rel_raman_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[i].rel_raman_intensity));
        if (windows[SPECTRUM].mode == SPECTRUM_INS &&
            molecules[imol].normal_modes[i].rel_ins_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[i].rel_ins_intensity));
        (*drawEnd)();
        glPopName();
      }
    }

/* Plot the selected wave number in red */

    if (molecules[imol].mode > (-1) && molecules[imol].mode <= molecules[imol].nmodes)
    {
      setWindowColor(FOREGROUND, stdcol[RED], red);
      frq=wnScale*molecules[imol].normal_modes[molecules[imol].mode].wavenumber;
	if (frq >= windows[SPECTRUM].left && frq <= windows[SPECTRUM].right)
	{
        glPushName(molecules[imol].mode+1);
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)(frq, max(0.0, windows[SPECTRUM].top));
        if (windows[SPECTRUM].mode == SPECTRUM_ALL)
          (*drawVertex2d)(frq, windows[SPECTRUM].bottom);
        if (windows[SPECTRUM].mode == SPECTRUM_IR &&
            molecules[imol].normal_modes[molecules[imol].mode].rel_ir_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[molecules[imol].mode].rel_ir_intensity));
        if (windows[SPECTRUM].mode == SPECTRUM_RAMAN &&
            molecules[imol].normal_modes[molecules[imol].mode].rel_raman_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[molecules[imol].mode].rel_raman_intensity));
        if (windows[SPECTRUM].mode == SPECTRUM_INS &&
            molecules[imol].normal_modes[imol].rel_ins_intensity != 0.0)
          (*drawVertex2d)(frq, min(windows[SPECTRUM].bottom, molecules[imol].normal_modes[molecules[imol].mode].rel_ins_intensity));
        (*drawEnd)();
        glPopName();
      }
    }
  }
  else
  {
/* Draw spectrum with gaussian functions */

    s=(double *)getmem(molecules[imol].nmodes, sizeof(double));
    scan=(double *)getmem((size_t)(width+1), sizeof(double));
    for (i=0; i<molecules[imol].nmodes; i++)
    {
      if (molecules[imol].normal_modes[i].ir_intensity > 0.0 &&
          molecules[imol].normal_modes[i].wavenumber > 0.0)
      {
        zz=1.0-exp(f*wnScale*molecules[imol].normal_modes[i].wavenumber);
        s[i]=0.5*molecules[imol].normal_modes[i].rel_ir_intensity*wnScale
            *molecules[imol].normal_modes[i].wavenumber/zz;
      }
    }

/* Scan spectrum */
    frq=windows[SPECTRUM].left;
    frqinc=(windows[SPECTRUM].right-windows[SPECTRUM].left)/(double)width;
    for (i=0; i<width+1; i++)
    {
      scan[i]=0.0;
      for (j=0; j<molecules[imol].nmodes; j++)
      {
        if (molecules[imol].normal_modes[j].ir_intensity > 0.0 &&
            molecules[imol].normal_modes[j].wavenumber > 0.0)
        {
          shape=frq-wnScale*molecules[imol].normal_modes[j].wavenumber;
          shape=exp(-32.0*shape*shape/(wnScale*molecules[imol].normal_modes[j].wavenumber));
          scan[i]+=s[j]*shape;
        }
      }
/*    scan[i]*=a; */
      frq+=frqinc;
    }

/* Scale intensities  */

    wmax=scan[0];
    for (i=1; i<width+1; i++)
      wmax=max(wmax, scan[i]);
    for (i=0; i<width+1; i++)
      scan[i]*=100.0/wmax;

    (*drawBegin)(GL_LINE_STRIP);
    (*drawVertex2d)(max(0.0, windows[SPECTRUM].left), max(0.0, windows[SPECTRUM].top));

/* And draw it */

/*  file=fopen("spectrum.tbl", "w"); */
    frq=windows[SPECTRUM].left;
    for (i=0; i<width; i++)
    {
	if (scan[i] >= windows[SPECTRUM].top && scan[i] <= windows[SPECTRUM].bottom &&
	    scan[i+1] >= windows[SPECTRUM].top && scan[i+1] <= windows[SPECTRUM].bottom)
	{
        (*drawVertex2d)(frq, scan[i]);
	}
      else if (scan[i] > windows[SPECTRUM].bottom && scan[i+1] < windows[SPECTRUM].bottom)
	{
	  a=(scan[i]-scan[i+1])/frqinc;
	  b=scan[i]-a*frq;
        (*drawBegin)(GL_LINE_STRIP);
        (*drawVertex2d)((windows[SPECTRUM].bottom-b)/a, windows[SPECTRUM].bottom);
	}
	else if (scan[i] < windows[SPECTRUM].bottom && scan[i+1] > windows[SPECTRUM].bottom)
	{
	  a=(scan[i]-scan[i+1])/frqinc;
	  b=scan[i]-a*frq;
        (*drawVertex2d)((windows[SPECTRUM].bottom-b)/a, windows[SPECTRUM].bottom);
        (*drawEnd)();
	}
	else if (scan[i] < windows[SPECTRUM].top && scan[i+1] > windows[SPECTRUM].top)
	{
	  a=(scan[i]-scan[i+1])/frqinc;
	  b=scan[i]-a*frq;
        (*drawBegin)(GL_LINE_STRIP);
        (*drawVertex2d)((windows[SPECTRUM].top-b)/a, windows[SPECTRUM].top);
	}
	else if (scan[i] > windows[SPECTRUM].top && scan[i+1] < windows[SPECTRUM].top)
	{
	  a=(scan[i]-scan[i+1])/frqinc;
	  b=scan[i]-a*frq;
        (*drawBegin)(GL_LINE_STRIP);
        (*drawVertex2d)((windows[SPECTRUM].top-b)/a, windows[SPECTRUM].top);
	}
/*    fprintf(file, "%10.3f  %10.6f\n", frq, scan[i]); */
      frq+=frqinc;
    }
    (*drawEnd)();
/*  fclose(file); */
    fremem((void **)&scan);
  }
  glPopName();

/* If we have an observed spectrum draw it */

  if (expwn != NULL)
  {
    setWindowColor(FOREGROUND, stdcol[BLUE], blue);
    (*drawBegin)(GL_LINE_STRIP);
    for (j=0; j<expwnc; j++)
      (*drawVertex2d)(expwn[j], expint[j]);
    (*drawEnd)();
    if (!lines)
    {
/*    zz=0.0;
      wmax=0.0;
      for (i=0; i<expwnc; i++)
      {
        frq=0.0;
        for (j=0; j<molecules[imol].nmodes; j++)
        {
          if (molecules[imol].normal_modes[j].ir_intensity > 0.0 &&
              molecules[imol].normal_modes[j].wavenumber > 0.0)
          {
            shape=expwn[i]-wnScale*molecules[imol].normal_modes[j].wavenumber;
            shape=exp(-32.0*shape*shape/(wnScale*molecules[imol].normal_modes[j].wavenumber));
            frq+=s[j]*shape;
          }
        }
        zz+=fabs(fabs(expint[i])-fabs(frq));
        wmax+=fabs(expint[i]);
      }
      printf("R = %10.4f\n", zz/wmax); */
      fremem((void **)&s);
    }
  }
  if (swapBuffers) glXSwapBuffers(XtDisplay(windows[SPECTRUM].widget), XtWindow(windows[SPECTRUM].widget));
}

void paper(Dimension width, Dimension height)
{
  double grid, gridStep, xpix, ypix, top, bottom, left, right, texty;
  int swidth1, swidth2, sfullheight, sheight; 
  char line[6], *word, dummy[8];
  register int i;

  word=getStringResource(topShell, "wavenumber");
  swidth1=StringWidth(windows[SPECTRUM].font, word)+2;   /* Write labels */
  swidth2=StringWidth(windows[SPECTRUM].font, "100")+2;
  sfullheight=StringHeight(windows[SPECTRUM].font)+4;
  sheight=StringAscent(windows[SPECTRUM].font)+2;
  xpix=(windows[SPECTRUM].right-windows[SPECTRUM].left)/(double)(width-2*(swidth2+5));
  ypix=fabs(windows[SPECTRUM].top-windows[SPECTRUM].bottom)/(double)(height-2*(sfullheight+5));
  windows[SPECTRUM].horizontalMargin=swidth2+5;
  windows[SPECTRUM].verticalMargin=sfullheight+5;
  left=windows[SPECTRUM].left-xpix*windows[SPECTRUM].horizontalMargin;
  right=windows[SPECTRUM].right+xpix*windows[SPECTRUM].horizontalMargin;
  if (xIsTop)
  {
    bottom=windows[SPECTRUM].bottom+ypix*windows[SPECTRUM].verticalMargin;
    top=windows[SPECTRUM].top-ypix*windows[SPECTRUM].verticalMargin;
    texty=top+sheight*ypix;
  }
  else
  {
    bottom=windows[SPECTRUM].top-ypix*windows[SPECTRUM].verticalMargin;
    top=windows[SPECTRUM].bottom+ypix*windows[SPECTRUM].verticalMargin;
    texty=bottom+(sfullheight-sheight)*ypix;
  }
  glOrtho(left, right, bottom, top, windows[SPECTRUM].near,
          windows[SPECTRUM].far);

  drawBackground(SPECTRUM, windows[SPECTRUM].background, (double)0.0);
  setWindowColor(FOREGROUND, windows[SPECTRUM].foreground,
                 windows[SPECTRUM].foreground_rgb);
  (*drawLineWidth)((GLfloat)spectrumLineWidth);

  (*drawString)(word, right, texty, 0.0, (double)swidth1*xpix,
                windows[SPECTRUM].GLfontId, 'r');
  word=getStringResource(topShell, "intensity");
  if (xIsTop)
  {
    (*drawString)(word, left+xpix, bottom-(double)(sfullheight-sheight)*ypix, 0.0, 0.0,
                  windows[SPECTRUM].GLfontId, 'l');
  }
  else
  {
    (*drawString)(word, left+xpix, top-(double)sheight*ypix, 0.0, 0.0,
                  windows[SPECTRUM].GLfontId, 'l');
  }

  (*drawBegin)(GL_LINE_LOOP);                            /* Draw frame */
  (*drawVertex2d)(windows[SPECTRUM].left, windows[SPECTRUM].top);
  (*drawVertex2d)(windows[SPECTRUM].left, windows[SPECTRUM].bottom);
  (*drawVertex2d)(windows[SPECTRUM].right, windows[SPECTRUM].bottom);
  (*drawVertex2d)(windows[SPECTRUM].right, windows[SPECTRUM].top);
  (*drawEnd)();
  gridStep=makeTics(windows[SPECTRUM].left, windows[SPECTRUM].right, &grid, dummy);
  while (grid < windows[SPECTRUM].right)
  {
    if (grid >= windows[SPECTRUM].left && grid <= windows[SPECTRUM].right)
    {
      (*drawBegin)(GL_LINES);                 /* Tick marks */
      (*drawVertex2d)(grid, windows[SPECTRUM].top);
      (*drawVertex2d)(grid, windows[SPECTRUM].top-5.0*ypix);
      (*drawEnd)();
      (*drawBegin)(GL_LINES);
      (*drawVertex2d)(grid, windows[SPECTRUM].bottom);
      (*drawVertex2d)(grid, windows[SPECTRUM].bottom+5.0*ypix);
      (*drawEnd)();
      if (showGrid)
      {
        glEnable(GL_LINE_STIPPLE);            /* Dashed lines */
        (*drawLineStipple)(1, 0xf0f0);
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)(grid, windows[SPECTRUM].top);
        (*drawVertex2d)(grid, windows[SPECTRUM].bottom);
        (*drawEnd)();
        (*drawDisable)(GL_LINE_STIPPLE);
      }
      sprintf(line, "%d", (int)grid);
      swidth2=StringWidth(windows[SPECTRUM].font, line);
      if (((windows[SPECTRUM].left < windows[SPECTRUM].right &&
           grid+0.5*(double)swidth2*xpix < windows[SPECTRUM].right-(double)swidth1*xpix) ||
          (windows[SPECTRUM].left > windows[SPECTRUM].right &&
           grid+0.5*(double)swidth2*xpix > windows[SPECTRUM].right-(double)swidth1*xpix)))
      {
        (*drawString)(line, grid, texty, 0.0, (double)swidth2*xpix,
  	                windows[SPECTRUM].GLfontId, 'c');
      }
    }
    grid+=gridStep;
  }
  grid=0.0;
  for (i=0; i<5; i++)
  {
    if (grid >= windows[SPECTRUM].top && grid <= windows[SPECTRUM].bottom)
    {
      (*drawBegin)(GL_LINES);                 /* Tick marks */
      (*drawVertex2d)(windows[SPECTRUM].left, grid);
      (*drawVertex2d)(windows[SPECTRUM].left-5.0*xpix, grid);
      (*drawEnd)();
      (*drawBegin)(GL_LINES);
      (*drawVertex2d)(windows[SPECTRUM].right, grid);
      (*drawVertex2d)(windows[SPECTRUM].right+5.0*xpix, grid);
      (*drawEnd)();
      if (showGrid)
      {
        glEnable(GL_LINE_STIPPLE);            /* Dashed lines */
        (*drawLineStipple)(1, 0xf0f0);
        (*drawBegin)(GL_LINES);
        (*drawVertex2d)(windows[SPECTRUM].left, grid);
        (*drawVertex2d)(windows[SPECTRUM].right, grid);
        (*drawEnd)();
        (*drawDisable)(GL_LINE_STIPPLE);
      }
      sprintf(line, "%d", (int)grid);
      if (xIsTop)
      {
        (*drawString)(line, left+xpix, grid+0.5*(double)(sheight-2)*ypix,
                      0.0, 0.0, windows[SPECTRUM].GLfontId, 'l');
      }
      else
      {
        (*drawString)(line, left+xpix, grid-0.5*(double)(sheight-2)*ypix,
                      0.0, 0.0, windows[SPECTRUM].GLfontId, 'l');
      }
    }
    grid+=25.0;
  }
}

void readSpectrum(Widget w, caddr_t client_data, XmAnyCallbackStruct *data)
{
  FILE *file;
  size_t maxwnc=100;
  double maxint=0.0;
  char *filename, line[MAXLENLINE], *word;
  register int i;

  if ((filename=selectFile("*", "", TRUE)) != NULL)
  {
    if ((file=fopen(filename, "r")) != NULL)
    {
      if (expwn != NULL)
      {
        fremem((void **)&expwn);
        fremem((void **)&expint);
      }
      expwn=(double *)getmem(maxwnc, sizeof(double));
      expint=(double *)getmem(maxwnc, sizeof(double));
      expwnc=0;
      while (fgets(line, MAXLENLINE, file) != NULL)
      {
        if (line[0] == '#' || isalpha(line[0])) continue;
        if ((word=strtok(line, " \t")) != NULL)
          expwn[expwnc]=atof(word);
        if ((word=strtok(NULL, " \t")) != NULL)
          expint[expwnc]=atof(word);
        maxint=expint[expwnc] > maxint ? expint[expwnc] : maxint;
        if (++expwnc >= maxwnc)
        {
          maxwnc+=100;
          expwn=(double *)expmem((void *)expwn, maxwnc, sizeof(double));
          expint=(double *)expmem((void *)expint, maxwnc, sizeof(double));
        }
      }
      expwn=(double *)expmem((void *)expwn, expwnc, sizeof(double));
      expint=(double *)expmem((void *)expint, expwnc, sizeof(double));
      fclose(file);
      for (i=0; i<expwnc; i++)
        expint[i]=100.0*expint[i]/maxint;
      setMenuItem(SPECTRUM_DELETE, True);
      redraw(SPECTRUM);
    }
  }
}

void deleteSpectrum(Widget w, caddr_t client_data, XmAnyCallbackStruct *data)
{
  fremem((void **)&expwn);
  fremem((void **)&expint);
  expwnc=0;
  setMenuItem(SPECTRUM_DELETE, False);
}

void setWindowColor(int where, Pixel color, const float *rgbColor)
{
  if (rgbMode)
  {
    switch (where)
    {
      case FOREGROUND: (*drawColor4fv)(rgbColor);
                       break;
      case BACKGROUND: (*drawClearColor)(rgbColor[0], rgbColor[1],
                                         rgbColor[2], rgbColor[3]);
                       break;
    }
  }
  else
  {
    switch (where)
    {
      case FOREGROUND: glIndexi(color);
                       break;
      case BACKGROUND: glClearIndex(color);
                       break;
    }
  }
}

Pixel getPixel(int color)
{
  return(stdcol[color]);
}

void pixelToWorld(int which, double *xpix, double *ypix)
{
  Dimension width, height;

  XtVaGetValues(windows[which].widget, XmNwidth, &width, XmNheight, &height, NULL);
  *xpix=(windows[which].right-windows[which].left)/(double)(width-2*windows[which].horizontalMargin);
  *ypix=(windows[which].top-windows[which].bottom)/(double)(height-2*windows[which].verticalMargin);
}

void quitSpectrum(Widget w, caddr_t client_data, XmAnyCallbackStruct *data)
{
  register int i;

  if (windows[SPECTRUM].widget == NULL) return;
  glXMakeCurrent(XtDisplay(windows[VIEWER].widget), XtWindow(windows[VIEWER].widget), windows[VIEWER].context);
  fremem((void **)&windows[SPECTRUM].font);
  XtUnmanageChild(XtParent(windows[SPECTRUM].widget));
  XtDestroyWidget(XtParent(windows[SPECTRUM].widget));
  windows[SPECTRUM].widget=NULL;
  if (nmolecule > 0)
  {
    if (animate) setAnimation(animate);
    deleteAnnotation(&normalModeAnnotation);
    for (i=0; i<nmolecule; i++)
    {
      molecules[i].mode=(-1);
      restoreGeometry(molecules[i].coord, i);
    }
    redraw(VIEWER);
  }
  setMenuItem(VIEWER_SPECTRUM, True);
}

void spectrumKeyAction(KeySym keysym)
{
  XmToggleButtonCallbackStruct data;
/*const GLfloat black[4] = {0.0, 0.0, 0.0, 1.0};*/
  int imol;
/*char line[MAXLENLINE], *word;*/

  imol=windows[SPECTRUM].set;
  switch (keysym)
  {
    case XK_Print:  printDialog((Widget)0, (caddr_t)SPECTRUM, (XmAnyCallbackStruct *)0);
                    break;
    case XK_Left:   while (TRUE)
                    {
                      molecules[imol].mode--;
                      if (molecules[imol].mode < 0) molecules[imol].mode=molecules[imol].nmodes-1;
                      if (windows[SPECTRUM].mode == SPECTRUM_IR &&
                          molecules[imol].normal_modes[molecules[imol].mode].ir_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_RAMAN &&
                          molecules[imol].normal_modes[molecules[imol].mode].raman_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_INS &&
                          molecules[imol].normal_modes[molecules[imol].mode].ins_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_ALL &&
                          molecules[imol].normal_modes[molecules[imol].mode].wavenumber != 0.0) break;
                    }
                    break;
    case XK_Right:  while (TRUE)
                    {
                      molecules[imol].mode++;
                      if (molecules[imol].mode >= molecules[imol].nmodes) molecules[imol].mode=0;
                      if (windows[SPECTRUM].mode == SPECTRUM_IR &&
                          molecules[imol].normal_modes[molecules[imol].mode].ir_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_RAMAN &&
                          molecules[imol].normal_modes[molecules[imol].mode].raman_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_INS &&
                          molecules[imol].normal_modes[molecules[imol].mode].ins_intensity != 0.0) break;
                      if (windows[SPECTRUM].mode == SPECTRUM_ALL &&
                          molecules[imol].normal_modes[molecules[imol].mode].wavenumber != 0.0) break;
                    }
                    break;
    case XK_Tab:    if (nmolecule > 1)
                    {
                      data.set=TRUE;
                      do
                      {
                        imol++;
                        if (imol >= nmolecule) imol=0;
                      } while (molecules[imol].normal_modes == NULL);
                      windows[SPECTRUM].set=imol;
                      (void)selectMolecule((Widget)0, (XtPointer)&windows[SPECTRUM].selectMenu[windows[SPECTRUM].set], &data);
                    }
                    break;
  }
  restoreGeometry(molecules[imol].coord, imol);
  if (animate == DISTORT) distortGeometry(amplitude);
  annotateNormalMode(&molecules[imol]);
}

void annotateNormalMode(struct MOLECULE *mol)
{
  const GLfloat black[4] = {0.0, 0.0, 0.0, 1.0};
  char line[MAXLENLINE], *word;

  if (mol->mode != (-1))
  {
    word=getStringResource(topShell, "wavenumberTitle");
    sprintf(line, word, mol->normal_modes[mol->mode].sym,
            wnScale*mol->normal_modes[mol->mode].wavenumber);
    normalModeAnnotation=makeAnnotation(normalModeAnnotation, CENTERED, 0.0, 1.0,
                                          0.0, stdcol[BLACK], black, 0, 0, line);
  }
  else
  {
    if (normalModeAnnotation) deleteAnnotation(&normalModeAnnotation);
  }
}
