/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)
  
	Adresse mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.

	Ce logiciel est un programme informatique servant à visualiser des
	structures atomiques dans un rendu pseudo-3D. 

	Ce logiciel est régi par la licence CeCILL soumise au droit français et
	respectant les principes de diffusion des logiciels libres. Vous pouvez
	utiliser, modifier et/ou redistribuer ce programme sous les conditions
	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA 
	sur le site "http://www.cecill.info".

	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez 
	pris connaissance de la licence CeCILL, et que vous en avez accepté les
	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
*/

/*   LICENCE SUM UP
	Copyright CEA, contributors : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2005)

	E-mail address:
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.

	This software is a computer program whose purpose is to visualize atomic
	configurations in 3D.

	This software is governed by the CeCILL  license under French law and
	abiding by the rules of distribution of free software.  You can  use, 
	modify and/ or redistribute the software under the terms of the CeCILL
	license as circulated by CEA, CNRS and INRIA at the following URL
	"http://www.cecill.info". 

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "math.h"

#include "support.h"
#include "interface.h"
#include "gtk_main.h"

#include "gtk_move.h"
#include "gtk_pick.h"
#include "gtk_interactive.h"
#include "gtk_renderingWindowWidget.h"
#include "openGLFunctions/interactive.h"
#include "extensions/marks.h"
#include "extraFunctions/plane.h"
#include "extraFunctions/translate.h"
#include "extraGtkFunctions/gtk_numericalEntryWidget.h"
#include "extraGtkFunctions/gtk_elementComboBox.h"
#include "coreTools/toolMatrix.h"
#include "extensions/box.h"

/**
 * SECTION: gtk_move
 * @short_description: The move tab in the interactive dialog.
 *
 * <para>This action tab provides widgets to move single or group of
 * atoms. It provides also widgets to remove or add atoms. And finally
 * it provides a way to change the basis-set by picking atoms to form
 * vertices of a new basis-set.</para>
 */

/* Callbacks. */
static void refreshMoveAxesValues(VisuGlView *view);
static void onAxeChoosen(VisuUiNumericalEntry *entry, double oldValue, gpointer data);
static void onRemoveNodeClicked(GtkButton *button, gpointer user_data);
static void onAddNodeClicked(GtkButton *button, gpointer user_data);
static void onMoveMethod(GtkToggleButton *toggle, gpointer data);
static void onDuplicate(GtkButton *button, gpointer data);
static void onMoveToggled(GtkToggleButton *toggle, gpointer data);
static void onSpinBasis(GtkSpinButton *spin, gpointer data);
static gboolean removeHighlight(gpointer data);
static void onChooseBasis(GtkToggleButton *toggle, gpointer data);
static void onBasisSelected(VisuInteractive *inter, VisuInteractivePick pick,
			    VisuData *dataObj, VisuNode *nodes0,
                            VisuNode *node1, VisuNode *node2, gpointer data);
static void onApplyBasis(GtkButton *button, gpointer data);
static void _setDataModel(VisuData *dataObj);
static void _setSpinRange(VisuData *dataObj);
static void onPositionChanged(VisuData *dataObj, GArray *ids, gpointer data);
static void onStartMove(VisuInteractive *inter, GArray* nodeIds, gpointer data);
static void onGetAxisClicked(VisuGlView *view, GtkButton *button);
static void onPickClickStop(VisuInteractive *inter, gpointer data);

/* Local methods. */
static void setLabelsOrigin(VisuData *data, GArray *nodeIds);
static void drawBasisCell(VisuBox *box, float O[3], float mat[3][3]);

#define GTK_MOVE_INFO				\
  _("left-button\t\t\t\t: drag node(s) in the screen plane\n"	\
    "middle-button (wheel)\t\t: drag node(s) along specific axis\n"	\
    "shift-left-button\t\t\t: drag node(s) along x axis\n"	\
    "control-left-button\t\t\t: drag node(s) along y axis\n"	\
    "control-shift-left-button\t: drag node(s) along z axis\n"	\
    "right-button\t\t\t\t: switch to observe")

/* Pick informations hook */
static VisuInteractive *interPick = NULL, *interMove = NULL;
static int movedNode = -1;
static float moveNodeOrigin[3];
#define GTK_MOVE_NO_NODE         _("(<i>none</i>)")
static gulong onSpin_id[4];
static VisuInteractiveId currentMode = interactive_move;
static guint currentAxe;
static VisuData *_dataModel = NULL;
static gulong popInc_signal, popDec_signal, popChg_signal;
static VisuNodeMoverTranslation *translator = NULL;

/* Widgets */
static GtkWidget *notebookAction;
static GtkWidget *observeWindow;
static GtkWidget *entryAddXYZ[3];
static GtkWidget *entryAxeXYZ[3];
static GtkWidget *removeButton;
static GtkWidget *comboElements;
static GtkWidget *radioMovePick, *radioMoveRegion;
enum
  {
    COLUMN_NAME,             /* The label shown */
    COLUMN_POINTER_TO_DATA,  /* Pointer to the VisuElement. */
    N_COLUMNS
  };
static GtkWidget *checkDuplicate, *comboDuplicate, *buttonDupplicate;
static GtkWidget *labelOriginX, *labelOriginY, *labelOriginZ;
static GtkWidget *labelScreenHorizontal, *labelScreenVertical;
static GtkWidget *spinABC[4], *toggleABC[4], *applyBasis;
static gchar *lblSpinABC[4] = {"orig.:", "X:", "Y:", "Z:"};
static gint prevBasis[4] = {0, 0, 0, 0};
static guint timeoutBasis[4];
static VisuGlExtBox *extBasis;

