/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Luc BILLARD, Damien
	CALISTE, Olivier D'Astier, laboratoire L_Sim, (2001-2005)
  
	Adresse 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-2005)

	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 "panelSurfaces.h"
#include "panelSurfacesTools.h"
#include "panelPlanes.h"

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

#include <visu_gtk.h>
#include <support.h>
#include <gtk_main.h>
#include <opengl.h>
#include <gtk_renderingWindowWidget.h>
#include <openGLFunctions/objectList.h>
#include <openGLFunctions/interactive.h>
#include <extensions/surfs.h>

#include <extraFunctions/surfaces.h>
#include <extraFunctions/scalarFields.h>
#include <extraFunctions/scalarFieldSet.h>
#include <extraFunctions/pot2surf.h>
#include <extraGtkFunctions/gtk_toolPanelWidget.h>
#include <extraGtkFunctions/gtk_colorComboBoxWidget.h>
#include <extraGtkFunctions/gtk_shadeComboBoxWidget.h>
#include <extraGtkFunctions/gtk_valueIOWidget.h>
#include <extraGtkFunctions/gtk_fieldChooser.h>

/**
 * SECTION:panelSurfaces
 * @short_description: Gtk interface to load isosurfaces.
 * 
 * <para>This module contains the panel used to draw isosurfaces. From
 * it, you can draw isosurfaces on screen after they are loaded
 * through the #VisuGlExtSurfaces object. You can also access tools to
 * manage and create .surf files, these tools are related to the <link
 * linkend="v-sim-panelSurfacesTools">panelSurfacesTools</link>
 * module.</para>
 */

/* Global variables. */
static GtkWidget *panelSurfaces;
static GtkWidget *isosurfaces_gtk_vbox;
static GtkTreeStore *treeStore;
static GtkListStore *fields_data_list;
static GtkWidget *treeView;
static GtkTreeViewColumn *colorColumn;
enum
  {
    SURFACE_FILE_DENPOT, /* The entry is a potential or a density file. */
    SURFACE_FILE_SURF,   /* The entry is a surfaces file. */
    SURFACE_SURF         /* The entry is a surface. */
  };

enum
  {
    TYPE_COLUMN,
    LABEL_COLUMN,
    POTENTIAL_COLUMN,
    SHADE_COLUMN,
    ROW_FIELD_COLUMN,
    BOX_ROW_COLUMN,
    N_COLUMNS
  };

static void showHideVisuSurface(GtkTreeModel *model, GtkTreeIter *iter, gpointer value);
static void changeSurfaceColor(GtkTreeModel *model, GtkTreeIter *iter, gpointer color);
static gboolean isosurfaces_show_next();
static gboolean loadXMLFile(const gchar *filename, GError **error);

/* Callbacks. */
static void onDataFocused(GObject *visu, VisuData *dataObj, gpointer data);
static void onOpenClicked(GtkButton *button, gpointer data);
static void onTreeSelectionChanged(GtkTreeSelection *tree, gpointer data);
static void onAddButtonClicked(GtkButton *button, gpointer data);
static void onAddSpecialButtonClicked(GtkButton *button, gpointer data);
static void onGenerateChanged(GtkSpinButton *spin, gpointer data);
static void onRemoveButtonClicked(GtkButton *button, gpointer data);
static void onReorderToggled(GtkToggleButton *toggle, VisuGlExtSurfaces *surfs);
static void onNameEdited(GtkCellRendererText *cellrenderertext,
			 gchar *path, gchar *text, gpointer user_data);
static void onPotentialValueEdited(GtkCellRendererText *cellrenderertext,
				   gchar *path, gchar *text, gpointer user_data);
static void onShowHideAllButton(GtkButton *button, gpointer visibility);
static void onRangesChanged(VisuUiColorCombobox *colorComboBox, guint valId, gpointer data);
static void onEditPropertiesClicked(GtkButton *button, gpointer data);
static gboolean onTreeViewClicked(GtkWidget *widget, GdkEventButton *event,
				  gpointer user_data);
static gboolean onPropertiesClosed(GtkWidget *widget, GdkEvent *event, gpointer data);
static void onToolShadeChange(VisuUiShadeCombobox *combo, ToolShade *shade, gpointer data);
static void onMaskingToggled(GtkCellRendererToggle *cell_renderer,
			     gchar *path, gpointer user_data);
static void onTreeViewActivated(GtkTreeView *tree_view, GtkTreePath *path,
				GtkTreeViewColumn *column, gpointer user_data);
static void panelIsosurfacesUpdate_surfaceProperties();

/* Global widgets. */
static GtkWidget *edit_window;
static GtkWidget *edit_combo_color;
static GtkWidget *vboxColorur;
static GtkWidget *vboxToolShade;
static GtkWidget *shadeCombo;
static GtkWidget *auto_reorder;
static GtkWidget *buttonAddSurface;
static GtkWidget *buttonAddSpecial;
static GtkWidget *buttonRemoveSurface;
static GtkWidget *buttonEdit;
static GtkWidget *buttonOpen;
static GtkWidget *buttonConvert;
static GtkWidget *checkAutoLoad;
static GtkWidget *checkDrawIntra;
static GtkWidget *valueIO;

/* Global flags. */
static gboolean reverse_order = FALSE;
static gboolean autoload_file = FALSE;

/* New types. */
typedef void (*ChangesMethod)(GtkTreeModel *model, GtkTreeIter *iter, gpointer data);


/* Get the iter of the selected row. */
static gboolean getSelectedRow(GtkTreeModel **model, GtkTreeIter *iter)
{
  GtkTreeSelection* tree_selection = 
    gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));

  *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView));
  g_return_val_if_fail(GTK_IS_TREE_MODEL(*model), FALSE);

  if (!gtk_tree_selection_get_selected(tree_selection, NULL, iter))
    return FALSE;

  return TRUE;
}

/* Call @method on iter depending on selection.
   - If there is no selection, changes are applied on all surfaces.
   - If selection is on a file, changes are applied on all surfaces on that file.
   - If selection is on a surface, changes are applied on all surfaces
     of the same file.
   */
static void applyChanges(ChangesMethod method, gpointer value)
{
  gboolean valid;
  gboolean validParent, validChild;
  GtkTreeModel *model;
  GtkTreeIter iter, parent, child;
  
  DBG_fprintf(stderr, "Panel VisuSurface: run in the treeview to apply changes.\n");
  valid = getSelectedRow(&model, &iter);
  if (!valid)
    {
      DBG_fprintf(stderr, " | run on all row without recurse.\n");
      /* Nothing is selected, we call the changing method on all surfaces. */
      for (validParent = gtk_tree_model_get_iter_first(model, &parent); validParent;
           validParent = gtk_tree_model_iter_next(model, &parent))
        for (validChild = gtk_tree_model_iter_children(model, &child, &parent);
             validChild; validChild = gtk_tree_model_iter_next(model, &child))
          method(model, &child, value);
    }
  else
    {
      DBG_fprintf(stderr, " | run on all row of the current file.\n");
      /* Something is selected, run method on it and possible children. */
      method(model, &iter, value);
      for (validChild = gtk_tree_model_iter_children(model, &child, &iter);
           validChild; validChild = gtk_tree_model_iter_next(model, &child))
        method(model, &child, value);
    }
}

typedef struct _RowSurface RowSurface;
struct _RowSurface {
  guint refcount;
  GtkTreeRowReference *row;
  VisuGlExtSurfaces *set;
  VisuSurface *surf;
  gulong sig_surf;
  VisuSurfaceResource *res;
  gulong sig_notify;
};

static void onResourceNotify(VisuSurfaceResource *res _U_, GParamSpec *pspec _U_, RowSurface *row)
{
  GtkTreeModel *model;
  GtkTreePath *path;
  GtkTreeIter iter;

  model = gtk_tree_row_reference_get_model(row->row);
  path = gtk_tree_row_reference_get_path(row->row);
  g_return_if_fail(path);
  g_return_if_fail(gtk_tree_model_get_iter(model, &iter, path));

  gtk_tree_model_row_changed(model, path, &iter);

  gtk_tree_path_free(path);
}

static void onSurfaceNotify(VisuSurface *surf, GParamSpec *pspec _U_, RowSurface *row)
{
  VisuSurfaceResource *res;

  res = visu_surface_getResource(surf);
  if (res == row->res)
    return;

  /* Update listeners. */
  g_signal_handler_disconnect(G_OBJECT(row->res), row->sig_notify);
  g_object_unref(row->res);
  row->res = res;
  g_object_ref(row->res);
  row->sig_notify = g_signal_connect(G_OBJECT(row->res), "notify",
                                     G_CALLBACK(onResourceNotify), (gpointer)row);
}

static RowSurface* row_surface_copy(RowSurface *row)
{
  if (row)
    row->refcount += 1;
  DBG_fprintf(stderr, "Panel Surfaces: ref row %p (%d).\n", (gpointer)row, (row) ? (int)row->refcount : -1);
  return row;
}

static void row_surface_free(RowSurface *row)
{
  if (!row)
    return;

  row->refcount -= 1;
  DBG_fprintf(stderr, "Panel Surfaces: unref row %p (%d).\n", (gpointer)row, (row) ? (int)row->refcount : -1);
  if (!row->refcount)
    {
      g_signal_handler_disconnect(row->surf, row->sig_surf);
      g_object_unref(row->surf);
      g_signal_handler_disconnect(row->res, row->sig_notify);
      g_object_unref(row->res);
      gtk_tree_row_reference_free(row->row);
      visu_gl_ext_surfaces_remove(row->set, row->surf);
      g_free(row);
    }
}

#define TYPE_ROW_SURFACE (row_surface_get_type())
static GType row_surface_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id =
      g_boxed_type_register_static("RowSurface", (GBoxedCopyFunc)row_surface_copy,
                                   (GBoxedFreeFunc)row_surface_free);
  return g_define_type_id;
}

typedef struct _RowPotential RowPotential;
struct _RowPotential {
  guint refcount;
  GtkTreeRowReference *row;
  VisuScalarField *field;
  gulong sig_field;
  VisuScalarfieldSet *set;
  int labelId;
};

static RowPotential* row_potential_copy(RowPotential *row)
{
  if (row)
    row->refcount += 1;
  DBG_fprintf(stderr, "Panel Surfaces: ref pot row %p (%d).\n", (gpointer)row, (row) ? (int)row->refcount : -1);
  return row;
}

static void row_potential_free(RowPotential *row)
{
  if (!row)
    return;

  row->refcount -= 1;
  DBG_fprintf(stderr, "Panel Surfaces: unref pot row %p (%d).\n", (gpointer)row, (row) ? (int)row->refcount : -1);
  if (!row->refcount)
    {
      g_object_unref(row->set);
      g_signal_handler_disconnect(row->field, row->sig_field);
      g_object_unref(row->field);
      gtk_tree_row_reference_free(row->row);
      g_free(row);
    }
}

#define TYPE_ROW_POTENTIAL (row_potential_get_type())
static GType row_potential_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id =
      g_boxed_type_register_static("RowPotential", (GBoxedCopyFunc)row_potential_copy,
                                   (GBoxedFreeFunc)row_potential_free);
  return g_define_type_id;
}

static void connectSurface(GtkTreeStore *model, GtkTreeIter *iter,
                           VisuGlExtSurfaces *set, VisuSurface *surf)
{
  RowSurface *row;
  GtkTreePath *path = gtk_tree_model_get_path(GTK_TREE_MODEL(model), iter);
  float *pot;

  g_return_if_fail(VISU_IS_SURFACE(surf));

  row = g_malloc(sizeof(RowSurface));
  DBG_fprintf(stderr, "Panel Surfaces: create a new row %p for surface %p.\n",
              (gpointer)row, (gpointer)surf);
  row->refcount = 0;
  row->row = gtk_tree_row_reference_new(GTK_TREE_MODEL(model), path);
  gtk_tree_path_free(path);
  row->surf = surf;
  g_object_ref(row->surf);
  row->sig_surf = g_signal_connect(G_OBJECT(surf), "notify::resource",
                                   G_CALLBACK(onSurfaceNotify), (gpointer)row);
  row->res = visu_surface_getResource(surf);
  g_object_ref(row->res);
  row->sig_notify = g_signal_connect(G_OBJECT(row->res), "notify",
                                     G_CALLBACK(onResourceNotify), (gpointer)row);

  pot = visu_surface_getPropertyFloat(surf, VISU_SURFACE_PROPERTY_POTENTIAL);
  gtk_tree_store_set(model, iter,
                     TYPE_COLUMN, SURFACE_SURF,
                     BOX_ROW_COLUMN, row,
                     POTENTIAL_COLUMN, (pot) ? pot[0] : G_MAXFLOAT,
                     -1);
  row->set = set;
  visu_gl_ext_surfaces_add(set, surf);
  DBG_fprintf(stderr, "Panel Surfaces: creation done for %p (%d).\n",
              (gpointer)surf, G_OBJECT(surf)->ref_count);
}

