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

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

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

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

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

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

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

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

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

#define V_SIM_GDK

#include "gtk_renderingWindowWidget.h"

#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>
#include <math.h> /* for sqrt function... */
#include <string.h>
#include <stdlib.h>

#include "support.h"
#include "visu_gtk.h"
#include "visu_tools.h"
#include "visu_basic.h"
#include "visu_dataatomic.h"
#include "visu_dataspin.h"
#include "visu_configFile.h"
#include "visu_glnodescene.h"
#include "OSOpenGL/visu_openGL.h"
#include "renderingBackend/visu_actionInterface.h"
#include "gtk_openGLWidget.h"
#include "coreTools/toolFileFormat.h"
#include "coreTools/toolConfigFile.h"
#include "extraGtkFunctions/gtk_dumpDialogWidget.h"
#include "extraGtkFunctions/gtk_orientationChooser.h"
#include "extraGtkFunctions/gtk_dataChooser.h"
#include "openGLFunctions/objectList.h"
#include "openGLFunctions/interactive.h"
#include "opengl.h"
#include "extensions/axes.h"
#include "extensions/box.h"
#include "extensions/box_legend.h"
#include "extensions/frame.h"
#include "extensions/legend.h"
#include "extensions/nodes.h"
#include "extensions/pairs.h"
#include "extensions/scale.h"
#include "extensions/planes.h"
#include "extensions/forces.h"
#include "dumpModules/dumpToSVG.h"
#include "dumpModules/glDump.h"
#include "gtk_main.h"

/**
 * SECTION:gtk_renderingWindowWidget
 * @short_description: Defines a complex widget used to render files
 * and print information.
 *
 * <para>This is a complex widget, inheriting from #GtkWindow, with a
 * rendering area and a status bar area. A #VisuData is always
 * attached to this widget, see visu_ui_rendering_window_setData(). If not
 * the V_Sim logo is displayed.</para>
 *
 * <para>The rendering area can receive keyboard or mouse events, see
 * visu_ui_rendering_window_class_getInteractive.</para>
 *
 * <para>The status bar area has different buttons to load or export a
 * file. It also display some usefull information like the number of
 * rendered nodes. It has also a real status bar location displaying
 * tips about current available actions. One can add news using
 * visu_ui_rendering_window_pushMessage().</para>
 */

typedef enum
  {
    event_button_press,
    event_button_release,
    event_motion_notify,
    event_key_press,
    event_key_release,
    event_scroll
  } InteractiveEventsId;

struct InteractiveEvents_struct
{
  gulong callbackId;
  InteractiveEventsId id;
};
typedef struct InteractiveEvents_struct InteractiveEvents;


struct GtkInfoArea_struct
{
  GtkWidget *area;

  GtkWidget *infoBar;
  GtkWidget *searchEntry;

  GtkWidget *hboxFileInfo;
  GtkWidget *labelSize;
  GtkWidget *labelNb;
  GBinding *bind_nb;
  GtkWidget *labelFileInfo;
  GBinding *bind_info;
  gboolean fileInfoFreeze;

  GtkWidget *hboxTools;
  GtkWidget *dumpButton;
  GtkWidget *loadButton;
  GtkWidget *raiseButton;
  GtkWidget *reloadButton;

  GtkWidget *statusInfo, *progress;
  guint progressId, waitId;
  GtkWidget *cancelButton;
  GCancellable *cancel;

  GtkWidget *hboxInteractive;

  guint statusInfoId;
};
typedef struct GtkInfoArea_struct GtkInfoArea;

#define GTK_STATUSINFO_NOFILEINFO _("<span style=\"italic\">No description is available</span>")
#define GTK_STATUSINFO_NONB       _("<span style=\"italic\">Nothing is loaded</span>")
#define FLAG_PARAMETER_RED_COORD   "config_showReducedCoordinates"
#define DESC_PARAMETER_RED_COORD   "Display coordinates in reduced values when picking a node ; boolean 0 or 1"
static gboolean _useReducedCoordinates = FALSE;

#define MENU_CAMERA_SAVE    "<VisuUiRenderingWindow>/Camera/Save"
#define MENU_CAMERA_RESTORE "<VisuUiRenderingWindow>/Camera/Restore"
#define MENU_CAMERA_ORIENT  "<VisuUiRenderingWindow>/Camera/Orientation"
#define MENU_CAMERA_1       "<VisuUiRenderingWindow>/Camera/select1"
#define MENU_CAMERA_2       "<VisuUiRenderingWindow>/Camera/select2"
#define MENU_CAMERA_3       "<VisuUiRenderingWindow>/Camera/select3"
#define MENU_CAMERA_4       "<VisuUiRenderingWindow>/Camera/select4"
#define MENU_CAMERA_5       "<VisuUiRenderingWindow>/Camera/select5"
#define MENU_CAMERA_6       "<VisuUiRenderingWindow>/Camera/select6"
#define MENU_CAMERA_7       "<VisuUiRenderingWindow>/Camera/select7"
#define MENU_CAMERA_8       "<VisuUiRenderingWindow>/Camera/select8"
#define MENU_CAMERA_9       "<VisuUiRenderingWindow>/Camera/select9"
static const gchar* cameraAccels[] = {MENU_CAMERA_1, MENU_CAMERA_2, MENU_CAMERA_3, MENU_CAMERA_4,
                                      MENU_CAMERA_5, MENU_CAMERA_6, MENU_CAMERA_7, MENU_CAMERA_8,
                                      MENU_CAMERA_9};
static guint cameraKeys[] = {GDK_KEY_1, GDK_KEY_2, GDK_KEY_3, GDK_KEY_4, GDK_KEY_5,
                             GDK_KEY_6, GDK_KEY_7, GDK_KEY_8, GDK_KEY_9};

enum {
  EXPORT_SIGNAL,
  OPEN_SIGNAL,
  RELOAD_SIGNAL,
  SHOW_ACTION_DIALOG_SIGNAL,
  SHOW_MAIN_PANEL_SIGNAL,
  SHOW_ORIENTATION_SIGNAL,
  LOAD_NEXT_FILE_SIGNAL,
  LOAD_PREV_FILE_SIGNAL,
  SEARCH_SIGNAL,
  LAST_SIGNAL
};
enum
  {
    PROP_0,
    LABEL_PROP,
    DATA_PROP,
    VIEW_PROP,
    SCENE_PROP,
    INTER_PROP,
    COORD_PROP,
    SELECTION_PROP,
    TOOLBAR_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];
enum
{
  TEXT_PLAIN,
  TEXT_URI_LIST
};

/* Local variables. */
static VisuInteractive *interPickObs = NULL;
static guint _signals[LAST_SIGNAL] = { 0 };
static VisuUiRenderingWindow *_defaultRendering = NULL;

/* Local methods. */
static void exportParameters(GString *data, VisuData *dataObj);
static void gtkStatusInfo_createBar(VisuUiRenderingWindow *window,
                                    gboolean withToolBar);
static gulong addInteractiveEventListeners(VisuUiRenderingWindow *window,
					   InteractiveEventsId id);
static GtkWidget* buildCameraMenu(VisuUiRenderingWindow *window);
static void _setLabelSize(GtkInfoArea *info, gint width, gint height);
static void getOpenGLAreaSize(VisuUiRenderingWindow *window,
			      guint *width, guint *height);
static void _redraw(VisuUiRenderingWindow *window, gboolean forceRedraw);

/* Local callbacks */
static void visu_ui_rendering_window_dispose (GObject* obj);
static void visu_ui_rendering_window_finalize(GObject* obj);
static void visu_ui_rendering_window_constructed (GObject* obj);
static void visu_ui_rendering_window_get_property(GObject* obj, guint property_id,
                                                  GValue *value, GParamSpec *pspec);
static void visu_ui_rendering_window_set_property(GObject* obj, guint property_id,
                                                  const GValue *value,
                                                  GParamSpec *pspec);
static gboolean toNNodeLabel(GBinding *binding, const GValue *source_value,
                             GValue *target_value, gpointer data);
static gboolean toFileInfo(GBinding *binding, const GValue *source_value,
                           GValue *target_value, gpointer data);
static void onDataNotify(VisuUiRenderingWindow *window,
                         GParamSpec *psepc, VisuGlNodeScene *scene);
static void onLoadingNotified(VisuUiRenderingWindow *window,
                              GParamSpec *pspec, VisuGlNodeScene *scene);
static void onMarkClearClicked(VisuUiRenderingWindow *window, GtkButton *button);
static void onNodeInfoClicked(VisuUiRenderingWindow *window, GtkToggleButton *button);
static void onRaiseButtonClicked(VisuUiRenderingWindow *window, gpointer user_data);
static void onGlDirty(VisuUiRenderingWindow *window);
static gboolean onDragMotion(GtkWidget *widget, GdkDragContext *context,
			     gint x, gint y, guint t, gpointer user_data);
static void onDropData(VisuUiRenderingWindow *window, GdkDragContext *context,
		       gint x, gint y, GtkSelectionData *selection_data,
		       guint target_type, guint time, GtkWidget *glArea);
static gboolean onCameraMenu(VisuUiRenderingWindow *window, GdkEventButton *event,
			     GtkEventBox *ev);
static void onCameraMenuSelected(GtkMenuShell *menushell, gpointer user_data);
static void onCameraMenuClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onCameraMenuCurrentClicked(GtkMenuItem *menuitem, gpointer user_data);
static void onCameraMenuOrientationClicked(GtkMenuItem *menuitem, gpointer user_data);
static void minimalPickInfo(VisuInteractive *inter, VisuInteractivePick pick,
			    VisuData *dataObj, VisuNode *node0,
                            VisuNode *node1, VisuNode *node2, gpointer data);
static void minimalPickError(VisuInteractive *inter,
			     VisuInteractivePickError error, gpointer data);
static void onCancelButtonClicked(GtkButton *button, gpointer data);
static gboolean onCameraAccel(GtkAccelGroup *accel, GObject *obj,
                              guint key, GdkModifierType mod, gpointer data);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
static void _onSearchClose(GtkInfoBar *bar, gint response, gpointer data);
static void _onSearchEdited(GtkEntry *entry, gpointer data);
#endif
static void onEntryCoord(VisuUiRenderingWindow *window);

struct _VisuUiRenderingWindow
{
  GtkVBox generalVBox;
  gboolean dispose_has_run;

  GdkCursor *cursorRotate;
  GdkCursor *cursorWatch;
  GdkCursor *cursorPointer;
  GdkCursor *cursorPirate;
  GdkCursor *cursorGrab;

  /*********************************/
  /* Dealing with the OpenGL area. */
  /*********************************/
  /* The OpenGL area and it's notification zone. */
  GtkWidget *openGLArea;
  /* This pointer give the handle to rule all interactive actions. */
  GList *inters;
  /* This is a list of currently connected
     signal for the interactive mode. */
  GList *interactiveEvents;
  /* A pointer on the current used cursor. */
  GdkCursor *currentCursor;
  GdkCursor *refCursor;
  /* Rendered extensions. */
  VisuGlNodeScene *glScene;
  gulong sig_data, sig_load;

  /*************************************/
  /* Dealing with the information bar. */
  /*************************************/
  /* TO BE INTEGRATED. */
  GtkInfoArea info;
  /* TO BE INTEGRATED. */
  int nbStatusMessage;
  gboolean useReducedCoordinates;
  gint selectedNodeId;
  gboolean withToolbar;

  GtkAccelGroup *accel;
};

struct _VisuUiRenderingWindowClass
{
  GtkVBoxClass parent_class;

  void (*renderingWindow) (VisuUiRenderingWindow *window);

  /* Action signals for keybindings, do not connect to these */
  void (*export) (VisuUiRenderingWindow *window);
  void (*open)   (VisuUiRenderingWindow *window);
  void (*reload) (VisuUiRenderingWindow *window);
  void (*orient) (VisuUiRenderingWindow *window);
  void (*search) (VisuUiRenderingWindow *window);

  /* To be removed when redraw is a signal object. */
  VisuUiRenderingWindow *redrawWidget;
};

G_DEFINE_TYPE(VisuUiRenderingWindow, visu_ui_rendering_window, GTK_TYPE_BOX)

/* Local callbacks */
static gboolean timeOutPopMessage(gpointer data);
static gboolean onFocus(GtkWidget *wd, GdkEvent *event, gpointer data);
static void onSizeChangeEvent(VisuUiRenderingWindow *window, GtkAllocation *allocation);
static void onRealiseEvent(GtkWidget *wd, gpointer data);
static void onExport(VisuUiRenderingWindow *window);
static void onOpen(VisuUiRenderingWindow *window);
static void _onSearch(VisuUiRenderingWindow *window);
static void _orientationChooser(VisuUiRenderingWindow *window);
static gboolean _onSearchEsc(GtkWidget *widget, GdkEventKey *event, gpointer data);

