/*
Copyright (C) 2003 by Sean David Fleming

sean@ivec.org

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

The GNU GPL can also be found at http://www.gnu.org
*/

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>

#include "gdis.h"
#include "coords.h"
#include "edit.h"
#include "file.h"
#include "parse.h"
#include "matrix.h"
#include "numeric.h"
#include "surface.h"
#include "select.h"
#include "interface.h"
#include "shortcuts.h"
#include "opengl.h"

/* main structures */
extern struct sysenv_pak sysenv;
extern struct elem_pak elements[];

/* TODO - XML & add diffraction data to elements file */
/**************************************/
/* parse a file for gdis element data */
/**************************************/
/* type - should the element data be treated as default or patched */
/* ie the standard library, or modified values from a gdisrc file */
gint read_elem_data(FILE *fp, gint type)
{
gint i, n, key, flag, num_tokens; 
gint *set;
gchar **buff, *str, *symbol, line[LINELEN];
gdouble *sfc;
struct elem_pak elem, elem_default;
GSList *list;

/* checks */
g_return_val_if_fail(fp != NULL, 1);

/* FIXME - num_keys() is returning a value that is too small */
/*
n = num_keys();
*/
n = 1000;
set = g_malloc0(n * sizeof(gint));

while(!fgetline(fp,line))
  {
  list = get_keywords(line);
  if (list) 
    {
    switch(GPOINTER_TO_INT(list->data))
      {
      case GDIS_ELEM_START:
/* init for element */
         elem.shell_charge = 0.0;
/* init the set flags */
        for (i=n ; i-- ; )
          set[i] = 0;
/* keep looping until end of element data, or EOF */
        flag=0;
        while(!flag)
          {
/* get next line & process for keywords */
          if (fgetline(fp, line))
            break;
          list = get_keywords(line);
          buff = tokenize(line, &num_tokens);

/* process 1st keyword, and subsequent data (if any) */
          if (list)
            {
            key = GPOINTER_TO_INT(list->data);
g_assert(key < n);
            switch (key)
              {
/* these shouldn't be modified if not default */
              case SYMBOL:
                g_assert(num_tokens > 1);
                elem.symbol = g_strdup(*(buff+1));
                break; 
              case NAME:
                g_assert(num_tokens > 1);
                elem.name = g_strdup(*(buff+1));
                break; 
              case NUMBER:
                g_assert(num_tokens > 1);
                elem.number = (gint) str_to_float(*(buff+1));
                set[key]++;
                break; 
              case WEIGHT:
                g_assert(num_tokens > 1);
                elem.weight = str_to_float(*(buff+1));
                set[key]++;
                break; 
              case COVALENT:
                g_assert(num_tokens > 1);
                elem.cova = str_to_float(*(buff+1));
                set[key]++;
                break; 
              case VDW:
                g_assert(num_tokens > 1);
                elem.vdw = str_to_float(*(buff+1));
                set[key]++;
                break; 
              case CHARGE:
                g_assert(num_tokens > 1);
                elem.charge = str_to_float(*(buff+1));
                set[key]++;
                break; 
              case COLOUR:
                g_assert(num_tokens > 3);
                elem.colour[0] = str_to_float(*(buff+1));
                elem.colour[1] = str_to_float(*(buff+2));
                elem.colour[2] = str_to_float(*(buff+3));
if (VEC3MAGSQ(elem.colour) > 3.0)
  {
  VEC3MUL(elem.colour, 1.0/65535.0);
  }
                set[key]++;
                break; 
              case GDIS_END:
                flag++;
                break; 
              }
            }
          g_strfreev(buff); 
          }
/* store the element data according to type (ie global/model only etc.) */
        switch(type)
          {
          case DEFAULT:
            if (elem.number < 0 || elem.number > MAX_ELEMENTS-1)
              printf("Error: Element number %d outside allocated bounds.\n", elem.number);
            else
              memcpy(&elements[elem.number], &elem, sizeof(struct elem_pak));
            break;
          case MODIFIED:
/* TODO - scan for symbol as well (elem_seek with data = NULL) */
            if (!set[NUMBER])
              {
              printf("Warning: missing element number.\n");
              elem.number = 0;
              }
/* get default values */
            get_elem_data(elem.number, &elem_default, NULL);
/* fill out specified values */
            if (set[WEIGHT])
              elem_default.weight = elem.weight;
            if (set[COVALENT])
              elem_default.cova = elem.cova;
            if (set[VDW])
              elem_default.vdw = elem.vdw;
            if (set[CHARGE])
              elem_default.charge = elem.charge;
            if (set[COLOUR])
              {
              elem_default.colour[0] = elem.colour[0];
              elem_default.colour[1] = elem.colour[1];
              elem_default.colour[2] = elem.colour[2];
              }
            put_elem_data(&elem_default, NULL);
            break;
/* ignore other types */
          default:
            break;
          }
/* ignore all other keywords */
      default:
        break;
      }
    }

/* scattering factor coeffs */
  if (g_ascii_strncasecmp("\%gdis_sfc", line, 9) == 0)
    {
    while ((str = file_read_line(fp)))
      {
      buff = tokenize(str, &num_tokens);

if (num_tokens != 12)
  {
  /*
  printf("suspicious line: %s\n", str);
  */
  g_strfreev(buff);
  g_free(str);
  break;
  }
      symbol = g_strdup(*(buff+0));
      if (elem_symbol_test(symbol))
        {
        list = NULL;
        for (i=1 ; i<num_tokens ; i++)
          {
          sfc = g_malloc(sizeof(gdouble));
          *sfc = str_to_float(*(buff+i));
          list = g_slist_prepend(list, sfc);
          }
        list = g_slist_reverse(list);

        g_hash_table_insert(sysenv.sfc_table, symbol, list);

	g_strfreev(buff);
        }
      g_free(str);
      }
    }
  }
/* done */
g_free(set);
return(0);
}

