/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2006)
  
	Adresses mèl :
	BILLARD, non joignable par mèl ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 and Damien
	CALISTE and Olivier D'Astier, laboratoire L_Sim, (2001-2006)

	E-mail addresses :
	BILLARD, not reachable any more ;
	CALISTE, damien P caliste AT cea P fr.
	D'ASTIER, dastier AT iie P cnam 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 "gtkSpin.h"

#include <renderingMethods/renderingSpin.h>
#include <renderingBackend/visu_windowInterface.h>
#include <openGLFunctions/view.h>
#include <gtk_renderingWindowWidget.h>
#include "visuElements.h"
#include "visuConfig.h"

#include <assert.h>
#include <string.h>
#include <math.h>

#define RENDERING_NAME "Spin visualisation"

#define LABEL_ARROW_SIZE      _("Arrow size properties: ")
#define LABEL_ARROW_SIZE_MULT _("Arrow size properties <span style=\"italic\" size=\"smaller\" foreground=\"red\">\303\227 mult.</span>: ")

void set_view(GtkWidget* button, gpointer data);

/* A widget to select a shape for the arrows */
static GtkWidget *gtkw_element_shape_number = NULL;
static GtkWidget *heightResourceSpin = NULL;
static GtkWidget *rheightResourceSpin = NULL;
static GtkWidget *lengthResourceSpin = NULL;
static GtkWidget *rlengthResourceSpin = NULL;
static GtkWidget *gtkw_cone_theta_angle = NULL;
static GtkWidget *gtkw_cone_phi_angle = NULL;
static GtkWidget *gtkw_color_wheel_angle = NULL;
static GtkWidget *gtkw_use_element_color = NULL;
static GtkWidget *gtkw_use_element_color_hat = NULL;
static GtkWidget *gtkw_x = NULL, *gtkw_y = NULL, *gtkw_z = NULL; 
static GtkWidget *gtkw_set_view;
static GtkWidget *labelSizeProp;

static guint element_callback_ids[7];

static int need_redraw = 1;

static char* currentElementName;
static VisuElement* current_element = NULL;

static gulong configResourcesLoadedId;

struct FileChooserSpin_struct
{
  GtkLabel* label[2];
  GtkRadioButton* radio[2];
  gchar* files[2];
  GtkFileChooser *fileChooser;
  GtkDialog *dialog;
  int fileKind;
};
/* A special widget to select two kind of files at a time. */
int gtkSpinCreate_fileChooser(VisuData *data, GtkFileChooser **fileChooser,
			      GtkWidget **fileWidget);
/* Its callbacks. */
void fileSelectedForSpinMethod(GtkButton *button, gpointer data);
void fileActivatedForSpinMethod(GtkFileChooser *file, gpointer data);
void fileKindChangedForPosition(GtkToggleButton *button, gpointer data);
void fileKindChangedForSpin(GtkToggleButton *button, gpointer data);

/* Callbacks */
void global_resource_callback(GtkWidget* widget, gpointer data);
void element_resource_callback(GtkWidget* widget, char *property);
void changeSomethingSpin(VisuElement *ele);
void onSpinBoundsChangeArrowSize(GObject *obj, gfloat mult, gpointer data);
GtkWidget* createGtkInterfaceForSpinMethod();

GtkWidget* createGtkConfigForSpinMethod(void);
void destroyGtkConfigForSpinMethod();

void redraw(int i);

static void sync_global_resources(GObject *object, gpointer data);
static void sync_local_resources(GObject *trash, gpointer data);


/***************/
/* Public part */
/***************/

/* See h file for more info */
void initSpin_gtkPanel()
{
  setInterfaceMethods(pointerOnRenderingSpinMethod,
		      changeSomethingSpin,
		      createGtkInterfaceForSpinMethod);
}

/* See h file for more info */
void initSpin_gtkConfig()
{
  panelConfigSet_renderingInterface(pointerOnRenderingSpinMethod,
				    createGtkConfigForSpinMethod,
				    destroyGtkConfigForSpinMethod);

}

/* Creates the hbox containing x,y,z spin buttons */
static GtkWidget *gtk_spin_create_direction(float *cartesian_values)
{
  GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
  GtkWidget* label_x = gtk_label_new(_("x :"));
  GtkWidget* label_y = gtk_label_new(_(" y :"));
  GtkWidget* label_z = gtk_label_new(_(" z :"));

  gtk_misc_set_alignment(GTK_MISC(label_x), 1., 0.5);
  gtk_misc_set_alignment(GTK_MISC(label_y), 1., 0.5);
  gtk_misc_set_alignment(GTK_MISC(label_z), 1., 0.5);

  gtkw_x = gtk_spin_button_new_with_range(-99.99, 99.99, 0.05);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_x), cartesian_values[0]);
  g_signal_connect(gtkw_x, "value-changed", 
		   G_CALLBACK(global_resource_callback), "x");   

  gtkw_y = gtk_spin_button_new_with_range(-99.99, 99.99, 0.05);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_y), cartesian_values[1]);
  g_signal_connect(gtkw_y, "value-changed", 
		   G_CALLBACK(global_resource_callback), "y");   

  gtkw_z = gtk_spin_button_new_with_range(-99.99, 99.99, 0.05);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_z), cartesian_values[2]);
  g_signal_connect(gtkw_z, "value-changed", 
		   G_CALLBACK(global_resource_callback), "z");   

  gtk_box_pack_start(GTK_BOX(hbox), label_x, TRUE, TRUE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_x, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), label_y, TRUE, TRUE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_y, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), label_z, TRUE, TRUE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_z, FALSE, FALSE, 1);

  return hbox;
}

