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

easychem.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.
 *
 *
 * <begin legalese>
 * 
 * EasyChem is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
 * USA.
 * 
 * <end legalese>
 *
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <math.h>
#include <errno.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <pango/pango.h>

#define _XOPEN_SOURCE
#include <signal.h>
#include <unistd.h>

#include "common.h"
#include "auxi.h"
#include "bonds.h"
#include "detect.h"
#include "dialogs.h"
#include "drawing.h"
#include "export.h"
#include "library.h"

/* Here are the compile-time settings and limits. */
#define ZOOM_MIN  0.3
#define ZOOM_MAX  5.0
#define ZOOM_FACT 1.5
#define ZOOM_DEFAULT    1.0

#define ANGLE_MIN 3.0
#define ANGLE_MAX 10.0
#define ANGLE_SM_INCR   1.0
#define ANGLE_LG_INCR   2.0
#define ANGLE_DEFAULT   6.0

#define BOND_MIN  0.2
#define BOND_MAX  3.0
#define BOND_SM_INCR    0.05
#define BOND_LG_INCR    0.5
#define BOND_DEFAULT    1.0

#define NUM_UNDO  20
#define LIMIT_SMALL     5
#define SENSIB          9
#define CTL_SENSIB      6
#define ANGLE_SENS      0.15
#define EXCLUSION 0
#define HELP_RADIUS     (BOND_LENGTH * bond_size)
#define HELP_CLOSE      500000
#define TIME_LIMIT      200
#define STR_LIMIT 100

#define REDRAW { draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, \
                 zoom); gtk_widget_queue_draw (drawing_area); }

#define IS_ZERO(x) ((x) < .001 && (x) > -.001)

#define MSG(S) { gtk_statusbar_pop (GTK_STATUSBAR (statusbar), 0); \
                 gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, S); }
#define L_MSG(S) { if (learn_msg) { MSG(S); } }

/* Color stuff */
GdkColor black = { 0, 0, 0, 0 };
GdkColor blue = { 0, 0, 0, FULL_COLOR };
GdkColor red = { 0, FULL_COLOR, 0, 0 };
GdkColor gray = { 0, FULL_COLOR / 2, FULL_COLOR / 2, FULL_COLOR / 2 };
GdkGC *gc_help, *gc_sel, *gc_gray;


/* Here are the default properties, take care not to mess with that! */
struct Properties prop = { GLOBAL_SIZE_H, GLOBAL_SIZE_V, TRUE, NULL, NULL };


/* Some variables need to be static */
static GtkWidget *statusbar;
static GdkPixmap *pixmap = NULL;
static GtkItemFactory *item_factory;
static GtkWidget *window;
static GtkWidget *spin_angle, *spin_bond;
static GtkWidget *drawing_area, *menu_bond;
static GtkWidget *radio_adjust, *radio_add, *radio_rotate, *radio_edit,
                 *radio_ornament;
static gchar *filename = NULL;
static gchar *last_dlg_path = NULL;
static struct Bond *undo_list[NUM_UNDO];
static gchar *undo_list_txt[NUM_UNDO];
static GtkWidget *menu_undo_widget, *menu_redo_widget;
static struct Bond *bonds = NULL;
static struct Bond *new_bond = NULL;
static struct Bond *old_bonds = NULL;
static struct Bond *copy_buffer = NULL;
static LLINT x_begin = 0;
static LLINT y_begin = 0;
static LLINT x_point = 0;
static LLINT y_point = 0;
static LLINT *points_x = NULL;
static LLINT *points_y = NULL;
static short int *points_sel = NULL;
static int attraction = TRUE, learn_msg = FALSE;
static int pos_undo = 0;
static gint x_b, y_b;
static int is_drawing = DRAW_NO;
static int angle = (int) ANGLE_DEFAULT;
static int old_angle = 0;
static int mode = MODE_ADD;
static int draw_ctl = 0;
static int num_sel = 0;
static int num_points = 0;
static double zoom = ZOOM_DEFAULT;
static double bond_size = BOND_DEFAULT;
static double angle_shift = 0.0;
static double theta_ref;
static long int t_begin = 0;
static unsigned int export_type = 0;
static unsigned int gs_status = PROGRAM_UNKNOWN,
                    pstoedit_status = PROGRAM_UNKNOWN;


#ifdef WIN32
void
log_ignore (const gchar *log_domain, GLogLevelFlags log_level,
            const gchar *message, gpointer user_data)
{
  return;
}
#endif


/* Change the zoom factor and redraw what is needed */
static void
zoom_effect (void)
{
  gtk_widget_set_size_request (GTK_WIDGET (drawing_area),
                         U2S (prop.global_width),
                         U2S (prop.global_height));
  REDRAW;
}


static void
save_last_dlg_path (const gchar *file)
{
  gchar *tmp;

  tmp = g_path_get_dirname (file);
  if (last_dlg_path != NULL)
    g_free (last_dlg_path);
  last_dlg_path = g_strconcat (tmp, "/", NULL);
  g_free (tmp);
}


static void
handle_signal (int signal)
{
  FILE *file;

  fprintf (stderr, _("Received signal %d.\nWhoops. Things are going a little crazy.\n"), signal);
  file = fopen (".save.ech", "w");
  if (file == NULL)
    {
      file = fopen ("/tmp/save.ech", "w");
      if (file == NULL)
      exit (0);
      fputs (_("Trying to save the current contents into '/tmp/save.ech'.\n"),
           stderr);
    }
  else
    fputs (_("Trying to save the current contents into a '.save.ech' file.\n"),
         stderr);
  save_list_bonds (bonds, file, 0, 0);
  exit (0);
}



/* The user switches the attraction on/off */
static void
menu_attract (G_GNUC_UNUSED gpointer callback_data,
            G_GNUC_UNUSED guint callback_action,
            GtkWidget * menu_item)
{
  attraction = GTK_CHECK_MENU_ITEM (menu_item)->active;
}

/* The user can select whether he wants the learning messages. */
static void
menu_learn_msg (G_GNUC_UNUSED gpointer callback_data,
              G_GNUC_UNUSED guint callback_action,
            GtkWidget * menu_item)
{
  learn_msg = GTK_CHECK_MENU_ITEM (menu_item)->active;
}


/* This is used to raise the preferences dialog */
static void
menu_pref (void)
{
  prop_dialog ();
  zoom_effect ();
}


/* 'About' window */
static void
menu_about (void)
{
  GtkWidget *label, *dialog;

  dialog = gtk_dialog_new_with_buttons
    (_("About EasyChem"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR, _("OK"), 0, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);

  label = gtk_label_new ("EasyChem " VERSION);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  label = gtk_label_new ("Copyright (C) 2003-2004 Fran├žois-Xavier Coudert");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  label = gtk_label_new
    (_("This program is free software. See COPYING file for details."));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 20);
  label = gtk_label_new (_("Bug-reports, ideas and suggestions welcome!"));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 0);
  label = gtk_label_new (_("Please send a mail to:"));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 0);
  label = gtk_label_new ("<Francois-Xavier.Coudert@ens.fr>");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 5);

  label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show_all (dialog);

  (void) gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);
}


static void
menu_undo_update (void)
{
  GtkWidget * label;
 
  label = gtk_bin_get_child (GTK_BIN (menu_undo_widget));
  if (pos_undo >= NUM_UNDO - 1)
  {
    gtk_label_set_text_with_mnemonic (GTK_LABEL (label), "_Undo");
    gtk_widget_set_sensitive (menu_undo_widget, FALSE);
  }
  else
  {
    if (undo_list[pos_undo + 1] == NULL)
    {
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label), "_Undo");
      gtk_widget_set_sensitive (menu_undo_widget, FALSE);
    }
    else
    {
      if (undo_list_txt[pos_undo] != NULL)
        gtk_label_set_text_with_mnemonic (GTK_LABEL (label),
        g_strconcat ("_Undo ", undo_list_txt[pos_undo], NULL));
      else
        gtk_label_set_text_with_mnemonic (GTK_LABEL (label), "_Undo");
      gtk_widget_set_sensitive (menu_undo_widget, TRUE);
    }
  }

  label = gtk_bin_get_child (GTK_BIN (menu_redo_widget));
  if (pos_undo == 0)
  {
    gtk_label_set_text_with_mnemonic (GTK_LABEL (label), "_Redo");
    gtk_widget_set_sensitive (menu_redo_widget, FALSE);
  }
  else
  {
    if (undo_list[pos_undo - 1] == NULL)
      bug_in ("menu_undo_update");

    if (undo_list_txt[pos_undo - 1] != NULL)
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label),
      g_strconcat ("_Redo ", undo_list_txt[pos_undo - 1], NULL));
    else
      gtk_label_set_text_with_mnemonic (GTK_LABEL (label), "_Redo");

    gtk_widget_set_sensitive (menu_redo_widget, TRUE);
  }
}

static void
advance_undo (const gchar *txt)
{
  int i;

  if (bond_list_are_similar (undo_list[pos_undo], bonds))
    return;

  if (bonds == NULL)
    return;

  if (pos_undo == 0)
    {
      for (i = NUM_UNDO - 1; i >= 0; i--)
      {
        if (i == NUM_UNDO - 1)
          {
            if (undo_list[i] != NULL)
            free_bond_list (undo_list[i]);
            if (undo_list_txt[i] != NULL)
            g_free (undo_list_txt[i]);
          }
        else
        {
          undo_list[i + 1] = undo_list[i];
          undo_list_txt[i + 1] = undo_list_txt[i];
        }
      }
    }
  else
    {
      for (i = 0; i < pos_undo; i++)
      {
      if (undo_list[i] != NULL)
        free_bond_list (undo_list[i]);
      if (undo_list_txt[i] != NULL)
        g_free (undo_list_txt[i]);
      }

      for (i = pos_undo; i < NUM_UNDO; i++)
      {
      undo_list[i - pos_undo + 1] = undo_list[i];
      undo_list_txt[i - pos_undo + 1] = undo_list_txt[i];
      }

      for (i = NUM_UNDO - pos_undo + 1; i < NUM_UNDO; i++)
      {
      undo_list[i] = NULL;
      undo_list_txt[i] = NULL;
      }
    }

  pos_undo = 0;
  undo_list[0] = copy_bond_list (bonds);
  if (txt != NULL)
    undo_list_txt[0] = g_strdup (txt);
  else
    undo_list_txt[0] = NULL;

  /* TODO --change the text for Undo and Redo menu items */
  menu_undo_update ();
  gtk_widget_set_sensitive (menu_redo_widget, FALSE);
}