/* Interactive mode listeners. */
static gboolean OnEvent(VisuUiRenderingWindow *window, GdkEvent *event,
                              gpointer user_data);

static void visu_ui_rendering_window_class_init(VisuUiRenderingWindowClass *klass)
{
  GtkBindingSet *binding_set;
  VisuConfigFileEntry *resourceEntry;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: creating the class of the widget.\n");

  klass->export = onExport;
  klass->reload = visu_ui_rendering_window_reload;
  klass->open   = onOpen;
  klass->orient = _orientationChooser;
  klass->search = _onSearch;

  interPickObs = visu_interactive_new(interactive_measureAndObserve);
  visu_interactive_setMessage(interPickObs, _("Rotate with left b.,"
                                              " pick with right b.,"
                                              " setup ref. with"
                                              " <shift> or <control> b."));
  
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: connect the signals.\n");
  G_OBJECT_CLASS(klass)->dispose  = visu_ui_rendering_window_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_ui_rendering_window_finalize;
  G_OBJECT_CLASS(klass)->constructed  = visu_ui_rendering_window_constructed;
  G_OBJECT_CLASS(klass)->get_property = visu_ui_rendering_window_get_property;
  G_OBJECT_CLASS(klass)->set_property = visu_ui_rendering_window_set_property;

  /**
   * VisuUiRenderingWindow::export:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask for data export.
   *
   * Since: 3.6
   */
  _signals[EXPORT_SIGNAL] =
    g_signal_new("export", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                 G_STRUCT_OFFSET(VisuUiRenderingWindowClass, export),
                 NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::open:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to open new data.
   *
   * Since: 3.6
   */
  _signals[OPEN_SIGNAL] =
    g_signal_new("open", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                 G_STRUCT_OFFSET(VisuUiRenderingWindowClass, open),
                 NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::reload:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to reload current data.
   *
   * Since: 3.6
   */
  _signals[RELOAD_SIGNAL] =
    g_signal_new("reload", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                 G_STRUCT_OFFSET(VisuUiRenderingWindowClass, reload),
                 NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::search:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to search info in current data.
   *
   * Since: 3.7
   */
  _signals[SEARCH_SIGNAL] =
    g_signal_new("search", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                 G_STRUCT_OFFSET(VisuUiRenderingWindowClass, search),
                 NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::show-action-dialog:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to show the action dialog.
   *
   * Since: 3.6
   */
  _signals[SHOW_ACTION_DIALOG_SIGNAL] =
    g_signal_new("show-action-dialog", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::show-main-panel:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to raise the main panel.
   *
   * Since: 3.6
   */
  _signals[SHOW_MAIN_PANEL_SIGNAL] =
    g_signal_new("show-main-panel", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::show-orientation-chooser:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to precisely select a camera angle.
   *
   * Since: 3.7
   */
  _signals[SHOW_ORIENTATION_SIGNAL] =
    g_signal_new("show-orientation-chooser", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
                 G_STRUCT_OFFSET(VisuUiRenderingWindowClass, orient),
                 NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::load-next-file:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to load next file of a given list.
   *
   * Since: 3.7
   */
  _signals[LOAD_NEXT_FILE_SIGNAL] =
    g_signal_new("load-next-file", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);
  /**
   * VisuUiRenderingWindow::load-prev-file:
   * @window: the object emitting the signal.
   *
   * Signal emitted when the user ask to load previous file of a given list.
   *
   * Since: 3.7
   */
  _signals[LOAD_PREV_FILE_SIGNAL] =
    g_signal_new("load-prev-file", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
                 0, NULL, NULL, g_cclosure_marshal_VOID__VOID,
                 G_TYPE_NONE, 0);

  /**
   * VisuUiRenderingWindow::label:
   *
   * Store a label representing the currently loaded data.
   *
   * Since: 3.8
   */
  properties[LABEL_PROP] = g_param_spec_string("label", "Label",
                                               "Label representing the data",
                                               "", G_PARAM_READABLE);
  /**
   * VisuUiRenderingWindow::data:
   *
   * Store which #VisuData is rendered in the window.
   *
   * Since: 3.8
   */
  properties[DATA_PROP] = g_param_spec_object("data", "Data",
                                              "Data marks are applied to",
                                              VISU_TYPE_DATA, G_PARAM_READWRITE);
  /**
   * VisuUiRenderingWindow::gl-view:
   *
   * Store the #VisuGlView of the OpenGL area.
   *
   * Since: 3.8
   */
  properties[VIEW_PROP] = g_param_spec_object("gl-view", "GlView",
                                              "GlView mark labels are aligned with",
                                              VISU_TYPE_GL_VIEW,
                                              G_PARAM_READABLE);
  /**
   * VisuUiRenderingWindow::gl-scene:
   *
   * Store the #VisuGlNodeScene of the OpenGL area.
   *
   * Since: 3.8
   */
  properties[SCENE_PROP] = g_param_spec_object("gl-scene", "GlScene",
                                               "GlScene rendered in the window",
                                               VISU_TYPE_GL_NODE_SCENE,
                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  /**
   * VisuUiRenderingWindow::interactive:
   *
   * Store the current #VisuInteractive of the OpenGL area.
   *
   * Since: 3.8
   */
  properties[INTER_PROP] = g_param_spec_object("interactive", "Interactive",
                                               "current interactive session",
                                               VISU_TYPE_INTERACTIVE, G_PARAM_READABLE);
  /**
   * VisuUiRenderingWindow::coordinates-in-reduced:
   *
   * Display node coordinates in reduced values.
   *
   * Since: 3.8
   */
  properties[COORD_PROP] = g_param_spec_boolean("coordinates-in-reduced",
                                                "Coordinates in reduced values",
                                                "display node coordinates in reduced values",
                                                FALSE, G_PARAM_READWRITE);
  /**
   * VisuUiRenderingWindow::selection:
   *
   * Selected node, if any.
   *
   * Since: 3.8
   */
  properties[SELECTION_PROP] = g_param_spec_boxed("selection", "Selection",
                                                  "currently selected node, if any",
                                                  VISU_TYPE_NODE, G_PARAM_READABLE);
  /**
   * VisuUiRenderingWindow::with-toolbar:
   *
   * If the rendering widget has a toolbar or not.
   *
   * Since: 3.8
   */
  properties[TOOLBAR_PROP] = g_param_spec_boolean("with-toolbar", "With toolbar",
                                                  "if the widget has a toolbar or not",
                                                  TRUE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);

  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, properties);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: connect the bindings.\n");
  binding_set = gtk_binding_set_by_class(klass);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_s, GDK_CONTROL_MASK,
                               "export", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_o, GDK_CONTROL_MASK,
                               "open", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_r, GDK_CONTROL_MASK,
                               "reload", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_v, GDK_CONTROL_MASK,
                               "show-orientation-chooser", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_i, GDK_CONTROL_MASK,
                               "show-action-dialog", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_f, GDK_CONTROL_MASK,
                               "search", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_Home, 0, "show-main-panel", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_n, 0, "load-next-file", 0);
  gtk_binding_entry_add_signal(binding_set, GDK_KEY_p, 0, "load-prev-file", 0);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: add the resources.\n");
  resourceEntry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                                   FLAG_PARAMETER_RED_COORD,
                                                   DESC_PARAMETER_RED_COORD,
                                                   &_useReducedCoordinates, FALSE);
  visu_config_file_entry_setVersion(resourceEntry, 3.6f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                     exportParameters);

  DBG_fprintf(stderr, " | interPickObs has %d ref counts.\n",
              G_OBJECT(interPickObs)->ref_count);
}

static void visu_ui_rendering_window_dispose(GObject* obj)
{
  GList *ptList;
  InteractiveEvents *event;
  VisuUiRenderingWindow *window;
  VisuInteractive *inter;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: dispose object %p.\n", (gpointer)obj);

  window = VISU_UI_RENDERING_WINDOW(obj);
  if (window->dispose_has_run)
    return;
  window->dispose_has_run = TRUE;

  if (window->cursorPirate)
    g_clear_object(&window->cursorPirate);
  if (window->cursorRotate)
    g_clear_object(&window->cursorRotate);
  if (window->cursorWatch)
    g_clear_object(&window->cursorWatch);
  if (window->cursorPointer)
    g_clear_object(&window->cursorPointer);
  if (window->cursorGrab)
    g_clear_object(&window->cursorGrab);

  ptList = window->inters;
  while (ptList)
    {
      inter = VISU_INTERACTIVE(ptList->data);
      ptList = g_list_next(ptList);
      visu_ui_rendering_window_popInteractive(window, inter);
    }

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: releasing current handles.\n");
  if (window->info.progressId)
    g_source_remove(window->info.progressId);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: release current data handle.\n");
  g_clear_object(&window->info.cancel);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: removing interactive listeners.\n");
  for (ptList = window->interactiveEvents; ptList;
       ptList = g_list_next(ptList))
    {
      event = (InteractiveEvents*)ptList->data;
      DBG_fprintf(stderr, "  | disconnecting %d signal.\n", event->id);
      g_signal_handler_disconnect(G_OBJECT(window->openGLArea),
                                  event->callbackId);
      g_free(ptList->data);
    }
  if (window->interactiveEvents)
    g_list_free(window->interactiveEvents);
  window->interactiveEvents = (GList*)0;

  g_clear_object(&window->accel);

  if (window->sig_data)
    g_signal_handler_disconnect(window->glScene, window->sig_data);
  if (window->sig_load)
    g_signal_handler_disconnect(window->glScene, window->sig_load);
  g_clear_object(&window->glScene);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: chain to parent.\n");
  G_OBJECT_CLASS(visu_ui_rendering_window_parent_class)->dispose(obj);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: dispose done.\n");
}
static void visu_ui_rendering_window_finalize(GObject* obj)
{
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: finalize object %p.\n", (gpointer)obj);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_rendering_window_parent_class)->finalize(obj);
}
static void visu_ui_rendering_window_get_property(GObject* obj, guint property_id,
                                                  GValue *value, GParamSpec *pspec)
{
  VisuUiRenderingWindow *self = VISU_UI_RENDERING_WINDOW(obj);
  VisuData *dataObj;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case LABEL_PROP:
      if (!visu_gl_node_scene_getData(self->glScene))
        g_value_set_static_string(value, _("No file loaded"));
      else if (!VISU_IS_DATA_LOADABLE(visu_gl_node_scene_getData(self->glScene)))
        g_value_set_static_string(value, _("No filename"));
      else
        g_object_get_property(G_OBJECT(visu_gl_node_scene_getData(self->glScene)),
                              "label", value);
      DBG_fprintf(stderr, "%s.\n", g_value_get_string(value));
      break;
    case DATA_PROP:
      g_value_set_object(value, visu_gl_node_scene_getData(self->glScene));
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      break;
    case VIEW_PROP:
      g_value_set_object(value, visu_gl_node_scene_getGlView(self->glScene));
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      break;
    case SCENE_PROP:
      g_value_set_object(value,self->glScene);
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      break;
    case INTER_PROP:
      g_value_set_object(value, (self->inters) ? self->inters->data : (gpointer)0);
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      break;
    case COORD_PROP:
      g_value_set_boolean(value, self->useReducedCoordinates);
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      break;
    case SELECTION_PROP:
      dataObj = visu_gl_node_scene_getData(self->glScene);
      g_value_set_static_boxed(value, (self->selectedNodeId >= 0 && dataObj) ?
                               visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
                                                         self->selectedNodeId) :
                               (gconstpointer)0);
      DBG_fprintf(stderr, "%p.\n", g_value_get_boxed(value));
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_ui_rendering_window_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec)
{
  VisuUiRenderingWindow *self = VISU_UI_RENDERING_WINDOW(obj);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case SCENE_PROP:
      self->glScene = g_value_dup_object(value);
      break;
    case DATA_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      visu_gl_node_scene_setData(self->glScene, VISU_DATA(g_value_get_object(value)));
      break;
    case COORD_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      visu_ui_rendering_window_setDisplayCoordinatesInReduce(self, g_value_get_boolean(value));
      break;
    case TOOLBAR_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_boolean(value));
      self->withToolbar = g_value_get_boolean(value);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

static void visu_ui_rendering_window_constructed(GObject *obj)
{
  VisuUiRenderingWindow *self = VISU_UI_RENDERING_WINDOW(obj);
  GtkTargetList *target_list;
  VisuGlView *view;

  if (!self->glScene)
    self->glScene = visu_gl_node_scene_new();
  visu_interactive_setNodeList(interPickObs,
                               visu_gl_node_scene_getNodes(self->glScene));
  g_object_bind_property(self, "interactive",
                         visu_gl_node_scene_getMarks(self->glScene), "interactive",
                         G_BINDING_SYNC_CREATE);

  /* We create the statusinfo area. */
  gtkStatusInfo_createBar(self, self->withToolbar);
  gtk_box_pack_end(GTK_BOX(self), self->info.area, FALSE, FALSE, 0);

  /* The OpenGL area. */
  self->openGLArea = visu_ui_gl_widget_new(TRUE);
  view = visu_gl_node_scene_getGlView(self->glScene);
  g_return_if_fail(VISU_IS_GL_VIEW(view));
  gtk_widget_set_size_request(self->openGLArea,
                              MAX(100, view->window.width),
                              MAX(100, view->window.height));

  gtk_widget_set_can_focus(self->openGLArea, TRUE);
  g_signal_connect_swapped(G_OBJECT(self->openGLArea), "realize",
			   G_CALLBACK(onRealiseEvent), self);
  g_signal_connect_swapped(G_OBJECT(self->openGLArea), "size-allocate",
                           G_CALLBACK(onSizeChangeEvent), self);
  g_signal_connect(G_OBJECT(self->openGLArea), "enter-notify-event",
                   G_CALLBACK(onFocus), (gpointer)self);

  /* DnD */
  gtk_drag_dest_set(self->openGLArea,
                    (GTK_DEST_DEFAULT_ALL), NULL, 0, GDK_ACTION_COPY);
  target_list = gtk_target_list_new(NULL, 0);
  gtk_target_list_add_uri_targets(target_list, TEXT_URI_LIST);
  gtk_target_list_add_text_targets(target_list, TEXT_PLAIN);
  gtk_drag_dest_set_target_list(self->openGLArea, target_list);
  gtk_target_list_unref(target_list);
  g_signal_connect(self->openGLArea, "drag-motion",
		   G_CALLBACK(onDragMotion), NULL);
  g_signal_connect_swapped(self->openGLArea, "drag-data-received",
			   G_CALLBACK(onDropData), self);

  gtk_box_pack_start(GTK_BOX(self), self->openGLArea, TRUE, TRUE, 0);

  gtk_widget_show_all(GTK_WIDGET(self));
}

static void visu_ui_rendering_window_init(VisuUiRenderingWindow *renderingWindow)
{
  guint n;
  GClosure *closure;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: initializing new object (%p).\n",
	      (gpointer)renderingWindow);

  if (!g_type_class_peek(VISU_TYPE_DATA))
    visu_basic_init();
  
  gtk_orientable_set_orientation(GTK_ORIENTABLE(renderingWindow), GTK_ORIENTATION_VERTICAL);
  
    /* Initialisation des curseurs utiles. */
  renderingWindow->cursorPirate  = (GdkCursor*)0;
  renderingWindow->cursorRotate  = (GdkCursor*)0;
  renderingWindow->cursorWatch   = (GdkCursor*)0;
  renderingWindow->cursorPointer = (GdkCursor*)0;
  renderingWindow->cursorGrab    = (GdkCursor*)0;

  /* Set local variables. */
  DBG_fprintf(stderr, "                - setup the local variables.\n");
  renderingWindow->sig_data                 = 0;
  renderingWindow->sig_load                 = 0;
  renderingWindow->nbStatusMessage          = 0;
  renderingWindow->selectedNodeId           = -1;
  renderingWindow->useReducedCoordinates    = _useReducedCoordinates;
  renderingWindow->withToolbar              = TRUE;
  renderingWindow->interactiveEvents        = (GList*)0;
  renderingWindow->inters                   = (GList*)0;
  DBG_fprintf(stderr, "Gtk renderingWindow: bind interactive prop.\n");
  DBG_fprintf(stderr, " | interPickObs has %d ref counts.\n",
              G_OBJECT(interPickObs)->ref_count);
  g_signal_connect_object(VISU_CONFIG_FILE_PARAMETER, "parsed::" FLAG_PARAMETER_RED_COORD,
                          G_CALLBACK(onEntryCoord), (gpointer)renderingWindow, G_CONNECT_SWAPPED);

  /* Binding for the camera menu. */
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: connect the camera bindings.\n");
  renderingWindow->accel = gtk_accel_group_new();
  gtk_accel_map_add_entry(g_intern_static_string(MENU_CAMERA_RESTORE), GDK_KEY_r, 0);
  gtk_accel_map_add_entry(g_intern_static_string(MENU_CAMERA_SAVE), GDK_KEY_s, 0);
  gtk_accel_map_add_entry(g_intern_static_string(MENU_CAMERA_ORIENT),
                          GDK_KEY_v, GDK_CONTROL_MASK);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: connect the camera numbered bindings.\n");
  for (n = 0; n < 9; n++)
    {
      gtk_accel_map_add_entry(g_intern_static_string(cameraAccels[n]),
                              cameraKeys[n], GDK_CONTROL_MASK);
      closure = g_cclosure_new(G_CALLBACK(onCameraAccel),
                               (gpointer)renderingWindow,
                               (GClosureNotify)0);
      gtk_accel_group_connect_by_path(renderingWindow->accel,
                                      g_intern_static_string(cameraAccels[n]),
                                      closure);
      g_closure_unref(closure);
    }

  g_signal_connect(G_OBJECT(interPickObs), "node-selection",
		   G_CALLBACK(minimalPickInfo), (gpointer)renderingWindow);
  g_signal_connect(G_OBJECT(interPickObs), "selection-error",
		   G_CALLBACK(minimalPickError), (gpointer)renderingWindow);
  DBG_fprintf(stderr, " | interPickObs has %d ref counts.\n",
              G_OBJECT(interPickObs)->ref_count);

  if (!_defaultRendering)
    _defaultRendering = renderingWindow;
}

/**
 * visu_ui_rendering_window_new:
 * @width: its desired width ;
 * @height: its desired height ;
 * @withFrame: a boolean ;
 * @withToolBar: a boolean.
 *
 * A #VisuUiRenderingWindow widget is a GtkWindow that have an area for
 * OpenGL drawing and a statusBar with many stuff like action buttons,
 * real status bar for notifications, ... The rendering area can be
 * drawn with a frame or not. With this routine, only the
 * #VisuUiRenderingWindow widget is created.
 *
 * Returns: a newly created #VisuUiRenderingWindow widget.
 */
GtkWidget* visu_ui_rendering_window_new(int width, int height, gboolean withFrame _U_,
                                        gboolean withToolBar)
{
  VisuUiRenderingWindow *renderingWindow;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: create a new VisuUiRenderingWindow object.\n");

  renderingWindow = g_object_new(VISU_TYPE_UI_RENDERING_WINDOW,
                                 "with-toolbar", withToolBar, NULL);
  gtk_widget_set_size_request(renderingWindow->openGLArea, width, height);

  return GTK_WIDGET(renderingWindow);
}

/**
 * visu_ui_rendering_window_new_withGlScene:
 * @scene: a #VisuGlView object.
 * @withToolBar: a boolean.
 *
 * Like visu_ui_rendering_window_new(), but use an already existing @view.
 *
 * Since: 3.8
 *
 * Returns: a newly created #VisuUiRenderingWindow widget.
 **/
GtkWidget* visu_ui_rendering_window_new_withGlScene(VisuGlNodeScene *scene,
                                                    gboolean withToolBar)
{
  return g_object_new(VISU_TYPE_UI_RENDERING_WINDOW, "gl-scene", scene,
                      "with-toolbar", withToolBar, NULL);
}

static gboolean _selectionToTog(GBinding *bind _U_, const GValue *from,
                                GValue *to, gpointer data)
{
  VisuUiRenderingWindow *window = VISU_UI_RENDERING_WINDOW(data);
  VisuNode *node;

  node = (VisuNode*)g_value_get_boxed(from);
  g_value_set_boolean(to, (node) ? visu_gl_node_scene_getMarkActive(window->glScene,
                                                                    node->number) :
                      FALSE);

  return TRUE;
}
static gboolean _selectionToSen(GBinding *bind _U_, const GValue *from,
                                GValue *to, gpointer data _U_)
{
  VisuNode *node;

  node = (VisuNode*)g_value_get_boxed(from);
  g_value_set_boolean(to, (node) ? TRUE : FALSE);

  return TRUE;
}
static void gtkStatusInfo_createBar(VisuUiRenderingWindow *window,
                                    gboolean withToolBar)
{
  GtkWidget *hbox;
  GtkWidget *wd, *image, *ev;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;
  tooltips = gtk_tooltips_new ();
#endif

  window->info.waitId = 0;

  window->info.area = gtk_vbox_new(FALSE, 0);

#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
  window->info.infoBar = gtk_info_bar_new();
  wd = gtk_info_bar_add_button(GTK_INFO_BAR(window->info.infoBar), _("_Close"),
                               GTK_RESPONSE_CLOSE);
  gtk_widget_set_focus_on_click(wd, FALSE);
  gtk_widget_set_no_show_all(window->info.infoBar, TRUE);
  g_signal_connect(G_OBJECT(window->info.infoBar), "response",
                   G_CALLBACK(_onSearchClose), (gpointer)window);
  g_signal_connect(G_OBJECT(window->info.infoBar), "key-press-event",
                   G_CALLBACK(_onSearchEsc), (gpointer)window);
  gtk_box_pack_start(GTK_BOX(window->info.area), window->info.infoBar, FALSE, FALSE, 0);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(gtk_info_bar_get_content_area(GTK_INFO_BAR(window->info.infoBar))), hbox);
  wd = gtk_label_new(_("Toggle highlight for node: "));
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  window->info.searchEntry = gtk_entry_new();
  gtk_entry_set_width_chars(GTK_ENTRY(window->info.searchEntry), 20);
  g_signal_connect(G_OBJECT(window->info.searchEntry), "activate",
                   G_CALLBACK(_onSearchEdited), (gpointer)window->glScene);
  gtk_box_pack_start(GTK_BOX(hbox), window->info.searchEntry, FALSE, FALSE, 0);
  gtk_widget_show_all(hbox);
#endif

  window->info.fileInfoFreeze = FALSE;
  window->info.hboxFileInfo = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(window->info.area), window->info.hboxFileInfo, FALSE, FALSE, 1);

  /* Size info */
  wd = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(window->info.hboxFileInfo), wd, FALSE, FALSE, 5);
  window->info.labelSize = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(window->info.labelSize), TRUE);
  gtk_box_pack_start(GTK_BOX(wd), window->info.labelSize, FALSE, FALSE, 0);

  wd = gtk_vseparator_new();
  gtk_box_pack_start(GTK_BOX(window->info.hboxFileInfo), wd, FALSE, FALSE, 0);

  /* Nb nodes */
  window->info.labelNb = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(window->info.labelNb), TRUE);
  gtk_box_pack_start(GTK_BOX(window->info.hboxFileInfo), window->info.labelNb, FALSE, FALSE, 5);

  wd = gtk_vseparator_new();
  gtk_box_pack_start(GTK_BOX(window->info.hboxFileInfo), wd, FALSE, FALSE, 0);

  /* File info */
  wd = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(window->info.hboxFileInfo), wd, TRUE, TRUE, 0);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 7
  image = gtk_image_new_from_icon_name("dialog-information",
                                       GTK_ICON_SIZE_MENU);
