1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Luc BILLARD et Damien
3 	CALISTE, laboratoire L_Sim, (2001-2005)
4 
5 	Adresse mèl :
6 	BILLARD, non joignable par mèl ;
7 	CALISTE, damien P caliste AT cea P fr.
8 
9 	Ce logiciel est un programme informatique servant à visualiser des
10 	structures atomiques dans un rendu pseudo-3D.
11 
12 	Ce logiciel est régi par la licence CeCILL soumise au droit français et
13 	respectant les principes de diffusion des logiciels libres. Vous pouvez
14 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
15 	de la licence CeCILL telle que diffusée par le CEA, le CNRS et l'INRIA
16 	sur le site "http://www.cecill.info".
17 
18 	Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
19 	pris connaissance de la licence CeCILL, et que vous en avez accepté les
20 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
21 */
22 
23 /*   LICENCE SUM UP
24 	Copyright CEA, contributors : Luc BILLARD et Damien
25 	CALISTE, laboratoire L_Sim, (2001-2005)
26 
27 	E-mail address:
28 	BILLARD, not reachable any more ;
29 	CALISTE, damien P caliste AT cea P fr.
30 
31 	This software is a computer program whose purpose is to visualize atomic
32 	configurations in 3D.
33 
34 	This software is governed by the CeCILL  license under French law and
35 	abiding by the rules of distribution of free software.  You can  use,
36 	modify and/ or redistribute the software under the terms of the CeCILL
37 	license as circulated by CEA, CNRS and INRIA at the following URL
38 	"http://www.cecill.info".
39 
40 	The fact that you are presently reading this means that you have had
41 	knowledge of the CeCILL license and that you accept its terms. You can
42 	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
43 */
44 #include "panelGeometry.h"
45 #include "panelBrowser.h"
46 
47 #include <support.h>
48 #include <visu_tools.h>
49 #include <visu_data.h>
50 #include <visu_basic.h>
51 #include <visu_gtk.h>
52 #include <gtk_main.h>
53 #include <gtk_renderingWindowWidget.h>
54 #include <extensions/box.h>
55 #include <extensions/geodiff.h>
56 #include <extensions/paths.h>
57 #include <openGLFunctions/objectList.h>
58 #include <coreTools/toolColor.h>
59 #include <coreTools/toolPhysic.h>
60 #include <extraFunctions/finder.h>
61 #include <extraGtkFunctions/gtk_colorComboBoxWidget.h>
62 #include <extraGtkFunctions/gtk_stippleComboBoxWidget.h>
63 #include <extraGtkFunctions/gtk_shadeComboBoxWidget.h>
64 #include <uiElements/ui_boxTransform.h>
65 
66 /**
67  * SECTION: panelGeometry
68  * @short_description: This tab gathers the geometry operation on a
69  * #VisuData, like periodic translation, physical units, ...
70  *
71  * <para>Nothing tunable here.</para>
72  */
73 
74 /* Local objects. */
75 static GtkWidget *panelGeometry;
76 static GtkWidget *togglePathSave;
77 static GtkWidget *hboxIOVisuPaths;
78 static GtkWidget *copyGeodiff, *pasteGeodiff, *addGeodiff;
79 
80 /* Local variables. */
81 static gboolean disableCallbacks;
82 static gboolean widgetsNotBuilt;
83 static gchar *exportPathFile = NULL;
84 
85 #define NO_PATH _("<span size=\"small\"><i>No stored path</i></span>")
86 #define PATH _("<span size=\"small\">Path has %d step(s)</span>")
87 
88 /* Local routines. */
89 static GtkWidget *createInteriorBox(VisuGlNodeScene *scene);
90 
91 /* Local callbacks. */
92 static void onEnter(VisuUiPanel *visu_ui_panel, VisuUiRenderingWindow *window);
93 static void onDataFocused(GObject *obj, VisuData* visuData, gpointer data);
94 static void onPathSaveToggled(GtkToggleButton *toggle, gpointer data);
95 static void onSavePathClicked(GtkButton *bt, gpointer data);
96 static void onLoadPathClicked(GtkButton *bt, gpointer data);
97 static void onDirBrowsed(VisuUiMain *obj, VisuUiDirectoryType type, gpointer user);
98 static void onCopyDiff(GtkButton *button, gpointer data);
99 static void onPasteDiff(GtkToggleButton *button, gpointer data);
100 static void onAddDiff(GtkButton *button, gpointer data);
101 
102 /**
103  * visu_ui_panel_geometry_init: (skip)
104  * @ui: a #VisuUiMain object.
105  *
106  * Should be used in the list declared in externalModules.h to be loaded by
107  * V_Sim on start-up. This routine will create the #VisuUiPanel where the box
108  * stuff can be tuned, such as the bounding box, its colour, and the actions linked
109  * to the periodicity (translation, dupplication...).
110  *
111  * Returns: a newly created #VisuUiPanel object.
112  */
113 VisuUiPanel* visu_ui_panel_geometry_init(VisuUiMain *ui)
114 {
115   panelGeometry = visu_ui_panel_newWithIconFromPath("Panel_geometry",
116 						_("Geometry operations"),
117 						_("Geometry"),
118 						"stock-geometry_20.png");
119   if (!panelGeometry)
120     return (VisuUiPanel*)0;
121 
122   visu_ui_panel_setDockable(VISU_UI_PANEL(panelGeometry), TRUE);
123 
124   /* Create the widgets that are needed even without the GTK interface be built. */
125   togglePathSave = gtk_toggle_button_new();
126   pasteGeodiff = gtk_toggle_button_new_with_label(_("Paste and align"));
127   copyGeodiff = gtk_button_new_from_icon_name("edit-copy", GTK_ICON_SIZE_BUTTON);
128   addGeodiff = gtk_button_new_from_icon_name("list-add", GTK_ICON_SIZE_BUTTON);
129 
130   /* Create the callbacks of all the sensitive widgets. */
131   g_signal_connect(panelGeometry, "page-entered",
132 		   G_CALLBACK(onEnter), (gpointer)visu_ui_main_getRendering(ui));
133   g_signal_connect(ui, "DirectoryChanged",
134 		   G_CALLBACK(onDirBrowsed), (gpointer)0);
135   g_signal_connect(ui, "DataFocused",
136 		   G_CALLBACK(onDataFocused), (gpointer)0);
137 
138   /* Private parameters. */
139   disableCallbacks = FALSE;
140   widgetsNotBuilt  = TRUE;
141 
142   return VISU_UI_PANEL(panelGeometry);
143 }
144 
145 static gboolean fromPathLength(GBinding *binding _U_, const GValue *source_value,
146                                GValue *target_value, gpointer user_data _U_)
147 {
148   gchar *text;
149 
150   if (g_value_get_uint(source_value))
151     text = g_strdup_printf(PATH, g_value_get_uint(source_value));
152   else
153     text = g_strdup(NO_PATH);
154   g_value_take_string(target_value, text);
155   return TRUE;
156 }
157 
158 static gboolean toShadeToggle(GBinding *binding _U_, const GValue *source_value,
159                               GValue *target_value, gpointer user_data _U_)
160 {
161   g_value_set_boolean(target_value, (g_value_get_boxed(source_value) != (gpointer)0));
162   return TRUE;
163 }
164 
165 static gboolean fromShadeToggle(GBinding *binding _U_, const GValue *source_value,
166                                 GValue *target_value, gpointer user_data)
167 {
168   if (g_value_get_boolean(source_value))
169     g_value_set_boxed(target_value, visu_ui_shade_combobox_getSelection(VISU_UI_SHADE_COMBOBOX(user_data)));
170   else
171     g_value_set_boxed(target_value, (gconstpointer)0);
172   return TRUE;
173 }
174 
175 static gboolean toShadeCombo(GBinding *binding _U_, const GValue *source_value,
176                              GValue *target_value, gpointer user_data _U_)
177 {
178   g_value_set_boxed(target_value, g_value_get_boxed(source_value));
179   return TRUE;
180 }
181 
182 static gboolean fromShadeCombo(GBinding *binding _U_, const GValue *source_value,
183                                GValue *target_value, gpointer user_data)
184 {
185   if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(user_data)))
186     return FALSE;
187 
188   g_value_set_boxed(target_value, g_value_get_boxed(source_value));
189   return TRUE;
190 }
191 
192 static GtkWidget *createInteriorBox(VisuGlNodeScene *scene)
193 {
194   GtkWidget *vbox, *vbox2, *hbox, *bt, *wd, *checkDiff, *checkOrdering, *labelVisuPaths;
195   GtkWidget *checkVisuPaths, *hboxVisuPaths, *hboxVisuPaths2, *checkAdjust;
196   GtkWidget *label;
197   GtkWidget *boxTransform;
198 #define X_LABEL _("dx:")
199 #define Y_LABEL _("dy:")
200 #define Z_LABEL _("dz:")
201 #if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
202   GtkTooltips *tooltips;
203 
204   tooltips = gtk_tooltips_new ();
205 #endif
206 
207   vbox = gtk_vbox_new(FALSE, 0);
208 
209   boxTransform = visu_ui_box_transform_new();
210   g_object_bind_property(scene, "data", boxTransform, "pointset", G_BINDING_SYNC_CREATE);
211   visu_ui_box_transform_bindGlExtBox(VISU_UI_BOX_TRANSFORM(boxTransform),
212                                      visu_gl_node_scene_getBox(scene));
213   gtk_box_pack_start(GTK_BOX(vbox), boxTransform, FALSE, FALSE, 0);
214 
215   /************************/
216   /* The multifile stuff. */
217   /************************/
218   label = gtk_label_new(_("<b>Multi file actions</b>"));
219   gtk_widget_set_margin_top(label, 15);
220   gtk_label_set_xalign(GTK_LABEL(label), 0.);
221   gtk_widget_set_name(label, "label_head");
222   gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
223   gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
224 
225   vbox2 = gtk_vbox_new(FALSE, 2);
226   gtk_widget_set_margin_start(vbox2, 15);
227   gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 0);
228 
229   checkAdjust = gtk_check_button_new_with_mnemonic(_("Automatic zoom _adjustment on file loading"));
230   g_object_bind_property(visu_gl_node_scene_getGlView(scene), "auto-adjust",
231                          checkAdjust, "active",
232                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
233   gtk_box_pack_start(GTK_BOX(vbox2), checkAdjust, FALSE, FALSE, 0);
234 
235   hbox = gtk_hbox_new(FALSE, 0);
236   gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
237   checkOrdering = gtk_check_button_new_with_mnemonic(_("with re_ordering"));
238   g_object_bind_property(scene, "reorder-reference", checkOrdering, "active",
239                          G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
240   gtk_widget_set_tooltip_text(checkOrdering,
241 			      _("On load of a new file, reorder the nodes to"
242                                 " minimize displacements."));
243   gtk_box_pack_end(GTK_BOX(hbox), checkOrdering, FALSE, FALSE, 0);
244   checkDiff = gtk_check_button_new_with_mnemonic(_("Show node _displacements"));
245   g_object_bind_property(scene, "geometry-differences", checkDiff, "active",
246                          G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
247   gtk_widget_set_tooltip_text(checkDiff,
248 			      _("When a new file is loaded, draw arrows on  nodes that"
249 				" represent their displacements with respect to their"
250 				" previous positions."));
251   gtk_box_pack_start(GTK_BOX(hbox), checkDiff, TRUE, TRUE, 0);
252 
253   hbox = gtk_hbox_new(FALSE, 2);
254   gtk_widget_set_margin_start(hbox, 25);
255   gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
256   gtk_box_pack_start(GTK_BOX(hbox), gtk_label_new("Current diff"), FALSE, FALSE, 0);
257   g_object_bind_property(pasteGeodiff, "active", addGeodiff, "sensitive", G_BINDING_SYNC_CREATE);
258   g_signal_connect(G_OBJECT(addGeodiff), "clicked",
259 		   G_CALLBACK(onAddDiff), (gpointer)pasteGeodiff);
260   gtk_box_pack_end(GTK_BOX(hbox), addGeodiff, FALSE, FALSE, 0);
261   gtk_widget_set_sensitive(pasteGeodiff, FALSE);
262   gtk_box_pack_end(GTK_BOX(hbox), pasteGeodiff, FALSE, FALSE, 0);
263   gtk_button_set_image_position(GTK_BUTTON(pasteGeodiff), GTK_POS_LEFT);
264   g_signal_connect(G_OBJECT(pasteGeodiff), "clicked",
265 		   G_CALLBACK(onPasteDiff), (gpointer)copyGeodiff);
266   gtk_widget_set_sensitive(copyGeodiff, FALSE);
267   gtk_box_pack_end(GTK_BOX(hbox), copyGeodiff, FALSE, FALSE, 0);
268   gtk_button_set_image_position(GTK_BUTTON(copyGeodiff), GTK_POS_LEFT);
269   g_signal_connect(G_OBJECT(copyGeodiff), "clicked",
270 		   G_CALLBACK(onCopyDiff), (gpointer)0);
271 
272   hbox = gtk_hbox_new(FALSE, 0);
273   gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
274   checkVisuPaths = gtk_check_button_new_with_mnemonic(_("Use _paths"));
275   g_object_bind_property(scene, "path-active", checkVisuPaths, "active",
276                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
277   gtk_widget_set_tooltip_text(checkVisuPaths,
278 			      _("Store differences between files and plot"
279 				" them as lines."));
280   gtk_box_pack_start(GTK_BOX(hbox), checkVisuPaths, TRUE, TRUE, 0);
281   hboxVisuPaths = gtk_hbox_new(FALSE, 2);
282   g_object_bind_property(checkVisuPaths, "active", hboxVisuPaths, "sensitive",
283                          G_BINDING_SYNC_CREATE);
284   gtk_box_pack_start(GTK_BOX(hbox), hboxVisuPaths, FALSE, FALSE, 0);
285   labelVisuPaths = gtk_label_new(NO_PATH);
286   gtk_label_set_use_markup(GTK_LABEL(labelVisuPaths), TRUE);
287   g_object_bind_property_full(scene, "path-length", labelVisuPaths, "label",
288                               G_BINDING_SYNC_CREATE, fromPathLength,
289                               (GBindingTransformFunc)0, (gpointer)0, (GDestroyNotify)0);
290   gtk_box_pack_start(GTK_BOX(hboxVisuPaths), labelVisuPaths, FALSE, FALSE, 0);
291   gtk_widget_set_tooltip_text(togglePathSave, _("When toggled, store differences"
292 						" between files as paths"
293 						" through nodes.."));
294   gtk_container_add(GTK_CONTAINER(togglePathSave),
295 		    gtk_image_new_from_icon_name("media-record",
296                                                  GTK_ICON_SIZE_MENU));
297   g_object_bind_property(scene, "record-path", togglePathSave, "active",
298                          G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
299   g_signal_connect(G_OBJECT(togglePathSave), "toggled",
300 		   G_CALLBACK(onPathSaveToggled), (gpointer)0);
301   gtk_box_pack_start(GTK_BOX(hboxVisuPaths), togglePathSave, FALSE, FALSE, 0);
302   bt = gtk_button_new();
303   gtk_widget_set_tooltip_text(bt, _("Remove all stored paths."));
304   gtk_container_add(GTK_CONTAINER(bt),
305 		    gtk_image_new_from_icon_name("edit-clear",
306                                                  GTK_ICON_SIZE_MENU));
307   g_signal_connect_swapped(G_OBJECT(bt), "clicked",
308                            G_CALLBACK(visu_gl_node_scene_clearPaths), scene);
309   gtk_box_pack_start(GTK_BOX(hboxVisuPaths), bt, FALSE, FALSE, 0);
310 
311   hboxVisuPaths2 = gtk_hbox_new(FALSE, 2);
312   g_object_bind_property(checkVisuPaths, "active", hboxVisuPaths2, "sensitive",
313                          G_BINDING_SYNC_CREATE);
314   gtk_box_pack_start(GTK_BOX(vbox2), hboxVisuPaths2, FALSE, FALSE, 0);
315 
316   bt = gtk_check_button_new_with_mnemonic(_("colourise with: "));
317   gtk_widget_set_tooltip_text(bt, _("If energy information was present"
318 				    " when loading file, colourise the paths"
319 				    " with shading colours."));
320   gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), bt, TRUE, TRUE, 0);
321   wd = visu_ui_shade_combobox_new(FALSE, FALSE);
322   g_object_bind_property_full(scene, "path-shade", bt, "active",
323                               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
324                               toShadeToggle, fromShadeToggle, wd, (GDestroyNotify)0);
325   g_object_bind_property_full(scene, "path-shade", wd, "shade",
326                               G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL,
327                               toShadeCombo, fromShadeCombo, bt, (GDestroyNotify)0);
328   g_object_bind_property(bt, "active", wd, "sensitive", G_BINDING_SYNC_CREATE);
329   gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), wd, FALSE, FALSE, 0);
330   hboxIOVisuPaths = gtk_hbox_new(FALSE, 2);
331 /*   gtk_widget_set_sensitive(hboxIOVisuPaths, FALSE); */
332   gtk_box_pack_start(GTK_BOX(hboxVisuPaths2), hboxIOVisuPaths, FALSE, FALSE, 0);
333   bt = gtk_button_new();
334   gtk_widget_set_tooltip_text(bt, _("Read a set of paths from a file and"
335 				    " add them to the current set."));
336   gtk_container_add(GTK_CONTAINER(bt),
337 		    gtk_image_new_from_icon_name("document-open",
338                                                  GTK_ICON_SIZE_MENU));
339   g_signal_connect(G_OBJECT(bt), "clicked",
340 		   G_CALLBACK(onLoadPathClicked), scene);
341   gtk_box_pack_start(GTK_BOX(hboxIOVisuPaths), bt, FALSE, FALSE, 0);
342   bt = gtk_button_new();
343   gtk_widget_set_tooltip_text(bt, _("Save the current set of paths"
344 				    " to an XML file."));
345   gtk_container_add(GTK_CONTAINER(bt),
346 		    gtk_image_new_from_icon_name("document-save",
347                                                  GTK_ICON_SIZE_MENU));
348   g_object_bind_property(scene, "path-length", bt, "sensitive", G_BINDING_SYNC_CREATE);
349   g_signal_connect(G_OBJECT(bt), "clicked",
350 		   G_CALLBACK(onSavePathClicked), scene);
351   gtk_box_pack_start(GTK_BOX(hboxIOVisuPaths), bt, FALSE, FALSE, 0);
352 
353   gtk_widget_show_all(vbox);
354 
355   widgetsNotBuilt = FALSE;
356 
357   return vbox;
358 }
359 static void updateSensitive(VisuData *dataObj)
360 {
361   VisuNodeValues *vals = dataObj ? visu_data_getNodeProperties(dataObj, VISU_DATA_DIFF_DEFAULT_ID) : (VisuNodeValues*)0;
362   gtk_widget_set_sensitive(copyGeodiff, (vals != (VisuNodeValues*)0));
363   gtk_widget_set_sensitive(pasteGeodiff, g_object_get_data(G_OBJECT(copyGeodiff),
364                                                            "stored-geodiff") != NULL);
365 }
366 
367 /*************/
368 /* Callbacks */
369 /*************/
370 static void onEnter(VisuUiPanel *visu_ui_panel _U_, VisuUiRenderingWindow *window)
371 {
372   VisuData *dataObj;
373 
374   if (widgetsNotBuilt)
375     {
376       DBG_fprintf(stderr, "Panel Geometry: first build on enter.\n");
377       gtk_container_set_border_width(GTK_CONTAINER(panelGeometry), 5);
378       gtk_container_add(GTK_CONTAINER(panelGeometry),
379                         createInteriorBox(visu_ui_rendering_window_getGlScene(window)));
380     }
381   dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry));
382   updateSensitive(dataObj);
383 }
384 static void onDataFocused(GObject *obj _U_, VisuData* visuData, gpointer data _U_)
385 {
386   DBG_fprintf(stderr, "Panel Geometry: Catch 'DataFocused' signal,"
387 	      " update values.\n");
388   if (visuData)
389     updateSensitive(visuData);
390 }
391 static guint highlightMatching(VisuGlExtMarks *marks, VisuData *dataObj, VisuNodeFinder *finder,
392                                const gfloat delta[3])
393 {
394   VisuNodeArrayIter iter;
395   GArray *ids;
396   gfloat xyz[3];
397   gint id;
398   guint ln;
399 
400   /* Remove previous marks. */
401   visu_gl_ext_marks_unHighlight(marks);
402 
403   /* Add matching nodes. */
404   ids = g_array_new(FALSE, FALSE, sizeof(guint));
405   if (dataObj)
406     {
407       visu_node_array_iter_new(VISU_NODE_ARRAY(dataObj), &iter);
408       for (visu_node_array_iterStart(VISU_NODE_ARRAY(dataObj), &iter); iter.node;
409            visu_node_array_iterNext(VISU_NODE_ARRAY(dataObj), &iter))
410         {
411           visu_data_getNodePosition(dataObj, iter.node, xyz);
412           xyz[0] += delta[0];
413           xyz[1] += delta[1];
414           xyz[2] += delta[2];
415           id = visu_node_finder_lookup(finder, xyz, 1.f);
416           if (id >= 0)
417             g_array_append_val(ids, id);
418         }
419     }
420   visu_gl_ext_marks_setHighlight(marks, ids, MARKS_STATUS_SET);
421   ln = ids->len;
422   g_array_unref(ids);
423 
424   return ln;
425 }
426 static void onDragGeodiff(VisuInteractive *inter _U_, const gfloat delta[3], gpointer data)
427 {
428   VisuUiRenderingWindow *window;
429   VisuGlExtGeodiff *extGeodiff;
430   /* VisuGlExtMarks *marks; */
431   /* VisuNodeFinder *finder; */
432   /* VisuData *dataObj; */
433   guint ln;
434   gchar *mess;
435 
436   window = visu_ui_main_class_getDefaultRendering();
437   extGeodiff = g_object_get_data(G_OBJECT(data), "ext-geodiff");
438 
439   /* marks = visu_ui_rendering_window_getMarks(window); */
440   /* finder = g_object_get_data(G_OBJECT(data), "finder-geodiff"); */
441   /* dataObj = visu_gl_ext_node_vectors_getData(VISU_GL_EXT_NODE_VECTORS(extGeodiff)); */
442   /* ln = highlightMatching(marks, dataObj, finder, delta); */
443 
444   ln = 123;
445   mess = g_strdup_printf(_("Displacement field match %d node(s)."), ln);
446   visu_ui_rendering_window_popMessage(window);
447   visu_ui_rendering_window_pushMessage(window, mess);
448   g_free(mess);
449 
450   visu_gl_ext_setTranslation(VISU_GL_EXT(extGeodiff), delta);
451 }
452 static void onDropGeodiff(VisuInteractive *inter _U_, const gfloat delta[3], gpointer data)
453 {
454   gfloat trans[3];
455   gfloat delta0[3] = {0.f, 0.f, 0.f};
456   VisuNodeValues *vect;
457   VisuPointset *dataObj;
458 
459   visu_gl_ext_setTranslation(VISU_GL_EXT(data), delta0);
460 
461   vect = visu_sourceable_getNodeModel(VISU_SOURCEABLE(data));
462   dataObj = VISU_POINTSET(visu_node_values_getArray(vect));
463   visu_pointset_getTranslation(dataObj, trans);
464   delta0[0] = delta[0] + trans[0];
465   delta0[1] = delta[1] + trans[1];
466   delta0[2] = delta[2] + trans[2];
467   visu_pointset_setTranslation(dataObj, delta0, FALSE);
468   g_object_unref(dataObj);
469 }
470 static void onCopyDiff(GtkButton *button, gpointer data _U_)
471 {
472   VisuData *dataObj;
473 
474   dataObj = visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry));
475 
476   g_object_ref(dataObj);
477   g_object_set_data_full(G_OBJECT(button), "stored-geodiff", (gpointer)dataObj,
478                          (GDestroyNotify)g_object_unref);
479 
480   gtk_widget_set_sensitive(pasteGeodiff, TRUE);
481 }
482 static void onPasteDiff(GtkToggleButton *button, gpointer data)
483 {
484   VisuData *dataObj;
485   VisuNodeFinder *finder;
486   VisuGlExtGeodiff *extGeodiff;
487   VisuInteractive *inter;
488   VisuUiRenderingWindow *window;
489   VisuGlExtMarks *marks;
490   gfloat delta[3] = {0.f, 0.f, 0.f};
491 
492   dataObj = g_object_get_data(G_OBJECT(data), "stored-geodiff");
493   g_return_if_fail(dataObj);
494 
495   extGeodiff = g_object_get_data(G_OBJECT(button), "ext-geodiff");
496   if (!extGeodiff)
497     {
498       extGeodiff = visu_gl_ext_geodiff_new(NULL);
499       visu_gl_ext_set_add
500         (VISU_GL_EXT_SET(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering())),
501          VISU_GL_EXT(extGeodiff));
502       g_object_set_data_full(G_OBJECT(button), "ext-geodiff", extGeodiff, g_object_unref);
503     }
504 
505   finder = g_object_get_data(G_OBJECT(button), "finder-geodiff");
506   if (!finder)
507     {
508       finder = visu_node_finder_new(visu_ui_panel_getData(VISU_UI_PANEL(panelGeometry)));
509       g_object_set_data_full(G_OBJECT(button), "finder-geodiff", finder, g_object_unref);
510     }
511 
512   inter = g_object_get_data(G_OBJECT(button), "inter-geodiff");
513   if (!inter)
514     {
515       inter = visu_interactive_new(interactive_drag);
516       visu_interactive_setMessage(inter, _("Drag the displacement field."));
517       g_object_set_data_full(G_OBJECT(button), "inter-geodiff", inter, g_object_unref);
518       g_signal_connect(G_OBJECT(inter), "move",
519                        G_CALLBACK(onDragGeodiff), (gpointer)button);
520       g_signal_connect(G_OBJECT(inter), "stop-move",
521                        G_CALLBACK(onDropGeodiff), (gpointer)extGeodiff);
522     }
523 
524   window = visu_ui_main_class_getDefaultRendering();
525   marks = visu_gl_node_scene_getMarks(visu_ui_rendering_window_getGlScene(window));
526   if (gtk_toggle_button_get_active(button))
527     {
528       VisuNodeValues *vals = visu_data_getNodeProperties(dataObj, VISU_DATA_DIFF_DEFAULT_ID);
529       visu_sourceable_setNodeModel(VISU_SOURCEABLE(extGeodiff),
530                                    VISU_NODE_VALUES(vals));
531       visu_gl_ext_node_vectors_setNodeRenderer(VISU_GL_EXT_NODE_VECTORS(extGeodiff),
532                                                VISU_NODE_ARRAY_RENDERER(visu_gl_node_scene_getNodes(visu_ui_rendering_window_getGlScene(window))));
533       visu_gl_ext_setActive(VISU_GL_EXT(extGeodiff), TRUE);
534       visu_ui_rendering_window_pushInteractive(window, inter);
535 
536       highlightMatching(marks, dataObj, finder, delta);
537     }
538   else
539     {
540       visu_sourceable_setNodeModel(VISU_SOURCEABLE(extGeodiff), (VisuNodeValues*)0);
541       visu_gl_ext_setActive(VISU_GL_EXT(extGeodiff), FALSE);
542       visu_ui_rendering_window_popInteractive(window, inter);
543 
544       highlightMatching(marks, (VisuData*)0, finder, delta);
545     }
546 }
547 static void onAddDiff(GtkButton *button _U_, gpointer data)
548 {
549   VisuGlExtNodeVectors *extGeodiff;
550   VisuNodeFinder *finder;
551   VisuDataDiff *geodiff;
552 
553   extGeodiff = g_object_get_data(G_OBJECT(data), "ext-geodiff");
554   geodiff = VISU_DATA_DIFF(visu_sourceable_getNodeModel(VISU_SOURCEABLE(extGeodiff)));
555   finder = g_object_get_data(G_OBJECT(data), "finder-geodiff");
556   visu_data_diff_applyWithFinder(geodiff, finder, 1.f);
557 
558   gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(data), FALSE);
559 }
560 
561 static void onPathSaveToggled(GtkToggleButton *toggle, gpointer data _U_)
562 {
563   if (gtk_toggle_button_get_active(toggle))
564     visu_ui_panel_browser_setMessage(_("Recording paths"), GTK_MESSAGE_INFO);
565   else
566     visu_ui_panel_browser_setMessage((const gchar*)0, GTK_MESSAGE_INFO);
567 }
568 static void onDirBrowsed(VisuUiMain *obj _U_, VisuUiDirectoryType type, gpointer user _U_)
569 {
570   if (type == VISU_UI_DIR_BROWSER)
571     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(togglePathSave), FALSE);
572 }
573 static void onSavePathClicked(GtkButton *bt _U_, gpointer data)
574 {
575   GtkWidget *wd;
576   gint response;
577   GError *error;
578   gchar *base;
579 
580   wd = gtk_file_chooser_dialog_new(_("Export current set of paths."), (GtkWindow*)0,
581 				   GTK_FILE_CHOOSER_ACTION_SAVE,
582 				   TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
583 				   TOOL_ICON_SAVE, GTK_RESPONSE_ACCEPT,
584 				   NULL);
585   if (!exportPathFile)
586     exportPathFile = g_build_filename(g_get_current_dir(), _("paths.xml"), NULL);
587   base = g_path_get_basename(exportPathFile);
588   gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(wd), base);
589   g_free(base);
590 #if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 7
591   gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(wd), TRUE);
592 #endif
593   do
594     {
595       response = gtk_dialog_run(GTK_DIALOG(wd));
596       if (exportPathFile)
597 	g_free(exportPathFile);
598       exportPathFile = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wd));
599       switch (response)
600 	{
601 	case GTK_RESPONSE_ACCEPT:
602 	  error = (GError*)0;
603 	  if (!visu_gl_node_scene_exportPathsToXML(VISU_GL_NODE_SCENE(data),
604                                                    exportPathFile, &error))
605 	    {
606 	      visu_ui_raiseWarning(_("Export current set of paths."),
607 				   error->message, GTK_WINDOW(wd));
608 	      g_error_free(error);
609 	      response = GTK_RESPONSE_NONE;
610 	    }
611 	  break;
612 	default:
613 	  response = GTK_RESPONSE_ACCEPT;
614 	  break;
615 	}
616     }
617   while (response != GTK_RESPONSE_ACCEPT);
618   gtk_widget_destroy(wd);
619 }
620 static void onLoadPathClicked(GtkButton *bt _U_, gpointer data)
621 {
622   GtkWidget *wd;
623   gint response;
624   GError *error;
625   const gchar *directory;
626 
627   wd = gtk_file_chooser_dialog_new(_("Load a set of paths."), (GtkWindow*)0,
628 				   GTK_FILE_CHOOSER_ACTION_OPEN,
629 				   TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
630 				   TOOL_ICON_OPEN, GTK_RESPONSE_ACCEPT,
631 				   NULL);
632   directory = visu_ui_main_getLastOpenDirectory(visu_ui_main_class_getCurrentPanel());
633   if (directory)
634     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(wd), directory);
635   do
636     {
637       response = gtk_dialog_run(GTK_DIALOG(wd));
638       if (exportPathFile)
639 	g_free(exportPathFile);
640       exportPathFile = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(wd));
641       switch (response)
642 	{
643 	case GTK_RESPONSE_ACCEPT:
644 	  error = (GError*)0;
645 	  if (!visu_gl_node_scene_parsePathsFromXML(VISU_GL_NODE_SCENE(data),
646                                                     exportPathFile, &error))
647 	    {
648 	      visu_ui_raiseWarning(_("Load a set of paths."),
649 				   error->message, GTK_WINDOW(wd));
650 	      g_error_free(error);
651 	      response = GTK_RESPONSE_NONE;
652 	    }
653 	  break;
654 	default:
655 	  response = GTK_RESPONSE_ACCEPT;
656 	  break;
657 	}
658     }
659   while (response != GTK_RESPONSE_ACCEPT);
660   gtk_widget_destroy(wd);
661 }
662