/* Creates the hbox containing the color_wheel_angle button */
static GtkWidget *gtk_spin_create_color_wheel(float color_wheel_angle)
{
  GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
  GtkWidget* label = gtk_label_new(_("Rotate color wheel : "));

  gtkw_color_wheel_angle = gtk_spin_button_new_with_range(0, 360., 3);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_color_wheel_angle), color_wheel_angle);
  g_signal_connect(gtkw_color_wheel_angle, "value-changed",
		   G_CALLBACK(global_resource_callback), "color_wheel_angle");
  gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(gtkw_color_wheel_angle), TRUE);

  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_color_wheel_angle, FALSE, FALSE, 1);

  return hbox;
}

/* Creates the hbox containing the color cone parameters buttons */
static GtkWidget *gtk_spin_create_rotate_axe(float *spherical_values)
{
  GtkWidget* hbox = gtk_hbox_new(FALSE, 0);
  GtkWidget* label_theta = gtk_label_new("Theta :");
  GtkWidget* label_phi = gtk_label_new(" Phi : ");
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new();

  gtkw_cone_theta_angle = gtk_spin_button_new_with_range(0, 180., 3);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_theta_angle), spherical_values[1]);
  g_signal_connect(gtkw_cone_theta_angle, "value-changed",
		   G_CALLBACK(global_resource_callback), "cone_theta_angle");

  gtkw_cone_phi_angle = gtk_spin_button_new_with_range(0, 360., 3);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_phi_angle), spherical_values[2]);
  g_signal_connect(gtkw_cone_phi_angle, "value-changed",
		   G_CALLBACK(global_resource_callback), "cone_phi_angle");
  gtk_spin_button_set_wrap(GTK_SPIN_BUTTON(gtkw_cone_phi_angle), TRUE);

  gtkw_set_view = gtk_button_new_with_label (_("Set ortho."));
  gtk_tooltips_set_tip(tooltips, gtkw_set_view,
		       _("Set the cone orientation to be orthogonal to the screen."), NULL);
  g_signal_connect(gtkw_set_view, "clicked",
		   G_CALLBACK(set_view), NULL);

  gtk_box_pack_start(GTK_BOX(hbox), label_theta, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_cone_theta_angle, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), label_phi, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_cone_phi_angle, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox), gtkw_set_view, TRUE, FALSE, 1);

  return hbox;
}
  
/* Creates the vbox displayed in the config panel */
GtkWidget* createGtkConfigForSpinMethod()
{
  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
  GtkWidget *label = gtk_label_new(_("Color distribution options : "));
  GtkWidget *label2 = gtk_label_new(_("Color cone orientation : "));
  float cartesian[3];
  float spherical[3];
  float *cone_theta_angle = rspin_global_resource_get("cone_theta_angle");  
  float *cone_phi_angle = rspin_global_resource_get("cone_phi_angle");  
  float *color_wheel_angle = rspin_global_resource_get("color_wheel_angle");  

  DBG_fprintf(stderr, "Gtk Spin : building specific spin rendering method config widget.\n");

  spherical[0] = 1;
  spherical[1] = *cone_theta_angle;
  spherical[2] = *cone_phi_angle;

  spherical_to_cartesian(cartesian, spherical);

  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 3);
  gtk_box_pack_start(GTK_BOX(vbox), gtk_spin_create_color_wheel(*color_wheel_angle), FALSE, FALSE, 3);
  gtk_widget_set_name(label, "label_head_2");

  gtk_box_pack_start(GTK_BOX(vbox), label2, FALSE, FALSE, 3);
  gtk_box_pack_start(GTK_BOX(vbox), gtk_spin_create_direction(cartesian), FALSE, FALSE, 3);
  gtk_box_pack_start(GTK_BOX(vbox), gtk_spin_create_rotate_axe(spherical), FALSE, FALSE, 3);
  gtk_widget_set_name(label2, "label_head_2");

  gtk_widget_show_all(vbox);

  configResourcesLoadedId = g_signal_connect(G_OBJECT(visu), "resourcesLoaded",
					     G_CALLBACK(sync_global_resources), NULL);

  return vbox;
}
void destroyGtkConfigForSpinMethod()
{
  DBG_fprintf(stderr, "Gtk Spin : remove signal from config rendering widget.\n");
  g_signal_handler_disconnect(G_OBJECT(visu), configResourcesLoadedId);
}

/* Initialise the gtk methods associated with
   the spin rendering method. */
void initSpin_gtkMain()
{
  gtkMainSet_renderingSpecificMethods(pointerOnRenderingSpinMethod,
				      (createGtkWidgetFunc)0,
				      gtkSpinCreate_fileChooser);
}