#else
  image = gtk_image_new_from_stock(GTK_STOCK_SAVE,
				   GTK_ICON_SIZE_MENU);
#endif
  gtk_box_pack_start(GTK_BOX(wd), image, FALSE, FALSE, 5);
  window->info.labelFileInfo = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(window->info.labelFileInfo), TRUE);
  gtk_label_set_xalign(GTK_LABEL(window->info.labelFileInfo), 0.);
  gtk_widget_set_margin_start(window->info.labelFileInfo, 5);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 5
  gtk_label_set_ellipsize(GTK_LABEL(window->info.labelFileInfo), PANGO_ELLIPSIZE_END);
#endif
  gtk_box_pack_start(GTK_BOX(wd), window->info.labelFileInfo, TRUE, TRUE, 0);
  ev = gtk_event_box_new();
  gtk_widget_set_tooltip_text(ev, _("Click here to get the list of"
				    " saved camera positions.\n"
				    "Use 's' and 'r' keys to save and"
				    " restore camera settings. <Shift> + 's'"
                                    " remove the current camera from the list."));
  g_signal_connect_swapped(G_OBJECT(ev), "button-release-event",
                           G_CALLBACK(onCameraMenu), (gpointer)window);
  gtk_box_pack_end(GTK_BOX(wd), ev, FALSE, FALSE, 0);
  image = gtk_image_new_from_icon_name("zoom-fit-best",
                                       GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(ev), image);

  /* Status */
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(window->info.area), hbox, FALSE, FALSE, 0);

  /* Handle box for action buttons. */
  if (withToolBar)
    {
      /* The container */
      window->info.hboxTools = gtk_hbox_new(TRUE, 0);
      gtk_box_pack_start(GTK_BOX(hbox), window->info.hboxTools, FALSE, FALSE, 0);

      /* Load button */
      window->info.loadButton = gtk_button_new();
      g_object_set(G_OBJECT(window->info.loadButton), "can-default", FALSE, "can-focus", FALSE,
                   "has-default", FALSE, "has-focus", FALSE, NULL);
      gtk_widget_set_focus_on_click(window->info.loadButton, FALSE);
      gtk_widget_set_tooltip_text(window->info.loadButton,
                                  _("Open Ctrl+o"));
      g_signal_connect_swapped(G_OBJECT(window->info.loadButton), "clicked",
			       G_CALLBACK(onOpen), (gpointer)window);
      image = gtk_image_new_from_icon_name("document-open",
                                           GTK_ICON_SIZE_MENU);
      gtk_container_add(GTK_CONTAINER(window->info.loadButton), image);
      gtk_box_pack_start(GTK_BOX(window->info.hboxTools), window->info.loadButton, FALSE, FALSE, 0);
      /* Refresh button */
      wd = gtk_button_new();
      g_object_bind_property(window, "data", wd, "sensitive", G_BINDING_SYNC_CREATE);
      g_object_set(G_OBJECT(wd), "can-default", FALSE, "can-focus", FALSE,
                   "has-default", FALSE, "has-focus", FALSE, NULL);
      gtk_widget_set_focus_on_click(wd, FALSE);
      gtk_widget_set_tooltip_text(wd,
                                  _("Reload the current file Ctrl+r"));
      g_signal_connect_swapped(G_OBJECT(wd), "clicked",
                               G_CALLBACK(visu_ui_rendering_window_reload), (gpointer)window);
      image = gtk_image_new_from_icon_name("view-refresh",
                                           GTK_ICON_SIZE_MENU);
      gtk_container_add(GTK_CONTAINER(wd), image);
      gtk_box_pack_start(GTK_BOX(window->info.hboxTools), wd, FALSE, FALSE, 0);
      window->info.reloadButton = wd;
      /* Save button */
      window->info.dumpButton = gtk_button_new();
      g_object_bind_property(window, "data",
                             window->info.dumpButton, "sensitive", G_BINDING_SYNC_CREATE);
      g_object_set(G_OBJECT(window->info.dumpButton), "can-default", FALSE, "can-focus", FALSE,
                   "has-default", FALSE, "has-focus", FALSE, NULL);
      gtk_widget_set_focus_on_click(window->info.dumpButton, FALSE);
      gtk_widget_set_tooltip_text(window->info.dumpButton,
                                  _("Export Ctrl+s"));
      g_signal_connect_swapped(G_OBJECT(window->info.dumpButton), "clicked",
                               G_CALLBACK(onExport), (gpointer)window);
      gtk_widget_set_sensitive(window->info.dumpButton, FALSE);
      image = gtk_image_new_from_icon_name("document-save-as",
                                           GTK_ICON_SIZE_MENU);
      gtk_container_add(GTK_CONTAINER(window->info.dumpButton), image);
      gtk_box_pack_start(GTK_BOX(window->info.hboxTools), window->info.dumpButton, FALSE, FALSE, 0);
      /* Auto-raise command panel button */
      window->info.raiseButton = gtk_button_new();
      g_object_set(G_OBJECT(window->info.raiseButton), "can-default", FALSE, "can-focus", FALSE,
                   "has-default", FALSE, "has-focus", FALSE, NULL);
      gtk_widget_set_focus_on_click(window->info.raiseButton, FALSE);
      gtk_widget_set_tooltip_text(window->info.raiseButton,
                                  _("Raise the command panel window.\n"
                                    "  Use <home> as key binding."));
      g_signal_connect_swapped(G_OBJECT(window->info.raiseButton), "clicked",
			       G_CALLBACK(onRaiseButtonClicked), (gpointer)window);
      image = gtk_image_new_from_icon_name("go-up",
                                           GTK_ICON_SIZE_MENU);
      gtk_widget_show(image);
      gtk_container_add(GTK_CONTAINER(window->info.raiseButton), image);
      /* gtk_widget_set_no_show_all(window->info.raiseButton, TRUE); */
      gtk_box_pack_start(GTK_BOX(window->info.hboxTools), window->info.raiseButton, FALSE, FALSE, 0);
    }
  else
    {
      window->info.loadButton = (GtkWidget*)0;
      window->info.dumpButton = (GtkWidget*)0;
      window->info.raiseButton = (GtkWidget*)0;
    }

  /* The status bar or progress */
  window->info.statusInfo = gtk_statusbar_new();
  gtk_box_pack_start(GTK_BOX(hbox), window->info.statusInfo, TRUE, TRUE, 0);