/********************/
/* Public routines. */
/********************/
static gboolean toValue(GBinding *binding _U_, const GValue *from_value,
                        GValue *to_value, gpointer user_data)
{
  gfloat *delta = (gfloat*)g_value_get_boxed(from_value);
  g_return_val_if_fail(delta, FALSE);

  DBG_fprintf(stderr, "Gtk Move: set entry %d to %g.\n",
              GPOINTER_TO_INT(user_data), delta[GPOINTER_TO_INT(user_data)]);
  g_value_set_double(to_value, delta[GPOINTER_TO_INT(user_data)]);
  return TRUE;
}
static gboolean fromValue(GBinding *binding, const GValue *from_value,
                          GValue *to_value, gpointer user_data)
{
  gfloat delta[3];
  visu_node_mover_translation_get(VISU_NODE_MOVER_TRANSLATION(g_binding_get_source(binding)), delta);

  delta[GPOINTER_TO_INT(user_data)] = g_value_get_double(from_value);
  g_value_set_boxed(to_value, delta);
  return TRUE;
}
static gboolean toNMoves(GBinding *binding _U_, const GValue *from_value,
                         GValue *to_value, gpointer user_data _U_)
{
  GArray *ids = (GArray*)g_value_get_boxed(from_value);
  gchar numero[128];

  if (!ids || !ids->len)
    g_value_set_static_string(to_value, GTK_MOVE_NO_NODE);
  else if (ids->len == 1)
    {
      sprintf(numero, _("(node %d)"), g_array_index(ids, guint, 0) + 1);
      g_value_set_string(to_value, numero);
    }
  else
    {
      sprintf(numero, _("(%d nodes)"), ids->len);
      g_value_set_string(to_value, numero);
    }
  return TRUE;
}
static gboolean toDupSensitive(GBinding *binding _U_, const GValue *from_value,
                               GValue *to_value, gpointer user_data _U_)
{
  GArray *ids = (GArray*)g_value_get_boxed(from_value);

  g_value_set_boolean(to_value, (ids && ids->len));
  return TRUE;
}
/**
 * visu_ui_interactive_move_initBuild: (skip)
 * @main: the main interface.
 * @label: a location to store the name of the move tab ;
 * @help: a location to store the help message to be shown at the
 * bottom of the window ;
 * @radio: a location on the radio button that will be toggled when
 * the move action is used.
 * 
 * This routine should be called in conjonction to the
 * visu_ui_interactive_pick_initBuild() one. It completes the creation of widgets
 * (and also initialisation of values) for the move tab.
 */
