/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD et Damien
	CALISTE, laboratoire L_Sim, (2001-2006)
  
	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-2006)

	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 "panelPlanes.h"

#include <GL/gl.h>
#include <GL/glu.h> 

#include <visu_object.h>
#include <support.h>
#include <gtk_main.h>
#include <openGLFunctions/objectList.h>
#include <visu_extension.h>
#include <renderingBackend/visu_windowInterface.h>
#include <gtk_renderingWindowWidget.h>

#include <extraFunctions/plane.h>
#include <extraGtkFunctions/gtk_colorComboBoxWidget.h>
#include <extraGtkFunctions/gtk_numericalEntryWidget.h>

#include "visuConfig.h"

#define PANEL_PLANES_NO_PLANE_LIST _("<span font_desc=\"smaller\"><i>none</i></span>")

GtkWidget *vBoxPlanes;
GtkWidget *checkUsePlanes;
GtkWidget *spinbuttonNVect[3];
GtkWidget *spinbuttonDistance;
GtkWidget *widgetColorPlane;
GtkWidget *treeviewPlanes;
GtkListStore *listStoredPlanes;
GtkWidget *labelDistance;
GtkWidget *buttonRotate;
GtkWidget *hboxHidingMode;
GtkWidget *vboxDistChange;
GtkWidget *entryDistFrom, *entryDistTo, *entryDistStep, *spinDistDelay;
GtkWidget *imageDistPlay, *imageDistStop;


/* Variables for the IO tab. */
GtkWidget *hboxSaveList;
gchar *currentSaveListFile;
GtkWidget *buttonSaveList;
GtkWidget *labelPlaneList;

/* Callbacks for the IO tab. */
static void onPlaneListOpen(GtkButton *button, gpointer data);
static void onPlaneListSave(GtkButton *button, gpointer data);
static void onPlaneListSaveAs(GtkButton *button, gpointer data);

gulong onAskForHide_signal_id;
guint isPlayingDistanceId;
gdouble directionDist;

/* Local callbacks. */
gboolean checkPlanesOnEnter(gpointer data);
void onPlaneUsed(GtkToggleButton *button, gpointer data);
void onPlaneAdd(GtkButton *button, gpointer data);
void onPlaneRemove(GtkButton *button, gpointer data);
void onSpinNVectChange(GtkSpinButton *spin, gpointer data);
void onSpinDistanceChange(GtkSpinButton *spin, gpointer data);
void onPlaneColorChange(ColorComboBox *combo, Color *selectedColor, gpointer data);
void rebuildListPlanes(VisuData *dataObj);
void onDataReadyForRendering(GObject *obj, VisuData *dataObj, gpointer data);
void onNewFileLoaded(GObject *obj, VisuData *dataObj, gpointer data);
void onTreeSelectionChanged(GtkTreeSelection *tree, gpointer data);
void onNodePositionChanged(VisuData *dataObj, gpointer data);
void onSetCameraPosition(GtkButton *button, gpointer data);
void onSpinBoundsChangeDistance(GObject *obj, gfloat val, gpointer data);
void onGtkPlanesDrawnToggled(GtkCellRendererToggle *cell_renderer,
			     gchar *path, gpointer user_data);
void onGtkPlanesHideToggled(GtkCellRendererToggle *cell_renderer,
			    gchar *path, gpointer user_data);
void onGtkPlanesHidingModeToggled(GtkToggleButton *toggle, gpointer data);
static void onAskForHideNodes(VisuData *visuData, gboolean *redraw, gpointer data);
static void onPlayStopDist(GtkButton *button, gpointer data);
static void onSpinDistDelayChange(GtkSpinButton *spin, gpointer data);

/* Local methods. */
static void stopPlayStop(gpointer data);
static gboolean playDistances(gpointer data);
static gboolean stopPlayOnLeave(gpointer data);
/* Call createInteriorPlanes() to create all the widgets,
   and if dataObj is not null, it set callbacks as if
   a new VisuData has been loaded. */
static void createAndInitPlanePanel(VisuData *dataObj);
static void createInteriorPlanes();
static void loadPlaneFile(VisuData *dataObj, gchar *filename);

gboolean isPlanesInitialised;

OpenGLExtension *extensionPlanes;
int identifierAllPlanes;

enum
  {
    COLUMN_PLANE_DRAWN,
    COLUMN_PLANE_LABEL,
    COLUMN_PLANE_HIDE_IS_ON,
    COLUMN_PLANE_HIDDEN_SIDE,
    COLUMN_PLANE_COLOR_PIXBUF,
    COLUMN_PLANE_POINTER,
    N_COLUMN_PLANE
  };

/* String used to labelled planes, dist. means 'distance' and
   norm. means 'normal' (50 chars max). */
#define LABEL_PLANE _("norm.:(%3d;%3d;%3d)\ndistance: %6.2f")

subPanel* initPanelPlanes()
{
  /* Long description */
  char *cl = _("Drawing planes");
  /* Short description */
  char *tl = _("Planes");
  subPanel *panelPlanes;

  vBoxPlanes = gtk_vbox_new (FALSE, 0);
  gtk_widget_set_sensitive(vBoxPlanes, FALSE);
  panelPlanes = createSubPanelWithIconFromPath(cl, tl, "stock-channels-16.png",
					       vBoxPlanes, checkPlanesOnEnter,
					       stopPlayOnLeave);
  if (!panelPlanes)
    return (subPanel*)0;

  /* call the visu add routine */
  addSubPanel(panelPlanes);

  isPlanesInitialised = FALSE;
  checkUsePlanes = (GtkWidget*)0;
  isPlayingDistanceId = 0;
  currentSaveListFile = (gchar*)0;

  /* Book some OpenGL list to draw planes. */
  identifierAllPlanes = openGLObjectList_new(1);
  extensionPlanes = OpenGLExtension_new(_("Planes"), _("Draw some planes."),
					identifierAllPlanes, rebuildListPlanes);
  extensionPlanes->used = 0;
  OpenGLExtensionSet_priority(extensionPlanes, OPENGL_EXTENSION_PRIORITY_NORMAL);
  OpenGLExtensionSet_sensitiveToRenderingMode(extensionPlanes, TRUE);
  OpenGLExtensionRegister(extensionPlanes);

  /* Add the signal for the vBoxPlanes. */
  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		   G_CALLBACK(onDataReadyForRendering), (gpointer)0);

  return panelPlanes;
}

gboolean drawPlanes(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data)
{
  Plane *plane;
  gboolean used;

  g_return_val_if_fail(IS_VISU_DATA_TYPE(data), FALSE);

  used = FALSE;
  gtk_tree_model_get(model, iter, COLUMN_PLANE_DRAWN, &used, -1);
  if (used)
    {
      plane = (Plane*)0;
      gtk_tree_model_get(model, iter, COLUMN_PLANE_POINTER, &plane, -1);
      if (plane)
	planeDraw(visuDataGet_openGLView(VISU_DATA(data)), plane);
    }  
  return FALSE;
}

void rebuildListPlanes(VisuData *dataObj)
{
  DBG_fprintf(stderr, "Panel planes : rebuilding object list for planes.\n");
  glNewList(identifierAllPlanes, GL_COMPILE);

  /* Set blend if not present. */
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  gtk_tree_model_foreach(GTK_TREE_MODEL(listStoredPlanes), drawPlanes, (gpointer)dataObj);
  glEndList();
}

static void createAndInitPlanePanel(VisuData *dataObj)
{
  createInteriorPlanes();
  isPlanesInitialised = TRUE;
  /* Force to connect signal if a file is already loaded. */
  if (dataObj)
    onNewFileLoaded(visu, dataObj, (gpointer)0);
}

gboolean checkPlanesOnEnter(gpointer data)
{
  RenderingWindow *window;

  if (!isPlanesInitialised)
    {
      window = RENDERING_WINDOW(visuRenderingWindowGet_current());
      g_return_val_if_fail(window, FALSE);
      createAndInitPlanePanel(renderingWindowGet_visuData(window));
    }

  return FALSE;
}

