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

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

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

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

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

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

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

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

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

#include <support.h>
#include <visu_tools.h>
#include <visu_data.h>
#include <visu_basic.h>
#include <visu_gtk.h>
#include <gtk_main.h>
#include <gtk_renderingWindowWidget.h>
#include <extensions/box.h>
#include <extensions/geodiff.h>
#include <extensions/paths.h>
#include <openGLFunctions/objectList.h>
#include <coreTools/toolColor.h>
#include <coreTools/toolPhysic.h>
#include <extraFunctions/finder.h>
#include <extraGtkFunctions/gtk_colorComboBoxWidget.h>
#include <extraGtkFunctions/gtk_stippleComboBoxWidget.h>
#include <extraGtkFunctions/gtk_shadeComboBoxWidget.h>
#include <uiElements/ui_boxTransform.h>

/**
 * SECTION: panelGeometry
 * @short_description: This tab gathers the geometry operation on a
 * #VisuData, like periodic translation, physical units, ...
 *
 * <para>Nothing tunable here.</para>
 */

/* Local objects. */
static GtkWidget *panelGeometry;
static GtkWidget *togglePathSave;
static GtkWidget *hboxIOVisuPaths;
static GtkWidget *copyGeodiff, *pasteGeodiff, *addGeodiff;

/* Local variables. */
static gboolean disableCallbacks;
static gboolean widgetsNotBuilt;
static gchar *exportPathFile = NULL;

#define NO_PATH _("<span size=\"small\"><i>No stored path</i></span>")
#define PATH _("<span size=\"small\">Path has %d step(s)</span>")

/* Local routines. */
static GtkWidget *createInteriorBox(VisuGlNodeScene *scene);

/* Local callbacks. */
static void onEnter(VisuUiPanel *visu_ui_panel, VisuUiRenderingWindow *window);
static void onDataFocused(GObject *obj, VisuData* visuData, gpointer data);
static void onPathSaveToggled(GtkToggleButton *toggle, gpointer data);
static void onSavePathClicked(GtkButton *bt, gpointer data);
static void onLoadPathClicked(GtkButton *bt, gpointer data);
static void onDirBrowsed(VisuUiMain *obj, VisuUiDirectoryType type, gpointer user);
static void onCopyDiff(GtkButton *button, gpointer data);
static void onPasteDiff(GtkToggleButton *button, gpointer data);
static void onAddDiff(GtkButton *button, gpointer data);

/**
 * visu_ui_panel_geometry_init: (skip)
 * @ui: a #VisuUiMain object.
 *
 * 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 box
 * stuff can be tuned, such as the bounding box, its colour, and the actions linked
 * to the periodicity (translation, dupplication...).
 *
 * Returns: a newly created #VisuUiPanel object.
 */
VisuUiPanel* visu_ui_panel_geometry_init(VisuUiMain *ui)
{
  panelGeometry = visu_ui_panel_newWithIconFromPath("Panel_geometry",
						_("Geometry operations"),
						_("Geometry"),
						"stock-geometry_20.png");
  if (!panelGeometry)
    return (VisuUiPanel*)0;

  visu_ui_panel_setDockable(VISU_UI_PANEL(panelGeometry), TRUE);

  /* Create the widgets that are needed even without the GTK interface be built. */
  togglePathSave = gtk_toggle_button_new();
  pasteGeodiff = gtk_toggle_button_new_with_label(_("Paste and align"));
  copyGeodiff = gtk_button_new_from_icon_name("edit-copy", GTK_ICON_SIZE_BUTTON);
  addGeodiff = gtk_button_new_from_icon_name("list-add", GTK_ICON_SIZE_BUTTON);

  /* Create the callbacks of all the sensitive widgets. */
  g_signal_connect(panelGeometry, "page-entered",
		   G_CALLBACK(onEnter), (gpointer)visu_ui_main_getRendering(ui));
  g_signal_connect(ui, "DirectoryChanged",
		   G_CALLBACK(onDirBrowsed), (gpointer)0);
  g_signal_connect(ui, "DataFocused",
		   G_CALLBACK(onDataFocused), (gpointer)0);

  /* Private parameters. */
  disableCallbacks = FALSE;
  widgetsNotBuilt  = TRUE;

  return VISU_UI_PANEL(panelGeometry);
}