static void
init_everything (void)
{
  int i;

  free_bond_list (old_bonds);
  old_bonds = NULL;
  free_bond_list (bonds);
  bonds = NULL;
  free_bond_list (new_bond);
  new_bond = NULL;

  attraction = TRUE;
  learn_msg = FALSE;

  pos_undo = 0;
  for (i = 0; i < NUM_UNDO; i++)
  {
    if (undo_list[i] != NULL)
      {
      free_bond_list (undo_list[i]);
      undo_list[i] = NULL;
      }
    if (undo_list_txt[i] != NULL)
    {
      g_free (undo_list_txt[i]);
      undo_list_txt[i] = NULL;
    }
  }
  menu_undo_update ();
  gtk_check_menu_item_set_active
    (GTK_CHECK_MENU_ITEM (gtk_item_factory_get_widget
                    (item_factory, _("/Options/Attraction"))), TRUE);
  gtk_widget_set_sensitive (radio_adjust, TRUE);
  gtk_widget_set_sensitive (radio_edit, TRUE);
  gtk_widget_set_sensitive (radio_add, TRUE);
  gtk_widget_set_sensitive (radio_rotate, TRUE);
  gtk_widget_set_sensitive (radio_ornament, TRUE);

  x_begin = 0;
  y_begin = 0;
  x_point = 0;
  y_point = 0;
  x_b = 0;
  y_b = 0;
  theta_ref = 0.0;
  t_begin = 0;

  g_free (points_x);
  points_x = NULL;
  g_free (points_y);
  points_y = NULL;
  g_free (points_sel);
  points_sel = NULL;
  num_points = 0;

  num_sel = 0;
  draw_ctl = 0;
  is_drawing = DRAW_NO;
  angle = (int) ANGLE_DEFAULT;
  old_angle = 0;
  mode = MODE_ADD;
  zoom = ZOOM_DEFAULT;
  bond_size = BOND_DEFAULT;
  angle_shift = 0.0;
  g_free (filename);
  filename = NULL;

  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_angle), ANGLE_DEFAULT);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin_bond), BOND_DEFAULT);
  gtk_option_menu_set_history (GTK_OPTION_MENU (menu_bond), 1);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_add), TRUE);
  gtk_window_set_title (GTK_WINDOW (window), "EasyChem");

  zoom_effect ();
}


static void
menu_save_as_ok (G_GNUC_UNUSED GtkWidget * widget, GtkFileSelection * filew)
{
  GtkWidget *dialog, *label;
  const gchar *name;
  gchar *basename;
  gint response;
  FILE *file;

  name = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
  if (g_file_test (name, G_FILE_TEST_EXISTS))
    {
      dialog = gtk_dialog_new_with_buttons
      (_("This file already exists!"), GTK_WINDOW (filew),
       GTK_DIALOG_MODAL, _("No"), 1, _("Yes"), 2, NULL);
      label = gtk_label_new (_("Overwrite the existing file?"));
      gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                    label, TRUE, TRUE, 10);
      gtk_widget_show (label);
      response = gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      if (response != 2)
      return;
    }


  file = fopen (name, "w");
  if (file == NULL)
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Cannot open file '%s'"),
       name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }

  g_free (filename);
  filename = g_strdup (name);
  save_list_bonds (bonds, file, 0, 0);
  fclose (file);

  save_last_dlg_path (name);
  basename = g_path_get_basename (name);
  gtk_window_set_title (GTK_WINDOW (window),
                  g_strdup_printf ("EasyChem [%s]", basename));
  g_free (basename);

  gtk_object_destroy (GTK_OBJECT (filew));
}


static void
menu_save_as (void)
{
  GtkWidget *filew;

  filew = gtk_file_selection_new (_("Save file in EasyChem format"));
  if (last_dlg_path != NULL)
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew),
                                   last_dlg_path);
  gtk_window_set_modal (GTK_WINDOW (filew), TRUE);
  gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew), ".ech");
  g_signal_connect_swapped
    (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
     "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (filew));
  gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (filew));
  gtk_file_selection_set_select_multiple (GTK_FILE_SELECTION (filew), FALSE);
  g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                "clicked", G_CALLBACK (menu_save_as_ok),
                (gpointer) filew);
  gtk_widget_show (filew);
}


static void
menu_save (void)
{
  GtkWidget *dialog;
  FILE *file;

  if (filename == NULL)
    menu_save_as ();
  else
    {
      file = fopen (filename, "w");
      if (file == NULL)
      {
        dialog = gtk_message_dialog_new
          (GTK_WINDOW (window), GTK_DIALOG_DESTROY_WITH_PARENT,
           GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
           _("Cannot open file '%s'"), filename);
        gtk_dialog_run (GTK_DIALOG (dialog));
        gtk_widget_destroy (dialog);
        return;
      }

      save_list_bonds (bonds, file, 0, 0);
      fclose (file);
    }
}


static void
menu_export_ok (G_GNUC_UNUSED GtkWidget * widget, GtkFileSelection * filew)
{
  GtkWidget *dialog, *label;
  gchar *name;
  gint response;

  name =
    (gchar *) gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
  if (g_file_test (name, G_FILE_TEST_EXISTS))
    {
      dialog = gtk_dialog_new_with_buttons
      (_("This file already exists!"), GTK_WINDOW (filew),
       GTK_DIALOG_MODAL, _("No"), 1, _("Yes"), 2, NULL);
      label = gtk_label_new (_("Overwrite the existing file?"));
      gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
      gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                    label, TRUE, TRUE, 10);
      gtk_widget_show (label);
      response = gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      if (response != 2)
      return;
    }

  gdk_flush ();
  save_last_dlg_path (name);

  if (export_type == EXPORT_EPS)
    eps_export (bonds, name, GTK_WINDOW (filew), 1);
  if (export_type == EXPORT_EPS_SIZE)
    eps_size_export (bonds, name, GTK_WINDOW (filew));
  if (export_type == EXPORT_EPS_NOBBOX)
    eps_export (bonds, name, GTK_WINDOW (filew), 0);
  if (export_type == EXPORT_PDF)
    pdf_export (bonds, name, GTK_WINDOW (filew));
  if (export_type == EXPORT_FIG)
    fig_export (bonds, name, GTK_WINDOW (filew));
  if (export_type == EXPORT_FIG_PRECISE)
    fig_pstoedit_export (bonds, name, GTK_WINDOW (filew));

  gtk_object_destroy (GTK_OBJECT (filew));
}


static void
menu_export_file ()
{
  const char suffix[EXPORT_DELIMITER][5] = { ".eps", ".eps", ".eps", ".pdf",
    ".fig", ".fig" };
  int len;
  gchar *base, *base2;
  GtkWidget *filew;

  base2 = g_strdup (filename);
  if (base2 != NULL)
    {
      len = strlen (base2);
      if (strncmp (base2 + (len - 4), ".ech", 4) == 0)
      base2[len - 4] = '\0';
      base = g_strconcat (base2, suffix[export_type], NULL);
      g_free (base2);
    }
  else
    base = g_strdup (suffix[export_type]);

  filew = gtk_file_selection_new (_("Export file"));
  if (last_dlg_path != NULL)
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew),
                                   last_dlg_path);
  gtk_window_set_modal (GTK_WINDOW (filew), TRUE);
  gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew), base);
  g_signal_connect_swapped
    (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
     "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (filew));
  gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (filew));
  gtk_file_selection_set_select_multiple (GTK_FILE_SELECTION (filew), FALSE);
  g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                "clicked", G_CALLBACK (menu_export_ok), (gpointer) filew);
  gtk_widget_show (filew);
}


static void
menu_export (void)
{
  int response;
  unsigned int i;
  GtkWidget *dialog, *label, *radio[EXPORT_DELIMITER], *latex;

  if (gs_status == PROGRAM_UNKNOWN)
    gs_status = detect_gs ();

  if (pstoedit_status == PROGRAM_UNKNOWN)
    pstoedit_status = detect_pstoedit ();

  dialog = gtk_dialog_new_with_buttons (_("Choice for exporting format"),
                                      GTK_WINDOW (window),
                              GTK_DIALOG_MODAL |
                              GTK_DIALOG_NO_SEPARATOR, _("OK"), 1,
                              _("Cancel"), 0, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);
  label = gtk_label_new (_("What kind of file do you want to produce?"));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);

/* The format that will be used */
  radio[0] = gtk_radio_button_new_with_label
      (NULL, _("Encapsulated postscript (.eps)"));
  radio[1] = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (radio[0]), _("EPS, optimized for size (.eps)"));
  radio[2] = gtk_radio_button_new_with_label_from_widget
      (GTK_RADIO_BUTTON (radio[0]),
       _("Encapsulated postscript with poor bounding box (.eps)"));
  radio[3] = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (radio[0]), _("Portable document file (.pdf)"));
  radio[4] = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (radio[0]), _("Xfig document (.fig)"));
  radio[5] = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (radio[0]),
     _("Enhanced xfig document (.fig) using pstoedit"));
  
  if (gs_status == PROGRAM_NOT_FOUND)
  {
    gtk_widget_set_sensitive (radio[0], FALSE);
    gtk_widget_set_sensitive (radio[1], FALSE);
    gtk_widget_set_sensitive (radio[3], FALSE);
  }
  if (pstoedit_status == PROGRAM_NOT_FOUND)
    gtk_widget_set_sensitive (radio[5], FALSE);
  
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio[2]), TRUE);
  if (GTK_WIDGET_SENSITIVE (radio[0]))
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio[0]), TRUE);

  for (i = 0; i < EXPORT_DELIMITER; i++)
    gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  radio[i], TRUE, TRUE, 3);

/* Options */
  latex = gtk_check_button_new_with_label (_("Use LaTeX fonts"));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), latex,
                      TRUE, TRUE, 20);
  if (prop.latex_export)
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (latex), TRUE);
  else
    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (latex), FALSE);
  
  gtk_widget_show_all (dialog);

  response = gtk_dialog_run (GTK_DIALOG (dialog));
  if (response == 0)
    {
      gtk_widget_destroy (dialog);
      return;
    }
  export_type = 0;
  for (i = 0; i < EXPORT_DELIMITER; i++)
    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio[i])))
      export_type = i;
  prop.latex_export = gtk_toggle_button_get_active
                        (GTK_TOGGLE_BUTTON (latex));
  gtk_widget_destroy (dialog);
  menu_export_file ();
}


static void
menu_group (void)
{
  if (is_drawing)
    return;

  if (num_sel <= 1)
    return;

  group_selection (&bonds, &num_sel);
  if (old_bonds != NULL)
    free_bond_list (old_bonds);
  old_bonds = NULL;
  advance_undo ("grouping");
  return;
}

static void
menu_ungroup (void)
{
  if (is_drawing)
    return;

  if (num_sel == 0)
    return;

  ungroup_selection (&bonds, &num_sel);
  if (old_bonds != NULL)
    free_bond_list (old_bonds);
  old_bonds = NULL;
  advance_undo ("ungrouping");
  return;
}