#if GTK_MAJOR_VERSION < 3
  gtk_statusbar_set_has_resize_grip(GTK_STATUSBAR(window->info.statusInfo), FALSE);
#endif
  window->info.statusInfoId = gtk_statusbar_get_context_id(GTK_STATUSBAR(window->info.statusInfo),
                                                    "OpenGL statusbar.");
  gtk_widget_set_no_show_all(window->info.statusInfo, TRUE);
  gtk_widget_show(window->info.statusInfo);
  window->info.progressId = 0;
  window->info.progress = gtk_progress_bar_new();
  g_object_bind_property(window->glScene, "loading-message", window->info.progress, "text", 0);
  gtk_box_pack_start(GTK_BOX(hbox), window->info.progress, TRUE, TRUE, 0);
  gtk_widget_set_no_show_all(window->info.progress, TRUE);
  window->info.cancelButton = gtk_button_new_with_mnemonic(_("_Cancel"));
  gtk_box_pack_start(GTK_BOX(hbox), window->info.cancelButton, FALSE, FALSE, 0);
  gtk_widget_set_no_show_all(window->info.cancelButton, TRUE);
  g_signal_connect(G_OBJECT(window->info.cancelButton), "clicked",
                   G_CALLBACK(onCancelButtonClicked), (gpointer)window);
  window->info.cancel = g_cancellable_new();
  
  /* The interactive button zone. */
  window->info.hboxInteractive = gtk_hbox_new(TRUE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), window->info.hboxInteractive, FALSE, FALSE, 0);

  /* Action button */
  wd = gtk_toggle_button_new();
  gtk_widget_set_sensitive(wd, FALSE);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), FALSE);
  gtk_widget_set_focus_on_click(wd, FALSE);
  g_object_set(G_OBJECT(wd), "can-default", FALSE, "can-focus", FALSE,
	       "has-default", FALSE, "has-focus", FALSE, NULL);
  g_object_bind_property_full(window, "selection", wd, "sensitive", G_BINDING_SYNC_CREATE,
                              _selectionToSen, NULL, (gpointer)0, (GDestroyNotify)0);
  g_object_bind_property_full(window, "selection", wd, "active", G_BINDING_SYNC_CREATE,
                              _selectionToTog, NULL,
                              (gpointer)window, (GDestroyNotify)0);
  g_signal_connect_swapped(G_OBJECT(wd), "clicked",
                           G_CALLBACK(onNodeInfoClicked), (gpointer)window);
  gtk_widget_set_tooltip_text(wd, _("Measure / remove information"
				    " for the selected node."));
  image = gtk_image_new_from_icon_name("document-properties",
                                       GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(wd), image);
  gtk_box_pack_end(GTK_BOX(window->info.hboxInteractive), wd, FALSE, FALSE, 0);

  /* Clean marks button */
  wd = gtk_button_new();
  g_object_bind_property(window->glScene, "data", wd, "sensitive", G_BINDING_SYNC_CREATE);
  gtk_widget_set_focus_on_click(wd, FALSE);
  g_object_set(G_OBJECT(wd), "can-default", FALSE, "can-focus", FALSE,
	       "has-default", FALSE, "has-focus", FALSE, NULL);
  g_signal_connect_swapped(G_OBJECT(wd), "clicked",
                           G_CALLBACK(onMarkClearClicked), (gpointer)window);
  gtk_widget_set_tooltip_text(wd, _("Remove all measurement marks."));
  image = gtk_image_new_from_icon_name("edit-clear",
                                       GTK_ICON_SIZE_MENU);
  gtk_container_add(GTK_CONTAINER(wd), image);
  gtk_box_pack_end(GTK_BOX(window->info.hboxInteractive), wd, FALSE, FALSE, 0);

  if (withToolBar)
    visu_ui_rendering_window_pushMessage(window,
                                         _("Use the 'open' button to render a file."));
}

static gboolean onFocus(GtkWidget *wd _U_, GdkEvent *event _U_, gpointer data)
{
  visu_ui_rendering_window_setCurrent(VISU_UI_RENDERING_WINDOW(data), FALSE);

  return FALSE;
}
static void onSizeChangeEvent(VisuUiRenderingWindow *window,
			      GtkAllocation *allocation)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));
  /* Return if no changes in size (this event is called even the size
     is not really changed but has been negociated. */
  if (!visu_gl_view_setViewport(visu_gl_node_scene_getGlView(window->glScene),
                                allocation->width, allocation->height))
    return;

  _setLabelSize(&window->info, allocation->width, allocation->height);
}

static void onRealiseEvent(GtkWidget *wd, gpointer data _U_)
{
  guint w, h;
  VisuUiRenderingWindow *window;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: initializing OpenGL variable for"
	      "the new OpenGL area.\n");
  window = VISU_UI_RENDERING_WINDOW(wd);

  /* Initialisation des curseurs utiles. */
  window->cursorPirate  = gdk_cursor_new_for_display(gtk_widget_get_display(wd),
                                                     GDK_PIRATE);
  window->cursorRotate  = gdk_cursor_new_for_display(gtk_widget_get_display(wd),
                                                     GDK_EXCHANGE);
  window->cursorWatch   = gdk_cursor_new_for_display(gtk_widget_get_display(wd),
                                                     GDK_WATCH);
  window->cursorPointer = gdk_cursor_new_for_display(gtk_widget_get_display(wd),
                                                     GDK_DOTBOX);
  window->cursorGrab    = gdk_cursor_new_for_display(gtk_widget_get_display(wd),
                                                     GDK_FLEUR);
  window->currentCursor = window->cursorPirate;
  window->refCursor     = window->cursorPirate;
  
  /* If we have a VisuData object attached, we set its size. */
  w = h = 0;
  getOpenGLAreaSize(window, &w, &h);
  visu_gl_view_setViewport(visu_gl_node_scene_getGlView(window->glScene), w, h);
  _setLabelSize(&window->info, w, h);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: changing the cursor.\n");
  /* We set the cursor. */
  gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
			window->currentCursor);  
  DBG_fprintf(stderr, " | cursor OK.\n");

  gtk_widget_grab_focus(wd);
  visu_ui_rendering_window_setCurrent(window, FALSE);

  /* Attach no redraw redraw method. */
  window->sig_load =
    g_signal_connect_object(window->glScene, "notify::loading",
                            G_CALLBACK(onLoadingNotified), window, G_CONNECT_SWAPPED);
  window->sig_data =
    g_signal_connect_object(window->glScene, "notify::data",
                            G_CALLBACK(onDataNotify), window, G_CONNECT_SWAPPED);
  onDataNotify(window, (GParamSpec*)0, window->glScene);

  g_signal_connect_object(G_OBJECT(window->glScene), "notify::dirty",
                          G_CALLBACK(onGlDirty), window, G_CONNECT_SWAPPED);

  /* Set V_Sim OpenGL options. */
  visu_gl_initContext(VISU_GL(window->glScene));
  DBG_fprintf(stderr, " | openGL context OK\n");
  _redraw(window, FALSE);
}