/*****************************************************/
/* write the global exceptions to a specified stream */
/*****************************************************/
/* NB: should be suitable for appending (hence *fp) to gdisrc file */
gint write_elem_data(FILE *fp)
{
GSList *list;
struct elem_pak *elem;

/* checks */
g_return_val_if_fail(fp != NULL, 1);

list = sysenv.elements;
while(list != NULL)
  {
/* only write those values that are different to default library */
  elem = list->data;
  if (elem)
    {
    fprintf(fp, "%%gdis_elem\n");
    fprintf(fp, "number: %d\n", elem->number);
    fprintf(fp, "weight: %f\n", elem->weight);
    fprintf(fp, "  cova: %f\n", elem->cova);
    fprintf(fp, "   vdw: %f\n", elem->vdw);
    fprintf(fp, "charge: %f\n", elem->charge);
    fprintf(fp, "colour: %f %f %f\n", elem->colour[0], 
                                      elem->colour[1],
                                      elem->colour[2]);
    fprintf(fp, "%%gdis_end\n");
    }
  list = g_slist_next(list);
  }
return(0);
}

void write_sfc(gpointer key, gpointer val, gpointer data)
{
GSList *list;
FILE *fp = data;

fprintf(fp, "%s", (gchar *) key);
for (list=val ; list ; list=g_slist_next(list))
  fprintf(fp, " %f", *((gdouble *) list->data)); 
fprintf(fp, " \n");
}

void write_sfc_data(FILE *fp)
{
fprintf(fp, "%%gdis_sfc\n");
g_hash_table_foreach(sysenv.sfc_table, &write_sfc, fp);
fprintf(fp, "%%gdis_end\n");
}

/***************************************/
/* find all unique elements in a model */
/***************************************/
#define DEBUG_FIND_UNIQUE 0
GSList *find_unique(gint mode, struct model_pak *model)
{
gint found;
GSList *list, *types, *unique;
struct core_pak *core;

unique = NULL;

/* go through all atoms */
for (list=model->cores ; list ; list=g_slist_next(list))
  {
  core = list->data;

/* determine if current atom type is in the list */
  found=0;
  for (types=unique ; types ; types = g_slist_next(types))
    {
    switch(mode)
      {
      case ELEMENT:
        if (GPOINTER_TO_INT(types->data) == core->atom_code)
          found++;
        break;

      case LABEL:
      case LABEL_NORMAL:
      case LABEL_GHOST:
        if (g_ascii_strcasecmp(types->data, core->atom_label) == 0)
          found++;
        break;
      }
    if (found)
      break;
    }

/* not in list, so add it */
  if (!found)
    {
    switch(mode)
      {
      case ELEMENT:
        unique = g_slist_prepend(unique, GINT_TO_POINTER(core->atom_code));
        break;
      case LABEL:
        unique = g_slist_prepend(unique, g_strdup(core->atom_label));
        break;
      case LABEL_NORMAL:
        if (!core->ghost)
          unique = g_slist_prepend(unique, g_strdup(core->atom_label));
        break;
      case LABEL_GHOST:
        if (core->ghost)
          unique = g_slist_prepend(unique, g_strdup(core->atom_label));
        break;
      }
    }
  }

#if DEBUG_FIND_UNIQUE
printf("Found: ");
for (types=unique ; types ; types = g_slist_next(types))
  {
  switch(mode)
    {
    case ELEMENT:
      printf("[%3d] ", GPOINTER_TO_INT(types->data));
      break;
    case LABEL:
    case LABEL_NORMAL:
    case LABEL_GHOST:
      printf("[%s] ", (gchar *) types->data);
      break;
    }
  }
printf("\n");
#endif

/* return reversed list (preserves order) */
return(g_slist_reverse(unique));
}

