/*
 * EasyChem
 * A program for creating and editing molecular formulas.
 *
 * Copyright (C) 2003, 2004, 2005 François-Xavier Coudert
 * 
 * Distributed under the General Public Licence (GPL).
 * See file COPYING in the source distribution for more details.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>

#include "common.h"
#include "bonds.h"
#include "auxi.h"


/* Here are the compile-time settings and limits. */
#define LIMIT_SMALL     5
#define FIG_LENGTH	300

/* Some macros that helps keeping the code clean. */
#define IS_SMALL(a) ((a) < LIMIT_SMALL && (a) > -LIMIT_SMALL)
#define IS_BETWEEN(a,b,c) ((a) <= (b) && (b) <= (c))
#define MAX4(a,b,c,d) (MAX ((a), MAX ((b), MAX ((c),(d)))))
#define MIN4(a,b,c,d) (MIN ((a), MIN ((b), MIN ((c),(d)))))


extern int bond_list_are_similar (struct Bond *list1, struct Bond *list2);

/*******************************************************************/
/*******************      MEMORY MANAGEMENT      *******************/
/*******************************************************************/


/* Free the memory used by a ornaments list */
void
free_orn_list (struct Ornament *list)
{
  struct Ornament * orn = list, * next;

  while (orn != NULL)
  {
    next = orn->next;
    g_free (orn);
    orn = next;
  }
}



/* This routine deletes a given object, freeing memory used for text and
 * group_members. The 'next' object is not freed! */
void
free_bond (struct Bond *bond)
{
  g_free (bond->text);
  g_free (bond->pango);
  g_free (bond->pango_left);
  g_free (bond->pango_right);
  if (bond->type == BOND_GROUP)
    free_bond_list (bond->group_members);
  free_orn_list (bond->ornaments[0]);
  free_orn_list (bond->ornaments[1]);
  g_free (bond);

  return;
}


/* Free the memory used by a bond list. */
void
free_bond_list (struct Bond *list)
{
  struct Bond *current, *next;

  if (list == NULL)
    return;
  next = list;

  while (next != NULL)
    {
      current = next;
      next = next->next;
      free_bond (current);
    }
}


struct Ornament *
copy_orn_list (struct Ornament * list)
{
  struct Ornament * orn = NULL, * new = NULL, * old = list;

  if (list == NULL)
    return NULL;

  while (old != NULL)
  {
    if (new == NULL)
    {
      new = g_new (struct Ornament, 1);
      orn = new;
    }
    else
    {
      orn->next = g_new (struct Ornament, 1);
      orn = orn->next;
    }

    *orn = *old;    
    orn->next = NULL;

    old = old->next;
  }
  return new;
}


/* Create a copy of a given bond. */
struct Bond *
copy_bond (struct Bond *bond)
{
  struct Bond *new;

  new = g_new (struct Bond, 1);

  new->x1 = bond->x1;
  new->y1 = bond->y1;
  new->x2 = bond->x2;
  new->y2 = bond->y2;
  new->x3 = bond->x3;
  new->y3 = bond->y3;
  new->x4 = bond->x4;
  new->y4 = bond->y4;
  new->width = bond->width;
  new->color = bond->color;
  new->type = bond->type;
  new->text = g_strdup (bond->text);
  new->pango = g_strdup (bond->pango);
  new->pango_right = g_strdup (bond->pango_right);
  new->pango_left = g_strdup (bond->pango_left);
  new->selected = bond->selected;
  new->ornaments[0] = copy_orn_list (bond->ornaments[0]);
  new->ornaments[1] = copy_orn_list (bond->ornaments[1]);
  new->next = NULL;
  if (bond->type == BOND_GROUP)
    new->group_members = copy_bond_list (bond->group_members);
  else
    new->group_members = NULL;

  return new;
}


/* Create a copy of the given bond list */
struct Bond *
copy_bond_list (struct Bond *list)
{
  struct Bond *first, *current_old, *current_new;

  if (list == NULL)
    return NULL;

  first = copy_bond (list);

  current_new = first;
  current_old = list->next;

  while (current_old != NULL)
    {
      current_new->next = copy_bond (current_old);
      current_new = current_new->next;
      current_old = current_old->next;
    }

  return first;
}


/* This adds bond list list2 at the end of list1. All list1 bonds are
 * unselected. Selection counts are updated. list2 can be NULL. list1
 * should not. WARNING: this does not add a copy of list2, but list2
 * itself. Don't use list2 after that. */
void
concat_bond_lists (struct Bond *list1, struct Bond *list2, int *num_sel)
{
  struct Bond * current = list1;
  
  if (list1 == NULL)
    return;
  
  clear_selection (list1, num_sel);
  while (current->next != NULL)
    current = current->next;

  current->next = list2;
  *num_sel = num_sel_recompute (list1);
  return;
}


/* Recomputes a fresh num_sel from the given bond list. */
int
num_sel_recompute (struct Bond *list)
{
  int n = 0;
  struct Bond * current = list;

  if (list == NULL)
    return 0;

  do
    if (current->selected & SEL_YES)
      n++;
  while ((current = current->next) != NULL);

  return n;
}

/* If the 'groups' parameter is non-zero, then the atoms/groups are
 * counted as their ink rectangle, and not as a single point. */
void
bounds_of_bond (struct Bond *bond, LLINT * xmin, LLINT * xmax,
		LLINT * ymin, LLINT * ymax, const int groups)
{
  int t1, t2, dir;
  LLINT x, y, r, w;

  switch (bond->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_UP:
    case BOND_DOWN:
    case BOND_DASHED:
    case BOND_ARROW:
    case BOND_GROUP:
      if (bond->x1 < *xmin)
	*xmin = bond->x1;
      if (bond->x2 < *xmin)
	*xmin = bond->x2;
      if (bond->x1 > *xmax)
	*xmax = bond->x1;
      if (bond->x2 > *xmax)
	*xmax = bond->x2;
      if (bond->y1 < *ymin)
	*ymin = bond->y1;
      if (bond->y2 < *ymin)
	*ymin = bond->y2;
      if (bond->y1 > *ymax)
	*ymax = bond->y1;
      if (bond->y2 > *ymax)
	*ymax = bond->y2;
      break;

    case BOND_CIRCLE:
      x = 0.5 * (bond->x1 + bond->x2);
      y = 0.5 * (bond->y1 + bond->y2);
      r = hypot ((double) bond->x1 - x, (double) bond->y1 - y);
      if (x - r < *xmin)
	*xmin = x - r;
      if (y - r < *ymin)
	*ymin = y - r;
      if (x + r > *xmax)
	*xmax = x + r;
      if (y + r > *ymax)
	*ymax = y + r;
      break;

    case BOND_ARC:
      info_arc (&x, &y, &t1, &t2, &dir, (double) bond->x1,
		(double) bond->y1, (double) bond->x2, (double) bond->y2,
		(double) bond->x3, (double) bond->y3);
      r = hypot ((double) bond->x1 - x, (double) bond->y1 - y);

      t2 += t1;

      while (t1 < 0)
	{
	  t1 += 180 * 64;
	  t2 += 180 * 64;
	}

      if ((t1 <= 180 * 64 && t2 >= 180 * 64) || t2 >= 3 * 180 * 64)
	w = x - r;
      else
	w = MIN (bond->x1, bond->x2);
      if (w < *xmin)
	*xmin = w;

      if (t2 >= 2 * 180 * 64)
	w = x + r;
      else
	w = MAX (bond->x1, bond->x2);
      if (w > *xmax)
	*xmax = w;

      if ((t1 <= 90 * 64 && t2 >= 90 * 64) || t2 >= 5 * 90 * 64)
	w = y - r;
      else
	w = MIN (bond->y1, bond->y2);
      if (w < *ymin)
	*ymin = w;

      if ((t1 <= 3 * 90 * 64 && t2 >= 3 * 90 * 64) || t2 >= 7 * 90 * 64)
	w = y + r;
      else
	w = MAX (bond->y1, bond->y2);
      if (w > *ymax)
	*ymax = w;

      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      if (groups == 0)
      {
      if (bond->x3 < *xmin)
	*xmin = bond->x3;
      if (bond->x3 > *xmax)
	*xmax = bond->x3;
      if (bond->y3 < *ymin)
	*ymin = bond->y3;
      if (bond->y3 > *ymax)
	*ymax = bond->y3;
      }
      else
      {
      if (bond->x4 < *xmin)
	*xmin = bond->x4;
      if (bond->y4 < *ymin)
	*ymin = bond->y4;
      if (bond->x4 + bond->x2 > *xmax)
	*xmax = bond->x4 + bond->x2;
      if (bond->y4 + bond->y2 > *ymax)
	*ymax = bond->y4 + bond->y2;
      }
      break;

    default:
      bug_in ("bounds_of_bond");
    }
}


/* Update the SEL_CLOSE_1 and SEL_CLOSE_2 flags. */
void
update_sel_close (struct Bond *list)
{
  struct Bond *current, *i;

  if (list == NULL)
    return;
  current = list;

  do
    {
      current->selected &= SEL_ALL - (SEL_CLOSE_1 + SEL_CLOSE_2);
      if (current->type == BOND_GROUP)
	{
	  current = current->next;
	  continue;
	}
      i = list;
      do
	{
	  if (i->type == BOND_GROUP)
	    {
	      i = i->next;
	      continue;
	    }
	  if (i != current && (i->selected & SEL_YES))
	    {
	      if (current->type >= BOND_ATOM)
		{
		  if ((current->x3 == i->x1 && current->y3 == i->y1)
		      || (current->x3 == i->x2 && current->y3 == i->y2))
		    current->selected |= SEL_CLOSE_1;
		}
	      else if (i->type >= BOND_ATOM)
		{
		  if (current->x1 == i->x3 && current->y1 == i->y3)
		    current->selected |= SEL_CLOSE_1;
		  if (current->x2 == i->x3 && current->y2 == i->y3)
		    current->selected |= SEL_CLOSE_2;
		}
	      else
		{
		  if ((current->x1 == i->x1 && current->y1 == i->y1)
		      || (current->x1 == i->x2 && current->y1 == i->y2))
		    current->selected |= SEL_CLOSE_1;
		  if ((current->x2 == i->x1 && current->y2 == i->y1)
		      || (current->x2 == i->x2 && current->y2 == i->y2))
		    current->selected |= SEL_CLOSE_2;
		}
	    }
	  i = i->next;
	}
      while (i != NULL);

      current = current->next;
    }
  while (current != NULL);
}