static void createInteriorPlanes()
{
  GtkWidget *alignment1;
  GtkWidget *hbox1, *hbox;
  GtkWidget *image1;
  GtkWidget *label1, *label;
  GtkWidget *scrolledPlanes;
  GtkWidget *hbox2;
  GtkWidget *alignment3;
  GtkWidget *vbox3, *vboxIO;
  GtkWidget *hbox4;
  GtkWidget *label2;
  GtkObject *spinbuttonNVect_adj[3];
  GtkWidget *hbox5;
  GtkObject *spinbuttonDistance_adj;
  GtkWidget *hbox3;
  GtkWidget *label4;
  GtkWidget *alignment2;
  GtkWidget *vbox2, *vbox;
  GtkWidget *buttonPlaneAdd, *buttonDistPlayStop;
  GtkWidget *image2;
  GtkWidget *buttonPlaneRemove, *button;
  GtkWidget *image4;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkWidget *image;
  GtkTooltips *tooltips;
  GSList *radiobuttonHideUnion_group;
  GtkWidget *radiobuttonHideInter, *radiobuttonHideUnion;
  GtkWidget *notebook;

  int i;

  tooltips = gtk_tooltips_new ();

  checkUsePlanes = gtk_check_button_new ();
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkUsePlanes), extensionPlanes->used);
  gtk_widget_show (checkUsePlanes);
  gtk_box_pack_start (GTK_BOX (vBoxPlanes), checkUsePlanes, FALSE, FALSE, 0);

  alignment1 = gtk_alignment_new (0.5, 0.5, 0, 0);
  gtk_widget_show (alignment1);
  gtk_container_add (GTK_CONTAINER (checkUsePlanes), alignment1);

  hbox1 = gtk_hbox_new (FALSE, 2);
  gtk_widget_show (hbox1);
  gtk_container_add (GTK_CONTAINER (alignment1), hbox1);

  image1 = create_pixmap (mainWindow, "stock-channels-16.png");
  gtk_widget_show (image1);
  gtk_box_pack_start (GTK_BOX (hbox1), image1, FALSE, FALSE, 0);

  label1 = gtk_label_new_with_mnemonic (_("Use planes"));
  gtk_widget_set_name(label1, "label_head");
  gtk_widget_show (label1);
  gtk_box_pack_start (GTK_BOX (hbox1), label1, FALSE, FALSE, 0);

  scrolledPlanes = gtk_scrolled_window_new (NULL, NULL);
  gtk_widget_show (scrolledPlanes);
  gtk_box_pack_start (GTK_BOX (vBoxPlanes), scrolledPlanes, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledPlanes), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolledPlanes), GTK_SHADOW_IN);

  treeviewPlanes = gtk_tree_view_new ();
  gtk_widget_show (treeviewPlanes);
  gtk_container_add (GTK_CONTAINER (scrolledPlanes), treeviewPlanes);
  gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (treeviewPlanes), TRUE);

  notebook = gtk_notebook_new();
  gtk_widget_show(notebook);
  gtk_box_pack_start(GTK_BOX(vBoxPlanes), notebook, FALSE, FALSE, 2);

  /* Page 1  : simple tools*/
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_widget_show(vbox);
  label = gtk_label_new(_("Simple tools"));
  gtk_widget_show(label);
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox, label);

  /* Hiding Mode */
  hboxHidingMode = gtk_hbox_new (FALSE, 0);
  gtk_widget_show(hboxHidingMode);
  gtk_box_pack_start(GTK_BOX(vbox), hboxHidingMode, FALSE, FALSE, 2);

  label1 = gtk_label_new(_("Hiding mode: "));
  gtk_widget_show(label1);
  gtk_box_pack_start(GTK_BOX(hboxHidingMode), label1, FALSE, FALSE, 0);

  radiobuttonHideUnion = gtk_radio_button_new(NULL);
  gtk_widget_show(radiobuttonHideUnion);
  gtk_box_pack_start(GTK_BOX(hboxHidingMode), radiobuttonHideUnion, FALSE, FALSE, 0);
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radiobuttonHideUnion), (GSList*)0);
  radiobuttonHideUnion_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobuttonHideUnion));
  gtk_tooltips_set_tip(tooltips, radiobuttonHideUnion,
		       _("Hide all elements that are hidden by al least one plane."), NULL);
  hbox1 = gtk_hbox_new (FALSE, 2);
  gtk_widget_show(hbox1);
  gtk_container_add(GTK_CONTAINER(radiobuttonHideUnion), hbox1);
  image1 = create_pixmap(mainWindow, "stock-union.png");
  gtk_widget_show(image1);
  gtk_box_pack_start(GTK_BOX(hbox1), image1, FALSE, FALSE, 0);
  label1 = gtk_label_new(_("Union"));
  gtk_widget_show(label1);
  gtk_box_pack_start(GTK_BOX(hbox1), label1, FALSE, FALSE, 0);

  radiobuttonHideInter = gtk_radio_button_new(NULL);
  gtk_widget_show(radiobuttonHideInter);
  gtk_box_pack_start(GTK_BOX(hboxHidingMode), radiobuttonHideInter, FALSE, FALSE, 0);
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radiobuttonHideInter), radiobuttonHideUnion_group);
  radiobuttonHideUnion_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radiobuttonHideInter));
  gtk_tooltips_set_tip(tooltips, radiobuttonHideInter,
		       _("Hide elements only if they are hidden by all planes."), NULL);
  hbox1 = gtk_hbox_new (FALSE, 2);
  gtk_widget_show(hbox1);
  gtk_container_add(GTK_CONTAINER(radiobuttonHideInter), hbox1);
  image1 = create_pixmap(mainWindow, "stock-inter.png");
  gtk_widget_show(image1);
  gtk_box_pack_start(GTK_BOX(hbox1), image1, FALSE, FALSE, 0);
  label1 = gtk_label_new(_("Intersection"));
  gtk_widget_show(label1);
  gtk_box_pack_start(GTK_BOX(hbox1), label1, FALSE, FALSE, 0);

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radiobuttonHideUnion), TRUE);
  gtk_widget_set_sensitive(hboxHidingMode, FALSE);

  /* Planes parameters */
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 0);

  vbox3 = gtk_vbox_new (FALSE, 3);
  gtk_widget_show (vbox3);
  gtk_box_pack_start(GTK_BOX(hbox2), vbox3, FALSE, FALSE, 2);
  buttonRotate = gtk_button_new();
  gtk_tooltips_set_tip(tooltips, buttonRotate, _("Set the camera to look in the direction"
						 " of the normal of the selected plane."), NULL);
  gtk_box_pack_start(GTK_BOX(vbox3), buttonRotate, TRUE, FALSE, 0);
  gtk_widget_show(buttonRotate);
  image = create_pixmap(mainWindow, "stock_rotate_20.png");
  gtk_widget_show(image);
  gtk_container_add(GTK_CONTAINER(buttonRotate), image);
  gtk_widget_set_sensitive(buttonRotate, FALSE);
  

  alignment3 = gtk_alignment_new (0.5, 0.5, 0, 0);
  gtk_widget_show (alignment3);
  gtk_box_pack_start (GTK_BOX (hbox2), alignment3, TRUE, TRUE, 0);

  vbox3 = gtk_vbox_new (FALSE, 0);
  gtk_widget_show (vbox3);
  gtk_container_add (GTK_CONTAINER (alignment3), vbox3);

  hbox4 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox4);
  gtk_box_pack_start (GTK_BOX (vbox3), hbox4, TRUE, TRUE, 0);

  label2 = gtk_label_new (_("Normal: "));
  gtk_widget_show (label2);
  gtk_box_pack_start (GTK_BOX (hbox4), label2, TRUE, TRUE, 0);
  gtk_misc_set_alignment (GTK_MISC (label2), 1, 0.5);

  for (i = 0; i < 3; i++)
    {
      spinbuttonNVect_adj[i] = gtk_adjustment_new (1, -20, 20, 1, 3, 3);
      spinbuttonNVect[i] = gtk_spin_button_new (GTK_ADJUSTMENT (spinbuttonNVect_adj[i]), 1, 0);
      gtk_widget_show (spinbuttonNVect[i]);
      gtk_box_pack_start (GTK_BOX (hbox4), spinbuttonNVect[i], FALSE, FALSE, 0);
      gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbuttonNVect[i]), TRUE);
    }

  hbox5 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox5);
  gtk_box_pack_start (GTK_BOX (vbox3), hbox5, TRUE, TRUE, 0);

  labelDistance = gtk_label_new ("");
  if (panelConfigGet_spinBoundsValue() == 1.)
    gtk_label_set_text(GTK_LABEL(labelDistance),
		       _("Distance from origin: "));
  else
    {
      gtk_label_set_text(GTK_LABEL(labelDistance),
			 _("Dist. from origin <span style=\"italic\" size=\"smaller\""
			   " foreground=\"red\">\303\227 mult.</span>: "));
      gtk_label_set_use_markup(GTK_LABEL(labelDistance), TRUE);
    }
  gtk_widget_show (labelDistance);
  gtk_box_pack_start (GTK_BOX (hbox5), labelDistance, TRUE, TRUE, 0);
  gtk_misc_set_alignment (GTK_MISC (labelDistance), 1, 0.5);

  spinbuttonDistance_adj = gtk_adjustment_new (1, -1000, 1000, 1, 10, 10);
  spinbuttonDistance = gtk_spin_button_new (GTK_ADJUSTMENT (spinbuttonDistance_adj), 1, 2);
  gtk_widget_show (spinbuttonDistance);
  gtk_box_pack_start (GTK_BOX (hbox5), spinbuttonDistance, FALSE, FALSE, 0);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinbuttonDistance), TRUE);

  hbox3 = gtk_hbox_new (FALSE, 0);
  gtk_widget_show (hbox3);
  gtk_box_pack_start (GTK_BOX (vbox3), hbox3, TRUE, TRUE, 0);

  label4 = gtk_label_new (_("Color: "));
  gtk_widget_show (label4);
  gtk_box_pack_start (GTK_BOX (hbox3), label4, TRUE, TRUE, 0);
  gtk_misc_set_alignment (GTK_MISC (label4), 1, 0.5);

  widgetColorPlane = colorComboBox_new(TRUE);
  gtk_widget_show(widgetColorPlane);
  gtk_box_pack_start(GTK_BOX(hbox3), widgetColorPlane, FALSE, FALSE, 0);


  alignment2 = gtk_alignment_new (0.5, 0.5, 1, 0);
  gtk_widget_show (alignment2);
  gtk_box_pack_start (GTK_BOX (hbox2), alignment2, FALSE, FALSE, 0);

  vbox2 = gtk_vbox_new (FALSE, 2);
  gtk_widget_show (vbox2);
  gtk_container_add (GTK_CONTAINER (alignment2), vbox2);