static void onSurfaceAdded(VisuGlExtSurfaces *surfs,
                           VisuSurface *surf, GtkTreeStore *model)
{
  GtkTreeIter iter, child, *root;
  gboolean valid, valid2, found;
  RowSurface *row;
  RowPotential *field;
  VisuScalarField *origin;

  root = (GtkTreeIter*)0;
  origin = g_object_get_data(G_OBJECT(surf), "origin");
  found = FALSE;
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
       valid && !found; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
                         ROW_FIELD_COLUMN, &field,
                         BOX_ROW_COLUMN, &row, -1);
      if (row)
        {
          found = (row->surf == surf);
          row_surface_free(row);
        }
      if (field)
        {
          if (field->field == origin)
            root = gtk_tree_iter_copy(&iter);
          row_potential_free(field);
        }
      for (valid2 = gtk_tree_model_iter_children(GTK_TREE_MODEL(model), &child, &iter);
           valid2 && !found; valid2 = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &child))
        {
          gtk_tree_model_get(GTK_TREE_MODEL(model), &child,
                             BOX_ROW_COLUMN, &row, -1);
          if (row)
            {
              found = (row->surf == surf);
              row_surface_free(row);
            }
        }
    }
  if (found)
    return;
  gtk_tree_store_append(model, &iter, root);
  if (root)
    gtk_tree_iter_free(root);
  connectSurface(model, &iter, surfs, surf);
}

static void onSurfaceRemoved(VisuGlExtSurfaces *surfs _U_,
                             VisuSurface *surf, GtkTreeStore *model)
{
  GtkTreeIter iter;
  gboolean valid;
  RowSurface *row;

  valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
  while (valid)
    {
      gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
                         BOX_ROW_COLUMN, &row,
                         -1);
      if (row && row->surf == surf)
        valid = gtk_tree_store_remove(model, &iter);
      else
        valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter);
      if (row)
        row_surface_free(row);
    }
}

/***********************/
/* Visibility Handling */
/***********************/
static VisuSurfaceResource* resourceAt(GtkTreeModel *model, GtkTreeIter *iter)
{
  RowSurface *row;
  VisuSurfaceResource *res;

  gtk_tree_model_get(model, iter, BOX_ROW_COLUMN, &row, -1);
  if (row)
    {
      res = row->res;
      row_surface_free(row);
      return res;
    }
  return (VisuSurfaceResource*)0;
}
/* Change the visibility of one surface (given by its iter).
   The visibility is in value (using GPOINTER_TO_INT) and
   recurse is a flag to tell if changes must be propagated to
   all elements with the same name or not. */
static void showHideVisuSurface(GtkTreeModel *model, GtkTreeIter *iter, gpointer value)
{
  VisuSurfaceResource *res;

  res = resourceAt(model, iter);
  if (res)
    g_object_set(G_OBJECT(res), "rendered", (gboolean)GPOINTER_TO_INT(value), NULL);
}

/* Callback for the "Draw" check box. 
   If the selected surface were drawn, hides it and uncheck the button.
   If it was hidden, shows it and check the button. */
static void onDisplayToggled(GtkCellRendererToggle *cell_renderer, 
                             gchar *string_path, gpointer user_data)
{
  GtkTreePath* path = gtk_tree_path_new_from_string(string_path);
  GtkTreeIter iter;
  gboolean valid;

  valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(user_data), &iter, path);
  gtk_tree_path_free(path);
  if (!valid)
    return;

  DBG_fprintf(stderr, "Panel VisuSurface: toggle show box on '%s'.\n", string_path);
  /* We change the visibility of the surface (both resource and check box). */
  g_object_set(G_OBJECT(resourceAt(GTK_TREE_MODEL(user_data), &iter)), "rendered",
               !gtk_cell_renderer_toggle_get_active(cell_renderer), NULL);
}
static void onMaskingToggled(GtkCellRendererToggle *cell_renderer,
			     gchar *string_path, gpointer user_data)
{
  GtkTreePath* path = gtk_tree_path_new_from_string(string_path);
  GtkTreeIter iter;
  gboolean valid;

  valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(user_data), &iter, path);
  gtk_tree_path_free(path);
  if (!valid)
    return;

  DBG_fprintf(stderr, "Panel VisuSurface: toggle show box on '%s'.\n", string_path);
  /* We change the visibility of the surface (both resource and check box). */
  g_object_set(G_OBJECT(resourceAt(GTK_TREE_MODEL(user_data), &iter)), "maskable",
               !gtk_cell_renderer_toggle_get_active(cell_renderer), NULL);
}
/**
 * visu_ui_panel_surfaces_showAll:
 * @show: TRUE to show all surfaces, FALSE to hide them.
 * 
 * Shows or hides all surfaces and check their "draw" status in the panel accordingly.
 *
 * Returns: TRUE if surface list should be rebuild and redraw.
 **/
gboolean visu_ui_panel_surfaces_showAll(gboolean show)
{
  GtkTreeModel *model;
  GtkTreeIter iter, parent;
  gboolean valid;
  
  if (!getSelectedRow(&model, &iter))
    return FALSE;

  parent = iter;
  if ((gtk_tree_model_iter_n_children(model, &iter) > 0) ||
      gtk_tree_model_iter_parent(model, &parent, &iter))
    for (valid = gtk_tree_model_iter_children(model, &iter, &parent);
         valid; valid = gtk_tree_model_iter_next(model, &iter))
      showHideVisuSurface(model, &iter, GINT_TO_POINTER(show));
  else
    showHideVisuSurface(model, &iter, GINT_TO_POINTER(show));
  return TRUE;
}

void onShowHideAllButton(GtkButton *button _U_, gpointer visibility)
{
  visu_ui_panel_surfaces_showAll(GPOINTER_TO_INT(visibility));
}


/******************************/
/* Color & material Handling */
/******************************/
/* Changes the colour for a specific surface identified by its iter.
   If recurse is TRUE, changes are also applied on surfaces with
   the same name. */
void changeSurfaceColor(GtkTreeModel *model, GtkTreeIter *iter, gpointer colorComboBox)
{
  VisuSurfaceResource *res;
  float *rgba, *material;
  ToolColor *color;

  res = resourceAt(model, iter);
  if (!res)
    return;

  /* We get a color from the values of the ranges. */
  material = visu_ui_color_combobox_getRangeMaterial(VISU_UI_COLOR_COMBOBOX(colorComboBox));
  rgba = visu_ui_color_combobox_getRangeColor(VISU_UI_COLOR_COMBOBOX(colorComboBox));
  color = tool_color_new(rgba);

  g_object_set(G_OBJECT(res), "color", color, "material", material, NULL);

  g_free(material);
  g_free(rgba);
  g_free(color);
}

/* Callback for when the combo color is changed in the edit panel */
void onColorChanged(VisuUiColorCombobox *combo,
                    ToolColor *color _U_, gpointer data _U_)
{
  /* We change all surfaces depending on the selected one. */
  DBG_fprintf(stderr, "Panel VisuSurface: caught the 'color-selected' signal.\n");
  applyChanges(changeSurfaceColor, (gpointer)combo);
}

static void onRangesChanged(VisuUiColorCombobox *colorComboBox,
			    guint valId _U_, gpointer data _U_)
{
  /* We change all surfaces depending on the selected one. */
  DBG_fprintf(stderr, "Panel VisuSurface: caught the "
                      "'[material:color]-value-changed' signal.\n");
  applyChanges(changeSurfaceColor, (gpointer)colorComboBox);
}
static void onToolShadeChange(VisuUiShadeCombobox *combo _U_,
                              ToolShade *shade, gpointer data _U_)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter, child;
  int type;
  RowPotential *field;
  float value, alpha;
  ToolColor color;
  double minmax[2];
  RowSurface *row;
  VisuSurfaceResource *res;

  DBG_fprintf(stderr, "Panel VisuSurface: caught the "
                      "'shade-selected' signal.\n");

  valid = getSelectedRow(&model, &iter);
  g_return_if_fail(valid);

  gtk_tree_model_get(model, &iter,
                     TYPE_COLUMN, &type,
		     ROW_FIELD_COLUMN, &field, -1);
  g_return_if_fail(type == SURFACE_FILE_DENPOT && field);

  visu_scalar_field_getMinMax(field->field, minmax);
  row_potential_free(field);
  alpha = sqrt((float)gtk_tree_model_iter_n_children(model, &iter));

  /* For each children, we apply the shade. */
  for (valid = gtk_tree_model_iter_children(model, &child, &iter); valid;
       valid = gtk_tree_model_iter_next(model, &child))
    {
      gtk_tree_model_get(model, &child,
			 TYPE_COLUMN, &type,
			 POTENTIAL_COLUMN, &value,
			 BOX_ROW_COLUMN, &row, -1);
      g_return_if_fail(type == SURFACE_SURF);

      tool_shade_valueToRGB(shade, color.rgba,
                            (value - minmax[0]) / (minmax[1] - minmax[0]));
      color.rgba[3] = exp(-alpha * (1. - (value - minmax[0]) / (minmax[1] - minmax[0])));

      res = visu_surface_resource_new_fromCopy((const gchar*)0,
                                               visu_surface_getResource(row->surf));
      g_object_set(G_OBJECT(res), "color", &color, NULL);
      visu_surface_setResource(row->surf, res);
      g_object_unref(G_OBJECT(res));
      row_surface_free(row);
    }
}

/**
 * visu_ui_panel_surfaces_editProperties:
 * @iter: the currently selected row iter (or NULL).
 *
 * Opens a new window allowing to edit surface properties.
 */