/* Duplicate the selected bonds in a list */
void
duplicate_selected_in_list (struct Bond *list)
{
  struct Bond *current, *new;

  if (list == NULL)
    return;

  current = list;
  new = list;
  while (new->next != NULL)
    new = new->next;

  do
    {
      if (current->selected & SEL_YES)
	{
	  new->next = copy_bond (current);
	  new->next->selected = SEL_NO;
	  new = new->next;
	}
    }
  while ((current = current->next) != NULL);
}


/* Returns a non-zero value if the two given ornament lists have
 * the same properties... */
int
orn_lists_are_similar (struct Ornament *orn1, struct Ornament *orn2)
{
  if (orn1 == NULL && orn2 == NULL)
    return 1;

  if (orn1 == NULL || orn2 == NULL)
    return 0;

#define DIFF(X) { if (orn1->X != orn2->X) return 0; }
  DIFF (type);
  DIFF (number);
  DIFF (angle);
  DIFF (spacing);
  DIFF (size);
  DIFF (dist);
#undef DIFF

  return orn_lists_are_similar (orn1->next, orn2->next);
}


/* Returns a non-zero value if the two given bonds have the same
 * properties... */
int
is_similar (struct Bond *bond1, struct Bond *bond2)
{
  LLINT x1, y1, x2, y2, r1, r2;
  int s1, s2, t1, t2, dir1, dir2;

  if (bond1->type != bond2->type)
    return 0;
  if (! orn_lists_are_similar (bond1->ornaments[0], bond2->ornaments[0]))
    return 0;
  if (! orn_lists_are_similar (bond1->ornaments[1], bond2->ornaments[1]))
    return 0;

  if (BOND_HAS_WIDTH (bond1) && bond1->width != bond2->width)
    return 0;

  switch (bond1->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_DASHED:
      if ((IS_SMALL (bond1->x1 - bond2->x1) &&
	   IS_SMALL (bond1->y1 - bond2->y1) &&
	   IS_SMALL (bond1->x2 - bond2->x2) &&
	   IS_SMALL (bond1->y2 - bond2->y2)) ||
	  (IS_SMALL (bond1->x1 - bond2->x2) &&
	   IS_SMALL (bond1->y1 - bond2->y2) &&
	   IS_SMALL (bond1->x2 - bond2->x1) &&
	   IS_SMALL (bond1->y2 - bond2->y1)))
	return 1;
      break;

    case BOND_CIRCLE:
      x1 = (bond1->x1 + bond1->x2) / 2;
      y1 = (bond1->x1 + bond1->x2) / 2;
      x2 = (bond2->x1 + bond2->x2) / 2;
      y2 = (bond2->x1 + bond2->x2) / 2;
      r1 = SQR (x1 - bond1->x1) + SQR (y1 - bond1->y1);
      r2 = SQR (x1 - bond2->x1) + SQR (y1 - bond2->y1);
      if (IS_SMALL (x1 - x2) && IS_SMALL (y1 - y2) && IS_SMALL (r1 - r2))
	return 1;
      break;

    case BOND_UP:
    case BOND_DOWN:
    case BOND_ARROW:
      if (IS_SMALL (bond1->x1 - bond2->x1) &&
	  IS_SMALL (bond1->y1 - bond2->y1) &&
	  IS_SMALL (bond1->x2 - bond2->x2) &&
	  IS_SMALL (bond1->y2 - bond2->y2))
	return 1;
      break;

    case BOND_ARC:
      info_arc (&x1, &y1, &s1, &t1, &dir1, (double) bond1->x1,
		(double) bond1->y1,
		(double) bond1->x2, (double) bond1->y2, (double) bond1->x3,
		(double) bond1->y3);
      info_arc (&x2, &y2, &s2, &t2, &dir2, (double) bond2->x1,
		(double) bond2->y1, (double) bond2->x2, (double) bond2->y2,
		(double) bond2->x3, (double) bond2->y3);
      if (IS_SMALL (x1 - x2) && IS_SMALL (y1 - y2) && IS_SMALL (s1 - s2)
	  && IS_SMALL (t1 - t2))
	return 1;
      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      if (IS_SMALL (bond1->x3 - bond2->x3)
	  && IS_SMALL (bond1->y3 - bond2->y3))
	if (strcmp (bond1->text, bond2->text) == 0)
	  return 1;
      break;

    case BOND_GROUP:
      return bond_list_are_similar (bond1->group_members,
				    bond2->group_members);
      break;

    default:
      bug_in ("is_similar");
    }

  return 0;
}

/* Remove all groups containing nothing */
void
remove_empty_groups (struct Bond **list, int *nul_sel)
{
  struct Bond *current, *previous;

  previous = NULL;
  current = *list;
  if (current == NULL)
    return;
  
  do
    {
    if (current->type == BOND_GROUP && current->group_members == NULL)
      {
        if (current->selected & SEL_YES)
          (*nul_sel)--;

        if (previous == NULL)
          *list = current->next;
        else
          previous->next = current->next;
        free_bond (current);

        if (previous == NULL)
          current = *list;
        else
          current = previous->next;
      }
    }
  while ((current = current->next) != NULL);
}


/* Remove duplicate bonds in the given list */
void
remove_duplicate_in_list (struct Bond **list, int *nul_sel)
{
  struct Bond *current, *i, *previous;

  current = *list;
  if (current == NULL)
    return;

  previous = NULL;
  do
    {
      i = *list;
      while (i != NULL)
	{
	  if (i != current && is_similar (i, current))
	    {
	      if (i->selected & SEL_YES)
		(*nul_sel)--;

	      if (previous == NULL)
		*list = i->next;
	      else
		previous->next = i->next;
	      free_bond (i);

	      if (previous == NULL)
		i = *list;
	      else
		i = previous->next;
	    }
	  else
	    {
	      previous = i;
	      i = i->next;
	    }
	}
    }
  while ((current = current->next) != NULL);
}



/* This is used to add a bond with given x1, y1, x2, y2 and type. */
/* If options is non-zero, adding a simple bond can make a double bond */
struct Bond *
add_bond (const LLINT x1, const LLINT y1, const LLINT x2,
	  const LLINT y2, const int type, struct Bond **list,
	  const int options)
{
  struct Bond *current, *new;

  current = *list;
  new = current;
  if (current != NULL)
    do
      {
	if ((IS_SMALL (current->x1 - x1) && IS_SMALL (current->x2 - x2)
	     && IS_SMALL (current->y1 - y1) && IS_SMALL (current->y2 - y2))
	    || (IS_SMALL (current->x1 - x2) && IS_SMALL (current->x2 - x1)
		&& IS_SMALL (current->y1 - y2)
		&& IS_SMALL (current->y2 - y1)))
	  {
	    if (options && current->type == BOND_SIMPLE
		&& type == BOND_SIMPLE)
	      current->type = BOND_DOUBLE;
	    return current;
	  }
	new = current;
      }
    while ((current = current->next) != NULL);

  current = new;
  new = g_new (struct Bond, 1);
  if (current != NULL)
    current->next = new;
  else
    {
      current = new;
      *list = new;
    }

  new->x1 = x1;
  new->y1 = y1;
  new->x2 = x2;
  new->y2 = y2;
  new->x3 = 0;
  new->y3 = 0;
  new->x4 = 0;
  new->y4 = 0;
  new->width = 1.0;
  new->color = black;
  new->type = type;
  new->text = NULL;
  new->pango = NULL;
  new->pango_left = NULL;
  new->pango_right = NULL;
  new->selected = SEL_NO;
  new->ornaments[0] = NULL;
  new->ornaments[1] = NULL;
  new->next = NULL;

  return new;
}


/* Here we add a atom */
struct Bond *
add_atom (const LLINT x, const LLINT y, struct Bond **list, const int type)
{
  struct Bond *current, *new;

  current = *list;
  new = current;
  if (current != NULL)
    do
      {
	if (BOND_HAS_TEXT (current) &&
	    (IS_SMALL (current->x3 - x) && IS_SMALL (current->y3 - y)))
	  {
	    g_free (current->text);
	    g_free (current->pango);
	    g_free (current->pango_left);
	    g_free (current->pango_right);
	    current->text = NULL;
	    current->pango = NULL;
	    current->pango_left = NULL;
	    current->pango_right = NULL;
	    current->selected = SEL_NO;
	    return current;
	  }
	new = current;
      }
    while ((current = current->next) != NULL);

  current = new;
  new = g_new (struct Bond, 1);

  if (current != NULL)
    current->next = new;
  else
    {
      current = new;
      *list = new;
    }

  new->x1 = 0;
  new->y1 = 0;
  new->x2 = 0;
  new->y2 = 0;
  new->x3 = x;
  new->y3 = y;
  new->x4 = 0;
  new->y4 = 0;
  new->width = 1.0;
  new->color = black;
  new->type = type;
  new->text = NULL;
  new->pango = NULL;
  new->pango_left = NULL;
  new->pango_right = NULL;
  new->selected = SEL_NO;
  new->ornaments[0] = NULL;
  new->ornaments[1] = NULL;
  new->next = NULL;

  return new;
}


