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 
45 #include <math.h>
46 #include <string.h>
47 
48 #include <GL/gl.h>
49 #include <GL/glu.h>
50 
51 #include "view.h"
52 #include "objectList.h"
53 #include <visu_tools.h>
54 #include <renderingMethods/iface_nodeArrayRenderer.h>
55 #include <renderingBackend/visu_actionInterface.h>
56 #include <visu_configFile.h>
57 
58 
59 #include "interactive.h"
60 
61 #define FLAG_PARAMETER_OBSERVE_METHOD "opengl_observe_method"
62 #define DESC_PARAMETER_OBSERVE_METHOD "Choose the observe method ; integer (0: constrained mode, 1: walker mode)"
63 
64 #define FLAG_PARAMETER_CAMERA_SETTINGS "opengl_prefered_camera_orientation"
65 #define DESC_PARAMETER_CAMERA_SETTINGS "Saved prefered camera position ; three angles, two shifts, zoom and perspective level"
66 static float cameraData[7];
dissect_disp(tvbuff_t * tvb,packet_info * pinfo,proto_tree * parent_tree,void * data)67 
68 /**
69  * SECTION:interactive
70  * @short_description: Gives tools to interact with the rendered
71  * area.
72  *
73  * <para>When one wants some interactions on the rendering area
74  * (either from the mouse or from the keyboard), one should create a new
75  * interactive object using visu_interactive_new(). Then, during the
76  * interactive session, several modes have been implemented:
77  * <itemizedlist>
78  * <listitem><para>
79  * #interactive_mark, is a mode where the mouse can select
80  * a node and this nodes become highlighted.
81  * </para></listitem>
82  * <listitem><para>
83  * #interactive_move, the mouse is then used to move
84  * picked nodes. Moves are possible in the plane of the screen or
85  * constrained along axis of the bounding box.
86  * </para></listitem>
87  * <listitem><para>
88  * #interactive_observe, in this mode, the mouse is used
89  * to change the position of camera, its orientation and its zoom
90  * characteristics.
91  * </para></listitem>
92  * <listitem><para>
93  * #interactive_pick, this mode is the most complex
94  * picking mode. It can select atoms up to two references and measure
95  * distances and angles.
96  * </para></listitem>
97  * <listitem><para>
98  * #interactive_pickAndObserve, this mode is a simplified
99  * mix of obervation and picking.
100  * </para></listitem>
101  * </itemizedlist>
102  * The observe mode has two different moving algorithms that can be
103  * changed using visu_interactive_class_setPreferedObserveMethod(). The
104  * first, called 'constrained' (cf. #interactive_constrained)
105  * corresponds to movements along parallels and meridians. When the
106  * mouse is moved along x axis, the camera raotates along a
107  * parallel. When the camera is moved along y axis, the camera rotate
108  * along a meridian. The top is always pointing to the north pole (in
109  * fact, omega is always forced to 0 in this mode). This mode has a
110  * 'strange' behavior when the observer is near a pole: moving mouse
111  * along x axis make the box rotates on itself. It is normal, because
112  * movements on x axis is equivalent to movements on parallel and near
113  * the poles, parallel are small circle around the z axis. This can be
114  * unnatural in some occasion and the other mode, called 'walker' (see
115  * #interactive_walker) can be used instead of the 'constrained'
116  * mode. In the former, the moving is done has if the observer was a
117  * walking ant on a sphere : moving the mouse along y axis makes the
118  * ant go on or forward ; and x axis movements makes the ant goes on
119  * its left or on it right. This is a more natural way to move the box
120  * but it has the inconvient that it is hard to return in a given
121  * position (omega has never the right value).
122  * </para>
123  */
124 
125 /**
126  * VisuInteractiveClass:
127  * @parent: the parent.
128  * @preferedObserveMethod: the prefered method to move the camera.
129  * @savedCameras: the list of saved cameras.
130  * @lastCamera: a pointer on the last selected camera.
131  *
132  * An opaque structure representing the class of #VisuInteractive objects.
133  */
134 /**
135  * VisuInteractive:
136  *
137  * All fields are private.
138  */
139 
140 struct _VisuInteractive
141 {
142   VisuObject parent;
143 
144   /* Internal object gestion. */
145   gboolean dispose_has_run;
146 
147   /* The kind of interactive (pick, observe...) */
148   VisuInteractiveId id;
149 
150   /* Data needed in case of pick. */
151   /* The last selected node. */
152   gint idSelected;
153   /* The current first reference. */
154   gint idRef1;
155   /* The current second reference. */
156   gint idRef2;
157   /* */
158   GArray *idRegion;
159   /* The position of the pick. */
160   int xOrig, yOrig;
161   int xPrev, yPrev;
162 
163   /* Data for the move action. */
164   gboolean movingPicked;
165   GArray *movingNodes;
166   float movingAxe[3];
167 
168   /* The OpenGL list used to identify nodes. */
169   VisuGlExtNodes *nodeList;
170   gulong data_sig;
171 
172   /* Signals to listen to. */
173   VisuData *dataObj;
174   gulong popDec_signal;
175 
176   /* Copy of last event. */
177   ToolSimplifiedEvents ev;
178 
179   /* Some message explaining the purpose of the interactive session. */
180   gchar *message;
181 };
182 
183 enum
184   {
185     INTERACTIVE_OBSERVE_SIGNAL,
186     INTERACTIVE_PICK_ERROR_SIGNAL,
187     INTERACTIVE_PICK_NODE_SIGNAL,
188     INTERACTIVE_PICK_REGION_SIGNAL,
189     INTERACTIVE_PICK_MENU_SIGNAL,
190     INTERACTIVE_START_MOVE_SIGNAL,
191     INTERACTIVE_MOVE_SIGNAL,
192     INTERACTIVE_STOP_MOVE_SIGNAL,
193     INTERACTIVE_STOP_SIGNAL,
194 /*     INTERACTIVE_EVENT_SIGNAL, */
195     N_INTERACTIVE_SIGNALS
196   };
197 
198 static VisuInteractiveClass *local_class = NULL;
199 
200 /* Internal variables. */
201 static guint interactive_signals[N_INTERACTIVE_SIGNALS] = { 0 };
202 
203 /* Object gestion methods. */
204 static void visu_interactive_dispose (GObject* obj);
205 static void visu_interactive_finalize(GObject* obj);
206 
207 /* Local methods. */
208 static void exportParameters(GString *data, VisuData *dataObj);
209 static void onReadCamera(VisuConfigFile *obj, VisuConfigFileEntry *entry, VisuInteractiveClass *klass);
210 static void onPopulationChange(VisuInteractive *inter, GArray *nodes, VisuData *dataObj);
211 static void _setData(VisuInteractive *inter, VisuData *dataObj);
212 static gboolean observe(VisuInteractive *inter, VisuGlView *view,
213 			ToolSimplifiedEvents *ev);
214 static gboolean pick(VisuInteractive *inter, ToolSimplifiedEvents *ev);
215 static gboolean move(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev);
216 static gboolean mark(VisuInteractive *inter, ToolSimplifiedEvents *ev);
217 static gboolean pickAndObserve(VisuInteractive *inter,
218                                VisuGlView *view, ToolSimplifiedEvents *ev);
219 static gboolean drag(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev);
220 
221 G_DEFINE_TYPE(VisuInteractive, visu_interactive, VISU_TYPE_OBJECT)
222 
223 static void g_cclosure_marshal_NODE_SELECTION(GClosure *closure,
224                                               GValue *return_value _U_,
225                                               guint n_param_values,
226                                               const GValue *param_values,
227                                               gpointer invocation_hint _U_,
228                                               gpointer marshal_data)
proto_reg_handoff_disp(void)229 {
230   typedef void (*callbackFunc)(gpointer data1, guint kind, VisuNodeArray *arr,
231                                VisuNode *node1, VisuNode *node2, VisuNode *node3,
232                                gpointer data2);
233   register callbackFunc callback;
234   register GCClosure *cc = (GCClosure*)closure;
235   register gpointer data1, data2;
236 
237   g_return_if_fail(n_param_values == 6);
238 
239   if (G_CCLOSURE_SWAP_DATA(closure))
240     {
241       data1 = closure->data;
242       data2 = g_value_peek_pointer(param_values + 0);
243     }
244   else
245     {
246       data1 = g_value_peek_pointer(param_values + 0);
247       data2 = closure->data;
248     }
249   callback = (callbackFunc)(size_t)(marshal_data ? marshal_data : cc->callback);
250 
251   DBG_fprintf(stderr, "Interactive: marshall callback for node-selection"
252               " with nodes %p, %p, %p.\n",
253               (gpointer)g_value_get_boxed(param_values + 3),
254               (gpointer)g_value_get_boxed(param_values + 4),
255               (gpointer)g_value_get_boxed(param_values + 5));
256   callback(data1, g_value_get_uint(param_values + 1),
257            g_value_get_object(param_values + 2),
258            (VisuNode*)g_value_get_boxed(param_values + 3),
259            (VisuNode*)g_value_get_boxed(param_values + 4),
260            (VisuNode*)g_value_get_boxed(param_values + 5),
261            data2);
262 }
263 static void g_cclosure_marshal_NODE_MENU(GClosure *closure,
264                                          GValue *return_value _U_,
265                                          guint n_param_values,
266                                          const GValue *param_values,
267                                          gpointer invocation_hint _U_,
268                                          gpointer marshal_data)
269 {
270   typedef void (*callbackFunc)(gpointer data1, gint x, gint y,
271                                VisuNode *node, gpointer data2);
272   register callbackFunc callback;
273   register GCClosure *cc = (GCClosure*)closure;
274   register gpointer data1, data2;
275 
276   g_return_if_fail(n_param_values == 4);
277 
278   if (G_CCLOSURE_SWAP_DATA(closure))
279     {
280       data1 = closure->data;
281       data2 = g_value_peek_pointer(param_values + 0);
282     }
283   else
284     {
285       data1 = g_value_peek_pointer(param_values + 0);
286       data2 = closure->data;
287     }
288   callback = (callbackFunc)(size_t)(marshal_data ? marshal_data : cc->callback);
289 
290   DBG_fprintf(stderr, "Interactive: marshall callback for menu at %dx%d"
291               " with node %p.\n",
292               g_value_get_int(param_values + 1),
293               g_value_get_int(param_values + 2),
294               (gpointer)g_value_get_boxed(param_values + 3));
295   callback(data1, g_value_get_int(param_values + 1),
296            g_value_get_int(param_values + 2),
297            (VisuNode*)g_value_get_boxed(param_values + 3),
298            data2);
299 }
300 
301 static void visu_interactive_class_init(VisuInteractiveClass *klass)
302 {
303   GType paramBool[1] = {G_TYPE_BOOLEAN};
304   GType paramUInt[1] = {G_TYPE_UINT};
305   VisuConfigFileEntry *resourceEntry;
306   int rgMethod[2] = {interactive_constrained, interactive_walker};
307   float rgCamera[2] = {-G_MAXFLOAT, G_MAXFLOAT};
308 
309   local_class = klass;
310 
311   DBG_fprintf(stderr, "visu Interactive: creating the class of the object.\n");
312 
313   DBG_fprintf(stderr, "                - adding new signals ;\n");
314   /**
315    * VisuInteractive::observe:
316    * @obj: the object emitting the signal.
317    * @bool: a boolean.
318    *
319    * This signal is emitted each time an observe session is start
320    * (@bool is TRUE) or finished (@bool is FALSE).
321    *
322    * Since: 3.6
323    */
324   interactive_signals[INTERACTIVE_OBSERVE_SIGNAL] =
325     g_signal_newv("observe", G_TYPE_FROM_CLASS(klass),
326 		  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
327 		  NULL, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN,
328 		  G_TYPE_NONE, 1, paramBool);
329 
330   /**
331    * VisuInteractive::selection-error:
332    * @obj: the object emitting the signal.
333    * @err: an error value.
334    *
335    * This signal is emitted each time a selection fails, providing the
336    * error in @err (see #VisuInteractivePickError).
337    *
338    * Since: 3.6
339    */
340   interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL] =
341     g_signal_newv("selection-error", G_TYPE_FROM_CLASS(klass),
342 		  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
343 		  NULL, NULL, NULL, g_cclosure_marshal_VOID__UINT,
344 		  G_TYPE_NONE, 1, paramUInt);
345 
346   /**
347    * VisuInteractive::node-selection:
348    * @obj: the object emitting the signal.
349    * @kind: a flag, see #VisuInteractivePick.
350    * @array: (type VisuNodeArray*) (transfer none): the #VisuNodeArray
351    * hosting @node1, @node2 and @node3.
352    * @node1: (type VisuNode*) (transfer none): the primary node.
353    * @node2: (type VisuNode*) (transfer none): the secondary
354    * node, if any.
355    * @node3: (type VisuNode*) (transfer none): the tertiary
356    * node, if any.
357    *
358    * This signal is emitted each time a single node selection succeed, providing the
359    * kind in @kind (see #VisuInteractivePick). The corresponding nodes
360    * are stored in @node1, @node2 and @node3.
361    *
362    * Since: 3.6
363    */
364   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL] =
365     g_signal_new("node-selection", G_TYPE_FROM_CLASS(klass),
366                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
367                  0, NULL, NULL, g_cclosure_marshal_NODE_SELECTION,
368                  G_TYPE_NONE, 5, G_TYPE_UINT, VISU_TYPE_NODE_ARRAY, VISU_TYPE_NODE,
369                  VISU_TYPE_NODE, VISU_TYPE_NODE, NULL);
370 
371   /**
372    * VisuInteractive::region-selection:
373    * @obj: the object emitting the signal.
374    * @nodes: (element-type guint): an array of node ids.
375    *
376    * This signal is emitted each time a region selection succeed. The corresponding nodes
377    * are stored in @nodes.
378    *
379    * Since: 3.6
380    */
381   interactive_signals[INTERACTIVE_PICK_REGION_SIGNAL] =
382     g_signal_new("region-selection", G_TYPE_FROM_CLASS(klass),
383                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
384                  0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
385                  G_TYPE_NONE, 1, G_TYPE_ARRAY);
386 
387   /**
388    * VisuInteractive::menu:
389    * @obj: the object emitting the signal.
390    * @x: the x coordinate.
391    * @y: the y coordinate.
392    * @node: (type VisuNode*) (transfer none) (allow-none): a #VisuNode.
393    *
394    * This signal is emitted each time a menu key stroke is done.
395    *
396    * Since: 3.7
397    */
398   interactive_signals[INTERACTIVE_PICK_MENU_SIGNAL] =
399     g_signal_new("menu", G_TYPE_FROM_CLASS(klass),
400                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
401                  0, NULL, NULL, g_cclosure_marshal_NODE_MENU,
402                  G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, VISU_TYPE_NODE);
403 
404   /**
405    * VisuInteractive::start-move:
406    * @obj: the object emitting the signal.
407    * @nodes: (element-type guint): an array of node ids.
408    *
409    * This signal is emitted each time a set of nodes are clicked to be
410    * moved. The corresponding nodes are stored in @nodes.
411    *
412    * Since: 3.6
413    */
414   interactive_signals[INTERACTIVE_START_MOVE_SIGNAL] =
415     g_signal_new("start-move", G_TYPE_FROM_CLASS(klass),
416                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
417                  0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
418                  G_TYPE_NONE, 1, G_TYPE_ARRAY);
419 
420   /**
421    * VisuInteractive::move:
422    * @obj: the object emitting the signal.
423    * @delta: the delta of applied translation.
424    *
425    * This signal is emitted each time a set of nodes are moved. The
426    * corresponding movement translation is stored in delta.
427    *
428    * Since: 3.6
429    */
430   interactive_signals[INTERACTIVE_MOVE_SIGNAL] =
431     g_signal_new("move", G_TYPE_FROM_CLASS(klass),
432                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
433                  0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
434                  G_TYPE_NONE, 1, TOOL_TYPE_VECTOR);
435   /**
436    * VisuInteractive::stop-move:
437    * @obj: the object emitting the signal.
438    * @delta: the delta of applied translation.
439    *
440    * This signal is emitted when a set of nodes are finished moving. The
441    * corresponding movement complete translation is stored in delta.
442    *
443    * Since: 3.8
444    */
445   interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL] =
446     g_signal_new("stop-move", G_TYPE_FROM_CLASS(klass),
447                  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
448                  0, NULL, NULL, g_cclosure_marshal_VOID__BOXED,
449                  G_TYPE_NONE, 1, TOOL_TYPE_VECTOR);
450   /**
451    * VisuInteractive::stop:
452    * @obj: the object emitting the signal.
453    *
454    * This signal is emitted each time a set of nodes are stopped to be
455    * moved.
456    *
457    * Since: 3.6
458    */
459   interactive_signals[INTERACTIVE_STOP_SIGNAL] =
460     g_signal_newv("stop", G_TYPE_FROM_CLASS(klass),
461 		  G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
462 		  NULL, NULL, NULL, g_cclosure_marshal_VOID__VOID,
463 		  G_TYPE_NONE, 0, NULL);
464 
465   /* Connect freeing methods. */
466   G_OBJECT_CLASS(klass)->dispose  = visu_interactive_dispose;
467   G_OBJECT_CLASS(klass)->finalize = visu_interactive_finalize;
468 
469   DBG_fprintf(stderr, "                - add the resources.\n");
470   visu_config_file_addIntegerArrayEntry(VISU_CONFIG_FILE_PARAMETER,
471                                         FLAG_PARAMETER_OBSERVE_METHOD,
472                                         DESC_PARAMETER_OBSERVE_METHOD,
473                                         1, (int*)&klass->preferedObserveMethod, rgMethod, FALSE);
474   resourceEntry = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_PARAMETER,
475                                                       FLAG_PARAMETER_CAMERA_SETTINGS,
476                                                       DESC_PARAMETER_CAMERA_SETTINGS,
477                                                       7, cameraData, rgCamera, FALSE);
478   visu_config_file_entry_setVersion(resourceEntry, 3.7f);
479   visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
480                                      exportParameters);
481 
482   g_signal_connect(VISU_CONFIG_FILE_PARAMETER, "parsed::" FLAG_PARAMETER_CAMERA_SETTINGS,
483                    G_CALLBACK(onReadCamera), klass);
484 
485   /* Set local variables to default. */
486   DBG_fprintf(stderr, "                - setup the local variables.\n");
487   klass->preferedObserveMethod = interactive_constrained;
488 }
489 
490 static void visu_interactive_init(VisuInteractive *obj)
491 {
492   DBG_fprintf(stderr, "Visu Interactive: creating a new interactive session (%p).\n",
493 	      (gpointer)obj);
494 
495   obj->dispose_has_run = FALSE;
496   obj->idRef1          = -99;
497   obj->idRef2          = -99;
498   obj->idSelected      = -99;
499   obj->idRegion        = (GArray*)0;
500   obj->movingAxe[0]    = 1.f;
501   obj->movingAxe[1]    = 0.f;
502   obj->movingAxe[2]    = 0.f;
503   obj->movingPicked    = FALSE;
504   obj->movingNodes     = (GArray*)0;
505 
506   obj->nodeList        = (VisuGlExtNodes*)0;
507 
508   obj->dataObj         = (VisuData*)0;
509   obj->popDec_signal   = (gulong)0;
510 
511   obj->message = (gchar*)0;
512 }
513 
514 static void onPopulationChange(VisuInteractive *inter, GArray *nodes, VisuData *dataObj _U_)
515 {
516   guint i;
517 
518   DBG_fprintf(stderr, "Visu Interactive:  remove the specific nodes.\n");
519   for (i = 0; i < nodes->len; i++)
520     {
521       if (inter->idRef1 == g_array_index(nodes, gint, i))
522 	inter->idRef1 = -1;
523       if (inter->idRef2 == g_array_index(nodes, gint, i))
524 	inter->idRef2 = -1;
525       if (inter->idSelected == g_array_index(nodes, gint, i))
526 	inter->idSelected = -1;
527     }
528   if (inter->idRegion)
529     g_array_unref(inter->idRegion);
530   inter->idRegion = (GArray*)0;
531 }
532 
533 /* This method can be called several times.
534    It should unref all of its reference to
535    GObjects. */
536 static void visu_interactive_dispose(GObject* obj)
537 {
538   DBG_fprintf(stderr, "Visu Interactive: dispose object %p.\n", (gpointer)obj);
539 
540   if (VISU_INTERACTIVE(obj)->dispose_has_run)
541     return;
542 
543   VISU_INTERACTIVE(obj)->dispose_has_run = TRUE;
544 
545   visu_interactive_setNodeList(VISU_INTERACTIVE(obj), (VisuGlExtNodes*)0);
546 
547   /* Chain up to the parent class */
548   G_OBJECT_CLASS(visu_interactive_parent_class)->dispose(obj);
549 }
550 /* This method is called once only. */
551 static void visu_interactive_finalize(GObject* obj)
552 {
553   g_return_if_fail(obj);
554 
555   DBG_fprintf(stderr, "Visu Interactive: finalize object %p.\n", (gpointer)obj);
556 
557   if (VISU_INTERACTIVE(obj)->movingNodes)
558     g_array_unref(VISU_INTERACTIVE(obj)->movingNodes);
559   if (VISU_INTERACTIVE(obj)->idRegion)
560     g_array_unref(VISU_INTERACTIVE(obj)->idRegion);
561   g_free(VISU_INTERACTIVE(obj)->message);
562 
563   /* Chain up to the parent class */
564   G_OBJECT_CLASS(visu_interactive_parent_class)->finalize(obj);
565 }
566 
567 /**
568  * visu_interactive_new:
569  * @type: a #VisuInteractiveId flag.
570  *
571  * Creates a new interactive session of the given @type.
572  *
573  * Returns: (transfer full): a newly created object.
574  */
575 VisuInteractive* visu_interactive_new(VisuInteractiveId type)
576 {
577   VisuInteractive *inter;
578 
579   inter = VISU_INTERACTIVE(g_object_new(VISU_TYPE_INTERACTIVE, NULL));
580   g_return_val_if_fail(inter, (VisuInteractive*)0);
581 
582   DBG_fprintf(stderr, "Visu Interactive: start new interactive session %p (%d).\n",
583 	      (gpointer)inter, type);
584 
585   inter->id = type;
586 
587   return inter;
588 }
589 /**
590  * visu_interactive_getType:
591  * @inter: a #VisuInteractive object.
592  *
593  * It returns the kind of interactive session.
594  *
595  * Returns: a #VisuInteractiveId value.
596  */
597 VisuInteractiveId visu_interactive_getType(VisuInteractive *inter)
598 {
599   g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), interactive_none);
600 
601   return inter->id;
602 }
603 /**
604  * visu_interactive_setType:
605  * @inter: a #VisuInteractive object.
606  * @id: a #VisuInteractiveId.
607  *
608  * It changes the kind of interactive session.
609  *
610  * Returns: TRUE if indeed changed.
611  */
612 gboolean visu_interactive_setType(VisuInteractive *inter, VisuInteractiveId id)
613 {
614   g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), FALSE);
615 
616   if (inter->id == id)
617     return FALSE;
618 
619   inter->id = id;
620   return TRUE;
621 }
622 
623 static void onDataNotified(VisuInteractive *inter, GParamSpec *pspec _U_, VisuGlExtNodes *nodes)
624 {
625   _setData(inter, VISU_DATA(visu_node_array_renderer_getNodeArray(VISU_NODE_ARRAY_RENDERER(nodes))));
626 }
627 
628 /**
629  * visu_interactive_setNodeList:
630  * @inter: a #VisuInteractive object.
631  * @nodes: (transfer full) (allow-none): a #VisuGlExtNodes object.
632  *
633  * Associate a #VisuGlExtNodes object for node selection. This is
634  * mandatory for move, pick and mark actions.
635  *
636  * Since: 3.7
637  **/
638 void visu_interactive_setNodeList(VisuInteractive *inter, VisuGlExtNodes *nodes)
639 {
640   g_return_if_fail(VISU_IS_INTERACTIVE(inter));
641 
642   if (inter->nodeList == nodes)
643     return;
644 
645   if (inter->nodeList)
646     {
647       g_signal_handler_disconnect(inter->nodeList, inter->data_sig);
648       g_object_unref(inter->nodeList);
649     }
650   if (nodes)
651     {
652       g_object_ref(nodes);
653       inter->data_sig = g_signal_connect_swapped(nodes, "notify::data",
654                                                  G_CALLBACK(onDataNotified), inter);
655     }
656   inter->nodeList = nodes;
657   _setData(inter, (nodes) ? VISU_DATA(visu_node_array_renderer_getNodeArray(VISU_NODE_ARRAY_RENDERER(nodes))) : (VisuData*)0);
658 }
659 
660 static void _setData(VisuInteractive *inter, VisuData *dataObj)
661 {
662   if (inter->idRef1 >= 0 &&
663       (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idRef1)))
664     inter->idRef1 = -99;
665   if (inter->idRef2 >= 0 &&
666       (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idRef2)))
667     inter->idRef2 = -99;
668   if (inter->idSelected >= 0 &&
669       (!dataObj || !visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), inter->idSelected)))
670     inter->idSelected = -99;
671   DBG_fprintf(stderr, "Visu Interactive: specific nodes are now:\n");
672   DBG_fprintf(stderr, " - selected    %d.\n", inter->idSelected);
673   DBG_fprintf(stderr, " - ref. 1      %d.\n", inter->idRef1);
674   DBG_fprintf(stderr, " - ref. 2      %d.\n", inter->idRef2);
675 
676   if (inter->idRegion)
677     g_array_unref(inter->idRegion);
678   inter->idRegion = (GArray*)0;
679 
680   if (inter->dataObj == dataObj)
681     return;
682 
683   if (inter->dataObj)
684     {
685       g_signal_handler_disconnect(inter->dataObj, inter->popDec_signal);
686       g_object_unref(inter->dataObj);
687     }
688   if (dataObj)
689     {
690       g_object_ref(dataObj);
691       inter->popDec_signal =
692         g_signal_connect_swapped(dataObj, "PopulationDecrease",
693                                  G_CALLBACK(onPopulationChange), (gpointer)inter);
694     }
695   inter->dataObj = dataObj;
696 }
697 
698 /**
699  * visu_interactive_popSavedCamera:
700  * @inter: a #VisuInteractive object.
701  *
702  * @inter object stores camera settings as a ring. This routine goes
703  * to the next camera in the ring and returns the current one. The
704  * popped camera is not actually removed from the ring.
705  *
706  * Since: 3.6
707  *
708  * Returns: a pointer to the previously current #VisuGlCamera. It
709  * is owned by V_Sim and should not be touched.
710  */
711 VisuGlCamera* visu_interactive_popSavedCamera(VisuInteractive *inter)
712 {
713   VisuGlCamera *cur;
714   VisuInteractiveClass *klass;
715 
716   klass = VISU_INTERACTIVE_GET_CLASS(inter);
717   g_return_val_if_fail(klass, (VisuGlCamera*)0);
718 
719   if (!klass->lastCamera)
720     return (VisuGlCamera*)0;
721 
722   cur = (VisuGlCamera*)klass->lastCamera->data;
723 
724   klass->lastCamera = g_list_next(klass->lastCamera);
725   if (!klass->lastCamera)
726     klass->lastCamera = klass->savedCameras;
727 
728   DBG_fprintf(stderr, "Interactive: pop, pointing now at camera %d.\n",
729 	      g_list_position(klass->savedCameras, klass->lastCamera));
730 
731   return cur;
732 }
733 /**
734  * visu_interactive_getSavedCameras:
735  * @inter: a #VisuInteractive object.
736  * @cameras: (out) (element-type VisuGlCamera*): a location to store a list of cameras.
737  * @head: (out) (element-type VisuGlCamera*): a location to store a list of cameras.
738  *
739  * @inter object stores camera settings as a ring. One can access the
740  * set of saved cameras thanks to @cameras or to the current position
741  * in the ring thanks to @head. @cameras or @head are not copied and
742  * are owned by V_Sim. They should be considered read-only.
743  *
744  * Since: 3.6
745  */
746 void visu_interactive_getSavedCameras(VisuInteractive *inter,
747                                       GList **cameras, GList **head)
748 {
749   VisuInteractiveClass *klass;
750 
751   klass = VISU_INTERACTIVE_GET_CLASS(inter);
752   g_return_if_fail(klass);
753 
754   *cameras = klass->savedCameras;
755   *head    = klass->lastCamera;
756 }
757 static gboolean cmpCameras(VisuGlCamera *c1, VisuGlCamera *c2)
758 {
759   return (c1 == c2) ||
760     (c1->theta == c2->theta &&
761      c1->phi == c2->phi &&
762      c1->omega == c2->omega &&
763      c1->xs == c2->xs &&
764      c1->ys == c2->ys/*  && */
765      /* c1->gross == c2->gross && */
766      /* c1->d_red == c2->d_red */);
767 }
768 static void _pushSavedCamera(VisuInteractiveClass *klass, VisuGlCamera *camera)
769 {
770   VisuGlCamera *tmp;
771 
772   g_return_if_fail(klass && camera);
773 
774   for (klass->lastCamera = klass->savedCameras ;
775        klass->lastCamera &&
776 	 !cmpCameras((VisuGlCamera*)klass->lastCamera->data, camera);
777        klass->lastCamera = g_list_next(klass->lastCamera));
778 
779   /* Case we don't find it, we add. */
780   if (!klass->lastCamera || (VisuGlCamera*)klass->lastCamera->data != camera)
781     {
782       tmp = g_malloc(sizeof(VisuGlCamera));
783       *tmp = *camera;
784       klass->savedCameras = g_list_prepend(klass->savedCameras, (gpointer)tmp);
785     }
786   klass->lastCamera = klass->savedCameras;
787   DBG_fprintf(stderr, "Interactive: push, storing now %d cameras.\n",
788 	      g_list_length(klass->savedCameras));
789 }
790 /**
791  * visu_interactive_pushSavedCamera:
792  * @inter: a #VisuInteractive object.
793  * @camera: a #VisuGlCamera object.
794  *
795  * @inter object stores camera settings as a ring. The given @camera
796  * is copied in the ring if its values not already exist. The current camera
797  * is set to this new one.
798  *
799  * Since: 3.6
800  */
801 void visu_interactive_pushSavedCamera(VisuInteractive *inter, VisuGlCamera *camera)
802 {
803   VisuInteractiveClass *klass;
804 
805   klass = VISU_INTERACTIVE_GET_CLASS(inter);
806   _pushSavedCamera(klass, camera);
807 }
808 gboolean visuInteractiveRemove_savedCamera(VisuInteractive *inter, VisuGlCamera *camera)
809 {
810   VisuInteractiveClass *klass;
811   GList *lst;
812 
813   klass = VISU_INTERACTIVE_GET_CLASS(inter);
814   g_return_val_if_fail(klass, FALSE);
815 
816   for (lst = klass->savedCameras ;
817        lst && !cmpCameras((VisuGlCamera*)lst->data, camera);
818        lst = g_list_next(lst));
819 
820   /* Case we don't find it, we return. */
821   if (!lst)
822     return FALSE;
823 
824   g_free(lst->data);
825   klass->savedCameras = g_list_delete_link(klass->savedCameras, lst);
826   if (klass->lastCamera == lst)
827     klass->lastCamera = lst->next;
828   if (!klass->lastCamera)
829     klass->lastCamera = klass->savedCameras;
830   DBG_fprintf(stderr, "Interactive: remove, storing now %d cameras.\n",
831 	      g_list_length(klass->savedCameras));
832 
833   return TRUE;
834 }
835 
836 
837 /**
838  * visu_interactive_handleEvent:
839  * @inter: a #VisuInteractive object ;
840  * @view: a #VisuGlView object the interaction happened on.
841  * @ev: a simplified event.
842  *
843  * This routine should be called by the rendering window when some
844  * event is raised on the rendering surface.
845  */
846 void visu_interactive_handleEvent(VisuInteractive *inter,
847                                   VisuGlView *view, ToolSimplifiedEvents *ev)
848 {
849   gboolean stop;
850 
851   g_return_if_fail(VISU_IS_INTERACTIVE(inter));
852 
853   DBG_fprintf(stderr, "Visu Interactive: handle event at %dx%d (%d %d).\n", ev->x, ev->y,
854               ev->button, ev->buttonType);
855   inter->ev = *ev;
856   switch (inter->id)
857     {
858     case interactive_observe:
859       stop = observe(inter, view, ev);
860       break;
861     case interactive_measureAndObserve:
862       stop = pickAndObserve(inter, view, ev);
863       break;
864     case interactive_measure:
865     case interactive_pick:
866       stop = pick(inter, ev);
867       break;
868     case interactive_move:
869       stop = move(inter, view, ev);
870       break;
871     case interactive_mark:
872       stop = mark(inter, ev);
873       break;
874     case interactive_drag:
875       stop = drag(inter, view, ev);
876       break;
877     default:
878       stop = FALSE;
879       break;
880     }
881 
882   if (stop)
883     g_signal_emit(G_OBJECT(inter),
884 		  interactive_signals[INTERACTIVE_STOP_SIGNAL], 0, NULL);
885 }
886 /**
887  * visu_interactive_setReferences:
888  * @inter: a #VisuInteractive object.
889  * @from: another #VisuInteractive object.
890  *
891  * Copies all node ids used as reference from @from to @inter.
892  */
893 void visu_interactive_setReferences(VisuInteractive *inter, VisuInteractive *from)
894 {
895   g_return_if_fail(VISU_IS_INTERACTIVE(inter) && VISU_IS_INTERACTIVE(from));
896 
897   inter->idSelected = from->idSelected;
898   inter->idRef1     = from->idRef1;
899   inter->idRef2     = from->idRef2;
900 }
901 /**
902  * visu_interactive_getEvent:
903  * @inter: a #VisuInteractive object.
904  *
905  * This routine can be called in callbacks of @inter to get some
906  * details about the event that raise signals like
907  * VisuInteractive::node-selection.
908  *
909  * Since: 3.7
910  *
911  * Returns: (transfer none): a location with details on the last event.
912  **/
913 ToolSimplifiedEvents* visu_interactive_getEvent(VisuInteractive *inter)
914 {
915   g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), (ToolSimplifiedEvents*)0);
916 
917   return &inter->ev;
918 }
919 
920 /******************************************************************************/
921 
922 static gboolean pickAndObserve(VisuInteractive *inter,
923                                VisuGlView *view, ToolSimplifiedEvents *ev)
924 {
925   /* If button 1 or 2, use observe mode */
926   if (ev->button != 3 && ev->specialKey != Key_Menu)
927     return observe(inter, view, ev);
928   /* If button 3, use pick mode with button 3 as button 1 */
929   else
930     {
931       if (ev->shiftMod && !ev->controlMod)
932 	{
933 	  ev->button = 2;
934 	}
935       else if (!ev->shiftMod && ev->controlMod)
936 	{
937 	  ev->button = 2;
938 	}
939       else if (ev->shiftMod && ev->controlMod)
940 	{
941 	  ev->shiftMod = FALSE;
942 	  ev->controlMod = TRUE;
943 	  ev->button = 1;
944 	}
945       else
946 	ev->button = 1;
947       ev->motion = FALSE;
948 
949       return pick(inter, ev);
950     }
951 }
952 
953 static gboolean observe(VisuInteractive *inter, VisuGlView *view,
954 			ToolSimplifiedEvents *ev)
955 {
956   int sign_theta;
957   int dx, dy;
958   VisuGlCamera *camera;
959   float angles[3], ratio;
960   gdouble gross, persp;
961   gboolean zoom;
962 
963   g_return_val_if_fail(ev && inter, TRUE);
964   if (ev->button == 3)
965     return (ev->buttonType == TOOL_BUTTON_TYPE_PRESS);
966   /* If the realese event is triggered, we exit only if it's
967      not a scroll event (button 4 and 5). */
968   if (ev->button > 0 && ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
969     if (ev->button != 4 && ev->button != 5 )
970       {
971 	if (ev->button == 1)
972           g_signal_emit(G_OBJECT(inter),
973                         interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
974                         0 , FALSE, NULL);
975 	return FALSE;
976       }
977 
978   g_return_val_if_fail(view, FALSE);
979 
980 /*   fprintf(stderr, "%d %d, %d\n", ev->x, ev->y, ev->button); */
981 /*   fprintf(stderr, "%d %d, %d\n", ev->shiftMod, ev->controlMod, ev->motion); */
982   /* Support de la roulette en zoom et perspective. */
983   if (ev->specialKey == Key_Page_Up || ev->specialKey == Key_Page_Down ||
984       ev->button == 4 || ev->button == 5)
985     {
986       g_object_get(view, "zoom", &gross, "perspective", &persp, NULL);
987       if ((ev->button == 4 || ev->specialKey == Key_Page_Up) && !ev->shiftMod)
988         g_object_set(view, "zoom", MIN(gross * 1.1, 999.), NULL);
989       else if ((ev->button == 4 || ev->specialKey == Key_Page_Up) && ev->shiftMod)
990         g_object_set(view, "perspective", MAX(persp / 1.1, 1.1), NULL);
991       else if ((ev->button == 5 || ev->specialKey == Key_Page_Down) && !ev->shiftMod)
992         g_object_set(view, "zoom", MAX(gross / 1.1, 0.2), NULL);
993       else if ((ev->button == 5 || ev->specialKey == Key_Page_Down) && ev->shiftMod)
994         g_object_set(view, "perspective", MIN(persp * 1.1, 100.), NULL);
995     }
996   else if (ev->button && !ev->motion)
997     {
998       inter->xOrig = ev->x;
999       inter->yOrig = ev->y;
1000       if (ev->button == 1)
1001 	g_signal_emit(G_OBJECT(inter),
1002 		      interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1003 		      0 , TRUE, NULL);
1004     }
1005   else if (ev->motion ||
1006 	   ev->specialKey == Key_Arrow_Down  || ev->specialKey == Key_Arrow_Up ||
1007 	   ev->specialKey == Key_Arrow_Right || ev->specialKey == Key_Arrow_Left)
1008     {
1009       if (ev->motion)
1010 	{
1011 	  dx =   ev->x - inter->xOrig;
1012 	  dy = -(ev->y - inter->yOrig);
1013 	}
1014       else
1015 	{
1016           g_signal_emit(G_OBJECT(inter),
1017                         interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1018                         0 , TRUE, NULL);
1019 	  dx = 0;
1020 	  dy = 0;
1021 	  if (ev->specialKey == Key_Arrow_Left)
1022 	    dx = -10;
1023 	  else if (ev->specialKey == Key_Arrow_Right)
1024 	    dx = +10;
1025 	  else if (ev->specialKey == Key_Arrow_Down)
1026 	    dy = -10;
1027 	  else if (ev->specialKey == Key_Arrow_Up)
1028 	    dy = +10;
1029 	}
1030       zoom = (ev->button == 2);
1031       if(!zoom && !ev->shiftMod && !ev->controlMod)
1032 	{
1033 	  if (local_class->preferedObserveMethod == interactive_constrained)
1034 	    {
1035 	      if(view->camera.theta > 0.0)
1036 		sign_theta = 1;
1037 	      else
1038 		sign_theta = -1;
1039 	      visu_gl_view_rotateBox(view, dy * 180.0f / view->window.height,
1040 				   -dx * 180.0f / view->window.width * sign_theta, angles);
1041               if (ev->motion)
1042                 visu_gl_view_setThetaPhiOmega(view, angles[0], angles[1], 0.,
1043                                               VISU_GL_CAMERA_THETA |
1044                                               VISU_GL_CAMERA_PHI);
1045               else
1046                 g_object_set(view, "theta", angles[0], "phi", angles[1], NULL);
1047 	    }
1048 	  else if (local_class->preferedObserveMethod == interactive_walker)
1049 	    {
1050 	      visu_gl_view_rotateCamera(view, dy * 180.0f / view->window.height,
1051 				      -dx * 180.0f / view->window.width, angles);
1052               if (ev->motion)
1053                 visu_gl_view_setThetaPhiOmega(view, angles[0], angles[1], angles[2],
1054                                               VISU_GL_CAMERA_THETA |
1055                                               VISU_GL_CAMERA_PHI |
1056                                               VISU_GL_CAMERA_OMEGA);
1057               else
1058                 g_object_set(view, "theta", angles[0], "phi", angles[1],
1059                              "omega", angles[2], NULL);
1060 	    }
1061 	}
1062       else if(!zoom && ev->shiftMod && !ev->controlMod)
1063 	{
1064 	  ratio = 1. / MIN(view->window.width, view->window.height) /
1065 	    view->camera.gross * (view->camera.d_red - 1.f) / view->camera.d_red;
1066           visu_gl_view_setXsYs(view,
1067                                view->camera.xs + (float)dx * ratio,
1068                                view->camera.ys + (float)dy * ratio,
1069                                VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1070 	}
1071       else if(!zoom && ev->controlMod && !ev->shiftMod)
1072 	{
1073 	  if (ABS(dx) > ABS(dy))
1074 	    visu_gl_view_setThetaPhiOmega(view, 0., 0.,
1075                                           view->camera.omega + dx *
1076                                           180.0f / view->window.width,
1077                                           VISU_GL_CAMERA_OMEGA);
1078 	  else
1079 	    visu_gl_view_setThetaPhiOmega(view, 0., 0.,
1080                                           view->camera.omega + dy *
1081                                           180.0f / view->window.height,
1082                                           VISU_GL_CAMERA_OMEGA);
1083 	}
1084       else if(zoom && !ev->shiftMod)
1085 	visu_gl_view_setGross(view, view->camera.gross *
1086                               (1. + (float)dy *
1087                                3.0f / view->window.height));
1088       else if(zoom && ev->shiftMod)
1089 	visu_gl_view_setPersp(view, view->camera.d_red *
1090                               (1. - (float)dy *
1091                                5.0f / view->window.height));
1092       if (ev->motion)
1093 	{
1094 	  inter->xOrig = ev->x;
1095 	  inter->yOrig = ev->y;
1096 	}
1097       else
1098         g_signal_emit(G_OBJECT(inter),
1099                       interactive_signals[INTERACTIVE_OBSERVE_SIGNAL],
1100                       0 , FALSE, NULL);
1101     }
1102   else if (ev->letter == 'r')
1103     {
1104       camera = visu_interactive_popSavedCamera(inter);
1105       if (camera)
1106 	{
1107           g_object_set(view, "theta", camera->theta, "phi", camera->phi,
1108                        "omega", camera->omega, "zoom", camera->gross,
1109                        "perspective", camera->d_red, NULL);
1110           visu_gl_view_setXsYs(view, camera->xs, camera->ys,
1111                                VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1112 	}
1113     }
1114   else if (ev->letter == 's' && !ev->shiftMod && !ev->controlMod)
1115     visu_interactive_pushSavedCamera(inter, &view->camera);
1116   else if (ev->letter == 's' && ev->shiftMod && !ev->controlMod)
1117     visuInteractiveRemove_savedCamera(inter, &view->camera);
1118   else if (ev->letter == 'x')
1119     visu_gl_view_alignToAxis(view, TOOL_XYZ_X);
1120   else if (ev->letter == 'y')
1121     visu_gl_view_alignToAxis(view, TOOL_XYZ_Y);
1122   else if (ev->letter == 'z')
1123     visu_gl_view_alignToAxis(view, TOOL_XYZ_Z);
1124   return FALSE;
1125 }
1126 static void glDrawSelection(VisuInteractive *inter, int x, int y)
1127 {
1128   int viewport[4];
1129 
1130   glPushAttrib(GL_ENABLE_BIT);
1131   glDisable(GL_FOG);
1132   glEnable(GL_BLEND);
1133   glDisable(GL_DEPTH_TEST);
1134   glDisable(GL_LIGHTING);
1135   glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
1136   glGetIntegerv(GL_VIEWPORT, viewport);
1137   glMatrixMode(GL_PROJECTION);
1138   glPushMatrix();
1139   glLoadIdentity();
1140   gluOrtho2D(0.0, (float)viewport[2], 0., (float)viewport[3]);
1141   glMatrixMode(GL_MODELVIEW);
1142   glPushMatrix();
1143   glLoadIdentity();
1144   glColor4f(1.f, 1.f, 1.f, 1.f);
1145   glLineWidth(2);
1146   glDrawBuffer(GL_FRONT);
1147   /* We erase the previous selecting rectangle. */
1148   glBegin(GL_LINE_LOOP);
1149   glVertex3f(inter->xOrig, (float)viewport[3] - inter->yOrig, 0.f);
1150   glVertex3f(inter->xPrev, (float)viewport[3] - inter->yOrig, 0.f);
1151   glVertex3f(inter->xPrev, (float)viewport[3] - inter->yPrev, 0.f);
1152   glVertex3f(inter->xOrig, (float)viewport[3] - inter->yPrev, 0.f);
1153   glEnd();
1154   glFlush();
1155 
1156   /* We draw the new selecting rectangle. */
1157   if (x > 0 && y > 0)
1158     {
1159       glBegin(GL_LINE_LOOP);
1160       glVertex3f(inter->xOrig, (float)viewport[3] - inter->yOrig, 0.f);
1161       glVertex3f(x, (float)viewport[3] - inter->yOrig, 0.f);
1162       glVertex3f(x, (float)viewport[3] - y, 0.f);
1163       glVertex3f(inter->xOrig, (float)viewport[3] - y, 0.f);
1164       glEnd();
1165       glFlush();
1166     }
1167   glDrawBuffer(GL_BACK);
1168   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1169   glPopMatrix();
1170   glMatrixMode(GL_PROJECTION);
1171   glPopMatrix();
1172   glMatrixMode(GL_MODELVIEW);
1173   glPopAttrib();
1174 }
1175 
1176 static gboolean pick(VisuInteractive *inter, ToolSimplifiedEvents *ev)
1177 {
1178   int nodeId;
1179   gboolean pickOnly;
1180   VisuInteractivePick pick;
1181   VisuInteractivePickError error;
1182   VisuNodeArray *nodeArray;
1183   VisuNode *nodes[3];
1184 
1185   g_return_val_if_fail(ev && inter, TRUE);
1186   /* We store the pickInfo into the VisuData on press,
1187      but we apply it only at release if there was no drag action. */
1188   if (ev->button == 3)
1189     {
1190       if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1191 	return TRUE;
1192       else
1193 	return FALSE;
1194     }
1195 
1196   pickOnly = (inter->id == interactive_pick);
1197 
1198   if (ev->button == 1 && ev->motion && !pickOnly)
1199     {
1200       /* We drag a selecting area. */
1201       /* A nodeInfo should have been stored, we retrieve it. */
1202       DBG_fprintf(stderr, "Interactive: pick, drag to %dx%d.\n", ev->x, ev->y);
1203       glDrawSelection(inter, ev->x, ev->y);
1204       inter->xPrev = ev->x;
1205       inter->yPrev = ev->y;
1206     }
1207   else if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1208     {
1209       /* We create and store a nodeInfo data. */
1210       DBG_fprintf(stderr, "Interactive: pick, press at %dx%d.\n", ev->x, ev->y);
1211       inter->xOrig = ev->x;
1212       inter->yOrig = ev->y;
1213       inter->xPrev = ev->x;
1214       inter->yPrev = ev->y;
1215     }
1216   else if (ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1217     {
1218       g_return_val_if_fail(inter->nodeList, TRUE);
1219 
1220       /* Reset current selection. */
1221       if (inter->idRegion)
1222 	g_array_unref(inter->idRegion);
1223       inter->idRegion = (GArray*)0;
1224       inter->idSelected = -1;
1225 
1226       DBG_fprintf(stderr, "Interactive: pick, release from %dx%d.\n", inter->xOrig, inter->yOrig);
1227 
1228       /* Set new selection from click. */
1229       pick = PICK_NONE;
1230       /* If no drag action, we select the node and compute the pick mesure. */
1231       if ((inter->xOrig == inter->xPrev &&
1232 	   inter->yOrig == inter->yPrev) || pickOnly)
1233 	{
1234 	  nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1235 
1236 	  error = PICK_ERROR_NO_SELECTION;
1237 
1238 	  DBG_fprintf(stderr, "Visu Interactive: set selection (single %d %d).\n",
1239 		      ev->shiftMod, ev->controlMod);
1240 	  if (pickOnly && (ev->button == 1 || ev->button == 2))
1241 	    {
1242 	      DBG_fprintf(stderr, "Visu Interactive: set pick node %d.\n", nodeId);
1243 	      inter->idSelected = nodeId;
1244 	      pick = (nodeId >= 0)?PICK_SELECTED:PICK_NONE;
1245 	    }
1246 	  else if (!pickOnly && ev->button == 1 && !ev->controlMod)
1247 	    {
1248 	      if (nodeId >= 0)
1249 		{
1250 		  pick = PICK_SELECTED;
1251 		  if (nodeId == inter->idRef1 || nodeId == inter->idRef2)
1252 		    {
1253 		      error = PICK_ERROR_SAME_REF;
1254 		      pick = PICK_NONE;
1255 		    }
1256 
1257 		  if (pick != PICK_NONE)
1258 		    {
1259 		      inter->idSelected = nodeId;
1260 		      if (inter->idRef1 >= 0)
1261 			pick = (inter->idRef2 < 0)?PICK_DISTANCE:PICK_ANGLE;
1262 		    }
1263 		}
1264 	      else
1265 		pick = PICK_NONE;
1266 	    }
1267 	  else if (!pickOnly && ev->button == 1 && ev->controlMod)
1268 	    pick = (nodeId < 0)?PICK_NONE:PICK_HIGHLIGHT;
1269 	  else if (!pickOnly && ev->button == 2 && ev->shiftMod && !ev->controlMod)
1270 	    {
1271 	      DBG_fprintf(stderr, "Visu Interactive: set ref1 info for node %d.\n",
1272 			  nodeId);
1273 	      pick = PICK_REFERENCE_1;
1274 	      /* The error case. */
1275 	      if (nodeId >= 0 && inter->idRef2 == nodeId)
1276 		{
1277 		  error = PICK_ERROR_SAME_REF;
1278 		  pick = PICK_NONE;
1279 		}
1280 	      else if (nodeId < 0 && inter->idRef2 >= 0)
1281 		{
1282 		  error = PICK_ERROR_REF2;
1283 		  pick = PICK_NONE;
1284 		}
1285 
1286 	      if (pick != PICK_NONE)
1287 		{
1288 		  inter->idRef1 = nodeId;
1289 		  pick = (nodeId < 0)?PICK_UNREFERENCE_1:PICK_REFERENCE_1;
1290 		}
1291 	    }
1292 	  else if (!pickOnly && ev->button == 2 && !ev->shiftMod && ev->controlMod)
1293 	    {
1294 	      DBG_fprintf(stderr, "Visu Interactive: set ref2 info for node %d.\n",
1295 			  nodeId);
1296 	      pick = PICK_REFERENCE_2;
1297 	      /* The error case. */
1298 	      if (nodeId >= 0 && inter->idRef1 < 0)
1299 		{
1300 		  error = PICK_ERROR_REF1;
1301 		  pick = PICK_NONE;
1302 		}
1303 	      else if (nodeId >= 0 && inter->idRef1 == nodeId)
1304 		{
1305 		  error = PICK_ERROR_SAME_REF;
1306 		  pick = PICK_NONE;
1307 		}
1308 
1309 	      if (pick != PICK_NONE)
1310 		{
1311 		  inter->idRef2 = nodeId;
1312 		  pick = (nodeId < 0)?PICK_UNREFERENCE_2:PICK_REFERENCE_2;
1313 		}
1314 	    }
1315 	  else if (!pickOnly && ev->button == 2 && !ev->shiftMod && !ev->controlMod)
1316 	    pick = (nodeId < 0)?PICK_NONE:PICK_INFORMATION;
1317 	  else
1318 	    return FALSE;
1319 
1320 	  DBG_fprintf(stderr, " | OK.\n");
1321 	  if (pick != PICK_NONE)
1322 	    {
1323               nodeArray = VISU_NODE_ARRAY(inter->dataObj);
1324 	      nodes[0] = visu_node_array_getFromId(nodeArray, nodeId);
1325 	      nodes[1] = visu_node_array_getFromId
1326 		(nodeArray, (pick != PICK_REFERENCE_1)?inter->idRef1:nodeId);
1327 	      nodes[2] = visu_node_array_getFromId
1328 		(nodeArray, (pick != PICK_REFERENCE_2)?inter->idRef2:nodeId);
1329               DBG_fprintf(stderr, "Interactive: emit node-selection with nodes"
1330                           " %p, %p, %p.\n", (gpointer)nodes[0],
1331                           (gpointer)nodes[1], (gpointer)nodes[2]);
1332               switch (pick)
1333                 {
1334                 case (PICK_SELECTED):
1335                 case (PICK_HIGHLIGHT):
1336                 case (PICK_INFORMATION):
1337                   if (nodes[0])
1338                     g_signal_emit(G_OBJECT(inter),
1339                                   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1340                                   0, pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1341                   break;
1342                 case (PICK_REFERENCE_1):
1343                 case (PICK_UNREFERENCE_1):
1344                   g_signal_emit(G_OBJECT(inter),
1345                                 interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1346                                 0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1347                   break;
1348                 case (PICK_REFERENCE_2):
1349                 case (PICK_UNREFERENCE_2):
1350                   if (nodes[1])
1351                     g_signal_emit(G_OBJECT(inter),
1352                                   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1353                                   0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1354                   break;
1355                 case (PICK_DISTANCE):
1356                   if (nodes[0] && nodes[1])
1357                     g_signal_emit(G_OBJECT(inter),
1358                                   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1359                                   0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1360                   break;
1361                 case (PICK_ANGLE):
1362                   if (nodes[0] && nodes[1] && nodes[2])
1363                     g_signal_emit(G_OBJECT(inter),
1364                                   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1365                                   0 , pick, inter->dataObj, nodes[0], nodes[1], nodes[2]);
1366                   break;
1367                 default:
1368                   break;
1369                 }
1370 	    }
1371 	  else if (pick == PICK_NONE && error != PICK_ERROR_NONE)
1372 	    g_signal_emit(G_OBJECT(inter),
1373 			  interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1374 			  0 , error, NULL);
1375 	}
1376       /* If drag action, we compute all the nodes in the rectangle
1377 	 and we erase it. */
1378       else
1379 	{
1380 	  /* We get the list of selected nodes. */
1381 	  glDrawSelection(inter, -1, -1);
1382 	  /* Get all nodes in the region. */
1383 	  inter->idRegion = visu_gl_ext_nodes_getSelectionByRegion(inter->nodeList,
1384                                                                    inter->xOrig, inter->yOrig,
1385                                                                    ev->x, ev->y);
1386 	  DBG_fprintf(stderr, "Interactive: set selection (drag).\n");
1387 	  /* Copy the region list. */
1388 	  if (inter->idRegion)
1389             g_signal_emit(G_OBJECT(inter),
1390                           interactive_signals[INTERACTIVE_PICK_REGION_SIGNAL],
1391                           0 , inter->idRegion, NULL);
1392 	  else
1393 	    g_signal_emit(G_OBJECT(inter),
1394 			  interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1395 			  0 , PICK_ERROR_NO_SELECTION, NULL);
1396 	}
1397     }
1398   else if (ev->specialKey == Key_Menu)
1399     {
1400       g_return_val_if_fail(inter->nodeList, TRUE);
1401 
1402       /* Set new selection from click. */
1403       pick = PICK_NONE;
1404       /* We select the node or none. */
1405       nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1406       if (nodeId >= 0)
1407         {
1408           nodeArray = VISU_NODE_ARRAY(inter->dataObj);
1409           nodes[0] = visu_node_array_getFromId(nodeArray, nodeId);
1410         }
1411       else
1412         nodes[0] = (VisuNode*)0;
1413       g_signal_emit(G_OBJECT(inter),
1414                     interactive_signals[INTERACTIVE_PICK_MENU_SIGNAL],
1415                     0, ev->root_x, ev->root_y, nodes[0], NULL);
1416     }
1417   return FALSE;
1418 }
1419 
1420 static gboolean move(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev)
1421 {
1422   int dx, dy, nodeId;
1423   float ratio, z, xyz[3];
1424   VisuNode *node;
1425   float delta[3], delta0[3], centre[3];
1426   guint i;
1427 
1428   g_return_val_if_fail(ev && inter, TRUE);
1429   if (ev->button == 3)
1430     {
1431       if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1432 	return TRUE;
1433       else
1434 	return FALSE;
1435     }
1436   if (ev->button != 1 && ev->button != 2 && ev->button != 4 && ev->button != 5 &&
1437       ev->specialKey != Key_Arrow_Left && ev->specialKey != Key_Arrow_Right &&
1438       ev->specialKey != Key_Arrow_Up && ev->specialKey != Key_Arrow_Down)
1439     return FALSE;
1440   DBG_fprintf(stderr, "Visu Interactive: event (%d - %d, %d %d).\n",
1441               ev->button, ev->specialKey, ev->shiftMod, ev->controlMod);
1442 
1443   if ((ev->motion == 1 || ev->button == 4 || ev->button == 5 ||
1444        ev->specialKey == Key_Arrow_Left || ev->specialKey == Key_Arrow_Right ||
1445        ev->specialKey == Key_Arrow_Up || ev->specialKey == Key_Arrow_Down) &&
1446       inter->movingNodes)
1447     {
1448       DBG_fprintf(stderr, "Visu Interactive: drag action (%dx%d).\n", ev->x, ev->y);
1449       if (ev->specialKey == Key_Arrow_Left)
1450         {
1451           ev->x = inter->xPrev - 1;
1452           ev->y = inter->yPrev;
1453           ev->button = 1;
1454         }
1455       else if (ev->specialKey == Key_Arrow_Right)
1456         {
1457           ev->x = inter->xPrev + 1;
1458           ev->y = inter->yPrev;
1459           ev->button = 1;
1460         }
1461       else if (ev->specialKey == Key_Arrow_Up)
1462         {
1463           ev->x = inter->xPrev;
1464           ev->y = inter->yPrev - 1;
1465           ev->button = 1;
1466         }
1467       else if (ev->specialKey == Key_Arrow_Down)
1468         {
1469           ev->x = inter->xPrev;
1470           ev->y = inter->yPrev + 1;
1471           ev->button = 1;
1472         }
1473       dx =   ev->x - inter->xPrev;
1474       dy = -(ev->y - inter->yPrev);
1475       DBG_fprintf(stderr, " | dx x dy : %d x %d\n", dx, dy);
1476 
1477       g_return_val_if_fail(inter->nodeList, TRUE);
1478 
1479       /* Get the camera orientation. */
1480       if (!ev->shiftMod && !ev->controlMod)
1481 	{
1482 	  if (ev->button == 1)
1483 	    {
1484 	      visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(view)), centre);
1485 	      glMatrixMode(GL_MODELVIEW);
1486 	      glPushMatrix();
1487 	      glTranslated(-centre[0], -centre[1], -centre[2]);
1488               z = 0.f;
1489               for (i = 0; i < inter->movingNodes->len; i++)
1490 		{
1491                   node = visu_node_array_getFromId
1492                     (VISU_NODE_ARRAY(inter->dataObj),
1493                      g_array_index(inter->movingNodes, guint, i));
1494 		  visu_data_getNodePosition(inter->dataObj, node, xyz);
1495 		  z += visu_gl_view_getZCoordinate(view, xyz);
1496 		}
1497               z /= (float)inter->movingNodes->len;
1498 	      /* Now, z contains the prev real space coord z of the
1499                  selected nodes (averaged). */
1500 
1501 	      visu_gl_view_getRealCoordinates(view, delta,  (float)ev->x, (float)ev->y, z);
1502 	      visu_gl_view_getRealCoordinates(view, delta0, (float)inter->xPrev,
1503                                               (float)inter->yPrev, z);
1504 	      glPopMatrix();
1505 	      /* Now, delta contains the new real space coord. */
1506 
1507 	      delta[0] -= delta0[0];
1508 	      delta[1] -= delta0[1];
1509 	      delta[2] -= delta0[2];
1510 	    }
1511 	  else if (ev->button > 1)
1512 	    {
1513 	      if (ev->button == 4)
1514 		dy = -5;
1515 	      else if (ev->button == 5)
1516 		dy = +5;
1517 	      ratio = visu_gl_window_getFileUnitPerPixel(&view->window) * dy;
1518 	      delta[0] = inter->movingAxe[0] * ratio;
1519 	      delta[1] = inter->movingAxe[1] * ratio;
1520 	      delta[2] = inter->movingAxe[2] * ratio;
1521 	    }
1522 	}
1523       else if (ev->button == 1)
1524 	{
1525 	  delta[0] = 0.f;
1526 	  delta[1] = 0.f;
1527 	  delta[2] = 0.f;
1528 	  ratio = visu_gl_window_getFileUnitPerPixel(&view->window);
1529 	  if (ABS(dy) > ABS(dx))
1530 	    ratio *= ((dy > 0)?1.f:-1.f);
1531 	  else
1532 	    ratio *= ((dx > 0)?1.f:-1.f);
1533 	  if (ev->shiftMod && !ev->controlMod)
1534 	    delta[0] = ratio * sqrt(dx * dx + dy * dy);
1535 	  else if (ev->controlMod && !ev->shiftMod)
1536 	    delta[1] = ratio * sqrt(dx * dx + dy * dy);
1537 	  else if (ev->controlMod && ev->shiftMod)
1538 	    delta[2] = ratio * sqrt(dx * dx + dy * dy);
1539 	}
1540       else
1541 	return FALSE;
1542 
1543       DBG_fprintf(stderr, "Visu Interactive: drag a list of %d nodes of %gx%gx%g.\n",
1544 		  inter->movingNodes->len,
1545 		  delta[0], delta[1], delta[2]);
1546       visu_node_array_shiftNodes(VISU_NODE_ARRAY(inter->dataObj), inter->movingNodes, delta);
1547 
1548       /* Update stored position for drag info. */
1549       inter->xPrev = ev->x;
1550       inter->yPrev = ev->y;
1551 
1552       DBG_fprintf(stderr, "Visu Interactive: emit the 'move' signal.\n");
1553       g_signal_emit(G_OBJECT(inter),
1554 		    interactive_signals[INTERACTIVE_MOVE_SIGNAL],
1555 		    0 , delta, NULL);
1556     }
1557   else if ((ev->button == 1 || ev->button == 2) &&
1558 	   ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1559     {
1560       /* Store the position to find the drag values. */
1561       inter->xOrig = inter->xPrev = ev->x;
1562       inter->yOrig = inter->yPrev = ev->y;
1563 
1564       /* Empty previously picked movingNodes. */
1565       if (inter->movingPicked)
1566         {
1567 	  g_array_unref(inter->movingNodes);
1568 	  inter->movingNodes = (GArray*)0;
1569         }
1570 
1571       /* Case no list set yet. */
1572       if (inter->movingNodes == (GArray*)0)
1573 	{
1574           g_return_val_if_fail(inter->nodeList, FALSE);
1575 
1576           nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1577 	  if (nodeId < 0)
1578 	    return FALSE;
1579 
1580 	  inter->movingNodes = g_array_new(FALSE, FALSE, sizeof(guint));
1581           g_array_append_val(inter->movingNodes, nodeId);
1582 	  inter->movingPicked = TRUE;
1583 	}
1584 
1585       DBG_fprintf(stderr, "Visu Interactive: emit the 'start-move' signal.\n");
1586       g_signal_emit(G_OBJECT(inter),
1587 		    interactive_signals[INTERACTIVE_START_MOVE_SIGNAL],
1588 		    0, inter->movingNodes, NULL);
1589     }
1590   else if ((ev->button == 1 || ev->button == 2) &&
1591            ev->buttonType == TOOL_BUTTON_TYPE_RELEASE &&
1592            inter->movingNodes)
1593     {
1594   /*     DBG_fprintf(stderr, "Visu Interactive: stop dragging a list of %d nodes.\n", */
1595   /*       	  inter->movingNodes->len); */
1596   /*     node = (VisuNode*)0; */
1597       DBG_fprintf(stderr, "Visu Interactive: emit the 'stop-move' signal.\n");
1598       g_signal_emit(G_OBJECT(inter),
1599 		    interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL],
1600 		    0, delta, NULL);
1601     }
1602 
1603   return FALSE;
1604 }
1605 /**
1606  * visu_interactive_setMovingNodes:
1607  * @inter: a #VisuInteractive object.
1608  * @nodeIds: (element-type guint) (allow-none): a list of node ids.
1609  *
1610  * Defines the nodes that should be moved if @inter is a move
1611  * action session. The list is actually copied.
1612  */
1613 void visu_interactive_setMovingNodes(VisuInteractive *inter, GArray *nodeIds)
1614 {
1615   g_return_if_fail(VISU_IS_INTERACTIVE(inter) && inter->id == interactive_move);
1616 
1617   /* Empty the node list. */
1618   if (inter->movingNodes)
1619     g_array_unref(inter->movingNodes);
1620   inter->movingNodes = (GArray*)0;
1621   if (nodeIds && nodeIds->len > 0)
1622     {
1623       inter->movingNodes = nodeIds;
1624       DBG_fprintf(stderr, "Visu Interactive: set the list of %d nodes to move.\n",
1625                   nodeIds->len);
1626       g_array_ref(nodeIds);
1627     }
1628   inter->movingPicked = FALSE;
1629 }
1630 /**
1631  * visu_interactive_setMovingAxe:
1632  * @inter: a #VisuInteractive object.
1633  * @axe: a direction.
1634  *
1635  * Defines the axe that can be used to move along if @inter is a move
1636  * action session.
1637  */
1638 void visu_interactive_setMovingAxe(VisuInteractive *inter, float axe[3])
1639 {
1640   float norm;
1641 
1642   norm = 1.f / sqrt(axe[0] * axe[0] + axe[1] * axe[1] + axe[2] * axe[2]);
1643   inter->movingAxe[0] = axe[0] * norm;
1644   inter->movingAxe[1] = axe[1] * norm;
1645   inter->movingAxe[2] = axe[2] * norm;
1646 }
1647 static void getDeltaScreen(gfloat delta[3], VisuGlView *view,
1648                            gint x, gint y, gint xOrig, gint yOrig)
1649 {
1650   float z, centre[3], delta0[3];
1651 
1652   visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(view)), centre);
1653   glMatrixMode(GL_MODELVIEW);
1654   glPushMatrix();
1655   glTranslated(-centre[0], -centre[1], -centre[2]);
1656   z = visu_gl_view_getZCoordinate(view, centre);
1657   visu_gl_view_getRealCoordinates(view, delta,  (float)x, (float)y, z);
1658   visu_gl_view_getRealCoordinates(view, delta0, (float)xOrig, (float)yOrig, z);
1659   glPopMatrix();
1660   /* Now, delta contains the new real space coord. */
1661 
1662   delta[0] -= delta0[0];
1663   delta[1] -= delta0[1];
1664   delta[2] -= delta0[2];
1665 }
1666 static void getDeltaAlong(gfloat delta[3], VisuGlView *view, gfloat movingAxe[3])
1667 {
1668   int dy;
1669   float ratio;
1670 
1671   dy = 5;
1672   ratio = visu_gl_window_getFileUnitPerPixel(&view->window) * dy;
1673   delta[0] = movingAxe[0] * ratio;
1674   delta[1] = movingAxe[1] * ratio;
1675   delta[2] = movingAxe[2] * ratio;
1676 }
1677 static void getDeltaAxis(gfloat delta[3], VisuGlView *view, ToolXyzDir dir, gint dx, gint dy)
1678 {
1679   float ratio;
1680 
1681   delta[0] = 0.f;
1682   delta[1] = 0.f;
1683   delta[2] = 0.f;
1684   ratio = visu_gl_window_getFileUnitPerPixel(&view->window);
1685   DBG_fprintf(stderr, " | dx x dy : %d x %d\n", dx, dy);
1686   if (ABS(dy) > ABS(dx))
1687     ratio *= ((dy > 0)?1.f:-1.f);
1688   else
1689     ratio *= ((dx > 0)?1.f:-1.f);
1690   delta[dir] = ratio * sqrt(dx * dx + dy * dy);
1691 }
1692 static void getDelta(VisuInteractive *inter, VisuGlView *view,
1693                      ToolSimplifiedEvents *ev, gfloat delta[3])
1694 {
1695   int dx, dy;
1696 
1697   /* Get the real space shift. */
1698   if (ev->button == 1)
1699     {
1700       dx =   ev->x - inter->xOrig;
1701       dy = -(ev->y - inter->yOrig);
1702       if (!ev->shiftMod && !ev->controlMod)
1703         getDeltaScreen(delta, view, ev->x, ev->y, inter->xOrig, inter->yOrig);
1704       else if (ev->shiftMod && !ev->controlMod)
1705         getDeltaAxis(delta, view, TOOL_XYZ_X, dx, dy);
1706       else if (ev->controlMod && !ev->shiftMod)
1707         getDeltaAxis(delta, view, TOOL_XYZ_Y, dx, dy);
1708       else if (ev->controlMod && ev->shiftMod)
1709         getDeltaAxis(delta, view, TOOL_XYZ_Z, dx, dy);
1710     }
1711   else
1712     {
1713       getDeltaAlong(delta, view, inter->movingAxe);
1714       if (ev->button == 4)
1715         {
1716           delta[0] *= -1.f;
1717           delta[1] *= -1.f;
1718           delta[2] *= -1.f;
1719         }
1720     }
1721 }
1722 static gboolean drag(VisuInteractive *inter, VisuGlView *view, ToolSimplifiedEvents *ev)
1723 {
1724   float delta[3];
1725 
1726   g_return_val_if_fail(ev && inter, TRUE);
1727   if (ev->button == 3)
1728     {
1729       if (ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1730 	return TRUE;
1731       else
1732 	return FALSE;
1733     }
1734   if (ev->button != 1 && ev->button != 4 && ev->button != 5 &&
1735       ev->specialKey != Key_Arrow_Left && ev->specialKey != Key_Arrow_Right &&
1736       ev->specialKey != Key_Arrow_Up && ev->specialKey != Key_Arrow_Down)
1737     return FALSE;
1738   DBG_fprintf(stderr, "Visu Interactive: event (%d - %d, %d %d).\n",
1739               ev->button, ev->specialKey, ev->shiftMod, ev->controlMod);
1740 
1741   if ((ev->motion == 1 || ev->button == 4 || ev->button == 5) ||
1742       ev->specialKey == Key_Arrow_Left || ev->specialKey == Key_Arrow_Right ||
1743       ev->specialKey == Key_Arrow_Up || ev->specialKey == Key_Arrow_Down)
1744     {
1745       if (ev->specialKey == Key_Arrow_Left)
1746         {
1747           ev->x = inter->xPrev - 1;
1748           ev->y = inter->yPrev;
1749           ev->button = 1;
1750         }
1751       else if (ev->specialKey == Key_Arrow_Right)
1752         {
1753           ev->x = inter->xPrev + 1;
1754           ev->y = inter->yPrev;
1755           ev->button = 1;
1756         }
1757       else if (ev->specialKey == Key_Arrow_Up)
1758         {
1759           ev->x = inter->xPrev;
1760           ev->y = inter->yPrev - 1;
1761           ev->button = 1;
1762         }
1763       else if (ev->specialKey == Key_Arrow_Down)
1764         {
1765           ev->x = inter->xPrev;
1766           ev->y = inter->yPrev + 1;
1767           ev->button = 1;
1768         }
1769       DBG_fprintf(stderr, "Visu Interactive: drag action at (%dx%d).\n", ev->x, ev->y);
1770 
1771       getDelta(inter, view, ev, delta);
1772       DBG_fprintf(stderr, "Visu Interactive: drag a mouse of %gx%gx%g.\n",
1773 		  delta[0], delta[1], delta[2]);
1774 
1775       /* Update stored position for drag info. */
1776       inter->xPrev = ev->x;
1777       inter->yPrev = ev->y;
1778 
1779       DBG_fprintf(stderr, "Visu Interactive: emit the 'move' signal.\n");
1780       g_signal_emit(G_OBJECT(inter),
1781 		    interactive_signals[INTERACTIVE_MOVE_SIGNAL],
1782 		    0 , delta, NULL);
1783     }
1784   else if (ev->button == 1 && ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1785     {
1786       g_return_val_if_fail(inter->nodeList, TRUE);
1787 
1788       /* Store the position to find the drag values. */
1789       inter->xOrig = inter->xPrev = ev->x;
1790       inter->yOrig = inter->yPrev = ev->y;
1791 
1792       /* DBG_fprintf(stderr, "Visu Interactive: emit the 'start-move' signal.\n"); */
1793       /* g_signal_emit(G_OBJECT(inter), */
1794       /*   	    interactive_signals[INTERACTIVE_START_MOVE_SIGNAL], */
1795       /*   	    0, inter->movingNodes, NULL); */
1796     }
1797   else if (ev->button == 1 && ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1798     {
1799       getDelta(inter, view, ev, delta);
1800       DBG_fprintf(stderr, "Visu Interactive: stop dragging mouse of %gx%gx%g.\n",
1801 		  delta[0], delta[1], delta[2]);
1802       g_signal_emit(G_OBJECT(inter),
1803 		    interactive_signals[INTERACTIVE_STOP_MOVE_SIGNAL],
1804 		    0 , delta, NULL);
1805     }
1806 
1807   return FALSE;
1808 }
1809 /**
1810  * visu_interactive_highlight:
1811  * @inter: a #VisuInteractive object.
1812  * @nodeId: a node id.
1813  *
1814  * This routine simulates an highlight action on @nodeId. This triggers the signal
1815  * #VisuInteractive::node-selection, or #VisuInteractive::selection-error
1816  * if @nodeId is invalid.
1817  *
1818  * Since: 3.7
1819  **/
1820 void visu_interactive_highlight(VisuInteractive *inter, guint nodeId)
1821 {
1822   VisuNode *nodes;
1823 
1824   nodes = visu_node_array_getFromId(VISU_NODE_ARRAY(inter->dataObj), nodeId);
1825   if (nodes)
1826     g_signal_emit(G_OBJECT(inter),
1827                   interactive_signals[INTERACTIVE_PICK_NODE_SIGNAL],
1828                   0 , PICK_HIGHLIGHT, inter->dataObj, nodes, (VisuNode*)0, (VisuNode*)0);
1829   else
1830     g_signal_emit(G_OBJECT(inter),
1831 		  interactive_signals[INTERACTIVE_PICK_ERROR_SIGNAL],
1832 		  0 , PICK_ERROR_NO_SELECTION, NULL);
1833 }
1834 
1835 /**
1836  * visu_interactive_setMessage:
1837  * @inter: a #VisuInteractive object.
1838  * @message: some text.
1839  *
1840  * Set a describing @message to be shown when @inter is used.
1841  *
1842  * Since: 3.8
1843  **/
1844 void visu_interactive_setMessage(VisuInteractive *inter, const gchar *message)
1845 {
1846   g_return_if_fail(VISU_IS_INTERACTIVE(inter));
1847 
1848   g_free(inter->message);
1849   inter->message = g_strdup(message);
1850 }
1851 
1852 /**
1853  * visu_interactive_getMessage:
1854  * @inter: a #VisuInteractive object.
1855  *
1856  * Retrieves the message associated to @inter.
1857  *
1858  * Since: 3.8
1859  *
1860  * Returns: a message.
1861  **/
1862 const gchar* visu_interactive_getMessage(VisuInteractive *inter)
1863 {
1864   g_return_val_if_fail(VISU_IS_INTERACTIVE(inter), (const gchar*)0);
1865   return inter->message;
1866 }
1867 
1868 static gboolean mark(VisuInteractive *inter, ToolSimplifiedEvents *ev)
1869 {
1870   int nodeId;
1871 
1872   g_return_val_if_fail(ev && inter, TRUE);
1873   if (ev->button == 3 && ev->buttonType == TOOL_BUTTON_TYPE_PRESS)
1874     return TRUE;
1875   if (ev->buttonType == TOOL_BUTTON_TYPE_RELEASE)
1876     return FALSE;
1877 
1878   g_return_val_if_fail(inter->nodeList, TRUE);
1879 
1880   nodeId = visu_gl_ext_nodes_getSelection(inter->nodeList, ev->x, ev->y);
1881   visu_interactive_highlight(inter, (guint)nodeId);
1882   return FALSE;
1883 }
1884 
1885 static void onReadCamera(VisuConfigFile *obj _U_, VisuConfigFileEntry *entry _U_, VisuInteractiveClass *klass)
1886 {
1887   VisuGlCamera camera;
1888 
1889   memset(&camera, 0, sizeof(VisuGlCamera));
1890   visu_gl_camera_setThetaPhiOmega(&camera, cameraData[0], cameraData[1], cameraData[2],
1891                                 VISU_GL_CAMERA_THETA | VISU_GL_CAMERA_PHI | VISU_GL_CAMERA_OMEGA);
1892   visu_gl_camera_setXsYs(&camera, cameraData[3], cameraData[4], VISU_GL_CAMERA_XS | VISU_GL_CAMERA_YS);
1893   visu_gl_camera_setGross(&camera, cameraData[5]);
1894   visu_gl_camera_setPersp(&camera, cameraData[6]);
1895   _pushSavedCamera(klass, &camera);
1896 }
1897 static void exportParameters(GString *data, VisuData *dataObj _U_)
1898 {
1899   VisuGlCamera *camera;
1900   GList *tmp;
1901 
1902   if (!local_class)
1903     visu_interactive_get_type();
1904 
1905   g_string_append_printf(data, "# %s\n", DESC_PARAMETER_OBSERVE_METHOD);
1906   g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_OBSERVE_METHOD,
1907 			 local_class->preferedObserveMethod);
1908   if (local_class->savedCameras)
1909     g_string_append_printf(data, "# %s\n", DESC_PARAMETER_CAMERA_SETTINGS);
1910   for (tmp = local_class->savedCameras; tmp; tmp = g_list_next(tmp))
1911     {
1912       camera = (VisuGlCamera*)tmp->data;
1913       g_string_append_printf(data, "%s[gtk]: %7.5g %7.5g %7.5g   %4.3g %4.3g   %g   %g\n",
1914                              FLAG_PARAMETER_CAMERA_SETTINGS,
1915                              camera->theta, camera->phi, camera->omega,
1916                              camera->xs, camera->ys, camera->gross, camera->d_red);
1917     }
1918   if (local_class->savedCameras)
1919     g_string_append(data, "\n");
1920 }
1921 void visu_interactive_class_setPreferedObserveMethod(VisuInteractiveMethod method)
1922 {
1923   g_return_if_fail(method == interactive_constrained ||
1924 		   method == interactive_walker);
1925 
1926   if (!local_class)
1927     visu_interactive_get_type();
1928 
1929   local_class->preferedObserveMethod = method;
1930 }
1931 VisuInteractiveMethod visu_interactive_class_getPreferedObserveMethod()
1932 {
1933   if (!local_class)
1934     visu_interactive_get_type();
1935 
1936   return local_class->preferedObserveMethod;
1937 }
1938 
1939 /* From an array of nodes ids, we specify their 2D coordinates */
1940 void visu_interactive_class_getNodes2DCoordinates(VisuData *dataObj, guint *nodeIds,
1941     	guint nNodes, GLfloat *coordinates2D, guint *size)
1942 {
1943   int i,j;
1944   guint k;
1945   VisuNode *node;
1946   float xyz[3], centre[3];
1947   GLfloat *coord;
1948   GLint nValues;
1949 
1950   visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(dataObj)), centre);
1951   coord = g_malloc(sizeof(GLfloat) * nNodes * (1 + 2));
1952   glFeedbackBuffer(nNodes * (2 + 1), GL_2D, coord);
1953   glRenderMode(GL_FEEDBACK);
1954   glPushMatrix();
1955   glTranslated(-centre[0], -centre[1], -centre[2]);
1956 
1957   /* Get nodes from ids and store their 2D coordinates */
1958   glBegin(GL_POINTS);
1959   for(k = 0; k < nNodes; k++){
1960     node = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), nodeIds[k]);
1961     if(node != NULL)
1962     {
1963       visu_data_getNodePosition(dataObj, node, xyz);
1964       glVertex3fv(xyz);
1965     }
1966   }
1967   glEnd();
1968   glPopMatrix();
1969   nValues = glRenderMode(GL_RENDER);
1970   i = 0;
1971   j = 0;
1972   /* Keep only the coordinates */
1973   while (i < nValues)
1974   {
1975     if (coord[i] == GL_POINT_TOKEN)
1976     {
1977       coordinates2D[j] = coord[i + 1];
1978       coordinates2D[j+1] = coord[i + 2];
1979       i += 3;
1980       j += 2;
1981     }
1982     else
1983       i++;
1984   }
1985   *size = j;
1986 }
1987