void visu_ui_panel_surfaces_editProperties(GtkTreeIter *iter)
{
  GtkWidget *hbox, *label, *expand, *notebook;
  gint type;

  if (!GTK_IS_WINDOW(edit_window))
    {
      edit_window = gtk_dialog_new_with_buttons
	(_("Edit surface properties"),
	 GTK_WINDOW(visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelSurfaces))), 0,
	 TOOL_ICON_CLOSE, GTK_RESPONSE_ACCEPT, NULL);

      gtk_window_set_default_size(GTK_WINDOW(edit_window), 320, -1);
      gtk_window_set_type_hint(GTK_WINDOW(edit_window), GDK_WINDOW_TYPE_HINT_UTILITY);
      gtk_window_set_skip_pager_hint(GTK_WINDOW(edit_window), TRUE);
      gtk_container_set_border_width(GTK_CONTAINER(edit_window), 3);

      notebook = gtk_notebook_new();
      gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(edit_window))),
			 notebook, TRUE, TRUE, 0);

      /* Create the colour tab. */
      vboxColorur = gtk_vbox_new(FALSE, 0);
      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vboxColorur,
			       gtk_label_new(_("Color")));

      hbox = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(vboxColorur), hbox, FALSE, FALSE, 0);

      label = gtk_label_new(_("Color: "));
      gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);

      edit_combo_color = visu_ui_color_combobox_newWithRanges(TRUE);
      visu_ui_color_combobox_setExpanded(VISU_UI_COLOR_COMBOBOX(edit_combo_color), TRUE);
      gtk_box_pack_start(GTK_BOX(hbox), edit_combo_color, TRUE, TRUE, 2);

      expand = visu_ui_color_combobox_getRangeWidgets(VISU_UI_COLOR_COMBOBOX(edit_combo_color));
      gtk_box_pack_start(GTK_BOX(vboxColorur), expand, FALSE, FALSE, 0);

      /* Set the callbacks. */
      g_signal_connect(G_OBJECT(edit_window), "response",
		       G_CALLBACK(gtk_widget_hide), (gpointer)0);
      g_signal_connect(G_OBJECT(edit_window), "delete-event",
		       G_CALLBACK(onPropertiesClosed), (gpointer)0);
      g_signal_connect(G_OBJECT(edit_window), "destroy-event",
		       G_CALLBACK(onPropertiesClosed), (gpointer)0);
      g_signal_connect(G_OBJECT(edit_combo_color), "color-selected",
		       G_CALLBACK(onColorChanged), (gpointer)0);
      g_signal_connect(G_OBJECT(edit_combo_color), "material-value-changed",
		       G_CALLBACK(onRangesChanged), (gpointer)0);
      g_signal_connect(G_OBJECT(edit_combo_color), "color-value-changed",
		       G_CALLBACK(onRangesChanged), (gpointer)0);

     /* Create the shade tab. */
      vboxToolShade = gtk_vbox_new(FALSE, 0);
      gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vboxToolShade,
			       gtk_label_new(_("Shade")));
      
      label = gtk_label_new(_("Apply a shade to the current surfaces of"
			      " the selected scalar field."));
      gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_FILL);
      gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
      gtk_box_pack_start(GTK_BOX(vboxToolShade), label, FALSE, FALSE, 5);

      hbox = gtk_hbox_new(FALSE, 0);
      gtk_box_pack_start(GTK_BOX(vboxToolShade), hbox, FALSE, FALSE, 5);

      label = gtk_label_new(_("ToolShade: "));
      gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

      shadeCombo = visu_ui_shade_combobox_new(TRUE, TRUE);
      gtk_box_pack_start(GTK_BOX(hbox), shadeCombo, TRUE, TRUE, 0);

      /* Set the callbacks. */
      g_signal_connect(G_OBJECT(shadeCombo), "shade-selected",
		       G_CALLBACK(onToolShadeChange), (gpointer)0);

      gtk_widget_show_all(edit_window);
    }
  else
    gtk_window_present(GTK_WINDOW(edit_window));
  
  /* Set the radio combo. */
  if (iter)
    gtk_tree_model_get(GTK_TREE_MODEL(treeStore), iter,
		       TYPE_COLUMN, &type, -1);
  else
    type = SURFACE_FILE_DENPOT;

  panelIsosurfacesUpdate_surfaceProperties();
}
static void panelIsosurfacesUpdate_surfaceProperties()
{
  gboolean valid;
  int type;
  GtkTreeModel *model;
  GtkTreeIter iter;
  ToolShade *shade;
  float material[5];
  RowSurface *row;

  shade = (ToolShade*)0;
  type = SURFACE_SURF;
  valid = getSelectedRow(&model, &iter);
  if (valid)
    {
      gtk_tree_model_get(model, &iter,
			 TYPE_COLUMN, &type,
			 SHADE_COLUMN, &shade, -1);

      if (type == SURFACE_SURF)
        {
          gtk_tree_model_get(model, &iter,
                             BOX_ROW_COLUMN, &row, -1);
          visu_ui_color_combobox_setRangeColor
            (VISU_UI_COLOR_COMBOBOX(edit_combo_color),
             visu_surface_resource_getColor(row->res)->rgba, FALSE);
          visu_ui_color_combobox_setRangeMaterial
            (VISU_UI_COLOR_COMBOBOX(edit_combo_color),
             visu_surface_resource_getMaterial(row->res), FALSE);
          row_surface_free(row);
        }
      else
        {
          material[0] = 0.2f;
          material[1] = 1.0f;
          material[2] = 0.5f;
          material[3] = 0.5f;
          material[4] = 0.0f;
          visu_ui_color_combobox_setRangeMaterial
            (VISU_UI_COLOR_COMBOBOX(edit_combo_color), material, FALSE);
        }
    }
  
  /* Set widgets sensitive or not. */
  if (vboxToolShade)
    {
      gtk_widget_set_sensitive(vboxToolShade, (type == SURFACE_FILE_DENPOT));
      visu_ui_shade_combobox_setSelectionByShade(VISU_UI_SHADE_COMBOBOX(shadeCombo), shade);
    }
}

static gboolean onPropertiesClosed(GtkWidget *widget,
				   GdkEvent *event _U_, gpointer data _U_)
{
  gtk_widget_hide(widget);
  return TRUE;
}


static void onEditPropertiesClicked(GtkButton *button _U_, gpointer data _U_)
{
  GtkTreeModel *model;
  GtkTreeIter iter;
  gboolean valid;

  DBG_fprintf(stderr, "Panel VisuSurface: clicked on properties button.\n");
  valid = getSelectedRow(&model, &iter);
  if (valid)
    visu_ui_panel_surfaces_editProperties(&iter);
  else
    visu_ui_panel_surfaces_editProperties((GtkTreeIter*)0);
}



/* See header for more info */
static void onOpenClicked(GtkButton *button _U_, gpointer data)
{
  char *file_choosed;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips = gtk_tooltips_new ();
#endif
  GtkWidget *dialog;
  gboolean invalid, redraw;
  VisuData *dataObj;
  float trans[3] = {0.f, 0.f, 0.f};
  VisuScalarFieldMethod *fmt;
  GtkTreeModel *model;
  GtkTreeIter iter;
  RowSurface *row;
  RowPotential *field;

  dialog = visu_ui_field_chooser_new((GtkWindow*)0);

  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelSurfaces));
  redraw = FALSE;
  invalid = TRUE;
  while(invalid)
    {
      switch(gtk_dialog_run(GTK_DIALOG(dialog)))
	{
	case GTK_RESPONSE_ACCEPT:
	  file_choosed = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
          fmt = visu_ui_field_chooser_getFileFormat(VISU_UI_FIELD_CHOOSER(dialog));
          g_object_set(VISU_GL_NODE_SCENE(data), "auto-adjust", (visu_ui_field_chooser_getFit(VISU_UI_FIELD_CHOOSER(dialog)) == VISU_UI_FIT_TO_BOX), NULL);
	  if(visu_ui_panel_surfaces_loadFile(file_choosed, &iter, (GHashTable*)0, fmt))
	    {
	      redraw = TRUE;
	      invalid = FALSE;
	    }
	  break;
	default:
	  invalid = FALSE;
	}
    }

  /* We update the VisuData bounding box. */
  if (redraw && visu_ui_field_chooser_getFit(VISU_UI_FIELD_CHOOSER(dialog)) ==
      VISU_UI_FIT_TO_SURFACE)
    {
      getSelectedRow(&model, &iter);
      gtk_tree_model_get(model, &iter,
                         BOX_ROW_COLUMN, &row,
                         ROW_FIELD_COLUMN, &field,
                         -1);
      DBG_fprintf(stderr, "Panel Surfaces: update data bounding box.\n");
      if (field)
        {
          visu_boxed_setBox(VISU_BOXED(dataObj), VISU_BOXED(field->field));
          row_potential_free(field);
        }
      else if (row)
        {
          visu_boxed_setBox(VISU_BOXED(dataObj), VISU_BOXED(row->surf));
          row_surface_free(row);
        }
      visu_pointset_setTranslationPeriodic(VISU_POINTSET(dataObj), trans, FALSE);
    }

  /* Free everything. */
  DBG_fprintf(stderr, "Panel Surfaces: free load dialog.\n");
  gtk_widget_destroy(dialog);
}
static gchar* _getFieldLabel(const VisuScalarfieldSet *set,
                             const VisuScalarField *field)
{
  double minmax[2];

  if (visu_scalar_field_isEmpty(field))
    return g_strdup_printf(_("<b>%s</b>\n  <span size=\"smaller\"><i>"
                             "no data</i></span>"),
                           visu_scalarfield_set_getLabel(set, field));
  else
    {
      visu_scalar_field_getMinMax(field, minmax);
      return g_strdup_printf(_("<b>%s</b>\n  <span size=\"smaller\"><i>"
                               "Den./pot. data (min|max)\n  %g | %g</i></span>"),
                             visu_scalarfield_set_getLabel(set, field),
                             minmax[0], minmax[1]);
    }
}
static void onFieldChanged(VisuScalarField *field _U_, RowPotential *row)
{
  GtkTreeModel *model;
  GtkTreePath *path;
  GtkTreeIter iter;
  gchar *label;

  model = gtk_tree_row_reference_get_model(row->row);
  path = gtk_tree_row_reference_get_path(row->row);
  g_return_if_fail(path);
  g_return_if_fail(gtk_tree_model_get_iter(model, &iter, path));
  gtk_tree_path_free(path);

  label = _getFieldLabel(row->set, row->field);
  if (GTK_IS_LIST_STORE(model))
    gtk_list_store_set(GTK_LIST_STORE(model), &iter, row->labelId, label, -1);
  if (GTK_IS_TREE_STORE(model))
    gtk_tree_store_set(GTK_TREE_STORE(model), &iter, row->labelId, label, -1);

  g_free(label);
}
static RowPotential* _newRowField(GtkTreeModel *model, GtkTreeIter *iter, int labelId,
                                  VisuScalarfieldSet *set, VisuScalarField *field)
{
  RowPotential *row;
  GtkTreePath *path = gtk_tree_model_get_path(model, iter);

  row = g_malloc(sizeof(RowPotential));
  DBG_fprintf(stderr, "Panel Surfaces: create a new row %p for potential %p.\n",
              (gpointer)row, (gpointer)field);
  row->refcount = 0;
  row->row = gtk_tree_row_reference_new(model, path);
  gtk_tree_path_free(path);
  row->labelId = labelId;
  row->set = g_object_ref(set);
  row->field = g_object_ref(field);
  row->sig_field = g_signal_connect(G_OBJECT(field), "changed",
                                    G_CALLBACK(onFieldChanged), (gpointer)row);
  DBG_fprintf(stderr, "Panel Surfaces: creation done for %p (%d).\n",
              (gpointer)field, G_OBJECT(field)->ref_count);
  return row;
}
static void _addField(VisuScalarfieldSet *set, VisuScalarField *field,
                      GtkListStore *model)
{
  gchar *label;
  GtkTreeIter iter;

  label = _getFieldLabel(set, field);

  gtk_list_store_append(model, &iter);
  gtk_list_store_set(model, &iter,
                     VISU_UI_SURFACES_FIELD_LABEL, label,
                     VISU_UI_SURFACES_FIELD_ROW, _newRowField(GTK_TREE_MODEL(model),
                                                              &iter, VISU_UI_SURFACES_FIELD_LABEL,
                                                              set, field),
                     -1);

  gtk_tree_store_append(treeStore, &iter, (GtkTreeIter*)0);
  gtk_tree_store_set(treeStore, &iter,
                     LABEL_COLUMN, label,
                     TYPE_COLUMN, SURFACE_FILE_DENPOT,
                     ROW_FIELD_COLUMN, _newRowField(GTK_TREE_MODEL(treeStore),
                                                    &iter, LABEL_COLUMN, set, field),
                     -1);
  DBG_fprintf(stderr, " | field %p.\n", (gpointer)field);
  g_free(label);
}
static void _removeField(VisuScalarfieldSet *set _U_, VisuScalarField *field,
                         GtkListStore *model)
{
  gboolean valid;
  GtkTreeIter iter;
  RowPotential *row;
  
  valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(model), &iter,
                         VISU_UI_SURFACES_FIELD_ROW, &row,
                         -1);
      if (row->field == field)
        {
          row_potential_free(row);
          gtk_list_store_remove(model, &iter);
          break;
        }
      row_potential_free(row);
    }

  valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStore), &iter);
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStore), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(treeStore), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &iter,
                         ROW_FIELD_COLUMN, &row,
                         -1);
      if (row->field == field)
        {
          row_potential_free(row);
          gtk_tree_store_remove(treeStore, &iter);
          break;
        }
      row_potential_free(row);
    }
}
/**
 * visu_ui_panel_surfaces_addSurfaces:
 * @surfs: (element-type VisuSurface*): a #VisuSurface object.
 * @name: a name @surf comes from.
 * @iter: (out caller-allocates): a location to store the iter.
 * 
 * This routine can be used to add a #VisuSurface to the tree
 * view. @iter is then populated with the row it has been inserted to.
 *
 * Since: 3.7
 **/
void visu_ui_panel_surfaces_addSurfaces(GList *surfs, const gchar *name,
                                        GtkTreeIter *iter)
{
  gchar *label;
  GList *it;
  GtkTreeIter child;
  
  label = g_strdup_printf(_("<b>%s</b>\n  <span size=\"smaller\"><i>"
                            "Surfaces data</i></span>"), name);

  gtk_tree_store_append(treeStore, iter, (GtkTreeIter*)0);
  gtk_tree_store_set(treeStore, iter,
                     LABEL_COLUMN, label,
                     TYPE_COLUMN, SURFACE_FILE_SURF,
                     -1);
  g_free(label);

  for (it = surfs; it; it = g_list_next(it))
    visu_ui_panel_surfaces_addSurface(VISU_SURFACE(it->data), iter, &child);
}
/**
 * visu_ui_panel_surfaces_addSurface:
 * @surf: (transfer full): a #VisuSurface object.
 * @root: (allow-none): the iter to attach the surface to.
 * @iter: (out caller-allocates): a location to store the iter.
 * 
 * This routine can be used to add a #VisuSurface to the tree
 * view. @iter is then populated with the row it has been inserted to.
 *
 * Since: 3.7
 **/