/* Computes the distance from a point to a given bond */
double
distance_to_bond (const LLINT x, const LLINT y, struct Bond *bond)
{
  double s;

  switch (bond->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_UP:
    case BOND_DOWN:
    case BOND_DASHED:
    case BOND_ARROW:
      s = ((bond->x2 - x) * ((double) (bond->x2 - bond->x1))
	   + (bond->y2 - y) * ((double) (bond->y2 - bond->y1)))
	/ (SQR ((double) (bond->x2 - bond->x1))
	   + SQR ((double) (bond->y2 - bond->y1)));
      if (s < 0.0)
	return SQR ((double) (bond->x2 - x)) + SQR ((double) (bond->y2 - y));
      else
	{
	  if (s > 1.0)
	    return SQR ((double) (bond->x1 - x))
	      + SQR ((double) (bond->y1 - y));
	  else
	    return SQR ((x - bond->x2) + s * (bond->x2 - bond->x1))
	      + SQR ((y - bond->y2) + s * (bond->y2 - bond->y1));
	}
      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      if (x > bond->x4 && x < bond->x4 + bond->x2
	  && y > bond->y4 && y < bond->y4 + bond->y2)
	return 0;
      else
	return VERY_BIG;
      break;

    case BOND_CIRCLE:
      s = hypot ((double) bond->x1 - bond->x2,
		 (double) bond->y1 - bond->y2) / 2
	- hypot (((double) bond->x1 + bond->x2) / 2 - x,
		 ((double) bond->y1 + bond->y2) / 2 - y);
      return SQR (s);
      break;

    case BOND_ARC:
      return dist2_to_arc (bond, x, y);
      break;

    case BOND_GROUP:
      return distance_to_bond_list (x, y, bond->group_members);
    }

  bug_in ("distance_to_bond");
}


struct Bond *
closest_end_of_bond (const LLINT x, const LLINT y,
	             unsigned int *end, struct Bond *list,
		     double * dist, const double sensibility)
{
  struct Bond *current = list, *best = NULL, *recurs;
  double d_best = VERY_BIG, d1, d2;
  unsigned int recurs_end;

  while (current != NULL)
  {
    switch (current->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_UP:
    case BOND_DOWN:
    case BOND_DASHED:
    case BOND_ARROW:
    case BOND_CIRCLE:
    case BOND_ARC:
      d1 = SQR ((double) (current->x1 - x)) + SQR ((double) (current->y1 - y));
      d2 = SQR ((double) (current->x2 - x)) + SQR ((double) (current->y2 - y));
      
      if (d1 <= d2 && d1 < d_best)
      {
	*end = 0;
	d_best = d1;
	best = current;
      }
      if (d2 <= d1 && d2 < d_best)
      {
	*end = 1;
	d_best = d2;
	best = current;
      }
      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      d1 = SQR ((double) (current->x3 - x)) + SQR ((double) (current->y3 - y));
      if (d1 < d_best)
      {
	*end = 0;
	d_best = d1;
	best = current;
      }
      break;

    case BOND_GROUP:
      recurs = closest_end_of_bond (x, y, &recurs_end,
	                            current->group_members, &d1, VERY_BIG);
      if (d1 < d_best)
      {
	*end = recurs_end;
	d_best = d1;
	best = recurs;
      }
      break;
    }

    current = current->next;
  }

  if (dist != NULL)
    *dist = d_best;

  if (d_best < sensibility)
    return best;
  else
    return NULL;
}



/* Returns a pointer to the closest bond. */
struct Bond *
closest_bond (const LLINT x, const LLINT y, struct Bond *list,
	      const LLINT sensibility)
{
  double best_dist2, dist2;
  struct Bond *best, *current;

  if (list == NULL)
    return NULL;

  best = NULL;
  best_dist2 = VERY_BIG;
  current = list;

  do
    {
      dist2 = distance_to_bond (x, y, current);

      if (best == NULL || dist2 < best_dist2)
	{
	  best = current;
	  best_dist2 = dist2;
	}
    }
  while ((current = current->next) != NULL);

  if (best_dist2 < sensibility)
    return best;
  else
    return NULL;
}


double
distance_to_bond_list (const LLINT x, const LLINT y, struct Bond *list)
{
  double best_dist2, dist2;
  struct Bond *current;

  if (list == NULL)
    return VERY_BIG;

  best_dist2 = VERY_BIG;
  current = list;

  do
    {
      dist2 = distance_to_bond (x, y, current);

      if (dist2 < best_dist2)
	best_dist2 = dist2;
    }
  while ((current = current->next) != NULL);

  return best_dist2;
}


/* Routine used to set all selected fields to 0. */
void
clear_selection (struct Bond *list, int *num_sel)
{
  struct Bond *current;

  *num_sel = 0;
  if (list == NULL)
    return;

  current = list;
  do
    current->selected = SEL_NO;
  while ((current = current->next) != NULL);
}


/* This routine clears the given attribute in the 'selected' field. This
 * shloud NOT be used with SEL_YES (num_selected will be crazy
 * otherwise). */
void
clear_selection_attribute (struct Bond *list, const int attribute)
{
  struct Bond *current;

  if (list == NULL)
    return;

  current = list;
  do
    current->selected &= SEL_ALL - attribute;
  while ((current = current->next) != NULL);
}


/* Select only the objects that are in a given rectangle */
void
select_in_rectangle (LLINT x1, LLINT y1, LLINT x2,
		     LLINT y2, struct Bond *list, int *num_sel)
{
  struct Bond *current;
  LLINT t;

  *num_sel = 0;
  if (x2 < x1)
    {
      t = x1;
      x1 = x2;
      x2 = t;
    }
  if (y2 < y1)
    {
      t = y1;
      y1 = y2;
      y2 = t;
    }

  if (list == NULL)
    return;

#define SELECT { (*num_sel)++; current->selected |= SEL_YES; }
#define UNSELECT { current->selected &= SEL_ALL - SEL_YES; }

  current = list;
  do
    {
      switch (current->type)
	{
	case BOND_SIMPLE:
	case BOND_DOUBLE:
	case BOND_TRIPLE:
	case BOND_UP:
	case BOND_DOWN:
	case BOND_DASHED:
	case BOND_ARROW:
	case BOND_CIRCLE:	/* FIXME This is wrong */
	case BOND_ARC:		/* FIXME This is wrong */
	case BOND_GROUP:
	  if (IS_BETWEEN (x1, current->x1, x2)
	      && IS_BETWEEN (x1, current->x2, x2)
	      && IS_BETWEEN (y1, current->y1, y2)
	      && IS_BETWEEN (y1, current->y2, y2))
	    {
	      SELECT;
	    }
	  else
	    {
	      UNSELECT;
	    }
	  break;

	case BOND_ATOM:
	case BOND_GROUP_L:
	case BOND_GROUP_R:
	  if (IS_BETWEEN (x1, current->x4, x2)
	      && IS_BETWEEN (x1, current->x4 + current->x2, x2)
	      && IS_BETWEEN (y1, current->y4, y2)
	      && IS_BETWEEN (y1, current->y4 + current->y2, y2))
	    {
	      SELECT;
	    }
	  else
	    {
	      UNSELECT;
	    }
	  break;
	}
    }
  while ((current = current->next) != NULL);

#undef SELECT
#undef UNSELECT
}


/* Switch the selection state of the objects in a given rectangle */
void
change_in_rectangle (LLINT x1, LLINT y1, LLINT x2,
		     LLINT y2, struct Bond *list, int *num_sel)
{
  struct Bond *current;
  LLINT t;

  if (x2 < x1)
    {
      t = x1;
      x1 = x2;
      x2 = t;
    }
  if (y2 < y1)
    {
      t = y1;
      y1 = y2;
      y2 = t;
    }

  if (list == NULL)
    return;

#define SWITCH { \
	  if (current->selected == SEL_NO) \
	    { (*num_sel)++; current->selected = SEL_YES; } \
	  else \
	    { (*num_sel)--; current->selected = SEL_NO; } }

  current = list;
  do
    {
      switch (current->type)
	{
	case BOND_SIMPLE:
	case BOND_DOUBLE:
	case BOND_TRIPLE:
	case BOND_UP:
	case BOND_DOWN:
	case BOND_DASHED:
	case BOND_ARROW:
	case BOND_CIRCLE:	/* FIXME This is wrong */
	case BOND_ARC:		/* FIXME This is wrong */
	case BOND_GROUP:
	  if (IS_BETWEEN (x1, current->x1, x2)
	      && IS_BETWEEN (x1, current->x2, x2)
	      && IS_BETWEEN (y1, current->y1, y2)
	      && IS_BETWEEN (y1, current->y2, y2))
	    SWITCH;
	  break;

	case BOND_ATOM:
	case BOND_GROUP_L:
	case BOND_GROUP_R:
	  if (IS_BETWEEN (x1, current->x4, x2)
	      && IS_BETWEEN (x1, current->x4 + current->x2, x2)
	      && IS_BETWEEN (y1, current->y4, y2)
	      && IS_BETWEEN (y1, current->y4 + current->y2, y2))
	    SWITCH;
	}
      break;
    }
  while ((current = current->next) != NULL);

#undef SWITCH
}


/* Select the entire molecule that 'ref' is in. This routine assumes that
 * nothing is selected, and num_sel is really 0. */
void
select_molecule (struct Bond *list, struct Bond *ref, int *num_sel)
{
  struct Bond *current;

  if (list == NULL || ref == NULL)
    return;

  current = list;
  (*num_sel)++;
  ref->selected = SEL_YES;
  do
    {
      if (current == ref || (current->selected & SEL_YES))
	continue;
      if (bonds_are_close (current, ref))
	select_molecule (list, current, num_sel);
    }
  while ((current = current->next) != NULL);
}



/* FIXME: this is not true... */
int
bonds_are_close (struct Bond *bond1, struct Bond *bond2)
{
  if (bond1->type == BOND_GROUP || bond2->type == BOND_GROUP)
    return 0;

  if (bond1->type >= BOND_ATOM)
    {
      if ((IS_SMALL (bond1->x3 - bond2->x1)
	   && IS_SMALL (bond1->y3 - bond2->y1))
	  || (IS_SMALL (bond1->x3 - bond2->x2)
	      && IS_SMALL (bond1->y3 - bond2->y2)))
	return 1;
    }
  else
    {
      if (bond2->type >= BOND_ATOM)
	{
	  if ((IS_SMALL (bond1->x1 - bond2->x3)
	       && IS_SMALL (bond1->y1 - bond2->y3))
	      || (IS_SMALL (bond1->x2 - bond2->x3)
		  && IS_SMALL (bond1->y2 - bond2->y3)))
	    return 1;
	}
      else
	{
	  if ((IS_SMALL (bond1->x1 - bond2->x1)
	       && IS_SMALL (bond1->y1 - bond2->y1))
	      || (IS_SMALL (bond1->x1 - bond2->x2)
		  && IS_SMALL (bond1->y1 - bond2->y2))
	      || (IS_SMALL (bond1->x2 - bond2->x1)
		  && IS_SMALL (bond1->y2 - bond2->y1))
	      || (IS_SMALL (bond1->x2 - bond2->x2)
		  && IS_SMALL (bond1->y2 - bond2->y2)))
	    return 1;
	}
    }

  return 0;
}