static void
menu_align (void)
{
  GtkWidget *dialog, *label, *hbox, *hn, *hl, *hc, *hr, *vn, *vt, *vc, *vb;
  gint response;
  int vertical, horizontal;

  if (is_drawing)
    return;

  if (num_sel <= 1)
    return;

  dialog = gtk_dialog_new_with_buttons
    (_("Align objects"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL, _("OK"), 2, _("Cancel"), 1, NULL);
  label = gtk_label_new (_("Horizontal alignment:"));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);

  hbox = gtk_hbox_new (FALSE, 0);
  hn = gtk_radio_button_new_with_label (NULL, _("No alignment"));
  hl = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (hn), _("Left"));
  hc = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (hn), _("Center"));
  hr = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (hn), _("Right"));
  gtk_box_pack_start (GTK_BOX (hbox), hn, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), hl, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), hc, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), hr, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  hbox, TRUE, TRUE, 2);

  label = gtk_label_new (_("Vertical alignment:"));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);

  hbox = gtk_hbox_new (FALSE, 0);
  vn = gtk_radio_button_new_with_label (NULL, _("No alignment"));
  vt = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (vn), _("Top"));
  vc = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (vn), _("Center"));
  vb = gtk_radio_button_new_with_label_from_widget
    (GTK_RADIO_BUTTON (vn), _("Bottom"));
  gtk_box_pack_start (GTK_BOX (hbox), vn, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), vt, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), vc, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (hbox), vb, TRUE, TRUE, 2);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  hbox, TRUE, TRUE, 2);

  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (hn), TRUE);
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (vn), TRUE);

  gtk_widget_show_all (dialog);
  response = gtk_dialog_run (GTK_DIALOG (dialog));

  if (response == 2)
    {
      horizontal = 0;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hl)))
      horizontal = 1;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hc)))
      horizontal = 2;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (hr)))
      horizontal = 3;

      vertical = 0;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vt)))
      vertical = 1;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vc)))
      vertical = 2;
      if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (vb)))
      vertical = 3;

      gtk_widget_destroy (dialog);

      if (horizontal == 0 && vertical == 0)
      return;

      align_selection (bonds, horizontal, vertical);
      advance_undo ("aligning");
      REDRAW;
    }

  if (response == 1)
    {
      gtk_widget_destroy (dialog);
      return;
    }
}

#ifdef DEBUG
static void
menu_debug (void)
{
  printf ("This is the content of 'bonds'\n");
  save_list_bonds (bonds, stdout, 1, 0);
  printf ("This is the content of 'old_bonds'\n");
  save_list_bonds (old_bonds, stdout, 1, 0);
}
#endif


/* The predefined angles attract the mouse cursor */
static int
mouse_attract_help (LLINT * x, LLINT * y)
{
  LLINT x_test, y_test;
  int i;
  int flag = 0;

  if (!attraction)
    return 0;

  for (i = 0; i < 2 * angle; i++)
    {
      x_test = x_begin + HELP_RADIUS * cos (angle_shift + (i * G_PI) / angle);
      y_test = y_begin + HELP_RADIUS * sin (angle_shift + (i * G_PI) / angle);
      if (SQR (*x - x_test) + SQR (*y - y_test) < HELP_CLOSE)
      {
        flag = 1;
        *x = x_test;
        *y = y_test;
      }
    }

  return flag;
}


/* To force the mouse to go in one of the predefined directions */
static void
mouse_force_angle (LLINT * x, LLINT * y)
{
  double theta, r;

  theta = ATAN2 ((double) *y - y_begin, (double) *x - x_begin);
  while (theta < 0)
    theta += 2 * G_PI;
  theta = INT (angle * theta / G_PI) * G_PI / angle;
  r = hypot ((double) *x - x_begin, (double) *y - y_begin);
  *x = x_begin + INT (r * cos (theta));
  *y = y_begin + INT (r * sin (theta));
}


/* The rotation can be done at special angles */
static int
mouse_attract_angle (double *theta)
{
  double closest;

  if (!attraction)
    return 0;

  while (*theta < 0)
    *theta += 2 * G_PI;
  closest = G_PI / angle * INT (*theta * angle / G_PI);

  if (closest - *theta < ANGLE_SENS && closest - *theta > -ANGLE_SENS)
    {
      *theta = closest;
      return 1;
    }

  return 0;
}


/* Compute the attraction */
static int
mouse_attract_move (LLINT * x, LLINT * y)
{
  int i, j;
  LLINT best, cx, cy, x_best, y_best;

  if (bonds == NULL)
    return 0;

  x_best = 0;
  y_best = 0;
  best = BIG;

  i = 0;
  while (i < num_points)
    {
      j = 0;
      while (j < num_points)
      {
        cx = points_x[i] + *x - x_begin;
        cy = points_y[i] + *y - y_begin;

        if (points_sel[i])
          if (SQR (cx - points_x[j]) + SQR (cy - points_y[j]) < best)
            {
            x_best = points_x[j] - cx;
            y_best = points_y[j] - cy;
            best = SQR (cx - points_x[j]) + SQR (cy - points_y[j]);
            }

        j++;
      }

      i++;
    }

  *x = *x + x_best;
  *y = *y + y_best;

  if (best == BIG)
    return 0;
  else
    return 1;
}


static int
mouse_attract_move_atom (LLINT * x, LLINT * y, LLINT x_at, LLINT y_at)
{
  LLINT best, cx, cy, x_best, y_best;
  int i;

  if ((!attraction) || bonds == NULL)
    return 0;

  x_best = 0;
  y_best = 0;
  best = BIG;

  cx = x_at + *x - x_begin;
  cy = y_at + *y - y_begin;

  i = 0;
  while (i < num_points)
    {
      if (SQR (cx - points_x[i]) + SQR (cy - points_y[i]) < best)
      {
        x_best = points_x[i] - cx;
        y_best = points_y[i] - cy;
        best = SQR (cx - points_x[i]) + SQR (cy - points_y[i]);
      }

      i++;
    }

  *x = *x + x_best;
  *y = *y + y_best;

  if (best == BIG)
    return 0;
  else
    return 1;
}


/* Already existing points attract the mouse cursor too */
static int
mouse_attract_point (LLINT * x, LLINT * y, struct Bond *list)
{
  struct Bond *current;
  LLINT x_best, y_best, best, x_group, y_group;
  int flag = 0;

  if ((!attraction) || bonds == NULL)
    return 0;

  current = list;
  best = BIG;
  x_best = *x;
  y_best = *y;

  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_ARC:

        if (SQR (*x - current->x1) + SQR (*y - current->y1) < best)
          {
            x_best = current->x1;
            y_best = current->y1;
            best = SQR (*x - current->x1) + SQR (*y - current->y1);
            flag = 1;
          }

        if (SQR (*x - current->x2) + SQR (*y - current->y2) < best)
          {
            x_best = current->x2;
            y_best = current->y2;
            best = SQR (*x - current->x2) + SQR (*y - current->y2);
            flag = 1;
          }
        break;

      case BOND_CIRCLE:
        break;

      case BOND_ATOM:
      case BOND_GROUP_L:
      case BOND_GROUP_R:
        if (SQR (*x - current->x3) + SQR (*y - current->y3) < best)
          {
            x_best = current->x3;
            y_best = current->y3;
            best = SQR (*x - current->x3) + SQR (*y - current->y3);
            flag = 1;
          }
        break;

      case BOND_GROUP:
        x_group = *x;
        y_group = *y;
        if (mouse_attract_point
            (&x_group, &y_group, current->group_members))
          {
            if (SQR (*x - x_group) + SQR (*y - y_group) < best)
            {
              x_best = x_group;
              y_best = y_group;
              best = SQR (*x - x_group) + SQR (*y - y_group);
              flag = 1;
            }
          }
        break;

      default:
        bug_in ("mouse_attract_point");
        break;
      }


      current = current->next;
    }

  *x = x_best;
  *y = y_best;
  return flag;
}


/* We draw the control points of a bond */
static void
draw_control_points (struct Bond *ref)
{
  if (ref->type < BOND_ATOM)
    {
      gdk_draw_rectangle (pixmap, gc_help, TRUE,
                    U2S (ref->x1) - 1, U2S (ref->y1) - 1, 3, 3);
      gdk_draw_rectangle (pixmap, gc_help, TRUE,
                    U2S (ref->x2) - 1, U2S (ref->y2) - 1, 3, 3);
    }

  if (ref->type == BOND_ARC)
    gdk_draw_rectangle (pixmap, gc_help, TRUE,
                  U2S (ref->x3) - 1, U2S (ref->y3) - 1, 3, 3);

  if (ref->type == BOND_GROUP)
    {
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x1) - 1, U2S (ref->y1) - 1,
                 U2S (ref->x1) - 1, U2S (ref->y1) + 5);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x1) - 1, U2S (ref->y1) - 1,
                 U2S (ref->x1) + 5, U2S (ref->y1) - 1);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x2) + 1, U2S (ref->y1) - 1,
                 U2S (ref->x2) + 1, U2S (ref->y1) + 5);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x2) + 1, U2S (ref->y1) - 1,
                 U2S (ref->x2) - 5, U2S (ref->y1) - 1);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x1) - 1, U2S (ref->y2) + 1,
                 U2S (ref->x1) - 1, U2S (ref->y2) - 5);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x1) - 1, U2S (ref->y2) + 1,
                 U2S (ref->x1) + 5, U2S (ref->y2) + 1);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x2) + 1, U2S (ref->y2) + 1,
                 U2S (ref->x2) + 1, U2S (ref->y2) - 5);
      gdk_draw_line (pixmap, gc_help,
                 U2S (ref->x2) + 1, U2S (ref->y2) + 1,
                 U2S (ref->x2) - 5, U2S (ref->y2) + 1);
    }
}


/* Draw the "angle help" */
static void
draw_help (void)
{
  int x, y, i;

  for (i = 0; i < 2 * angle; i++)
    {
      x = x_begin + HELP_RADIUS * cos (angle_shift + (i * G_PI) / angle);
      y = y_begin + HELP_RADIUS * sin (angle_shift + (i * G_PI) / angle);
      gdk_draw_rectangle (pixmap, gc_help, TRUE,
                    U2S (x) - 1, U2S (y) - 1, 3, 3);
    }
}


/* Draw the center of the future polygon. */
static void
draw_brush_polygon (LLINT x, LLINT y, GtkWidget * widget)
{
  LLINT cx, cy, x_old, y_old;
  int i;
  double r, theta;

  draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);

  cx = 0.5 * (x + x_begin) - 0.5 * (y - y_begin) / tan (G_PI / angle);
  cy = 0.5 * (y + y_begin) + 0.5 * (x - x_begin) / tan (G_PI / angle);

  r = hypot ((double) x_begin - cx, (double) y_begin - cy);
  theta = atan2 ((double) cy - y_begin, (double) cx - x_begin);

  x_old = INT (cx - r * cos (theta));
  y_old = INT (cy - r * sin (theta));
  for (i = 1; i <= angle; i++)
    {
      x = INT (cx - r * cos (theta + (2 * i * G_PI) / angle));
      y = INT (cy - r * sin (theta + (2 * i * G_PI) / angle));
      gdk_draw_line (pixmap, widget->style->black_gc,
                 U2S (x), U2S (y), U2S (x_old), U2S (y_old));
      x_old = x;
      y_old = y;
    }

  draw_help ();
  gtk_widget_queue_draw (drawing_area);
}


/* Create a new backing pixmap of the appropriate size */
static gint
configure_event (GtkWidget * widget)
{
  if (pixmap)
    g_object_unref (pixmap);

  pixmap =
    gdk_pixmap_new (widget->window, U2S (prop.global_width),
                U2S (prop.global_height), -1);
  gdk_draw_rectangle (pixmap, widget->style->white_gc, TRUE, 0, 0,
                  U2S (prop.global_width), U2S (prop.global_height));

  draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);
  return TRUE;
}