void visu_ui_panel_surfaces_addSurface(VisuSurface *surf,
                                       GtkTreeIter *root, GtkTreeIter *iter)
{
  GtkTreePath *path;
  VisuGlExtSurfaces *surfs;

  DBG_fprintf(stderr, "Panel Surfaces: adding surface %p (%d).\n",
              (gpointer)surf, G_OBJECT(surf)->ref_count);
  gtk_tree_store_append(treeStore, iter, root);
  surfs = visu_gl_node_scene_addSurfaces(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  connectSurface(treeStore, iter, surfs, surf);
  g_object_unref(surf);
  if (root)
    {
      path = gtk_tree_model_get_path(GTK_TREE_MODEL(treeStore), root);
      gtk_tree_view_expand_row(GTK_TREE_VIEW(treeView), path, TRUE);
      gtk_tree_path_free(path);
    }
  DBG_fprintf(stderr, "Panel Surfaces: addition done for surface %p (%d).\n",
              (gpointer)surf, G_OBJECT(surf)->ref_count);
}
static void onFieldReady(GObject *obj _U_, GAsyncResult *res, gpointer data _U_)
{
  gboolean valid;
  GError *error;

  error = (GError*)0;
  valid = g_task_propagate_boolean(G_TASK(res), &error);
  if (!valid)
    visu_ui_raiseWarning(_("Loading a file"),
                         (error) ? error->message : _("Unknown error"),
                         (GtkWindow*)0);
  g_clear_error(&error);
}
static gboolean _loadScalarField(const gchar *filename,
                                 VisuScalarFieldMethod *meth,
                                 GHashTable *table)
{
  return visu_scalarfield_set_addFromFile(visu_scalarfield_set_getDefault(),
                                          meth, filename, table, NULL,
                                          onFieldReady, NULL);
}
static gboolean visu_ui_panel_surfaces_loadSurface(const gchar *filename,
                                                   GList **list)
{
  gboolean valid;
  GError *error;
  GList *tmplst;

  g_return_val_if_fail(list, FALSE);

  error = (GError*)0;
  valid = visu_surface_loadFile(filename, list, &error);
  if (!valid)
    {
      if (error)
	g_error_free(error);
      return FALSE;
    }

  if (error)
    {
      visu_ui_raiseWarning(_("Loading a file"),
                           error->message, (GtkWindow*)0);
      g_error_free(error);
      for (tmplst = *list; tmplst; tmplst = g_list_next(tmplst))
        g_object_unref(G_OBJECT(tmplst->data));
      g_list_free(*list);
      return TRUE;
    }

  return TRUE;
}
/**
 * visu_ui_panel_surfaces_loadFile:
 * @file_name: (type filename): the file you want to try to load
 * @iter: (out caller-allocates): a location to store the #GtkTreeIter
 * that will be set.
 * @table: (allow-none): a set of different #Option (can be NULL).
 * @meth: a #VisuScalarFieldMethod object.
 * 
 * Tries to load the given @file_name  and if it succeeds, adds loaded surfaces 
 * to the isosurfaces panel. If @file_name is a #VisuScalarField then,
 * @meth is used to load it. If @fitToBox is not %NULL, the load
 * surfaces or scalar fields are fit to it.
 * 
 * Return value: TRUE in case of success.
 */
gboolean visu_ui_panel_surfaces_loadFile(const char* file_name,
                                         GtkTreeIter *iter,
                                         GHashTable *table,
                                         VisuScalarFieldMethod *meth)
{
  gchar *name;
  gboolean valid;
  /* gchar *filename; */
  int type;
  GList *list;

  g_return_val_if_fail(file_name, FALSE);

  list = (GList*)0;
  valid = visu_ui_panel_surfaces_loadSurface(file_name, &list);
  if (valid)
    {
      if (!list)
        return FALSE;
      type = SURFACE_FILE_SURF;
    }
  else
    {
      valid = _loadScalarField(file_name, meth, table);
      if (valid)
        type = SURFACE_FILE_DENPOT;
    }
  if (!valid)
    /* Not a surface nor a density/potential file. */
    return FALSE;

  /* Before adding it to the tree, we may remove all identic
     already existing file. */
  /* DBG_fprintf(stderr, "Panel VisuSurface: may remove an existing entry.\n"); */
  /* valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(isosurfaces_data_list), */
  /*       				&iter); */
  /* while (valid) */
  /*   { */
  /*     gtk_tree_model_get(GTK_TREE_MODEL(isosurfaces_data_list), */
  /*       		 &iter, FILELABEL_COLUMN, &filename, -1); */
  /*     iter2 = iter; */
  /*     valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(isosurfaces_data_list), */
  /*       			       &iter); */
  /*     if (!strcmp(filename, file_name)) */
  /*       panel_isosurfaces_remove(&iter2); */
  /*   } */

  /* We create a root element for the file. */
  DBG_fprintf(stderr, "Panel VisuSurface: add a new root entry '%s'.\n", file_name);
  name = g_path_get_basename(file_name);
  if (type == SURFACE_FILE_SURF)
    {
      visu_ui_panel_surfaces_addSurfaces(list, name, iter); 
      gtk_tree_selection_select_iter
        (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)), iter);
    }
  g_free(name);
  g_list_free(list);


  return TRUE;
}

/* See header file for more info */
static gboolean isosurfaces_show_next()
{
  GtkTreeModel *model;
  GtkTreeIter iter, selected_iter;
  GtkTreeSelection* selection;

  if (!getSelectedRow(&model, &selected_iter))
    return FALSE;

  if (gtk_tree_model_iter_children(model, &iter, &selected_iter))
    selected_iter = iter;

  iter = selected_iter;
  if(reverse_order == FALSE)
    {
      if (!gtk_tree_model_iter_next(model, &iter))
	{
	  reverse_order = TRUE;
          iter = selected_iter;
	  if (!gtk_tree_model_iter_previous(model, &iter))
	    return FALSE;
	}
    }
  else
    {
      if (!gtk_tree_model_iter_previous(model, &iter))
	{
	  reverse_order = FALSE;
          iter = selected_iter;
	  if (!gtk_tree_model_iter_next(model, &iter))
	    return FALSE;
	}
    }
  showHideVisuSurface(model, &selected_iter, GINT_TO_POINTER(FALSE));
  showHideVisuSurface(model, &iter, GINT_TO_POINTER(TRUE));

  /* Set the selection to the new row. */
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView));
  gtk_tree_selection_select_iter(selection, &iter);

  return TRUE;
}

static void onPlayStop(gpointer data)
{
  gtk_button_set_label(GTK_BUTTON(data), _("Play"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), FALSE);
}

/* Callback for the "play" button. Registers the above function to be called at a specified time
   interval. */
static void isosurfaces_play(GtkToggleButton *play_stop, GtkWidget *time_interval)
{
  static int event_id = 0;

  if(gtk_toggle_button_get_active(play_stop) == TRUE)
    {
      gtk_button_set_label(GTK_BUTTON(play_stop), _("Stop"));
      visu_ui_panel_surfaces_showAll(FALSE);
      event_id = g_timeout_add_full(G_PRIORITY_DEFAULT,
                                    gtk_spin_button_get_value(GTK_SPIN_BUTTON(time_interval)),
                                    isosurfaces_show_next, play_stop, onPlayStop);
    }
  else
    g_source_remove(event_id);
}

static void onDisplayLabel(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                           GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  const gchar *resLbl;
  gchar *label;
  RowSurface *row;

  gtk_tree_model_get(model, iter, BOX_ROW_COLUMN, &row, -1);
  if (row)
    {
      resLbl = visu_surface_resource_getLabel(row->res);
      row_surface_free(row);
      label = g_strdup((resLbl) ? resLbl : VISU_UI_SURFACE_NAME_CHOOSE);
    }
  else
    gtk_tree_model_get(model, iter, LABEL_COLUMN, &label, -1);
  g_object_set(G_OBJECT(cell), "markup", label,
               "foreground", (row != NULL) ? "blue" : NULL,
               "editable", (row != NULL), NULL);
  g_free(label);
}
static void onDisplayShow(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                          GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  RowSurface *row;

  gtk_tree_model_get(model, iter, BOX_ROW_COLUMN, &row, -1);
  g_object_set(G_OBJECT(cell), "visible", (row != NULL),
               "active", (row) ? visu_surface_resource_getRendered(row->res) : FALSE, NULL);
  if (row)
    row_surface_free(row);
}
static void onDisplayPot(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  float pot;
  GtkTreeIter parent;
  int type, kind;
  gchar *lbl;

  gtk_tree_model_get(model, iter, POTENTIAL_COLUMN, &pot, TYPE_COLUMN, &kind, -1);
  type = SURFACE_SURF;
  if (gtk_tree_model_iter_parent(model, &parent, iter))
    gtk_tree_model_get(model, &parent, TYPE_COLUMN, &type, -1);
  lbl = g_strdup_printf("%g", pot);
  g_object_set(G_OBJECT(cell),
               "visible", (kind == SURFACE_SURF && pot != G_MAXFLOAT),
               "text", lbl,
               "editable", (type == SURFACE_FILE_DENPOT),
               "foreground", (type == SURFACE_FILE_DENPOT) ? "blue" : NULL,
               NULL);
  g_free(lbl);
}
static void onDisplayColor(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                           GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  RowSurface *row;
  GdkPixbuf *pixbuf;

  gtk_tree_model_get(model, iter, BOX_ROW_COLUMN, &row, -1);
  if (row)
    {
      pixbuf = tool_color_get_stamp(visu_surface_resource_getColor(row->res), TRUE);
      g_object_set(cell, "pixbuf", pixbuf, NULL);
      g_object_unref(pixbuf);
      row_surface_free(row);
    }
  g_object_set(cell, "visible", (row != NULL), NULL);
}
static void onDisplayMask(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                          GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  RowSurface *row;

  gtk_tree_model_get(model, iter, BOX_ROW_COLUMN, &row, -1);
  g_object_set(G_OBJECT(cell), "visible", (row != NULL),
               "active", (row) ? visu_surface_resource_getMaskable(row->res) : FALSE, NULL);
  if (row)
    row_surface_free(row);
}
/* Sets up the model/view architecture used to store the surfaces info. */
static void isosurfaces_make_tree_view(VisuGlExtSurfaces *surfs)
{
  GtkWidget *image;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column = NULL;

  treeStore = gtk_tree_store_new(N_COLUMNS,
                                 G_TYPE_INT, /* TYPE_COLUMN */
                                 G_TYPE_STRING, /* LABEL_COLUMN */
                                 G_TYPE_FLOAT, /* POTENTIAL_COLUMN */
                                 TOOL_TYPE_SHADE, /* SHADE_COLUMN */
                                 TYPE_ROW_POTENTIAL, /* ROW_FIELD_COLUMN */
                                 TYPE_ROW_SURFACE); /* BOX_ROW_COLUMN
                                                       */
  g_signal_connect(surfs, "added", G_CALLBACK(onSurfaceAdded), (gpointer)treeStore);
  g_signal_connect(surfs, "removed", G_CALLBACK(onSurfaceRemoved), (gpointer)treeStore);

  treeView = 
    gtk_tree_view_new_with_model(GTK_TREE_MODEL(treeStore));

  renderer = gtk_cell_renderer_text_new();
  g_signal_connect(G_OBJECT(renderer), "edited",
		   G_CALLBACK(onNameEdited), treeStore);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, onDisplayLabel,
                                          (gpointer)0, (GDestroyNotify)0);
  gtk_tree_view_column_set_title(column, _("File / label"));
  gtk_tree_view_column_set_expand(column, TRUE);
  gtk_tree_view_column_set_alignment(column, 0);
  gtk_tree_view_column_set_resizable(column, TRUE);
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

  renderer = gtk_cell_renderer_toggle_new();
  g_signal_connect(renderer, "toggled", G_CALLBACK(onDisplayToggled), treeStore);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, onDisplayShow,
                                          treeStore, (GDestroyNotify)0);
  gtk_tree_view_column_set_expand(column, FALSE);
  gtk_tree_view_column_set_alignment(column, 0.5);
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

  renderer = gtk_cell_renderer_text_new();
  g_signal_connect(G_OBJECT(renderer), "edited",
		   G_CALLBACK(onPotentialValueEdited), treeStore);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, onDisplayPot,
                                          treeStore, (GDestroyNotify)0);
  gtk_tree_view_column_set_title(column, _("Value"));
  gtk_tree_view_column_set_sort_column_id(GTK_TREE_VIEW_COLUMN(column),
					  POTENTIAL_COLUMN);
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

  renderer = gtk_cell_renderer_pixbuf_new ();
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, onDisplayColor,
                                          treeStore, (GDestroyNotify)0);
  image = gtk_image_new_from_icon_name("applications-graphics",
                                       GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_widget_show(image);
  gtk_tree_view_column_set_widget(column, image);
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

  colorColumn = column;

  renderer = gtk_cell_renderer_toggle_new();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onMaskingToggled), treeStore);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, onDisplayMask,
                                          treeStore, (GDestroyNotify)0);
  image = create_pixmap((GtkWidget*)0, "stock-masking.png");
  gtk_widget_show(image);
  gtk_tree_view_column_set_widget(column, image);
  gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeView), TRUE);  

  g_signal_connect(gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)),
		   "changed", G_CALLBACK(onTreeSelectionChanged), (gpointer)0);
  g_signal_connect(G_OBJECT(treeView), "button-release-event",
		   G_CALLBACK(onTreeViewClicked), (gpointer)0);
  g_signal_connect(G_OBJECT(treeView), "row-activated",
		   G_CALLBACK(onTreeViewActivated), (gpointer)0);
}