/* Select the entire molecule that 'ref' is in. This routine assumes that
 * initially, the flag SEL_TEMP is off for every bond! */
void
select_add_molecule (struct Bond *list, struct Bond *ref, int *num_sel)
{
  struct Bond *current;

  if (list == NULL || ref == NULL)
    return;

  ref->selected |= SEL_TEMP;
  if (ref->selected & SEL_YES)
    {
      ref->selected &= SEL_ALL - SEL_YES;
      (*num_sel)--;
    }
  else
    {
      ref->selected |= SEL_YES;
      (*num_sel)++;
    }

  current = list;
  do
    {
      if (current != ref && ((current->selected & SEL_TEMP) == 0)
	  && bonds_are_close (current, ref))
	select_add_molecule (list, current, num_sel);
    }
  while ((current = current->next) != NULL);
}


/* Remove a bond from a list. */
void
delete_bond (struct Bond **list, struct Bond *ref, int *num_sel)
{
  struct Bond *current;

  if (list == NULL || ref == NULL)
    return;

  if (ref->selected & SEL_YES)
    (*num_sel)--;
  if (ref == *list)
    {
      *list = ref->next;
      free_bond (ref);
      return;
    }


  current = *list;
  do
    if (current->next == ref)
      {
	current->next = ref->next;
	free_bond (ref);
	return;
      }
  while ((current = current->next) != NULL);
}


/* Remove a entire molecule from a list. SEL_TEMP shloud be cleared. */
void
delete_molecule (struct Bond **list, struct Bond *ref, int *num_sel)
{
  struct Bond *current;

  if (list == NULL || ref == NULL)
    return;

  ref->selected |= SEL_TEMP;
  current = *list;
  do
    {
      if (current != ref && ((current->selected & SEL_TEMP) == 0)
	  && ((IS_SMALL (current->x1 - ref->x1)
	       && IS_SMALL (current->y1 - ref->y1))
	      || (IS_SMALL (current->x1 - ref->x2)
		  && IS_SMALL (current->y1 - ref->y2))
	      || (IS_SMALL (current->x2 - ref->x1)
		  && IS_SMALL (current->y2 - ref->y1))
	      || (IS_SMALL (current->x2 - ref->x2)
		  && IS_SMALL (current->y2 - ref->y2))))
	{
	  delete_molecule (list, current, num_sel);
	  current = *list;
	}
    }
  while ((current = current->next) != NULL);

  delete_bond (list, ref, num_sel);
}


/* We compute the number of selected bonds in the given list. */
int
update_num_sel (struct Bond *list)
{
  int num_sel;
  struct Bond *current;

  if (list == NULL)
    return 0;

  num_sel = 0;
  current = list;
  do
    if (current->selected &= SEL_YES)
      num_sel++;
  while ((current = current->next) != NULL);

  return num_sel;
}


/* We look if there is an atom close to (x,y), and modify the
 * coordinates if need be. */
int
closest_point (struct Bond *list, LLINT * x, LLINT * y,
	       const LLINT sensibility)
{
  double best = VERY_BIG;
  LLINT x_save, y_save;
  struct Bond *current;

  if (list == NULL)
    return 0;
  x_save = *x;
  y_save = *y;

  current = list;
  do
    {
      if (SQR (x_save - current->x1) + SQR (y_save - current->y1)
	  < MIN (best, sensibility))
	{
	  *x = current->x1;
	  *y = current->y1;
	  best = SQR (x_save - *x) + SQR (y_save - *y);
	}
      if (SQR (x_save - current->x2) + SQR (y_save - current->y2)
	  < MIN (best, sensibility))
	{
	  *x = current->x2;
	  *y = current->y2;
	  best = SQR (x_save - *x) + SQR (y_save - *y);
	}
      if (current->type == BOND_ARC)
	{
	  if (SQR (x_save - current->x3) + SQR (y_save - current->y3)
	      < MIN (best, sensibility))
	    {
	      *x = current->x3;
	      *y = current->y3;
	      best = SQR (x_save - *x) + SQR (y_save - *y);
	    }
	}
    }
  while ((current = current->next) != NULL);

  if (best < sensibility)
    return 1;
  else
    return 0;
}


/* Modifie *x and *y so that they will be the center of the selection of
 * a given list. */
void
center_of_selection (struct Bond *list, LLINT * x_center, LLINT * y_center)
{
  struct Bond *current;
  LLINT cx, cy;
  int n;

  if (list == NULL)
    return;

  cx = 0;
  cy = 0;
  n = 0;
  current = list;
  do
    {
      if (current->selected & SEL_YES)
	switch (current->type)
	  {
	  case BOND_ARC:
	    n++;
	    cx += current->x3;
	    cy += current->y3;

	    /* Fall through */
	  case BOND_SIMPLE:
	  case BOND_DOUBLE:
	  case BOND_TRIPLE:
	  case BOND_UP:
	  case BOND_DOWN:
	  case BOND_DASHED:
	  case BOND_ARROW:
	  case BOND_GROUP:
	  case BOND_CIRCLE:
	    n++;
	    cx += current->x1;
	    cy += current->y1;
	    n++;
	    cx += current->x2;
	    cy += current->y2;
	    break;

	  case BOND_ATOM:
	  case BOND_GROUP_L:
	  case BOND_GROUP_R:
	    n++;
	    cx += current->x3;
	    cy += current->y3;
	    break;

	  default:
	    bug_in ("center_of_selection");
	  }
    }
  while ((current = current->next) != NULL);

  *x_center = cx / n;
  *y_center = cy / n;
}


/* This removes from a list the bonds that are selected */
void
delete_selection (struct Bond **list, int *num_sel)
{
  struct Bond *current;

  if (*list == NULL)
    return;
  current = *list;

  do
    if (current->selected & SEL_YES)
      {
	delete_bond (list, current, num_sel);
	current = NULL;
      }
  while ((current = (current == NULL ? *list : current->next)) != NULL);
}


/* Create a new bond list from all the selected items in a given list.
 * This is used for cut/copy mechanism. */
struct Bond *
copy_selection (struct Bond *list, LLINT xshift, LLINT yshift)
{
  struct Bond *new_list = NULL, *new = NULL, *current = list;

  if (list == NULL)
    return NULL;

  do
    if (current->selected & SEL_YES)
    {
      if (new_list == NULL)
      {
	new_list = copy_bond (current);
	new = new_list;
      }
      else
      {
	new->next = copy_bond (current);
	new = new->next;
      }
      move_bond_from_pos (new, new, xshift, yshift);
    }
  while ((current = current->next) != NULL);

  return new_list;
}


static char object_name[BOND_DELIMITER][5] = { "smpl", "dble", "trpl",
  "up__", "down", "dash", "arrw", "arc_", "crcl", "atom", "grpl", "grpr",
  "bgrp"};


/* The procedure used to save a file. 'debug' is used when debugging
 * instead of saving a real file, and 'no_title' is set when the function
 * is called recursively. */
void
save_list_bonds (struct Bond *list, FILE * file, const int debug,
		 const int no_title)
{
  struct Bond *current;
  struct Ornament *orn;

  if (!no_title)
    fprintf (file, "# Save file for EasyChem (version " VERSION ")\n\n");
  if (list == NULL)
    return;
  current = list;

  do
    {
      fprintf (file, "%s %" LLFORMAT " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT
	       " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT,
	       object_name[current->type], current->x1, current->y1,
	       current->x2, current->y2, current->x3, current->y3,
	       current->x4, current->y4);
      fprintf (file, " %lg", current->width);
      fprintf (file, " %u %u %u", (current->color).red,
 	       (current->color).green, (current->color).blue);
      if (debug)
	fprintf (file, " %d", current->selected);
      if (BOND_HAS_TEXT (current))
	fprintf (file, " '%s\\EndOfText\n", current->text);
      else
	fprintf (file, "\n");

      if (current->type == BOND_GROUP)
	{
	  fprintf (file, "<<<<<\n");
	  save_list_bonds (current->group_members, file, debug, 1);
	  fprintf (file, ">>>>>\n");
	}

      if (current->ornaments[0] != NULL)
      {
	orn = current->ornaments[0];
	while (orn != NULL)
	{
          fprintf (file, "orn0 %u %u %d %d %lg %lg\n", orn->type,
	           orn->number, orn->angle, orn->spacing,
		   orn->dist, orn->size);
	  orn = orn->next;
	}
      }
      if (current->ornaments[1] != NULL)
      {
	orn = current->ornaments[1];
	while (orn != NULL)
	{
          fprintf (file, "orn1 %u %u %d %d %lg %lg\n", orn->type,
	           orn->number, orn->angle, orn->spacing,
		   orn->dist, orn->size);
	  orn = orn->next;
	}
      }

    }
  while ((current = current->next) != NULL);
}


/* The procedure used to load a file. If shift is non-zero, then
 * the loaded bond list is shifted from its normal position. This is
 * used for merging files. */