/* Creates a custom file chooser which allows the user to select a spin and a position file */
int gtkSpinCreate_fileChooser(VisuData *data, GtkFileChooser **fileChooser,
			      GtkWidget **fileWidget)
{
  GtkWidget *fileSelection;
  GList *filters, *tmpLst, *tmpLst2;
  GtkWidget *dialog;
  gchar* directory, *filename;
  GtkFileFilter *filterPos, *filterSpin, *filterAll;
  GtkWidget *table, *hbox, *align;
  GtkWidget *label, *button, *image;
  GtkWidget *labelPosition, *labelSpin;
  GtkWidget *radioPosition, *radioSpin;
  GtkWidget *frame;
  GtkWidget *hiding_mode;

  struct FileChooserSpin_struct fileChooserSpin;
  gint returnVal;


  if (!data)
    return 0;

  dialog = gtk_dialog_new_with_buttons(_("Load session"),
				       GTK_WINDOW(mainWindow),
				       GTK_DIALOG_MODAL,
				       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
				       GTK_STOCK_OPEN, GTK_RESPONSE_OK,
				       NULL);
  *fileWidget = dialog;

  label = gtk_label_new(_("Select a new file:"));
  gtk_widget_set_name(label, "label_head_2");
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 2);

  tmpLst = renderingMethodGet_fileType(pointerOnRenderingSpinMethod, FILE_KIND_POSITION);
  filterPos = gtk_file_filter_new ();
  while(tmpLst)
    {
      tmpLst2 = ((FileFormat*)tmpLst->data)->fileType;
      while (tmpLst2)
	{
	  gtk_file_filter_add_pattern (filterPos, (char*)tmpLst2->data);
	  tmpLst2 = g_list_next(tmpLst2);
	}
      tmpLst = g_list_next(tmpLst);
    }
  gtk_file_filter_set_name(filterPos,
			   renderingMethodGet_fileTypeName(pointerOnRenderingSpinMethod,
							   FILE_KIND_POSITION));

  tmpLst = renderingMethodGet_fileType(pointerOnRenderingSpinMethod, FILE_KIND_SPIN);
  filterSpin = gtk_file_filter_new ();
  while(tmpLst)
    {
      tmpLst2 = ((FileFormat*)tmpLst->data)->fileType;
      while (tmpLst2)
	{
	  gtk_file_filter_add_pattern (filterSpin, (char*)tmpLst2->data);
	  tmpLst2 = g_list_next(tmpLst2);
	}
      tmpLst = g_list_next(tmpLst);
    }
  gtk_file_filter_set_name(filterSpin,
			   renderingMethodGet_fileTypeName(pointerOnRenderingSpinMethod,
							   FILE_KIND_SPIN));
  filterAll = gtk_file_filter_new ();
  gtk_file_filter_add_pattern (filterAll, "*");
  gtk_file_filter_set_name(filterAll, _("All files"));
  
  fileSelection = gtk_file_chooser_widget_new(GTK_FILE_CHOOSER_ACTION_OPEN);
  *fileChooser = GTK_FILE_CHOOSER(fileSelection);
  gtk_widget_set_size_request(fileSelection, 650, 300);
  gtk_widget_show(fileSelection);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), fileSelection, TRUE, TRUE, 2);
  g_signal_connect(G_OBJECT(fileSelection), "file-activated",
		   G_CALLBACK(fileSelectedForSpinMethod), (gpointer)&fileChooserSpin);

  directory = getLastOpenDirectory();
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fileSelection), directory);
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fileSelection),
			      filterPos);
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fileSelection),
			      filterSpin);
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fileSelection),
			      filterAll);
  gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fileSelection), filterPos);

  label = gtk_label_new(_("Current files :"));
  gtk_widget_set_name(label, "label_head_2");
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, FALSE, FALSE, 2);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, FALSE, FALSE, 2);

  /* The table for the two file types selections (spin & position). */
  table = gtk_table_new(2, 3, FALSE);
  gtk_widget_show(table);
  gtk_box_pack_start(GTK_BOX(hbox), table, TRUE, TRUE, 2);
  
  /* Position button. */
  radioPosition = gtk_radio_button_new_with_label(NULL, _("position"));
  gtk_widget_show(radioPosition);
  gtk_table_attach(GTK_TABLE(table), radioPosition, 0, 1, 0, 1, GTK_SHRINK, GTK_SHRINK, 2, 0);
/*   g_signal_connect(G_OBJECT(radioPosition), "toggled", */
/* 		   G_CALLBACK(fileKindChangedForPosition), (gpointer)&fileChooserSpin.fileKind); */
  g_signal_connect(G_OBJECT(radioPosition), "toggled",
		   G_CALLBACK(fileKindChangedForPosition), (gpointer)&fileChooserSpin);

  /* Spin button */
  radioSpin = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radioPosition),
							  _("spin"));
  gtk_widget_show(radioSpin);
  gtk_table_attach(GTK_TABLE(table), radioSpin, 0, 1, 1, 2, GTK_SHRINK, GTK_SHRINK, 2, 0);