GtkWidget* visu_ui_interactive_move_initBuild(VisuUiMain *main, gchar **label,
                                              gchar **help, GtkWidget **radio)
{
  GtkWidget *wd, *hbox, *vbox, *bt, *lbl;
  VisuData *data;
  VisuGlView *view;
  VisuNodeArrayIter dataIter;
  guint i, idmax;
  VisuGlNodeScene *scene;
  GtkWidget *entryMoveXYZ[3], *cancelButton;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();
#endif

  scene = visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering());

  extBasis = visu_gl_ext_box_new("New basis set highlight");
  visu_gl_ext_lined_setWidth(VISU_GL_EXT_LINED(extBasis), 1.f);
  visu_gl_ext_lined_setStipple(VISU_GL_EXT_LINED(extBasis), 57568);
  visu_gl_ext_box_setExpandStipple(extBasis, 57568);
  visu_gl_ext_setActive(VISU_GL_EXT(extBasis), FALSE);
  visu_gl_ext_set_add(VISU_GL_EXT_SET(scene), VISU_GL_EXT(extBasis));

  *label = g_strdup("Pick");
  *help  = g_strdup(GTK_MOVE_INFO);
  *radio = lookup_widget(main->interactiveDialog, "radioMove");
  g_signal_connect(G_OBJECT(*radio), "toggled",
		   G_CALLBACK(onMoveToggled), (gpointer)main->interactiveDialog);

  view = visu_gl_node_scene_getGlView(scene);
  data = visu_gl_node_scene_getData(scene);
  if (data)
    {
      visu_node_array_iter_new(VISU_NODE_ARRAY(data), &dataIter);
      idmax = dataIter.idMax + 1;
    }
  else
    idmax = 1;

  /* The node movers. */
  translator = visu_node_mover_translation_new();
  visu_gl_node_scene_addMover(scene, VISU_NODE_MOVER(translator));

  /* We create here the two interactives. */
  interMove = visu_interactive_new(interactive_move);
  g_signal_connect(G_OBJECT(interMove), "start-move",
		   G_CALLBACK(onStartMove), (gpointer)scene);
  g_signal_connect_swapped(G_OBJECT(interMove), "move",
                           G_CALLBACK(visu_node_mover_translation_add),
                           (gpointer)translator);
  g_signal_connect_swapped(G_OBJECT(interMove), "stop-move",
                           G_CALLBACK(visu_node_mover_push), (gpointer)translator);
  g_signal_connect(G_OBJECT(interMove), "stop",
		   G_CALLBACK(visu_ui_interactive_toggle), (gpointer)0);
  interPick = visu_interactive_new(interactive_pick);
  visu_interactive_setMessage(interPick, _("Pick a node with the mouse"));
  g_signal_connect(G_OBJECT(interPick), "node-selection",
		   G_CALLBACK(onBasisSelected), (gpointer)0);
  g_signal_connect(G_OBJECT(interPick), "stop",
		   G_CALLBACK(onPickClickStop), (gpointer)0);

  observeWindow = main->interactiveDialog;
  
  vbox = lookup_widget(main->interactiveDialog, "vbox21");

  notebookAction = lookup_widget(main->interactiveDialog, "notebookAction");

  /* The move node action. */
  removeButton = gtk_button_new();
  gtk_box_pack_end(GTK_BOX(lookup_widget(main->interactiveDialog, "hbox72")),
                   removeButton, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(removeButton,
                              _("Suppress node (either picked one or selected ones)."));
  gtk_container_add(GTK_CONTAINER(removeButton),
                    gtk_image_new_from_icon_name("list-remove", GTK_ICON_SIZE_MENU));
  g_signal_connect(G_OBJECT(removeButton), "clicked",
		   G_CALLBACK(onRemoveNodeClicked), (gpointer)0);
  gtk_widget_show_all(removeButton);

  lbl = lookup_widget(main->interactiveDialog, "labelNMoves");
  gtk_label_set_use_markup(GTK_LABEL(lbl), TRUE);
  g_object_bind_property_full(visu_ui_interactive_pick_getSelection(), "selection",
                              lbl, "label", G_BINDING_SYNC_CREATE,
                              toNMoves, NULL, (gpointer)0, (GDestroyNotify)0);

  radioMovePick = lookup_widget(main->interactiveDialog, "radioMovePick");
  gtk_widget_set_name(radioMovePick, "message_radio");
  radioMoveRegion = lookup_widget(main->interactiveDialog, "radioMoveRegion");
  gtk_widget_set_name(radioMoveRegion, "message_radio");

  labelOriginX = lookup_widget(main->interactiveDialog, "labelOriginalX");
  labelOriginY = lookup_widget(main->interactiveDialog, "labelOriginalY");
  labelOriginZ = lookup_widget(main->interactiveDialog, "labelOriginalZ");

  labelScreenHorizontal = lookup_widget(main->interactiveDialog, "labelHorizontalAxe");
  labelScreenVertical   = lookup_widget(main->interactiveDialog, "labelVerticalAxe");
  g_signal_connect_object(G_OBJECT(view), "notify::theta",
                          G_CALLBACK(refreshMoveAxesValues), labelScreenHorizontal, 0);
  g_signal_connect_object(G_OBJECT(view), "notify::phi",
                          G_CALLBACK(refreshMoveAxesValues), labelScreenHorizontal, 0);
  g_signal_connect_object(G_OBJECT(view), "notify::omega",
                          G_CALLBACK(refreshMoveAxesValues), labelScreenHorizontal, 0);

  wd = lookup_widget(main->interactiveDialog, "tableMovePick");
  entryMoveXYZ[0] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryMoveXYZ[0]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryMoveXYZ[0], 1, 2, 1, 1);
  g_object_bind_property_full(translator, "translation", entryMoveXYZ[0], "value",
                              G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                              toValue, fromValue, GINT_TO_POINTER(0), (GDestroyNotify)0);
  entryMoveXYZ[1] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryMoveXYZ[1]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryMoveXYZ[1], 3, 2, 1, 1);
  g_object_bind_property_full(translator, "translation", entryMoveXYZ[1], "value",
                              G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                              toValue, fromValue, GINT_TO_POINTER(1), (GDestroyNotify)0);
  entryMoveXYZ[2] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryMoveXYZ[2]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryMoveXYZ[2], 5, 2, 1, 1);
  g_object_bind_property_full(translator, "translation", entryMoveXYZ[2], "value",
                              G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                              toValue, fromValue, GINT_TO_POINTER(2), (GDestroyNotify)0);
  entryAxeXYZ[0] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAxeXYZ[0]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryAxeXYZ[0], 1, 1, 1, 1);
  entryAxeXYZ[1] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAxeXYZ[1]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryAxeXYZ[1], 3, 1, 1, 1);
  entryAxeXYZ[2] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAxeXYZ[2]), 8);
  gtk_grid_attach(GTK_GRID(wd), entryAxeXYZ[2], 5, 1, 1, 1);
  bt = gtk_button_new();
  gtk_grid_attach(GTK_GRID(wd), bt, 7, 1, 1, 1);
  gtk_widget_set_tooltip_text(bt, _("Capture the perpendicular axis to the current view."));
  gtk_container_add(GTK_CONTAINER(bt),
                    gtk_image_new_from_icon_name("zoom-fit-best", GTK_ICON_SIZE_MENU));
  g_signal_connect_swapped(G_OBJECT(bt), "clicked",
                           G_CALLBACK(onGetAxisClicked), (gpointer)view);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_grid_attach(GTK_GRID(wd), hbox, 7, 2, 1, 1);
  cancelButton = gtk_button_new();
  gtk_box_pack_start(GTK_BOX(hbox), cancelButton, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(cancelButton, _("Apply translation values to coordinates."));
  gtk_container_add(GTK_CONTAINER(cancelButton),
                    gtk_image_new_from_icon_name("edit-redo", GTK_ICON_SIZE_MENU));
  g_signal_connect_swapped(G_OBJECT(cancelButton), "clicked",
                           G_CALLBACK(visu_node_mover_animate), translator);
  g_object_bind_property(translator, "valid", cancelButton, "sensitive",
                         G_BINDING_SYNC_CREATE);
  cancelButton = gtk_button_new();
  gtk_box_pack_start(GTK_BOX(hbox), cancelButton, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(cancelButton, _("Return coordinates to initial values."));
  gtk_container_add(GTK_CONTAINER(cancelButton),
                    gtk_image_new_from_icon_name("edit-undo", GTK_ICON_SIZE_MENU));
  g_signal_connect_swapped(G_OBJECT(cancelButton), "clicked",
                           G_CALLBACK(visu_node_mover_undo), translator);
  g_object_bind_property(translator, "undo-stack-depth", cancelButton, "sensitive",
                         G_BINDING_SYNC_CREATE);
  gtk_widget_show_all(wd);

  /* The add line. */
  wd = lookup_widget(main->interactiveDialog, "hboxAddNode");
  entryAddXYZ[0] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAddXYZ[0]), 6);
  gtk_box_pack_start(GTK_BOX(wd), entryAddXYZ[0], FALSE, FALSE, 0);
  entryAddXYZ[1] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAddXYZ[1]), 6);
  gtk_box_pack_start(GTK_BOX(wd), entryAddXYZ[1], FALSE, FALSE, 0);
  entryAddXYZ[2] = visu_ui_numerical_entry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryAddXYZ[2]), 6);
  gtk_box_pack_start(GTK_BOX(wd), entryAddXYZ[2], FALSE, FALSE, 0);
  /* We create the structure that store the VisuElements */
  comboElements = visu_ui_element_combobox_new(FALSE, FALSE, (const gchar*)0);
  g_object_bind_property(scene, "data", comboElements, "nodes", G_BINDING_SYNC_CREATE);
  gtk_box_pack_start(GTK_BOX(wd), comboElements, FALSE, FALSE, 0);
  gtk_box_reorder_child(GTK_BOX(wd), comboElements, 0);
  bt = gtk_button_new();
  gtk_box_pack_end(GTK_BOX(wd), bt, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(bt, _("Add a new node."));
  gtk_container_add(GTK_CONTAINER(bt),
                    gtk_image_new_from_icon_name("list-add", GTK_ICON_SIZE_MENU));
  g_signal_connect(G_OBJECT(bt), "clicked",
		   G_CALLBACK(onAddNodeClicked), (gpointer)0);
  gtk_widget_show_all(wd);

  /* The duplicate line. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
  wd = gtk_label_new(_("<b>Duplicate nodes:</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 0);
  wd = gtk_label_new(_("<span size=\"smaller\">(the nodes listed in the pick tab)</span>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  gtk_widget_show_all(hbox);
  hbox = gtk_hbox_new(FALSE, 0);
  g_object_bind_property_full(visu_ui_interactive_pick_getSelection(), "selection",
                              hbox, "sensitive", G_BINDING_SYNC_CREATE,
                              toDupSensitive, NULL, (gpointer)0, (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  wd = gtk_check_button_new_with_mnemonic(_("du_plicate nodes as they are"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), TRUE);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  checkDuplicate = wd;
  wd = gtk_label_new(_(" or as new: "));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = visu_ui_element_combobox_new(FALSE, FALSE, (const gchar*)0);
  g_object_bind_property(scene, "data", wd, "nodes", G_BINDING_SYNC_CREATE);
  g_object_bind_property(checkDuplicate, "active", wd, "sensitive",
                         G_BINDING_SYNC_CREATE | G_BINDING_INVERT_BOOLEAN);
  comboDuplicate = wd;
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  wd = gtk_button_new_with_mnemonic(_("_duplicate"));
  gtk_box_pack_end(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  buttonDupplicate = wd;
  gtk_widget_show_all(hbox);

  /* The Basis line. */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
  wd = gtk_label_new(_("<b>Change the basis set:</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  gtk_widget_show_all(hbox);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
  for (i = 0; i < 4; i++)
    {
      /* if (i == 1) */
      /*   { */
      /*     gtk_widget_show_all(hbox); */
      /*     hbox = gtk_hbox_new(FALSE, 0); */
      /*     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); */
      /*   } */
      wd = gtk_label_new(_(lblSpinABC[i]));
      gtk_label_set_xalign(GTK_LABEL(wd), 1.);
      gtk_box_pack_start(GTK_BOX(hbox), wd, TRUE, TRUE, 5);
      spinABC[i] = gtk_spin_button_new_with_range(0, idmax, 1);
      onSpin_id[i] = g_signal_connect(G_OBJECT(spinABC[i]), "value-changed",
				      G_CALLBACK(onSpinBasis), GINT_TO_POINTER(i));
      gtk_box_pack_start(GTK_BOX(hbox), spinABC[i], FALSE, FALSE, 0);
      toggleABC[i] = gtk_toggle_button_new();
      gtk_button_set_relief(GTK_BUTTON(toggleABC[i]), GTK_RELIEF_NONE);
      gtk_box_pack_start(GTK_BOX(hbox), toggleABC[i], FALSE, FALSE, 0);
      gtk_container_add(GTK_CONTAINER(toggleABC[i]),
			gtk_image_new_from_icon_name("edit-find",
                                                     GTK_ICON_SIZE_MENU));
      gtk_widget_set_tooltip_text(toggleABC[i],
                                  _("Select node by picking it on the rendering area."));
      g_signal_connect(G_OBJECT(toggleABC[i]), "toggled",
		       G_CALLBACK(onChooseBasis), GINT_TO_POINTER(i));
    }
  applyBasis = gtk_button_new_with_mnemonic(_("_Apply"));
  gtk_box_pack_start(GTK_BOX(hbox), applyBasis, FALSE, FALSE, 0);
  gtk_widget_set_sensitive(applyBasis, FALSE);
  g_signal_connect(G_OBJECT(applyBasis), "clicked",
		   G_CALLBACK(onApplyBasis), (gpointer)0);
  gtk_widget_show_all(hbox);

  if (data)
    _setDataModel(data);

  for (i = 0; i < 3; i++)
    {
      g_signal_connect(G_OBJECT(entryAxeXYZ[i]), "value-changed",
		       G_CALLBACK(onAxeChoosen), (gpointer)0);
    }
  wd = lookup_widget(main->interactiveDialog, "radioMovePick");
  g_signal_connect(G_OBJECT(wd), "toggled",
		   G_CALLBACK(onMoveMethod), (gpointer)0);
  wd = lookup_widget(main->interactiveDialog, "radioMoveRegion");
  g_signal_connect(G_OBJECT(wd), "toggled",
		   G_CALLBACK(onMoveMethod), (gpointer)0);

  g_signal_connect(G_OBJECT(buttonDupplicate), "clicked",
		   G_CALLBACK(onDuplicate), (gpointer)0);
  g_signal_connect_swapped(hbox, "destroy", G_CALLBACK(_setDataModel), (gpointer)0);
  g_signal_connect_swapped(hbox, "destroy", G_CALLBACK(g_object_unref), extBasis);
  g_signal_connect_swapped(hbox, "destroy", G_CALLBACK(g_object_unref), interMove);
  g_signal_connect_swapped(hbox, "destroy", G_CALLBACK(g_object_unref), interPick);
  g_signal_connect_swapped(hbox, "destroy", G_CALLBACK(g_object_unref), translator);

  return (GtkWidget*)0;
}

static void setLabelsOrigin(VisuData *data, GArray *nodeIds)
{
  gchar numero[256];
  VisuNode *node;

  if (nodeIds && nodeIds->len > 0)
    {
      if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioMovePick)))
	{
	  movedNode = g_array_index(nodeIds, gint, 0);
	  node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), movedNode);
	  g_return_if_fail(node && node->number == (guint)movedNode);

	  /* Set the origin position values. */
	  moveNodeOrigin[0] = node->xyz[0];
	  moveNodeOrigin[1] = node->xyz[1];
	  moveNodeOrigin[2] = node->xyz[2];
	  sprintf(numero, "<span size=\"small\">/ %5.2f</span>", moveNodeOrigin[0]);
	  gtk_label_set_markup(GTK_LABEL(labelOriginX), numero);
	  sprintf(numero, "<span size=\"small\">/ %5.2f</span>", moveNodeOrigin[1]);
	  gtk_label_set_markup(GTK_LABEL(labelOriginY), numero);
	  sprintf(numero, "<span size=\"small\">/ %5.2f</span>", moveNodeOrigin[2]);
	  gtk_label_set_markup(GTK_LABEL(labelOriginZ), numero);
	}
      else
	{
	  movedNode = -1;
	  gtk_label_set_markup(GTK_LABEL(labelOriginX), "");
	  gtk_label_set_markup(GTK_LABEL(labelOriginY), "");
	  gtk_label_set_markup(GTK_LABEL(labelOriginZ), "");
	}
    }
  else
    {
      movedNode = -1;
      gtk_label_set_markup(GTK_LABEL(labelOriginX), "");
      gtk_label_set_markup(GTK_LABEL(labelOriginY), "");
      gtk_label_set_markup(GTK_LABEL(labelOriginZ), "");
    }
  /* Set the sensitivity. */
  gtk_widget_set_sensitive(removeButton, (nodeIds && nodeIds->len > 0));
}