static gboolean fromPathLength(GBinding *binding _U_, const GValue *source_value,
                               GValue *target_value, gpointer user_data _U_)
{
  gchar *text;

  if (g_value_get_uint(source_value))
    text = g_strdup_printf(PATH, g_value_get_uint(source_value));
  else
    text = g_strdup(NO_PATH);
  g_value_take_string(target_value, text);
  return TRUE;
}

static gboolean toShadeToggle(GBinding *binding _U_, const GValue *source_value,
                              GValue *target_value, gpointer user_data _U_)
{
  g_value_set_boolean(target_value, (g_value_get_boxed(source_value) != (gpointer)0));
  return TRUE;
}

static gboolean fromShadeToggle(GBinding *binding _U_, const GValue *source_value,
                                GValue *target_value, gpointer user_data)
{
  if (g_value_get_boolean(source_value))
    g_value_set_boxed(target_value, visu_ui_shade_combobox_getSelection(VISU_UI_SHADE_COMBOBOX(user_data)));
  else
    g_value_set_boxed(target_value, (gconstpointer)0);
  return TRUE;
}

static gboolean toShadeCombo(GBinding *binding _U_, const GValue *source_value,
                             GValue *target_value, gpointer user_data _U_)
{
  g_value_set_boxed(target_value, g_value_get_boxed(source_value));
  return TRUE;
}

static gboolean fromShadeCombo(GBinding *binding _U_, const GValue *source_value,
                               GValue *target_value, gpointer user_data)
{
  if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(user_data)))
    return FALSE;

  g_value_set_boxed(target_value, g_value_get_boxed(source_value));
  return TRUE;
}