/* Redraw the screen from the backing pixmap */
static gint
expose_event (GtkWidget * widget, GdkEventExpose * event)
{
  gdk_draw_pixmap (widget->window,
               widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
               pixmap,
               event->area.x, event->area.y,
               event->area.x, event->area.y,
               event->area.width, event->area.height);

  return FALSE;
}


/* Draw a line on the screen, and eras any previous one */
static void
draw_brush (GtkWidget * widget, const LLINT x, const LLINT y)
{
  draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);
  gdk_draw_line (pixmap, widget->style->black_gc, U2S (x_begin),
             U2S (y_begin), U2S (x), U2S (y));
  draw_help ();
  gtk_widget_queue_draw (drawing_area);
}


/* Draw a rectangle indicating the selection. */
static void
draw_selection_rectangle (GtkWidget * widget, const gint x, const gint y)
{
  draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);
  gdk_draw_line (pixmap, widget->style->black_gc, x_b, y_b, x_b, y);
  gdk_draw_line (pixmap, widget->style->black_gc, x_b, y_b, x, y_b);
  gdk_draw_line (pixmap, widget->style->black_gc, x_b, y, x, y);
  gdk_draw_line (pixmap, widget->style->black_gc, x, y_b, x, y);
  gtk_widget_queue_draw (drawing_area);
}


static void
cancel_action ()
{
  if (old_bonds != NULL)
    {
      free_bond_list (bonds);
      bonds = old_bonds;
      old_bonds = NULL;
    }

  if (is_drawing == DRAW_THIRD)
    delete_bond (&bonds, new_bond, &num_sel);

  L_MSG ("");
  g_free (points_x);
  points_x = NULL;
  g_free (points_y);
  points_y = NULL;
  g_free (points_sel);
  points_sel = NULL;
  num_points = 0;

  remove_duplicate_in_list (&bonds, &num_sel);
  is_drawing = DRAW_NO;

  REDRAW;
}


static gint
button_press_event (G_GNUC_UNUSED GtkWidget * widget, GdkEventButton * event)
{
  struct Bond *new;
  LLINT x_old, y_old;
  int type;
  unsigned int end;
  gchar *buff;
  GtkWidget *dialog;

  if (pixmap == NULL || event->type != GDK_BUTTON_PRESS)
    return TRUE;

  if (mode == MODE_ORNAMENT)
  {
    new = closest_end_of_bond (S2U (event->x), S2U (event->y), &end,
                             bonds, NULL, SQR (S2U (SENSIB)));
    if (new == NULL)
      return TRUE;
    
    edit_ornaments (bonds, new, end, GTK_WINDOW (window));
    REDRAW;
    return TRUE;
  } 

  if (is_drawing == DRAW_THIRD && event->button == 1)
    {
      x_old = S2U (event->x);
      y_old = S2U (event->y);
      mouse_attract_point (&x_old, &y_old, bonds);
      new_bond->x3 = x_old;
      new_bond->y3 = y_old;
      new_bond = NULL;
      free_bond_list (old_bonds);
      old_bonds = NULL;
      L_MSG ("");
      is_drawing = DRAW_NO;
      advance_undo ("drawing arc");
      REDRAW;
      return TRUE;
    }

  type = gtk_option_menu_get_history (GTK_OPTION_MENU (menu_bond));
  bond_size = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin_bond));

  x_begin = S2U (event->x);
  y_begin = S2U (event->y);
  if (x_begin > prop.global_width)
    x_begin = prop.global_width;
  if (y_begin > prop.global_height)
    y_begin = prop.global_height;

  t_begin = precise_time ();
  switch (mode)
    {
    case MODE_ADD:

      if ((!is_drawing) && (type == BOND_ATOM) && event->button == 1)
      {
        mouse_attract_point (&x_begin, &y_begin, bonds);
        clear_selection (bonds, &num_sel);
        old_bonds = copy_bond_list (bonds);
        new_bond = add_atom (x_begin, y_begin, &bonds, type);
        if (new_bond != NULL)
          {
            num_sel++;
            new_bond->selected = SEL_YES;
            new_bond->type = BOND_ATOM;
            new_bond->color = black;
            buff = edit_text (NULL, &(new_bond->type), &(new_bond->width),
                          &(new_bond->color), GTK_WINDOW (window));
            new_bond->text = buff;
            if ((new_bond->text == NULL) || (strlen (new_bond->text) == 0))
            {
              free_bond_list (bonds);
              bonds = old_bonds;
              old_bonds = NULL;
            }
            else
            {
              if (text_to_pango (new_bond) == 0)
                {
                  dialog = gtk_message_dialog_new
                  (GTK_WINDOW (window), GTK_DIALOG_DESTROY_WITH_PARENT,
                   GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
                   _("There seems to be a syntax problem in your text : '%s'. Until you change it, it will be represented by a '(null)' block of text."),
                   new_bond->text);
                  gtk_dialog_run (GTK_DIALOG (dialog));
                  gtk_widget_destroy (dialog);
                }
              free_bond_list (old_bonds);
              old_bonds = NULL;
              adjust_atom (new_bond, drawing_area, mode, zoom);
              advance_undo ("adding text");
            }

            is_drawing = DRAW_NO;
          }
        else
          free_bond_list (old_bonds);

        REDRAW;
        return TRUE;
      }

      switch (event->button)
      {
      case 1:
        if (is_drawing)
          return TRUE;

        mouse_attract_point (&x_begin, &y_begin, bonds);
        x_old = x_begin;
        y_old = y_begin;

        if (event->state & GDK_CONTROL_MASK)
          {
            is_drawing = DRAW_POLY;
            L_MSG
            (_("Control-clicking enables you to draw a n-membered ring (n is set by the 'ring size' value)."));
          }
        else
          {
            is_drawing = DRAW_NORMAL;
            L_MSG (_("Release the mouse button to draw the bond."));
          }
        if (type == BOND_ARC)
          is_drawing = DRAW_SECOND;

        angle =
          gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin_angle));
        if (angle != old_angle)
          angle_shift = 0.0;
        old_angle = angle;
        draw_help ();
        gtk_widget_queue_draw (drawing_area);
        break;

      case 3:
        if (is_drawing)
          {
            cancel_action ();
            L_MSG (_("You clicked with the right button. Drawing canceled."));
            return TRUE;
          }
        new = closest_bond (x_begin, y_begin, bonds,
                        (LLINT) SQR (S2U (SENSIB)));
        if (new != NULL)
          {
            if (event->state & GDK_CONTROL_MASK)
            {
              clear_selection_attribute (bonds, SEL_TEMP);
              delete_molecule (&bonds, new, &num_sel);
              L_MSG
                (_("You used the Control key and the right button to delete an entire molecule."));
            }
            else
            {
              delete_bond (&bonds, new, &num_sel);
              L_MSG (_("You used the right button to delete an object."));
            }
            advance_undo ("delete");
            REDRAW;
          }
        break;
      }
      break;

/****************/
    case MODE_EDIT:
      new = closest_bond (x_begin, y_begin, bonds,
                    (LLINT) SQR (S2U (SENSIB)));
      if (new != NULL)
      {
        if (event->button == 1 && ! BOND_HAS_TEXT (new))
          edit_bond (new, GTK_WINDOW (window));
        else
          {
            switch (new->type)
            {
            case BOND_ARROW:
              new->type = BOND_SIMPLE;
              break;
            case BOND_ARC:
              break;
            case BOND_CIRCLE:
              break;
            case BOND_ATOM:
            case BOND_GROUP_R:
            case BOND_GROUP_L:
              buff =
                edit_text (new->text, &(new->type), &(new->width),
                           &(new->color), GTK_WINDOW (window));
              g_free (new->text);
              new->text = buff;
              if (text_to_pango (new) == 0)
                {
                  dialog = gtk_message_dialog_new
                  (GTK_WINDOW (window), GTK_DIALOG_DESTROY_WITH_PARENT,
                   GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
                   _("There seems to be a syntax problem in your text : '%s'. Until you change it, it will be represented by a '(null)' block of text."),
                   new->text);
                  gtk_dialog_run (GTK_DIALOG (dialog));
                  gtk_widget_destroy (dialog);
                }
              break;
            case BOND_GROUP:
              break;
            default:
              (new->type)++;
              break;
            }
            if (BOND_HAS_TEXT (new))
            adjust_atom (new, drawing_area, mode, zoom);
          }

        advance_undo ("editing");
        REDRAW;
      }

      break;

/****************/
    case MODE_ADJUST:
      switch (event->button)
      {
      case 1:
        new = closest_bond (x_begin, y_begin, bonds,
                        (LLINT) SQR (S2U (SENSIB)));
        if (new != NULL)
          {
            if (event->state & GDK_SHIFT_MASK)
            {
              if (new->selected & SEL_YES)
                {
                  num_sel--;
                  new->selected &= SEL_NO;
                }
              else
                {
                  num_sel++;
                  new->selected |= SEL_YES;
                }
            }
            else if (!(new->selected & SEL_YES) && num_sel > 0)
            {
              clear_selection (bonds, &num_sel);
              new->selected = SEL_YES;
              num_sel = 1;
            }
            REDRAW;

            if (num_sel == 0)
            {
              x_point = x_begin;
              y_point = y_begin;
              new = closest_bond (x_begin, y_begin, bonds,
                              (LLINT) SQR (S2U (SENSIB)));
              if (new == NULL ||
                  (new->type < BOND_ATOM &&
                   closest_point (bonds, &x_point, &y_point,
                              (LLINT) SQR (S2U (CTL_SENSIB)))))
                {
                  new = NULL;
                  is_drawing = DRAW_MOVE_POINT;
                }
              else
                {
                  if (new == NULL)
                  return TRUE;
                  new->selected |= SEL_YES;
                  num_sel++;
                  REDRAW;
                  if (event->state & GDK_CONTROL_MASK)
                  {
                    is_drawing = DRAW_COPY;
                    duplicate_selected_in_list (bonds);
                  }
                  else
                  {
                    if (event->state & GDK_SHIFT_MASK)
                      is_drawing = DRAW_MOVE_SEP;
                    else
                      is_drawing = DRAW_MOVE;
                  }
                }
            }
            else        /* num_sel == 0 */
            {
              if (event->state & GDK_SHIFT_MASK)
                is_drawing = DRAW_MOVE_SEP;
              else
                {
                  if (event->state & GDK_CONTROL_MASK)
                  {
                    is_drawing = DRAW_COPY;
                    duplicate_selected_in_list (bonds);
                  }
                  else
                  is_drawing = DRAW_MOVE;
                }
            }

            free_bond_list (old_bonds);
            update_sel_close (bonds);
            old_bonds = copy_bond_list (bonds);
            if (attraction)
            {
              g_free (points_x);
              points_x = NULL;
              g_free (points_y);
              points_y = NULL;
              g_free (points_sel);
              points_sel = NULL;
            num_points = generate_points (bonds, &points_x, &points_y,
                                    &points_sel);
            }
            else
            num_points = 0;
            break;
          }
        else                  /* The mouse is not close to a bond */
          {
            if (event->state & GDK_CONTROL_MASK)
            return TRUE;
            if (event->state & GDK_SHIFT_MASK)
            is_drawing = DRAW_SEL_ADD;
            else
            is_drawing = DRAW_SEL;
            x_b = event->x;
            y_b = event->y;
          }
        break;

      case 3:
        cancel_action ();
        break;
      }
      break;

    case MODE_ROTATE:
      if (event->button == 1)
      {
        angle =
          gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin_angle));
        if (angle != old_angle)
          angle_shift = 0.0;
        old_angle = angle;

        new = closest_bond (x_begin, y_begin, bonds,
                        (LLINT) SQR (S2U (SENSIB)));
        if (new == NULL || !(new->selected & SEL_YES))
          {
            x_b = event->x;
            y_b = event->y;
            is_drawing = DRAW_SEL;
          }
        else
          {
            center_of_selection (bonds, &x_point, &y_point);
            theta_ref = ATAN2 (y_begin - y_point, x_begin - x_point);

            is_drawing = DRAW_ROTATE;
            free_bond_list (old_bonds);
            update_sel_close (bonds);
            old_bonds = copy_bond_list (bonds);
          }
      }

      break;
    }

  return TRUE;
}