/*   g_signal_connect(G_OBJECT(radioSpin), "toggled", */
/* 		   G_CALLBACK(fileKindChangedForSpin), (gpointer)&fileChooserSpin.fileKind); */
  g_signal_connect(G_OBJECT(radioSpin), "toggled",
		   G_CALLBACK(fileKindChangedForSpin), (gpointer)&fileChooserSpin);

  /* Hiding mode button */
  hiding_mode = gtk_check_button_new_with_label ("Hide elements containing nodes with null or negative module");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hiding_mode),
			       *(int *)rspin_global_resource_get("hiding_mode"));
  gtk_widget_show(hiding_mode);
  gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hiding_mode, FALSE, FALSE, 2);
  g_signal_connect(G_OBJECT(hiding_mode), "toggled",
		   G_CALLBACK(global_resource_callback), "hiding_mode");
  

  /* Layout labels */
  label = gtk_label_new(":");
  gtk_widget_show(label);
  gtk_table_attach(GTK_TABLE(table), label, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 5, 0);

  label = gtk_label_new(":");
  gtk_widget_show(label);
  gtk_table_attach(GTK_TABLE(table), label, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 5, 0);

  labelPosition = gtk_label_new(_("None"));
  gtk_misc_set_alignment(GTK_MISC(labelPosition), 0., 0.5);
  gtk_widget_show(labelPosition);
  gtk_table_attach(GTK_TABLE(table), labelPosition, 2, 3, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);

  labelSpin = gtk_label_new(_("None"));
  gtk_misc_set_alignment(GTK_MISC(labelSpin), 0., 0.5);
  gtk_widget_show(labelSpin);
  gtk_table_attach(GTK_TABLE(table), labelSpin, 2, 3, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);

  /* Right aligned apply button. */
  align = gtk_alignment_new(0.5, 1.0, 0., 0.);
  gtk_widget_show(align);
  gtk_box_pack_start(GTK_BOX(hbox), align, FALSE, FALSE, 2);
  button = gtk_button_new_from_stock(GTK_STOCK_APPLY);
  gtk_widget_show(button);
  gtk_container_add(GTK_CONTAINER(align), button);
  g_signal_connect(G_OBJECT(button), "clicked",
		   G_CALLBACK(fileSelectedForSpinMethod), (gpointer)&fileChooserSpin);

/*   frame = gtk_frame_new(NULL); */
/*   gtk_widget_show (frame); */
/*   gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), frame, TRUE, TRUE, 0); */

/*   label = gtk_label_new(_("The spin method uses 2 files: one stores the position and the other is used for the spin orientation. These two files are required to render a spin configurations. Either position or spin file can be kept between two views if they have the same number of nodes.")); */
/*   gtk_widget_show(label); */
/*   gtk_container_add(GTK_CONTAINER(frame), label); */
/*   gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); */
/*   gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); */
/*   gtk_misc_set_padding(GTK_MISC(label), 5, 5); */

/*   hbox = gtk_hbox_new(FALSE, 0); */
/*   gtk_widget_show(hbox); */
/*   gtk_frame_set_label_widget(GTK_FRAME(frame), hbox); */
/*   image = gtk_image_new_from_stock(GTK_STOCK_HELP, GTK_ICON_SIZE_SMALL_TOOLBAR); */
/*   gtk_widget_show(image); */
/*   gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); */
/*   label = gtk_label_new (_("Help")); */
/*   gtk_widget_show(label); */
/*   gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); */
  

  gtk_widget_set_name(dialog, "filesel");
  gtk_window_set_position(GTK_WINDOW(dialog), GTK_WIN_POS_CENTER_ON_PARENT);

  fileChooserSpin.label[FILE_KIND_POSITION] = GTK_LABEL(labelPosition);
  fileChooserSpin.label[FILE_KIND_SPIN] = GTK_LABEL(labelSpin);
  fileChooserSpin.radio[FILE_KIND_POSITION] = GTK_RADIO_BUTTON(radioPosition);
  fileChooserSpin.radio[FILE_KIND_SPIN] = GTK_RADIO_BUTTON(radioSpin);
  fileChooserSpin.dialog = GTK_DIALOG(dialog);
  fileChooserSpin.fileChooser = GTK_FILE_CHOOSER(fileSelection);
  fileChooserSpin.fileKind = FILE_KIND_POSITION;
  fileChooserSpin.files[FILE_KIND_POSITION] = (gchar*)0;
  fileChooserSpin.files[FILE_KIND_SPIN] = (gchar*)0;
  

  do
    {
      returnVal = gtk_dialog_run(GTK_DIALOG(dialog));
      if (returnVal == GTK_RESPONSE_OK)
	{
	  fileSelectedForSpinMethod((GtkButton*)0, (gpointer)&fileChooserSpin);
	  if (!fileChooserSpin.files[FILE_KIND_POSITION] || !fileChooserSpin.files[FILE_KIND_SPIN])
	    {
	      raiseAlertDialog(_("One position AND one spin file have to be chosen."));
	      returnVal = 0;
	    }
	  else
	    returnVal = 1;
	}
      else
	returnVal = -1;
    }
  while (returnVal == 0);

  if (returnVal > 0)
    {
      visuDataAdd_file(data, fileChooserSpin.files[FILE_KIND_POSITION], FILE_KIND_POSITION);
      visuDataAdd_file(data, fileChooserSpin.files[FILE_KIND_SPIN], FILE_KIND_SPIN);
      g_free(fileChooserSpin.files[FILE_KIND_POSITION]);
      g_free(fileChooserSpin.files[FILE_KIND_SPIN]);
      return 1;
    }
  else
    return 0;
}