static gboolean onDragMotion(GtkWidget *widget, GdkDragContext *context,
			     gint x _U_, gint y _U_, guint t, gpointer data _U_)
{
  /*   GList *tmpLst; */
  GdkAtom atom;

  /*   DBG_fprintf(stderr, "VisuUiRenderingWindow: Hey ! You dnd move something !\n"); */
  /*   for (tmpLst = context->targets; tmpLst; tmpLst = g_list_next(tmpLst)) */
  /*     { */
  /*       DBG_fprintf(stderr, " | dnd: '%s'\n", */
  /* 		  gdk_atom_name(GDK_POINTER_TO_ATOM(tmpLst->data))); */
  /*     } */
  atom = gtk_drag_dest_find_target(widget, context,
				   gtk_drag_dest_get_target_list(widget));
  if (atom != GDK_NONE)
    gdk_drag_status(context, GDK_ACTION_COPY, t);
  else
    gdk_drag_status(context, 0, t);
  return (atom != GDK_NONE);
}

static void onDropData(VisuUiRenderingWindow *window, GdkDragContext *context,
		       gint x _U_, gint y _U_, GtkSelectionData *data,
		       guint type, guint time _U_, GtkWidget *glArea _U_)
{
  gchar **filenames;
  int i, n, delta, delta2;
  VisuDataLoadable *newData;

  if (window == NULL || context == NULL || data == NULL ||
      gtk_selection_data_get_length(data) < 0)
    return;
  
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 21
  if (gdk_drag_context_get_suggested_action(context) != GDK_ACTION_COPY)
    return;
#else
  if (context->action != GDK_ACTION_COPY)
    return;
#endif

  DBG_fprintf(stderr, " | data: '%d' -> '%s'\n", type,
  	      gtk_selection_data_get_data(data));

  switch (type)
    {
    case TEXT_URI_LIST:
      filenames = gtk_selection_data_get_uris(data);
      break;
    case TEXT_PLAIN:
      filenames = g_strsplit((gchar*)gtk_selection_data_get_data(data), "\n", -1);
      break;
    default:
      filenames = g_malloc(sizeof(gchar*));
      filenames[0] = (gchar*)0;
      break;
    }

  gtk_drag_finish(context, TRUE, TRUE, time);

  for (i = 0, n = 0; filenames[i]; i++)
    {
      g_strstrip(filenames[i]);
      if (filenames[i][0] != '\0')
        n +=1;
    }

  newData = (VisuDataLoadable*)0;
  if (n == 1 && filenames[0][0] != '\0')
    {
      delta = (strncmp("file://", filenames[0], 7))?0:7;
      newData = VISU_DATA_LOADABLE(visu_data_atomic_new(filenames[0] + delta, (VisuDataLoader*)0));
    }
  else if (n == 2)
    {
      delta = (strncmp("file://", filenames[0], 7))?0:7;
      delta2 = (strncmp("file://", filenames[1], 7))?0:7;
      newData = VISU_DATA_LOADABLE(visu_data_spin_new(filenames[0] + delta, filenames[1] + delta2, (VisuDataLoader*)0, (VisuDataLoader*)0));
    }
  else
    visu_ui_raiseWarning(_("Drag and drop"), _("Too many dropped files."), (GtkWindow*)0);
  g_strfreev(filenames);

  visu_ui_rendering_window_loadFile(window, newData, 0);
}

static void _pushNodeInfo(VisuUiRenderingWindow *window, const VisuData *dataObj,
                          const VisuNode *node)
{
  float posSelect[3], posRef[3];
  GString *str;
  VisuElement *ele;
  const gchar *comment;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));
  
  if (window->useReducedCoordinates)
    {
      visu_data_getNodeUserPosition(dataObj, node, posRef);
      visu_box_convertXYZtoBoxCoordinates(visu_boxed_getBox(VISU_BOXED(dataObj)),
                                          posSelect, posRef);
    }
  else
    visu_data_getNodeUserPosition(dataObj, node, posSelect);
  str = g_string_new(_("Selected node number "));
  ele = visu_node_array_getElement(VISU_NODE_ARRAY_CONST(dataObj), node);
  g_string_append_printf(str, "%d - %s (%7.3g;%7.3g;%7.3g)",
                         node->number + 1, ele->name,
                         posSelect[0], posSelect[1], posSelect[2]);
  comment = visu_data_getNodeLabelAt(dataObj, node);
  if (comment)
    g_string_append_printf(str, " %s", comment);
  visu_ui_rendering_window_pushMessage(window, str->str);
  g_string_free(str, TRUE);
}

static void minimalPickInfo(VisuInteractive *inter _U_, VisuInteractivePick pick,
			    VisuData *dataObj, VisuNode *node0,
                            VisuNode *node1, VisuNode *node2 _U_, gpointer data)
{
  float posSelect[3], posRef[3], dist;
  GString *str;
  VisuUiRenderingWindow *window;
  int i;

  window = VISU_UI_RENDERING_WINDOW(data);
  g_return_if_fail(window);

  while (window->nbStatusMessage > 1)
    visu_ui_rendering_window_popMessage(window);
  window->selectedNodeId = -1;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: update the status bar after pick.\n");
  switch (pick)
    {
    case PICK_SELECTED:
      window->selectedNodeId = (gint)node0->number;
      g_object_notify_by_pspec(G_OBJECT(window), properties[SELECTION_PROP]);

      _pushNodeInfo(window, dataObj, node0);
      return;
    case PICK_DISTANCE:
      g_object_notify_by_pspec(G_OBJECT(window), properties[SELECTION_PROP]);
      /* Have a ref and a selected node, then distance informations is computed. */
      visu_data_getNodePosition(dataObj, node0, posSelect);
      visu_data_getNodePosition(dataObj, node1, posRef);
      str = g_string_new(_("Distance between nodes "));
      dist = 0.;
      for (i = 0; i < 3; i++)
	dist += (posRef[i] - posSelect[i]) * (posRef[i] - posSelect[i]);
      dist = sqrt(dist);
      g_string_append_printf(str, _("%d and %d : %7.3f"),
			     node1->number + 1, node0->number + 1, dist);
      visu_ui_rendering_window_pushMessage(window, str->str);
      g_string_free(str, TRUE);
      return;
    case PICK_REFERENCE_1:
      g_object_notify_by_pspec(G_OBJECT(window), properties[SELECTION_PROP]);
      visu_ui_rendering_window_pushMessage(window, _("<shift> right-click on"
					    " background to unset reference."));
      return;
    case PICK_REFERENCE_2:
      g_object_notify_by_pspec(G_OBJECT(window), properties[SELECTION_PROP]);
      visu_ui_rendering_window_pushMessage(window,
				  _("<ctrl> right-click on"
				    " background to unset second reference."));
      return;
    default:
      return;
    }
}
static void minimalPickError(VisuInteractive *inter _U_,
			     VisuInteractivePickError error, gpointer data)
{
  VisuUiRenderingWindow *window;

  window = VISU_UI_RENDERING_WINDOW(data);

  switch (error)
    {
    case PICK_ERROR_NO_SELECTION:
      window->selectedNodeId = -1;
      g_object_notify_by_pspec(G_OBJECT(window), properties[SELECTION_PROP]);

      visu_ui_rendering_window_pushMessage(window, _("No node has been selected."));
      return;
    case PICK_ERROR_SAME_REF:
      visu_ui_rendering_window_pushMessage(window, _("Picked node is already used"
					    " as a reference."));
      return;
    case PICK_ERROR_REF1:
      visu_ui_rendering_window_pushMessage(window, _("Can't pick a second reference"
					    " without any first one"
					    " (use <shift> right-click)."));
      return;
    case PICK_ERROR_REF2:
      visu_ui_rendering_window_pushMessage(window, _("Can't remove first reference"
					    " before removing the second one."));
      return;
    default:
      return;
    }
}

static gulong addInteractiveEventListeners(VisuUiRenderingWindow *window,
					   InteractiveEventsId id)
{
  GList* ptList;
  InteractiveEvents *event;
  gboolean found;

  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), (gulong)0);

  found  = FALSE;
  for (ptList = window->interactiveEvents; ptList && !found;
       ptList = g_list_next(ptList))
    {
      event = (InteractiveEvents*)ptList->data;
      if (event->id == id)
	found = TRUE;
    }
  if (found)
    return (gulong)0;

  event = g_malloc(sizeof(InteractiveEvents));
  event->id = id;
  switch (id)
    {
    case event_button_press:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
						   "button-press-event",
						   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    case event_button_release:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
						   "button-release-event",
						   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    case event_motion_notify:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
                                                   "motion-notify-event",
                                                   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    case event_key_press:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
                                                   "key-press-event",
                                                   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    case event_key_release:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
                                                   "key-release-event",
                                                   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    case event_scroll:
      event->callbackId = g_signal_connect_swapped(G_OBJECT(window->openGLArea),
                                                   "scroll-event",
                                                   G_CALLBACK(OnEvent), (gpointer)window);
      break;
    default:
      g_warning("Unknown event to add.");
      g_free(event);
      return (gulong)0;
    };
  window->interactiveEvents = g_list_prepend(window->interactiveEvents,
                                             (gpointer)event);
  return event->callbackId;
}
static void setInteractiveType(VisuUiRenderingWindow *window,
			       VisuInteractiveId type)
{
  VisuUiRenderingWindowClass *klass;
  InteractiveEvents *event;
  gulong id;
  GList *ptList;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));  
  klass = VISU_UI_RENDERING_WINDOW_CLASS(G_OBJECT_GET_CLASS(window));
  g_return_if_fail(klass);

  /* We set the cursors. */
  switch (type)
    {
    case interactive_observe:
    case interactive_measureAndObserve:
      if (gtk_widget_get_realized(GTK_WIDGET(window)))
        gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                              window->cursorRotate);
      window->currentCursor = window->cursorRotate;
      window->refCursor = window->cursorRotate;
      break;
    case interactive_measure:
    case interactive_pick:
    case interactive_move:
    case interactive_mark:
      if (gtk_widget_get_realized(GTK_WIDGET(window)))
        gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                              window->cursorPointer);
      window->currentCursor = window->cursorPointer;
      window->refCursor = window->cursorPointer;
      break;
    case interactive_none:
      if (gtk_widget_get_realized(GTK_WIDGET(window)))
        gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                              window->cursorPirate);
      window->currentCursor = window->cursorPirate;
      window->refCursor = window->cursorPirate;
      break;
    case interactive_drag:
      if (gtk_widget_get_realized(GTK_WIDGET(window)))
        gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                              window->cursorGrab);
      window->currentCursor = window->cursorGrab;
      window->refCursor = window->cursorGrab;
      break;
    }
  /* We set the listeners. */
  if (type != interactive_none)
    {
      DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: setup signals.\n");
      id = addInteractiveEventListeners(window, event_button_release);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
      id = addInteractiveEventListeners(window, event_button_press);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
      id = addInteractiveEventListeners(window, event_motion_notify);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
      id = addInteractiveEventListeners(window, event_key_press);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
      id = addInteractiveEventListeners(window, event_key_release);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
      id = addInteractiveEventListeners(window, event_scroll);
      if (id)
	DBG_fprintf(stderr, "  | connecting %ld signal.\n", id);
    }
  else
    {
      DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: removing interactive listeners.\n");
      for (ptList = window->interactiveEvents; ptList;
           ptList = g_list_next(ptList))
	{
          event = (InteractiveEvents*)ptList->data;
	  DBG_fprintf(stderr, "  | disconnecting %d signal.\n", event->id);
	  g_signal_handler_disconnect(G_OBJECT(window->openGLArea),
                                      event->callbackId);
	  g_free(ptList->data);
	}
      if (window->interactiveEvents)
	g_list_free(window->interactiveEvents);
      window->interactiveEvents = (GList*)0;
    }
}
/**
 * visu_ui_rendering_window_pushInteractive:
 * @window: a #VisuUiRenderingWindow object.
 * @inter: a #VisuInteractive object.
 *
 * It adds @inter to the stack of interactive sessions currently
 * attached to @window and launch it.
 *
 * Since: 3.6
 */