static gint
button_release_event (GtkWidget * widget, GdkEventButton * event)
{
  struct Bond *new;
  LLINT x, y, x_old, y_old;
  double cx, cy, r, theta;
  int i, type;

  if (pixmap == NULL || !is_drawing)
    return TRUE;
  type = gtk_option_menu_get_history (GTK_OPTION_MENU (menu_bond));

  x = S2U (event->x);
  y = S2U (event->y);
  if (x > prop.global_width)
    x = prop.global_width;
  if (y > prop.global_height)
    y = prop.global_height;

  switch (event->button)
    {
    case 1:

      if (precise_time () - t_begin < TIME_LIMIT)
      {
        if (old_bonds != NULL)
          {
            free_bond_list (bonds);
            bonds = old_bonds;
            old_bonds = NULL;
          }

/* We select the molecule (the click was very short) */
        new = closest_bond (x, y, bonds, (LLINT) SQR (S2U (SENSIB)));

        if (new == NULL)
          {
            is_drawing = DRAW_NO;
            if (mode == MODE_ADJUST)
            clear_selection (bonds, &num_sel);
            L_MSG
            (_("You made a short click in a blank area. If you wanted to draw a bond, you need to key the mouse button pressed."));
            REDRAW;
            return TRUE;
          }
        if (event->state & GDK_SHIFT_MASK)
          {
            if (event->state & GDK_CONTROL_MASK)
            {
              clear_selection_attribute (bonds, SEL_TEMP);
              select_add_molecule (bonds, new, &num_sel);
            }
            else
            {           /* TODO */
            }
          }
        else
          {
            if (event->state & GDK_CONTROL_MASK)
            {
              clear_selection (bonds, &num_sel);
              select_molecule (bonds, new, &num_sel);
            }
            else
            {           /* TODO */
            }
          }
        REDRAW;
        is_drawing = DRAW_NO;
        return TRUE;
      }

      L_MSG ("");
      switch (is_drawing)
      {
      case DRAW_NORMAL:
      case DRAW_SECOND:
        clear_selection (bonds, &num_sel);
        if (!mouse_attract_point (&x, &y, bonds))
          mouse_attract_help (&x, &y);
        if (event->state & GDK_SHIFT_MASK)
          mouse_force_angle (&x, &y);

        new = NULL;
        if (x != x_begin || y != y_begin)
          {
            new = add_bond (x_begin, y_begin, x, y, type, &bonds, 1);
            if (is_drawing == DRAW_NORMAL)
            advance_undo ("drawing");
          }
        if (new != NULL)
          {
            new->selected = SEL_YES;
            num_sel++;
          }

        if (is_drawing == DRAW_SECOND && new != NULL)
          {
            new_bond = new;
            new->x3 = -1;
            is_drawing = DRAW_THIRD;
            L_MSG
            (_("To set the third point of the arc, you now need to click where you want it to be."));
            REDRAW;
            return TRUE;
          }
        break;

      case DRAW_POLY:
        clear_selection (bonds, &num_sel);
        if (x == x_begin && y == y_begin)
          break;
        if (!mouse_attract_point (&x, &y, bonds))
          mouse_attract_help (&x, &y);
        if (event->state & GDK_SHIFT_MASK)
          mouse_force_angle (&x, &y);

        cx = 0.5 * (x + x_begin) - 0.5 * (y - y_begin) / tan (G_PI / angle);
        cy = 0.5 * (y + y_begin) + 0.5 * (x - x_begin) / tan (G_PI / angle);

        r = hypot ((double) x_begin - cx, (double) y_begin - cy);
        theta = atan2 ((double) cy - y_begin, (double) cx - x_begin);

        x_old = INT (cx - r * cos (theta));
        y_old = INT (cy - r * sin (theta));
        for (i = 1; i <= angle; i++)
          {
            x = INT (cx - r * cos (theta + (2 * i * G_PI) / angle));
            y = INT (cy - r * sin (theta + (2 * i * G_PI) / angle));
            new = add_bond (x, y, x_old, y_old, BOND_SIMPLE, &bonds, 0);
            if (new != NULL)
            {
              new->selected = SEL_YES;
              num_sel++;
            }
            x_old = x;
            y_old = y;
          }
        remove_duplicate_in_list (&bonds, &num_sel);
        advance_undo ("drawing ring");
        break;

      case DRAW_SEL:
        select_in_rectangle ((LLINT) S2U (x_b), (LLINT) S2U (y_b),
                         x, y, bonds, &num_sel);
        break;

      case DRAW_SEL_ADD:
        change_in_rectangle ((LLINT) S2U (x_b), (LLINT) S2U (y_b), x, y,
                         bonds, &num_sel);
        break;

      case DRAW_COPY:
      case DRAW_MOVE:
      case DRAW_MOVE_POINT:
        if (old_bonds != NULL)
          {
            free_bond_list (old_bonds);
            old_bonds = NULL;
            g_free (points_x);
            points_x = NULL;
            g_free (points_y);
            points_y = NULL;
            g_free (points_sel);
            points_sel = NULL;
            num_points = 0;
          }
        remove_duplicate_in_list (&bonds, &num_sel);
        advance_undo (is_drawing == DRAW_COPY ? "copy" : "move");
        break;

      case DRAW_ROTATE:
        if (old_bonds != NULL)
          {
            free_bond_list (old_bonds);
            old_bonds = NULL;
          }
        remove_duplicate_in_list (&bonds, &num_sel);
        advance_undo ("rotation");
        break;
      }

      REDRAW;
      is_drawing = DRAW_NO;
      break;

    case 2:
      if (is_drawing != DRAW_NORMAL && is_drawing != DRAW_POLY)
      break;
      if (!mouse_attract_point (&x, &y, bonds))
      mouse_attract_help (&x, &y);
      if (event->state & GDK_CONTROL_MASK)
      {
        if (IS_ZERO (angle_shift))
          angle_shift = G_PI / 2;
        else
          angle_shift = 0.0;
      }
      else
      angle_shift = atan2 ((double) y - (double) y_begin, (double) x -
                       (double) x_begin);

      if (is_drawing == DRAW_POLY)
      draw_brush_polygon (x, y, widget);
      else
      draw_brush (widget, x, y);
      gtk_widget_queue_draw (drawing_area);
      break;

    case 3:
      cancel_action ();
      break;
    }

  return TRUE;
}


static gint
key_press_event (G_GNUC_UNUSED GtkWidget * widget, GdkEventKey * event)
{
  switch (event->keyval)
    {
    case GDK_Escape:
      if (!is_drawing)
      {
        clear_selection (bonds, &num_sel);
        REDRAW;
      }
      else
      {
        cancel_action ();
        L_MSG (_("Hitting the Escape key aborts the current operation."));
      }
      break;

    case GDK_BackSpace:
    case GDK_Delete:
      if (!is_drawing)
      {
        delete_selection (&bonds, &num_sel);
        L_MSG
          (_("You used the Backspace and Delete keys to delete the selection."));
        if (old_bonds != NULL)
          free_bond_list (old_bonds);
        old_bonds = NULL;
        advance_undo ("delete");
        REDRAW;
      }
      break;
    }
  return FALSE;
}


/* La souris bouge */
static gint
motion_notify_event (GtkWidget * widget, GdkEventMotion * event)
{
  struct Bond *current_new, *current_old, *new;
  double theta;
  int ix, iy;
  LLINT x, y;
  GdkModifierType state;

  if (event->is_hint)
    gdk_window_get_pointer (event->window, &ix, &iy, &state);
  else
    {
      ix = event->x;
      iy = event->y;
      state = event->state;
    }

  if (S2U (ix) > prop.global_width)
    ix = U2S (prop.global_width);
  if (S2U (iy) > prop.global_height)
    iy = U2S (prop.global_height);

  if (mode == MODE_ADJUST)
    {
      new = closest_bond ((LLINT) S2U (ix), (LLINT) S2U (iy), bonds,
                    (LLINT) SQR (S2U (SENSIB)));
      if (new != NULL && num_sel == 0)
      {
        draw_ctl = 1;
        draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);
        draw_control_points (new);
        gtk_widget_queue_draw (drawing_area);
      }
      else
      {
        if (draw_ctl)
          {
            draw_ctl = 0;
            REDRAW;
          }
      }
    }

  switch (is_drawing)
    {
    case DRAW_SEL:
    case DRAW_SEL_ADD:
      draw_selection_rectangle (widget, ix, iy);
      break;

    case DRAW_NORMAL:
    case DRAW_POLY:
    case DRAW_SECOND:
      x = S2U (ix);
      y = S2U (iy);
      if (!mouse_attract_point (&x, &y, bonds))
      mouse_attract_help (&x, &y);
      if (event->state & GDK_SHIFT_MASK)
      mouse_force_angle (&x, &y);

      if (pixmap != NULL)
      {
        if (is_drawing == DRAW_POLY)
          draw_brush_polygon (x, y, widget);
        else
          draw_brush (widget, x, y);
      }
      break;

    case DRAW_MOVE_POINT:
      x = S2U (ix);
      y = S2U (iy);
      mouse_attract_move_atom (&x, &y, x_point, y_point);
      if (event->state & GDK_SHIFT_MASK)
      mouse_force_angle (&x, &y);
      move_point_from_pos (bonds, old_bonds, x_point, y_point, x - x_begin,
                     y - y_begin);

      REDRAW;
      break;

    case DRAW_MOVE:
    case DRAW_MOVE_SEP:
    case DRAW_COPY:
      x = S2U (ix);
      y = S2U (iy);
      mouse_attract_move (&x, &y);
      if (event->state & GDK_SHIFT_MASK)
      mouse_force_angle (&x, &y);

      current_new = bonds;
      current_old = old_bonds;
      do
      {
        if (current_new->selected & SEL_YES)
          move_bond_from_pos (current_new, current_old,
                        x - x_begin, y - y_begin);

        if (is_drawing == DRAW_MOVE)
          move_point_close (current_new, current_old, x - x_begin,
                        y - y_begin);

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

      REDRAW;
      break;

    case DRAW_ROTATE:
      x = S2U (ix);
      y = S2U (iy);
      theta = theta_ref - ATAN2 (y - y_point, x - x_point);
      (void) mouse_attract_angle (&theta);
      rotate_angle (bonds, old_bonds, x_point, y_point,
                sin (theta), cos (theta), 0);

      REDRAW;
      break;
    }

  return TRUE;
}


