/*******************************************************************************
*                                                                              *
*                                   Viewmol                                    *
*                                                                              *
*                              C O L E D I T . C                               *
*                                                                              *
*                 Copyright (c) Joerg-R. Hill, October 2003                    *
*                                                                              *
********************************************************************************
*
* $Id: coledit.c,v 1.6 2003/11/07 10:58:54 jrh Exp $
* $Log: coledit.c,v $
* Revision 1.6  2003/11/07 10:58:54  jrh
* Release 2.4
*
* Revision 1.5  2000/12/10 15:03:02  jrh
* Release 2.3
*
* Revision 1.4  1999/05/24 01:24:58  jrh
* Release 2.2.1
*
* Revision 1.3  1999/02/07 21:45:41  jrh
* Release 2.2
*
* Revision 1.2  1998/01/26 00:47:11  jrh
* Release 2.1
*
* Revision 1.1  1996/12/10  18:40:03  jrh
* Initial revision
*
*/
#include<stdlib.h>
#include<string.h>
#include<X11/Xlib.h>
#include<X11/Intrinsic.h>
#include<X11/Xutil.h>
#include<X11/Xatom.h>
#include<Xm/Xm.h>
#include<Xm/DrawingA.h>
#include<Xm/Form.h>
#include<Xm/Label.h>
#include<Xm/LabelG.h>
#include<Xm/MwmUtil.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<X11/Xmu/StdCmap.h>
#include<GL/glx.h>
#include "viewmol.h"
#include "dialog.h"

void colorEditor(Widget, caddr_t, XmAnyCallbackStruct *);
void colorEditorExit(Widget, caddr_t, XmPushButtonCallbackStruct *);
void GetColor(Widget, caddr_t, XmToggleButtonCallbackStruct *);
void SetColor(Widget, float *, XmScrollBarCallbackStruct *);
void getRGBColor(Widget, Pixel, float *, float *, float *);
void storeColor(Pixel, struct WINDOW *, int);
void SetSmooth(Widget, int *, XmToggleButtonCallbackStruct *);
XVisualInfo *getVisualInfo(Display *, Visual *);
Colormap getRGBcolormap(Display *, int, XVisualInfo *);

extern Widget CreatePushButtonRow(Widget, struct PushButtonRow *, int);
extern void MapBox(Widget, caddr_t, XmAnyCallbackStruct *);
extern void GetMessageBoxButton(Widget, XtPointer, caddr_t);
extern Pixel getPixel(int);
extern void redraw(int);
extern char *getStringResource(Widget, char *);
extern int getIntResource(Widget, char *);
extern void setMenuItem(int, int);
extern void *getmem(size_t, size_t);
extern void *expmem(void *, size_t, size_t);
extern void fremem(void **);
extern int messgb(Widget, int, char *, struct PushButtonRow *, int);
extern Widget initShell(Widget, char *, Widget *, Widget *);

extern Widget topShell;
extern struct MOLECULE *molecules;
extern struct WINDOW windows[];
extern struct ELEMENT *elements;
extern Pixel stdcol[9], historyColor;
extern float historyColor_rgb[4];
extern int rgbMode, ne, debug, projectionMode;
extern int nmolecule;

static Widget dialog, red, green, blue, button[9];
static Pixel pixelSave;
static float rSave, gSave, bSave;
static int window, where, smoothSave;