struct Bond *
load_list_bonds (FILE * file, const int recursive, const int shift)
{
#define LONG 500
#define SHIFT 3000
  char buf[LONG], name[5];
  struct Bond * list = NULL, * current = NULL;
  struct Ornament * orn[2] = { NULL, NULL};
  int type, i;
  LLINT x1, y1, x2, y2, x3, y3, x4, y4;
  unsigned int red, green, blue;
  double width;

  if (!recursive)
    {
      fgets (buf, LONG, file);
      fgets (buf, LONG, file);
    }

  while (fscanf (file, "%4s", name) == 1)
    {
      type = -1;
      if (strncmp (name, "orn", 3) == 0)
      {
	i = atoi (name + 3);
	if (current == NULL)
	  bug_in ("load_list_bonds");
	if (orn[i] == NULL)
	{
	  orn[i] = g_new (struct Ornament, 1);
	  current->ornaments[i] = orn[i];
	}
	else
	{
	  orn[i]->next = g_new (struct Ornament, 1);
	  orn[i] = orn[i]->next;
	}
	orn[i]->next = NULL;
	fscanf (file, "%u %u %u %u %lg %lg", &(orn[i]->type),
                &(orn[i]->number), &(orn[i]->angle),
		&(orn[i]->spacing), &(orn[i]->dist), &(orn[i]->size));
	continue;
      }
      orn[0] = NULL;
      orn[1] = NULL;
      for (i = 0; i < BOND_DELIMITER; i++)
	if (strncmp (name, object_name[i], 4) == 0)
	  type = i;
      if (type == -1)
	return list;

      if (current == NULL)
	{
	  list = g_new (struct Bond, 1);
	  current = list;
	}
      else
	{
	  current->next = g_new (struct Bond, 1);
	  current = current->next;
	}

      fscanf (file, "%" LLFORMAT " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT
	      " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT " %" LLFORMAT,
	      &x1, &y1, &x2, &y2, &x3, &y3, &x4, &y4);
      fscanf (file, "%lg %u %u %u ", &width, &red, &green, &blue);
      current->type = type;
      current->x1 = x1;
      current->x2 = x2;
      current->x3 = x3;
      current->x4 = x4;
      current->y1 = y1;
      current->y2 = y2;
      current->y3 = y3;
      current->y4 = y4;
      current->width = width;
      (current->color).red = red;
      (current->color).green = green;
      (current->color).blue = blue;
      current->selected = SEL_NO;
      current->text = NULL;
      current->pango = NULL;
      current->pango_left = NULL;
      current->pango_right = NULL;
      current->ornaments[0] = NULL;
      current->ornaments[1] = NULL;
      current->next = NULL;

      if (BOND_HAS_TEXT (current))
	{
	  if (shift)
	  {
	    current->x3 += SHIFT;
	    current->y3 += SHIFT;
	  }

	  fgets (buf, LONG - 1, file);
	  for (i = strlen (buf) - 11; i < (signed) strlen (buf); i++)
	    buf[i] = 0;
	  if (buf[0] == '\'')
	    current->text = g_strdup (buf + 1);
	  else /* This is compatibility for version <= 0.3 */
	    current->text = g_strdup (buf);
	  text_to_pango (current);	/* TODO -- gestion d'erreur */
	}
      else
      {
	if (shift)
	{
	  current->x1 += SHIFT;
	  current->y1 += SHIFT;
	  current->x2 += SHIFT;
	  current->y2 += SHIFT;
	  if (current->type == BOND_ARC)
	  {
	    current->x3 += SHIFT;
	    current->y3 += SHIFT;
	  }
	}
      }

      if (type == BOND_GROUP)
	{
	  fgets (buf, LONG - 1, file);
	  current->group_members = load_list_bonds (file, 1, shift);
	  fgets (buf, LONG - 1, file);
	}
    }

  return list;
#undef LONG
#undef SHIFT
}


void
move_bond_from_pos (struct Bond *new, struct Bond *old, const LLINT x,
		    const LLINT y)
{
  struct Bond *current_new, *current_old;

  switch (new->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_UP:
    case BOND_DOWN:
    case BOND_DASHED:
    case BOND_CIRCLE:
    case BOND_ARROW:
      new->x1 = old->x1 + x;
      new->y1 = old->y1 + y;
      new->x2 = old->x2 + x;
      new->y2 = old->y2 + y;
      break;

    case BOND_ARC:
      new->x1 = old->x1 + x;
      new->y1 = old->y1 + y;
      new->x2 = old->x2 + x;
      new->y2 = old->y2 + y;
      new->x3 = old->x3 + x;
      new->y3 = old->y3 + y;
      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      new->x1 = old->x1 + x;
      new->y1 = old->y1 + y;
      new->x3 = old->x3 + x;
      new->y3 = old->y3 + y;
      new->x4 = old->x4 + x;
      new->y4 = old->y4 + y;
      break;

    case BOND_GROUP:
      current_new = new->group_members;
      current_old = old->group_members;
      do
	{
	  move_bond_from_pos (current_new, current_old, x, y);

	  current_new = current_new->next;
	  current_old = current_old->next;
	}
      while (current_new != NULL);
      update_bounds_of_group (new);
      break;

    default:
      bug_in ("move_bond_from_pos");
    }
}


void
move_point_from_pos (struct Bond *new, struct Bond *old, const LLINT x_point,
		     const LLINT y_point, const LLINT x, const LLINT y)
{
  struct Bond *current_new, *current_old;

  current_new = new;
  current_old = old;

  do
    {
      switch (current_old->type)
	{
	case BOND_ARC:
	  if (IS_SMALL (current_old->x3 - x_point)
	      && IS_SMALL (current_old->y3 - y_point))
	    {
	      current_new->x3 = current_old->x3 + x;
	      current_new->y3 = current_old->y3 + y;
	    }

	  /* Fall through */
	case BOND_SIMPLE:
	case BOND_DOUBLE:
	case BOND_TRIPLE:
	case BOND_UP:
	case BOND_DOWN:
	case BOND_DASHED:
	case BOND_CIRCLE:
	case BOND_ARROW:
	  if (IS_SMALL (current_old->x1 - x_point)
	      && IS_SMALL (current_old->y1 - y_point))
	    {
	      current_new->x1 = current_old->x1 + x;
	      current_new->y1 = current_old->y1 + y;
	    }
	  if (IS_SMALL (current_old->x2 - x_point)
	      && IS_SMALL (current_old->y2 - y_point))
	    {
	      current_new->x2 = current_old->x2 + x;
	      current_new->y2 = current_old->y2 + y;
	    }
	  break;

	case BOND_ATOM:
	case BOND_GROUP_L:
	case BOND_GROUP_R:
	  if (IS_SMALL (current_old->x3 - x_point)
	      && IS_SMALL (current_old->y3 - y_point))
	    {
	      current_new->x1 = current_old->x1 + x;
	      current_new->y1 = current_old->y1 + y;
	      current_new->x3 = current_old->x3 + x;
	      current_new->y3 = current_old->y3 + y;
	      current_new->x4 = current_old->x4 + x;
	      current_new->y4 = current_old->y4 + y;
	    }
	  break;

	case BOND_GROUP:
	  break;

	default:
	  bug_in ("move_point_from_pos");
	}

      current_new = current_new->next;
      current_old = current_old->next;
    }
  while (current_new != NULL);
}


/* This routine is used to move the points in a given bond list that are
 * close to moving objects (that is, they have their SEL_CLOSE_1 or
 * SEL_CLOSE_2 flags set). */
void
move_point_close (struct Bond *new, struct Bond *old, const LLINT x,
		  const LLINT y)
{
  switch (new->type)
    {
    case BOND_SIMPLE:
    case BOND_DOUBLE:
    case BOND_TRIPLE:
    case BOND_UP:
    case BOND_DOWN:
    case BOND_DASHED:
    case BOND_ARROW:
    case BOND_ARC:
      if (new->selected & SEL_CLOSE_1)
	{
	  new->x1 = old->x1 + x;
	  new->y1 = old->y1 + y;
	}
      if (new->selected & SEL_CLOSE_2)
	{
	  new->x2 = old->x2 + x;
	  new->y2 = old->y2 + y;
	}
      break;

    case BOND_ATOM:
    case BOND_GROUP_L:
    case BOND_GROUP_R:
      if (new->selected & SEL_CLOSE_1)
	{
	  new->x1 = old->x1 + x;
	  new->y1 = old->y1 + y;
	  new->x3 = old->x3 + x;
	  new->y3 = old->y3 + y;
	  new->x4 = old->x4 + x;
	  new->y4 = old->y4 + y;
	}
      break;

    case BOND_GROUP:
    case BOND_CIRCLE:
      break;

    default:
      bug_in ("move_point_close");
    }
}


/* This routine predicts the number of interstring points for the given
 * list of bonds, in order to allocate memory in the 'generate_points'
 * routine. */
unsigned int
count_points (struct Bond *list)
{
  struct Bond *current = list;
  unsigned int count = 0;

  if (list == NULL)
    return 0;

  do
    {
      switch (current->type)
	{
	case BOND_CIRCLE:
	  break;

	case BOND_SIMPLE:
	case BOND_DOUBLE:
	case BOND_TRIPLE:
	case BOND_UP:
	case BOND_DOWN:
	case BOND_DASHED:
	case BOND_ARROW:
	case BOND_ARC:
	  count += 2;
	  break;

	case BOND_ATOM:
	case BOND_GROUP_L:
	case BOND_GROUP_R:
	  count += 1;
	  break;

	case BOND_GROUP:
	  count += count_points (current->group_members);
	  break;

	default:
	  bug_in ("generate_points");
	}
    }
  while ((current = current->next) != NULL);

  return count;
}


/* This is the recursive routine that is called by 'generate_points'
 * after allocation of memory. */
int
generate_points_sub (struct Bond *list, LLINT * x, LLINT * y, short int *sel)
{
  unsigned int n = 0;
  struct Bond *current = list;

  if (list == NULL)
    return 0;

  do
    {
      switch (current->type)
	{
	case BOND_CIRCLE:
	  break;

	case BOND_SIMPLE:
	case BOND_DOUBLE:
	case BOND_TRIPLE:
	case BOND_UP:
	case BOND_DOWN:
	case BOND_DASHED:
	case BOND_ARROW:
	case BOND_ARC:
	  x[n] = current->x1;
	  y[n] = current->y1;
	  if (current->selected & SEL_YES)
	    sel[n] = 1;
	  else
	    sel[n] = 0;
	  n++;
	  x[n] = current->x2;
	  y[n] = current->y2;
	  if (current->selected & SEL_YES)
	    sel[n] = 1;
	  else
	    sel[n] = 0;
	  n++;
	  break;

	case BOND_ATOM:
	case BOND_GROUP_L:
	case BOND_GROUP_R:
	  x[n] = current->x3;
	  y[n] = current->y3;
	  if (current->selected & SEL_YES)
	    sel[n] = 1;
	  else
	    sel[n] = 0;
	  n++;
	  break;

	case BOND_GROUP:
	  n +=
	    generate_points_sub (current->group_members, x + n, y + n,
				 sel + n);
	  break;

	default:
	  bug_in ("generate_points");
	}
    }
  while ((current = current->next) != NULL);

  return n;
}