/*   gtk_container_set_border_width (GTK_CONTAINER (vbox2), 3); */

  buttonPlaneAdd = gtk_button_new ();
  gtk_widget_show (buttonPlaneAdd);
  gtk_box_pack_start (GTK_BOX (vbox2), buttonPlaneAdd, FALSE, FALSE, 0);

  image2 = gtk_image_new_from_stock ("gtk-add", GTK_ICON_SIZE_BUTTON);
  gtk_widget_show (image2);
  gtk_container_add (GTK_CONTAINER (buttonPlaneAdd), image2);

  buttonPlaneRemove = gtk_button_new ();
  gtk_widget_show (buttonPlaneRemove);
  gtk_box_pack_start (GTK_BOX (vbox2), buttonPlaneRemove, FALSE, FALSE, 0);

  image4 = gtk_image_new_from_stock ("gtk-remove", GTK_ICON_SIZE_BUTTON);
  gtk_widget_show (image4);
  gtk_container_add (GTK_CONTAINER (buttonPlaneRemove), image4);

  /* Page   : advanced tools*/
  vboxDistChange = gtk_vbox_new(FALSE, 0);
  gtk_widget_set_sensitive(vboxDistChange, FALSE);
  gtk_widget_show(vboxDistChange);
  label = gtk_label_new(_("Advanced tools"));
  gtk_widget_show(label);
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vboxDistChange, label);

  label = gtk_label_new(_("Change selected plane distance"));
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(vboxDistChange), label, FALSE, FALSE, 3);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);

  gtk_box_pack_start(GTK_BOX(vboxDistChange), hbox, FALSE, FALSE, 0);
  label = gtk_label_new(_("From: "));
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
  entryDistFrom = numericalEntry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryDistFrom), 7);
  gtk_widget_show(entryDistFrom);
  gtk_box_pack_start(GTK_BOX(hbox), entryDistFrom, FALSE, FALSE, 0);
  label = gtk_label_new(_("to: "));
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
  entryDistTo = numericalEntry_new(0.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryDistTo), 7);
  gtk_widget_show(entryDistTo);
  gtk_box_pack_start(GTK_BOX(hbox), entryDistTo, FALSE, FALSE, 0);
  label = gtk_label_new(_("step: "));
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
  entryDistStep = numericalEntry_new(1.);
  gtk_entry_set_width_chars(GTK_ENTRY(entryDistStep), 7);
  gtk_widget_show(entryDistStep);
  gtk_box_pack_start(GTK_BOX(hbox), entryDistStep, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  gtk_box_pack_start(GTK_BOX(vboxDistChange), hbox, FALSE, FALSE, 0);
  label = gtk_label_new (_("Play at "));
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  spinDistDelay = gtk_spin_button_new_with_range(10, 10000, 25);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinDistDelay), 500.);
  gtk_widget_show(spinDistDelay);
  gtk_box_pack_start(GTK_BOX(hbox), spinDistDelay, FALSE, TRUE, 0);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(spinDistDelay), TRUE);
  label = gtk_label_new(_(" ms"));
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
  buttonDistPlayStop = gtk_button_new();
  gtk_tooltips_set_tip(tooltips, buttonDistPlayStop,
		       _("Change the distance parameter of he selected file"
			 " at the given rate."), NULL);
  gtk_widget_show(buttonDistPlayStop);
  gtk_box_pack_start(GTK_BOX(hbox), buttonDistPlayStop, FALSE, FALSE, 15);
  hbox1 = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox1);
  gtk_container_add(GTK_CONTAINER(buttonDistPlayStop), hbox1);
  imageDistPlay = create_pixmap(mainWindow, "stock_media-play.png");
  gtk_widget_show(imageDistPlay);
  gtk_box_pack_start(GTK_BOX(hbox1), imageDistPlay, TRUE, TRUE, 0);
  imageDistStop = create_pixmap(mainWindow, "stock_media-stop.png");
  gtk_box_pack_start(GTK_BOX(hbox1), imageDistStop, TRUE, TRUE, 0);

  /* The IO tab. */
  vboxIO = gtk_vbox_new(FALSE, 0);
  gtk_widget_show(vboxIO);
  label = gtk_label_new(_("File tools"));
  gtk_widget_show(label);
  gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vboxIO, label);
  
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  gtk_box_pack_start(GTK_BOX(vboxIO), hbox, FALSE, FALSE, 0);
  label = gtk_label_new(_("Load a plane list:"));
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
  button = gtk_button_new_from_stock(GTK_STOCK_OPEN);
  gtk_widget_show(button);
  gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(button), "clicked",
		   G_CALLBACK(onPlaneListOpen), (gpointer)0);

  label = gtk_label_new(_("Current loaded list:"));
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(vboxIO), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);

  labelPlaneList = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(labelPlaneList), TRUE);
  gtk_label_set_markup(GTK_LABEL(labelPlaneList), PANEL_PLANES_NO_PLANE_LIST);
  gtk_widget_show(labelPlaneList);
  gtk_box_pack_start(GTK_BOX(vboxIO), labelPlaneList, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(labelPlaneList), 0., 0.5);