void visu_ui_rendering_window_pushInteractive(VisuUiRenderingWindow *window,
                                              VisuInteractive *inter)
{
  VisuInteractiveId type;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window) &&
		   VISU_IS_INTERACTIVE(inter));

  DBG_fprintf(stderr, " | interPickObs (%p) has %d ref counts.\n",
              (gpointer)interPickObs, G_OBJECT(interPickObs)->ref_count);

  type = visu_interactive_getType(inter);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: push a new interactive"
	      " session (%d / %d).\n", type, g_list_length(window->inters));
  DBG_fprintf(stderr, " | inter (%p) has %d ref counts.\n",
              (gpointer)inter, G_OBJECT(inter)->ref_count);
  window->inters = g_list_prepend(window->inters, inter);
  g_object_ref(G_OBJECT(inter));
  g_object_notify_by_pspec(G_OBJECT(window), properties[INTER_PROP]);
  DBG_fprintf(stderr, " | inter (%p) has %d ref counts.\n",
              (gpointer)inter, G_OBJECT(inter)->ref_count);
  if (visu_interactive_getMessage(inter))
    visu_ui_rendering_window_pushMessage(window, visu_interactive_getMessage(inter));

  visu_interactive_setNodeList(inter, visu_gl_node_scene_getNodes(window->glScene));

  setInteractiveType(window, type);
}
/**
 * visu_ui_rendering_window_popInteractive:
 * @window: a #VisuUiRenderingWindow object.
 * @inter: a #VisuInteractive object.
 *
 * It removes @inter from the stack of interactive sessions currently
 * attached to @window. If @inter was first on the stack, the next
 * session is launched.
 *
 * Since: 3.6
 */
void visu_ui_rendering_window_popInteractive(VisuUiRenderingWindow *window,
                                             VisuInteractive *inter)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  visu_interactive_setNodeList(inter, (VisuGlExtNodes*)0);

  if (!window->inters || window->inters->data != inter)
    return;

  window->inters = g_list_remove(window->inters, inter);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: pop an old interactive"
	      " session (%d).\n", g_list_length(window->inters));
  g_object_notify_by_pspec(G_OBJECT(window), properties[INTER_PROP]);
  if (visu_interactive_getMessage(inter))
    visu_ui_rendering_window_popMessage(window);

  DBG_fprintf(stderr, " | inter has %d ref counts.\n",
              G_OBJECT(inter)->ref_count);
  g_object_unref(G_OBJECT(inter));
  
  if (window->inters)
    setInteractiveType(window, visu_interactive_getType(VISU_INTERACTIVE(window->inters->data)));
  else
    setInteractiveType(window, interactive_none);
}

static gboolean OnEvent(VisuUiRenderingWindow *window, GdkEvent *event,
                        gpointer user_data _U_)
{
  ToolSimplifiedEvents ev;
  GList *cameras, *head;

  if (!window->inters)
    return FALSE;

  if (!tool_simplified_events_new_fromGdk(&ev, event))
    return FALSE;
  
  if (ev.button || ev.letter != '\0' || ev.specialKey != Key_None)
    {
      gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                            window->cursorWatch);
      visu_interactive_handleEvent(VISU_INTERACTIVE(window->inters->data),
                                   visu_gl_node_scene_getGlView(window->glScene), &ev);
      gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
                            window->currentCursor);
    }

  /* Specific handlings. */
  if (ev.letter != '\0')
    {
      switch (ev.letter)
        {
        case 'r':
          /* If any camera, print a message. */
          visu_interactive_getSavedCameras(VISU_INTERACTIVE(window->inters->data),
                                           &cameras, &head);
          if (cameras)
            visu_ui_rendering_window_pushMessage
              (window, _("Restore saved camera position."));
          else
            visu_ui_rendering_window_pushMessage
              (window, _("No saved camera. Use 's' to save one."));
          break;
        case 's':
          if (!ev.shiftMod)
            visu_ui_rendering_window_pushMessage
              (window, _("Save current camera position."));
          else
            visu_ui_rendering_window_pushMessage
              (window, _("Pop current camera position."));
          break;
        case 'x':
          visu_ui_rendering_window_pushMessage
            (window, _("Align camera with X box axis."));
          break;
        case 'y':
          visu_ui_rendering_window_pushMessage
            (window, _("Align camera with Y box axis."));
          break;
        case 'z':
          visu_ui_rendering_window_pushMessage
            (window, _("Align camera with Z box axis."));
          break;
        default:
          break;
        }
#if GLIB_MINOR_VERSION > 13
      g_timeout_add_seconds(3, timeOutPopMessage, (gpointer)window);
#else
      g_timeout_add(3000, timeOutPopMessage, (gpointer)window);
#endif
    }

  if (event->type == GDK_KEY_PRESS && !ev.letter && ev.specialKey == Key_None &&
      (event->key.keyval == GDK_KEY_Shift_L || event->key.keyval == GDK_KEY_Shift_R))
    {
      gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
			    window->cursorGrab);
      window->currentCursor = window->cursorGrab;
    }
  else if (event->type == GDK_KEY_RELEASE && !ev.letter && ev.specialKey == Key_None &&
           (event->key.keyval == GDK_KEY_Shift_L || event->key.keyval == GDK_KEY_Shift_R))
    {
      gdk_window_set_cursor(gtk_widget_get_window(window->openGLArea),
			    window->refCursor);
      window->currentCursor = window->refCursor;
    }

  return FALSE;
}

static gboolean timeOutPopMessage(gpointer data)
{
  visu_ui_rendering_window_popMessage(VISU_UI_RENDERING_WINDOW(data));

  return FALSE;
}

/**
 * visu_ui_rendering_window_pushMessage:
 * @window: a valid #VisuUiRenderingWindow object ;
 * @message: an UTF8 string to print on the status bar.
 *
 * Use this method to add some informations on the status bar.
 */
void visu_ui_rendering_window_pushMessage(VisuUiRenderingWindow *window, const gchar *message)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  gtk_statusbar_push(GTK_STATUSBAR(window->info.statusInfo),
		     window->info.statusInfoId, message);
  window->nbStatusMessage += 1;
}
/**
 * visu_ui_rendering_window_popMessage:
 * @window: a valid #VisuUiRenderingWindow object.
 *
 * Remove the last message.
 */
void visu_ui_rendering_window_popMessage(VisuUiRenderingWindow *window)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  gtk_statusbar_pop(GTK_STATUSBAR(window->info.statusInfo),
		    window->info.statusInfoId);
  window->nbStatusMessage -= 1;
}
static void getOpenGLAreaSize(VisuUiRenderingWindow *window,
			      guint *width, guint *height)
{
  GtkAllocation alloc;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window) && width && height);

  gtk_widget_get_allocation(window->openGLArea, &alloc);
  *width = alloc.width;
  *height = alloc.height;
}
static void onDataNotify(VisuUiRenderingWindow *window,
                         GParamSpec *pspec _U_, VisuGlNodeScene *scene)
{
  VisuData *data;

  DBG_fprintf(stderr, "##### VisuData association to a window #####\n");
  data = visu_gl_node_scene_getData(scene);

  /* Reset the statusbar informations and other GUI parameters. */
  while (window->nbStatusMessage > 1)
    visu_ui_rendering_window_popMessage(window);
  if (!window->inters && data)
    visu_ui_rendering_window_pushInteractive(window, interPickObs);
  else if (!data)
    visu_ui_rendering_window_popInteractive(window, interPickObs);

  g_clear_object(&window->info.bind_nb);
  g_clear_object(&window->info.bind_info);
  if (data)
    {
      DBG_fprintf(stderr, " | current data has %d ref counts.\n",
                  G_OBJECT(data)->ref_count);

      /* Attach the default redraw method. */
      visu_ui_gl_widget_setModel(VISU_UI_GL_WIDGET(window->openGLArea),
                                 VISU_GL_EXT_SET(window->glScene));
      window->info.bind_nb =
        g_object_bind_property_full(data, "n-nodes", window->info.labelNb, "label",
                                    G_BINDING_SYNC_CREATE, toNNodeLabel,
                                    (GBindingTransformFunc)0,
                                    (gpointer)0, (GDestroyNotify)0);
      window->info.bind_info =
        g_object_bind_property_full(data, "description", window->info.labelFileInfo, "label",
                                    G_BINDING_SYNC_CREATE, toFileInfo,
                                    (GBindingTransformFunc)0,
                                    (gpointer)0, (GDestroyNotify)0);
    }
  else
    {
      /* Attach the default redraw method. */
      visu_ui_gl_widget_setModel(VISU_UI_GL_WIDGET(window->openGLArea),
                                 (VisuGlExtSet*)0);
    }

  g_object_notify_by_pspec(G_OBJECT(window), properties[DATA_PROP]);
  g_object_notify_by_pspec(G_OBJECT(window), properties[LABEL_PROP]);

  if (!data)
    {
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
      gtk_widget_hide(GTK_WIDGET(window->info.infoBar));
#endif
      gtk_widget_grab_focus(window->openGLArea);
    }
}
/**
 * visu_ui_rendering_window_getGlScene:
 * @window: a valid #VisuUiRenderingWindow object.
 *
 * This method is used to get the #VisuGlExtSet attached to the
 * rendering window.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): the #VisuGlNodeScene attached to the @window
 * or NULL on error.
 **/
VisuGlNodeScene* visu_ui_rendering_window_getGlScene(VisuUiRenderingWindow *window)
{
  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), (VisuGlNodeScene*)0);
  return window->glScene;
}
/**
 * visu_ui_rendering_window_getAccelGroup:
 * @window: a #VisuUiRenderingWindow object.
 *
 * Retrieve the accelerator group of @window.
 *
 * Since: 3.7
 *
 * Returns: (transfer none): the #GtkAccelGroup object of @window.
 **/
GtkAccelGroup* visu_ui_rendering_window_getAccelGroup(VisuUiRenderingWindow *window)
{
  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), (GtkAccelGroup*)0);
  return window->accel;
}