void colorEditor(Widget widget, caddr_t which, XmAnyCallbackStruct *data)
{
  Widget board, form, form1, form2=(Widget)0, smoothRed, smoothGreen, smoothBlue;
  Pixmap selected, unselected;
  Pixel p;
  static struct PushButtonRow buttons[] = {
    { "ok", colorEditorExit, (XtPointer)TRUE, NULL },
    { "cancel", colorEditorExit, (XtPointer)FALSE, NULL }
  };
  static unsigned char selectedData[] = {
    0xff, 0xff, 0xff, 0x01, 0x00, 0x80, 0x01, 0x00, 0xb8, 0x01, 0x00, 0xb8,
    0x01, 0x00, 0x9c, 0x01, 0x00, 0x9c, 0x01, 0x00, 0x8e, 0x01, 0x00, 0x8e,
    0x01, 0x00, 0x87, 0x01, 0x00, 0x87, 0x01, 0x80, 0x83, 0x01, 0x80, 0x83,
    0x01, 0x80, 0x81, 0x19, 0xc0, 0x81, 0x39, 0xc0, 0x80, 0x71, 0xe0, 0x80,
    0xe1, 0x60, 0x80, 0xc1, 0x71, 0x80, 0x81, 0x33, 0x80, 0x01, 0x3f, 0x80,
    0x01, 0x1e, 0x80, 0x01, 0x1c, 0x80, 0x01, 0x00, 0x80, 0xff, 0xff, 0xff};
  static unsigned char unselectedData[] = {
    0xff, 0xff, 0xff, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80,
    0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80,
    0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80,
    0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80,
    0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80,
    0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0x01, 0x00, 0x80, 0xff, 0xff, 0xff};
  float *c=NULL, r, g, b, r1, g1, b1;
  int depth;
  char buttonName[8];
  register int i, j;

  /* This function creates the dialog for the color editor */

  setMenuItem(VIEWER_FOREGROUND, False);
  setMenuItem(VIEWER_BACKGROUND, False);
  if (windows[SPECTRUM].widget != 0)
  {
    setMenuItem(SPECTRUM_FOREGROUND, False);
    setMenuItem(SPECTRUM_BACKGROUND, False);
  }
  if (windows[MO].widget != 0)
  {
    setMenuItem(MO_FOREGROUND, False);
    setMenuItem(MO_BACKGROUND, False);
  }
  if (windows[HISTORY].widget != 0)
  {
    setMenuItem(HISTORY_ENERGY, False);
    setMenuItem(HISTORY_GRADIENT, False);
    setMenuItem(HISTORY_BACKGROUND, False);
  }

  window=(int)which & 0x7;
  where=((int)which & 0xFFFFFFF8) >> 3;

  dialog=initShell(topShell, "colorEditor", &board, &form);

  form1=XtVaCreateWidget("controlarea1", xmRowColumnWidgetClass, form,
                         XmNorientation, XmHORIZONTAL,
                         XmNtopAttachment, XmATTACH_FORM,
                         XmNleftAttachment, XmATTACH_FORM,
                         XmNrightAttachment, XmATTACH_FORM,
                         XmNradioBehavior, True,
                         NULL);
  switch (where)
  {
    case BACKGROUND: if (rgbMode)
                     {
                       r=windows[window].background_rgb[0];
                       g=windows[window].background_rgb[1];
                       b=windows[window].background_rgb[2];
                       pixelSave=-1;
                     }
                     else
                     {
                       getRGBColor(windows[window].widget, windows[window].background, &r, &g, &b);
                       pixelSave=windows[window].background;
                     }
                     break;
    case FOREGROUND: if (rgbMode)
                     {
                       r=windows[window].foreground_rgb[0];
                       g=windows[window].foreground_rgb[1];
                       b=windows[window].foreground_rgb[2];
                       pixelSave=-1;
                     }
                     else
                     {
                       getRGBColor(windows[window].widget, windows[window].foreground, &r, &g, &b);
                       pixelSave=windows[window].foreground;
                     }
                     break;
    case GNORM:      if (rgbMode)
                     {
                       r=historyColor_rgb[0];
                       g=historyColor_rgb[1];
                       b=historyColor_rgb[2];
                       pixelSave=-1;
                     }
                     else
                     {
                       getRGBColor(windows[window].widget, historyColor, &r, &g, &b);
                       pixelSave=historyColor;
                     }
                     break;
  }
  XtVaGetValues(topShell, XtNdepth, &depth, NULL);
  j=WHITE;
  for (i=0; i<=MAGENTA; i++)
  {
    sprintf(buttonName, "button%1d", i);
    p=getPixel(i);
    selected=XCreatePixmapFromBitmapData(XtDisplay(dialog), XtWindow(dialog),
                                         (char *)selectedData, 24, 24, stdcol[j], p, depth);
    unselected=XCreatePixmapFromBitmapData(XtDisplay(dialog), XtWindow(dialog),
                                           (char *)unselectedData, 24, 24, stdcol[BLACK], p, depth);
    button[i]=XtVaCreateManagedWidget(buttonName, xmToggleButtonWidgetClass, form1,
                                      XmNlabelType, XmPIXMAP,
                                      XmNlabelPixmap, unselected,
                                      XmNselectPixmap, selected,
                                      XmNindicatorOn, False,
                                      NULL);
    XtAddCallback(button[i], XmNvalueChangedCallback, (XtCallbackProc)GetColor, (caddr_t)i);
    getRGBColor(button[i], p, &r1, &g1, &b1);
    if (r1 == r && g1 == g && b1 == b) XtVaSetValues(button[i], XmNset, True, NULL);
    j=BLACK;
  }
  if (rgbMode && where == BACKGROUND && window == VIEWER)
  {
    form2=XtVaCreateWidget("controlarea2", xmRowColumnWidgetClass, form,
                           XmNorientation, XmHORIZONTAL,
                           XmNtopAttachment, XmATTACH_WIDGET,
                           XmNtopWidget, form1,
                           XmNleftAttachment, XmATTACH_FORM,
                           XmNrightAttachment, XmATTACH_FORM,
                           NULL);
    smoothRed=XtVaCreateManagedWidget("smoothRed", xmToggleButtonWidgetClass, form2,
                                      XtVaTypedArg, XmNforeground, XmRString, "Red", 4,
                                      NULL);
    XtAddCallback(smoothRed, XmNvalueChangedCallback, (XtCallbackProc)SetSmooth,
                  (XtPointer)1);
    if (windows[window].smooth & 1) XtVaSetValues(smoothRed, XmNset, True, NULL);
    smoothGreen=XtVaCreateManagedWidget("smoothGreen", xmToggleButtonWidgetClass, form2,
                                        XtVaTypedArg, XmNforeground, XmRString, "Green", 6,
                                        NULL);
    XtAddCallback(smoothGreen, XmNvalueChangedCallback, (XtCallbackProc)SetSmooth,
                  (XtPointer)2);
    if (windows[window].smooth & 2) XtVaSetValues(smoothGreen, XmNset, True, NULL);
    smoothBlue=XtVaCreateManagedWidget("smoothBlue", xmToggleButtonWidgetClass, form2,
                                       XtVaTypedArg, XmNforeground, XmRString, "Blue", 5,
                                        NULL);
    XtAddCallback(smoothBlue, XmNvalueChangedCallback, (XtCallbackProc)SetSmooth,
                  (XtPointer)4);
    if (windows[window].smooth & 4) XtVaSetValues(smoothBlue, XmNset, True, NULL);
  }
  rSave=r;
  gSave=g;
  bSave=b;
  red=XtVaCreateManagedWidget("red", xmScaleWidgetClass, form,
                              XmNorientation, XmHORIZONTAL,
                              XtVaTypedArg, XmNtroughColor, XmRString, "Red", 4,
                              XtVaTypedArg, XmNforeground, XmRString, "Red", 4,
                              XmNvalue, (int)(1000.*r),
                              XmNminimum, 0,
                              XmNmaximum, 1000,
                              XmNdecimalPoints, 3,
                              XmNshowValue, True,
                              XmNsensitive, True,
                              XmNleftAttachment, XmATTACH_FORM,
                              XmNrightAttachment, XmATTACH_FORM,
                              NULL);
  switch (where)
  {
    case FOREGROUND: c=&windows[window].foreground_rgb[0];
                     break;
    case BACKGROUND: c=&windows[window].background_rgb[0];
                     break;
    case GNORM:      c=&historyColor_rgb[0];
                     break;
  }
  XtAddCallback(red, XmNvalueChangedCallback, (XtCallbackProc)SetColor, c);
  XtAddCallback(red, XmNdragCallback, (XtCallbackProc)SetColor, c);
  green=XtVaCreateManagedWidget("green", xmScaleWidgetClass, form,
                                XmNorientation, XmHORIZONTAL,
                                XtVaTypedArg, XmNtroughColor, XmRString, "Green", 6,
                                XtVaTypedArg, XmNforeground, XmRString, "Green", 6,
                                XmNvalue, (int)(1000.*g),
                                XmNminimum, 0,
                                XmNmaximum, 1000,
                                XmNdecimalPoints, 3,
                                XmNshowValue, True,
                                XmNsensitive, True,
                                XmNleftAttachment, XmATTACH_FORM,
                                XmNrightAttachment, XmATTACH_FORM,
                                NULL);
  switch (where)
  {
    case FOREGROUND: c=&windows[window].foreground_rgb[1];
                     break;
    case BACKGROUND: c=&windows[window].background_rgb[1];
                     break;
    case GNORM:      c=&historyColor_rgb[1];
                     break;
  }
  XtAddCallback(green, XmNvalueChangedCallback, (XtCallbackProc)SetColor, c);
  XtAddCallback(green, XmNdragCallback, (XtCallbackProc)SetColor, c);
  blue=XtVaCreateManagedWidget("blue", xmScaleWidgetClass, form,
                               XmNorientation, XmHORIZONTAL,
                               XtVaTypedArg, XmNtroughColor, XmRString, "Blue", 5,
                               XtVaTypedArg, XmNforeground, XmRString, "Blue", 5,
                               XmNvalue, (int)(1000.*b),
                               XmNminimum, 0,
                               XmNmaximum, 1000,
                               XmNdecimalPoints, 3,
                               XmNshowValue, True,
                               XmNsensitive, True,
                               XmNleftAttachment, XmATTACH_FORM,
                               XmNrightAttachment, XmATTACH_FORM,
                               NULL);
  switch (where)
  {
    case FOREGROUND: c=&windows[window].foreground_rgb[2];
                     break;
    case BACKGROUND: c=&windows[window].background_rgb[2];
                     break;
    case GNORM:      c=&historyColor_rgb[2];
                     break;
  }
  XtAddCallback(blue, XmNvalueChangedCallback, (XtCallbackProc)SetColor, c);
  XtAddCallback(blue, XmNdragCallback, (XtCallbackProc)SetColor, c);

  XtManageChild(form1);
  if (rgbMode && where == BACKGROUND && window == VIEWER) XtManageChild(form2);


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

void colorEditorExit(Widget widget, caddr_t which, XmPushButtonCallbackStruct *data)
{
  XColor c;
  Colormap colormap;

  XtDestroyWidget(dialog);
  if (!(int)which)
  {
    switch (where)
    {
      case BACKGROUND: windows[window].background=pixelSave;
                       windows[window].background_rgb[0]=rSave;
                       windows[window].background_rgb[1]=gSave;
                       windows[window].background_rgb[2]=bSave;
                       windows[window].smooth=smoothSave;
                       break;
      case FOREGROUND: windows[window].foreground=pixelSave;
                       windows[window].foreground_rgb[0]=rSave;
                       windows[window].foreground_rgb[1]=gSave;
                       windows[window].foreground_rgb[2]=bSave;
                       break;
      case GNORM:      historyColor=pixelSave;
                       historyColor_rgb[0]=rSave;
                       historyColor_rgb[1]=gSave;
                       historyColor_rgb[2]=bSave;
                       break;
    }
    if (!rgbMode)
    {
      c.pixel=pixelSave;
      c.red=(unsigned short)(rSave*65535.+0.5);
      c.green=(unsigned short)(gSave*65535.+0.5);
      c.blue=(unsigned short)(bSave*65535.+0.5);
      c.flags=DoRed | DoGreen | DoBlue;
      XtVaGetValues(windows[window].widget,
                    XmNcolormap, &colormap,
                    NULL);
      XStoreColor(XtDisplay(windows[window].widget), colormap, &c);
    }
    redraw(window);
  }
  if (projectionMode != ORTHO) setMenuItem(VIEWER_FOREGROUND, True);
  setMenuItem(VIEWER_BACKGROUND, True);
  if (windows[SPECTRUM].widget != 0)
  {
    setMenuItem(SPECTRUM_FOREGROUND, True);
    setMenuItem(SPECTRUM_BACKGROUND, True);
  }
  if (windows[MO].widget != 0)
  {
    setMenuItem(MO_FOREGROUND, True);
    setMenuItem(MO_BACKGROUND, True);
  }
  if (windows[HISTORY].widget != 0)
  {
    setMenuItem(HISTORY_ENERGY, True);
    setMenuItem(HISTORY_GRADIENT, True);
    setMenuItem(HISTORY_BACKGROUND, True);
  }
}

void GetColor(Widget widget, caddr_t which, XmToggleButtonCallbackStruct *data)
{
  XColor c;
  Colormap colormap;
  float r, g, b;

  if (data->set)
  {
    getRGBColor(widget, stdcol[(int)which], &r, &g, &b);
    switch (where)
    {
      case BACKGROUND: windows[window].background_rgb[0]=r;
                       windows[window].background_rgb[1]=g;
                       windows[window].background_rgb[2]=b;
                       c.pixel=windows[window].background;
                       break;
      case FOREGROUND: windows[window].foreground_rgb[0]=r;
                       windows[window].foreground_rgb[1]=g;
                       windows[window].foreground_rgb[2]=b;
                       c.pixel=windows[window].foreground;
                       break;
      case GNORM:      historyColor_rgb[0]=r;
                       historyColor_rgb[1]=g;
                       historyColor_rgb[2]=b;
                       c.pixel=historyColor;
                       break;
    }
    XtVaSetValues(red,   XmNvalue, (int)(1000.*r), NULL);
    XtVaSetValues(green, XmNvalue, (int)(1000.*g), NULL);
    XtVaSetValues(blue,  XmNvalue, (int)(1000.*b), NULL);
    if (!rgbMode)
    {
      c.red=(unsigned short)(r*65535.+0.5);
      c.green=(unsigned short)(g*65535.+0.5);
      c.blue=(unsigned short)(b*65535.+0.5);
      c.flags=DoRed | DoGreen | DoBlue;
      XtVaGetValues(widget,
                    XmNcolormap, &colormap,
                    NULL);
      XStoreColor(XtDisplay(windows[window].widget), colormap, &c);
    }
    redraw(window);
  }
}

void SetColor(Widget widget, float *color, XmScrollBarCallbackStruct *data)
{
  Colormap colormap;
  XColor c;
  float r, g, b;
  register int i;

  *color=0.001*(double)(data->value);
  switch (where)
  {
    case BACKGROUND: c.pixel=windows[window].background;
                     c.red=(unsigned short)(windows[window].background_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(windows[window].background_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(windows[window].background_rgb[2]*65535.+0.5);
                     break;
    case FOREGROUND: c.pixel=windows[window].foreground;
                     c.red=(unsigned short)(windows[window].foreground_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(windows[window].foreground_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(windows[window].foreground_rgb[2]*65535.+0.5);
                     break;
    case GNORM:      c.pixel=historyColor;
                     c.red=(unsigned short)(historyColor_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(historyColor_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(historyColor_rgb[2]*65535.+0.5);
                     break;
  }
  if (!rgbMode)
  {
    c.flags=DoRed | DoGreen | DoBlue;
    XtVaGetValues(windows[window].widget,
                  XmNcolormap, &colormap,
                  NULL);
    XStoreColor(XtDisplay(windows[window].widget), colormap, &c);
  }
  for (i=0; i<=MAGENTA; i++)
  {
    XtVaSetValues(button[i], XmNset, False, NULL);
    getRGBColor(button[i], stdcol[i], &r, &g, &b);
    if ((unsigned short)(65535.*r+0.5) == c.red && (unsigned short)(65535.*g+0.5) == c.green
        && (unsigned short)(65535.*b+0.5) == c.blue)
    {
      XtVaSetValues(button[i], XmNset, True, NULL);
      break;
    }
  }
  redraw(window);
}

void getRGBColor(Widget widget, Pixel pixel, float *r, float *g, float *b)
{
  Display *display;
  Colormap colormap;
  XColor color;

  display=XtDisplay(widget);
  XtVaGetValues(widget, XmNcolormap, &colormap, NULL);
  color.pixel=pixel;
  XQueryColor(display, colormap, &color);
  *r=(float)(color.red)/65535.;
  *g=(float)(color.green)/65535.;
  *b=(float)(color.blue)/65535.;
}

void storeColor(Pixel pixel, struct WINDOW *window, int where)
{
  Display *display;
  Colormap colormap;
  XColor c;

  switch (where)
  {
    case BACKGROUND: getRGBColor(window->widget, pixel, &(window->background_rgb[0]),
                                 &(window->background_rgb[1]), &(window->background_rgb[2]));
                     break;
    case FOREGROUND: getRGBColor(window->widget, pixel, &(window->foreground_rgb[0]),
                                 &(window->foreground_rgb[1]), &(window->foreground_rgb[2]));
                     break;
    case GNORM:      display=XtDisplay(window->widget);
                     XtVaGetValues(window->widget, XmNcolormap, &colormap, NULL);
                     c.pixel=pixel;
                     XQueryColor(display, colormap, &c);
                     historyColor_rgb[0]=(float)(c.red)/65535.;
                     historyColor_rgb[1]=(float)(c.green)/65535.;
                     historyColor_rgb[2]=(float)(c.blue)/65535.;
                     break;
  }
  if (rgbMode) return;
/*if (rgbMode)
    colormap=DefaultColormapOfScreen(XtScreen(window->widget));
  else*/
    XtVaGetValues(window->widget, XmNcolormap, &colormap, NULL);
  c.flags=DoRed | DoGreen | DoBlue;
  switch (where)
  {
    case BACKGROUND: c.pixel=window->background;
                     c.red=(unsigned short)(window->background_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(window->background_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(window->background_rgb[2]*65535.+0.5);
                     break;
    case FOREGROUND: c.pixel=window->foreground;
                     c.red=(unsigned short)(window->foreground_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(window->foreground_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(window->foreground_rgb[2]*65535.+0.5);
                     break;
    case GNORM:      c.pixel=historyColor;
                     c.red=(unsigned short)(historyColor_rgb[0]*65535.+0.5);
                     c.green=(unsigned short)(historyColor_rgb[1]*65535.+0.5);
                     c.blue=(unsigned short)(historyColor_rgb[2]*65535.+0.5);
                     break;
  }
  XStoreColor(XtDisplay(window->widget), colormap, &c);
}

void loadColorMap(void)
{
  char *colorname[9] = { "black", "white", "red", "green", "blue", "yellow",
                         "magenta", "cyan1", "SkyBlue"};
  Display *display;
  Colormap colormap;
  XColor hardwarecolor, exactcolor;
  register int i;

  display=XtDisplay(windows[VIEWER].widget);
  XtVaGetValues(windows[VIEWER].widget,
                XmNcolormap, &colormap,
                NULL);
  for (i=0; i<9; i++)
  {
    if (XAllocNamedColor(display, colormap, colorname[i], &hardwarecolor,
                         &exactcolor))
      stdcol[i]=hardwarecolor.pixel;
    else
      stdcol[i]=0L;
  }
}

void loadAtomColors(XVisualInfo *visualInfo, Colormap colormap)
{
  static struct PushButtonRow buttons[] = {{"exit", GetMessageBoxButton, (XtPointer)0, NULL}};
  static unsigned long *pixels;
  static int allocated=0;
  Display *display;
  XColor c;
  float r, g, b;
  unsigned long nramp;
  struct ELEMENT **e;
  char *word;
  size_t total=0;
  int reserved=0, count=0, hasOrbitals=FALSE;
  register int i, j, k;

  for (i=0; i<nmolecule; i++)
  {
    total+=molecules[i].na;
    if (molecules[i].basisset != NULL || molecules[i].orbitals != NULL)
      hasOrbitals=TRUE;
  }
  if (hasOrbitals) total+=2;
  e=(struct ELEMENT **)getmem(total, sizeof(struct ELEMENT *));

  k=0;
  for (i=0; i<nmolecule; i++)
  {
    for (j=0; j<molecules[i].na; j++)
      e[k++]=molecules[i].atoms[j].element;
  }
  if (hasOrbitals)
  {
    for (i=0; i<ne; i++)
    {
      if (!strcmp(elements[i].symbol, "Ps")) e[k++]=&elements[i];
      if (!strcmp(elements[i].symbol, "Ms")) e[k++]=&elements[i];
    }
  }
  for (i=0; i<total; i++)
  {
    if (e[i])
    {
      for (j=i+1; j<total; j++)
        if (e[j] && e[j] == e[i]) e[j]=(struct ELEMENT *)0;
    }
  }
  for (i=0; i<total; i++)
    if (e[i]) count++;

  display=XtDisplay(windows[VIEWER].widget);
  reserved=getIntResource(topShell, "reservedColors");
  if (!allocated)
  {
    nramp=(visualInfo->colormap_size-20-reserved)/count;
    pixels=(unsigned long *)getmem((size_t)(nramp*count), sizeof(unsigned long));
    while (!XAllocColorCells(display, colormap, True, NULL, 0, pixels,
                             (unsigned long)(nramp*count-reserved)))
    {
      nramp--;
      if (nramp < 3)
      {
        word=getStringResource(topShell, "noColors");
        messgb(topShell, 3, word, buttons, 1);
        exit(-1);
      }
    }
    allocated=nramp*count-reserved;
  }
  else
    nramp=(allocated+reserved)/count;
  if (reserved) nramp-=reserved/count+1;
  if (debug) printf("%ld colors per atom available.\n", nramp);
  k=0;
  c.flags=DoRed | DoGreen | DoBlue;
  for (i=0; i<total; i++)
  {
    if (e[i])
    {
      r=e[i]->dark[0];
      g=e[i]->dark[1];
      b=e[i]->dark[2];
      c.pixel=pixels[k];
      c.red=(unsigned short)(r*65535.+0.5);
      c.green=(unsigned short)(g*65535.+0.5);
      c.blue=(unsigned short)(b*65535.+0.5);
      XStoreColor(display, colormap, &c);
      e[i]->colormap[0]=pixels[k++];
      r=(e[i]->light[0]-e[i]->dark[0])/(nramp-1);
      g=(e[i]->light[1]-e[i]->dark[1])/(nramp-1);
      b=(e[i]->light[2]-e[i]->dark[2])/(nramp-1);
      for (j=1; j<nramp-1; j++)
      {
        c.pixel=pixels[k];
        c.red=(unsigned short)((e[i]->dark[0]+j*r)*65535.+0.5);
        c.green=(unsigned short)((e[i]->dark[1]+j*g)*65535.+0.5);
        c.blue=(unsigned short)((e[i]->dark[2]+j*b)*65535.+0.5);
        XStoreColor(display, colormap, &c);
        if (j == nramp/2) e[i]->colormap[1]=pixels[k];
        k++;
      }
      r=e[i]->light[0];
      g=e[i]->light[1];
      b=e[i]->light[2];
      c.pixel=pixels[k];
      c.red=(unsigned short)(r*65535.+0.5);
      c.green=(unsigned short)(g*65535.+0.5);
      c.blue=(unsigned short)(b*65535.+0.5);
      XStoreColor(display, colormap, &c);
      e[i]->colormap[2]=pixels[k++];
    }
  }
  fremem((void **)&e);
}

void allocWindowColors(void)
{
  static struct PushButtonRow buttons[] = {{"exit", GetMessageBoxButton, (XtPointer)0, NULL}};
  Display *display;
  Colormap colormap;
  Pixel pixel;
  unsigned long ncolors=9, *pixels;
  char *word;
  register int i;

  if (rgbMode)
  {
    XtVaGetValues(windows[VIEWER].widget, XmNbackground, &pixel, NULL);
    getRGBColor(windows[VIEWER].widget, pixel, &(windows[VIEWER].background_rgb[0]),
                &(windows[VIEWER].background_rgb[1]), &(windows[VIEWER].background_rgb[2]));
    XtVaGetValues(windows[VIEWER].widget, XmNforeground, &pixel, NULL);
    getRGBColor(windows[VIEWER].widget, pixel, &(windows[VIEWER].foreground_rgb[0]),
                &(windows[VIEWER].foreground_rgb[1]), &(windows[VIEWER].foreground_rgb[2]));
    windows[VIEWER].background_rgb[3]=1.0;
    windows[VIEWER].foreground_rgb[3]=1.0;
  }
  else
  {
    display=XtDisplay(windows[VIEWER].widget);
    XtVaGetValues(windows[VIEWER].widget, XmNcolormap, &colormap, NULL);
    pixels=(unsigned long *)getmem((size_t)ncolors, sizeof(unsigned long));
    if (!XAllocColorCells(display, colormap, True, NULL, 0, pixels, ncolors))
    {
      word=getStringResource(topShell, "noColors");
      messgb(topShell, 3, word, buttons, 1);
      exit(-1);
    }
    i=0;
    windows[VIEWER].background=pixels[i++];
    windows[VIEWER].foreground=pixels[i++];
    windows[SPECTRUM].background=pixels[i++];
    windows[SPECTRUM].foreground=pixels[i++];
    windows[HISTORY].background=pixels[i++];
    windows[HISTORY].foreground=pixels[i++];
    historyColor=pixels[i++];
    windows[MO].background=pixels[i++];
    windows[MO].foreground=pixels[i++];
    XtVaGetValues(windows[VIEWER].widget, XmNbackground, &pixel, NULL);
    storeColor(pixel, &windows[VIEWER], BACKGROUND);
    XtVaGetValues(windows[VIEWER].widget, XmNforeground, &pixel, NULL);
    storeColor(pixel, &windows[VIEWER], FOREGROUND);
    fremem((void **)&pixels);
  }
}

void SetSmooth(Widget widget, int *which, XmToggleButtonCallbackStruct *data)
{
  if (data->set) windows[window].smooth |= (int)which;
  else           windows[window].smooth &= ~(int)which;
  redraw(window);
}

XVisualInfo *getVisualInfo(Display *display, Visual *vi)
{
  XVisualInfo template;
  int items;

  template.visual=vi;
  template.visualid=vi->visualid;
  /* leaks memory */
  return(XGetVisualInfo(display, VisualIDMask, &template, &items));
}

Colormap getRGBcolormap(Display *dpy, int scrnum, XVisualInfo *visinfo)
{
/*
 * Return an X colormap to use for OpenGL RGB-mode rendering.
 * Input:  dpy - the X display
 *         scrnum - the X screen number
 *         visinfo - the XVisualInfo as returned by glXChooseVisual()
 * Return:  an X Colormap or 0 if there's a _serious_ error.
 */
  Atom hp_cr_maps;
  Status status;
  int numCmaps, i, using_mesa;
  XStandardColormap *standardCmaps;
  Window root = RootWindow(dpy,scrnum);

   /*
    * First check if visinfo's visual matches the default/root visual.
    */
   if (visinfo->visual==DefaultVisual(dpy,scrnum)) {
      /* use the default/root colormap */
      return DefaultColormap( dpy, scrnum );
   }

   /*
    * Check if we're using Mesa.
    */
   if (strstr(glXQueryServerString( dpy, scrnum, GLX_VERSION ), "Mesa")) {
      using_mesa = 1;
   }
   else {
      using_mesa = 0;
   }

   /*
    * Next, if we're using Mesa and displaying on an HP with the "Color
    * Recovery" feature and the visual is 8-bit TrueColor, search for a
    * special colormap initialized for dithering.  Mesa will know how to
    * dither using this colormap.
    */
   if (using_mesa) {
      hp_cr_maps = XInternAtom( dpy, "_HP_RGB_SMOOTH_MAP_LIST", True );
      if (hp_cr_maps
          && visinfo->visual->class==TrueColor
          && visinfo->depth==8) {
         status = XGetRGBColormaps( dpy, root, &standardCmaps,
                                    &numCmaps, hp_cr_maps );
         if (status) {
            for (i=0; i < numCmaps; i++) {
               if (standardCmaps[i].visualid==visinfo->visual->visualid) {
                  Colormap cmap = standardCmaps[i].colormap;
                  XFree(standardCmaps);
                  return cmap;
               }
            }
            XFree(standardCmaps);
         }
      }
   }

   /*
    * Next, try to find a standard X colormap.
    */
#ifndef SOLARIS_BUG
   status = XmuLookupStandardColormap( dpy, visinfo->screen,
                                       visinfo->visualid,
                                       visinfo->depth,
                                       XA_RGB_DEFAULT_MAP,
                                       /* replace */ False,
                                       /* retain */ True);
   if (status == 1) {
      status = XGetRGBColormaps( dpy, root, &standardCmaps,
                                 &numCmaps, XA_RGB_DEFAULT_MAP);
      if (status == 1) {
         for (i = 0; i < numCmaps; i++) {
            if (standardCmaps[i].visualid == visinfo->visualid) {
               Colormap cmap = standardCmaps[i].colormap;
               XFree(standardCmaps);
               return cmap;
            }
         }
         XFree(standardCmaps);
      }
   }
#endif

   /*
    * If we get here, give up and just allocate a new colormap.
    */
   return XCreateColormap( dpy, root, visinfo->visual, AllocNone );
}