void fileActivatedForSpinMethod(GtkFileChooser *file, gpointer data)
{
  fileSelectedForSpinMethod((GtkButton*)0, data);
}

void fileSelectedForSpinMethod(GtkButton *button, gpointer data)
{
  gchar *filename = NULL, *filenameUTF8;
  struct FileChooserSpin_struct *values;
  int res;
  GString *message;

  values = (struct FileChooserSpin_struct*)data;
  filename = gtk_file_chooser_get_filename(values->fileChooser);
  if (!g_file_test(filename, G_FILE_TEST_IS_DIR) && filename != NULL)
    {
      DBG_fprintf(stderr, "Gtk Spin : select '%s' as file %d\n", filename, values->fileKind);
      res = 0;
      values->files[values->fileKind] = filename;
      filenameUTF8 = getStringInUTF8(filename);
      message = g_string_new("");
      if (g_utf8_strlen(filenameUTF8, 51) > 50)
	g_string_printf(message, "(...)%s",
			 g_utf8_offset_to_pointer(filenameUTF8,
						  g_utf8_strlen(filenameUTF8, -1) - 50));
      else
	g_string_printf(message, "%s", filenameUTF8);
      gtk_label_set_text(values->label[values->fileKind], message->str);
      g_string_free(message, TRUE);
      g_free(filenameUTF8);
      if (values->fileKind == FILE_KIND_POSITION)
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(values->radio[FILE_KIND_SPIN]), TRUE);
      else if (values->fileKind == FILE_KIND_SPIN)
	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(values->radio[FILE_KIND_POSITION]), TRUE);
      
      if (values->files[FILE_KIND_POSITION] && values->files[FILE_KIND_SPIN])
	gtk_dialog_response(values->dialog, GTK_RESPONSE_OK);
    }
}

void fileKindChangedForPosition(GtkToggleButton *button, gpointer data)
{
  struct FileChooserSpin_struct* param = data;
  GSList *tmpLst;

  if (!gtk_toggle_button_get_active(button))
    return;

  tmpLst = gtk_file_chooser_list_filters(param->fileChooser);
  gtk_file_chooser_set_filter(param->fileChooser, (GtkFileFilter*)(tmpLst->data));
  param->fileKind = FILE_KIND_POSITION;

}

void fileKindChangedForSpin(GtkToggleButton *button, gpointer data)
{
  struct FileChooserSpin_struct* param = data;
  GSList *tmpLst;

  if (!gtk_toggle_button_get_active(button))
    return;

  tmpLst = gtk_file_chooser_list_filters(param->fileChooser);
  gtk_file_chooser_set_filter(param->fileChooser, (GtkFileFilter*)(tmpLst->next->data));
  param->fileKind = FILE_KIND_SPIN;
}

/* Create the gtk widgets (a hbox with a spin with
   positive values) and return it. */