/***********************/
/* set the atom colour */
/***********************/
void init_atom_colour(struct core_pak *core, struct model_pak *model)
{
struct elem_pak elem_data;

get_elem_data(core->atom_code, &elem_data, model);
ARR3SET(core->colour, elem_data.colour);
VEC3MUL(core->colour, 65535.0);
if (core->ghost)
  core->colour[3] = 0.5;
else
  core->colour[3] = 1.0;
}

/***********************/
/* set the atom charge */
/***********************/
void init_atom_charge(struct core_pak *core, struct model_pak *model)
{
struct elem_pak elem_data;

if (core->lookup_charge)
  {
  get_elem_data(core->atom_code, &elem_data, model);
  core->charge = elem_data.charge;
  }
}

/************************/
/* set the atom charges */
/************************/
void init_model_charges(struct model_pak *model)
{
GSList *list;

for (list=model->cores ; list ; list=g_slist_next(list))
  init_atom_charge(list->data, model);
}

/*******************************************/
/* net charge on atom (ie including shell) */
/*******************************************/
gdouble atom_charge(struct core_pak *core)
{
gdouble q;
struct shel_pak *shell;

q = core->charge;
if (core->shell)
  {
  shell = core->shell;
  q += shell->charge;
  }
return(q);
}

/********************************************/
/* initialize element properties for a core */
/********************************************/
void elem_init(struct core_pak *core, struct model_pak *model)
{
struct elem_pak elem;

g_assert(model != NULL);
g_assert(core != NULL);

/* NB: assumes core->atom_code is the atomic number */

get_elem_data(core->atom_code, &elem, model);
core->atom_code = elem.number;
core->bond_cutoff = elem.cova;
core->charge = elem.charge;
ARR3SET(core->colour, elem.colour);
VEC3MUL(core->colour, 65535.0);
}

/*********************************************************/
/* compute net charge and dipole (if model is a surface) */
/*********************************************************/
#define DEBUG_CALC_EMP 0
void calc_emp(struct model_pak *data)
{
gdouble qsum, x[3], p[3];
GSList *list;
struct core_pak *core;
struct shel_pak *shell;

/* checks */
g_assert(data != NULL);

/* TODO - speed up by getting to a local var the elem data for unique atoms */
/* cores & shell dipole calc */
qsum=0.0;
VEC3SET(p, 0.0, 0.0, 0.0);
for (list=data->cores ; list ; list=g_slist_next(list))
  {
  core = list->data;
/*
  if (core->region != REGION1A)
    continue;
*/
  if (core->status & DELETED)
    continue;

  ARR3SET(x, core->x);
  vecmat(data->latmat, x);

/* dipole */
  VEC3MUL(x, core->charge);
  ARR3ADD(p, x);
/* monopole */
  qsum += core->charge;

/* NB: add shell constribution at the core's z location */
  if (core->shell)
    {
    shell = core->shell;
    ARR3SET(x, shell->x);
    vecmat(data->latmat, x);

/* dipole */
    VEC3MUL(x, shell->charge);
    ARR3ADD(p, x);
/* monopole */
    qsum += shell->charge;
    }
  }

#if DEBUG_CALC_EMP
printf("surface: %f %f %f  (%f)\n", 
data->surface.miller[0], data->surface.miller[1], data->surface.miller[2], 
data->surface.shift);
P3VEC("dipole: ", p);
printf("sum charge: %e\n", qsum);
if (qsum*qsum > FRACTION_TOLERANCE)
  printf("Warning: your model has a net charge = %f\n", qsum);
#endif

/* only surfaces get the z dipole */
if (data->periodic == 2)
  data->gulp.sdipole = p[2];

data->gulp.qsum = qsum;
}

/***************************************************/
/* does a given string represent an atomic number? */
/***************************************************/
gint elem_number_test(const gchar *input)
{
gint n=0;

if (str_is_float(input))
  {
  n = str_to_float(input);
  if (n<0 || n>MAX_ELEMENTS-1)
    n=0; 
  }
return(n);
}