static int
try_save (void)
{
  GtkWidget *dialog;
  FILE *file;

  if (filename != NULL)
    {
      file = fopen (filename, "w");
      if (file == NULL)
      {
        dialog = gtk_message_dialog_new
          (GTK_WINDOW (window), GTK_DIALOG_DESTROY_WITH_PARENT,
           GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE,
           _("Cannot open file '%s'"), filename);
        gtk_dialog_run (GTK_DIALOG (dialog));
        gtk_widget_destroy (dialog);
        return 0;
      }

      save_list_bonds (bonds, file, 0, 0);
      fclose (file);
      return 1;
    }
  else
    {
      menu_save_as ();
      return 0;
    }
}


/* This is the function registered by the atexit mechanism. It is the
 * last thing easychem does before exiting. */
static void
easychem_quit (void)
{
  write_config_file ();
}


/* This is the function that will be used to make sure that everything is
 * clean before exiting. After that, only easychem_quit is called. */
static void
menu_quit (void)
{
  GtkWidget *dialog, *label;
  gint response;

  if (filename != NULL && unmodified (bonds, filename))
    gtk_exit (0);
  if (filename == NULL && bonds == NULL)
    gtk_exit (0);

  dialog = gtk_dialog_new_with_buttons
    (_("Quit"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL, _("Save"), 2, _("Discard"), 1, NULL);
  label = gtk_label_new (_("Do you want to save your work first?"));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show (label);
  response = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);

  if (response <= 0)
    return;
  if (response == 1)
    gtk_exit (0);

  if (try_save ())
    gtk_exit (0);
}


static gint
try_destroy_window (void)
{
  menu_quit ();
  return TRUE;
}


static void
menu_new (void)
{
  GtkWidget *dialog, *label;
  gint response;

  if (filename != NULL && unmodified (bonds, filename))
    {
      init_everything ();
      return;
    }
  if (filename == NULL && bonds == NULL)
    {
      init_everything ();
      return;
    }

  dialog = gtk_dialog_new_with_buttons
    (_("New file"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL, _("Yes"), 2, _("No"), 1, NULL);
  label = gtk_label_new (_("Do you want to save your work first?"));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show (label);
  response = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);

  if (response == 1)
    {
      init_everything ();
      return;
    }

  if (response == 2 && try_save ())
    {
      init_everything ();
      return;
    }
}


static void
menu_open_ok (G_GNUC_UNUSED GtkWidget * widget, GtkFileSelection * filew)
{
  GtkWidget *dialog;
  const gchar *name;
  gchar *basename;
  FILE *file;

  name = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
  if (!g_file_test (name, G_FILE_TEST_IS_REGULAR))
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Cannot open file '%s'"),
       name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }

  file = fopen (name, "r");
  if (file == NULL)
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Cannot open file '%s'"),
       name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }

  init_everything ();
  bonds = load_list_bonds (file, 0, 0);
  adjust_all_atom_in_list (bonds, drawing_area, mode, zoom);

  if (bonds == NULL)
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR,
       GTK_BUTTONS_CLOSE, _("File '%s' has incorrect format"), name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }
  fclose (file);

  save_last_dlg_path (name);
  filename = g_strdup (name);
  basename = g_path_get_basename (name);
  gtk_window_set_title (GTK_WINDOW (window),
                  g_strdup_printf ("EasyChem [%s]", basename));
  g_free (basename);

  gtk_object_destroy (GTK_OBJECT (filew));
  draw_bond_list (bonds, 0, 0, drawing_area, pixmap, mode, zoom);
}


static void
open (void)
{
  GtkWidget *filew;

  filew = gtk_file_selection_new (_("Open file in EasyChem format"));
  if (last_dlg_path != NULL)
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew),
                                   last_dlg_path);
  gtk_window_set_modal (GTK_WINDOW (filew), TRUE);
  gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew), ".ech");
  g_signal_connect_swapped
    (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
     "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (filew));
  gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (filew));
  gtk_file_selection_set_select_multiple (GTK_FILE_SELECTION (filew), FALSE);
  g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                "clicked", G_CALLBACK (menu_open_ok), (gpointer) filew);
  gtk_widget_show (filew);
}


static void
menu_open (void)
{
  GtkWidget *dialog, *label;
  gint response;

  if (filename != NULL && unmodified (bonds, filename))
    {
      open ();
      return;
    }
  if (filename == NULL && bonds == NULL)
    {
      open ();
      return;
    }

/* Ask if the user doesn't want to save his work. */
  dialog = gtk_dialog_new_with_buttons
    (_("Open file"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL, _("Yes"), 2, _("No"), 1, NULL);
  label = gtk_label_new (_("Do you want to save your work first?"));
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 10);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show (label);
  response = gtk_dialog_run (GTK_DIALOG (dialog));
  gtk_widget_destroy (dialog);

  if (response == 1 || (response == 2 && try_save ()))
    {
      open ();
      return;
    }
}


void
menu_undo (void)
{
  if (is_drawing)
    {
      cancel_action ();
      return;
    }

  if (pos_undo >= NUM_UNDO - 1 || undo_list[pos_undo + 1] == NULL)
    bug_in ("menu_undo");

  pos_undo++;
  if (bonds != NULL)
    free_bond_list (bonds);
  bonds = copy_bond_list (undo_list[pos_undo]);

  menu_undo_update ();
  REDRAW;
  MSG (_("Yeah! Undone, boss!"));
}


void
menu_redo (void)
{
  if (is_drawing)
    {
      cancel_action ();
      return;
    }

  if (pos_undo == 0)
    bug_in ("menu_redo");

  pos_undo--;
  if (bonds != NULL)
    free_bond_list (bonds);
  bonds = copy_bond_list (undo_list[pos_undo]);

  menu_undo_update ();
  REDRAW;
}


void
menu_del (void)
{
  if (is_drawing)
    {
      cancel_action ();
      return;
    }

  if (num_sel == 0)
    return;

  delete_selection (&bonds, &num_sel);
  if (old_bonds != NULL)
    free_bond_list (old_bonds);
  old_bonds = NULL;
  REDRAW;
}


void
menu_copy (void)
{
  if (is_drawing)
    {
      cancel_action ();
      return;
    }

  if (num_sel == 0)
    return;

  if (copy_buffer != NULL)
    free_bond_list (copy_buffer);
  copy_buffer = copy_selection (bonds, 1000, -1000);
}


void
menu_cut (void)
{
  menu_copy ();
  menu_del ();
  advance_undo ("cutting");
}


void
menu_paste (void)
{
  struct Bond *tmp;
  
  if (is_drawing)
  {
    cancel_action ();
    return;
  }
  
  tmp = copy_selection (copy_buffer, 1000, -1000);
  if (bonds == NULL)
  {
    bonds = copy_buffer;
    num_sel = num_sel_recompute (bonds);
  }
  else
    concat_bond_lists (bonds, copy_buffer, &num_sel);

  copy_buffer = tmp;
  advance_undo ("paste");
  REDRAW;
}



static void
menu_flip_h (void)
{
  struct Bond *current;
  LLINT cx, cy;

  if (num_sel == 0 || bonds == NULL)
    return;

  current = bonds;
  center_of_selection (bonds, &cx, &cy);
  do
    {
      if (!(current->selected & SEL_YES))
      continue;

      if (current->type < BOND_ATOM)
      {
        current->x1 = 2 * cx - current->x1;
        current->x2 = 2 * cx - current->x2;
      }
      else
      {
        current->x3 = 2 * cx - current->x3;
        adjust_atom (current, drawing_area, mode, zoom);
      }

      if (current->type == BOND_ARC)
      current->x3 = 2 * cx - current->x3;

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

  advance_undo ("horizontal flip");
  REDRAW;
}


static void
menu_flip_v (void)
{
  struct Bond *current;
  LLINT cx, cy;

  if (num_sel == 0 || bonds == NULL)
    return;

  current = bonds;
  center_of_selection (bonds, &cx, &cy);
  do
    {
      if (!(current->selected & SEL_YES))
      continue;

      if (current->type < BOND_ATOM)
      {
        current->y1 = 2 * cy - current->y1;
        current->y2 = 2 * cy - current->y2;
      }
      else
      {
        current->y3 = 2 * cy - current->y3;
        adjust_atom (current, drawing_area, mode, zoom);
      }

      if (current->type == BOND_ARC)
      current->y3 = 2 * cy - current->y3;

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

  advance_undo ("vertical flip");
  REDRAW;
}


static void
menu_invert (void)
{
  struct Bond *current;
  LLINT cx, cy;

  if (num_sel == 0 || bonds == NULL)
    return;

  current = bonds;
  center_of_selection (bonds, &cx, &cy);
  do
    {
      if (!(current->selected & SEL_YES))
      continue;

      if (current->type < BOND_ATOM)
      {
        current->x1 = 2 * cx - current->x1;
        current->x2 = 2 * cx - current->x2;
        current->y1 = 2 * cy - current->y1;
        current->y2 = 2 * cy - current->y2;
      }
      else
      {
        current->x3 = 2 * cx - current->x3;
        current->y3 = 2 * cy - current->y3;
        adjust_atom (current, drawing_area, mode, zoom);
      }

      if (current->type == BOND_ARC)
      {
        current->x3 = 2 * cx - current->x3;
        current->y3 = 2 * cy - current->y3;
      }

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

  advance_undo ("inversion");
  REDRAW;
}


static void
menu_merge_ok (G_GNUC_UNUSED GtkWidget * widget, GtkFileSelection * filew)
{
  struct Bond *new_bonds, *current;
  GtkWidget *dialog;
  const gchar *name;
  FILE *file;

  name = gtk_file_selection_get_filename (GTK_FILE_SELECTION (filew));
  if (!g_file_test (name, G_FILE_TEST_IS_REGULAR))
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Cannot open file '%s'"),
       name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }

  file = fopen (name, "r");
  if (file == NULL)
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Cannot open file '%s'"),
       name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }
  save_last_dlg_path (name);

  new_bonds = load_list_bonds (file, 0, 1);
  if (new_bonds == NULL)
    {
      dialog = gtk_message_dialog_new
      (GTK_WINDOW (filew), GTK_DIALOG_DESTROY_WITH_PARENT,
       GTK_MESSAGE_ERROR,
       GTK_BUTTONS_CLOSE, _("File '%s' has incorrect format"), name);
      gtk_dialog_run (GTK_DIALOG (dialog));
      gtk_widget_destroy (dialog);
      return;
    }
  fclose (file);

  clear_selection (bonds, &num_sel);
  select_all (new_bonds, &num_sel);
  if (bonds == NULL)
    bonds = new_bonds;
  else
    {
      current = bonds;
      while (current->next != NULL)
      current = current->next;
      current->next = new_bonds;
    }

  adjust_all_atom_in_list (bonds, drawing_area, mode, zoom);
  mode = MODE_ADJUST;
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_adjust), TRUE);
  gtk_object_destroy (GTK_OBJECT (filew));
  advance_undo ("merge file");
  REDRAW;
}