static void setMovingNodes()
{
  GArray *nodeIds;

  DBG_fprintf(stderr, "Gtk Move: set the moving list.\n");

  nodeIds = (GArray*)0;
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioMovePick)))
    nodeIds = visu_ui_selection_get(visu_ui_interactive_pick_getSelection());
  visu_interactive_setMovingNodes(interMove, nodeIds);
  setLabelsOrigin((VisuData*)0, nodeIds);
  if (nodeIds)
    {
      visu_node_mover_setNodes(VISU_NODE_MOVER(translator), nodeIds);
      g_array_unref(nodeIds);
    }
}
/**
 * visu_ui_interactive_move_start:
 * @window: a #VisuUiRenderingWindow object.
 *
 * Initialise a moving session.
 */
void visu_ui_interactive_move_start(VisuUiRenderingWindow *window)
{
  DBG_fprintf(stderr, "Gtk Move: start the move panel.\n");
  visu_ui_rendering_window_pushInteractive(window, interMove);
  if (currentMode == interactive_pick)
    visu_ui_rendering_window_pushInteractive(window, interPick);

  /* Set the moving list. */
  setMovingNodes();
}
/**
 * visu_ui_interactive_move_stop:
 * @window: a #VisuUiRenderingWindow object.
 *
 * Finalise a moving session.
 */