static GtkWidget *createInteriorBox(VisuGlNodeScene *scene)
{
  GtkWidget *vbox, *vbox2, *hbox, *bt, *wd, *checkDiff, *checkOrdering, *labelVisuPaths;
  GtkWidget *checkVisuPaths, *hboxVisuPaths, *hboxVisuPaths2, *checkAdjust;
  GtkWidget *label;
  GtkWidget *boxTransform;
#define X_LABEL _("dx:")
#define Y_LABEL _("dy:")
#define Z_LABEL _("dz:")
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();
#endif

  vbox = gtk_vbox_new(FALSE, 0);

  boxTransform = visu_ui_box_transform_new();
  g_object_bind_property(scene, "data", boxTransform, "pointset", G_BINDING_SYNC_CREATE);
  visu_ui_box_transform_bindGlExtBox(VISU_UI_BOX_TRANSFORM(boxTransform),
                                     visu_gl_node_scene_getBox(scene));
  gtk_box_pack_start(GTK_BOX(vbox), boxTransform, FALSE, FALSE, 0);

  /************************/
  /* The multifile stuff. */
  /************************/
  label = gtk_label_new(_("<b>Multi file actions</b>"));
  gtk_widget_set_margin_top(label, 15);
  gtk_label_set_xalign(GTK_LABEL(label), 0.);
  gtk_widget_set_name(label, "label_head");
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

  vbox2 = gtk_vbox_new(FALSE, 2);
  gtk_widget_set_margin_start(vbox2, 15);
  gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
  
  checkAdjust = gtk_check_button_new_with_mnemonic(_("Automatic zoom _adjustment on file loading"));
  g_object_bind_property(visu_gl_node_scene_getGlView(scene), "auto-adjust",
                         checkAdjust, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_box_pack_start(GTK_BOX(vbox2), checkAdjust, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  checkOrdering = gtk_check_button_new_with_mnemonic(_("with re_ordering"));
  g_object_bind_property(scene, "reorder-reference", checkOrdering, "active",
                         G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
  gtk_widget_set_tooltip_text(checkOrdering,
			      _("On load of a new file, reorder the nodes to"
                                " minimize displacements."));
  gtk_box_pack_end(GTK_BOX(hbox), checkOrdering, FALSE, FALSE, 0);
  checkDiff = gtk_check_button_new_with_mnemonic(_("Show node _displacements"));
  g_object_bind_property(scene, "geometry-differences", checkDiff, "active",
                         G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
  gtk_widget_set_tooltip_text(checkDiff,
			      _("When a new file is loaded, draw arrows on  nodes that"
				" represent their displacements with respect to their"
				" previous positions."));
  gtk_box_pack_start(GTK_BOX(hbox), checkDiff, TRUE, TRUE, 0);

  hbox = gtk_hbox_new(FALSE, 2);
  gtk_widget_set_margin_start(hbox, 25);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("Current diff"), FALSE, FALSE, 0);
  g_object_bind_property(pasteGeodiff, "active", addGeodiff, "sensitive", G_BINDING_SYNC_CREATE);
  g_signal_connect(G_OBJECT(addGeodiff), "clicked",
		   G_CALLBACK(onAddDiff), (gpointer)pasteGeodiff);
  gtk_box_pack_end(GTK_BOX(hbox), addGeodiff, FALSE, FALSE, 0);
  gtk_widget_set_sensitive(pasteGeodiff, FALSE);
  gtk_box_pack_end(GTK_BOX(hbox), pasteGeodiff, FALSE, FALSE, 0);
  gtk_button_set_image_position(GTK_BUTTON(pasteGeodiff), GTK_POS_LEFT);
  g_signal_connect(G_OBJECT(pasteGeodiff), "clicked",
		   G_CALLBACK(onPasteDiff), (gpointer)copyGeodiff);
  gtk_widget_set_sensitive(copyGeodiff, FALSE);
  gtk_box_pack_end(GTK_BOX(hbox), copyGeodiff, FALSE, FALSE, 0);
  gtk_button_set_image_position(GTK_BUTTON(copyGeodiff), GTK_POS_LEFT);
  g_signal_connect(G_OBJECT(copyGeodiff), "clicked",
		   G_CALLBACK(onCopyDiff), (gpointer)0);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  checkVisuPaths = gtk_check_button_new_with_mnemonic(_("Use _paths"));
  g_object_bind_property(scene, "path-active", checkVisuPaths, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  gtk_widget_set_tooltip_text(checkVisuPaths,
			      _("Store differences between files and plot"
				" them as lines."));
  gtk_box_pack_start(GTK_BOX(hbox), checkVisuPaths, TRUE, TRUE, 0);
  hboxVisuPaths = gtk_hbox_new(FALSE, 2);
  g_object_bind_property(checkVisuPaths, "active", hboxVisuPaths, "sensitive",
                         G_BINDING_SYNC_CREATE);
  gtk_box_pack_start(GTK_BOX(hbox), hboxVisuPaths, FALSE, FALSE, 0);
  labelVisuPaths = gtk_label_new(NO_PATH);
  gtk_label_set_use_markup(GTK_LABEL(labelVisuPaths), TRUE);
  g_object_bind_property_full(scene, "path-length", labelVisuPaths, "label",
                              G_BINDING_SYNC_CREATE, fromPathLength,
                              (GBindingTransformFunc)0, (gpointer)0, (GDestroyNotify)0);
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths), labelVisuPaths, FALSE, FALSE, 0);
  gtk_widget_set_tooltip_text(togglePathSave, _("When toggled, store differences"
						" between files as paths"
						" through nodes.."));
  gtk_container_add(GTK_CONTAINER(togglePathSave),
		    gtk_image_new_from_icon_name("media-record",
                                                 GTK_ICON_SIZE_MENU));
  g_object_bind_property(scene, "record-path", togglePathSave, "active",
                         G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
  g_signal_connect(G_OBJECT(togglePathSave), "toggled",
		   G_CALLBACK(onPathSaveToggled), (gpointer)0);
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths), togglePathSave, FALSE, FALSE, 0);
  bt = gtk_button_new();
  gtk_widget_set_tooltip_text(bt, _("Remove all stored paths."));
  gtk_container_add(GTK_CONTAINER(bt),
		    gtk_image_new_from_icon_name("edit-clear",
                                                 GTK_ICON_SIZE_MENU));
  g_signal_connect_swapped(G_OBJECT(bt), "clicked",
                           G_CALLBACK(visu_gl_node_scene_clearPaths), scene);
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths), bt, FALSE, FALSE, 0);

  hboxVisuPaths2 = gtk_hbox_new(FALSE, 2);
  g_object_bind_property(checkVisuPaths, "active", hboxVisuPaths2, "sensitive",
                         G_BINDING_SYNC_CREATE);
  gtk_box_pack_start(GTK_BOX(vbox2), hboxVisuPaths2, FALSE, FALSE, 0);

  bt = gtk_check_button_new_with_mnemonic(_("colourise with: "));
  gtk_widget_set_tooltip_text(bt, _("If energy information was present"
				    " when loading file, colourise the paths"
				    " with shading colours."));
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), bt, TRUE, TRUE, 0);
  wd = visu_ui_shade_combobox_new(FALSE, FALSE);
  g_object_bind_property_full(scene, "path-shade", bt, "active",
                              G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                              toShadeToggle, fromShadeToggle, wd, (GDestroyNotify)0);
  g_object_bind_property_full(scene, "path-shade", wd, "shade",
                              G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
                              toShadeCombo, fromShadeCombo, bt, (GDestroyNotify)0);
  g_object_bind_property(bt, "active", wd, "sensitive", G_BINDING_SYNC_CREATE);
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), wd, FALSE, FALSE, 0);
  hboxIOVisuPaths = gtk_hbox_new(FALSE, 2);