/***************************************************/
/* does a given string represent an element symbol */
/***************************************************/
#define DEBUG_ELEM_TYPE 0
gint elem_symbol_test(const gchar *input)
{
gint i, j, m, n;
gchar *elem1, **buff;

/* checks */
if (input == NULL)
  return(0);
/* zero length string matches something, so force it to return 0 */
if (!strlen(input))
  return(0);

/* duplicate for manipulation */
elem1 = g_strdup(input);

/* remove anything but alphabetic chars */
for (i=0 ; i<strlen(elem1) ; i++)
  if (!g_ascii_isalpha(*(elem1+i)))
    *(elem1+i) = ' ';

/* remove trailing/leading spaces & use only the first token */
buff = tokenize(elem1, &n);
if (n)
  {
  g_free(elem1);
  elem1 = g_strdup(*buff);
  }
g_strfreev(buff);

m = strlen(elem1);

/* catch Deuterium */
if (g_ascii_strncasecmp(elem1, "D", m) == 0)
  *elem1 = 'H';

#if DEBUG_ELEM_TYPE
printf("Looking for [%s]...", elem1);
#endif

/* attempt to match atom type with database */
j=0;
/* FIXME - elim dependence on const */
for(i=1 ; i<sysenv.num_elements ; i++)
  {
/* only compare if lengths match */
  n = strlen(elements[i].symbol);
  if (n == m)
    {
    if (g_ascii_strncasecmp(elem1, elements[i].symbol, m) == 0)
      {
      j = i;
      break;
      }
    }
  }

#if DEBUG_ELEM_TYPE
if (j)
  printf("found.\n");
else
  printf("not found.\n");
#endif

/* done */
g_free(elem1);
return(j);
}

/*************************************************************/
/* does a given string represent an element symbol or number */
/*************************************************************/
gint elem_test(const gchar *input)
{
gint n;

n = elem_symbol_test(input);
if (!n)
  n = elem_number_test(input);

return(n);
}

/*********************************/
/* PDB hack for element matching */
/*********************************/
#define DEBUG_PDB_ELEM_TYPE 0
gint pdb_elem_type(const gchar *input)
{
gint i, j, len;
gchar *tmp;

#if DEBUG_PDB_ELEM_TYPE
printf("pdb1: [%s]\n", input);
#endif

/* checks */
if (input == NULL)
  return(0);
/* zero length string matches something, so force it to return 0 */
len = strlen(input);
if (!len)
  return(0);

/* if we encounter C, O, or H then ignore the rest of the string */
for (i=0 ; i<len ; i++)
  {
  switch(*(input+i))
    {
    case 'c':
    case 'C':
    case 'o':
    case 'O':
    case 'h':
    case 'H':
      tmp = g_strndup(input, i+1);
      j = elem_symbol_test(tmp);
      g_free(tmp);
      return(j);
    }
  }
/* default element matching */
return(elem_symbol_test(input));
}

/*************************/
/* retrieve element data */
/*************************/
/* will return non-default data (if it exists) otherwise the default */
#define DEBUG_GET_ELEM 0
gint get_elem_data(gint code, struct elem_pak *elem, struct model_pak *model)
{
GSList *list;
struct elem_pak *elem_data;

/* search local exceptions */
if (model)
  {
  for (list=model->elements ; list ; list=g_slist_next(list))
    {
    elem_data = list->data;
    if (code == elem_data->number)
      {
#if DEBUG_GET_ELEM
printf("Retrieving local exception.\n");
#endif
      memcpy(elem, elem_data, sizeof(struct elem_pak));
      return(0);
      }
    }
  }
/* search global exceptions */
for (list=sysenv.elements ; list ; list=g_slist_next(list))
  {
  elem_data = list->data;
  if (code == elem_data->number)
    {
#if DEBUG_GET_ELEM
printf("Retrieving global exception.\n");
#endif
    memcpy(elem, elem_data, sizeof(struct elem_pak));
    return(0);
    }
  }

/* return from the standard database */
if (code >= 0 && code < sysenv.num_elements)
  {
#if DEBUG_GET_ELEM
printf("Retrieving default.\n");
#endif
  memcpy(elem, &elements[code], sizeof(struct elem_pak));
  return(0);
  }

#if DEBUG_GET_ELEM
printf("ERROR: bad element code.\n");
#endif
return(1);
}