void visu_ui_interactive_move_stop(VisuUiRenderingWindow *window)
{
  DBG_fprintf(stderr, "Gtk Move: unset the move panel.\n");

  if (currentMode == interactive_pick)
    visu_ui_rendering_window_popInteractive(window, interPick);
  visu_ui_rendering_window_popInteractive(window, interMove);
}
static void onStartMove(VisuInteractive *inter _U_, GArray* nodeIds, gpointer data)
{
  DBG_fprintf(stderr, "Gtk Move: callback on start-move action.\n");
  setLabelsOrigin(visu_gl_node_scene_getData(VISU_GL_NODE_SCENE(data)), nodeIds);
  visu_node_mover_setNodes(VISU_NODE_MOVER(translator), nodeIds);
  visu_node_mover_translation_reset(translator);
  return;
}

/****************/
/* Private part */
/****************/
static void _setDataModel(VisuData *dataObj)
{
  if (_dataModel)
    {
      g_signal_handler_disconnect(_dataModel, popDec_signal);
      g_signal_handler_disconnect(_dataModel, popInc_signal);
      g_signal_handler_disconnect(_dataModel, popChg_signal);
      g_object_unref(_dataModel);
    }
  _dataModel = dataObj;
  if (dataObj)
    {
      g_object_ref(dataObj);
      popDec_signal =
        g_signal_connect(G_OBJECT(dataObj), "PopulationDecrease",
                         G_CALLBACK(_setSpinRange), (gpointer)0);
      popInc_signal =
        g_signal_connect(G_OBJECT(dataObj), "PopulationIncrease",
                         G_CALLBACK(_setSpinRange), (gpointer)0);
      popChg_signal =
        g_signal_connect(G_OBJECT(dataObj), "position-changed",
                         G_CALLBACK(onPositionChanged), (gpointer)0);

      _setSpinRange(dataObj);
    }
  /* We remove the basisset drawing. */
  visu_gl_ext_setActive(VISU_GL_EXT(extBasis), FALSE);
}