/***************************/
/* GtkStatusInfo functions */
/***************************/
static void _setLabelSize(GtkInfoArea *info, gint width, gint height)
{
  gchar *str;

  g_return_if_fail(info);

  if (info->fileInfoFreeze)
    return;

  str = g_strdup_printf("<span size=\"smaller\"><b>%s</b> %dx%d</span>", _("Size:"), width, height);
  gtk_label_set_markup(GTK_LABEL(info->labelSize), str);
  g_free(str);
}
static void onNodeInfoClicked(VisuUiRenderingWindow *window, GtkToggleButton *button)
{
  if (window->selectedNodeId >= 0)
    visu_gl_node_scene_setMark(window->glScene, window->selectedNodeId,
                               gtk_toggle_button_get_active(button));
}
static void onMarkClearClicked(VisuUiRenderingWindow *window, GtkButton *button _U_)
{
  visu_gl_node_scene_removeMarks(window->glScene);
}
static gboolean onCameraMenu(VisuUiRenderingWindow *window, GdkEventButton *event,
			     GtkEventBox *ev _U_)
{
  GtkWidget *wd;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: click on the camera menu.\n");
  wd = buildCameraMenu(window);
  if (!wd)
    return TRUE;

  g_signal_connect(G_OBJECT(wd), "selection-done",
		   G_CALLBACK(onCameraMenuSelected), (gpointer)window);

  gtk_widget_show_all(wd);
#if GTK_MAJOR_VERSION < 2 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION < 22)
  gtk_menu_popup(GTK_MENU(wd), NULL, NULL, NULL, NULL, 
		 1, event->time);
#else
  gtk_menu_popup_at_pointer(GTK_MENU(wd), (const GdkEvent*)event);
#endif

  return TRUE;
}
static GtkWidget* buildCameraMenu(VisuUiRenderingWindow *window)
{
  GtkWidget *menu, *item;
  gchar *lbl;
  GList *cameras, *head, *tmpLst, *rCameras;
  VisuGlCamera *current;
  guint n;

  if (!window->inters)
    return (GtkWidget*)0;

  /* All camera. */
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: get the cameras.\n");
  visu_interactive_getSavedCameras(VISU_INTERACTIVE(window->inters->data),
				  &cameras, &head);
  /*   if (!cameras) */
  /*     return (GtkWidget*)0; */

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: build the menu.\n");
  menu = gtk_menu_new();
  gtk_menu_set_accel_group(GTK_MENU(menu), window->accel);
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: create the camera menu %p.\n",
	      (gpointer)menu);

  /* Set a title. */
  item = gtk_menu_item_new_with_label(_("Camera menu (saved in 'v_sim.par'):"));
  gtk_widget_set_sensitive(item, FALSE);
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  item = gtk_separator_menu_item_new();
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Put the current camera. */
  current = &visu_gl_node_scene_getGlView(window->glScene)->camera;
  lbl = g_strdup_printf(_("save current camera:\n"
			  "(\316\270 %6.1f\302\260 ; \317\206 %6.1f\302\260 ; \317\211 %6.1f\302\260) "
			  "dx %4.1f dy %4.1f"),
			current->theta, current->phi, current->omega,
			current->xs, current->ys);
  item = gtk_menu_item_new_with_label(lbl);
  g_free(lbl);
  g_signal_connect(G_OBJECT(item), "activate",
                   G_CALLBACK(onCameraMenuCurrentClicked), window);
  gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item),
                               g_intern_static_string(MENU_CAMERA_SAVE));
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Put an option to open the view selector. */
  item = gtk_menu_item_new_with_label(_("select precisely a camera view"));
  g_signal_connect(G_OBJECT(item), "activate",
                   G_CALLBACK(onCameraMenuOrientationClicked), window);
  gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item),
                               g_intern_static_string(MENU_CAMERA_ORIENT));
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  /* Separator. */
  item = gtk_separator_menu_item_new();
  gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
  if (!cameras)
    {
      item = gtk_menu_item_new_with_label(_("No saved camera. Use 's' to save one."));
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }
  else
    {
      item = gtk_menu_item_new_with_label(_("List of saved cameras:"));
      gtk_widget_set_sensitive(item, FALSE);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }

  rCameras = g_list_reverse(g_list_copy(cameras));
  for (tmpLst = rCameras, n = 0; tmpLst; tmpLst = g_list_next(tmpLst), n+= 1)
    {
      current = (VisuGlCamera*)tmpLst->data;
      lbl = g_strdup_printf(_("(\316\270 %6.1f\302\260 ; \317\206 %6.1f\302\260 ; \317\211 %6.1f\302\260) "
			      "dx %4.1f dy %4.1f"),
			    current->theta, current->phi, current->omega,
			    current->xs, current->ys);
      item = gtk_menu_item_new_with_label(lbl);
      DBG_fprintf(stderr, " | add menu item %p (%p)\n", (gpointer)item, (gpointer)window);
      g_free(lbl);
      if (current == (VisuGlCamera*)head->data)
        gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item),
                                     g_intern_static_string(MENU_CAMERA_RESTORE));
      else if (n < 9)
        gtk_menu_item_set_accel_path(GTK_MENU_ITEM(item),
                                     g_intern_static_string(cameraAccels[n]));
      g_signal_connect(G_OBJECT(item), "activate",
		       G_CALLBACK(onCameraMenuClicked), window);
      g_object_set_data(G_OBJECT(item), "Camera", (gpointer)current);
      gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
    }
  g_list_free(rCameras);
  return menu;
}
static void _setCamera(VisuUiRenderingWindow *window, VisuGlCamera *camera)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));
  if (!window->inters || !camera)
    return;
  
  visu_interactive_pushSavedCamera(VISU_INTERACTIVE(window->inters->data), camera);
  visu_gl_node_scene_setGlCamera(window->glScene, camera);
}
static gboolean onCameraAccel(GtkAccelGroup *accel _U_, GObject *obj,
                              guint key, GdkModifierType mod _U_, gpointer data)
{
  VisuUiRenderingWindow *window;
  GList *cameras, *head, *rCameras;
  VisuGlCamera *camera;

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: get accelerator for object %p.\n",
              (gpointer)obj);
  window = VISU_UI_RENDERING_WINDOW(data);
  /* All camera. */
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: get the cameras.\n");
  visu_interactive_getSavedCameras(VISU_INTERACTIVE(window->inters->data),
                                   &cameras, &head);
  if (!cameras)
    return TRUE;

  rCameras = g_list_reverse(g_list_copy(cameras));
  camera = g_list_nth_data(rCameras, key - GDK_KEY_1);
  g_list_free(rCameras);
  _setCamera(window, camera);

  return TRUE;
}
static void onCameraMenuSelected(GtkMenuShell *menushell, gpointer user_data _U_)
{
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: destroy the camera menu %p.\n",
	      (gpointer)menushell);
  gtk_widget_destroy(GTK_WIDGET(menushell));
}
static void onCameraMenuClicked(GtkMenuItem *menuitem, gpointer user_data)
{
  _setCamera(VISU_UI_RENDERING_WINDOW(user_data),
             (VisuGlCamera*)g_object_get_data(G_OBJECT(menuitem), "Camera"));
}
static void onCameraMenuCurrentClicked(GtkMenuItem *menuitem _U_, gpointer user_data)
{
  visu_interactive_pushSavedCamera(VISU_INTERACTIVE(VISU_UI_RENDERING_WINDOW(user_data)->inters->data),
                                   &visu_gl_node_scene_getGlView(VISU_UI_RENDERING_WINDOW(user_data)->glScene)->camera);
}
static void onOrientationChanged(VisuUiOrientationChooser *orientationChooser,
				 gpointer data)
{
  float values[2];

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: orientation changed.\n");
  visu_ui_orientation_chooser_getAnglesValues(orientationChooser, values);

  g_object_set(visu_gl_node_scene_getGlView(VISU_UI_RENDERING_WINDOW(data)->glScene),
               "theta", values[0], "phi", values[1], NULL);
}
static void _orientationChooser(VisuUiRenderingWindow *window)
{
  GtkWidget *orientationChooser;
  VisuGlView *view;
  VisuGlCamera *current;
  float values[2];

  view = visu_gl_node_scene_getGlView(window->glScene);
  orientationChooser = visu_ui_orientation_chooser_new
    (VISU_UI_ORIENTATION_DIRECTION, TRUE, VISU_BOXED(view), NULL);
  gtk_window_set_modal(GTK_WINDOW(orientationChooser), TRUE);
  current = &view->camera;
  values[0] = current->theta;
  values[1] = current->phi;
  visu_ui_orientation_chooser_setAnglesValues(VISU_UI_ORIENTATION_CHOOSER(orientationChooser),
                                     values);
  g_signal_connect(G_OBJECT(orientationChooser), "values-changed",
                   G_CALLBACK(onOrientationChanged), window);
  gtk_widget_show(orientationChooser);
  
  switch (gtk_dialog_run(GTK_DIALOG(orientationChooser)))
    {
    case GTK_RESPONSE_ACCEPT:
      DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: accept changings on orientation.\n");
      break;
    default:
      DBG_fprintf(stderr, "Gtk Observe: reset values on orientation.\n");
      g_object_set(view, "theta", values[0], "phi", values[1], NULL);
    }
  DBG_fprintf(stderr, "Gtk Observe: orientation object destroy.\n");
  gtk_widget_destroy(orientationChooser);
}
static void onCameraMenuOrientationClicked(GtkMenuItem *menuitem _U_, gpointer data)
{
  _orientationChooser(VISU_UI_RENDERING_WINDOW(data));
}

void visu_ui_rendering_window_lockUI(VisuUiRenderingWindow *window, gboolean status)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  gtk_widget_set_sensitive(window->info.hboxTools, !status);
  gtk_widget_set_sensitive(window->info.hboxInteractive, !status);
  gtk_widget_set_sensitive(window->info.hboxFileInfo, !status);
}

struct _load_struct
{
  VisuUiRenderingWindow *window;
  VisuDataLoadable *data;
  guint iSet;
};
static void stopProgress(VisuUiRenderingWindow *window)
{
  if (window->info.progressId)
    g_source_remove(window->info.progressId);
  window->info.progressId = 0;

  gtk_widget_hide(window->info.progress);
  gtk_widget_hide(window->info.cancelButton);
  gtk_widget_show(window->info.statusInfo);

  visu_ui_rendering_window_lockUI(window, FALSE);
}
static gboolean popProgress(gpointer data)
{
  VisuUiRenderingWindow *window;

  window = VISU_UI_RENDERING_WINDOW(data);

  gtk_progress_bar_pulse(GTK_PROGRESS_BAR(window->info.progress));

  return TRUE;
}
static gboolean showProgress(gpointer data)
{
  VisuUiRenderingWindow *window;

  window = VISU_UI_RENDERING_WINDOW(data);

  visu_ui_rendering_window_lockUI(window, TRUE);
  gtk_widget_show(window->info.progress);
  gtk_widget_show(window->info.cancelButton);
  gtk_widget_hide(window->info.statusInfo);

  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(window->info.progress),
                            _("Loading file..."));

  if (window->info.progressId)
    g_source_remove(window->info.progressId);
  window->info.progressId = g_timeout_add(100, popProgress, data);
  window->info.waitId = 0;
  
  return FALSE;
}
static void onCancelButtonClicked(GtkButton *button _U_, gpointer data)
{
  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(VISU_UI_RENDERING_WINDOW(data)->info.progress),
                            _("Cancellation request, waiting for reply..."));
  g_cancellable_cancel(VISU_UI_RENDERING_WINDOW(data)->info.cancel);
}

static void onLoadingNotified(VisuUiRenderingWindow *window,
                              GParamSpec *pspec _U_, VisuGlNodeScene *scene)
{
  gboolean loading;

  g_object_get(scene, "loading", &loading, NULL);
  if (loading)
    {
      if (window->info.waitId)
        return;
      window->info.waitId = g_timeout_add(500, showProgress, window);
    }
  else
    {
      if (!window->info.waitId)
        return;
      g_source_remove(window->info.waitId);
      window->info.waitId = 0;
      stopProgress(window);
    }
}

static gboolean _loadFile(gpointer data)
{
  GError *error;
  gboolean res;
  VisuUiRenderingWindow *window;
  struct _load_struct *pt;

  /* obj is the new object and main the panel that handle the
     loading. */
  pt = (struct _load_struct*)data;
  window = pt->window;

  DBG_fprintf(stderr, "VisuUiRenderingWindow: loading process ... %p points to"
	      " previous VisuData.\n", (gpointer)visu_gl_node_scene_getData(window->glScene));
  DBG_fprintf(stderr, " | loading data has %d ref counts.\n",
              G_OBJECT(pt->data)->ref_count);
  
  error = (GError*)0;
  res = visu_gl_node_scene_loadData(window->glScene, pt->data,
                                    pt->iSet, window->info.cancel, &error);
  DBG_fprintf(stderr, " | loaded data has %d ref counts.\n",
              G_OBJECT(pt->data)->ref_count);

  if (!res)
    {
      if (error)
	{
	  visu_ui_raiseWarning(_("Loading a file"), error->message, NULL);
	  g_error_free(error);
	}
      else
	g_warning("No error message.");
    }
  else
    visu_ui_storeRecent(visu_data_loadable_getFilename(pt->data, 0));

  /* We release a ref on obj, since
     visu_ui_rendering_window_setData has increased it. */
  g_object_unref(pt->data);
  g_free(pt);
	 
  return FALSE;
}
/**
 * visu_ui_rendering_window_loadFile:
 * @window: a valid #VisuUiRenderingWindow object.
 * @data: (transfer full) (allow-none): the #VisuData to be loaded.
 * @iSet: the id of @data to load.
 *
 * This method calls the general function to load data from file
 * and deals with errors with gtkDialogs. The filenames must have
 * already been set into @data using visu_data_addFile().
 */
void visu_ui_rendering_window_loadFile(VisuUiRenderingWindow *window, VisuDataLoadable *data, guint iSet)
{
  struct _load_struct *pt;

  pt = g_malloc(sizeof(struct _load_struct));
  pt->window = window;
  pt->data   = data;
  pt->iSet   = iSet;
  g_idle_add(_loadFile, pt);
}
/**
 * visu_ui_rendering_window_open:
 * @window: the window the file will by rendered on ;
 * @parent: (allow-none): the parent window for the filechooser dialog.
 *
 * Do as if the load button has been pushed, i.e. open a filechooser
 * dialog on the @parent window, and load the resulting file,
 * refreshing the view if necessary.
 */
void visu_ui_rendering_window_open(VisuUiRenderingWindow *window, GtkWindow *parent)
{
  GtkWidget *chooser;
  VisuDataLoadable *newData;
  gchar *directory;
  const gchar *curDir;
  VisuUiMain *ui;

  ui = visu_ui_main_class_getCurrentPanel();
  chooser = visu_ui_data_chooser_new(parent);
  if (ui && (curDir = visu_ui_main_getLastOpenDirectory(ui)))
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(chooser), curDir);
  newData = visu_ui_data_chooser_run(VISU_UI_DATA_CHOOSER(chooser));
  if (ui)
    {
      directory = gtk_file_chooser_get_current_folder(GTK_FILE_CHOOSER(chooser));
      visu_ui_main_setLastOpenDirectory(ui, directory, VISU_UI_DIR_FILE);
      g_free(directory);
    }
  gtk_widget_destroy(GTK_WIDGET(chooser));

  if (!newData)
    return;

  DBG_fprintf(stderr, "Gtk RenderingWindow: open file ref count %d.\n",
              G_OBJECT(newData)->ref_count);

  visu_ui_rendering_window_setCurrent(window, TRUE);
  if (newData && !visu_node_array_getNNodes(VISU_NODE_ARRAY(newData)))
    visu_ui_rendering_window_loadFile(window, g_object_ref(newData), 0);
  else if (newData)
    {
      visu_gl_node_scene_setData(window->glScene, VISU_DATA(newData));
      visu_ui_storeRecent(visu_data_loadable_getFilename(newData, 0));
    }
  g_object_unref(newData);
}

