Logo Search packages:      
Sourcecode: easychem version File versions  Download package

bonds.c

/*
 * 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");
}


Generated by  Doxygen 1.6.0   Back to index