/* This subroutine generates x and y arrays, which are the coordinates of
 * all interesting points in the given bond list. The 'sel' array tells
 * us whether a point is part of a selected object. */
int
generate_points (struct Bond *list, LLINT ** x, LLINT ** y, short int **sel)
{
  unsigned int n;

  if (list == NULL)
    return 0;

  n = count_points (list);
  *x = (LLINT *) calloc (n, sizeof (LLINT));
  *y = (LLINT *) calloc (n, sizeof (LLINT));
  *sel = (short int *) calloc (n, sizeof (short int));

  return generate_points_sub (list, *x, *y, *sel);
}



/* Warning : this procedure has to work properly even if list_new and
 * list_old are pointing to the same thing!!!! Please take care.*/
void
rotate_angle (struct Bond *list_new, struct Bond *list_old,
	      const LLINT x_orig, const LLINT y_orig,
	      const double st, const double ct, const int recursive)
{
  struct Bond *new, *old;
  double xs, dx, dy, dX, dY;

  new = list_new;
  old = list_old;
  do
    {
      if (recursive || (new->selected & SEL_YES))
	switch (old->type)
	  {
	  case BOND_ARC:
	    new->x3 = old->x3 - x_orig;
	    new->y3 = old->y3 - y_orig;

	    xs = new->x3;
	    new->x3 = ct * new->x3 + st * new->y3;
	    new->y3 = -st * xs + ct * new->y3;

	    new->x3 += x_orig;
	    new->y3 += y_orig;

	    /* Fall through */
	  case BOND_SIMPLE:
	  case BOND_DOUBLE:
	  case BOND_TRIPLE:
	  case BOND_UP:
	  case BOND_DOWN:
	  case BOND_DASHED:
	  case BOND_ARROW:
	  case BOND_CIRCLE:
	    new->x1 = old->x1 - x_orig;
	    new->y1 = old->y1 - y_orig;
	    new->x2 = old->x2 - x_orig;
	    new->y2 = old->y2 - y_orig;

	    xs = new->x1;
	    new->x1 = ct * new->x1 + st * new->y1;
	    new->y1 = -st * xs + ct * new->y1;
	    xs = new->x2;
	    new->x2 = ct * new->x2 + st * new->y2;
	    new->y2 = -st * xs + ct * new->y2;

	    new->x1 += x_orig;
	    new->y1 += y_orig;
	    new->x2 += x_orig;
	    new->y2 += y_orig;
	    break;

	  case BOND_ATOM:
	  case BOND_GROUP_L:
	  case BOND_GROUP_R:
	    dx = old->x1 - old->x3;
	    dy = old->y1 - old->y3;
	    dX = old->x4 - old->x3;
	    dY = old->y4 - old->y3;

	    new->x3 = old->x3 - x_orig;
	    new->y3 = old->y3 - y_orig;

	    xs = new->x3;
	    new->x3 = ct * new->x3 + st * new->y3;
	    new->y3 = -st * xs + ct * new->y3;

	    new->x3 += x_orig;
	    new->y3 += y_orig;

	    new->x1 = new->x3 + dx;
	    new->y1 = new->y3 + dy;
	    new->x4 = new->x3 + dX;
	    new->y4 = new->y3 + dY;
	    break;

	  case BOND_GROUP:
	    rotate_angle (new->group_members, old->group_members,
			  x_orig, y_orig, st, ct, 1);
	    update_bounds_of_group (new);
	    break;

	  default:
	    bug_in ("rotate_angle");
	  }

      new = new->next;
      old = old->next;
    }
  while (new != NULL);
}



/* Returns are non-zero value if the two given bond lists are similar
 * in every detail. */
int
bond_list_are_similar (struct Bond *list1, struct Bond *list2)
{
  struct Bond *current1, *current2;

  current1 = list1;
  current2 = list2;

  if ((current1 == NULL) && (current2 == NULL))
    return 1;
  if ((current1 == NULL) || (current2 == NULL))
    return 0;

  do
    {
      if (!is_similar (current1, current2))
	return 0;

      current1 = current1->next;
      current2 = current2->next;
    }
  while ((current1 != NULL) && (current2 != NULL));

  if ((current1 != NULL) || (current2 != NULL))
    return 0;

  return 1;
}


int
unmodified (struct Bond *list, const char filename[])
{
  FILE *file;
  struct Bond *saved;

  if (filename == NULL)
    return 0;

  file = fopen (filename, "r");
  if (file == NULL)
    return 0;
  saved = load_list_bonds (file, 0, 0);
  fclose (file);

  if (bond_list_are_similar (saved, list))
    return 1;
  else
    return 0;
}


void
select_all (struct Bond *list, int *num_sel)
{
  struct Bond *current;

  *num_sel = 0;
  if (list == NULL)
    return;

  current = list;
  do
    {
      current->selected |= SEL_YES;
      (*num_sel)++;
    }
  while ((current = current->next) != NULL);
}



/* This returns the bounds of the selection */
void
bounds_of_selection (struct Bond *list, LLINT * xmin, LLINT * xmax,
		     LLINT * ymin, LLINT * ymax)
{
  struct Bond *current;

  *xmin = 2 * prop.global_width;
  *xmax = -2 * prop.global_width;
  *ymin = 2 * prop.global_height;
  *ymax = -2 * prop.global_height;

  current = list;
  do
    if (current->selected & SEL_YES)
      bounds_of_bond (current, xmin, xmax, ymin, ymax, 1);
  while ((current = current->next) != NULL);
}


/* This returns the bounds of the selection */
void
bounds_of_list (struct Bond *list, LLINT * xmin, LLINT * xmax,
		LLINT * ymin, LLINT * ymax)
{
  struct Bond *current;

  *xmin = 2 * prop.global_width;
  *xmax = -2 * prop.global_width;
  *ymin = 2 * prop.global_height;
  *ymax = -2 * prop.global_height;

  current = list;
  do
    bounds_of_bond (current, xmin, xmax, ymin, ymax, 1);
  while ((current = current->next) != NULL);
}


/*******************************************************************/
/*******************           ORNAMENTS         *******************/
/*******************************************************************/


/* Removes all ornaments on a given point. This is used just before
 * putting them back in place. A pointer is returnd to the place where
 * new ornaments on this point should go ('first'). */
struct Bond *
delete_ornaments_on_point (struct Bond *list, const LLINT x, const LLINT y)
{
  struct Bond * current = list, * first = NULL;
  int end;

  while (current != NULL)
  {
    if (current->type == BOND_GROUP)
    {
      if (first == NULL)
        first = delete_ornaments_on_point (current->group_members, x, y);
      else
	(void) delete_ornaments_on_point (current->group_members, x, y);
      current = current->next;
      continue;
    }
    end = -1;

    if (BOND_HAS_TEXT (current))
    {
      if (IS_SMALL (current->x3 - x) && IS_SMALL (current->y3 - y))
        end = 0;
    }
    else
    {
      if (IS_SMALL (current->x1 - x) && IS_SMALL (current->y1 - y))
        end = 0;
      if (IS_SMALL (current->x2 - x) && IS_SMALL (current->y2 - y))
        end = 1;
    }
    if (end >= 0)
    {
      if (first == NULL)
	first = current;
      free_orn_list(current->ornaments[end]);
      current->ornaments[end] = NULL;
    }
    
    current = current->next;
  }

  return first;
}


/* Gathers all ornaments on a given point to an 'orns' array, with at
 * most 'max' elements. Returns the number of ornaments found.
 *
 * NB: this doesn't suppress the original ornaments!
 * */
unsigned int
gather_ornaments_on_point (struct Bond *list, struct Ornament orns[],
	                   const unsigned int max, const LLINT x,
			   const LLINT y)
{
  struct Ornament * orn;
  struct Bond * current = list;
  unsigned int found = 0;
  int end;

  if (current == NULL)
    return 0;

  while (current != NULL)
  {
    if (current->type == BOND_GROUP)
    {
      found += gather_ornaments_on_point (current->group_members,
                                       	  &(orns[found]),
	                                  max - found, x, y);
      current = current->next;
      continue;
    }

/* Identify if we're at the right point */
    end = -1;
    if (BOND_HAS_TEXT (current))
    {
      if (IS_SMALL (current->x3 - x) && IS_SMALL (current->y3 - y))
        end = 0;
    }
    else
    {
      if (IS_SMALL (current->x1 - x) && IS_SMALL (current->y1 - y))
        end = 0;
      if (IS_SMALL (current->x2 - x) && IS_SMALL (current->y2 - y))
        end = 1;
    }

/* And put ornaments into the array */
    if (end >= 0)
    {
      orn = current->ornaments[end];
    
      while (orn != NULL && found < max)
      {
	orns[found] = *orn;
	found++;
	orn = orn->next;
      }
    }

    current = current->next;
  }

  return found;
}



/*******************************************************************/
/*******************           GROUPING          *******************/
/*******************************************************************/


/* This groups the selected bonds */
void
group_selection (struct Bond **list, int *num_sel)
{
  struct Bond *current, *new, *last, *last_sel;
  LLINT xmin, xmax, ymin, ymax;

  if (*list == NULL)
    return;

  new = g_new (struct Bond, 1);
  bounds_of_selection (*list, &xmin, &xmax, &ymin, &ymax);
  new->x1 = xmin;
  new->y1 = ymin;
  new->x2 = xmax;
  new->y2 = ymax;
  new->x3 = 0;
  new->y3 = 0;
  new->x4 = 0;
  new->y4 = 0;
  new->type = BOND_GROUP;
  new->selected = SEL_YES;
  new->text = NULL;
  new->pango = NULL;
  new->pango_left = NULL;
  new->pango_right = NULL;
  new->ornaments[0] = NULL;
  new->ornaments[1] = NULL;
  new->next = *list;

  last = new;
  last_sel = NULL;
  current = new->next;
  do
    {
      if (current->selected & SEL_YES)
	{
	  if (last_sel == NULL)
	    new->group_members = current;
	  else
	    last_sel->next = current;

	  current->selected = SEL_NO;
	  last_sel = current;
	  last->next = current->next;
	  current = current->next;
	  last_sel->next = NULL;
	}
      else
	{
	  last = current;
	  current = current->next;
	}
    }
  while (current != NULL);

  *list = new;
  *num_sel = 1;
}