/* Callback for time interval value spin button.
   Changes the frequency used to play the surfaces. */
static void isosurfaces_change_time_interval(GtkWidget *spin_button _U_,
				      GtkWidget *play_stop)
{
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_stop), FALSE);
}

static void _compute(GtkTreeIter *iter, const float *values,
                     const gchar **names, guint nValues)
{
  VisuSurface *surf;
  RowPotential *row;
  guint i, type;
  GtkTreeIter child;

  gtk_tree_model_get(GTK_TREE_MODEL(treeStore), iter,
                     TYPE_COLUMN, &type,
                     ROW_FIELD_COLUMN, &row, -1);
  g_return_if_fail(type == SURFACE_FILE_DENPOT);

  for (i = 0; i < nValues; i++)
    {
      surf = visu_surface_new_fromScalarField(row->field, values[i], names[i]);
      if (surf)
        visu_ui_panel_surfaces_addSurface(surf, iter, &child);	  
    }
  row_potential_free(row);
}
static gboolean panel_isosurfaces_remove(GtkTreeModel *model, GtkTreeIter *iter)
{
  int type;
  gboolean valid;
  GtkTreeIter parent, child;

  g_return_val_if_fail(iter, FALSE);

  gtk_tree_model_get(model, iter, TYPE_COLUMN, &type, -1);
  
  switch (type)
    {
    case SURFACE_FILE_DENPOT:
      /* If the denpot has children, we remove them.
	 And don't delete the data. */
      if (gtk_tree_model_iter_has_child(model, iter))
	{
          for (valid = gtk_tree_model_iter_children(model, &child, iter);
               valid; valid = gtk_tree_model_iter_children(model, &child, iter))
            gtk_tree_store_remove(GTK_TREE_STORE(model), &child);
          visu_ui_value_io_setSensitiveSave(VISU_UI_VALUE_IO(valueIO), FALSE);
          return TRUE;
	}
      else
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
      return TRUE;
    case SURFACE_FILE_SURF:
      /* We remove the entry from the tree. */
      gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
      /* We need to rebuild the order list. */
      return TRUE;
    case SURFACE_SURF:
      if (gtk_tree_model_iter_parent(model, &parent, iter))
        {
	  gtk_tree_model_get(model, &parent, TYPE_COLUMN, &type, -1);
	  if (type == SURFACE_FILE_SURF &&
              gtk_tree_model_iter_n_children(model, &parent) == 1)
            gtk_tree_store_remove(GTK_TREE_STORE(model), &parent);
          else
            gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
        }
      else
        gtk_tree_store_remove(GTK_TREE_STORE(model), iter);
      /* We need to rebuild the order list. */
      return TRUE;
    default:
      g_warning("Wrong type for the selected entry.");
    }
  return FALSE;
}


static void _destroyStore(GtkTreeStore *model)
{
  DBG_fprintf(stderr, "Panel Surfaces: destroying surfaces model.\n");

  gtk_tree_store_clear(model);
  g_object_unref(model);
}

/* Creates the main panel. */
void isosurfaces_create_gtk_interface(VisuUiPanel *visu_ui_panel)
{
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips = gtk_tooltips_new ();
#endif
  GtkWidget *top_hbox = gtk_hbox_new (FALSE, 0);
  GtkWidget *h_box3 = gtk_hbox_new (FALSE, 0);
  GtkWidget *show_all = gtk_button_new();
  GtkWidget *show_none = gtk_button_new();
  GtkWidget *play_stop =
    gtk_toggle_button_new_with_label(_("Play"));
  GtkWidget *time_interval = 
    gtk_spin_button_new_with_range  (50, 2000, 10);
  GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
/*   GtkWidget *reorder_button = gtk_button_new_with_label("Re-order"); */
  GtkWidget *image_color = create_pixmap((GtkWidget*)0, "stock_effects-object-colorize_20.png");
  GtkWidget *image_show = create_pixmap((GtkWidget*)0, "stock-select-all_20.png");
  GtkWidget *image_hide = create_pixmap((GtkWidget*)0, "stock-unselect-all_20.png");
  GtkWidget *tree_vbox = gtk_vbox_new(FALSE, 0);
  GtkWidget *tree_hbox = gtk_hbox_new(FALSE, 0);
  GtkWidget *image, *vbox, *wd;
  VisuGlExtSurfaces *surfs;
  VisuGlNodeScene *scene;

  vbox = gtk_vbox_new(FALSE, 0);

  scene = visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering());
  surfs = visu_gl_node_scene_addSurfaces(scene);

  wd = gtk_check_button_new_with_mnemonic(_("_Use isosurfaces"));
  g_object_bind_property(surfs, "active", wd, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  buttonEdit = gtk_button_new();
  gtk_widget_set_sensitive(buttonEdit, FALSE);

  gtk_container_add(GTK_CONTAINER(buttonEdit), image_color);
  gtk_container_add(GTK_CONTAINER(show_all), image_show);
  gtk_container_add(GTK_CONTAINER(show_none), image_hide);

  /* The line about loading files. */
  gtk_box_pack_start(GTK_BOX(vbox), top_hbox, FALSE, FALSE, 0);
  checkAutoLoad = gtk_check_button_new_with_mnemonic(_("Auto _load data file"));
  gtk_widget_set_margin_top(checkAutoLoad, 10);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkAutoLoad), autoload_file);
  gtk_widget_set_tooltip_text(checkAutoLoad,
			_("Try to load a data file whenever a new V_Sim file is loaded."
			  " If the new file contains a scalar field, it is loaded, otherwise"
			  " a surface file is tested using a .surf extension on the file name."));
  gtk_box_pack_start(GTK_BOX(top_hbox), checkAutoLoad, TRUE, TRUE, 2);
  buttonOpen = gtk_button_new();
  buttonConvert = gtk_button_new_with_mnemonic(_("_Convert"));
  gtk_widget_set_tooltip_text(buttonOpen, _("Load a surface file or a potential/density file."));
  gtk_box_pack_end(GTK_BOX(top_hbox), buttonOpen, FALSE, FALSE, 2);
  image = gtk_image_new_from_icon_name("document-open", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER( buttonOpen), image);
  gtk_widget_set_tooltip_text(buttonConvert, _("Several built-in tools to create .surf files."));
  gtk_box_pack_end(GTK_BOX(top_hbox), buttonConvert, FALSE, FALSE, 2);
  
  isosurfaces_make_tree_view(surfs);
  g_signal_connect_swapped(visu_ui_panel, "destroy",
                           G_CALLBACK(_destroyStore), treeStore);

  isosurfaces_gtk_vbox = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), isosurfaces_gtk_vbox, TRUE, TRUE, 0);

  auto_reorder = gtk_check_button_new_with_mnemonic(_("_Reorder on the fly"));
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(auto_reorder), FALSE);

/*   gtk_widget_set_sensitive(reorder_button, FALSE); */

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(play_stop), FALSE);

  g_signal_connect(G_OBJECT(auto_reorder), "toggled",
		   G_CALLBACK(onReorderToggled), surfs);
