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