/* This routine performs the ungrouping of  all selected groups. */
void
ungroup_selection (struct Bond **list, int *num_sel)
{
  struct Bond *current, *current_group, *last;

  if (*list == NULL)
    return;

  last = NULL;
  current = *list;
  do
    {
      if ((current->selected & SEL_YES) && (current->type == BOND_GROUP))
	{
	  current_group = current->group_members;
	  while (current_group->next != NULL)
	    {
	      current_group->selected = SEL_YES;
	      (*num_sel)++;
	      current_group = current_group->next;
	    }
	  current_group->selected = SEL_YES;

	  current_group->next = *list;
	  if (last == NULL)
	    last = current_group;
	  *list = current->group_members;

	  last->next = current->next;
	  current->group_members = NULL;
	  g_free (current);
	  current = last->next;
	}
      else
	{
	  last = current;
	  current = current->next;
	}
    }
  while (current != NULL);
}


/* This procedure is called whenever we want to update the x1, y1, x2 and
 * y2 values of a group (ie, the bounds of the group) */
void
update_bounds_of_group (struct Bond *group)
{
  LLINT xmin, xmax, ymin, ymax;
  struct Bond *current;

  xmin = 2 * prop.global_width;
  xmax = -2 * prop.global_width;
  ymin = 2 * prop.global_height;
  ymax = -2 * prop.global_height;

  current = group->group_members;
  do
    {
      bounds_of_bond (current, &xmin, &xmax, &ymin, &ymax, 1);
    }
  while ((current = current->next) != NULL);

  group->x1 = xmin;
  group->x2 = xmax;
  group->y1 = ymin;
  group->y2 = ymax;
}


/* This routine aligns the selected objects (bonds, groups) on a given
 * line.
 *    Horizontal --> 0 means 'none'
 *               --> 1 means 'left'
 *               --> 2 means 'center'
 *               --> 3 means 'right'
 *      Vertical --> 0 means 'none'
 *               --> 1 means 'top'
 *               --> 2 means 'center'
 *               --> 3 means 'bottom'
 * */
void
align_selection (struct Bond *list, const int horizontal, const int vertical)
{
  LLINT xmin, xmax, ymin, ymax, Xmin, Xmax, Ymin, Ymax;
  struct Bond *current;

  if (list == NULL)
    return;

  bounds_of_selection (list, &Xmin, &Xmax, &Ymin, &Ymax);

  current = list;
  do
    {
      if (!(current->selected & SEL_YES))
	continue;

      xmin = 2 * prop.global_width;
      xmax = -2 * prop.global_width;
      ymin = 2 * prop.global_height;
      ymax = -2 * prop.global_height;
      bounds_of_bond (current, &xmin, &xmax, &ymin, &ymax, 0);
      switch (horizontal)
	{
	case 0:		/* None */
	  break;

	case 1:		/* Left */
	  move_bond_from_pos (current, current, Xmin - xmin, (LLINT) 0);
	  break;

	case 2:		/* Center */
	  move_bond_from_pos (current, current,
			      ((Xmax - xmax) - (xmin - Xmin)) / 2, (LLINT) 0);
	  break;

	case 3:		/* Right */
	  move_bond_from_pos (current, current, Xmax - xmax, (LLINT) 0);
	  break;
	}
      switch (vertical)
	{
	case 0:		/* None */
	  break;

	case 1:		/* Top */
	  move_bond_from_pos (current, current, (LLINT) 0, Ymin - ymin);
	  break;

	case 2:		/* Center */
	  move_bond_from_pos (current, current, (LLINT) 0,
			      ((Ymax - ymax) - (ymin - Ymin)) / 2);
	  break;

	case 3:		/* Bottom */
	  move_bond_from_pos (current, current, (LLINT) 0, Ymax - ymax);
	  break;
	}
    }
  while ((current = current->next) != NULL);

  return;
}



/* Here we work on rich text. Parsers and all routines to transform rich
 * text to different formats will be here. */
int
text_to_pango (struct Bond *bond)
{
  gchar *left, *right;

  g_free (bond->pango);
  bond->pango = NULL;
  g_free (bond->pango_right);
  bond->pango_right = NULL;
  g_free (bond->pango_left);
  bond->pango_left = NULL;
  bond->pango = richtext_to_pango (bond->text, 0, strlen (bond->text));
  if (bond->pango == NULL)
    return 0;

  parts_of_group_text (bond, &left, &right);
  if (bond->type != BOND_ATOM)
    {
      bond->pango_left = richtext_to_pango (left, 0, strlen (left));
      bond->pango_right = richtext_to_pango (right, 0, strlen (right));

      if ((bond->pango_left == NULL) || (bond->pango_right == NULL))
	return 0;
    }
  return 1;
}



unsigned int
corresponding_bracket (const gchar * text, const unsigned p)
{
  unsigned int pos = p + 1;
  int brack = 0;

  while ((text[pos] != '}') || (brack != 0))
    {
      if (text[pos] == '{')
	brack++;
      if (text[pos] == '}')
	brack--;
      if ((text[pos] == 0) || (brack < 0))
	return 0;
      pos++;
    }
  return pos;
}


gchar *
richtext_to_pango (const gchar * text, const unsigned int p1i,
		   const unsigned int p2i)
{
#define IS_CONTROL(x) (((x) == '\\') || ((x) == '_') || ((x) == '^') \
                       || ((x) == '{') || ((x) == '}'))

#define CMD_N 2
#define CMD_MAX_LEN 7

  const unsigned int cmd_len[CMD_N] = { 5, 7 };
  const char cmd[CMD_N][CMD_MAX_LEN] = { "emph{", "textbf{" };
  const char cmd_pango[CMD_N][CMD_MAX_LEN] = { "i", "b" };

#define ENT_N 56
#define ENT_MAX_LEN 7

  const unsigned int ent_len[ENT_N] = { /*alpha */ 5, 4, 5, 5, 7, 4, 3, 5,
    4, 5, 6, 2, 2, 2, 7, 2, 3, 5, 3, 7, 3, 3, 3, 5, /*Alpha */ 5, 4, 5, 5,
    7, 4, 3, 5, 4, 5, 6, 2, 2, 2, 7, 2, 3, 5, 3, 7, 3, 3, 3, /*Omega */ 5,
    2, 2, 2, 2, /*euro */ 4, 2, /*ss */ 2, 1
  };
  const char ent[ENT_N][ENT_MAX_LEN] = { "alpha", "beta", "gamma", "delta",
    "epsilon", "zeta", "eta", "theta", "iota", "kappa", "lambda", "mu",
    "nu", "xi", "omicron", "pi", "rho", "sigma", "tau", "upsilon", "phi",
    "chi", "psi", "omega", "Alpha", "Beta", "Gamma", "Delta", "Epsilon",
    "Zeta", "Eta", "Theta", "Iota", "Kappa", "Lambda", "Mu", "Nu", "Xi",
    "Omicron", "Pi", "Rho", "Sigma", "Tau", "Upsilon", "Phi", "Chi", "Psi",
    "Omega", "oe", "OE", "ae", "AE", "euro", "TM", "ss", "-"
  };
  const char ent_pango[ENT_N][ENT_MAX_LEN] = { "α", "β", "γ", "δ", "ε",
    "ζ", "η", "θ", "ι", "κ", "λ", "μ", "ν", "ξ", "ο", "π", "ρ",
    "σ", "τ", "υ", "φ", "χ", "ψ", "ω", "Α", "Β", "Γ",
    "Δ", "Ε", "ζ", "Η", "Θ", "Ι", "Κ", "Λ", "Μ", "Ν",
    "Ξ", "Ο", "Π", "Ρ", "Σ", "Τ", "Υ", "Φ", "Χ", "Ψ", "Ω",
    "œ", "Œ", "æ", "Æ", "€", "™", "ß", "-"
  };

  gchar *pre, *total, *in, *end;
  unsigned int p1 = p1i, p2, i, j;

  while ((p1 < p2i) && !IS_CONTROL (text[p1]))
    p1++;