/*   g_signal_connect(G_OBJECT(reorder_button), "clicked", */
/* 		   G_CALLBACK(isosurfaces_rebuild_gl_list_in_order_and_redraw), NULL); */
  g_signal_connect(G_OBJECT(buttonOpen), "clicked",
		   G_CALLBACK(onOpenClicked), scene);
  g_signal_connect(G_OBJECT(buttonConvert), "clicked",
		   G_CALLBACK(visu_ui_panel_surfaces_tools_init), (gpointer)0);
  g_signal_connect(G_OBJECT(buttonEdit), "clicked",
		   G_CALLBACK(onEditPropertiesClicked), (gpointer)0);
  g_signal_connect(G_OBJECT(show_all), "clicked",
		   G_CALLBACK(onShowHideAllButton), GINT_TO_POINTER(1));
  g_signal_connect(G_OBJECT(show_none), "clicked",
		   G_CALLBACK(onShowHideAllButton), GINT_TO_POINTER(0));
  g_signal_connect(G_OBJECT(play_stop), "toggled",
		   G_CALLBACK(isosurfaces_play), time_interval);
  g_signal_connect(G_OBJECT(time_interval), "value_changed",
		   G_CALLBACK(isosurfaces_change_time_interval), play_stop);

  /* The main viewport. */
  gtk_box_pack_start(GTK_BOX(isosurfaces_gtk_vbox), tree_hbox, TRUE, TRUE, 0);
  gtk_box_pack_start(GTK_BOX(tree_hbox), scrolled_window, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), 
				 GTK_POLICY_AUTOMATIC, 
				 GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window), GTK_SHADOW_IN);
  gtk_container_add (GTK_CONTAINER (scrolled_window), treeView);
  gtk_box_pack_start(GTK_BOX(tree_hbox), tree_vbox, FALSE, FALSE, 2);

  gtk_widget_set_tooltip_text(show_none, _("Hides all surfaces"));
  gtk_box_pack_start(GTK_BOX(tree_vbox), show_none, FALSE, FALSE, 2);
  gtk_widget_set_tooltip_text(show_all, _("Shows all surfaces"));
  gtk_box_pack_start(GTK_BOX(tree_vbox), show_all, FALSE, FALSE, 2);
 
  gtk_widget_set_tooltip_text(buttonEdit,
		       _("Change color and material properties."));
  gtk_box_pack_end(GTK_BOX(tree_vbox), buttonEdit, FALSE, FALSE, 2);

  buttonAddSurface = gtk_button_new();
  gtk_widget_set_valign(buttonAddSurface, GTK_ALIGN_END);
  gtk_widget_set_sensitive(buttonAddSurface, FALSE);
  gtk_widget_set_tooltip_text(buttonAddSurface,
		       _("Add a new surface."));
  g_signal_connect(G_OBJECT(buttonAddSurface), "clicked",
		   G_CALLBACK(onAddButtonClicked), NULL);
  gtk_box_pack_start(GTK_BOX(tree_vbox), buttonAddSurface, TRUE, TRUE, 2);
  image = gtk_image_new_from_icon_name("list-add", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonAddSurface), image);

  buttonAddSpecial = gtk_button_new();
  gtk_widget_set_sensitive(buttonAddSpecial, FALSE);
  gtk_widget_set_tooltip_text(buttonAddSpecial,
		       _("Add many surfaces to the list of surfaces."));
  g_signal_connect(G_OBJECT(buttonAddSpecial), "clicked",
		   G_CALLBACK(onAddSpecialButtonClicked), NULL);
  gtk_box_pack_start(GTK_BOX(tree_vbox), buttonAddSpecial, FALSE, FALSE, 2);
  image = gtk_image_new_from_icon_name("system-run", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonAddSpecial), image);

  buttonRemoveSurface = gtk_button_new ();
  gtk_widget_set_valign(buttonRemoveSurface, GTK_ALIGN_START);
  gtk_widget_set_sensitive(buttonRemoveSurface, FALSE);
  gtk_widget_set_tooltip_text(buttonRemoveSurface,
		       _("Remove selected surface or file."));
  g_signal_connect(G_OBJECT(buttonRemoveSurface), "clicked",
		   G_CALLBACK(onRemoveButtonClicked), NULL);
  gtk_box_pack_start(GTK_BOX(tree_vbox), buttonRemoveSurface, TRUE, TRUE, 2);
  image = gtk_image_new_from_icon_name("list-remove", GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER(buttonRemoveSurface), image);

  /* The surfaces tool line. */
  gtk_box_pack_start(GTK_BOX(isosurfaces_gtk_vbox), h_box3, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(play_stop, _("Starts/stops showing isosurfaces at specified rate"));
  gtk_box_pack_start(GTK_BOX(h_box3), play_stop, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(h_box3), gtk_label_new(_("@")), FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(time_interval, _("Selects rate to show isosurfaces"));
  gtk_entry_set_width_chars(GTK_ENTRY(time_interval), 3);
  gtk_box_pack_start(GTK_BOX(h_box3), time_interval, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(h_box3), gtk_label_new(_("ms")), FALSE, FALSE, 0);

  valueIO = visu_ui_value_io_new(visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelSurfaces)),
		       _("Import iso-values from an existing XML file."),
		       _("Export iso-values to the current XML file."),
		       _("Export iso-values to a new XML file."));
  visu_ui_value_io_connectOnOpen(VISU_UI_VALUE_IO(valueIO), loadXMLFile);
  visu_ui_value_io_connectOnSave(VISU_UI_VALUE_IO(valueIO),
                                 visu_ui_panel_surfaces_exportXMLFile);
  gtk_widget_set_margin_start(valueIO, 20);
  gtk_box_pack_end(GTK_BOX(h_box3), valueIO, TRUE, TRUE, 0);

  /* The option line. */
  h_box3 = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(isosurfaces_gtk_vbox), h_box3, FALSE, FALSE, 0);

  checkDrawIntra = gtk_check_button_new_with_mnemonic(_("Draw _intra surfaces"));
  gtk_widget_set_tooltip_text(checkDrawIntra,
		       _("Draw the interior of iso-surfaces with the complementary colour."));
  g_object_bind_property(surfs, "draw-intra",
                         checkDrawIntra, "active",
                         G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
  gtk_box_pack_start(GTK_BOX(h_box3), checkDrawIntra, FALSE, FALSE, 0);

  gtk_box_pack_end(GTK_BOX(h_box3), auto_reorder, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(auto_reorder,
			_("Automatically re-orders surfaces in back to front order"
			  " whenever camera is modified (can be slow but get rid of"
			  " transparency problems). This has no effect if the"
			  " transparency option of the OpenGL panel is set"));

  gtk_widget_show_all(vbox);

  gtk_container_add(GTK_CONTAINER(visu_ui_panel), vbox);
}
/**
 * visu_ui_panel_surfaces_parseXMLFile:
 * @filename: a location to read the XML data.
 * @error: a location to store possible error.
 *
 * This routine reads an XML file and setup the resources and the
 * isovalues to the selected row accordingly.
 *
 * Returns: TRUE on success.
 */
gboolean visu_ui_panel_surfaces_parseXMLFile(const gchar *filename, GError **error)
{
  GtkTreeModel *model;
  GtkTreeIter iter, child;
  GList *surfaces, *lst;
  RowPotential *row;
  int type;
  /* VisuData *dataObj; */
  
  g_return_val_if_fail(getSelectedRow(&model, &iter), FALSE);

  gtk_tree_model_get(model, &iter,
                     TYPE_COLUMN, &type,
		     ROW_FIELD_COLUMN, &row, -1);
  g_return_val_if_fail(type == SURFACE_FILE_DENPOT, FALSE);

  if (!visu_surface_parseXMLFile(filename, &surfaces, row->field, error))
    {
      row_potential_free(row);
      return FALSE;
    }
  row_potential_free(row);
  
  for (lst = surfaces; lst; lst = g_list_next(lst))
    visu_ui_panel_surfaces_addSurface(VISU_SURFACE(lst->data), &iter, &child);
  g_list_free(surfaces);

  return TRUE;
}
static gboolean loadXMLFile(const gchar *filename, GError **error)
{
  if (visu_ui_panel_surfaces_parseXMLFile(filename, error))
    {
      /* Update the sensitivity. */
      visu_ui_value_io_setSensitiveSave(VISU_UI_VALUE_IO(valueIO), TRUE);
      return TRUE;
    }
  else
    return FALSE;
}
/**
 * visu_ui_panel_surfaces_exportXMLFile:
 * @filename: a filename to export to.
 * @error: (allow-none): a location to store an error.
 *
 * Export to @filename the list of isosurfaces values of the selected
 * scalar-field file.
 *
 * Since: 3.7
 *
 * Returns: TRUE if everything goes right.
 **/
gboolean visu_ui_panel_surfaces_exportXMLFile(const gchar *filename, GError **error)
{
  GtkTreeModel *model;
  GtkTreeIter iter, child;
  int n;
  float *values;
  gboolean valid;
  RowSurface *row;
  VisuSurfaceResource **res;

  if (!getSelectedRow(&model, &iter))
    return TRUE;

  n = gtk_tree_model_iter_n_children(model, &iter);
  values = g_malloc(sizeof(float) * n);
  res = g_malloc(sizeof(VisuSurfaceResource*) * n);
  for (valid = gtk_tree_model_iter_children(model, &child, &iter), n = 0;
       valid; valid = gtk_tree_model_iter_next(model, &child))
    {
      gtk_tree_model_get(model, &child,
                         BOX_ROW_COLUMN, &row,
                         POTENTIAL_COLUMN, values + n, -1);
      res[n] = row->res;
      row_surface_free(row);
      if (values[n] != G_MAXFLOAT)
        n += 1;
    }
  valid = TRUE;
  if (n > 0)
    valid = visu_surface_exportXMLFile(filename, values, res, n, error);

  g_free(res);
  g_free(values);

  return valid;
}


/* static VisuSurface** getAllVisuSurfaceList() */
/* { */
/*   int n; */
/*   gboolean valid; */
/*   GtkTreeIter iter; */
/*   VisuSurface **surfaces; */

/*   n = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(isosurfaces_data_list), */
/* 				     (GtkTreeIter*)0); */
/*   surfaces = g_malloc(sizeof(VisuSurface*) * (n + 1)); */
/*   n = 0; */
/*   valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(isosurfaces_data_list), &iter); */
/*   while (valid) */
/*     { */
/*       gtk_tree_model_get(GTK_TREE_MODEL(isosurfaces_data_list), */
/* 			 &iter, OBJ_SURF_COLUMN, surfaces + n, -1); */
/*       /\* In case the surface n is NULL, we don't count it. *\/ */
/*       if (surfaces[n]) */
/* 	n += 1; */
/*       valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(isosurfaces_data_list), &iter); */
/*     } */
/*   surfaces[n] = (VisuSurface*)0; */
/*   DBG_fprintf(stderr, "Panel VisuSurface: get list of all surfaces (%d).\n", n); */
/*   return surfaces; */
/* } */

static void onTreeSelectionChanged(GtkTreeSelection *tree, gpointer data _U_)
{
  gboolean res;
  GtkTreeModel *model;
  GtkTreeIter iter;
  int type;
  RowPotential *row;
  double mimax[2];
  gboolean empty, hasChildren;

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

  res = gtk_tree_selection_get_selected(tree, &model, &iter);
  if (res)
    {
      gtk_tree_model_get(model, &iter, TYPE_COLUMN, &type,
                         ROW_FIELD_COLUMN, &row, -1);
      hasChildren = (type == SURFACE_FILE_DENPOT) &&
	gtk_tree_model_iter_has_child(model, &iter);
      if (type == SURFACE_FILE_DENPOT)
        visu_scalar_field_getMinMax(row->field, mimax);
      if (row)
        row_potential_free(row);
    }
  else
    {
      type = -1;
      mimax[0] = 0.;
      mimax[1] = 1.;
      hasChildren = FALSE;
    }
    
  empty = !gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStore), &iter);

  gtk_widget_set_sensitive(buttonRemoveSurface, (type >= 0));
  gtk_widget_set_sensitive(buttonAddSurface, (type == SURFACE_FILE_DENPOT));
  gtk_widget_set_sensitive(buttonAddSpecial, (type == SURFACE_FILE_DENPOT &&
                                              mimax[0] <= mimax[1]));
  gtk_widget_set_sensitive(buttonEdit, !empty);
  visu_ui_value_io_setSensitiveOpen(VISU_UI_VALUE_IO(valueIO), (type == SURFACE_FILE_DENPOT));
  visu_ui_value_io_setSensitiveSave(VISU_UI_VALUE_IO(valueIO), (type == SURFACE_FILE_DENPOT &&
					       hasChildren));

  if (GTK_IS_WINDOW(edit_window))
    panelIsosurfacesUpdate_surfaceProperties();
}

static void onAddButtonClicked(GtkButton *button _U_, gpointer data _U_)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter, child;
  int type;
  RowPotential *row;
  VisuSurface *neg, *pos;

  valid = getSelectedRow(&model, &iter);
  g_return_if_fail(valid);

  gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &iter,
                     TYPE_COLUMN, &type,
                     ROW_FIELD_COLUMN, &row, -1);
  g_return_if_fail(type == SURFACE_FILE_DENPOT);
  
  visu_surface_new_defaultFromScalarField(row->field, &neg, &pos);
  if (neg)
    visu_ui_panel_surfaces_addSurface(neg, &iter, &child);
  if (pos)
    visu_ui_panel_surfaces_addSurface(pos, &iter, &child);
  row_potential_free(row);
}
static void onAddSpecialButtonClicked(GtkButton *button _U_, gpointer data _U_)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter;
  float *values;
  int nbValues;
  RowPotential *row;
  int type, i;
  double mimax[2];
  gchar *name;
  const gchar *names[1];
  GtkWidget *dialog, *progress;
  char mess[50];
  GList *lst, *children;

  valid = getSelectedRow(&model, &iter);
  g_return_if_fail(valid);

  gtk_tree_model_get(model, &iter, TYPE_COLUMN, &type,
                     ROW_FIELD_COLUMN, &row, -1);

  g_return_if_fail(type == SURFACE_FILE_DENPOT && row);

  visu_scalar_field_getMinMax(row->field, mimax);
  row_potential_free(row);

  g_return_if_fail(mimax[0] <= mimax[1]);

  values = (float*)0;
  name = (gchar*)0;
  dialog = visu_ui_panel_surfaces_generateValues(&nbValues, &values, &name,
                                                 (float)mimax[0], (float)mimax[1]);
  names[0] = name;

  if (!dialog)
    return;

  progress = (GtkWidget*)0;
  children = gtk_container_get_children
    (GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(dialog))));
  for (lst = children; lst; lst = lst->next)
       
    {
      gtk_widget_set_sensitive(GTK_WIDGET(lst->data), FALSE);
      if (GTK_IS_PROGRESS_BAR(lst->data))
	progress = GTK_WIDGET(lst->data);
    }
  g_list_free(children);
  for (i = 0; i < nbValues; i++)
    {
      sprintf(mess, "%d/%d", i + 1, nbValues);
      gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress), mess);
      gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress),
				    (float)i/(float)nbValues);
      visu_ui_wait();
      _compute(&iter, values + i, names, 1);
    }
  gtk_widget_destroy(dialog);

  if (values)
    g_free(values);
  if (name)
    g_free(name);

  visu_ui_value_io_setSensitiveSave(VISU_UI_VALUE_IO(valueIO), TRUE);
}
static void onRemoveButtonClicked(GtkButton *button _U_, gpointer data _U_)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter;

  valid = getSelectedRow(&model, &iter);
  g_return_if_fail(valid);

  panel_isosurfaces_remove(model, &iter);
}
static void onNameEdited(GtkCellRendererText *cellrenderertext _U_,
			 gchar *path, gchar *text, gpointer user_data)
{
  GtkTreeIter iter;
  gboolean status;
  int type;
  VisuSurfaceResource *res;
  RowSurface *row;

  if (!strcmp(text, VISU_UI_SURFACE_NAME_STR))
    return;

  status = gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(user_data),
					       &iter, path);
  g_return_if_fail(status);

  /* We get the VisuSurface of the edited object. */
  gtk_tree_model_get(GTK_TREE_MODEL(user_data), &iter,
                     BOX_ROW_COLUMN, &row,
		     TYPE_COLUMN, &type, -1);
  g_return_if_fail(type == SURFACE_SURF && row);

  /* We change the resource of the edited surface. */
  res = visu_surface_resource_new_fromCopy(text, row->res);
  DBG_fprintf(stderr, "Panel VisuSurface: new resources %p (%s).\n",
	      (gpointer)res, visu_surface_resource_getLabel(res));
  /* We set the new resource (may free the old one). */
  visu_surface_setResource(row->surf, res);
  g_object_unref(res);
  row_surface_free(row);
}
static void onPotentialValueEdited(GtkCellRendererText *cell,
				   gchar *path, gchar *text, gpointer user_data)
{
  GtkTreeIter iter, parent;
  GtkTreePath *treePath;
  float value = atof(text);
  VisuSurface *surf;
  RowPotential *field;
  int type;
  double minmax[2];
  gboolean status;
  gchar *str;
  RowSurface *row;
  VisuGlExtSurfaces *surfs;

  status = gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(user_data),
					       &iter, path);
  g_return_if_fail(status);

  gtk_tree_model_iter_parent(GTK_TREE_MODEL(user_data),
			     &parent, &iter);
  gtk_tree_model_get(GTK_TREE_MODEL(user_data), &parent,
		     ROW_FIELD_COLUMN, &field,
		     TYPE_COLUMN, &type, -1);
  g_return_if_fail(type == SURFACE_FILE_DENPOT && field);

  visu_scalar_field_getMinMax(field->field, minmax);
  g_return_if_fail(minmax[0] > minmax[1] ||
                   (value >= minmax[0] && value <= minmax[1]));

  /* We add the new surface. */
  DBG_fprintf(stderr, "Panel VisuSurface: create surface with id %d.\n", 0);
  surf = visu_surface_new_fromScalarField(field->field, value, (const gchar*)0);
  row_potential_free(field);
  if (!surf)
    {
      /* Put back the old value. */
      gtk_tree_model_get(GTK_TREE_MODEL(user_data), &iter,
                         POTENTIAL_COLUMN, &value, -1);
      str = g_strdup_printf("%g", value);
      g_object_set(G_OBJECT(cell), "text", str, NULL);
      g_free(str);
      return;
    }

  gtk_tree_model_get(GTK_TREE_MODEL(user_data), &iter,
		     BOX_ROW_COLUMN, &row, -1);
  visu_surface_setResource(surf, row->res);
  row_surface_free(row);

  surfs = visu_gl_node_scene_addSurfaces(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()));
  connectSurface(GTK_TREE_STORE(user_data), &iter, surfs, surf);
  g_object_unref(surf);

  /* We reselect the right iter. */
  treePath = gtk_tree_path_new_from_string(path);
  gtk_tree_selection_select_path
    (gtk_tree_view_get_selection(GTK_TREE_VIEW(treeView)), treePath);
  gtk_tree_path_free(treePath);
}