/*   gtk_widget_set_sensitive(hboxIOVisuPaths, FALSE); */
  gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), hboxIOVisuPaths, FALSE, FALSE, 0);
  bt = gtk_button_new();
  gtk_widget_set_tooltip_text(bt, _("Read a set of paths from a file and"
				    " add them to the current set."));
  gtk_container_add(GTK_CONTAINER(bt),
		    gtk_image_new_from_icon_name("document-open",
                                                 GTK_ICON_SIZE_MENU));
  g_signal_connect(G_OBJECT(bt), "clicked",
		   G_CALLBACK(onLoadPathClicked), scene);
  gtk_box_pack_start(GTK_BOX(hboxIOVisuPaths), bt, FALSE, FALSE, 0);
  bt = gtk_button_new();
  gtk_widget_set_tooltip_text(bt, _("Save the current set of paths"
				    " to an XML file."));
  gtk_container_add(GTK_CONTAINER(bt),
		    gtk_image_new_from_icon_name("document-save",
                                                 GTK_ICON_SIZE_MENU));
  g_object_bind_property(scene, "path-length", bt, "sensitive", G_BINDING_SYNC_CREATE);
  g_signal_connect(G_OBJECT(bt), "clicked",
		   G_CALLBACK(onSavePathClicked), scene);
  gtk_box_pack_start(GTK_BOX(hboxIOVisuPaths), bt, FALSE, FALSE, 0);

  gtk_widget_show_all(vbox);

  widgetsNotBuilt = FALSE;

  return vbox;
}
static void updateSensitive(VisuData *dataObj)
{
  VisuNodeValues *vals = dataObj ? visu_data_getNodeProperties(dataObj, VISU_DATA_DIFF_DEFAULT_ID) : (VisuNodeValues*)0;
  gtk_widget_set_sensitive(copyGeodiff, (vals != (VisuNodeValues*)0));
  gtk_widget_set_sensitive(pasteGeodiff, g_object_get_data(G_OBJECT(copyGeodiff),
                                                           "stored-geodiff") != NULL);
}