GtkWidget* createGtkInterfaceForSpinMethod()
{
  GtkWidget* hbox[6], *vbox;
  GtkWidget* shapeLabel, *hLabel, *rhLabel, *lLabel, *rlLabel;
  GtkWidget* cone_theta_label, *cone_phi_label, *color_wheel_label;
  float *value;
  const char **names = NULL;
  const char *temp = NULL;
  int i, j = 0;

  vbox = gtk_vbox_new(FALSE, 0);

  for(i=0; i<6; i++)
    {
      hbox[i] = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(vbox), hbox[i], FALSE, FALSE, 2);
      gtk_widget_show(hbox[i]);
    }

  /* Labels creation */

  shapeLabel = gtk_label_new(_("Arrow shape :"));
  gtk_widget_show(shapeLabel);

  labelSizeProp = gtk_label_new("");
  if (panelConfigGet_spinBoundsValue() == 1.)
    gtk_label_set_text(GTK_LABEL(labelSizeProp), LABEL_ARROW_SIZE);
  else
    {
      gtk_label_set_text(GTK_LABEL(labelSizeProp), LABEL_ARROW_SIZE_MULT);
      gtk_label_set_use_markup(GTK_LABEL(labelSizeProp), TRUE);
    }
  gtk_widget_show(labelSizeProp);

  hLabel = gtk_label_new(_("Hat length : "));
  gtk_widget_show(hLabel);

  rhLabel = gtk_label_new(_("Tail length : "));
  gtk_misc_set_alignment(GTK_MISC(rhLabel), 1.0, 0.5);
  gtk_widget_show(rhLabel);

  lLabel = gtk_label_new(_("Hat radius : "));
  gtk_widget_show(lLabel);

  rlLabel = gtk_label_new(_("Tail radius : "));
  gtk_misc_set_alignment(GTK_MISC(rlLabel), 1.0, 0.5);
  gtk_widget_show(rlLabel);

  /* Spin buttons creation */

  heightResourceSpin = gtk_spin_button_new_with_range(0, 9, 0.05);
  element_callback_ids[0] = g_signal_connect((gpointer)heightResourceSpin, "value-changed",
		   G_CALLBACK(element_resource_callback), "element_height");
  gtk_widget_show(heightResourceSpin);

  rheightResourceSpin = gtk_spin_button_new_with_range(0, 9, 0.05);
  element_callback_ids[1] = g_signal_connect((gpointer)rheightResourceSpin, "value-changed",
		   G_CALLBACK(element_resource_callback), "element_u_height");
  gtk_widget_show(rheightResourceSpin);

  lengthResourceSpin = gtk_spin_button_new_with_range(0, 9, 0.05);
  element_callback_ids[2] = g_signal_connect((gpointer)lengthResourceSpin, "value-changed",
		   G_CALLBACK(element_resource_callback), "element_length");
  gtk_widget_show(lengthResourceSpin);

  rlengthResourceSpin = gtk_spin_button_new_with_range(0, 9, 0.05);
  element_callback_ids[3] = g_signal_connect((gpointer)rlengthResourceSpin, "value-changed",
		   G_CALLBACK(element_resource_callback), "element_u_length");
  gtk_widget_show(rlengthResourceSpin);

  /* This callback is for multiplier */
  g_signal_connect(G_OBJECT(visuGtkObject), "spinBoundsChanged",
		   G_CALLBACK(onSpinBoundsChangeArrowSize), (gpointer)0);


  /* Check box creation */
  gtkw_use_element_color = gtk_check_button_new_with_label("Use element color for the tail");
  element_callback_ids[4] = g_signal_connect(gtkw_use_element_color, "toggled", 
		   G_CALLBACK(element_resource_callback), "use_element_color");   
  gtk_widget_show(gtkw_use_element_color);

  gtkw_use_element_color_hat = gtk_check_button_new_with_label("Use element color for the hat");
  element_callback_ids[5] = g_signal_connect(gtkw_use_element_color_hat, "toggled", 
		   G_CALLBACK(element_resource_callback), "use_element_color_hat");   
  gtk_widget_show(gtkw_use_element_color_hat);

  /* Combo boxes creation */
  gtkw_element_shape_number = gtk_combo_box_new_text();
  for (i=0; i<rspin_get_number_of_shapes(); i++)
    gtk_combo_box_append_text(GTK_COMBO_BOX(gtkw_element_shape_number), rspin_shape_number_to_name(i));
  /* set callback for the combo button. */
  element_callback_ids[6] = g_signal_connect(gtkw_element_shape_number, "changed",
		   G_CALLBACK(element_resource_callback), "element_shape_number");
  gtk_widget_show(gtkw_element_shape_number);

  /* Displaying every widget created so far */

  gtk_box_pack_start(GTK_BOX(hbox[j]), shapeLabel, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox[j++]), gtkw_element_shape_number, FALSE, FALSE, 3);

  gtk_box_pack_start(GTK_BOX(hbox[j++]), labelSizeProp, FALSE, FALSE, 1);

  gtk_box_pack_start(GTK_BOX(hbox[j]), hLabel, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox[j]), heightResourceSpin, FALSE,FALSE, 3);
  gtk_box_pack_start(GTK_BOX(hbox[j]), rhLabel, TRUE, TRUE, 1);
  gtk_box_pack_start(GTK_BOX(hbox[j++]), rheightResourceSpin, FALSE,FALSE, 3);

  gtk_box_pack_start(GTK_BOX(hbox[j]), lLabel, FALSE, FALSE, 1);
  gtk_box_pack_start(GTK_BOX(hbox[j]), lengthResourceSpin, FALSE,FALSE, 3);
  gtk_box_pack_start(GTK_BOX(hbox[j]), rlLabel, TRUE, TRUE, 1);
  gtk_box_pack_start(GTK_BOX(hbox[j++]), rlengthResourceSpin, FALSE,FALSE, 3);

  gtk_box_pack_start(GTK_BOX(hbox[j++]), gtkw_use_element_color_hat, FALSE, FALSE, 1);

  gtk_box_pack_start(GTK_BOX(hbox[j++]), gtkw_use_element_color, FALSE, FALSE, 1);

  g_signal_connect(G_OBJECT(visu), "resourcesLoaded",
		   G_CALLBACK(sync_local_resources), NULL);

  return vbox;
}

/* This function is called whenever the current element is changed in the gtk panel. */
void changeSomethingSpin(VisuElement *ele)
{
  const float *element_length = rspin_get_element_resource(ele, "element_length");
  const float *element_u_length = rspin_get_element_resource(ele, "element_u_length");
  const float *element_height = rspin_get_element_resource(ele, "element_height");
  const float *element_u_height = rspin_get_element_resource(ele, "element_u_height");
  const int *use_element_color = rspin_get_element_resource(ele, "use_element_color");
  const int *use_element_color_hat = rspin_get_element_resource(ele, "use_element_color_hat");
  const int *element_shape_number = rspin_get_element_resource(ele, "element_shape_number");
  float mult;
  int i;

  currentElementName = ele->name;
  current_element = ele;

  need_redraw = 0;

  mult = panelConfigGet_spinBoundsValue();
  
  g_signal_handler_block (heightResourceSpin, element_callback_ids[0] );  
  g_signal_handler_block (rheightResourceSpin, element_callback_ids[1] );  
  g_signal_handler_block (lengthResourceSpin, element_callback_ids[2] );  
  g_signal_handler_block (rlengthResourceSpin, element_callback_ids[3] );  
  g_signal_handler_block (gtkw_use_element_color, element_callback_ids[4] );  
  g_signal_handler_block (gtkw_use_element_color_hat, element_callback_ids[5] );  
  g_signal_handler_block (gtkw_element_shape_number, element_callback_ids[6] );  

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(heightResourceSpin), *element_height / mult);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(rheightResourceSpin), *element_u_height / mult);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(lengthResourceSpin), *element_length / mult);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(rlengthResourceSpin), *element_u_length / mult);
  
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkw_use_element_color), *use_element_color);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkw_use_element_color_hat), *use_element_color_hat);

  if (*element_shape_number >= 0)
    {
      gtk_combo_box_set_active(GTK_COMBO_BOX(gtkw_element_shape_number), *element_shape_number);
    }

  g_signal_handler_unblock (heightResourceSpin, element_callback_ids[0] );  
  g_signal_handler_unblock (rheightResourceSpin, element_callback_ids[1] );  
  g_signal_handler_unblock (lengthResourceSpin, element_callback_ids[2] );  
  g_signal_handler_unblock (rlengthResourceSpin, element_callback_ids[3] );  
  g_signal_handler_unblock (gtkw_use_element_color, element_callback_ids[4] );  
  g_signal_handler_unblock (gtkw_use_element_color_hat, element_callback_ids[5] );  
  g_signal_handler_unblock (gtkw_element_shape_number, element_callback_ids[6] );  

  need_redraw = 1;
}