/**********************************/
/* store non-default element data */
/**********************************/
#define DEBUG_PUT_ELEM_DATA 0
void put_elem_data(struct elem_pak *elem, struct model_pak *model)
{
gint flag, create;
GSList *list, *elements;
struct elem_pak *elem_data;
struct core_pak *core;

/* checks */
g_return_if_fail(elem != NULL);

#if DEBUG_PUT_ELEM_DATA
printf(" *** Saving element ***\n");
printf("symbol: %s\n", elem->symbol);
printf("  name: %s\n", elem->name);
printf("number: %d\n", elem->number);
printf("weight: %f\n", elem->weight);
printf("  cova: %f\n", elem->cova);
printf("   vdw: %f\n", elem->vdw);
printf("charge: %f\n", elem->charge);
printf("colour: %f %f %f\n", elem->colour[0], elem->colour[1], elem->colour[2]);
#endif

/* global or local database */
if (model)
  {
  elements = model->elements;
  flag = TRUE;
  }
else
  {
  elements = sysenv.elements;
  flag = FALSE;
  }

/* replace element if exists */
create = TRUE;
for (list=elements ; list ; list=g_slist_next(list))
  {
  elem_data = list->data;
  if (elem_data->number == elem->number)
    {
#if DEBUG_PUT_ELEM_DATA
printf("replacing existing...\n");
#endif
    memcpy(elem_data, elem, sizeof(struct elem_pak));
    create = FALSE;
    break;
    }
  }

/* create new item if it didn't exist */
if (create)
  {
#if DEBUG_PUT_ELEM_DATA
printf("creating new exception...\n");
#endif
  elem_data = g_malloc(sizeof(struct elem_pak));
  memcpy(elem_data, elem, sizeof(struct elem_pak));

  if (flag)
    model->elements = g_slist_prepend(model->elements, elem_data);
  else
    sysenv.elements = g_slist_prepend(sysenv.elements, elem_data);
  }

/* update atom bond_cutoff */
if (flag)
  {
  for (list=model->cores ; list ; list=g_slist_next(list))
    {
    core = list->data;
    if (core->atom_code == elem->number)
      core->bond_cutoff = elem->cova;
    }
  }
}

/* globals for the atom properties dialog */
GtkWidget *apd_label, *apd_type, *apd_charge, *apd_x, *apd_y, *apd_z;
GtkWidget *apd_growth, *apd_region, *apd_translate;
struct model_pak *apd_data=NULL;
struct core_pak *apd_core=NULL;

/*************************************************************/
/* get the nth colour in a sequence (eg sample RGB spectrum) */
/*************************************************************/
void get_colour(gdouble *colour, gint n)
{
gint byte;
gdouble r, g, b, f;

byte = 1 + n % 7;

if (byte & 4)
  r = 1.0;
else
  r = 0.0;

if (byte & 2)
  g = 1.0;
else
  g = 0.0;

if (byte & 1)
  b = 1.0;
else
  b = 0.0;

f = n / 7;
f *= 0.5;
f += 1.0;
f = 1.0/f;
r *= f;
g *= f;
b *= f;

VEC3SET(colour, r, g, b);
}

/****************************************/
/* change the colour scheme for an atom */
/****************************************/
void atom_colour_scheme(gint type, struct core_pak *core, struct model_pak *model)
{
gdouble t;
struct elem_pak elem;

switch (type)
  {
  case ELEM:
    get_elem_data(core->atom_code, &elem, model);
    break;

  case MOL:
/* get the sequence number for the colour type */
/* NB: core->molecule is deprec. */
    get_colour(elem.colour, core->molecule);
    break;

  case OCCUPANCY:
    VEC3MUL(core->colour, core->sof);
    return;

  case REGION:
/* get the sequence number for the colour type */
    get_colour(elem.colour, core->region);
    break;

  case GROWTH_SLICE:
    if (core->growth)
      {
      VEC3SET(elem.colour, 0.5, 0.5, 0.5);
      }
    else
      {
      get_elem_data(core->atom_code, &elem, model);
      }
    break;

  case TRANSLATE:
    if (core->translate)
      {
      VEC3SET(elem.colour, 0.5, 0.5, 0.5);
      }
    else
      {
      get_elem_data(core->atom_code, &elem, model);
      }
    break;

  case VELOCITY:
#define BC 1.3806503
#define AN 6.0221420
#define TMIN 0.0
#define TRANGE 2000.0

    get_elem_data(core->atom_code, &elem, model);
    t = elem.weight * VEC3MAGSQ(core->v) * 10.0 / (3.0*AN*BC);

/*
printf("T = %f\n", t);
*/

/* clamp */
t -= TMIN;
if (t > TRANGE)
  {
/*
  printf("tmax = %f\n", t + TMIN);
*/
  t = TRANGE;
  }
if (t < 0.0)
  t = 0.0;

/* bright red when hot (TMAX) */
    elem.colour[0] = t/TRANGE;
    elem.colour[1] = 0.0;
/* dark blue when cold */
    elem.colour[2] = 0.5 * (1.0 - t/TRANGE);
    break;
  }

ARR3SET(core->colour, elem.colour);
VEC3MUL(core->colour, 65535.0);
}

/*************************************/
/* set the colour scheme for a model */
/*************************************/
void model_colour_scheme(gint type, struct model_pak *model)
{
GSList *list;
struct core_pak *core;

model->colour_scheme = type;
for (list=model->cores ; list ; list=g_slist_next(list))
  {
  core = (struct core_pak *) list->data;
  atom_colour_scheme(type, core, model);
  }
}