#if GTK_MINOR_VERSION > 5
  gtk_label_set_ellipsize(GTK_LABEL(labelPlaneList), PANGO_ELLIPSIZE_START);
#endif

  hboxSaveList = gtk_hbox_new(FALSE, 0);
  gtk_widget_set_sensitive(hboxSaveList, FALSE);
  gtk_widget_show(hboxSaveList);
  gtk_box_pack_start(GTK_BOX(vboxIO), hboxSaveList, FALSE, FALSE, 0);
  alignment2 = gtk_alignment_new(1., 0.5, 0, 0);
  gtk_widget_show(alignment2);
  gtk_box_pack_start(GTK_BOX(hboxSaveList), alignment2, TRUE, TRUE, 0);
  buttonSaveList = gtk_button_new_from_stock(GTK_STOCK_SAVE);
  gtk_widget_set_sensitive(buttonSaveList, FALSE);
  gtk_widget_show(buttonSaveList);
  gtk_container_add(GTK_CONTAINER(alignment2), buttonSaveList);
  g_signal_connect(G_OBJECT(buttonSaveList), "clicked",
		   G_CALLBACK(onPlaneListSave), (gpointer)0);
  button = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
  gtk_widget_show(button);
  gtk_box_pack_start(GTK_BOX(hboxSaveList), button, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(button), "clicked",
		   G_CALLBACK(onPlaneListSaveAs), (gpointer)0);

  /* Add the callback methods. */
  g_signal_connect(G_OBJECT(checkUsePlanes), "toggled",
		   G_CALLBACK(onPlaneUsed), (gpointer)0);

  g_signal_connect(G_OBJECT(buttonPlaneAdd), "clicked",
		   G_CALLBACK(onPlaneAdd), (gpointer)0);
  g_signal_connect(G_OBJECT(buttonPlaneRemove), "clicked",
		   G_CALLBACK(onPlaneRemove), (gpointer)0);

  for (i = 0; i < 3; i++)
    g_signal_connect(G_OBJECT(spinbuttonNVect[i]), "value-changed",
		     G_CALLBACK(onSpinNVectChange), GINT_TO_POINTER(i));

  g_signal_connect(G_OBJECT(spinbuttonDistance), "value-changed",
		   G_CALLBACK(onSpinDistanceChange), (gpointer)0);

  g_signal_connect(G_OBJECT(widgetColorPlane), "color-selected",
		   G_CALLBACK(onPlaneColorChange), (gpointer)0);

  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		   G_CALLBACK(onNewFileLoaded), (gpointer)0);

  g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
		   "changed", G_CALLBACK(onTreeSelectionChanged), (gpointer)0);

  g_signal_connect(G_OBJECT(buttonRotate), "clicked",
		   G_CALLBACK(onSetCameraPosition), (gpointer)0);

  g_signal_connect(G_OBJECT(visuGtkObject), "spinBoundsChanged",
		   G_CALLBACK(onSpinBoundsChangeDistance), (gpointer)0);

  g_signal_connect(G_OBJECT(radiobuttonHideUnion), "toggled",
		   G_CALLBACK(onGtkPlanesHidingModeToggled), GINT_TO_POINTER(plane_hideUnion));
  g_signal_connect(G_OBJECT(radiobuttonHideInter), "toggled",
		   G_CALLBACK(onGtkPlanesHidingModeToggled), GINT_TO_POINTER(plane_hideInter));

  g_signal_connect(G_OBJECT(buttonDistPlayStop), "clicked",
		   G_CALLBACK(onPlayStopDist), (gpointer)0);
  g_signal_connect(G_OBJECT(spinDistDelay), "value-changed",
		   G_CALLBACK(onSpinDistDelayChange), (gpointer)0);


  /* Create the listModel */
  listStoredPlanes = gtk_list_store_new (N_COLUMN_PLANE,
					 G_TYPE_BOOLEAN,
					 G_TYPE_STRING,
					 G_TYPE_BOOLEAN,
					 G_TYPE_BOOLEAN,
					 GDK_TYPE_PIXBUF,
					 G_TYPE_POINTER);

  /* Render the associated tree */
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onGtkPlanesDrawnToggled), (gpointer)0);
  column = gtk_tree_view_column_new_with_attributes (_("Drawn"),
						     renderer,
						     "active", COLUMN_PLANE_DRAWN,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW(treeviewPlanes), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Parameters"),
						     renderer,
						     "text", COLUMN_PLANE_LABEL,
						     NULL);
  gtk_tree_view_column_set_expand(column, TRUE);
  gtk_tree_view_column_set_alignment(column, 0.5);
  gtk_tree_view_append_column (GTK_TREE_VIEW (treeviewPlanes), column);
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onGtkPlanesHideToggled), GINT_TO_POINTER(0));
  column = gtk_tree_view_column_new_with_attributes (_("Mask"),
						     renderer,
						     "active", COLUMN_PLANE_HIDE_IS_ON,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW(treeviewPlanes), column);
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onGtkPlanesHideToggled), GINT_TO_POINTER(1));
  column = gtk_tree_view_column_new_with_attributes (_("Invert"),
						     renderer,
						     "active", COLUMN_PLANE_HIDDEN_SIDE,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW(treeviewPlanes), column);
  renderer = gtk_cell_renderer_pixbuf_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Color"),
						     renderer,
						     "pixbuf", COLUMN_PLANE_COLOR_PIXBUF,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW(treeviewPlanes), column);

  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
			      GTK_SELECTION_SINGLE);
  gtk_tree_view_set_model(GTK_TREE_VIEW(treeviewPlanes), GTK_TREE_MODEL(listStoredPlanes));
}

GList* getListOfAllPlanes(gboolean maskingPlanesOnly)
{
  GList *planeList;
  GtkTreeIter iter;
  Plane *plane;
  
  DBG_fprintf(stderr, "Panel Plane : building plane list...\n");
  /* Build the list of all planes. */
  planeList = (GList*)0;
  if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(listStoredPlanes), &iter))
    do
      {
	gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes),
			   &iter, COLUMN_PLANE_POINTER, &plane, -1);
	if (!maskingPlanesOnly || (planeGet_hiddenState(plane) != PLANE_SIDE_NONE))
	  {
	    planeList = g_list_append(planeList, plane);
	    DBG_fprintf(stderr, " | found one plane %p.\n", (gpointer)plane);
	  }
      }
    while (gtk_tree_model_iter_next(GTK_TREE_MODEL(listStoredPlanes), &iter));

  return planeList;
}