/*************/
/* Callbacks */
/*************/
static void onEnter(VisuUiPanel *visu_ui_panel _U_, VisuUiRenderingWindow *window)
{
  VisuData *dataObj;

  if (widgetsNotBuilt)
    {
      DBG_fprintf(stderr, "Panel Geometry: first build on enter.\n");
      gtk_container_set_border_width(GTK_CONTAINER(panelGeometry), 5);
      gtk_container_add(GTK_CONTAINER(panelGeometry),
                        createInteriorBox(visu_ui_rendering_window_getGlScene(window)));
    }
  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry));
  updateSensitive(dataObj);
}
static void onDataFocused(GObject *obj _U_, VisuData* visuData, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel Geometry: Catch 'DataFocused' signal,"
	      " update values.\n");
  if (visuData)
    updateSensitive(visuData);
}
static guint highlightMatching(VisuGlExtMarks *marks, VisuData *dataObj, VisuNodeFinder *finder,
                               const gfloat delta[3])
{
  VisuNodeArrayIter iter;
  GArray *ids;
  gfloat xyz[3];
  gint id;
  guint ln;

  /* Remove previous marks. */
  visu_gl_ext_marks_unHighlight(marks);

  /* Add matching nodes. */
  ids = g_array_new(FALSE, FALSE, sizeof(guint));
  if (dataObj)
    {
      visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
      for (visu_node_array_iterStart(VISU_NODE_ARRAY(dataObj), &iter); iter.node;
           visu_node_array_iterNext(VISU_NODE_ARRAY(dataObj), &iter))
        {
          visu_data_getNodePosition(dataObj, iter.node, xyz);
          xyz[0] += delta[0];
          xyz[1] += delta[1];
          xyz[2] += delta[2];
          id = visu_node_finder_lookup(finder, xyz, 1.f);
          if (id >= 0)
            g_array_append_val(ids, id);
        }
    }
  visu_gl_ext_marks_setHighlight(marks, ids, MARKS_STATUS_SET);
  ln = ids->len;
  g_array_unref(ids);

  return ln;
}
static void onDragGeodiff(VisuInteractive *inter _U_, const gfloat delta[3], gpointer data)
{
  VisuUiRenderingWindow *window;
  VisuGlExtGeodiff *extGeodiff;
  /* VisuGlExtMarks *marks; */
  /* VisuNodeFinder *finder; */
  /* VisuData *dataObj; */
  guint ln;
  gchar *mess;

  window = visu_ui_main_class_getDefaultRendering();
  extGeodiff = g_object_get_data(G_OBJECT(data), "ext-geodiff");

  /* marks = visu_ui_rendering_window_getMarks(window); */
  /* finder = g_object_get_data(G_OBJECT(data), "finder-geodiff"); */
  /* dataObj = visu_gl_ext_node_vectors_getData(VISU_GL_EXT_NODE_VECTORS(extGeodiff)); */
  /* ln = highlightMatching(marks, dataObj, finder, delta); */

  ln = 123;
  mess = g_strdup_printf(_("Displacement field match %d node(s)."), ln);
  visu_ui_rendering_window_popMessage(window);
  visu_ui_rendering_window_pushMessage(window, mess);
  g_free(mess);

  visu_gl_ext_setTranslation(VISU_GL_EXT(extGeodiff), delta);
}
static void onDropGeodiff(VisuInteractive *inter _U_, const gfloat delta[3], gpointer data)
{
  gfloat trans[3];
  gfloat delta0[3] = {0.f, 0.f, 0.f};
  VisuNodeValues *vect;
  VisuPointset *dataObj;

  visu_gl_ext_setTranslation(VISU_GL_EXT(data), delta0);

  vect = visu_sourceable_getNodeModel(VISU_SOURCEABLE(data));
  dataObj = VISU_POINTSET(visu_node_values_getArray(vect));
  visu_pointset_getTranslation(dataObj, trans);
  delta0[0] = delta[0] + trans[0];
  delta0[1] = delta[1] + trans[1];
  delta0[2] = delta[2] + trans[2];
  visu_pointset_setTranslation(dataObj, delta0, FALSE);
  g_object_unref(dataObj);
}
static void onCopyDiff(GtkButton *button, gpointer data _U_)
{
  VisuData *dataObj;

  dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry));

  g_object_ref(dataObj);
  g_object_set_data_full(G_OBJECT(button), "stored-geodiff", (gpointer)dataObj,
                         (GDestroyNotify)g_object_unref);

  gtk_widget_set_sensitive(pasteGeodiff, TRUE);
}
static void onPasteDiff(GtkToggleButton *button, gpointer data)
{
  VisuData *dataObj;
  VisuNodeFinder *finder;
  VisuGlExtGeodiff *extGeodiff;
  VisuInteractive *inter;
  VisuUiRenderingWindow *window;
  VisuGlExtMarks *marks;
  gfloat delta[3] = {0.f, 0.f, 0.f};

  dataObj = g_object_get_data(G_OBJECT(data), "stored-geodiff");
  g_return_if_fail(dataObj);
  
  extGeodiff = g_object_get_data(G_OBJECT(button), "ext-geodiff");
  if (!extGeodiff)
    {
      extGeodiff = visu_gl_ext_geodiff_new(NULL);
      visu_gl_ext_set_add
        (VISU_GL_EXT_SET(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering())),
         VISU_GL_EXT(extGeodiff));
      g_object_set_data_full(G_OBJECT(button), "ext-geodiff", extGeodiff, g_object_unref);
    }

  finder = g_object_get_data(G_OBJECT(button), "finder-geodiff");
  if (!finder)
    {
      finder = visu_node_finder_new(visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry)));
      g_object_set_data_full(G_OBJECT(button), "finder-geodiff", finder, g_object_unref);
    }

  inter = g_object_get_data(G_OBJECT(button), "inter-geodiff");
  if (!inter)
    {
      inter = visu_interactive_new(interactive_drag);
      visu_interactive_setMessage(inter, _("Drag the displacement field."));
      g_object_set_data_full(G_OBJECT(button), "inter-geodiff", inter, g_object_unref);
      g_signal_connect(G_OBJECT(inter), "move",
                       G_CALLBACK(onDragGeodiff), (gpointer)button);
      g_signal_connect(G_OBJECT(inter), "stop-move",
                       G_CALLBACK(onDropGeodiff), (gpointer)extGeodiff);
    }

  window = visu_ui_main_class_getDefaultRendering();
  marks = visu_gl_node_scene_getMarks(visu_ui_rendering_window_getGlScene(window));
  if (gtk_toggle_button_get_active(button))
    {
      VisuNodeValues *vals = visu_data_getNodeProperties(dataObj, VISU_DATA_DIFF_DEFAULT_ID);
      visu_sourceable_setNodeModel(VISU_SOURCEABLE(extGeodiff),
                                   VISU_NODE_VALUES(vals));
      visu_gl_ext_node_vectors_setNodeRenderer(VISU_GL_EXT_NODE_VECTORS(extGeodiff),
                                               VISU_NODE_ARRAY_RENDERER(visu_gl_node_scene_getNodes(visu_ui_rendering_window_getGlScene(window))));
      visu_gl_ext_setActive(VISU_GL_EXT(extGeodiff), TRUE);
      visu_ui_rendering_window_pushInteractive(window, inter);
      
      highlightMatching(marks, dataObj, finder, delta);
    }
  else
    {
      visu_sourceable_setNodeModel(VISU_SOURCEABLE(extGeodiff), (VisuNodeValues*)0);
      visu_gl_ext_setActive(VISU_GL_EXT(extGeodiff), FALSE);
      visu_ui_rendering_window_popInteractive(window, inter);
      
      highlightMatching(marks, (VisuData*)0, finder, delta);
    }
}
static void onAddDiff(GtkButton *button _U_, gpointer data)
{
  VisuGlExtNodeVectors *extGeodiff;
  VisuNodeFinder *finder;
  VisuDataDiff *geodiff;

  extGeodiff = g_object_get_data(G_OBJECT(data), "ext-geodiff");
  geodiff = VISU_DATA_DIFF(visu_sourceable_getNodeModel(VISU_SOURCEABLE(extGeodiff)));
  finder = g_object_get_data(G_OBJECT(data), "finder-geodiff");
  visu_data_diff_applyWithFinder(geodiff, finder, 1.f);

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), FALSE);
}