static void onReorderToggled(GtkToggleButton *toggle, VisuGlExtSurfaces *surfs)
{
  visu_gl_ext_setGlView(VISU_GL_EXT(surfs),
                        (gtk_toggle_button_get_active(toggle))?visu_ui_panel_getView(VISU_UI_PANEL(panelSurfaces)):(VisuGlView*)0);
}

static void onDataFocused(GObject *visu _U_, VisuData *dataObj, gpointer data _U_)
{
  GtkTreeIter iter, child;
  gboolean valid, *flag;
  int type, n;
  gchar *fileCpy, *fileExtPosition;
  GString *dataFile;
  float *isoValues;
  int nValues;
  const gchar **isoNames;
  
  DBG_fprintf(stderr, "Panel Surfaces: caught the 'DataFocused'"
	      " signal.\n | setting sensititvity.\n");
  if (dataObj)
    {
      gtk_widget_set_sensitive(isosurfaces_gtk_vbox, TRUE);
      gtk_widget_set_sensitive(buttonOpen, TRUE);
      gtk_widget_set_sensitive(buttonConvert, TRUE);
    }
  else
    {
      gtk_widget_set_sensitive(isosurfaces_gtk_vbox, FALSE);
      gtk_widget_set_sensitive(buttonOpen, FALSE);
      gtk_widget_set_sensitive(buttonConvert, FALSE);
    }

  DBG_fprintf(stderr, " | update list of surfaces and fields.\n");
  /* Store all isovalues into isoValues array. */
  nValues = 0;
  isoValues = (float*)0;
  isoNames  = (const gchar**)0;
  /* Remove all scalar-fields. */
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStore), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(treeStore), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &iter,
                         TYPE_COLUMN, &type, -1);
      nValues = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(treeStore), &iter);

      /* If the data is a field, we store all its values and names. */
      if (type == SURFACE_FILE_DENPOT && nValues > 0 && !isoValues)
	{
          isoValues = g_malloc(sizeof(float) * nValues);
          isoNames  = g_malloc(sizeof(const gchar*) * (nValues + 1));
          for (valid = gtk_tree_model_iter_children(GTK_TREE_MODEL(treeStore), &child, &iter), n = 0;
               valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(treeStore), &child), n += 1)
            gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &child,
                               POTENTIAL_COLUMN, isoValues + n,
                               LABEL_COLUMN, isoNames + n, -1);
          isoNames[nValues] = (gchar*)0;
	}
    }
  gtk_tree_store_clear(treeStore);
  
  /* If the flag of autoload is not checked or no data is available, we return. */
  if (!dataObj || !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkAutoLoad)))
    {
      g_free(isoValues);
      g_free(isoNames);
      return;
    }

  /* Read if the new file has the VISU_SCALAR_FIELD_DEFINED_IN_STRUCT_FILE flag. */
  flag = (gboolean*)g_object_get_data(G_OBJECT(dataObj),
				      VISU_SCALAR_FIELD_DEFINED_IN_STRUCT_FILE);
  fileCpy = g_strdup(visu_data_loadable_getFilename(VISU_DATA_LOADABLE(dataObj), 0));
  if (!flag)
    {
      /* We try to find a surf file with the right name. */
      fileExtPosition = g_strrstr(fileCpy, ".");
      if (fileExtPosition)
	*fileExtPosition = '\0';
      dataFile = g_string_new("");
      g_string_printf(dataFile, "%s.%s", fileCpy, "surf");
      g_free(fileCpy);
      DBG_fprintf(stderr, "Panel VisuSurface: try to load a new surface file"
		  " ('%s') from the name of the rendered file.\n", dataFile->str);
      fileCpy = dataFile->str;
      g_string_free(dataFile, FALSE);
      if (!g_file_test(fileCpy, G_FILE_TEST_EXISTS))
	{
          g_free(fileCpy);
	  fileCpy = (gchar*)0;
          DBG_fprintf(stderr, " | file doesn't exist.\n");
        }
    }
  if (fileCpy)
    {
      visu_ui_panel_surfaces_loadFile(fileCpy, &iter,
                                      (GHashTable*)0, (VisuScalarFieldMethod*)0);
      g_free(fileCpy);
      /* We create the surfaces for the values readed from
	 isoValues. */
      valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStore), &iter);
      if (valid && isoValues)
	{
	  gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &iter,
			     TYPE_COLUMN, &type, -1);
	  if (type == SURFACE_FILE_DENPOT)
            _compute(&iter, isoValues, isoNames, nValues);
	}
    }
  g_free(isoValues);
  g_free(isoNames);
}

static gboolean onPopupClicked(GtkWidget *widget, GdkEventButton *event _U_,
			       gpointer user_data _U_)
{
  gtk_widget_destroy(widget);
  return FALSE;
}
static gboolean onTreeViewClicked(GtkWidget *widget, GdkEventButton *event,
				  gpointer user_data _U_)
{
  gboolean valid;
  GtkTreePath *path;
  GtkTreeIter iter;
  int type, row;
  RowPotential *field;
  GtkWidget *win, *vbox, *wd, *table;
  gchar *name, *markup;
  const gchar *comment;
  gint x, y;
  GList *options, *tmplst;

  if (event->button != 3)
    return FALSE;

  DBG_fprintf(stderr, "Panel VisuSurface: right clic detected.\n");

  valid = gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), (gint)event->x,
					(gint)event->y, &path, (GtkTreeViewColumn**)0,
                                        (gint*)0, (gint*)0);
  if (!valid)
    return FALSE;

  valid = gtk_tree_model_get_iter(GTK_TREE_MODEL(treeStore), &iter, path);
  gtk_tree_path_free(path);
  if (!valid)
    return FALSE;

  gtk_tree_model_get(GTK_TREE_MODEL(treeStore), &iter,
		     ROW_FIELD_COLUMN, &field,
		     TYPE_COLUMN, &type, -1);
  if (type != SURFACE_FILE_DENPOT)
    return FALSE;

  g_return_val_if_fail(field, FALSE);

  /* Ok, now, we have the scalar field.
     We create a popup window with info. */
  win = gtk_window_new(GTK_WINDOW_POPUP);
  gtk_widget_set_events(win, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_KEY_PRESS_MASK);
  gtk_grab_add(win);
#if GTK_MAJOR_VERSION > 2
  gdk_device_get_position(event->device, NULL, &x, &y);
#else
  gdk_display_get_pointer(gdk_screen_get_display(gtk_widget_get_screen(widget)),
			  (GdkScreen**)0, &x, &y, NULL);
#endif

  gtk_window_move(GTK_WINDOW(win), x, y);
  g_signal_connect(G_OBJECT(win), "button-press-event",
		   G_CALLBACK(onPopupClicked), (gpointer)0);
  gtk_container_set_border_width(GTK_CONTAINER(win), 20);
  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(win), vbox);

  /* Print the File name. */
  name = g_path_get_basename(visu_scalar_field_getLabel(field->field));
  markup = g_markup_printf_escaped("<span size=\"larger\"><b>%s</b></span>", name);
  g_free(name);
  wd = gtk_label_new("");
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_label_set_markup(GTK_LABEL(wd), markup);
  g_free(markup);
  gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  /* Print a list of all interesting characteristics of the file. */
  options = visu_scalar_field_getAllOptions(field->field);
  table = gtk_grid_new();
  tool_grid_resize(table, 1 + g_list_length(options), 2);
  gtk_widget_set_margin_start(table, 15);
  gtk_widget_set_margin_top(table, 15);
  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);

  wd = gtk_label_new("");
  gtk_label_set_xalign(GTK_LABEL(wd), 1.);
  gtk_label_set_markup(GTK_LABEL(wd), _("<span style=\"italic\">Comment:</span>"));
  gtk_grid_attach(GTK_GRID(table), wd, 0, 0, 1, 1);

  comment = visu_scalar_field_getCommentary(field->field);
  if (comment)
    wd = gtk_label_new(comment);
  else
    wd = gtk_label_new(_("None"));
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_grid_attach(GTK_GRID(table), wd, 1, 0, 1, 1);
  
  row = 1;
  tmplst = options;
  while(tmplst)
    {
      wd = gtk_label_new("");
      gtk_label_set_xalign(GTK_LABEL(wd), 1.);
      markup = g_markup_printf_escaped("<span style=\"italic\">%s:</span>",
                                       tool_option_getName((ToolOption*)tmplst->data));
      gtk_label_set_markup(GTK_LABEL(wd), markup);
      g_free(markup);
      gtk_grid_attach(GTK_GRID(table), wd, 0, row, 1, 1);

      wd = gtk_label_new("");
      gtk_label_set_xalign(GTK_LABEL(wd), 0.);
      markup = tool_option_getValueAndLabel((ToolOption*)tmplst->data);
      gtk_label_set_markup(GTK_LABEL(wd), markup);
      g_free(markup);
      gtk_grid_attach(GTK_GRID(table), wd, 1, row, 1, 1);
      
      row += 1;
      tmplst = g_list_next(tmplst);
    }
  g_list_free(options);

  gtk_widget_show_all(win);

  row_potential_free(field);

  return FALSE;
}
static void onTreeViewActivated(GtkTreeView *tree_view, GtkTreePath *path,
				GtkTreeViewColumn *column, gpointer user_data _U_)
{
  GtkTreeIter iter;

  /* We do something only if the color column is activated for a
     surface. */
  if (column != colorColumn)
    return;
  if (gtk_tree_path_get_depth(path) < 2)
    return;

  if (gtk_tree_model_get_iter(gtk_tree_view_get_model(tree_view), &iter, path))
    visu_ui_panel_surfaces_editProperties(&iter);
}
/**
 * visu_ui_panel_surfaces_getFields:
 * 
 * This method gives read access to the #GtkListStore used to store
 * the scalar field files.
 *
 * Returns: (transfer none): the #GtkListStore used by this panel to
 * store its scalar fields. It should be considered read-only.
 */