static void onAskForHideNodes(VisuData *visuData, gboolean *redraw, gpointer data)
{
  GList *listOfPlanes;

  if (!checkUsePlanes || !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkUsePlanes)))
    return;

  DBG_fprintf(stderr, "Panel Planes : caught the 'NodeAskForShowHide' signal for"
	      " VisuData %p.\n", (gpointer)visuData);
  listOfPlanes = getListOfAllPlanes(TRUE);
  if (listOfPlanes)
    {
      *redraw = planeShowHide_all(visuData, listOfPlanes) || *redraw;
      g_list_free(listOfPlanes);
    }
}
gboolean panelPlanesSet_use(VisuData *dataObj, gboolean value)
{
  gboolean used;
  int i, j, val;
  GtkTreeIter iter;

  if (extensionPlanes->used == value)
    return FALSE;

  extensionPlanes->used = value;
  
  if (!isPlanesInitialised)
    createAndInitPlanePanel(dataObj);

  /* Test if any planes are stored. */
  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(listStoredPlanes), &iter))
    return FALSE;

  val = panelPlanesApply_hidingScheme(dataObj);

  return (gboolean)val;
}
void onPlaneUsed(GtkToggleButton *button, gpointer data)
{
  RenderingWindow *window;
  VisuData *dataObj;
  gboolean res;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  dataObj = renderingWindowGet_visuData(window);

  res = panelPlanesSet_use(dataObj, gtk_toggle_button_get_active(button));
  if (res)
    visuData_createAllNodes(dataObj);

  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		 0 /* details */,
		 NULL);
}
void onPlaneAdd(GtkButton *button, gpointer userData)
{
  Plane* plane;
  float vect[3];
  int i;
  GtkTreeIter iter;
  Color *color;
  gchar str[50];
  float dist;
  GdkPixbuf *pixbufColorAlphaBox;
  RenderingWindow *window;
  VisuData *dataObj;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);
  dataObj = renderingWindowGet_visuData(window);

  pixbufColorAlphaBox = colorComboBoxGet_selectedPixbuf(COLOR_COMBOX(widgetColorPlane));
  color = colorComboBoxGet_selectedColor(COLOR_COMBOX(widgetColorPlane));
  for (i = 0; i < 3; i++)
    vect[i] = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbuttonNVect[i]));
  dist = (float)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbuttonDistance));
  plane = planeNew(visuDataGet_openGLView(dataObj), vect, dist, color);
  
  /* String used to labelled planes, dist. means 'distance' and
     norm. means 'normal' (50 chars max). */
  sprintf(str, LABEL_PLANE, (int)vect[0], (int)vect[1], (int)vect[2], dist);
  gtk_list_store_append(listStoredPlanes, &iter);
  gtk_list_store_set(listStoredPlanes, &iter,
		     COLUMN_PLANE_DRAWN, TRUE,
		     COLUMN_PLANE_LABEL, str,
		     COLUMN_PLANE_HIDE_IS_ON, FALSE,
		     COLUMN_PLANE_HIDDEN_SIDE, FALSE,
		     COLUMN_PLANE_COLOR_PIXBUF, (gpointer)pixbufColorAlphaBox,
		     COLUMN_PLANE_POINTER, (gpointer)plane,
		     -1);

  rebuildListPlanes((VisuData*)dataObj);
  gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)), &iter);
  if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStoredPlanes), (GtkTreeIter*)0) > 1)
    gtk_widget_set_sensitive(hboxHidingMode, TRUE);
  if (extensionPlanes->used)
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 /* details */,
		   NULL);
}
void onPlaneRemove(GtkButton *button, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  int val;
  RenderingWindow *window;
  VisuData *dataObj;

  DBG_fprintf(stderr, "Panel Plane : removing plane...\n");
  res = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
					&tmpList, &iter);
  if (res)
    {
      window = RENDERING_WINDOW(visuRenderingWindowGet_current());
      if (!window)
	return;

      dataObj = renderingWindowGet_visuData(window);

      res = TRUE;
      if (!gtk_list_store_remove(GTK_LIST_STORE(listStoredPlanes), &iter))
	res = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(listStoredPlanes), &iter);

      DBG_fprintf(stderr, "Panel Plane : OK plane found and removed.\n");

      if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStoredPlanes), (GtkTreeIter*)0) < 2)
	gtk_widget_set_sensitive(hboxHidingMode, FALSE);

      rebuildListPlanes((VisuData*)dataObj);
      /* If extension is not used, we leave here. */
      if (!extensionPlanes->used)
	return;

      /* Modify nodes visibility accordingly. */
      val = panelPlanesApply_hidingScheme(dataObj);
      if (val)
	visuData_createAllNodes(dataObj);
      if (extensionPlanes->used)
	g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		       0 /* details */,
		       NULL);

      if (res)
	gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
				       &iter);
    }
}
void onSpinNVectChange(GtkSpinButton *spin, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  Plane *plane;
  char str[50];
  float dist;
  float vect[3];
  gboolean hide, side;
  int i;
  int val;
  RenderingWindow *window;
  VisuData *dataObj;

  i = GPOINTER_TO_INT(data);
  if (i < 0 || i > 2)
    return;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  /* Get the VisuData to deal with. */
  dataObj = renderingWindowGet_visuData(window);

  res = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
					&tmpList, &iter);
  if (res)
    {
      plane = (Plane*)0;
      hide = FALSE;
      side = FALSE;
      gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
			 COLUMN_PLANE_POINTER, &plane,
			 COLUMN_PLANE_HIDE_IS_ON, &hide,
			 COLUMN_PLANE_HIDDEN_SIDE, &side,
			 -1);
      if (plane)
	{
	  planeGet_nVectUser(plane, vect);
	  vect[i] = (float)gtk_spin_button_get_value(spin);
	  val = planeSet_normalVector(plane, vect);
	  if (val < 0)
	    {
	      fprintf(stderr, "WARNING! Something goes wrong when assigning"
		      " the new normal vector : (%f; %f; %f).\n",
		      vect[0], vect[1], vect[2]);
	      planeGet_nVectUser(plane, vect);
	      for (i = 0; i < 3; i++)
		gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonNVect[i]), vect[i]);
	      return;
	    }
	  else if (val == 0)
	    return;
	  planeComputeInter(visuDataGet_openGLView(dataObj), plane);
	  planeGet_distanceFromOrigin(plane, &dist);
	  sprintf(str, LABEL_PLANE,
		  (int)vect[0], (int)vect[1], (int)vect[2], dist);
	  gtk_list_store_set(listStoredPlanes, &iter, COLUMN_PLANE_LABEL, str, -1);
	  /* Redraw all planes. */
	  rebuildListPlanes((VisuData*)dataObj);
	  /* If extension is not used, we leave here. */
	  if (!extensionPlanes->used)
	    return;

	  /* Need to modify the nodes visibility only if the current
	     plane use the masking function (hiddenState != PLANE_SIDE_NONE). */
	  if (planeGet_hiddenState(plane) != PLANE_SIDE_NONE)
	    {
	      /* Modify nodes visibility accordingly. */
	      val = panelPlanesApply_hidingScheme(dataObj);
	      if (val)
		visuData_createAllNodes(dataObj);
	    }
	  if (extensionPlanes->used)
	    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
			   0 /* details */,
			   NULL);
	}
    }
}
void onSpinDistanceChange(GtkSpinButton *spin, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  Plane *plane;
  char str[50];
  float dist, mult;
  float vect[3];
  gboolean hide, side;
  int val;
  RenderingWindow *window;
  VisuData *dataObj;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  /* Get the VisuData to deal with. */
  dataObj = renderingWindowGet_visuData(window);

  res = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
					&tmpList, &iter);
  if (res)
    {
      plane = (Plane*)0;
      hide = FALSE;
      side = FALSE;
      gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
			 COLUMN_PLANE_POINTER, &plane,
			 COLUMN_PLANE_HIDE_IS_ON, &hide,
			 COLUMN_PLANE_HIDDEN_SIDE, &side,
			 -1);
      if (plane)
	{
	  mult = panelConfigGet_spinBoundsValue();
	  dist = (float)gtk_spin_button_get_value(spin);
	  val = planeSet_distanceFromOrigin(plane, dist * mult);
	  if (val < 0)
	    {
	      fprintf(stderr, "WARNING! Something goes wrong when assigning"
		      " the new distance from origin : %f.\n",
		      dist);
	      gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonDistance), 0.);
	      return;
	    }
	  else if (val == 0)
	    return;
	  planeComputeInter(visuDataGet_openGLView(dataObj), plane);
	  planeGet_nVectUser(plane, vect);
	  sprintf(str, LABEL_PLANE,
		  (int)vect[0], (int)vect[1], (int)vect[2], dist);
	  gtk_list_store_set(listStoredPlanes, &iter, COLUMN_PLANE_LABEL, str, -1);
	  /* Redraw all planes. */
	  rebuildListPlanes((VisuData*)dataObj);
	  /* If extension is not used, we leave here. */
	  if (!extensionPlanes->used)
	    return;

	  /* Need to modify the nodes visibility only if the current
	     plane use the masking function (hiddenState != PLANE_SIDE_NONE). */
	  if (planeGet_hiddenState(plane) != PLANE_SIDE_NONE)
	    {
	      /* Modify nodes visibility accordingly. */
	      val = panelPlanesApply_hidingScheme(dataObj);
	      if (val)
		visuData_createAllNodes(dataObj);
	    }
	  if (extensionPlanes->used)
	    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
			   0 /* details */,
			   NULL);
	}
    }
}
void onSpinBoundsChangeDistance(GObject *obj, gfloat mult, gpointer data)
{
  DBG_fprintf(stderr, "Panel Plane : catch the 'spinBoundsChanged' signal,"
	      " should adjust the distance from origin (multiplier : %f).\n", mult);

  if (panelConfigGet_spinBoundsValue() == 1.)
    gtk_label_set_text(GTK_LABEL(labelDistance),
		       _("Distance from origin: "));
  else
    {
      gtk_label_set_text(GTK_LABEL(labelDistance),
			 _("Dist. from origin <span style=\"italic\" size=\"smaller\""
			   " foreground=\"red\">\303\227 mult.</span>: "));
      gtk_label_set_use_markup(GTK_LABEL(labelDistance), TRUE);
    }
}
void onPlaneColorChange(ColorComboBox *combo, Color *color, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  Plane *plane;
  GtkTreeModel *tree;
  int selected, val;
  GdkPixbuf *pixbufColorAlphaBox;
  RenderingWindow *window;
  VisuData *dataObj;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  /* Get the VisuData to deal with. */
  dataObj = renderingWindowGet_visuData(window);

  DBG_fprintf(stderr, "Panel Plane : Catch the 'color-selected' signal.\n");

  pixbufColorAlphaBox = colorComboBoxGet_selectedPixbuf(combo);

  /* Get the plane */
  res = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
					&tmpList, &iter);
  if (!res)
    return;
  plane = (Plane*)0;
  gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter, COLUMN_PLANE_POINTER, &plane, -1);
  if (!plane)
    return;

  /* Change the color value. */
  val = planeSet_color(plane, color);
  if (val < 0)
    {
      fprintf(stderr, "WARNING! Something goes wrong when assigning"
	      " the new color : %p.\n", (gpointer)color);
      return;
    }
  else if (val == 0)
    return;

  DBG_fprintf(stderr, "Panel Plane : color combobox changed to %p (%f,%f,%f,%f).\n",
	      (gpointer)color, color->rgba[0], color->rgba[1], color->rgba[2], color->rgba[3]);
  DBG_fprintf(stderr, "Panel Plane : Changed color (%p) for plane : %p.\n",
	      (gpointer)color, (gpointer)plane);

  gtk_list_store_set(listStoredPlanes, &iter, COLUMN_PLANE_COLOR_PIXBUF, pixbufColorAlphaBox, -1);
  rebuildListPlanes(dataObj);
  if (extensionPlanes->used)
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 /* details */,
		   NULL);
}