static void refreshMoveAxesValues(VisuGlView *view)
{
  gint id;
  float x[3], y[3];
  char tmpChr[20];

  id = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebookAction));
  DBG_fprintf(stderr, "Gtk Move: refresh screen basis set.\n");
  DBG_fprintf(stderr, " | %d %d\n", id + 1, VISU_UI_ACTION_MOVE);
  if (id + 1 == VISU_UI_ACTION_MOVE)
    {
      visu_gl_camera_getScreenAxes(&view->camera, x, y);
      sprintf(tmpChr, "(%4.2f;%4.2f;%4.2f)", x[0], x[1], x[2]);
      gtk_label_set_text(GTK_LABEL(labelScreenHorizontal), tmpChr);
      sprintf(tmpChr, "(%4.2f;%4.2f;%4.2f)", y[0], y[1], y[2]);
      gtk_label_set_text(GTK_LABEL(labelScreenVertical), tmpChr);
    }
}
static void onMoveMethod(GtkToggleButton *toggle, gpointer data _U_)
{
  if (!gtk_toggle_button_get_active(toggle))
    return;

  /* Set the moving list. */
  setMovingNodes();
}
static void onRemoveNodeClicked(GtkButton *button _U_, gpointer user_data _U_)
{
  GArray *nodes;
  VisuData *dataObj;
  VisuNode *node;
  int i;
  VisuElement *ele;
  gfloat xyz[3];

  DBG_fprintf(stderr, "Gtk Observe/pick: remove the selected node %d.\n", movedNode);
  
  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioMovePick)))
    {
      if (movedNode < 0)
	return;

      node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), movedNode);
      if (!node)
	return;
      xyz[0] = node->xyz[0];
      xyz[1] = node->xyz[1];
      xyz[2] = node->xyz[2];
      ele = visu_node_array_getElement(VISU_NODE_ARRAY(dataObj), node);

      nodes = g_array_new(FALSE, FALSE, sizeof(guint));
      g_array_append_val(nodes, movedNode);
      visu_node_array_removeNodes(VISU_NODE_ARRAY(dataObj), nodes);
      g_array_unref(nodes);

      /* Copy the coordinates to the add entries. */
      for (i = 0; i < 3; i++)
	visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryAddXYZ[i]),
                                         xyz[i]);
      visu_ui_element_combobox_setSelection(VISU_UI_ELEMENT_COMBOBOX(comboElements), ele->name);

      /* No need for rendering changed, the population decrease should
         take care of the rest. */
      /* g_signal_emit_by_name(G_OBJECT(dataObj), "RenderingChanged", ele, NULL); */
    }
  else
    {
      DBG_fprintf(stderr, "Gtk Move: get list of selected nodes.\n");
      nodes = visu_ui_selection_get(visu_ui_interactive_pick_getSelection());
      if (nodes->len > 0)
        visu_node_array_removeNodes(VISU_NODE_ARRAY(dataObj), nodes);
      g_array_unref(nodes);
    }
  /* We remove the node as selected one. */
  setLabelsOrigin((VisuData*)0, (GArray*)0);
}
static void onAddNodeClicked(GtkButton *button _U_, gpointer user_data _U_)
{
  VisuData *dataObj;
  GList *elements;
  VisuElement *element;
  float xyz[3];

  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  elements = visu_ui_element_combobox_getSelection(VISU_UI_ELEMENT_COMBOBOX(comboElements));
  g_return_if_fail(elements);
  element = (VisuElement*)elements->data;
  g_list_free(elements);

  xyz[0] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAddXYZ[0]));
  xyz[1] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAddXYZ[1]));
  xyz[2] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAddXYZ[2]));
  visu_data_addNodeFromElement(dataObj, element, xyz, FALSE);
}
static void onDuplicate(GtkButton *button _U_, gpointer data _U_)
{
  VisuData *dataObj;
  GList *lst;
  GArray *arr, *newArr;
  gboolean custom;
  VisuElement *element;
  float coord[3], trans[3];
  VisuNodeArrayIter iter;
  VisuNode *node;

  DBG_fprintf(stderr, "Gtk Move: duplicate selected nodes.\n");

  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  element = (VisuElement*)0;
  custom = !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkDuplicate));
  if (custom)
    {
      lst = visu_ui_element_combobox_getSelection(VISU_UI_ELEMENT_COMBOBOX(comboDuplicate));
      g_return_if_fail(lst);
      element = (VisuElement*)lst->data;
      DBG_fprintf(stderr, "Gtk Move: change element to '%s'.\n", element->name);
      g_list_free(lst);
    }

  arr = visu_ui_selection_get(visu_ui_interactive_pick_getSelection());
  if (!arr || !arr->len)
    {
      if (arr)
        g_array_free(arr, TRUE);
      return;
    }

  newArr = g_array_sized_new(FALSE, FALSE, sizeof(guint), arr->len);
  visu_pointset_getTranslation(VISU_POINTSET(dataObj), trans);
  visu_node_array_startAdding(VISU_NODE_ARRAY(dataObj));
  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
  for (visu_node_array_iterStartArray(VISU_NODE_ARRAY(dataObj), &iter, arr);
       iter.node; visu_node_array_iterNextArray(VISU_NODE_ARRAY(dataObj), &iter))
    {
      visu_data_getNodePosition(dataObj, iter.node, coord);
      coord[0] -= trans[0];
      coord[1] -= trans[1];
      coord[2] -= trans[2];
      if (element)
	node = visu_data_addNodeFromElement(dataObj, element, coord, FALSE);
      else
	node = visu_data_addNodeFromIndex(dataObj, iter.iElement, coord, FALSE);
      g_array_append_val(newArr, node->number);
    }
  visu_node_array_completeAdding(VISU_NODE_ARRAY(dataObj));
  visu_ui_selection_set(visu_ui_interactive_pick_getSelection(), newArr);
  g_array_unref(newArr);
}
static void onMoveToggled(GtkToggleButton *toggle, gpointer data)
{
  GtkWidget *wd;
  gboolean value;

  value = gtk_toggle_button_get_active(toggle);
  wd = lookup_widget(GTK_WIDGET(data), "vbox21");
  gtk_widget_set_sensitive(wd, value);
}
static gboolean setupBasisMatrix(VisuData *dataObj, float mat[3][3], float O[3])
{
  VisuNode *orig, *nodeA, *nodeB, *nodeC;
  float xyz[3];

  orig  =
    visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), (guint)gtk_spin_button_get_value
                                (GTK_SPIN_BUTTON(spinABC[0])) - 1);
  nodeA =
    visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), (guint)gtk_spin_button_get_value
                                (GTK_SPIN_BUTTON(spinABC[1])) - 1);
  nodeB =
    visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), (guint)gtk_spin_button_get_value
                                (GTK_SPIN_BUTTON(spinABC[2])) - 1);
  nodeC =
    visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), (guint)gtk_spin_button_get_value
                                (GTK_SPIN_BUTTON(spinABC[3])) - 1);
  if (!orig || !nodeA || !nodeB || !nodeC)
    return FALSE;

  visu_data_getNodePosition(dataObj, orig, O);
  visu_data_getNodePosition(dataObj, nodeA, xyz);
  mat[0][0] = xyz[0] - O[0];
  mat[1][0] = xyz[1] - O[1];
  mat[2][0] = xyz[2] - O[2];
  visu_data_getNodePosition(dataObj, nodeB, xyz);
  mat[0][1] = xyz[0] - O[0];
  mat[1][1] = xyz[1] - O[1];
  mat[2][1] = xyz[2] - O[2];
  visu_data_getNodePosition(dataObj, nodeC, xyz);
  mat[0][2] = xyz[0] - O[0];
  mat[1][2] = xyz[1] - O[1];
  mat[2][2] = xyz[2] - O[2];

  return TRUE;
}
static void onSpinBasis(GtkSpinButton *spin, gpointer data)
{
  int i;
  guint id;
  VisuGlNodeScene *scene;
  VisuGlExtMarks *marks;
  gboolean valid;
  VisuData *dataObj;
  GArray *ids;
  float mat[3][3], O[3];
  
  i = GPOINTER_TO_INT(data);

  scene = visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering());
  dataObj = visu_gl_node_scene_getData(scene);
  marks = visu_gl_node_scene_getMarks(scene);

  /* Remove the previous one. */
  if (prevBasis[i] > 0)
    removeHighlight(GINT_TO_POINTER(prevBasis[i]));
  if (timeoutBasis[i] > 0)
    g_source_remove(timeoutBasis[i]);

  prevBasis[i] = (gint)gtk_spin_button_get_value(spin);
  if (prevBasis[i] > 0 && visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), prevBasis[i]))
    {
      ids = g_array_new(FALSE, FALSE, sizeof(guint));
      id = prevBasis[i] - 1;
      g_array_append_val(ids, id);
      visu_gl_ext_marks_setHighlight(marks, ids, MARKS_STATUS_SET);
      g_array_unref(ids);
      /* Add a new highlight. */
#if GLIB_MINOR_VERSION > 13
      timeoutBasis[i] = g_timeout_add_seconds(1, removeHighlight,
					      GINT_TO_POINTER(prevBasis[i]));
#else
      timeoutBasis[i] = g_timeout_add(1000, removeHighlight,
				      GINT_TO_POINTER(prevBasis[i]));
#endif
    }

  valid = TRUE;
  for(i = 0; i < 4; i++)
    valid = valid && (gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinABC[i])) > 0.);
  gtk_widget_set_sensitive(applyBasis, valid);
  if (valid && setupBasisMatrix(dataObj, mat, O))
    {
      visu_gl_ext_setActive(VISU_GL_EXT(extBasis), TRUE);
      drawBasisCell(visu_boxed_getBox(VISU_BOXED(dataObj)), O, mat);
      if (tool_matrix_determinant(mat) < 0.f)
        visu_ui_interactive_setMessage(_("The new basis set will be indirect."), GTK_MESSAGE_WARNING);
      else
        visu_ui_interactive_unsetMessage();
    }
  else
    visu_gl_ext_setActive(VISU_GL_EXT(extBasis), FALSE);
}
static gboolean removeHighlight(gpointer data)
{
  int i;
  VisuGlExtMarks *marks;
  GArray *ids;

  i = GPOINTER_TO_INT(data) - 1;
  g_return_val_if_fail(i >= 0, FALSE);

  marks = visu_gl_node_scene_getMarks(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));

  /* Remove the previous one. */
  ids = g_array_new(FALSE, FALSE, sizeof(guint));
  g_array_append_val(ids, i);
  visu_gl_ext_marks_setHighlight(marks, ids, MARKS_STATUS_UNSET);
  g_array_unref(ids);

  return FALSE;
}
static void onChooseBasis(GtkToggleButton *toggle, gpointer data)
{
  guint i;
  VisuUiRenderingWindow *window;
  gboolean valid;

  window = visu_ui_main_class_getDefaultRendering();

  DBG_fprintf(stderr, "Gtk Move: one toggle chooser has been toggled.\n");
  valid = gtk_toggle_button_get_active(toggle);
  if (valid)
    {
      currentMode = interactive_pick;
      currentAxe = GPOINTER_TO_INT(data);
      visu_ui_rendering_window_pushInteractive(window, interPick);
    }
  else
    {
      currentMode = interactive_move;
      visu_ui_rendering_window_popInteractive(window, interPick);
    }

  for(i = 0; i < 4; i++)
    gtk_widget_set_sensitive(toggleABC[i], (!valid || (i == currentAxe)));
  gtk_widget_set_sensitive(lookup_widget(observeWindow, "hbox72"), !valid);
  gtk_widget_set_sensitive(lookup_widget(observeWindow, "tableMovePick"), !valid);
  gtk_widget_set_sensitive(lookup_widget(observeWindow, "hboxAddNode"), !valid);
}
static void onBasisSelected(VisuInteractive *inter _U_, VisuInteractivePick pick _U_,
			    VisuData *dataObj _U_, VisuNode *node0,
                            VisuNode *node1 _U_, VisuNode *node2 _U_, gpointer data _U_)
{
  g_return_if_fail(node0);

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinABC[currentAxe]),
			    node0->number + 1);
  gtk_toggle_button_set_active
    (GTK_TOGGLE_BUTTON(toggleABC[currentAxe]), FALSE);
}
static void onPickClickStop(VisuInteractive *inter _U_, gpointer data _U_)
{
  int i;

  for(i = 0; i < 4; i++)
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(toggleABC[i]), FALSE);
}
static void onApplyBasis(GtkButton *button _U_, gpointer data _U_)
{
  VisuData *dataObj;
  float matA[3][3], O[3];

  dataObj = visu_gl_node_scene_getData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  if (setupBasisMatrix(dataObj, matA, O))
    {
      if (!visu_data_setNewBasis(dataObj, matA, O))
        visu_ui_interactive_setMessage
          (_("Cannot change the basis: given matrix is singular."), GTK_MESSAGE_ERROR);
    }

  g_signal_handler_block(G_OBJECT(spinABC[0]), onSpin_id[0]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinABC[0]), 0);
  g_signal_handler_unblock(G_OBJECT(spinABC[0]), onSpin_id[0]);

  g_signal_handler_block(G_OBJECT(spinABC[1]), onSpin_id[1]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinABC[1]), 0);
  g_signal_handler_unblock(G_OBJECT(spinABC[1]), onSpin_id[1]);

  g_signal_handler_block(G_OBJECT(spinABC[2]), onSpin_id[2]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinABC[2]), 0);
  g_signal_handler_unblock(G_OBJECT(spinABC[2]), onSpin_id[2]);

  g_signal_handler_block(G_OBJECT(spinABC[3]), onSpin_id[3]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinABC[3]), 0);
  g_signal_handler_unblock(G_OBJECT(spinABC[3]), onSpin_id[3]);

  visu_gl_ext_setActive(VISU_GL_EXT(extBasis), FALSE);
}
static void _setSpinRange(VisuData *dataObj)
{
  VisuNodeArrayIter iter;

  visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
  DBG_fprintf(stderr, "Gtk Move: caught 'population changed', update spin up to %d.\n",
              iter.idMax + 1);
  g_signal_handler_block(G_OBJECT(spinABC[0]), onSpin_id[0]);
  gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinABC[0]), 0, iter.idMax + 1);
  g_signal_handler_unblock(G_OBJECT(spinABC[0]), onSpin_id[0]);

  g_signal_handler_block(G_OBJECT(spinABC[1]), onSpin_id[1]);
  gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinABC[1]), 0, iter.idMax + 1);
  g_signal_handler_unblock(G_OBJECT(spinABC[1]), onSpin_id[1]);

  g_signal_handler_block(G_OBJECT(spinABC[2]), onSpin_id[2]);
  gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinABC[2]), 0, iter.idMax + 1);
  g_signal_handler_unblock(G_OBJECT(spinABC[2]), onSpin_id[2]);

  g_signal_handler_block(G_OBJECT(spinABC[3]), onSpin_id[3]);
  gtk_spin_button_set_range(GTK_SPIN_BUTTON(spinABC[3]), 0, iter.idMax + 1);
  g_signal_handler_unblock(G_OBJECT(spinABC[3]), onSpin_id[3]);
}
static void onPositionChanged(VisuData *dataObj, GArray *ids _U_, gpointer data _U_)
{
  float mat[3][3], O[3];

  if (setupBasisMatrix(dataObj, mat, O))
    drawBasisCell(visu_boxed_getBox(VISU_BOXED(dataObj)), O, mat);
}
static void onAxeChoosen(VisuUiNumericalEntry *entry _U_, double oldValue _U_,
			 gpointer data _U_)
{
  float axe[3];

  axe[0] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[0]));
  axe[1] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[1]));
  axe[2] = (float)visu_ui_numerical_entry_getValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[2]));

  visu_interactive_setMovingAxe(interMove, axe);
}
static void onGetAxisClicked(VisuGlView *view, GtkButton *button _U_)
{
  visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[0]),
                                   view->camera.eye[0]);
  visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[1]),
                                   view->camera.eye[1]);
  visu_ui_numerical_entry_setValue(VISU_UI_NUMERICAL_ENTRY(entryAxeXYZ[2]),
                                   view->camera.eye[2]);
}

/********************/
/* Drawing methods. */
/********************/
static void drawBasisCell(VisuBox *box _U_, float O[3], float mat[3][3])
{
  DBG_fprintf(stderr, "Gtk Move: draw the new basis cell.\n");
  visu_gl_ext_box_setBasis(extBasis, O, mat);
}