GtkListStore* visu_ui_panel_surfaces_getFields()
{
  return fields_data_list;
}
/**
 * visu_ui_panel_surfaces_fieldsAt:
 * @model: a #GtkTreeModel object.
 * @iter: a #GtkTreeIter iter.
 *
 * Retrieves the #VisuScalarField object stored at @iter, if any.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuScalarField object.
 **/
VisuScalarField* visu_ui_panel_surfaces_fieldsAt(GtkTreeModel *model, GtkTreeIter *iter)
{
  RowPotential *row;
  VisuScalarField *field;
  
  gtk_tree_model_get(model, iter, VISU_UI_SURFACES_FIELD_ROW, &row, -1);
  if (!row)
    return (VisuScalarField*)0;

  field = g_object_ref(row->field);
  row_potential_free(row);
  return field;
}

struct GenerateWidgets_struct
{
  GtkWidget *spin_start;
  GtkWidget *spin_end;
  GtkWidget *spin_step;
  GtkWidget *spin_delta;
};

static void onSpecialModeToggled(GtkToggleButton *button, gpointer data)
{
  gtk_widget_set_sensitive(GTK_WIDGET(data), gtk_toggle_button_get_active(button));
}
static void onGenerateChanged(GtkSpinButton *spin, gpointer data)
{
  struct GenerateWidgets_struct *wds;
  float delta;

  wds = (struct GenerateWidgets_struct*)data;
  g_return_if_fail(wds);

  delta = gtk_spin_button_get_value(GTK_SPIN_BUTTON(wds->spin_end)) -
    gtk_spin_button_get_value(GTK_SPIN_BUTTON(wds->spin_start));
  if (spin == GTK_SPIN_BUTTON(wds->spin_step) ||
      spin != GTK_SPIN_BUTTON(wds->spin_delta))
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(wds->spin_delta), delta /
			      gtk_spin_button_get_value(GTK_SPIN_BUTTON
							(wds->spin_step)));
  if (spin == GTK_SPIN_BUTTON(wds->spin_delta) ||
      spin != GTK_SPIN_BUTTON(wds->spin_step))
    gtk_spin_button_set_value(GTK_SPIN_BUTTON(wds->spin_step), delta /
			      gtk_spin_button_get_value(GTK_SPIN_BUTTON
							(wds->spin_delta)));
}
/**
 * visu_ui_panel_surfaces_generateValues:
 * @nbValues: a location of an integer to store the number of generated values ;
 * @values: a pointer on a float array. The target of this pointer must be
 *          NULL and it will be allocated after a call to this method. Use
 *          g_free() after use to free it.
 * @name: a pointer to store a name. The target of this pointer must be
 *        NULL on enter. It is associated only if a name is given.
 * @minVal: the minimum value for the range ;
 * @maxVal: the maximum value for the range.
 * 
 * This method opens a little dialog window that is made to help the
 * user enter a list of values for creation of iso-surfaces. These values
 * are generated between @minVal and @maxVal.
 *
 * Returns: (transfer full): the dialog widget.
 */
GtkWidget* visu_ui_panel_surfaces_generateValues(int *nbValues, float **values,
					      gchar **name, float minVal,
					      float maxVal)
{
  GtkWidget *dialog, *table;
  float value, incr;
#define MAX_NB_VALUES 99

  struct GenerateWidgets_struct wds;
  GtkWidget *spin_start;
  GtkWidget *spin_end;
  GtkWidget *spin_step;
  GtkWidget *spin_delta;
  GtkWidget *radioStep, *radioDelta;
  GtkWidget *entry, *progress;

  GSList *radiobuttonCycle_group;

  g_return_val_if_fail(nbValues && values &&
		       !*values && name && !*name, (GtkWidget*)0);

  dialog = gtk_dialog_new_with_buttons (_("Generate iso-values"),
					NULL, GTK_DIALOG_MODAL,
					TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
					_("Generate"), GTK_RESPONSE_ACCEPT, NULL);

  table = gtk_grid_new();
  tool_grid_resize(table, 6, 2);

  spin_start = gtk_spin_button_new_with_range(minVal, maxVal, 0.0000001);
  g_signal_connect(G_OBJECT(spin_start), "value_changed",
		   G_CALLBACK(onGenerateChanged), (gpointer)&wds);
  spin_end = gtk_spin_button_new_with_range(minVal, maxVal, 0.0000001);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_end), maxVal);
  g_signal_connect(G_OBJECT(spin_end), "value_changed",
		   G_CALLBACK(onGenerateChanged), (gpointer)&wds);
  spin_step = gtk_spin_button_new_with_range(2, MAX_NB_VALUES, 1);
  g_signal_connect(G_OBJECT(spin_step), "value_changed",
		   G_CALLBACK(onGenerateChanged), (gpointer)&wds);
  spin_delta = gtk_spin_button_new_with_range(0.000001, maxVal - minVal,
					      (maxVal - minVal) / 200);
  g_signal_connect(G_OBJECT(spin_delta), "value_changed",
		   G_CALLBACK(onGenerateChanged), (gpointer)&wds);
  wds.spin_start = spin_start;
  wds.spin_end   = spin_end;
  wds.spin_step  = spin_step;
  wds.spin_delta = spin_delta;

  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spin_step), 10);

  radioStep = gtk_radio_button_new_with_label(NULL, _("Number of steps:"));
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radioStep), (GSList*)0);
  radiobuttonCycle_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radioStep));
  gtk_grid_attach(GTK_GRID(table), radioStep, 0, 2, 1, 1);
  g_signal_connect(G_OBJECT(radioStep), "toggled",
		   G_CALLBACK(onSpecialModeToggled), (gpointer)spin_step);

  radioDelta = gtk_radio_button_new_with_label(NULL, _("Delta of steps:"));
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radioDelta), radiobuttonCycle_group);
  radiobuttonCycle_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radioDelta));
  gtk_grid_attach(GTK_GRID(table), radioDelta, 1, 2, 1, 1);
  g_signal_connect(G_OBJECT(radioDelta), "toggled",
		   G_CALLBACK(onSpecialModeToggled), (gpointer)spin_delta);

  entry = gtk_entry_new();

  progress = gtk_progress_bar_new();

  gtk_grid_attach(GTK_GRID(table), spin_step, 0, 3, 1, 1);
  gtk_grid_attach(GTK_GRID(table), spin_delta, 1, 3, 1, 1);

  gtk_grid_attach(GTK_GRID(table), gtk_label_new(_("Start:")), 0, 0, 1, 1);
  gtk_grid_attach(GTK_GRID(table), gtk_label_new(_("End:")), 1, 0, 1, 1);
  gtk_grid_attach(GTK_GRID(table), spin_start, 0, 1, 1, 1);
  gtk_grid_attach(GTK_GRID(table), spin_end, 1, 1, 1, 1);
  gtk_grid_attach(GTK_GRID(table), gtk_label_new(_("Name (optional):")), 0, 4, 2, 1);
  gtk_grid_attach(GTK_GRID(table), entry, 0, 5, 2, 1);

  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
		     table, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
		     progress, FALSE, FALSE, 5);

  gtk_widget_set_sensitive(spin_delta, FALSE);

  gtk_widget_show_all(dialog);

  *values = (float*)0;
  *nbValues = 0;
  switch(gtk_dialog_run(GTK_DIALOG(dialog)))
    {
    case GTK_RESPONSE_ACCEPT:
      {
	float start = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_start));
	float end = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_end));
	int step = floor(gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_step)));

	*values = g_malloc(sizeof(float) * MAX_NB_VALUES);

	value = start;
	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioStep)))
	  {
	    if (fabsf(start - minVal) < 1e-6)
	      step += 1;
	    incr = (end - start) / step;
	  }
	else
	  incr = gtk_spin_button_get_value(GTK_SPIN_BUTTON(spin_delta));
	if (fabsf(value - minVal) < 1e-6)
	  value += incr;
	while ((incr > 0 && (value - end) < -1e-6) ||
	       (incr < 0 && (value - end) > 1e-6))
	  {
	    if (value > minVal && value < maxVal)
	      {
		DBG_fprintf(stderr, "Panel VisuSurface: create a new value, %d %f\n",
			    *nbValues, value);
		(*values)[*nbValues] = value;
		*nbValues += 1;
	      }
	    value += incr;
	  }
	*name = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
        break;
      }
    default:
      {
        gtk_widget_destroy(dialog);
        dialog = (GtkWidget*)0;
        break;
      }
      return dialog;
    }
  return dialog;
}

static void onInteractiveNotified(VisuUiRenderingWindow *window,
                                  GParamSpec *pspec _U_, VisuGlNodeScene *scene)
{
  VisuInteractive *inter;

  g_object_get(window, "interactive", &inter, NULL);
  if (!inter)
    return;

  visu_gl_ext_surfaces_setOnObserveOrdering(visu_gl_node_scene_addSurfaces(scene), inter);
  g_object_unref(inter);
}

static void _destroyFields(GtkListStore *model)
{
  DBG_fprintf(stderr, "Panel Surfaces: destroying fields model.\n");

  gtk_list_store_clear(model);
  g_object_unref(model);
}

/**
 * visu_ui_panel_surfaces_init: (skip)
 *
 * Should be used in the list declared in externalModules.h to be loaded by
 * V_Sim on start-up. This routine will create the #VisuUiPanel where the iso-surfaces
 * stuff can be done, such as creating a surface, loading a scalar field,
 * changing the properties...
 *
 * Returns: a newly created #VisuUiPanel object.
 */
VisuUiPanel* visu_ui_panel_surfaces_init(VisuUiMain *ui)
{
  /* Long description */
  char *cl = _("Drawing iso-surfaces");
  /* Short description */
  char *tl = "Isosurfaces";
  VisuScalarfieldSet *set;
  VisuScalarfieldSetIter iter;
  gboolean valid;

  panelSurfaces = visu_ui_panel_newWithIconFromPath("Panel_surfaces", cl, _(tl),
                                                    "stock-isosurfaces_20.png");
  visu_ui_panel_setDockable(VISU_UI_PANEL(panelSurfaces), TRUE);

  fields_data_list =  gtk_list_store_new(VISU_UI_SURFACES_FIELD_N_COLUMNS,
					 G_TYPE_STRING,
					 TYPE_ROW_POTENTIAL);
  g_signal_connect_swapped(panelSurfaces, "destroy",
                           G_CALLBACK(_destroyFields), fields_data_list);

  /* Create the widgets. */
  isosurfaces_create_gtk_interface(VISU_UI_PANEL(panelSurfaces));
  gtk_widget_set_sensitive(isosurfaces_gtk_vbox, FALSE);
  gtk_widget_set_sensitive(buttonOpen, FALSE);
  gtk_widget_set_sensitive(buttonConvert, FALSE);
  vboxColorur = (GtkWidget*)0;
  vboxToolShade  = (GtkWidget*)0;
  /* Add the signal for the vBoxVisuPlanes. */
  g_signal_connect(G_OBJECT(ui), "DataFocused",
		   G_CALLBACK(onDataFocused), (gpointer)0);
  g_signal_connect(visu_ui_main_getRendering(ui), "notify::interactive",
                   G_CALLBACK(onInteractiveNotified), visu_ui_rendering_window_getGlScene(visu_ui_main_getRendering(ui)));
  onInteractiveNotified(visu_ui_main_getRendering(ui), (GParamSpec*)0, visu_ui_rendering_window_getGlScene(visu_ui_main_getRendering(ui)));

  set = visu_scalarfield_set_getDefault();
  g_signal_connect(set, "added", G_CALLBACK(_addField), fields_data_list);
  g_signal_connect(set, "removed", G_CALLBACK(_removeField), fields_data_list);
  visu_scalarfield_set_iter_new(set, &iter);
  for (valid = visu_scalarfield_set_iter_next(&iter); valid;
       valid = visu_scalarfield_set_iter_next(&iter))
    _addField(set, iter.field, fields_data_list);

  if(!panelSurfaces)
    return NULL;

  return VISU_UI_PANEL(panelSurfaces);
}