void onDataReadyForRendering(GObject *obj, VisuData *dataObj, gpointer data)
{
  DBG_fprintf(stderr, "Panel Plane : caught 'dataReadyForRendering'"
	      " signal, setting sensitivity.\n");
  if (dataObj)
    gtk_widget_set_sensitive(vBoxPlanes, TRUE);
  else
    gtk_widget_set_sensitive(vBoxPlanes, FALSE);
}

void onNewFileLoaded(GObject *obj, VisuData *dataObj, gpointer data)
{
  GList *list, *tmplst;
  int val;
  gboolean needShowHide;
  OpenGLView *view;

  if (!dataObj)
    return;

  DBG_fprintf(stderr, "Panel Plane : caught 'dataReadyForRendering' signal,"
	      " connecting local signals.\n");
  
  onAskForHide_signal_id =
    g_signal_connect(G_OBJECT(dataObj), "NodeAskForShowHide",
		     G_CALLBACK(onAskForHideNodes), (gpointer)0);
  g_signal_connect(G_OBJECT(dataObj), "NodePositionChanged",
		   G_CALLBACK(onNodePositionChanged), (gpointer)0);

  if (!extensionPlanes->used)
    return;

  DBG_fprintf(stderr, "Panel Plane : caught 'dataReadyForRendering' signal,"
	      " recalculating intersections and masking set.\n");

  view = visuDataGet_openGLView(dataObj);
  g_return_if_fail(view);

  needShowHide = FALSE;
  list = getListOfAllPlanes(FALSE);
  tmplst = list;
  while (tmplst)
    {
      /* Compute the intersection with the new box */
      planeComputeInter(view, (Plane*)tmplst->data);
      /* Test if one of the plane at least need to mask some nodes. */
      needShowHide = needShowHide ||
	(planeGet_hiddenState((Plane*)tmplst->data) != PLANE_SIDE_NONE);
      tmplst = g_list_next(tmplst);
    }
  /* Compute the masking state */
  if (needShowHide)
    val = panelPlanesApply_hidingScheme(dataObj);
  g_list_free(list);
  rebuildListPlanes((VisuData*)dataObj);
}

void onNodePositionChanged(VisuData *dataObj, gpointer data)
{
  GList *list;
  gboolean needShowHide;

  DBG_fprintf(stderr, "Panel Plane : catch 'NodePositionChanged' signal,"
	      " recalculating masking properties.\n");

  needShowHide = FALSE;
  list = getListOfAllPlanes(TRUE);
  if (list)
    panelPlanesApply_hidingScheme(dataObj);
  g_list_free(list);
/*   rebuildListPlanes((VisuData*)dataObj); */
}


void onTreeSelectionChanged(GtkTreeSelection *tree, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  Plane *plane;
  float val, nPlane[3], dist, mult;
  int i;
  Color *colorPlane, *colorCombo;

  DBG_fprintf(stderr, "Panel Plane : catch 'changed' signal from "
	      " the gtkTreeSelection.\n");

  res = gtk_tree_selection_get_selected(tree, &tmpList, &iter);
  if (res)
    {
      plane = (Plane*)0;
      gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
			 COLUMN_PLANE_POINTER, &plane,
			 -1);
      planeGet_nVectUser(plane, nPlane);
      for (i = 0; i < 3; i++)
	{
	  val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbuttonNVect[i]));
	  if (val != nPlane[i])
	    gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonNVect[i]), nPlane[i]);
	}
      planeGet_distanceFromOrigin(plane, &dist);
      mult = panelConfigGet_spinBoundsValue();
      val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbuttonDistance));
      if (val != dist / mult)
	gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonDistance), dist / mult);
      planeGet_color(plane, &colorPlane);
      colorCombo = colorComboBoxGet_selectedColor(COLOR_COMBOX(widgetColorPlane));
      if (colorPlane != colorCombo)
	colorComboBoxSet_selectionByColor(COLOR_COMBOX(widgetColorPlane), colorPlane);

      gtk_widget_set_sensitive(buttonRotate, TRUE);
      gtk_widget_set_sensitive(vboxDistChange, TRUE);
      gtk_widget_set_sensitive(hboxSaveList, TRUE);
    }
  else
    {
      gtk_widget_set_sensitive(buttonRotate, FALSE);
      gtk_widget_set_sensitive(vboxDistChange, FALSE);
      gtk_widget_set_sensitive(hboxSaveList, FALSE);
    }
}