static void onPathSaveToggled(GtkToggleButton *toggle, gpointer data _U_)
{
  if (gtk_toggle_button_get_active(toggle))
    visu_ui_panel_browser_setMessage(_("Recording paths"), GTK_MESSAGE_INFO);
  else
    visu_ui_panel_browser_setMessage((const gchar*)0, GTK_MESSAGE_INFO);
}
static void onDirBrowsed(VisuUiMain *obj _U_, VisuUiDirectoryType type, gpointer user _U_)
{
  if (type == VISU_UI_DIR_BROWSER)
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglePathSave), FALSE);
}
static void onSavePathClicked(GtkButton *bt _U_, gpointer data)
{
  GtkWidget *wd;
  gint response;
  GError *error;
  gchar *base;

  wd = gtk_file_chooser_dialog_new(_("Export current set of paths."), (GtkWindow*)0,
				   GTK_FILE_CHOOSER_ACTION_SAVE,
				   TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
				   TOOL_ICON_SAVE, GTK_RESPONSE_ACCEPT,
				   NULL);
  if (!exportPathFile)
    exportPathFile = g_build_filename(g_get_current_dir(), _("paths.xml"), NULL);
  base = g_path_get_basename(exportPathFile);
  gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(wd), base);
  g_free(base);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 7
  gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(wd), TRUE);