void onSpinBoundsChangeArrowSize(GObject *obj, gfloat mult, gpointer data)
{
  int i, j;
  float val;
  char *props[5] = {"element_length", "element_height", "element_u_height",
		    "element_u_length", (char*)0};
  GenericRenderingWindow window;
  VisuData *dataObj;

  if (getRenderingMethodInUse() != pointerOnRenderingSpinMethod)
    return;

  if (panelConfigGet_spinBoundsValue() == 1.)
    gtk_label_set_text(GTK_LABEL(labelSizeProp), LABEL_ARROW_SIZE);
  else
    {
      gtk_label_set_text(GTK_LABEL(labelSizeProp), LABEL_ARROW_SIZE_MULT);
      gtk_label_set_use_markup(GTK_LABEL(labelSizeProp), TRUE);
    }

  window = visuRenderingWindowGet_current();
  if (!window)
    return;
    
  dataObj = visuRenderingWindowGet_visuData(window);
  for (i = 0; i < dataObj->ntype; i++)
    {
      for (j = 0; props[j]; j++)
	{
	  val = *(float *)rspin_get_element_resource(dataObj->fromIntToVisuElement[i],
						     props[j]);
	  rspin_set_element_resources_f(dataObj->fromIntToVisuElement[i],
					val * mult, props[j]);
	}
      rspin_createShapeSpin(visuDataGet_openGLView(dataObj),
			    dataObj->fromIntToVisuElement[i]);
    }
}



/* This is the callback func for each element resource. */
void element_resource_callback(GtkWidget* widget, char *property)
{
  if(property == NULL || current_element == NULL)
    return;

  if(strcmp(property, "use_element_color")==0
     || strcmp(property, "use_element_color_hat")==0)
    {
      int new_value = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));

      DBG_fprintf(stderr, "GtkSpin : Set element '%s' property to %d\n", property, new_value);
      rspin_set_element_resources_d(current_element, new_value, property);
    }
  else if(strcmp(property, "element_shape_number")==0)
    {
      int new_value = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));

      DBG_fprintf(stderr, "GtkSpin : Set element '%s' property to %d\n", property, new_value);
      rspin_set_element_resources_d(current_element, new_value, property);
    }
  else if(strcmp(property, "element_length") == 0 
	  || strcmp(property, "element_height") == 0
	  || strcmp(property, "element_u_height") == 0
	  || strcmp(property, "element_u_length") == 0)
    {
      float new_value = gtk_spin_button_get_value(GTK_SPIN_BUTTON(widget));
      float mult = panelConfigGet_spinBoundsValue();

      DBG_fprintf(stderr, "GtkSpin : Set element '%s' property to %f\n", property, new_value * mult);
      rspin_set_element_resources_f(current_element, new_value * mult, property);
    }  
  else
    {
      DBG_fprintf(stderr, _("\nWARNING : unknown property\n\n"));
      return;
    }

  if(need_redraw)
    redraw(1);
}