/*  pre = g_strndup (text + p1i, p2i - p1i);
  printf ("%s\n", pre);
  g_free (pre);*/

  if (p1 == p2i)
    return g_strndup (text + p1i, p2i - p1i);

  pre = g_strndup (text + p1i, p1 - p1i);
  if (pre == NULL)
    return NULL;
  switch (text[p1])
    {
    case '\\':
      if (text[p1 + 1] == '\\')
	{
	  end = richtext_to_pango (text, p1 + 2, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s\\%s", pre, end);
	  g_free (pre);
	  g_free (end);
	  return total;
	}
      else if (text[p1 + 1] == '{' || text[p1 + 1] == '}')
        {
	  end = richtext_to_pango (text, p1 + 2, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s%c%s", pre, text[p1 + 1], end);
	  g_free (pre);
	  g_free (end);
	  return total;
        }
      else
	{
	  /* Analysis of the possible commands (\textbf, \emph, ...) */
	  for (i = 0; i < CMD_N; i++)
	    if (strncmp (cmd[i], text + p1 + 1, cmd_len[i]) == 0)
	      {
		p2 = corresponding_bracket (text, p1 + cmd_len[i]);
		if ((p2 == 0) || (p2 <= p1 + 1))
		  {
		    g_free (pre);
		    return NULL;
		  }
		in = richtext_to_pango (text, p1 + 1 + cmd_len[i], p2);
		if (in == NULL)
		  {
		    g_free (pre);
		    return NULL;
		  }
		end = richtext_to_pango (text, p2 + 1, p2i);
		if (end == NULL)
		  {
		    g_free (pre);
		    g_free (in);
		    return NULL;
		  }
		total =
		  g_strdup_printf ("%s<%s>%s</%s>%s", pre, cmd_pango[i],
				   in, cmd_pango[i], end);
		g_free (pre);
		g_free (in);
		g_free (end);
		return total;
	      }

	  /* Analysis of the possible entities (\alpha, \oe, ...) */
	  for (i = 0; i < ENT_N; i++)
	    if (strncmp (ent[i], text + p1 + 1, ent_len[i]) == 0)
	      {
		if (IS_ALPHA (text[p1 + ent_len[i] + 1]))
		  break;
		j = 1;
		while (text[p1 + ent_len[i] + j] == ' ')
		  j++;
		end = richtext_to_pango (text, p1 + ent_len[i] + j, p2i);
		if (end == NULL)
		  {
		    g_free (pre);
		    return NULL;
		  }
		total = g_strdup_printf ("%s%s%s", pre, ent_pango[i], end);
		g_free (pre);
		g_free (end);
		return total;
	      }
	}
      break;

    case '_':
      if (text[p1 + 1] == '{')
	{
	  p2 = corresponding_bracket (text, p1 + 1);
	  if ((p2 == 0) || (p2 <= p1 + 1))
	    {
	      g_free (pre);
	      return NULL;
	    }
	  in = richtext_to_pango (text, p1 + 2, p2);
	  if (in == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  end = richtext_to_pango (text, p2 + 1, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      g_free (in);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s<sub>%s</sub>%s", pre, in, end);
	  g_free (pre);
	  g_free (in);
	  g_free (end);
	  return total;
	}
      else
	{
	  in = richtext_to_pango (text, p1 + 1, p1 + 2);
	  if (in == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  end = richtext_to_pango (text, p1 + 2, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      g_free (in);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s<sub>%s</sub>%s", pre, in, end);
	  g_free (in);
	  g_free (pre);
	  g_free (end);
	  return total;
	}
      break;

    case '^':
      if (text[p1 + 1] == '{')
	{
	  p2 = corresponding_bracket (text, p1 + 1);
	  if ((p2 == 0) || (p2 <= p1 + 1))
	    {
	      g_free (pre);
	      return NULL;
	    }
	  in = richtext_to_pango (text, p1 + 2, p2);
	  if (in == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  end = richtext_to_pango (text, p2 + 1, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      g_free (in);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s<sup>%s</sup>%s", pre, in, end);
	  g_free (pre);
	  g_free (in);
	  g_free (end);
	  return total;
	}
      else
	{
	  in = richtext_to_pango (text, p1 + 1, p1 + 2);
	  if (in == NULL)
	    {
	      g_free (pre);
	      return NULL;
	    }
	  end = richtext_to_pango (text, p1 + 2, p2i);
	  if (end == NULL)
	    {
	      g_free (pre);
	      g_free (in);
	      return NULL;
	    }
	  total = g_strdup_printf ("%s<sup>%s</sup>%s", pre, in, end);
	  g_free (in);
	  g_free (pre);
	  g_free (end);
	  return total;
	}
      break;

    case '{':
      p2 = corresponding_bracket (text, p1);
      if ((p2 == 0) || (p2 <= p1 + 1))
	{
	  g_free (pre);
	  return NULL;
	}
      in = richtext_to_pango (text, p1 + 1, p2);
      if (in == NULL)
	{
	  g_free (pre);
	  return NULL;
	}
      end = richtext_to_pango (text, p2 + 1, p2i);
      if (end == NULL)
	{
	  g_free (pre);
	  g_free (in);
	  return NULL;
	}
      total = g_strconcat (pre, in, end, NULL);
      g_free (pre);
      g_free (in);
      g_free (end);
      return total;
      break;
    }

  return NULL;

#undef IS_CONTROL
}


int
parts_of_group_text (struct Bond *bond, gchar ** left, gchar ** right)
{
  const unsigned int max = strlen (bond->text);
  unsigned int parent, parent2, i, j, pos, k, flag;
  int pos_begin;
  gchar *str, *str2, *str3;

  if (bond->type == BOND_ATOM)
    {
      if (left != NULL)
	*left = NULL;
      if (right != NULL)
	*right = NULL;
      return 1;
    }

/* flag is 1 when we are processing a \macroname, 2 for \{ or \} */
  if (bond->type == BOND_GROUP_L)
    {
      pos_begin = -1;
      parent = 0;
      flag = 0;
      for (pos = 0; pos < max; pos++)
	{
	  if ((flag == 1) && (!IS_ALPHA ((bond->text)[pos])))
	    flag = 0;
	  if ((bond->text)[pos] == '\\')
	    {
	      pos_begin = pos;
	      flag = 1;
	      continue;
	    }
	  if ((bond->text)[pos] == '{')
	    {
	      parent++;
	      pos_begin = -1;
	      flag = 0;
	      continue;
	    }
	  if ((bond->text)[pos] == '}')
	    {
	      parent--;
	      flag = 0;
	      continue;
	    }
	  if (((bond->text)[pos] == '_') || ((bond->text)[pos] == '^'))
	    continue;
	  if (flag == 1)
	    continue;
	  if ((pos_begin != -1) && ((bond->text)[pos] != ' '))
	    pos--;

/* Here, we found the first real caracter of the text string */
	  str = g_new (gchar, pos + parent + 2);
	  for (i = 0; i <= pos; i++)
	    str[i] = (bond->text)[i];
	  for (i = 0; i < parent; i++)
	    str[pos + 1 + i] = '}';
	  str[pos + parent + 1] = 0;
	  if (left != NULL)
	    *left = g_strdup (str);
	  if (pos_begin == -1)
	    str[pos] = 0;
	  else
	    str[pos_begin] = 0;
	  str2 = g_strdup_printf ("%s%s", str, (bond->text) + pos + 1);
	  if (right != NULL)
	    *right = g_strdup (str2);
	  g_free (str);
	  g_free (str2);
	  break;
	}
      return 1;
    }

/* flag is 1 when processing a \macrowithargument{} */
  if (bond->type == BOND_GROUP_R)
    {
      parent = 0;
      flag = 0;
      for (pos = max - 1; pos < max; pos--)
	{
	  if (IS_ALPHA ((bond->text)[pos]))
	    {
	      i = pos;
	      while (i != 0 && IS_ALPHA ((bond->text)[i]))
		i--;
	      if ((bond->text)[i] == '\\')
		pos = i;
	      if (flag == 1)
		continue;
	    }
	  if (flag == 1)
	    if (((bond->text)[pos] == '^') || ((bond->text)[pos] == '_'))
	      {
		flag = 0;
		continue;
	      }
	  flag = 0;
	  if ((bond->text)[pos] == '}')
	    {
	      parent++;
	      continue;
	    }
	  if ((bond->text)[pos] == '{')
	    {
	      flag = 1;
	      parent--;
	      continue;
	    }
/* Take care of the x^2 and CO_2 constructions */
	  if (pos >= 1)
	    if (((bond->text)[pos - 1] == '_')
		|| ((bond->text)[pos - 1] == '^'))
	      pos--;

/* Here, we found the last real caracter of the text string and exit the
 * loop. */
	  break;
	}			/* for (pos) */

      str = g_new (gchar, pos + parent + 1);
      for (i = 0; i < pos; i++)
	str[i] = (bond->text)[i];
      for (i = 0; i < parent; i++)
	str[pos + i] = '}';
      str[pos + parent] = 0;
      if (left != NULL)
	*left = g_strdup (str);
      g_free (str);
      parent2 = 0;
      str = g_strdup (bond->text + pos);
      for (i = pos; i <= pos; i--)
	{
	  if (parent == 0)
	    break;
	  if ((bond->text)[i] == '}')
	    {
	      parent2++;
	      continue;
	    }
	  if (parent2 > 0)
	    {
	      if ((bond->text)[i] == '{')
		parent2--;
	      continue;
	    }
	  if ((bond->text)[i] != '{')
	    continue;

/* Here, we process the case where we found a opening bracket */
	  if (i == 0)
	    {
	      str2 = g_strdup_printf ("{%s", str);
	      g_free (str);
	      str = str2;
	      parent--;
	      break;
	    }
	  if (((bond->text)[i - 1] == '^') || ((bond->text)[i - 1] == '_'))
	    {
	      str2 = g_strdup_printf ("%c{%s", (bond->text)[i - 1], str);
	      g_free (str);
	      str = str2;
	      i--;
	      parent--;
	      continue;
	    }

/* We look up to see if there is a macro before the opening bracket */
	  j = i - 1;
	  while ((j != 0) && IS_ALPHA ((bond->text)[j]))
	    j--;
	  if ((bond->text)[j] == '\\')
	    {
/* It is possible to have "foo\\\\bar{gee}" where bar is not a macro!! */
	      k = j;
	      while ((k != 0) && ((bond->text)[k] == '\\'))
		k--;
	      if ((k != 0) && ((j - k) % 2 == 0))
		{
		  str2 = g_strdup_printf ("{%s", str);
		  g_free (str);
		  str = str2;
		  i = k + 1;
		  parent--;
		  continue;
		}

/* Here, we have a real \macro and we have to act accordingly */
	      str3 = (gchar *) calloc (i - j + 2, sizeof (gchar));
	      (void) strncpy (str3, bond->text + j, i - j + 1);
	      str3[i - j + 1] = 0;
	      str2 = g_strdup_printf ("%s%s", str3, str);
	      g_free (str3);
	      g_free (str);
	      str = str2;
	      i = j;
	      parent--;
	      continue;
	    }
	}
      if (right != NULL)
	*right = g_strdup (str);
      g_free (str);
      return 1;
    }
  return 0;
}


gchar *
centered_part_of_group (struct Bond * bond)
{
  gchar *str;

  switch (bond->type)
    {
    case BOND_ATOM:
      return g_strdup (bond->text);
      break;
      
    case BOND_GROUP_L:
      parts_of_group_text (bond, &str, NULL);
      return str;
      break;
      
    case BOND_GROUP_R:
      parts_of_group_text (bond, NULL, &str);
      return str;
      break;
    }
  bug_in ("centered_part_of_group");
}