static void
menu_merge (void)
{
  GtkWidget *filew;

  if (is_drawing)
    cancel_action ();

  filew = gtk_file_selection_new (_("Open file for merging"));
  if (last_dlg_path != NULL)
    gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew),
                                   last_dlg_path);
  gtk_window_set_modal (GTK_WINDOW (filew), TRUE);
  gtk_file_selection_set_filename (GTK_FILE_SELECTION (filew), ".ech");
  g_signal_connect_swapped
    (G_OBJECT (GTK_FILE_SELECTION (filew)->cancel_button),
     "clicked", G_CALLBACK (gtk_widget_destroy), G_OBJECT (filew));
  gtk_file_selection_hide_fileop_buttons (GTK_FILE_SELECTION (filew));
  gtk_file_selection_set_select_multiple (GTK_FILE_SELECTION (filew), FALSE);
  g_signal_connect (G_OBJECT (GTK_FILE_SELECTION (filew)->ok_button),
                "clicked", G_CALLBACK (menu_merge_ok), (gpointer) filew);
  gtk_widget_show (filew);
}


static void
menu_rotate (void)
{
  GtkWidget *label, *dialog, *box, *spin;
  LLINT cx, cy;
  gint response, angle;

  if (num_sel == 0 || bonds == NULL)
    return;

  dialog = gtk_dialog_new_with_buttons
    (_("Rotate the selection"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR, _("Cancel"), 1, _("OK"),
     2, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);

  box = gtk_hbox_new (FALSE, 0);
  label = gtk_label_new (_("Angle of the rotation:"));
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, FALSE, 0);
  spin = gtk_spin_button_new_with_range (-360.0, 360.0, 1.0);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), 90.0);
  gtk_box_pack_start (GTK_BOX (box), spin, TRUE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  box, TRUE, TRUE, 0);
  label = gtk_label_new (_("(positive is clockwise)"));
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show_all (dialog);

  response = gtk_dialog_run (GTK_DIALOG (dialog));
  angle = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (spin));

  if (response != 2 || angle == 0 || angle == 360 || angle == -360)
    {
      gtk_widget_destroy (dialog);
      return;
    }

  center_of_selection (bonds, &cx, &cy);
  rotate_angle (bonds, bonds, cx, cy, -sin ((G_PI / 180) * angle),
            cos ((G_PI / 180) * angle), 0);

  gtk_widget_destroy (dialog);

  advance_undo ("rotation");
  REDRAW;
}


static void
menu_cleanup (void)
{
  remove_duplicate_in_list (&bonds, &num_sel);
  remove_empty_groups (&bonds, &num_sel);
  advance_undo ("clean-up");
  REDRAW;
}


static void
menu_select_all (void)
{
  select_all (bonds, &num_sel);
  REDRAW;
}


static void
menu_zoom_in (void)
{
  if (zoom * ZOOM_FACT <= ZOOM_MAX)
    zoom *= ZOOM_FACT;
  else
    {
      if (zoom != ZOOM_MAX)
      zoom = ZOOM_MAX;
      else
      return;
    }

  zoom_effect ();
}


static void
menu_zoom_out (void)
{
  if (zoom / ZOOM_FACT >= ZOOM_MIN)
    zoom /= ZOOM_FACT;
  else
    {
      if (zoom != ZOOM_MIN)
      zoom = ZOOM_MIN;
      else
      return;
    }

  zoom_effect ();
}


static void
menu_zoom (void)
{
  GtkWidget *label, *dialog, *box, *spin;
  gint response;

  dialog = gtk_dialog_new_with_buttons
    (_("Set the zoom factor"), GTK_WINDOW (window),
     GTK_DIALOG_MODAL | GTK_DIALOG_NO_SEPARATOR, _("Cancel"), 1,
     _("OK"), 2, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (dialog), 20);

  box = gtk_hbox_new (FALSE, 0);
  label = gtk_label_new (_("New zoom factor:"));
  gtk_box_pack_start (GTK_BOX (box), label, TRUE, FALSE, 0);
  spin = gtk_spin_button_new_with_range (ZOOM_MIN, ZOOM_MAX, 0.1);
  gtk_spin_button_set_value (GTK_SPIN_BUTTON (spin), zoom);
  gtk_box_pack_start (GTK_BOX (box), spin, TRUE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  box, TRUE, TRUE, 0);
  label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox),
                  label, TRUE, TRUE, 10);
  gtk_widget_show_all (dialog);

  response = gtk_dialog_run (GTK_DIALOG (dialog));

  if (response != 2)
    {
      gtk_widget_destroy (dialog);
      return;
    }

  zoom = gtk_spin_button_get_value (GTK_SPIN_BUTTON (spin));
  gtk_widget_destroy (dialog);
  zoom_effect ();
}


static void
menu_zoom_default (void)
{
  if (zoom == ZOOM_DEFAULT)
    return;

  zoom = ZOOM_DEFAULT;
  zoom_effect ();
}



static GtkItemFactoryEntry menu_items[] = {
  {__("/_File"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_File/_New"), "<CTL>N", menu_new, 0, "<StockItem>", GTK_STOCK_NEW},
  {__("/_File/_Open"), "<CTL>O", menu_open, 0, "<StockItem>", GTK_STOCK_OPEN},
  {__("/_File/_Save"), "<CTL>S", menu_save, 0, "<StockItem>", GTK_STOCK_SAVE},
  {__("/_File/_Save as"), NULL, menu_save_as, 0, "<Item>", NULL},
  {"/_File/sep1", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_File/_Merge"), "<CTL>M", menu_merge, 0, "<Item>", NULL},
  {__("/_File/_Export"), "<CTL>E", menu_export, 0, "<StockItem>",
   GTK_STOCK_CONVERT},
#ifdef DEBUG
  {__("/_File/_Debug"), "<CTL>D", menu_debug, 0, "<Item>", NULL},
#endif
  {"/_File/sep1", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_File/_Quit"), "<CTL>Q", menu_quit, 0, "<StockItem>", GTK_STOCK_QUIT},
  {__("/_Edit"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_Edit/_Undo"), "<CTL>Z", menu_undo, 0, "<StockItem>", GTK_STOCK_UNDO},
  {__("/_Edit/_Redo"), "<CTL>R", menu_redo, 0, "<StockItem>", GTK_STOCK_REDO},
  {"/_Edit/sep1", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_Edit/_Cut"), NULL, menu_cut, 0, "<StockItem>", GTK_STOCK_CUT},
  {__("/_Edit/_Copy"), NULL, menu_copy, 0, "<StockItem>", GTK_STOCK_COPY},
  {__("/_Edit/_Paste"), NULL, menu_paste, 0, "<StockItem>", GTK_STOCK_PASTE},
  {__("/_Edit/_Delete"), NULL, menu_del, 0, "<StockItem>", GTK_STOCK_DELETE},
  {"/_Edit/sep2", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_Edit/Select _all"), "<CTL>A", menu_select_all, 0, "<Item>", NULL},
  {__("/_Transformations"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_Transformations/Flip _horizontally"), "<CTL>H", menu_flip_h, 0, "<Item>", NULL},
  {__("/_Transformations/Flip _vertically"), "<CTL>V", menu_flip_v, 0, "<Item>", NULL},
  {__("/_Transformations/_Invert"), "<CTL>I", menu_invert, 0, "<Item>", NULL},
  {__("/_Transformations/_Rotate x degrees"), NULL, menu_rotate, 0, "<Item>", NULL},
  {"/_Transformations/sep1", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_Transformations/_Clean-up buffer"), NULL, menu_cleanup, 0, "<Item>", NULL},
  {"/_Transformations/sep2", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_Transformations/_Group"), "<CTL>G", menu_group, 0, "<Item>", NULL},
  {__("/_Transformations/_Ungroup"), "<CTL>U", menu_ungroup, 0, "<Item>", NULL},
  {__("/_Transformations/_Align"), "<CTL><SHFT>A", menu_align, 0, "<Item>", NULL},
  {__("/_Options"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_Options/_Attraction"), NULL, menu_attract, 0, "<CheckItem>", NULL},
  {__("/_Options/_Learning messages"), NULL, menu_learn_msg, 0, "<CheckItem>",
   NULL},
  {"/_Options/sep1", NULL, NULL, 0, "<Separator>", NULL},
  {__("/_Options/_Preferences"), NULL, menu_pref, 0, "<Item>", NULL},
  {__("/_View"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_View/Zoom _1:1"), "<CTL>1", menu_zoom_default, 0, "<Item>", NULL},
  {__("/_View/Zoom _in"), "<CTL>+", menu_zoom_in, 0, "<Item>", NULL},
  {__("/_View/Zoom _out"), "<CTL>-", menu_zoom_out, 0, "<Item>", NULL},
  {__("/_View/Precise _zoom"), "<ALT>Z", menu_zoom, 0, "<Item>", NULL},
  {__("/_Help"), NULL, NULL, 0, "<Branch>", NULL},
  {__("/_Help/_About EasyChem"), NULL, menu_about, 0, "<Item>", NULL}
};

static gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]);

static gchar *
translate (const gchar * str, G_GNUC_UNUSED gpointer data)
{
  return gettext (str);
}

static GtkWidget *
get_menubar_menu (GtkWidget * window)
{
  GtkAccelGroup *accel_group;

  accel_group = gtk_accel_group_new ();
  item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>",
                               accel_group);
  gtk_item_factory_set_translate_func (item_factory, &translate, NULL, NULL);
  gtk_item_factory_create_items (item_factory, (unsigned) nmenu_items,
                         menu_items, NULL);
  gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
  return gtk_item_factory_get_widget (item_factory, "<main>");
}



gint
changed_mode (void)
{
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_add)))
  {
    mode = MODE_ADD;
    gtk_widget_set_sensitive (menu_bond, TRUE);
    gtk_widget_set_sensitive (spin_angle, TRUE);
    gtk_widget_set_sensitive (spin_bond, TRUE);
    L_MSG (_("Press the left button to add a bond, and release it when you're done. Use CONTROL to add a ring."));
  }
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_adjust)))
  {
    mode = MODE_ADJUST;
    gtk_widget_set_sensitive (menu_bond, FALSE);
    gtk_widget_set_sensitive (spin_angle, FALSE);
    gtk_widget_set_sensitive (spin_bond, FALSE);
    L_MSG (_("You can change the selection, and move things around!"));
  }
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_rotate)))
  {
    mode = MODE_ROTATE;
    gtk_widget_set_sensitive (menu_bond, FALSE);
    gtk_widget_set_sensitive (spin_angle, TRUE);
    gtk_widget_set_sensitive (spin_bond, FALSE);
    L_MSG (_("You can change the selection, and rotate it."));
  }
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_edit)))
  {
    mode = MODE_EDIT;
    gtk_widget_set_sensitive (menu_bond, FALSE);
    gtk_widget_set_sensitive (spin_angle, FALSE);
    gtk_widget_set_sensitive (spin_bond, FALSE);
    L_MSG (_("Click on a bond to edit its properties. Middle-click to change its type immediatly."));
  }
  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (radio_ornament)))
  {
    mode = MODE_ORNAMENT;
    gtk_widget_set_sensitive (menu_bond, FALSE);
    gtk_widget_set_sensitive (spin_angle, FALSE);
    gtk_widget_set_sensitive (spin_bond, FALSE);
    L_MSG (_("Click on the end of a bond or an atom to add an ornament or edit existing ones."));
  }

  REDRAW;

  return TRUE;
}