/***********************************************/
/* change the properties of all selected atoms */
/***********************************************/
/* NB: this is a bit dangerous as you change everything in the */
/* selection to the specified value - even (eg) incompatible elements */
void selection_properties_change(gint type)
{
gint n, growth, region, translate;
gdouble charge;
const gchar *text;
GSList *list;
struct elem_pak edata;
struct model_pak *model;
struct core_pak *core;

model = sysenv.active_model;
if (!model)
  return;
if (!model->selection)
  return;

switch (type)
  {
  case NAME:
    text = gtk_entry_get_text(GTK_ENTRY(apd_label));
    n = elem_symbol_test(text);
    if (n)
      {
      get_elem_data(n, &edata, model);
/* make sure we alow enough space for the string and the \0 */
      n = (LABEL_SIZE-1 > strlen(text)) ? strlen(text) : LABEL_SIZE-1;
      for (list=model->selection ; list ; list=g_slist_next(list))
        {
        core = list->data;
        g_free(core->atom_label);
        core->atom_label = g_strdup(text);
/* update atttached shell */
        if (core->shell)
          {
          struct shel_pak *shell = core->shell;
          g_free(shell->shell_label);
          shell->shell_label = g_strdup(text);
          }
/* NEW - don't update element specific data if the element type was not */
/* changed - ie the user has just made a labelling change (eg C -> C1) */
        if (edata.number != core->atom_code)
          {
          core->atom_code = edata.number;
          core->bond_cutoff = edata.cova;
          }
        init_atom_colour(core, model);
        init_atom_charge(core, model);
        }
/* model updates */
      g_slist_free(model->unique_atom_list);
      model->unique_atom_list = find_unique(ELEMENT, model);
      calc_emp(model);
      }
    break;

  case CHARGE:
    charge = str_to_float(gtk_entry_get_text(GTK_ENTRY(apd_charge)));
    for (list=model->selection ; list ; list=g_slist_next(list))
      {
      core = list->data;
/* core updates */
      core->charge = charge;
      core->lookup_charge = FALSE;
      }
    calc_emp(model);
    break;

  case CORE_GROWTH_SLICE:
    growth = str_to_float(gtk_entry_get_text(GTK_ENTRY(apd_growth)));
    growth = CLAMP(growth, 0, 1);
    for (list=model->selection ; list ; list=g_slist_next(list))
      {
      core = list->data;
      core->growth = growth;
      if (model->colour_scheme == GROWTH_SLICE)
        atom_colour_scheme(GROWTH_SLICE, core, model);
      }
    break;

  case CORE_REGION:
    region = str_to_float(gtk_entry_get_text(GTK_ENTRY(apd_region)));
    if (region > model->region_max)
      model->region_max = region;

    for (list=model->selection ; list ; list=g_slist_next(list))
      {
      core = list->data;
      core->region = region;
      if (core->shell)
        (core->shell)->region = region;

      if (model->colour_scheme == REGION)
        atom_colour_scheme(REGION, core, model);
      }
    break;

  case CORE_TRANSLATE:
    translate = str_to_float(gtk_entry_get_text(GTK_ENTRY(apd_translate)));
    translate = CLAMP(translate, 0, 1);
    for (list=model->selection ; list ; list=g_slist_next(list))
      {
      core = list->data;
      core->translate = translate;
      if (core->shell)
        (core->shell)->translate = translate;

      if (model->colour_scheme == TRANSLATE)
        atom_colour_scheme(TRANSLATE, core, model);
      }
    break;

  case CORE_FF:
    text = gtk_entry_get_text(GTK_ENTRY(apd_type));
    for (list=model->selection ; list ; list=g_slist_next(list))
      {
      core = list->data;
      if (core->atom_type)
        g_free(core->atom_type);
      core->atom_type = g_strdup(text);
      }
    break;
  }

gui_refresh(GUI_MODEL_PROPERTIES);
gui_refresh(GUI_CANVAS);
}