void onSetCameraPosition(GtkButton *button, gpointer data)
{
  gboolean res;
  GtkTreeModel *tmpList;
  GtkTreeIter iter;
  float spherical[3];
  Plane *plane;
  float nPlane[3];
  RenderingWindow *window;

  res = gtk_tree_selection_get_selected(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)),
					&tmpList, &iter);
  if (res)
    {
      plane = (Plane*)0;
      gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
			 COLUMN_PLANE_POINTER, &plane,
			 -1);
      DBG_fprintf(stderr, "Panel Planes : Set the camera position to be"
		  " normal to the selected plane (%p).\n", (gpointer)plane);
      planeGet_nVectUser(plane, nPlane);
      cartesian_to_spherical(spherical, nPlane);
      window = RENDERING_WINDOW(visuRenderingWindowGet_current());
      g_return_if_fail(window);
      visuDataSet_angleOfView(renderingWindowGet_visuData(window),
			      spherical[1], spherical[2], 0.,
			     MASK_THETA | MASK_PHI);
      g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		     0 , NULL);
    }
}
void onGtkPlanesDrawnToggled(GtkCellRendererToggle *cell_renderer,
			     gchar *path, gpointer user_data)
{
  gboolean validIter, checked;
  GtkTreeIter iter;
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  validIter = gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(listStoredPlanes), &iter, path);
  if (!validIter)
    {
      fprintf(stderr, "INTERNAL ERROR! Wrong 'path' argument in"
	      " 'onGtkPlanesDrawnToggled' method.\n");
      return;
    }
  gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
		     COLUMN_PLANE_DRAWN, &checked, -1);
  gtk_list_store_set(listStoredPlanes, &iter,
		     COLUMN_PLANE_DRAWN, !checked, -1);
  DBG_fprintf(stderr, "Panel planes : use state (%d) for plane at path '%s'.\n",
	      !checked, path);

  rebuildListPlanes(renderingWindowGet_visuData(window));
  if (extensionPlanes->used)
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 /* details */,
		   NULL);
}
void onGtkPlanesHideToggled(GtkCellRendererToggle *cell_renderer,
			    gchar *path, gpointer user_data)
{
  gboolean validIter, checked, side;
  GtkTreeIter iter;
  int val;
  Plane *plane;
  RenderingWindow *window;
  VisuData *dataObj;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  validIter = gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(listStoredPlanes), &iter, path);
  if (!validIter)
    {
      fprintf(stderr, "INTERNAL ERROR! Wrong 'path' argument in"
	      " 'onGtkPlanesHideToggled' method.\n");
      return;
    }

  gtk_tree_model_get(GTK_TREE_MODEL(listStoredPlanes), &iter,
		     COLUMN_PLANE_POINTER, &plane,
		     COLUMN_PLANE_HIDE_IS_ON, &checked,
		     COLUMN_PLANE_HIDDEN_SIDE, &side, -1);
  if (GPOINTER_TO_INT(user_data) == 0)
    {
      checked = !checked;
      gtk_list_store_set(listStoredPlanes, &iter,
			 COLUMN_PLANE_HIDE_IS_ON, checked, -1);
    }
  else
    {
      side = !side;
      gtk_list_store_set(listStoredPlanes, &iter,
			 COLUMN_PLANE_HIDDEN_SIDE, side, -1);
    }
  /* Set the hidden status */
  if (!checked)
    val = planeSet_hiddenState(plane, PLANE_SIDE_NONE);
  else
    {
      if (side)
	val = planeSet_hiddenState(plane, PLANE_SIDE_MINUS);
      else
	val = planeSet_hiddenState(plane, PLANE_SIDE_PLUS);
    }
  if (!extensionPlanes->used)
    return;
  if (val)
    {
      /* Get the VisuData to deal with. */
      dataObj = renderingWindowGet_visuData(window);
      /* Modify nodes visibility accordingly. */
      val = panelPlanesApply_hidingScheme(dataObj);
      if (val)
	{
	  visuData_createAllNodes(dataObj);
	  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
			 0 /* details */,
			 NULL);
	}
    }
}
void onGtkPlanesHidingModeToggled(GtkToggleButton *toggle, gpointer data)
{
  int res;
  GList *planeList, *tmplst;
  gboolean needShowHide;
  RenderingWindow *window;
  VisuData *dataObj;

  if (!gtk_toggle_button_get_active(toggle))
    return;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  if (!window)
    return;

  DBG_fprintf(stderr, "Panel planes : set hiding mode to %d.\n", GPOINTER_TO_INT(data));

  res = planeSet_hidingMode(GPOINTER_TO_INT(data));
  if (!res || !extensionPlanes->used)
    return;
  /* Build the list of all planes. */
  planeList = getListOfAllPlanes(FALSE);
  if (!planeList || !planeList->next)
    return;
  /* Test if one of the plane has a mask attribute. */
  needShowHide = FALSE;
  tmplst = planeList;
  while (tmplst)
    {
      /* Test if one of the plane at least need to mask some nodes. */
      needShowHide = needShowHide ||
	(planeGet_hiddenState((Plane*)tmplst->data) != PLANE_SIDE_NONE);
      tmplst = g_list_next(tmplst);
    }
  res = 0;
  /* Get the VisuData to deal with. */
  dataObj = renderingWindowGet_visuData(window);
  if (needShowHide)
    /* Modify nodes visibility accordingly. */
    res = panelPlanesApply_hidingScheme(dataObj);
  g_list_free(planeList);
  if (res)
    {
      visuData_createAllNodes(dataObj);
      g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		     0 /* details */,
		     NULL);
    }
}
gboolean panelPlanesApply_hidingScheme(VisuData *data)
{
  gboolean redraw;

  visuDataEmit_askForShowHideNodes(data, &redraw);

  if (redraw)
    visuDataEmit_nodeRenderedChange(data);

  return redraw;
}


static void onPlayStopDist(GtkButton *button, gpointer data)
{
  DBG_fprintf(stderr, "Panel Plane : push the play/stop button.\n");

  if (!isPlayingDistanceId)
    {
      /* Launch play */
      gtk_widget_hide(imageDistPlay);
      gtk_widget_show(imageDistStop);

      gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonDistance),
				numericalEntryGet_value(NUMERICAL_ENTRY(entryDistFrom)));
      directionDist = 1.;
      isPlayingDistanceId =
	g_timeout_add_full(G_PRIORITY_DEFAULT + 30,
			   (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinDistDelay)),
			   playDistances, (gpointer)0,
			   stopPlayStop);
    }
  else
    {
      /* Stop play */
      g_source_remove(isPlayingDistanceId);
    }
}
static void stopPlayStop(gpointer data)
{
  isPlayingDistanceId = 0;
  gtk_widget_hide(imageDistStop);
  gtk_widget_show(imageDistPlay);
}
static gboolean playDistances(gpointer data)
{
  gdouble val, step;
  gboolean changed;
  
  if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStoredPlanes), NULL) == 0)
    return FALSE;

  val = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinbuttonDistance));
  step = numericalEntryGet_value(NUMERICAL_ENTRY(entryDistStep));
  changed = FALSE;
  if (directionDist > 0.)
    {
      if (val + step > numericalEntryGet_value(NUMERICAL_ENTRY(entryDistTo)))
	directionDist = -1.;
      else
	changed = TRUE;
    }
  else
    changed = TRUE;
  if (directionDist < 0.)
    {
      if (val - step < numericalEntryGet_value(NUMERICAL_ENTRY(entryDistFrom)))
	directionDist = +1.;
      else
	changed = TRUE;
    }
  /* If the direction has been changed twice in a row,
     then the step is too wide for the range, we stop. */
  if (!changed)
    return FALSE;

  DBG_fprintf(stderr, "Panel Planes : set new distance to %g.\n", val + directionDist * step);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinbuttonDistance),
			    val + directionDist * step);
  return TRUE;
}
static gboolean stopPlayOnLeave(gpointer data)
{
  if (isPlayingDistanceId != 0)
    {
      /* Stop play */
      g_source_remove(isPlayingDistanceId);
    }
  return FALSE;
}
static void onSpinDistDelayChange(GtkSpinButton *spin, gpointer data)
{
  if (isPlayingDistanceId)
    {
      /* Stop play. */
      g_source_remove(isPlayingDistanceId);
      /* Launch play. */
      gtk_widget_hide(imageDistPlay);
      gtk_widget_show(imageDistStop);
      isPlayingDistanceId =
	g_timeout_add_full(G_PRIORITY_DEFAULT + 30,
			   (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinDistDelay)),
			   playDistances, (gpointer)0,
			   stopPlayStop);
    }
}