static void onOpen(VisuUiRenderingWindow *window)
{
  visu_ui_rendering_window_open(window, (GtkWindow*)0);
}
static gboolean _reload(VisuUiRenderingWindow *window)
{
  GError *error;
  VisuData *dataObj;

  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), FALSE);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: reload current file.\n");
  dataObj = visu_gl_node_scene_getData(window->glScene);
  if (!VISU_IS_DATA_LOADABLE(dataObj))
    return FALSE;

  error = (GError*)0;
  if (!visu_data_loadable_reload(VISU_DATA_LOADABLE(dataObj),
                                 window->info.cancel, &error) && error)
    {
      visu_ui_raiseWarning(_("Reloading file"), error->message, NULL);
      g_error_free(error);
    }
  return FALSE;
}
/**
 * visu_ui_rendering_window_reload:
 * @window: a #VisuUiRenderingWindow object.
 *
 * This routines reloads the current #VisuData object by rereading it
 * on disk. If there is no current #VisuData object, it reports an
 * error.
 *
 * Since: 3.7
 */
void visu_ui_rendering_window_reload(VisuUiRenderingWindow *window)
{
  g_idle_add((GSourceFunc)_reload, window);
}

static gboolean toNNodeLabel(GBinding *binding _U_, const GValue *source_value,
                             GValue *target_value, gpointer data _U_)
{
  gchar *str;

  if (g_value_get_uint(source_value) > 0)
    str = g_strdup_printf("<span size=\"smaller\"><b>%s</b> %d</span>",
                          _("Nb nodes:"), g_value_get_uint(source_value));
  else
    str = g_strdup_printf("<span size=\"smaller\">%s</span>", GTK_STATUSINFO_NONB);
  g_value_take_string(target_value, str);
  return TRUE;
}
static gboolean toFileInfo(GBinding *binding _U_, const GValue *source_value,
                           GValue *target_value, gpointer data _U_)
{
  const gchar *descr;

  descr = g_value_get_string(source_value);
  if (!descr || !descr[0])
    descr = GTK_STATUSINFO_NOFILEINFO;
  
  g_value_take_string(target_value,
                      g_strdup_printf("<span size=\"smaller\">%s</span>", descr));
  return TRUE;
}
void updateDumpProgressBar(gpointer data)
{
  gdouble val;

  g_return_if_fail(GTK_PROGRESS_BAR(data));

  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(data), _("Saving image..."));
  val = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(data));
  if (val + 0.01 <= 1.0 && val >= 0.)
    gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(data), val + 0.01);
  visu_ui_wait();
}

static void onExport(VisuUiRenderingWindow *window)
{
  GtkWidget *dump;
  char *filename;
  VisuDump *format;
  gboolean res;
  GError *error;
  GdkCursor *cursorWatch;
  GtkProgressBar *dumpBar;
  VisuGlView *view;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  view = visu_gl_node_scene_getGlView(window->glScene);
  dump = visu_ui_dump_dialog_new(visu_gl_node_scene_getData(window->glScene),
                                 (GtkWindow*)0, (const gchar*)0,
                                 view->window.width, view->window.height);
  if (gtk_dialog_run(GTK_DIALOG(dump)) != GTK_RESPONSE_ACCEPT)
    {
      gtk_widget_destroy(dump);
      return;
    }

  filename = visu_ui_dump_dialog_getFilename(VISU_UI_DUMP_DIALOG(dump));
  format = visu_ui_dump_dialog_getType(VISU_UI_DUMP_DIALOG(dump));
  g_return_if_fail(format && filename);

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: dump image to file '%s' (format : %s)\n",
	      filename, tool_file_format_getName(TOOL_FILE_FORMAT(format)));

  cursorWatch = gdk_cursor_new_for_display(gtk_widget_get_display(dump), GDK_WATCH);
  dumpBar = visu_ui_dump_dialog_getProgressBar(VISU_UI_DUMP_DIALOG(dump));
  visu_ui_dump_dialog_start(VISU_UI_DUMP_DIALOG(dump));
  gtk_progress_bar_set_fraction(dumpBar, 0.);
  if (VISU_IS_DUMP_SCENE(format))
    gtk_progress_bar_set_text(dumpBar,
                              _("Waiting for generating image in memory..."));
  visu_ui_wait();
  gdk_window_set_cursor(gtk_widget_get_window(dump), cursorWatch);

  error = (GError*)0;
  DBG_fprintf(stderr, " | starting dump.\n");
  res = visu_gl_node_scene_dump(window->glScene, format, filename,
                                visu_ui_dump_dialog_getWidth(VISU_UI_DUMP_DIALOG(dump)),
                                visu_ui_dump_dialog_getHeight(VISU_UI_DUMP_DIALOG(dump)),
                                updateDumpProgressBar, (gpointer)dumpBar, &error);
  if (res)
    /* Save file as recent. */
    visu_ui_storeRecent(filename);

  if (!res && error)
    visu_ui_raiseWarning(_("Saving a file"), error->message, (GtkWindow*)0);
  gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(dump)), NULL);
  if (error)
    g_error_free(error);
  DBG_fprintf(stderr, " | release UI resources.\n");

  gtk_widget_destroy(dump);

  visu_ui_gl_widget_setCurrent(VISU_UI_GL_WIDGET(window->openGLArea), TRUE);

  g_object_unref(cursorWatch);
}

static void onRaiseButtonClicked(VisuUiRenderingWindow *window, gpointer user_data _U_)
{
  g_signal_emit(G_OBJECT(window), _signals[SHOW_MAIN_PANEL_SIGNAL],
		0 /* details */, NULL);
}

static void _redraw(VisuUiRenderingWindow *window, gboolean forceRedraw)
{
  VisuUiRenderingWindow *current;

  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: redraw to visible %d.\n",
              gtk_widget_get_visible(GTK_WIDGET(window)));
  if (!gtk_widget_get_visible(GTK_WIDGET(window)))
    return;
  current = VISU_UI_RENDERING_WINDOW_GET_CLASS(window)->redrawWidget;
  DBG_fprintf(stderr, "Gtk VisuUiRenderingWindow: redraw if current %p %p.\n",
              (gpointer)current, (gpointer)window);
  if (!current || current != window)
    return;

  if (!visu_gl_getImmediate(VISU_GL(window->glScene)) && !forceRedraw)
    {
      DBG_fprintf(stderr, "Redraw rejected since drawing is deferred and not forced.\n");
      return;
    }
  DBG_fprintf(stderr, "Redraw accepted let's go...\n");

  gtk_widget_queue_draw(window->openGLArea);
}
/**
 * visu_ui_rendering_window_setCurrent:
 * @window: a valid #VisuUiRenderingWindow object ;
 * @force: a boolean.
 *
 * Set the OpenGL area as the current rendering area. If @force is TRUE
 * then the context is switched whatever buffered value.
 *
 * Since: 3.5
 */
void visu_ui_rendering_window_setCurrent(VisuUiRenderingWindow *window, gboolean force)
{
  g_return_if_fail(VISU_IS_UI_RENDERING_WINDOW(window));

  visu_ui_gl_widget_setCurrent(VISU_UI_GL_WIDGET(window->openGLArea), force);
  VISU_UI_RENDERING_WINDOW_GET_CLASS(window)->redrawWidget = window;
}

static void onGlDirty(VisuUiRenderingWindow *window)
{
  DBG_fprintf(stderr, "Gtk RenderingWindow: Gl is dirty, redrawing.\n");
  _redraw(window, FALSE);
}
static void _onSearch(VisuUiRenderingWindow *window)
{
  if (!visu_gl_node_scene_getData(window->glScene))
    return;

#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
  gtk_widget_show(window->info.infoBar);
  gtk_widget_grab_focus(window->info.searchEntry);
  gtk_entry_set_icon_from_icon_name(GTK_ENTRY(window->info.searchEntry),
                                    GTK_ENTRY_ICON_SECONDARY, NULL);
  gtk_entry_set_text(GTK_ENTRY(window->info.searchEntry), "");
#endif
}
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
static void _onSearchClose(GtkInfoBar *bar, gint response, gpointer data)
{
  if (response == GTK_RESPONSE_CLOSE)
    {
      gtk_widget_hide(GTK_WIDGET(bar));
      gtk_widget_grab_focus(VISU_UI_RENDERING_WINDOW(data)->openGLArea);
    }
}
static void _onSearchEdited(GtkEntry *entry, gpointer data)
{
  VisuGlNodeScene *scene = VISU_GL_NODE_SCENE(data);
  gint i;
  gchar *end;
  const gchar *val;
  VisuNode *node;

  gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY, NULL);
  val = gtk_entry_get_text(entry);
  DBG_fprintf(stderr, "Gtk RenderingWindow: search on '%s'.\n", val);
  i = strtol(val, &end, 10);
  if (end == val || i <= 0)
    {
      gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY,
                                        "dialog-warning");
      return;
    }
  i -= 1;
  node = visu_node_array_getFromId(VISU_NODE_ARRAY(visu_gl_node_scene_getData(scene)), i);
  if (!node)
    {
      gtk_entry_set_icon_from_icon_name(entry, GTK_ENTRY_ICON_SECONDARY,
                                        "dialog-warning");
      return;
    }
  visu_interactive_highlight(interPickObs, i);
}
static gboolean _onSearchEsc(GtkWidget *widget, GdkEventKey *event, gpointer data _U_)
{
  if (event->keyval == GDK_KEY_Escape)
    {
      gtk_info_bar_response(GTK_INFO_BAR(widget), GTK_RESPONSE_CLOSE);
      return TRUE;
    }
  return FALSE;
}
#endif



/**
 * visu_ui_rendering_window_setDisplayCoordinatesInReduce:
 * @window: a #VisuUiRenderingWindow object.
 * @status: a boolean.
 *
 * If set, the coordinates of picked nodes are displayed in reduced values.
 *
 * Returns: TRUE if the value has been changed.
 *
 * Since: 3.6
 */
gboolean visu_ui_rendering_window_setDisplayCoordinatesInReduce(VisuUiRenderingWindow *window,
                                                                gboolean status)
{
  VisuData *dataObj;
  
  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), FALSE);
  
  if (status == window->useReducedCoordinates)
    return FALSE;
  
  window->useReducedCoordinates = status;
  g_object_notify_by_pspec(G_OBJECT(window), properties[COORD_PROP]);

  dataObj = visu_gl_node_scene_getData(window->glScene);
  if (window->selectedNodeId >= 0 && dataObj)
    {
      visu_ui_rendering_window_popMessage(window);
      _pushNodeInfo(window, dataObj,
                    visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
                                              window->selectedNodeId));
    }
  
  return TRUE;
}
/**
 * visu_ui_rendering_window_getDisplayCoordinatesInReduce:
 * @window: a #VisuUiRenderingWindow object.
 *
 * Picked nodes have their coordinates displayed in the status bar of the rendering
 * window. This methods retrieves if they are printed in reduced values or not.
 *
 * Returns: TRUE if the coordinates are displayed in reduced values.
 *
 * Since: 3.6
 */
gboolean visu_ui_rendering_window_getDisplayCoordinatesInReduce(const VisuUiRenderingWindow *window)
{
  g_return_val_if_fail(VISU_IS_UI_RENDERING_WINDOW(window), FALSE);

  return window->useReducedCoordinates;
}
/**
 * visu_ui_rendering_window_class_finalize: (skip)
 *
 * Free memory related to the #VisuInteractive object used 
 *
 * Since: 3.8
 **/
void visu_ui_rendering_window_class_finalize(void)
{
  if (interPickObs)
    g_object_unref(interPickObs);
  interPickObs = (VisuInteractive*)0;
}

/*************************************/
/* Routines related to config files. */
/*************************************/
static void onEntryCoord(VisuUiRenderingWindow *window)
{
  visu_ui_rendering_window_setDisplayCoordinatesInReduce(window, _useReducedCoordinates);
}
static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  if (!_defaultRendering)
    return;
  
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_RED_COORD);
  g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_RED_COORD,
			 _defaultRendering->useReducedCoordinates);
}