/* This is the callback func for each global resource. */
void global_resource_callback(GtkWidget* button, gpointer data)
{
  if(strcmp(data, "cone_theta_angle") == 0||
     strcmp(data, "cone_phi_angle") == 0||
     strcmp(data, "color_wheel_angle") == 0)
    {
      float new_value = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(button));
      float *value_storage = rspin_global_resource_get(data);  
      char *property = data;
      
      DBG_fprintf(stderr, "GtkSpin : Set global '%s' property to %f\n", property, new_value);
      if(value_storage == NULL || button == NULL || data == NULL)
	return;

      *value_storage = new_value;  

      if(need_redraw)
	{
	  float spherical[3];
	  float cartesian[3];
	  int i;

	  need_redraw = 0;
	  
	  spherical[0] = 1;
	  spherical[1] = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtkw_cone_theta_angle));
	  spherical[2] = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtkw_cone_phi_angle));

	  spherical_to_cartesian(cartesian, spherical);

	  for(i=0; i<3; i++)
	    if(cartesian[i] < 0.01 && cartesian [i] > -0.01)
	      cartesian[i] = 0;
	  
	  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_x), cartesian[0]);
	  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_y), cartesian[1]);
	  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_z), cartesian[2]);

	  need_redraw = 1;
	}
    }
  else if(strcmp(data, "x") == 0||
     strcmp(data, "y") == 0||
     strcmp(data, "z") == 0)
    {
      float cartesian[3];
      float spherical[3];
      float x = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtkw_x));
      float y = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtkw_y));
      float z = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(gtkw_z));
      
      float *cone_theta_angle = rspin_global_resource_get("cone_theta_angle");  
      float *cone_phi_angle = rspin_global_resource_get("cone_phi_angle");  
      
      if(button == NULL || data == NULL)
	return;


      if(need_redraw)
	{
	  need_redraw = 0;

	  cartesian[0] = x;
	  cartesian[1] = y;
	  cartesian[2] = z;
	  
	  cartesian_to_spherical(spherical, cartesian);

	  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_theta_angle), spherical[1]);
	  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_phi_angle), spherical[2]);

	  need_redraw = 1;
	}
    }
  else if(strcmp(data, "hiding_mode") == 0)
    {
      gboolean button_state = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button));
      *(int *)rspin_global_resource_get("hiding_mode") = button_state;
      return;
    }      
  else
    {
      DBG_fprintf(stderr, _("\nWARNING : unknown callback\n\n"));
      return;
    }

  if(need_redraw == 1)
    redraw(2);
}

void set_view(GtkWidget* button, gpointer data)
{
  float theta;
  float phi;
  RenderingWindow *window;
  VisuData *dataObj;
  OpenGLView *view;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);
  dataObj = renderingWindowGet_visuData(window);
  if (!dataObj)
    return;
  view = visuDataGet_openGLView(dataObj);

  theta = fModulo(view->camera->theta, 360);
  if(theta > 180)
      theta = 360 - theta;
  phi = fModulo(view->camera->phi, 360);

  need_redraw = 0;

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_theta_angle), theta);

  need_redraw = 1;

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_phi_angle), phi);
}

/* i = 1 if called by a function which modified an element resource
   i = 2 if called by a function which modified a global resource */
void redraw(int i)
{
  GenericRenderingWindow window;
  VisuData *dataObj;

  if(current_element == NULL)
    return;

  DBG_fprintf(stderr, "Gtk Spin : Updating rendering area.\n");

  window = visuRenderingWindowGet_current();
  g_return_if_fail(window);

  dataObj = visuRenderingWindowGet_visuData(window);
  if (!dataObj)
    return;

  if(i == 1)
    rspin_createShapeSpin(visuDataGet_openGLView(dataObj),
			  current_element);

  visuData_createAllNodes(dataObj);  
  
  g_signal_emit (visu, VISU_GET_CLASS (visu)->SpinAxes_signal_id,
		 0 , dataObj, NULL);
  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		 0 , NULL); 
}

static void sync_global_resources(GObject *object, gpointer data)
{
  const float *color_wheel_angle = rspin_global_resource_get("color_wheel_angle");
  const float *cone_phi_angle = rspin_global_resource_get("cone_phi_angle");
  const float *cone_theta_angle = rspin_global_resource_get("cone_theta_angle");
  float spherical[3];
  float cartesian[3];
  int i;

  DBG_fprintf(stderr, "Gtk Spin : caught the 'resourcesLoaded' signal.\n");

  if(gtkw_cone_theta_angle == NULL)
    return;

  spherical[0] = 1;
  spherical[1] = *cone_theta_angle; 
  spherical[2] = *cone_phi_angle;

  spherical_to_cartesian(cartesian, spherical);

  for(i=0; i<3; i++)
    if(cartesian[i] < 0.01 && cartesian [i] > -0.01)
      cartesian[i] = 0;

  need_redraw = 0;

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_x), cartesian[0]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_y), cartesian[1]);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_z), cartesian[2]);

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_theta_angle), *cone_theta_angle);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_cone_phi_angle), *cone_phi_angle); 
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkw_color_wheel_angle), *color_wheel_angle); 

  need_redraw = 1;  
}      

static void sync_local_resources(GObject *trash, gpointer data)
{
  const float *element_length;
  const float *element_u_length;
  const float *element_height;
  const float *element_u_height;
  const int *use_element_color;
  const int *use_element_color_hat;
  const int *element_shape_number;

  if(heightResourceSpin == NULL || current_element == NULL)
    return;
  
  element_length = rspin_get_element_resource(current_element, "element_length");
  element_u_length = rspin_get_element_resource(current_element, "element_u_length");
  element_height = rspin_get_element_resource(current_element, "element_height");
  element_u_height = rspin_get_element_resource(current_element, "element_u_height");
  use_element_color = rspin_get_element_resource(current_element, "use_element_color");
  use_element_color_hat = rspin_get_element_resource(current_element, "use_element_color_hat");
  element_shape_number = rspin_get_element_resource(current_element, "element_shape_number");
  
  need_redraw = 0;

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(heightResourceSpin), *element_height);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(rheightResourceSpin), *element_u_height);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(lengthResourceSpin), *element_length);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(rlengthResourceSpin), *element_u_length);

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkw_use_element_color), *use_element_color); 
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtkw_use_element_color_hat), *use_element_color_hat); 



  need_redraw = 1;
}