#endif
  do
    {
      response = gtk_dialog_run(GTK_DIALOG(wd));
      if (exportPathFile)
	g_free(exportPathFile);
      exportPathFile = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wd));
      switch (response)
	{
	case GTK_RESPONSE_ACCEPT:
	  error = (GError*)0;
	  if (!visu_gl_node_scene_exportPathsToXML(VISU_GL_NODE_SCENE(data),
                                                   exportPathFile, &error))
	    {
	      visu_ui_raiseWarning(_("Export current set of paths."),
				   error->message, GTK_WINDOW(wd));
	      g_error_free(error);
	      response = GTK_RESPONSE_NONE;
	    }
	  break;
	default:
	  response = GTK_RESPONSE_ACCEPT;
	  break;
	}
    }
  while (response != GTK_RESPONSE_ACCEPT);
  gtk_widget_destroy(wd);
}
static void onLoadPathClicked(GtkButton *bt _U_, gpointer data)
{
  GtkWidget *wd;
  gint response;
  GError *error;
  const gchar *directory;

  wd = gtk_file_chooser_dialog_new(_("Load a set of paths."), (GtkWindow*)0,
				   GTK_FILE_CHOOSER_ACTION_OPEN,
				   TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
				   TOOL_ICON_OPEN, GTK_RESPONSE_ACCEPT,
				   NULL);
  directory = visu_ui_main_getLastOpenDirectory(visu_ui_main_class_getCurrentPanel());
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(wd), directory);
  do
    {
      response = gtk_dialog_run(GTK_DIALOG(wd));
      if (exportPathFile)
	g_free(exportPathFile);
      exportPathFile = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wd));
      switch (response)
	{
	case GTK_RESPONSE_ACCEPT:
	  error = (GError*)0;
	  if (!visu_gl_node_scene_parsePathsFromXML(VISU_GL_NODE_SCENE(data),
                                                    exportPathFile, &error))
	    {
	      visu_ui_raiseWarning(_("Load a set of paths."),
				   error->message, GTK_WINDOW(wd));
	      g_error_free(error);
	      response = GTK_RESPONSE_NONE;
	    }
	  break;
	default:
	  response = GTK_RESPONSE_ACCEPT;
	  break;
	}
    }
  while (response != GTK_RESPONSE_ACCEPT);
  gtk_widget_destroy(wd);
}