/*****************************/
/* commit changes to an atom */
/*****************************/
void atom_properties_change(GtkWidget *w, gint type)
{
gint n, growth, region, translate;
const gchar *text;
struct elem_pak edata;
struct model_pak *model;

model = sysenv.active_model;
if (!model)
  return;

/* act on multiple atoms? */
if (g_slist_length(model->selection) > 1)
  {
  selection_properties_change(type);
  return;
  }

if (!apd_core)
  return;

switch(type)
  {
  case NAME:
    text = gtk_entry_get_text(GTK_ENTRY(apd_label));
    g_free(apd_core->atom_label);
    apd_core->atom_label = g_strdup(text);
    n = elem_symbol_test(text);

/* update atttached shell */
if (apd_core->shell)
  {
  struct shel_pak *shell = apd_core->shell;
  g_free(shell->shell_label);
  shell->shell_label = g_strdup(text);
  }

/* if recognized -> update */
    if (n)
      {
      get_elem_data(n, &edata, model);

/* NEW - don't update element specific data if the element type was not */
/* changed - ie the user has just made a labelling change (eg C -> C1) */
      if (n != apd_core->atom_code)
        {
        apd_core->atom_code = n;
        apd_core->bond_cutoff = edata.cova;
        }
      init_atom_colour(apd_core, model);
      init_atom_charge(apd_core, model);

      g_slist_free(model->unique_atom_list);
      model->unique_atom_list = find_unique(ELEMENT, model);
      calc_emp(model);

/* REFRESH */
      gui_refresh(GUI_MODEL_PROPERTIES);
      }
    break;

  case CORE_FF:
    text = gtk_entry_get_text(GTK_ENTRY(apd_type));
    if (apd_core->atom_type)
      g_free(apd_core->atom_type);
    apd_core->atom_type = g_strdup(text);
    break;

  case CHARGE:
    text = gtk_entry_get_text(GTK_ENTRY(apd_charge));
    apd_core->charge = str_to_float(text);
    apd_core->lookup_charge = FALSE;
    calc_emp(model);
    break;

  case COORD_X:
    text = gtk_entry_get_text(GTK_ENTRY(apd_x));
    apd_core->x[0] = str_to_float(text);
    coords_compute(model);
    break;

  case COORD_Y:
    text = gtk_entry_get_text(GTK_ENTRY(apd_y));
    apd_core->x[1] = str_to_float(text);
    coords_compute(model);
    break;

  case COORD_Z:
    text = gtk_entry_get_text(GTK_ENTRY(apd_z));
    apd_core->x[2] = str_to_float(text);
    coords_compute(model);
    break;

  case CORE_GROWTH_SLICE:
    text = gtk_entry_get_text(GTK_ENTRY(apd_growth));
    growth = CLAMP(str_to_float(text), 0, 1);
    apd_core->growth = growth;
    if (model->colour_scheme == GROWTH_SLICE)
      atom_colour_scheme(GROWTH_SLICE, apd_core, model);
    break;

  case CORE_REGION:
    text = gtk_entry_get_text(GTK_ENTRY(apd_region));
    region = str_to_float(text);
    if (region > model->region_max)
      model->region_max = region;
    apd_core->region = region;
    if (apd_core->shell)
      (apd_core->shell)->region = region;
    if (model->colour_scheme == REGION)
      atom_colour_scheme(REGION, apd_core, model);
    break;

  case CORE_TRANSLATE:
    text = gtk_entry_get_text(GTK_ENTRY(apd_translate));
    translate = CLAMP(str_to_float(text), 0, 1);
    apd_core->translate = translate;
    if (apd_core->shell)
      (apd_core->shell)->translate = translate;
    if (model->colour_scheme == TRANSLATE)
      atom_colour_scheme(TRANSLATE, apd_core, model);
    break;

  default:
    printf("Not yet modifiable...\n");
  }
gui_refresh(GUI_CANVAS);
}

/*************************************/
/* updates the dialog for a new atom */
/*************************************/
void gui_refresh_selection(void)
{
gdouble q;
gchar *element, *label, *type, *charge, *x, *y, *z, *growth, *region, *translate;
struct core_pak *core;
struct model_pak *model;

model = sysenv.active_model;
core = NULL;
if (model)
  {
  GSList *list = model->selection;
  if (g_slist_length(list) == 1)
    core = list->data;
  }

if (core && model)
  {
/* data available */
  element = g_strdup(elements[core->atom_code].symbol);
  label = g_strdup(core->atom_label);

  if (core->atom_type)
    type = g_strdup(core->atom_type);
  else
    type = g_strdup("");

  q = atom_charge(core); /* Replaced by C. Fisher 2004 */
  charge = g_strdup_printf("%9.4f", q);

  x = g_strdup_printf("%9.4f", core->x[0]);
  y = g_strdup_printf("%9.4f", core->x[1]);
  z = g_strdup_printf("%9.4f", core->x[2]);

  growth = g_strdup_printf("%d", core->growth);
  region = g_strdup_printf("%d", core->region);
  translate = g_strdup_printf("%d", core->translate);

  apd_core = core;
  }
else
  {
/* otherwise defaults */
  element = g_strdup("");
  label = g_strdup("");
  type = g_strdup("");
  charge = g_strdup("");
  x = g_strdup("");
  y = g_strdup("");
  z = g_strdup("");
  growth = g_strdup("");
  region = g_strdup("");
  translate = g_strdup("");
  }

/* prevent changes from messing up the atom_properties_change() callback */
apd_data = NULL;

/* entry updates */
gtk_entry_set_text(GTK_ENTRY(apd_label), label);
gtk_entry_set_text(GTK_ENTRY(apd_type), type);
gtk_entry_set_text(GTK_ENTRY(apd_charge), charge);
gtk_entry_set_text(GTK_ENTRY(apd_x), x);
gtk_entry_set_text(GTK_ENTRY(apd_y), y);
gtk_entry_set_text(GTK_ENTRY(apd_z), z);
gtk_entry_set_text(GTK_ENTRY(apd_growth), growth);
gtk_entry_set_text(GTK_ENTRY(apd_region), region);
gtk_entry_set_text(GTK_ENTRY(apd_translate), translate);

apd_data = model;

/* cleanup */
g_free(element);
g_free(label);
g_free(type);
g_free(charge);
g_free(x);
g_free(y);
g_free(z);
g_free(growth);
g_free(region);
g_free(translate);
}