/* IO tab. */
static void setCurrentSaveFile()
{
  gchar *filenameUTF8, *markup;

  if (!currentSaveListFile)
    return;

  /* Print the filename to screen. */
  filenameUTF8 = getStringInUTF8(currentSaveListFile);
  markup = g_markup_printf_escaped(_("<span style=\"italic\" size=\"smaller\">%s</span>"),
				   filenameUTF8);
  g_free(filenameUTF8);
  gtk_label_set_markup(GTK_LABEL(labelPlaneList), markup);
  g_free(markup);

  /* Since we've got a file name, save button can be activated. */
  gtk_widget_set_sensitive(buttonSaveList, TRUE);
}
gboolean panelPlanesLoad_file(VisuData *dataObj, gchar *filename, GError **error)
{
  GList *list, *tmplst;
  gboolean res;
  OpenGLView *view;
  float vect[3];
  float dist;
  Color *color;
  int hide;
  gchar str[50];
  GdkPixbuf *pixbufColorAlphaBox;
  GtkTreeIter iter;

  g_return_val_if_fail(dataObj && filename && error && *error == (GError*)0, FALSE);

  g_return_val_if_fail(isPlanesInitialised, FALSE);

  /* Try to load the file. */
  res = planesParse_XMLFile(filename, &list, error);
  if (!res)
    return FALSE;

  view = visuDataGet_openGLView(dataObj);
  g_return_val_if_fail(view, FALSE);

  DBG_fprintf(stderr, "Panel Planes : found %d planes in file '%s'.\n", 
	      g_list_length(list), filename);
  /* Begin to compute intersections of all planes and then
     add then to the list. */
  gtk_list_store_clear(listStoredPlanes);
  tmplst = list;
  while (tmplst)
    {
      planeComputeInter(view, (Plane*)tmplst->data);

      /* String used to labelled planes, dist. means 'distance' and
	 norm. means 'normal' (50 chars max). */
      planeGet_nVectUser((Plane*)tmplst->data, vect);
      planeGet_distanceFromOrigin((Plane*)tmplst->data, &dist);
      planeGet_color((Plane*)tmplst->data, &color);
      hide = planeGet_hiddenState((Plane*)tmplst->data);
      sprintf(str, LABEL_PLANE, (int)vect[0], (int)vect[1], (int)vect[2], dist);
      pixbufColorAlphaBox =
	colorComboBoxGet_pixbufFromColor(COLOR_COMBOX(widgetColorPlane), color);
      gtk_list_store_append(listStoredPlanes, &iter);
      gtk_list_store_set(listStoredPlanes, &iter,
			 COLUMN_PLANE_DRAWN, TRUE,
			 COLUMN_PLANE_LABEL, str,
			 COLUMN_PLANE_HIDE_IS_ON, (hide != 0),
			 COLUMN_PLANE_HIDDEN_SIDE, (hide == -1),
			 COLUMN_PLANE_COLOR_PIXBUF, (gpointer)pixbufColorAlphaBox,
			 COLUMN_PLANE_POINTER, tmplst->data,
			 -1);
      tmplst = g_list_next(tmplst);
    }
  /* Change the OpenGL list containing planes. */
  rebuildListPlanes(dataObj);
  /* Set the last iter as selected iter. */
  gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeviewPlanes)), &iter);
  /* If several planes exist, set the sensitivity of
     the masking mode. */
  if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(listStoredPlanes), (GtkTreeIter*)0) > 1)
    gtk_widget_set_sensitive(hboxHidingMode, TRUE);
  g_list_free(list);

  /* Save the file name. */
  if (currentSaveListFile)
    g_free(currentSaveListFile);
  currentSaveListFile = g_strdup(filename);

  setCurrentSaveFile();

  return extensionPlanes->used;
}
static void onPlaneListOpen(GtkButton *button, gpointer data)
{
  GtkWidget *openDialog;
  gchar *filename, *directory;
  RenderingWindow *window;
  VisuData *dataObj;
  gboolean res;
  GError *error;
  
  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);
  dataObj = renderingWindowGet_visuData(window);
  g_return_if_fail(dataObj);

  openDialog = gtk_file_chooser_dialog_new(_("Choose a file with a list of planes"),
                                           GTK_WINDOW(mainWindow), GTK_FILE_CHOOSER_ACTION_OPEN,
					   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					   GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
					   NULL);
  gtk_window_set_modal(GTK_WINDOW(openDialog), TRUE);
  gtk_window_set_transient_for(GTK_WINDOW(openDialog), GTK_WINDOW(mainWindow));
  gtk_window_set_position(GTK_WINDOW(openDialog), GTK_WIN_POS_CENTER_ON_PARENT);
  directory = getLastOpenDirectory();
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(openDialog), directory);
  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(openDialog), FALSE);
  if (gtk_dialog_run(GTK_DIALOG(openDialog)) != GTK_RESPONSE_ACCEPT)
    {
      gtk_widget_destroy(openDialog);
      return;
    }
  filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(openDialog));
  gtk_widget_destroy(openDialog);
  error = (GError*)0;
  res = panelPlanesLoad_file(dataObj, filename, &error);
  g_free(filename);
  if (error)
    {
      raiseAlertDialog(error->message);
      g_clear_error(&error);
      return;
    }
  /* If the extension is actually used, recompute the visibility of nodes. */
  if (res)
    {
      res = panelPlanesApply_hidingScheme(dataObj);
      if (res)
	visuData_createAllNodes(dataObj);
      g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		     0 /* details */,
		     NULL);
    }
}
static gboolean callbackSave(gchar* filename)
{
  GList *list;
  GError *error;
  gboolean res;

  g_return_val_if_fail(filename, FALSE);

  /* Get all planes in a list. */
  list = getListOfAllPlanes(FALSE);

  /* Give the list to the save method. */
  error = (GError*)0;
  res = planesExport_XMLFile(filename, list, &error);
  if (!res)
    {
      raiseAlertDialog(error->message);
      g_clear_error(&error);
      return FALSE;
    }

  /* Free the used list. */
  g_list_free(list);

  return TRUE;
}
static void onPlaneListSave(GtkButton *button, gpointer data)
{
  callbackSave(currentSaveListFile);
}
static void onPlaneListSaveAs(GtkButton *button, gpointer data)
{
  GtkWidget *saveDialog;
  gchar *directory, *filename;
  gboolean res;

  saveDialog = gtk_file_chooser_dialog_new(_("Choose a file to save the list of planes"),
                                           GTK_WINDOW(mainWindow), GTK_FILE_CHOOSER_ACTION_SAVE,
					   GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					   GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
					   NULL);
  gtk_window_set_modal(GTK_WINDOW(saveDialog), TRUE);
  gtk_window_set_transient_for(GTK_WINDOW(saveDialog), GTK_WINDOW(mainWindow));
  gtk_window_set_position(GTK_WINDOW(saveDialog), GTK_WIN_POS_CENTER_ON_PARENT);
  directory = getLastOpenDirectory();
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(saveDialog), directory);
  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(saveDialog), FALSE);
  gtk_dialog_set_default_response(GTK_DIALOG(saveDialog), GTK_RESPONSE_ACCEPT);
  if (gtk_dialog_run(GTK_DIALOG(saveDialog)) != GTK_RESPONSE_ACCEPT)
    {
      gtk_widget_destroy(saveDialog);
      return;
    }
  filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(saveDialog));
  gtk_widget_destroy(saveDialog);

  res = callbackSave(filename);
  if (res)
    {
      currentSaveListFile = filename;
      setCurrentSaveFile();
    }
  else
    g_free(filename);
}