/* And this is the main routine */
int
main (int argc, char *argv[])
{
  GtkWidget *scrolled_window, *vbox, *vboxp, *hbox, *label;
  GtkWidget *menu, *menu_item, *menubar;
  GtkObject *adjust;
  GdkCursor *cursor_cross;
  gchar *basename;
  FILE *file;
  int i;

#ifdef MACOSX
  macosx_initializecarbon ();
#include <X11/Xlib.h>
  Display *disp;
  
  if (getenv ("DISPLAY") == NULL)
    setenv ("DISPLAY", ":0.0", 0);
  if ((disp = XOpenDisplay (NULL)) != NULL)
    XCloseDisplay (disp);
  else
  {
    fputs ("There is no X server running (at least, none I can connect to).\n",
           stderr);
    fputs ("Please launch one before starting EasyChem.\n", stderr);
    macosx_noxrunning ();
    exit (1);
  }
#endif

/*#if GTK_MINOR_VERSION >= 2
  g_set_application_name ("Easychem");
#endif*/
#ifdef I18N
  setlocale (LC_ALL, "");
  bindtextdomain("easychem", PREFIX "/share/locale");
  textdomain ("easychem");
#endif
  setlocale (LC_NUMERIC, "C");
  gtk_init (&argc, &argv);
  signal (SIGINT, handle_signal);
  signal (SIGTERM, handle_signal);
  signal (SIGSEGV, handle_signal);

/* mingw doesn't have a SIGHUP */
#ifdef SIGHUP
  signal (SIGHUP, handle_signal);
#endif

/* win32 builds print annoying warnings about fonts */
#ifdef WIN32
  g_log_set_handler (NULL, G_LOG_LEVEL_WARNING | G_LOG_FLAG_FATAL
                           | G_LOG_FLAG_RECURSION, log_ignore, NULL);
#endif

  read_config_file ();
  
  angle = (int) ANGLE_DEFAULT;

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_title (GTK_WINDOW (window), "EasyChem");
  gtk_widget_set_size_request (window, 550, 500);
  g_signal_connect (G_OBJECT (window), "delete_event",
                G_CALLBACK (try_destroy_window), NULL);
  statusbar = gtk_statusbar_new ();

  vboxp = gtk_vbox_new (FALSE, 1);
  vbox = gtk_vbox_new (FALSE, 10);
  gtk_container_set_border_width (GTK_CONTAINER (vboxp), 1);
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 10);
  gtk_container_add (GTK_CONTAINER (window), vboxp);

  menubar = get_menubar_menu (window);
  gtk_box_pack_start (GTK_BOX (vboxp), menubar, FALSE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (vboxp), vbox, TRUE, TRUE, 0);
  gtk_box_pack_end (GTK_BOX (vboxp), statusbar, FALSE, TRUE, 0);
  gtk_statusbar_push (GTK_STATUSBAR (statusbar), 0, _("Welcome to EasyChem!"));

  scrolled_window = gtk_scrolled_window_new (NULL, NULL);
  gtk_container_set_border_width (GTK_CONTAINER (scrolled_window), 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
                          GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);

  gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);

/* Create the drawing area */
  drawing_area = gtk_drawing_area_new ();
  gtk_drawing_area_size (GTK_DRAWING_AREA (drawing_area),
                   U2S (prop.global_width), U2S (prop.global_height));
  gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW
                               (scrolled_window), drawing_area);

/* Signals used to handle backing pixmap */
  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
                G_CALLBACK (expose_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area), "configure_event",
                G_CALLBACK (configure_event), NULL);

/* Event signals */
  g_signal_connect (G_OBJECT (drawing_area), "motion_notify_event",
                G_CALLBACK (motion_notify_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area), "button_press_event",
                G_CALLBACK (button_press_event), NULL);
  g_signal_connect (G_OBJECT (drawing_area), "button_release_event",
                G_CALLBACK (button_release_event), NULL);
  g_signal_connect (G_OBJECT (window), "key_press_event",
                G_CALLBACK (key_press_event), NULL);

  gtk_widget_set_events (drawing_area, GDK_EXPOSURE_MASK
                   | GDK_LEAVE_NOTIFY_MASK
                   | GDK_BUTTON_PRESS_MASK
                   | GDK_BUTTON_RELEASE_MASK
                   | GDK_KEY_PRESS_MASK
                   | GDK_POINTER_MOTION_MASK
                   | GDK_POINTER_MOTION_HINT_MASK);

/* To switch modes */
  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  radio_adjust = gtk_radio_button_new_with_label (NULL, _("Adjust"));
  radio_add =
    gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON
                                     (radio_adjust), _("Add bonds"));
  radio_rotate = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON
                                                (radio_adjust),
                                                _("Rotate"));
  radio_edit = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON
                                              (radio_adjust),
                                              _("Edit"));
  radio_ornament = gtk_radio_button_new_with_label_from_widget
                     (GTK_RADIO_BUTTON (radio_adjust), _("Ornaments"));
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (radio_add), TRUE);
  gtk_box_pack_start (GTK_BOX (hbox), radio_adjust, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), radio_edit, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), radio_add, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), radio_rotate, FALSE, FALSE, 0);
  gtk_box_pack_start (GTK_BOX (hbox), radio_ornament, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (radio_adjust), "toggled",
                    G_CALLBACK (changed_mode), NULL);
  g_signal_connect (G_OBJECT (radio_edit), "toggled",
                    G_CALLBACK (changed_mode), NULL);
  g_signal_connect (G_OBJECT (radio_add), "toggled",
                    G_CALLBACK (changed_mode), NULL);
  g_signal_connect (G_OBJECT (radio_rotate), "toggled",
                    G_CALLBACK (changed_mode), NULL);
  g_signal_connect (G_OBJECT (radio_ornament), "toggled",
                    G_CALLBACK (changed_mode), NULL);

/* And the properties you can set. They are in a hbox. */
  hbox = gtk_hbox_new (FALSE, 4);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);

/* We have a label and a spin button for angle */
  label = gtk_label_new (_("Ring size:"));
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjust = gtk_adjustment_new (ANGLE_DEFAULT, ANGLE_MIN, ANGLE_MAX,
                         ANGLE_SM_INCR, ANGLE_LG_INCR, 0.0);

  spin_angle = gtk_spin_button_new (GTK_ADJUSTMENT (adjust), 0.0, 0);
  gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin_angle),
                             GTK_UPDATE_IF_VALID);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_angle), TRUE);
  gtk_box_pack_start (GTK_BOX (hbox), spin_angle, FALSE, FALSE, 0);

/* A label and a spin button for bond-type */
  label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5);
  label = gtk_label_new (_("Bond type:"));
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  menu = gtk_menu_new ();

#define ADD_MENU_ITEM(STR) { \
  menu_item = gtk_menu_item_new_with_label (STR); \
  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item); }

  ADD_MENU_ITEM (_("Simple"));
  ADD_MENU_ITEM (_("Double"));
  ADD_MENU_ITEM (_("Triple"));
  ADD_MENU_ITEM (_("Going up"));
  ADD_MENU_ITEM (_("Going down"));
  ADD_MENU_ITEM (_("Dashed"));
  ADD_MENU_ITEM (_("Arrow"));
  ADD_MENU_ITEM (_("Arc"));
  ADD_MENU_ITEM (_("Circle"));
  ADD_MENU_ITEM (_("Atom/Group"));

#undef ADD_MENU_ITEM

  menu_bond = gtk_option_menu_new ();
  gtk_option_menu_set_menu (GTK_OPTION_MENU (menu_bond), menu);
  gtk_option_menu_set_history (GTK_OPTION_MENU (menu_bond), 0);
  gtk_box_pack_start (GTK_BOX (hbox), menu_bond, FALSE, FALSE, 0);



/* Bond size and atom size spin buttons */
  label = gtk_label_new ("");
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 5);
  label = gtk_label_new (_("Bond size:"));
  gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);

  adjust = gtk_adjustment_new (BOND_DEFAULT, BOND_MIN, BOND_MAX,
                         BOND_SM_INCR, BOND_LG_INCR, 0.0);

  spin_bond = gtk_spin_button_new (GTK_ADJUSTMENT (adjust), 0.0, 2);
  gtk_spin_button_set_update_policy (GTK_SPIN_BUTTON (spin_bond),
                             GTK_UPDATE_IF_VALID);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spin_bond), TRUE);
  gtk_box_pack_start (GTK_BOX (hbox), spin_bond, FALSE, FALSE, 0);

/* Showing things */
  gtk_widget_show_all (window);

  cursor_cross = gdk_cursor_new (GDK_TCROSS);
  gdk_window_set_cursor (drawing_area->window, cursor_cross);

  gc_sel = gdk_gc_new (drawing_area->window);
  gc_gray = gdk_gc_new (drawing_area->window);
  gc_help = gdk_gc_new (drawing_area->window);
  gdk_gc_set_rgb_fg_color (gc_help, &red);
  gdk_gc_set_rgb_fg_color (gc_sel, &blue);
  gdk_gc_set_rgb_fg_color (gc_gray, &gray);

/* We can open a file from command-line. */
  if (argc == 2)
    {
      file = fopen (argv[1], "r");
      if (file == NULL)
      fprintf (stderr, _("Warning: cannot load file '%s'.\n"), argv[1]);
      else
      {
        bonds = load_list_bonds (file, 0, 0);
        adjust_all_atom_in_list (bonds, drawing_area, mode, zoom);
        fclose (file);
        if (bonds != NULL)
        {
          filename = g_strdup (argv[1]);
          basename = g_path_get_basename (filename);
          gtk_window_set_title (GTK_WINDOW (window),
                        g_strdup_printf ("EasyChem [%s]", basename));
          g_free (basename);
        }
      }
    }

  for (i = 0; i < NUM_UNDO; i++)
  {
    undo_list[i] = NULL;
    undo_list_txt[i] = NULL;
  }

  menu_undo_widget = gtk_item_factory_get_widget (item_factory,
                                            _("/Edit/Undo"));
  menu_redo_widget = gtk_item_factory_get_widget (item_factory,
                                            _("/Edit/Redo"));
  menu_undo_update ();
  gtk_check_menu_item_set_active
    (GTK_CHECK_MENU_ITEM (gtk_item_factory_get_widget
                    (item_factory, _("/Options/Attraction"))), TRUE);


/* Let's go, folks! */
  REDRAW;
  g_atexit (easychem_quit);
  gtk_main ();

  return 0;
}

Generated by  Doxygen 1.6.0   Back to index