/*******************************************/
/* display the properties of a single atom */
/*******************************************/
void gui_edit_widget(GtkWidget *box)
{
GtkWidget *frame, *hbox, *vbox, *entry;

/* checks */
g_return_if_fail(box != NULL);

/* two column element data display */
hbox = gtk_hbox_new(FALSE, 0);
gtk_box_pack_start(GTK_BOX(box), hbox, TRUE, TRUE, 0);

/* left vbox - titles */
vbox = gtk_vbox_new(TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);

/* TODO - put in a for loop? */

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Label");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "FF Type");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "X");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Y");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Z");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Charge");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Growth");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Region");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

entry = gtk_entry_new();
gtk_entry_set_text(GTK_ENTRY(entry), "Translate");
gtk_entry_set_editable(GTK_ENTRY(entry), FALSE);
gtk_box_pack_start(GTK_BOX(vbox), entry, FALSE, FALSE, 0);

/* right vbox - data */
vbox = gtk_vbox_new(TRUE, 0);
gtk_box_pack_end(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);

apd_label = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_label, FALSE, FALSE, 0);

apd_type = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_type, FALSE, FALSE, 0);

apd_x = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_x, FALSE, FALSE, 0);

apd_y = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_y, FALSE, FALSE, 0);

apd_z = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_z, FALSE, FALSE, 0);

apd_charge = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_charge, FALSE, FALSE, 0);

apd_growth = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_growth, FALSE, FALSE, 0);

apd_region = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_region, FALSE, FALSE, 0);

apd_translate = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(vbox), apd_translate, FALSE, FALSE, 0);

/* attach callbacks (NB: set initial data first) */
g_signal_connect(GTK_OBJECT(apd_label), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(NAME));
g_signal_connect(GTK_OBJECT(apd_type), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(CORE_FF));

g_signal_connect(GTK_OBJECT(apd_x), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(COORD_X));
g_signal_connect(GTK_OBJECT(apd_y), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(COORD_Y));
g_signal_connect(GTK_OBJECT(apd_z), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(COORD_Z));

g_signal_connect(GTK_OBJECT(apd_charge), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(CHARGE));

g_signal_connect(GTK_OBJECT(apd_growth), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change),
                 GINT_TO_POINTER(CORE_GROWTH_SLICE));

g_signal_connect(GTK_OBJECT(apd_region), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(CORE_REGION));
g_signal_connect(GTK_OBJECT(apd_translate), "activate",
                 GTK_SIGNAL_FUNC(atom_properties_change), GINT_TO_POINTER(CORE_TRANSLATE));

/* CURRENT */
frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING);
vbox = gtk_vbox_new(TRUE, 1);
gtk_container_add(GTK_CONTAINER(frame), vbox);

gui_button_x("Add atoms", gtk_mode_switch, (gpointer) ATOM_ADD, vbox);
gui_button_x("Add bonds", gtk_mode_switch, (gpointer) BOND_SINGLE, vbox);
gui_button_x("Delete bonds", gtk_mode_switch, (gpointer) BOND_DELETE, vbox);
gui_button_x("Normal mode", gtk_mode_switch, (gpointer) FREE, vbox);

frame = gtk_frame_new(NULL);
gtk_box_pack_start(GTK_BOX(box), frame, FALSE, FALSE, 0);
gtk_container_set_border_width(GTK_CONTAINER(frame), PANEL_SPACING);
vbox = gtk_vbox_new(TRUE, 1);
gtk_container_add(GTK_CONTAINER(frame), vbox);

gui_button_x("Mark as ghost", select_flag_ghost, NULL, vbox);
gui_button_x("Mark as normal", select_flag_normal, NULL, vbox);
}


syntax highlighted by Code2HTML, v. 0.9.1