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

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

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

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

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

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

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

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

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

#include <glib.h>
#include <GL/gl.h>
#include <math.h>
#include <string.h>

#include "marks.h"
#include <visu_elements.h>
#include <visu_dataloadable.h>
#include <visu_configFile.h>
#include <visu_extension.h>
#include <opengl.h>
#include <openGLFunctions/view.h>
#include <openGLFunctions/text.h>
#include <openGLFunctions/objectList.h>
#include <openGLFunctions/interactive.h>
#include <renderingMethods/elementAtomic.h>


/**
 * SECTION:marks
 * @short_description: Draw features on nodes, like measurement marks
 * or highlights.
 *
 * <para>#VisuGlExtMarks is used to store a set of mark on a list of
 * nodes. A mark can be a distance measurement, an angle measurement
 * or an highlight. The measurement marks are automatically updated by
 * listening to the #VisuInteractive::node-selection signal. On the
 * contrary, highlights are set, unset or toggled using
 * visu_gl_ext_marks_setHighlight().</para>
 * <para>In addition, #VisuGlExtMarks can be export to or loaded from an
 * XML file thanks to visu_gl_ext_marks_exportXMLFile() and visu_gl_ext_marks_parseXMLFile().</para>
 *
 * Since: 3.6
 */

typedef enum
  {
    MARK_BIG_SQUARE,
    MARK_SMALL_SQUARE,
    MARK_HIGHLIGHT,
    MARK_DISTANCE,
    MARK_ANGLE,
    MARK_LINE
  } VisuMarkType;

struct MarkInfo_struct
{
  /* Mark type. */
  VisuMarkType type;

  /* Id used to address the VisuNode when the
     pointer node1 has changed, for example when a new VisuData
     is loaded with the same geometry and we want to apply this mark. */
  guint idNode1;
  /* Idem for a second node. */
  guint idNode2;
  /* Idem for a third node. */
  guint idNode3;

  /* List of nodes */
  GList *nodes;
  guint size;
  GLfloat *coord;
  guint *nIds;
};

/**
 * VisuNodeInfo:
 * @id: an id;
 * @dist: a float.
 *
 * Some data.
 *
 * Since: 3.5
 */
typedef struct _VisuNodeInfo VisuNodeInfo;
struct _VisuNodeInfo
{
  guint id;
  float dist;
};

typedef struct _ExtNode ExtNode;
struct _ExtNode
{
  VisuGlExt parent;

  VisuGlExtMarks *marks;
};

struct _VisuGlExtMarksPrivate
{
  gboolean dispose_has_run;
  ExtNode *extNode;

  /* A reference on the #VisuData and #VisuGlView it refers to. */
  VisuGlView *view;
  gulong cameraAngles, cameraPosition, cameraZoom, cameraPersp;
  VisuNodeArrayRenderer *renderer;
  gulong nodePosition, nodeRender, nodeMaterial, nodePopulation, siz_sig, dat_sig;

  /* Interactive object to get measurement signals. */
  VisuInteractive *inter;
  gulong nodeSelection;

  /* A list of MarkInfo_struct elements. */
  GList *storedMarks;
  guint hasCameraMarks;
  gboolean drawValues;
  GArray *cachedHighlighted;

  /* Miscellaneous. */
  float infoRange;
  VisuGlExtMarksHidingModes hidingMode;
};


enum
  {
    MEASUREMENT_CHANGE_SIGNAL,
    NB_SIGNAL
  };
static guint signals[NB_SIGNAL];
enum
  {
    PROP_0,
    VIEW_PROP,
    INTER_PROP,
    HIDING_PROP,
    HIGHLIGHT_PROP,
    N_PROP
  };
static GParamSpec *properties[N_PROP];

/* Local variables. */
#define MARK_BIG_SQUARE_SIZE 8
#define MARK_SMALL_SQUARE_SIZE 4
static guchar markBigSquare[MARK_BIG_SQUARE_SIZE *
			    MARK_BIG_SQUARE_SIZE * 4];
static guchar markSmallSquare[MARK_SMALL_SQUARE_SIZE *
			      MARK_SMALL_SQUARE_SIZE * 4];
static float highlightFactor = 1.25f;

/* Local methods. */
static ExtNode* ext_node_new(VisuGlExtMarks *marks);
static struct MarkInfo_struct* markNew(VisuMarkType type);
static void markFree(struct MarkInfo_struct *mark);
static VisuMarkType markRemove(VisuGlExtMarks *marks, GList *rmList);
static void removeDot(VisuGlExtMarks *marks, guint nodeId, VisuMarkType type);
static void addDot(VisuGlExtMarks *marks, guint nodeId, VisuMarkType type);
static gboolean toggleDistance(VisuGlExtMarks *marks, guint nodeRefId,
			       guint nodeId, gboolean set);
static gboolean toggleAngle(VisuGlExtMarks *marks, guint nodeRefId,
			    guint nodeRef2Id, guint nodeId, gboolean set);
static gboolean toggleHighlight(VisuGlExtMarks *marks, guint nodeId,
				VisuGlExtMarksStatus status, gboolean *finalStatus);
static gboolean setInformation(VisuGlExtMarks *marks, VisuData *dataObj, guint node);
static void putMark(VisuData *data, guint nodeRefId, guint nodeId, VisuMarkType type);
static void drawMarkDot(VisuData *data, guint nodeId, VisuMarkType type);
static void drawMarkHighlight(VisuData *data, guint nodeId,
                              VisuGlView *view, VisuNodeArrayRenderer *renderer);
static void drawMarkDistance(VisuData *data, guint nodeRefId,
			     guint nodeId, VisuMarkType type);
static void drawMarkAngle(VisuData *data, guint nodeRefId, guint nodeRef2Id,
			  guint nodeId, guint id);
static void drawMarkLine(VisuData *data _U_, GLfloat *coord, guint size);

static void visu_gl_ext_marks_dispose(GObject *obj);
static void visu_gl_ext_marks_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec);
static void visu_gl_ext_marks_set_property(GObject* obj, guint property_id,
                                           const GValue *value,
                                           GParamSpec *pspec);
static void visu_gl_ext_marks_rebuild(VisuGlExt *ext);
static void visu_gl_ext_marks_draw(VisuGlExt *ext);
static gboolean visu_gl_ext_marks_setGlView(VisuGlExt *ext, VisuGlView *view);
static void visu_node_masker_interface_init(VisuNodeMaskerInterface *iface);
static gboolean _maskApply(const VisuNodeMasker *self, VisuNodeArray *array);
static VisuNodeInfo* _getDistanceList(VisuData *data, guint nodeId, float *minVal);

/* Local callbacks. */
static void _setDirty(VisuGlExtMarks *marks);
static void onNotifyData(VisuGlExtMarks *marks, GParamSpec *pspec,
                         VisuNodeArrayRenderer *renderer);
static void _populationDecrease(VisuGlExtMarks *marks, GArray *nodes);
/* static void updateListOnCameraChange(VisuGlView *view, gpointer data); */
static gboolean onNodeSelection(VisuInteractive *inter, VisuInteractivePick kind,
                                VisuData *dataObje, VisuNode *node1,
                                VisuNode *node2, VisuNode *node3, gpointer data);

/* V_Sim resources and paramaters. */
#define FLAG_RESOURCE_FACTOR "highlight_radiusFactor"
#define DESC_RESOURCE_FACTOR "Give the factor for the highlight radius ; one float (> 1.)"
static void exportResources(GString *data, VisuData *dataObj);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtMarks, visu_gl_ext_marks, VISU_TYPE_GL_EXT,
                        G_ADD_PRIVATE(VisuGlExtMarks)
                        G_IMPLEMENT_INTERFACE(VISU_TYPE_NODE_MASKER,
                                              visu_node_masker_interface_init))

static void visu_gl_ext_marks_class_init(VisuGlExtMarksClass *klass)
{
  int i;
  float rg[2] = {1.01f, G_MAXFLOAT};
  VisuConfigFileEntry *conf;

  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_marks_dispose;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_marks_get_property;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_marks_set_property;
  VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_marks_rebuild;
  VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_marks_draw;
  VISU_GL_EXT_CLASS(klass)->setGlView = visu_gl_ext_marks_setGlView;

  DBG_fprintf(stderr, "Visu Marks: installing signals.\n");
  /**
   * VisuGlExtMarks::measurementChanged:
   * @marks: the object emitting the signal.
   * @data: the #VisuData the measurement is done on.
   *
   * The list of measurements has been changed.
   *
   * Since: 3.6
   */
  signals[MEASUREMENT_CHANGE_SIGNAL] = 
    g_signal_new("measurementChanged", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT,
                 G_TYPE_NONE, 1, VISU_TYPE_DATA);

  /**
   * VisuGlExtMarks::gl-view:
   *
   * Store to which #VisuGlView the marks labels are aligned with.
   *
   * 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_READWRITE);
  /**
   * VisuGlExtMarks::interactive:
   *
   * Store which #VisuInteractive the marks should react on.
   *
   * Since: 3.8
   */
  properties[INTER_PROP] = g_param_spec_object("interactive", "Interactive",
                                               "Interactive the marks react on",
                                               VISU_TYPE_INTERACTIVE, G_PARAM_READWRITE);
  /**
   * VisuGlExtMarks::hiding-mode:
   *
   * Mode used to hide nodes.
   *
   * Since: 3.8
   */
  properties[HIDING_PROP] = g_param_spec_uint("hiding-mode", "Hiding mode",
                                              "nodes hiding property",
                                              0, HIDE_NON_HIGHLIGHT, HIDE_NONE,
                                              G_PARAM_READWRITE);
  /**
   * VisuGlExtMarks::highlight:
   *
   * Set of node ids with highlight.
   *
   * Since: 3.8
   */
  properties[HIGHLIGHT_PROP] = g_param_spec_boxed("highlight", "Highlight",
                                                  "node ids with highlight mark.",
                                                  G_TYPE_ARRAY, G_PARAM_READWRITE);

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

  /* Create the marks. */
  for (i = 0; i < MARK_BIG_SQUARE_SIZE * MARK_BIG_SQUARE_SIZE * 4 ; i++)
    markBigSquare[i] = 0xff;
  for (i = 0; i < MARK_SMALL_SQUARE_SIZE * MARK_SMALL_SQUARE_SIZE * 4; i++)
    markSmallSquare[i] = 0xff;

  conf = visu_config_file_addFloatArrayEntry(VISU_CONFIG_FILE_RESOURCE,
                                             FLAG_RESOURCE_FACTOR,
                                             DESC_RESOURCE_FACTOR,
                                             1, &highlightFactor, rg, FALSE);
  visu_config_file_entry_setVersion(conf, 3.6f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_RESOURCE,
                                     exportResources);
}
static void visu_node_masker_interface_init(VisuNodeMaskerInterface *iface)
{
  iface->apply = _maskApply;
}

static void visu_gl_ext_marks_init(VisuGlExtMarks *marks)
{
  DBG_fprintf(stderr, "Visu Marks: create a new object, %p.\n",
	      (gpointer)marks);

  marks->priv = visu_gl_ext_marks_get_instance_private(marks);
  marks->priv->dispose_has_run = FALSE;

  marks->priv->renderer           = (VisuNodeArrayRenderer*)0;
  marks->priv->view               = (VisuGlView*)0;
  marks->priv->storedMarks        = (GList*)0;
  marks->priv->cachedHighlighted  = (GArray*)0;
  marks->priv->hasCameraMarks     = 0;
  marks->priv->drawValues         = TRUE;
  marks->priv->infoRange          = 1.44f;
  marks->priv->extNode            = (ExtNode*)0;
  marks->priv->inter              = (VisuInteractive*)0;
  marks->priv->nodeSelection      = 0;
  marks->priv->hidingMode         = HIDE_NONE;
}

static void visu_gl_ext_marks_dispose(GObject *obj)
{
  VisuGlExtMarks *marks;

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

  marks = VISU_GL_EXT_MARKS(obj);

  if (marks->priv->dispose_has_run)
    return;
  marks->priv->dispose_has_run = TRUE;

  visu_gl_ext_marks_setDataRenderer(marks, (VisuNodeArrayRenderer*)0);
  DBG_fprintf(stderr, " | release VisuGlView.\n");
  visu_gl_ext_marks_setGlView(VISU_GL_EXT(marks), (VisuGlView*)0);
  DBG_fprintf(stderr, " | free internal VisuGlExt.\n");
  g_object_unref(G_OBJECT(marks->priv->extNode));

  DBG_fprintf(stderr, " | unconnect signals.\n");
  visu_gl_ext_marks_setInteractive(marks, (VisuInteractive*)0);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, " | chain up.\n");
  G_OBJECT_CLASS(visu_gl_ext_marks_parent_class)->dispose(obj);
  DBG_fprintf(stderr, " | OK.\n");
}
static void visu_gl_ext_marks_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec)
{
  VisuGlExtMarks *self = VISU_GL_EXT_MARKS(obj);

  DBG_fprintf(stderr, "Extension Marks: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case VIEW_PROP:
      g_value_set_object(value, self->priv->view);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->view);
      break;
    case INTER_PROP:
      g_value_set_object(value, self->priv->inter);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->inter);
      break;
    case HIDING_PROP:
      g_value_set_uint(value, self->priv->hidingMode);
      DBG_fprintf(stderr, "%d.\n", self->priv->hidingMode);
      break;
    case HIGHLIGHT_PROP:
      g_value_set_boxed(value, visu_gl_ext_marks_getHighlighted(self));
      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_gl_ext_marks_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec)
{
  VisuGlExtMarks *self = VISU_GL_EXT_MARKS(obj);

  DBG_fprintf(stderr, "Extension Marks: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case VIEW_PROP:
      visu_gl_ext_marks_setGlView(VISU_GL_EXT(obj),
                                  VISU_GL_VIEW(g_value_get_object(value)));
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->view);
      break;
    case INTER_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(value));
      visu_gl_ext_marks_setInteractive
        (self, VISU_INTERACTIVE(g_value_get_object(value)));
      break;
    case HIDING_PROP:
      DBG_fprintf(stderr, "%d.\n", g_value_get_uint(value));
      visu_gl_ext_marks_setHidingMode(self, g_value_get_uint(value));
      break;
    case HIGHLIGHT_PROP:
      DBG_fprintf(stderr, "%p.\n", g_value_get_boxed(value));
      visu_gl_ext_marks_setHighlight(self, g_value_get_boxed(value), MARKS_STATUS_SET);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}

/**
 * visu_gl_ext_marks_new:
 * @name: (allow-none): a possible name for the #VisuGlExt.
 *
 * Create a new #VisuGlExtMarks object. Make it listen to
 * #VisuInteractive::node-selection signal to update itself
 * automatically.
 *
 * Returns: the newly created object.
 */
VisuGlExtMarks* visu_gl_ext_marks_new(const gchar *name)
{
  char *name_ = "MarksInv";
  char *description = _("Draw some marks on element in video inverse.");
  VisuGlExtMarks *marks;

  marks = VISU_GL_EXT_MARKS(g_object_new(VISU_TYPE_GL_EXT_MARKS,
                                         "name", (name)?name:name_, "label", _(name),
                                         "description", description, "nGlObj", 1,
                                         "priority", VISU_GL_EXT_PRIORITY_LAST - 1,
                                         "saveState", TRUE, NULL));

  marks->priv->extNode = ext_node_new(marks);

  return marks;
}

/**
 * visu_gl_ext_marks_getInternalList:
 * @marks: a #VisuGlExtMarks object.
 *
 * Return an additional list used internaly.
 *
 * Since: 3.7
 *
 * Returns: (transfer none): a #VisuGlExt object.
 **/
VisuGlExt* visu_gl_ext_marks_getInternalList(VisuGlExtMarks *marks)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), (VisuGlExt*)0);

  return VISU_GL_EXT(marks->priv->extNode);
}

static struct MarkInfo_struct* markNew(VisuMarkType type)
{
  struct MarkInfo_struct *mark;

  mark          = g_malloc(sizeof(struct MarkInfo_struct));
  mark->type    = type;
  mark->idNode1 = -1;
  mark->idNode2 = -1;
  mark->idNode3 = -1;

  mark->nodes   = (GList*)0;
  mark->size    = 0;
  mark->coord   = (GLfloat*)0;
  mark->nIds    = (guint*)0;

  return (struct MarkInfo_struct*)mark;
}
static void markFree(struct MarkInfo_struct *mark)
{
  if (mark->nodes)
    g_list_free(mark->nodes);

  if (mark->coord)
    g_free(mark->coord);
  if (mark->nIds)
    g_free(mark->nIds);

  g_free(mark);
}
static VisuMarkType markRemove(VisuGlExtMarks *marks, GList *rmList)
{
  struct MarkInfo_struct *mark;
  gboolean hasCameraMarks;
  VisuMarkType type;

  mark = (struct MarkInfo_struct*)rmList->data;
  DBG_fprintf(stderr, "Visu Marks: remove mark (%d %d %d).\n",
	      mark->idNode1, mark->idNode2, mark->idNode3);
  hasCameraMarks = (marks->priv->hasCameraMarks > 0);
  type = mark->type;
  if (mark->type == MARK_LINE)
    marks->priv->hasCameraMarks -= 1;
  if (mark->type == MARK_HIGHLIGHT && marks->priv->cachedHighlighted)
    {
      g_array_unref(marks->priv->cachedHighlighted);
      marks->priv->cachedHighlighted = (GArray*)0;
    }
  markFree(mark);
  marks->priv->storedMarks = g_list_delete_link(marks->priv->storedMarks, rmList);
  
  if (hasCameraMarks && marks->priv->hasCameraMarks == 0)
    {
      g_signal_handler_disconnect(G_OBJECT(marks->priv->view), marks->priv->cameraAngles);
      g_signal_handler_disconnect(G_OBJECT(marks->priv->view), marks->priv->cameraPosition);
      g_signal_handler_disconnect(G_OBJECT(marks->priv->view), marks->priv->cameraZoom);
      g_signal_handler_disconnect(G_OBJECT(marks->priv->view), marks->priv->cameraPersp);
    }

  return type;
}

/**
 * visu_gl_ext_marks_setInteractive:
 * @marks: a #VisuGlExtMarks object.
 * @inter: (transfer full) (allow-none): a #VisuInteractive object.
 *
 * Listen to #VisuInteractive::node-selection signal to update @marks.
 *
 * Since: 3.7
 **/
void visu_gl_ext_marks_setInteractive(VisuGlExtMarks *marks, VisuInteractive *inter)
{
  g_return_if_fail(VISU_IS_GL_EXT_MARKS(marks));

  DBG_fprintf(stderr, "Visu Marks: set interactive object.\n");
  if (marks->priv->inter)
    {
      DBG_fprintf(stderr, " | old interactive (%p) has %d ref counts.\n",
                  (gpointer)marks->priv->inter, G_OBJECT(marks->priv->inter)->ref_count);
      g_signal_handler_disconnect(G_OBJECT(marks->priv->inter), marks->priv->nodeSelection);
      g_object_unref(marks->priv->inter);
    }
  if (inter)
    {
      g_object_ref(inter);
      marks->priv->nodeSelection = g_signal_connect(G_OBJECT(inter), "node-selection",
                                              G_CALLBACK(onNodeSelection), (gpointer)marks);
      DBG_fprintf(stderr, " | new interactive (%p) has %d ref counts.\n",
                  (gpointer)inter, G_OBJECT(inter)->ref_count);
    }
  else
    marks->priv->nodeSelection = 0;
      
  marks->priv->inter = inter;
  g_object_notify_by_pspec(G_OBJECT(marks), properties[INTER_PROP]);
}
static gboolean visu_gl_ext_marks_setGlView(VisuGlExt *ext, VisuGlView *view)
{
  VisuGlExtMarks *marks = VISU_GL_EXT_MARKS(ext);

  DBG_fprintf(stderr, " | marks has %d ref counts.\n",
              G_OBJECT(marks)->ref_count);
  /* We attach a new VisuGlView object. */
  if (marks->priv->view == view)
    return FALSE;

  /* We unref the previous VisuView and disconnect signals. */
  if (marks->priv->view)
    {
      /* if (marks->priv->hasCameraMarks > 0) */
      /*   { */
      /*     g_signal_handler_disconnect(marks->priv->view, marks->priv->cameraAngles); */
      /*     g_signal_handler_disconnect(marks->priv->view, marks->priv->cameraPosition); */
      /*     g_signal_handler_disconnect(marks->priv->view, marks->priv->cameraZoom); */
      /*     g_signal_handler_disconnect(marks->priv->view, marks->priv->cameraPersp); */
      /*   } */
      g_object_unref(marks->priv->view);
    }
  marks->priv->view = view;
  if (marks->priv->view)
    {
      /* if (marks->priv->hasCameraMarks > 0) */
      /*   { */
      /*     marks->priv->cameraAngles = */
      /*       g_signal_connect(marks->priv->view, "ThetaPhiOmegaChanged", */
      /*                        G_CALLBACK(updateListOnCameraChange), */
      /*                        (gpointer)marks); */
      /*     marks->priv->cameraPosition = */
      /*       g_signal_connect(marks->priv->view, "XsYsChanged", */
      /*                        G_CALLBACK(updateListOnCameraChange), */
      /*                        (gpointer)marks); */
      /*     marks->priv->cameraZoom = */
      /*       g_signal_connect(marks->priv->view, "GrossChanged", */
      /*                        G_CALLBACK(updateListOnCameraChange), */
      /*                        (gpointer)marks); */
      /*     marks->priv->cameraPersp = */
      /*       g_signal_connect(marks->priv->view, "PerspChanged", */
      /*                        G_CALLBACK(updateListOnCameraChange), */
      /*                        (gpointer)marks);   */
      /*   } */
      /* We ref the new given view. */
      g_object_ref(marks->priv->view);
    }
  g_object_notify_by_pspec(G_OBJECT(marks), properties[VIEW_PROP]);
  return TRUE;
}
static void _setData(VisuGlExtMarks *marks, VisuNodeArray *array)
{
  GList *list, *rmList;
  struct MarkInfo_struct*mark;
  gboolean remove;
  gboolean invDirty, dirDirty;
  VisuMarkType type;

  invDirty = dirDirty = FALSE;
  /* Mark list as dirty because of position changed, if necessary. */
  for (list = marks->priv->storedMarks; list; list = g_list_next(list))
    if (((struct MarkInfo_struct*)list->data)->type == MARK_HIGHLIGHT)
      dirDirty = TRUE;
    else
      invDirty = TRUE;

  /* We update the list of nodes with marks. */
  if (array)
    {
      /* Try to match previous marks on new VisuData. */
      list = marks->priv->storedMarks;
      while(list)
	{
	  mark = (struct MarkInfo_struct*)list->data;
	  DBG_fprintf(stderr, " | old mark %p of type %d.\n",
		      (gpointer)mark, mark->type);
	  remove = FALSE;
	  rmList = list;
	  /* We remove mark if one of the node can't be matched. */
	  switch (mark->type)
	    {
	    case MARK_BIG_SQUARE:
	    case MARK_SMALL_SQUARE:
	    case MARK_HIGHLIGHT:
	      remove = remove || !visu_node_array_getFromId(array, mark->idNode1);
	      break;
	    case MARK_DISTANCE:
	      remove = remove ||
		!visu_node_array_getFromId(array, mark->idNode1) ||
		!visu_node_array_getFromId(array, mark->idNode2);
	      break;
	    case MARK_ANGLE:
	      remove = remove ||
		!visu_node_array_getFromId(array, mark->idNode1) ||
		!visu_node_array_getFromId(array, mark->idNode2) ||
		!visu_node_array_getFromId(array, mark->idNode3);
	      break;
	    default:
	      g_warning("TODO implementation required.");
	    }
	  list = g_list_next(list);
	  if (remove)
	    {
	      DBG_fprintf(stderr, " | delete old mark %p of type %d.\n",
			  (gpointer)mark, mark->type);
	      type = markRemove(marks, rmList);
              if (type == MARK_HIGHLIGHT)
                dirDirty = TRUE;
              else
                invDirty = TRUE;
	    }
	}
    }
  else
    {
      invDirty = dirDirty = (g_list_length(marks->priv->storedMarks) > 0);
      for (list = marks->priv->storedMarks; list; list = g_list_next(list))
	markFree((struct MarkInfo_struct*)list->data);
      g_list_free(marks->priv->storedMarks);
      marks->priv->storedMarks   = (GList*)0;
      if (marks->priv->cachedHighlighted)
        g_array_unref(marks->priv->cachedHighlighted);
      marks->priv->cachedHighlighted = (GArray*)0;
    }
  DBG_fprintf(stderr, " | New mark list is pointing to %p.\n",
	      (gpointer)marks->priv->storedMarks);
  /* Signal changes. */
  g_object_notify_by_pspec(G_OBJECT(marks), properties[HIGHLIGHT_PROP]);
  g_signal_emit(G_OBJECT(marks), signals[MEASUREMENT_CHANGE_SIGNAL], 0, array);
  if (invDirty)
    visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
  if (dirDirty)
    visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
}
/**
 * visu_gl_ext_marks_setDataRenderer:
 * @marks: a #VisuGlExtMarks object.
 * @renderer: a #VisuNodeArrayRenderer object.
 *
 * Attach the given @marks to @data. @marks will be updated if @data
 * is changed and internal list of marks is updated with the new nodes
 * of @data.
 */
void visu_gl_ext_marks_setDataRenderer(VisuGlExtMarks *marks, VisuNodeArrayRenderer *renderer)
{
  DBG_fprintf(stderr, "Visu Marks: set new VisuNodeArrayRenderer %p (%p).\n",
	      (gpointer)renderer, (gpointer)marks->priv->renderer);

  if (marks->priv->renderer == renderer)
    return;

  /* We unref the previous VisuData and disconnect signals. */
  if (marks->priv->renderer)
    {
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->nodePosition);
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->nodeRender);
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->nodeMaterial);
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->nodePopulation);
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->siz_sig);
      g_signal_handler_disconnect(marks->priv->renderer, marks->priv->dat_sig);
      g_object_unref(marks->priv->renderer);
    }
  marks->priv->renderer = renderer;
  if (renderer)
    {
      /* We ref the new given data. */
      g_object_ref(renderer);

      /* Connect a signal on file change to remove everything. */
      marks->priv->nodePopulation =
        g_signal_connect_swapped(renderer, "nodes::population-decrease",
                                 G_CALLBACK(_populationDecrease), (gpointer)marks);
      marks->priv->nodePosition =
        g_signal_connect_swapped(renderer, "nodes::position",
                                 G_CALLBACK(_setDirty), (gpointer)marks);
      marks->priv->nodeRender =
        g_signal_connect_swapped(renderer, "nodes::visibility",
                                 G_CALLBACK(_setDirty), (gpointer)marks);
      marks->priv->nodeMaterial =
        g_signal_connect_swapped(renderer, "element-notify::color",
                                 G_CALLBACK(_setDirty), (gpointer)marks);
      marks->priv->siz_sig =
        g_signal_connect_swapped(renderer, "element-size-changed",
                                 G_CALLBACK(_setDirty), (gpointer)marks);
      marks->priv->dat_sig =
        g_signal_connect_swapped(renderer, "notify::data",
                                 G_CALLBACK(onNotifyData), (gpointer)marks);
    }
  _setData(marks, (renderer) ? visu_node_array_renderer_getNodeArray(renderer) : (VisuNodeArray*)0);
}

static void onNotifyData(VisuGlExtMarks *marks, GParamSpec *pspec _U_,
                         VisuNodeArrayRenderer *renderer)
{
  _setData(marks, visu_node_array_renderer_getNodeArray(renderer));
}
static void _populationDecrease(VisuGlExtMarks *marks, GArray *nodes)
{
  guint i;
  GList *list, *rmList;
  struct MarkInfo_struct*mark;
  gboolean remove;
  gboolean invDirty, dirDirty;
  VisuMarkType type;

  DBG_fprintf(stderr, "Visu Marks: caught 'PopulationDecrease'"
	      " signal (%p).\n", (gpointer)marks);
  
  invDirty = dirDirty = FALSE;

  /* Run through the mark list to get all nodeId and look into nodes
     to find if mark must be removed or not. */
  DBG_fprintf(stderr, " | list contains %d elements\n",
	      g_list_length(marks->priv->storedMarks));
  list = marks->priv->storedMarks;
  while(list)
    {
      mark = (struct MarkInfo_struct*)list->data;
      DBG_fprintf(stderr, " | mark %p of type %d.\n",
		  (gpointer)mark, mark->type);
      /* We remove mark if one of the node is in the list. */
      remove = FALSE;
      rmList = list;
      for (i = 0; !remove && i < nodes->len; i++)
	remove = remove ||
	  (g_array_index(nodes, guint, i) == mark->idNode1) ||
	  (g_array_index(nodes, guint, i) == mark->idNode2) ||
	  (g_array_index(nodes, guint, i) == mark->idNode3);
      list = g_list_next(list);
      if (remove)
	{
	  DBG_fprintf(stderr, " | delete mark %p of type %d.\n",
		      (gpointer)mark, mark->type);
          type = markRemove(marks, rmList);
          if (type == MARK_HIGHLIGHT)
            dirDirty = TRUE;
          else
            invDirty = TRUE;
	}
    }

  if (invDirty)
    visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
  if (dirDirty)
    visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
}
static void _setDirty(VisuGlExtMarks *marks)
{
  visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
  visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
}
/* static void updateListOnCameraChange(VisuGlView *view _U_, gpointer data _U_) */
/* { */
/*   GList *tmpLst; */
/*   struct MarkInfo_struct* mark; */
  /* VisuGlExtMarks *marks; */

  /* marks = (VisuGlExtMarks*)data; */
  /* g_return_if_fail(data); */

/*   for (tmpLst = mesureData->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst)) */
/*     { */
/*       mark = (struct MarkInfo_struct*)(tmpLst->data); */
/*       if (mark->type == PICK_MESURE_MARK_LINE) */
/* 	visu_interactive_class_getNodes2DCoordinates(mesureData->data, mark->nIds, g_list_length(mark->nodes), mark->coord, &mark->size); */
/*     } */
/*   marksDraw(marks, 0); */
/* } */
static gboolean onNodeSelection(VisuInteractive *inter _U_, VisuInteractivePick pick,
                                VisuData *dataObj, VisuNode *node0, VisuNode *node1,
                                VisuNode *node2, gpointer data)
{
  gint nodeId;
  VisuMarkType type;
  gboolean set, status;
  GList *list;
  VisuGlExtMarks *marks = VISU_GL_EXT_MARKS(data);

  DBG_fprintf(stderr, "Visu Marks: get a 'node-selection' signals -> %d.\n", pick);
  
  switch (pick)
    {
    case PICK_DISTANCE:
      if (marks->priv->drawValues)
	{
	  toggleDistance(marks, node1->number, node0->number, FALSE);
	  putMark(dataObj, node1->number, node0->number, MARK_DISTANCE);
          g_signal_emit(G_OBJECT(marks), signals[MEASUREMENT_CHANGE_SIGNAL], 0, dataObj);
          visu_gl_ext_setDirty(VISU_GL_EXT(data), TRUE);
	}
      return TRUE;
    case PICK_ANGLE:
      if (marks->priv->drawValues)
	{
	  toggleAngle(marks, node1->number, node2->number, node0->number, FALSE);
          g_signal_emit(G_OBJECT(marks), signals[MEASUREMENT_CHANGE_SIGNAL], 0, dataObj);
          visu_gl_ext_setDirty(VISU_GL_EXT(data), TRUE);
	}
      return TRUE;
    case PICK_UNREFERENCE_1:
    case PICK_UNREFERENCE_2:
    case PICK_REFERENCE_1:
    case PICK_REFERENCE_2:
      type = (pick == PICK_REFERENCE_1 || pick == PICK_UNREFERENCE_1)?
	MARK_BIG_SQUARE:MARK_SMALL_SQUARE;
      nodeId = -1;
      for (list = marks->priv->storedMarks; list; list = g_list_next(list))
	if (((struct MarkInfo_struct*)list->data)->type == type)
	  nodeId = ((struct MarkInfo_struct*)list->data)->idNode1;
      /* Erase previous one. */
      if (nodeId >= 0)
	{
	  removeDot(marks, nodeId, type);
	  putMark(dataObj, -1, nodeId, type);
	}
      /* Add new one. */
      if (pick == PICK_REFERENCE_1 || pick == PICK_REFERENCE_2)
	{
	  addDot(marks, node0->number, type);
	  putMark(dataObj, -1, node0->number, type);
	}
      visu_gl_ext_setDirty(VISU_GL_EXT(data), TRUE);
      return TRUE;
    case PICK_HIGHLIGHT:
      set = toggleHighlight(marks, node0->number, MARKS_STATUS_TOGGLE, &status);
      if (set)
	{
          g_object_notify_by_pspec(G_OBJECT(marks), properties[HIGHLIGHT_PROP]);
          if (marks->priv->hidingMode != HIDE_NONE)
            visu_node_masker_emitDirty(VISU_NODE_MASKER(marks));
          visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
	}
      return TRUE;
    case PICK_INFORMATION:
      set = setInformation(marks, dataObj, node0->number);
      if (set)
	{
          g_signal_emit(G_OBJECT(marks), signals[MEASUREMENT_CHANGE_SIGNAL], 0, dataObj);
          visu_gl_ext_setDirty(VISU_GL_EXT(data), TRUE);
	}
      return TRUE;
    case PICK_SELECTED:
    default:
      return TRUE;
    }
}

/* Method that add/remove  mark to the list of drawn marks. */
static void removeDot(VisuGlExtMarks *marks, guint nodeId, VisuMarkType type)
{
  struct MarkInfo_struct *mark;
  GList *tmpLst;

  g_return_if_fail(marks);

  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == type && mark->idNode1 == nodeId)
	{
	  DBG_fprintf(stderr, "Visu Marks: found a dot mark.\n");
	  markRemove(marks, tmpLst);
	  return;
	}
    }
}
static void addDot(VisuGlExtMarks *marks, guint nodeId, VisuMarkType type)
{
  struct MarkInfo_struct *mark;

  g_return_if_fail((type == MARK_BIG_SQUARE ||
		    type == MARK_SMALL_SQUARE ||
		    type == MARK_HIGHLIGHT));

  DBG_fprintf(stderr, "Visu Marks: add a new mark of type %d.\n", type);
  mark          = markNew(type);
  mark->idNode1 = nodeId;
  marks->priv->storedMarks = g_list_prepend(marks->priv->storedMarks, (gpointer)mark);
  if (type == MARK_HIGHLIGHT && marks->priv->cachedHighlighted)
    {
      g_array_unref(marks->priv->cachedHighlighted);
      marks->priv->cachedHighlighted = (GArray*)0;
    }
  return;
}
static gboolean toggleDistance(VisuGlExtMarks *marks, guint nodeRefId,
			       guint nodeId, gboolean set)
{
  struct MarkInfo_struct *mark;
  GList *tmpLst;

  g_return_val_if_fail(marks, FALSE);

  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == MARK_DISTANCE &&
	  ((mark->idNode1 == nodeRefId && mark->idNode2 == nodeId) ||
	   (mark->idNode2 == nodeRefId && mark->idNode1 == nodeId)))
	{
	  DBG_fprintf(stderr, "Visu Marks: found a distance mark.\n");
	  if (!set)
	    markRemove(marks, tmpLst);
	  return set;
	}
    }
  /* Found none, create a new one. */
  mark          = markNew(MARK_DISTANCE);
  mark->idNode1 = nodeRefId;
  mark->idNode2 = nodeId;
  DBG_fprintf(stderr, "Visu Marks: add a distance mark.\n");
  marks->priv->storedMarks = g_list_prepend(marks->priv->storedMarks, (gpointer)mark);
  return TRUE;
}
static gboolean toggleAngle(VisuGlExtMarks *marks, guint nodeRefId,
			    guint nodeRef2Id, guint nodeId, gboolean set)
{
  struct MarkInfo_struct *mark;
  GList *tmpLst;

  g_return_val_if_fail(marks, FALSE);

  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == MARK_ANGLE &&
	  mark->idNode1 == nodeRefId &&
	  ((mark->idNode2 == nodeRef2Id && mark->idNode3 == nodeId) ||
	   (mark->idNode3 == nodeRef2Id && mark->idNode2 == nodeId)))
	{
	  DBG_fprintf(stderr, "Visu Marks: found an angle mark.\n");
	  if (!set)
	    markRemove(marks, tmpLst);
	  return set;
	}
    }
  /* Found none, create a new one. */
  mark          = markNew(MARK_ANGLE);
  mark->idNode1 = nodeRefId;
  mark->idNode2 = nodeRef2Id;
  mark->idNode3 = nodeId;
  DBG_fprintf(stderr, "Visu Marks: add an angle mark.\n");
  marks->priv->storedMarks = g_list_prepend(marks->priv->storedMarks, (gpointer)mark);
  return TRUE;
}
static gboolean toggleHighlight(VisuGlExtMarks *marks, guint nodeId,
				VisuGlExtMarksStatus status, gboolean *finalStatus)
{
  struct MarkInfo_struct *mark;
  GList *tmpLst;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), FALSE);

  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == MARK_HIGHLIGHT &&
	  mark->idNode1 == nodeId)
	{
	  DBG_fprintf(stderr, "Visu Marks: found a highlight mark (%d).\n", nodeId);
	  if (status == MARKS_STATUS_TOGGLE || status == MARKS_STATUS_UNSET)
	    markRemove(marks, tmpLst);
	  if (finalStatus)
	    *finalStatus = (status != MARKS_STATUS_TOGGLE && status != MARKS_STATUS_UNSET);
	  /* Return if it has been changed. */
	  return (status == MARKS_STATUS_TOGGLE || status == MARKS_STATUS_UNSET);
	}
    }
  if (status == MARKS_STATUS_TOGGLE || status == MARKS_STATUS_SET)
    /* Found none, create a new one. */
    addDot(marks, nodeId, MARK_HIGHLIGHT);
  if (finalStatus)
    *finalStatus = (status == MARKS_STATUS_TOGGLE || status == MARKS_STATUS_SET);
  /* Return if it has been changed. */
  return (status == MARKS_STATUS_TOGGLE || status == MARKS_STATUS_SET);
}
/**
 * visu_gl_ext_marks_unHighlight:
 * @marks: a #VisuGlExtMarks object.
 *
 * Remove all highlight marks.
 *
 * Since: 3.8
 *
 * Returns: TRUE if anything was highlighted.
 **/
gboolean visu_gl_ext_marks_unHighlight(VisuGlExtMarks *marks)
{
  struct MarkInfo_struct *mark;
  GList *tmpLst, *rmLst;
  gboolean changed;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), FALSE);

  /* Look for the mark. */
  changed = FALSE;
  tmpLst = marks->priv->storedMarks;
  while (tmpLst)
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      rmLst = tmpLst;
      tmpLst = g_list_next(tmpLst);
      if (mark->type == MARK_HIGHLIGHT)
	{
          markRemove(marks, rmLst);
          changed = TRUE;
	}
    }
  if (changed)
    {
      g_object_notify_by_pspec(G_OBJECT(marks), properties[HIGHLIGHT_PROP]);
      if (marks->priv->hidingMode != HIDE_NONE)
        visu_node_masker_emitDirty(VISU_NODE_MASKER(marks));
      visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
    }
  return changed;
}
static gboolean setInformation(VisuGlExtMarks *marks, VisuData *dataObj, guint node)
{
  VisuNodeInfo *infos;
  float min;
  GList *lst, *tmpLst, *tmpLst2;
  int i, n;
  gboolean redraw;
  float *dists;
  guint *ids;
  float xyz1[3], xyz2[3];

  g_return_val_if_fail(marks, FALSE);

  DBG_fprintf(stderr, "Visu Marks: compute and print distances"
	      " and angles for %d.\n", node);
  infos = _getDistanceList(dataObj, node, &min);

  lst = (GList*)0;
  redraw = FALSE;
  for (i = 0; infos[i].id != node; i+= 1)
    if (infos[i].dist < marks->priv->infoRange * min)
      {
	toggleDistance(marks, node, infos[i].id, TRUE);
	lst = g_list_prepend(lst, GINT_TO_POINTER(infos[i].id));
	redraw = TRUE;
      }
  g_free(infos);

  n = g_list_length(lst);
  if (n > 1)
    {
      n = n * (n - 1 ) / 2;
      DBG_fprintf(stderr, "Visu Marks: there are %d angles at max.\n", n);
      ids = g_malloc(sizeof(guint) * n * 2);
      dists = g_malloc(sizeof(float) * n);
      i = 0;
      min = G_MAXFLOAT;
      for (tmpLst = lst; tmpLst; tmpLst = g_list_next(tmpLst))
	for (tmpLst2 = tmpLst->next; tmpLst2; tmpLst2 = g_list_next(tmpLst2))
	  {
	    ids[i * 2 + 0] = (guint)GPOINTER_TO_INT(tmpLst->data);
	    ids[i * 2 + 1] = (guint)GPOINTER_TO_INT(tmpLst2->data);
	    visu_data_getNodePosition(dataObj,
                                      visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
								ids[i * 2 + 0]),
                                      xyz1);
	    visu_data_getNodePosition(dataObj,
                                      visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj),
								ids[i * 2 + 1]),
                                      xyz2);
	    dists[i] =
	      (xyz1[0] - xyz2[0]) * (xyz1[0] - xyz2[0]) +
	      (xyz1[1] - xyz2[1]) * (xyz1[1] - xyz2[1]) +
	      (xyz1[2] - xyz2[2]) * (xyz1[2] - xyz2[2]);
	    DBG_fprintf(stderr, " | %d (%d %d) -> %g\n", i, ids[i * 2 + 0],
			ids[i * 2 + 1], dists[i]);
	    min = MIN(min, dists[i]);
	    i += 1;
	  }
  
      for (i = 0; i < n; i++)
	if (dists[i] < (2.75f * min))
	  {
	    DBG_fprintf(stderr, " | %d (%d %d) -> %g OK\n", i, ids[i * 2 + 0],
			ids[i * 2 + 1], dists[i]);
	    toggleAngle(marks, node, ids[i * 2 + 0],
			ids[i * 2 + 1], TRUE);
	  }
      g_free(ids);
      g_free(dists);
    }
  g_list_free(lst);

  return redraw;
}
/**
 * visu_gl_ext_marks_getActive:
 * @marks: a #VisuGlExtMarks object.
 * @nodeId: a node id.
 *
 * Retrieve if @nodeId is implied any measurement marks stored in @mark.
 *
 * Returns: TRUE if @nodeId participate to any mark (distance,
 * angle...).
 */
gboolean visu_gl_ext_marks_getActive(VisuGlExtMarks *marks, guint nodeId)
{
  GList *list;
  struct MarkInfo_struct *mark;

  g_return_val_if_fail(marks, FALSE);

  for (list = marks->priv->storedMarks; list; list = g_list_next(list))
    {
      mark = (struct MarkInfo_struct*)list->data;
      if ((mark->type == MARK_DISTANCE &&
	   mark->idNode1 == nodeId) ||
	  (mark->type == MARK_ANGLE &&
	   mark->idNode1 == nodeId))
	return TRUE;
    }
  return FALSE;
}
/**
 * visu_gl_ext_marks_getHighlighted:
 * @marks: a #VisuGlExtMarks object ;
 *
 * @marks has a list of mark for some nodes. These marks are only
 * highlight marks.
 *
 * Returns: (element-type guint) (transfer none): list of
 * highlighted nodes (starting from 0), should not be freed.
 *
 * Since: 3.6
 */
GArray* visu_gl_ext_marks_getHighlighted(const VisuGlExtMarks *marks)
{
  GList *tmpLst;
  struct MarkInfo_struct* mark;

  g_return_val_if_fail(marks, (GArray*)0);

  if (!marks->priv->cachedHighlighted)
    {
      marks->priv->cachedHighlighted = g_array_new(FALSE, FALSE, sizeof(guint));
      for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
        {
          mark = (struct MarkInfo_struct*)(tmpLst->data);
          if (mark->type == MARK_HIGHLIGHT)
            g_array_append_val(marks->priv->cachedHighlighted, mark->idNode1);
        }
    }
  return marks->priv->cachedHighlighted;
}
/**
 * visu_gl_ext_marks_getHighlightStatus:
 * @marks: a #VisuGlExtMarks object.
 * @nodeId: a node id (ranging from 0).
 *
 * Nodes can be highlighted.
 *
 * Since: 3.7
 *
 * Returns: TRUE if @nodeId has an highlight.
 **/
gboolean visu_gl_ext_marks_getHighlightStatus(VisuGlExtMarks *marks, guint nodeId)
{
  GList *tmpLst;
  struct MarkInfo_struct* mark;

  g_return_val_if_fail(marks, FALSE);

  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)(tmpLst->data);
      if (mark->type == MARK_HIGHLIGHT && mark->idNode1 == nodeId)
        return TRUE;
    }
  return FALSE;
}
/**
 * visu_gl_ext_marks_setHighlight:
 * @marks: a #VisuGlExtMarks object ;
 * @nodes: (element-type guint32): a set of node ids (0 started) ;
 * @status: changing command.
 *
 * @marks has a list of mark for some nodes. These marks can be
 * highlight (or distance, angles...). Depending on @status values,
 * the mark may be switch on or off.
 *
 * Returns: TRUE if redraw needed.
 *
 * Since: 3.6
 */
gboolean visu_gl_ext_marks_setHighlight(VisuGlExtMarks *marks, GArray *nodes,
                                        VisuGlExtMarksStatus status)
{
  gboolean redraw, res;
  guint i;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), FALSE);

  if (!nodes || !nodes->len)
    return FALSE;
  
  DBG_fprintf(stderr, "Visu Marks: set highlight status of list to %d.\n",
	      status);
  redraw = FALSE;
  for (i = 0; i < nodes->len; i++)
    {
      res = toggleHighlight(marks, g_array_index(nodes, guint, i),
			    status, (gboolean*)0);
      DBG_fprintf(stderr, " | %d -> %d\n", g_array_index(nodes, guint, i), res);
      redraw = redraw || res;
    }
  DBG_fprintf(stderr, "Visu Marks: resulting redraw %d.\n",
	      redraw);
  if (redraw)
    {
      g_object_notify_by_pspec(G_OBJECT(marks), properties[HIGHLIGHT_PROP]);
      if (marks->priv->hidingMode != HIDE_NONE)
        visu_node_masker_emitDirty(VISU_NODE_MASKER(marks));
      visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
    }
  return redraw;
}
/**
 * visu_gl_ext_marks_setDrawValues:
 * @marks: a #VisuGlExtMarks object.
 * @status: a boolean.
 *
 * Change if the measurements are printed or not (distance length, or
 * angles...).
 *
 * Returns: TRUE if @marks is modified.
 */
gboolean visu_gl_ext_marks_setDrawValues(VisuGlExtMarks *marks, gboolean status)
{
  g_return_val_if_fail(marks, FALSE);

  if (marks->priv->drawValues == status)
    return FALSE;

  marks->priv->drawValues = status;
  return TRUE;
}
/**
 * visu_gl_ext_marks_removeMeasures:
 * @marks: a #VisuGlExtMarks object.
 * @nodeId: a node id.
 *
 * This routine scans the @mark to remove all marks of distance or
 * angle where @nodeId is implied in.
 *
 * Returns: TRUE is @mark is changed.
 */
gboolean visu_gl_ext_marks_removeMeasures(VisuGlExtMarks *marks, gint nodeId)
{
  gboolean redraw;
  GList *list, *tmpLst;
  struct MarkInfo_struct *mark;

  g_return_val_if_fail(marks, FALSE);

  DBG_fprintf(stderr, "Visu Marks: remove measures (%d).\n", nodeId);
  redraw = FALSE;
  for (list = marks->priv->storedMarks; list; list = tmpLst)
    {
      tmpLst = g_list_next(list);
      mark = (struct MarkInfo_struct*)list->data;
      if ((mark->type == MARK_DISTANCE ||
	   mark->type == MARK_ANGLE) &&
	  (nodeId < 0 || mark->idNode1 == (guint)nodeId))
	{
	  markRemove(marks, list);
	  redraw = TRUE;
	}
    }
  if (!redraw)
    return FALSE;

  visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_marks_setInfos:
 * @marks: a #VisuGlExtMarks object.
 * @nodeId: a node id.
 * @status: a boolean.
 *
 * Depending on @status, it removes all measurements from @nodeId or
 * it calculate all first neighbour relations of @nodeId.
 *
 * Return: TRUE if @marks is changed.
 */
gboolean visu_gl_ext_marks_setInfos(VisuGlExtMarks *marks, guint nodeId, gboolean status)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), FALSE);
  g_return_val_if_fail(marks->priv->renderer, FALSE);

  DBG_fprintf(stderr, "Visu Marks: set info for node %d.\n", nodeId);

  if (status)
    {
      if (setInformation(marks, VISU_DATA(visu_node_array_renderer_getNodeArray(marks->priv->renderer)), nodeId))
	{
          visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
	  return TRUE;
	}
    }
  else
    return visu_gl_ext_marks_removeMeasures(marks, nodeId);
    
  return FALSE;
}


/****************************/
/* OpenGL drawing routines. */
/****************************/
static void drawMarkDot(VisuData *data, guint nodeId, VisuMarkType type)
{
  VisuNode *node;
  float xyz[3];
  VisuElement *ele;

  node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeId);
  g_return_if_fail(node);

  /* If the node is masked, then we don't draw the dot. */
  if (!node->rendered)
    return;
  /* If the element is masked, then we don't draw the dot. */
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), node);
  if (!visu_element_getRendered(ele))
    return;

  DBG_fprintf(stderr, "Visu Mark: draw a mark (%d) on node %d.\n",
	      (int)type, node->number);

  visu_data_getNodePosition(data, node, xyz);
  switch (type)
    {
    case MARK_BIG_SQUARE:
      glRasterPos3f(xyz[0], xyz[1], xyz[2]);
      glDrawPixels(MARK_BIG_SQUARE_SIZE,
		   MARK_BIG_SQUARE_SIZE,
		   GL_RGBA, GL_UNSIGNED_BYTE, markBigSquare);
      break;
    case MARK_SMALL_SQUARE:
      glRasterPos3f(xyz[0], xyz[1], xyz[2]);
      glDrawPixels(MARK_SMALL_SQUARE_SIZE,
		   MARK_SMALL_SQUARE_SIZE,
		   GL_RGBA, GL_UNSIGNED_BYTE, markSmallSquare);
      break;
    default:
      break;
    }
}
static void drawMarkHighlight(VisuData *data, guint nodeId,
                              VisuGlView *view, VisuNodeArrayRenderer *renderer)
{
  VisuNode *node;
  float xyz[3];
  VisuElement *ele;
  int nlat;
  float eleSize;
  GLUquadricObj *obj;
  VisuElementRenderer *eleRenderer;

  node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeId);
  g_return_if_fail(node);

  /* If the node is masked, then we don't draw the dot. */
  if (!node->rendered)
    return;
  /* If the element is masked, then we don't draw the dot. */
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), node);
  if (!visu_element_getRendered(ele))
    return;

  DBG_fprintf(stderr, "Visu Mark: draw a mark highlight on node %d.\n", node->number);

  visu_data_getNodePosition(data, node, xyz);
  g_return_if_fail(view);

  obj = gluNewQuadric();
  eleRenderer = visu_node_array_renderer_get(renderer, ele);
  visu_element_renderer_colorize(eleRenderer, VISU_ELEMENT_RENDERER_HIGHLIGHT_SEMI);
  eleSize = visu_element_renderer_getExtent(eleRenderer);
  nlat = visu_gl_view_getDetailLevel(view, eleSize * highlightFactor);
  glPushMatrix();
  glTranslated(xyz[0], xyz[1], xyz[2]);
  gluSphere(obj, (double)eleSize * highlightFactor, 2 * nlat, 2 * nlat);
  glPopMatrix();
  gluDeleteQuadric(obj);
}
static void drawMarkDistance(VisuData *data, guint nodeRefId,
			     guint nodeId, VisuMarkType type _U_)
{
  VisuNode *nodeRef;
  VisuNode *node;
  float xyzRef[3], xyz[3];
  VisuElement *ele;

  nodeRef = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeRefId);
  node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeId);
  g_return_if_fail(node && nodeRef);

  /* If one of the node is masked, then we don't draw the distance. */
  if (!nodeRef->rendered || !node->rendered)
    return;

  /* If one of the element is masked, then we don't draw the distance. */
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), nodeRef);
  if (!visu_element_getRendered(ele))
    return;
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), node);
  if (!visu_element_getRendered(ele))
    return;

  visu_data_getNodePosition(data, nodeRef, xyzRef);
  visu_data_getNodePosition(data, node, xyz);

  visu_gl_drawDistance(xyzRef, xyz, TRUE);
}
static void drawMarkAngle(VisuData *data, guint nodeRefId, guint nodeRef2Id,
			  guint nodeId, guint id)
{
  VisuNode *nodeRef, *nodeRef2, *node;
  float xyzRef[3], xyzRef2[3], xyz[3];
  VisuElement *ele;

  nodeRef = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeRefId);
  nodeRef2 = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeRef2Id);
  node = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeId);
  g_return_if_fail(node && nodeRef && nodeRef2);

  /* If one of the node is masked, then we don't draw the angle. */
  if (!nodeRef->rendered || !nodeRef2->rendered || !node->rendered)
    return;

  /* If one of the element is masked, then we don't draw the angle. */
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), nodeRef);
  if (!visu_element_getRendered(ele))
    return;
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), nodeRef2);
  if (!visu_element_getRendered(ele))
    return;
  ele = visu_node_array_getElement(VISU_NODE_ARRAY(data), node);
  if (!visu_element_getRendered(ele))
    return;

  visu_data_getNodePosition(data, nodeRef, xyzRef);
  visu_data_getNodePosition(data, nodeRef2, xyzRef2);
  visu_data_getNodePosition(data, node, xyz);

  DBG_fprintf(stderr, "Visu PickMesure: draw an angle mark on nodes %d/%d/%d.\n",
	      nodeRef->number, nodeRef2->number, node->number);
  visu_gl_drawAngle(xyzRef, xyzRef2, xyz, id, TRUE);
}
/* Calculate the node vertices coordinates (contained in the
array nodevertices), from node coordinates (xn,yn) */
static void defineNodeVertices(int nVert, double radius, double xn, double yn, double *nodeVertices)
{
  int i;
  for (i = 0; i < nVert; i++) {
    nodeVertices[2 * i] = (xn + radius * (cos (((2 * G_PI * i) / nVert))));
    nodeVertices[2 * i + 1] = (yn + radius * (sin (((2 * G_PI * i) / nVert))));
  }
}

/* Add node vertices to vertices array, from position i+1 */
static void addVerticesToGlobalArray(int nVert, double *nodeVertices, double *vertices, int i)
{
  int j;
  int k = 0;
  for (j = (i * 2) * nVert; j < (i * 2) * nVert + (nVert * 2); j += 2) {
    vertices[j] = nodeVertices[k];
    vertices[j + 1] = nodeVertices[k + 1];
    k += 2;
  }
}
static void drawMarkLine(VisuData *data _U_, GLfloat *coord, guint size)
{
  #define NEWLINE 1
  #define NEWNODE 2
  int i = 0, j;
  int elem = 0;
  int nNodes, nTotalVert;
  /***********************************/
  /* values must be selected by user */
  int nVert = 30;
  double radius = 30;
  /***********************************/
  double *vertices, *nodeVertices, *verticesKept;
  double link[2] = {0};
  double distance;
  gboolean visible = TRUE;
  gboolean li = FALSE;

  nNodes = size / 2;
  nTotalVert = nNodes * nVert;
  glMatrixMode(GL_PROJECTION);
  glPushMatrix();
  glLoadIdentity();
  gluOrtho2D(0.0, 600, 0., 600);
   
  glMatrixMode(GL_MODELVIEW);
  glPushMatrix();
  glLoadIdentity();
  vertices     = g_malloc(sizeof(double) * (nTotalVert * 2));
  nodeVertices = g_malloc(sizeof(double) * (nVert * 2));
  verticesKept = g_malloc(sizeof(double) * (nTotalVert * 2) + sizeof(int) * nTotalVert * 2);

  /* For each node, compute and add its vertices coordinates (nodeVertices) 
     to the vertices global array (vertices) */
  for (i = 0; i < nNodes; i++){
    defineNodeVertices(nVert, radius, coord[2 * i], coord[2 * i + 1], nodeVertices);
    addVerticesToGlobalArray(nVert, nodeVertices, vertices, i);
  }
  g_free(nodeVertices);
  verticesKept[elem] = NEWNODE;
  elem++;

  /* For each vertex */
  for (i = 0; i < nTotalVert; i++){
    /* We notify with a marker each new node */
    if (i / nVert != (i - 1) / nVert){
      verticesKept[elem] = NEWNODE;
      elem++;
    }
    /* For each node */
    for (j = 0; j < nNodes; j++){
      /* If vertex i doesn't belong to node j */
      if (j != i / nVert){
        /* Distance between vertex i and node j */
        distance = sqrt( (pow ((coord[2 * j + 1] - vertices[2 * i + 1]), 2)) +
                         (pow ((coord[2 * j]     - vertices[2 * i]),     2)) );
        if (distance < radius)
          visible = FALSE;
      }
    }
    if (visible){
      verticesKept[elem] = vertices[2 * i];
      verticesKept[elem + 1] = vertices[2 * i + 1];
      elem += 2;
    }
    else {
      if (verticesKept[elem - 1] != NEWLINE){
        verticesKept[elem] = NEWLINE;
        elem++;
      }
      visible = TRUE;
    }
  }

  glLineWidth(4.0);
  glColor3f(1.0, 1.0, 1.0);
  i = 0;

  while (i < elem){
    if (verticesKept[i] == NEWLINE)
      i++;
    else if (verticesKept[i] == NEWNODE){
      i++;
      if (verticesKept[i] != NEWNODE && verticesKept[i] != NEWLINE){
        link[0] = verticesKept[i];
        link[1] = verticesKept[i + 1];
        li = TRUE;
      }
      else {
        li = FALSE;
        while (verticesKept[i] == NEWNODE || verticesKept[i] == NEWLINE)
          i++;
      }
    }
    else {
      glBegin(GL_LINES);
      while (verticesKept[i + 2] != NEWNODE && verticesKept[i + 2] != NEWLINE && i + 2 < elem){
        glVertex2f(verticesKept[i], verticesKept[i + 1]);
        glVertex2f(verticesKept[i + 2], verticesKept[i + 3]);
        i += 2;
      }
      if (i + 2 >= elem){
        if (li){
          glVertex2f(verticesKept[i], verticesKept[i + 1]);
          glVertex2f(link[0], link[1]);
        }
        i = elem;
      }
      else if (verticesKept[i + 2] == NEWLINE)
        i += 3;
      else if (verticesKept[i + 2] == NEWNODE){
        if (li){
          glVertex2f(verticesKept[i], verticesKept[i + 1]);
          glVertex2f(link[0], link[1]);
        }
        i += 2;
      }
      glEnd();
    }
  }

  g_free(verticesKept);
  g_free(vertices);
  
  glPopMatrix();
  glMatrixMode(GL_PROJECTION);
  glPopMatrix();
  glMatrixMode(GL_MODELVIEW);
}
static void putMark(VisuData *data, guint nodeRefId, guint nodeId, VisuMarkType type)
{
  float centre[3];

  visu_box_getCentre(visu_boxed_getBox(VISU_BOXED(data)), centre);

  DBG_fprintf(stderr, "Visu Marks: draw directly a distance on front buffer.\n");

  glDrawBuffer(GL_FRONT);
  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_DEPTH_TEST);
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  glPushMatrix();
  glTranslated(-centre[0], -centre[1], -centre[2]);
  switch (type)
    {
    case MARK_BIG_SQUARE:
    case MARK_SMALL_SQUARE:
      drawMarkDot(data, nodeId, type);
      break;
    case MARK_DISTANCE:
      drawMarkDistance(data, nodeRefId, nodeId, type);
      break;
    default:
      g_warning("No direct drawing available for this type.");
    }
  glPopMatrix();
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glPopAttrib();
  glDrawBuffer(GL_BACK);

  glFlush();
}


/*************************/
/* Resources management. */
/*************************/
static void exportResources(GString *data, VisuData *dataObj _U_)
{
  visu_config_file_exportComment(data, DESC_RESOURCE_FACTOR);
  visu_config_file_exportEntry(data, FLAG_RESOURCE_FACTOR, NULL,
                               "%f", highlightFactor);
  visu_config_file_exportComment(data, "");
}


/****************************/
/* List drawing management. */
/****************************/
static void visu_gl_ext_marks_rebuild(VisuGlExt *ext)
{
  visu_gl_text_rebuildFontList();
  visu_gl_ext_marks_draw(ext);
}
static void visu_gl_ext_marks_draw(VisuGlExt *ext)
{
  VisuGlExtMarks *marks = VISU_GL_EXT_MARKS(ext);
  struct MarkInfo_struct *mark;
  GList *tmpLst;
  guint id;
  VisuData *dataObj;
  
  visu_gl_ext_setDirty(ext, FALSE);

  DBG_fprintf(stderr, "Visu Marks: update the list %p"
	      " of all marks.\n", (gpointer)marks->priv->storedMarks);
  glDeleteLists(visu_gl_ext_getGlList(ext), 1);

  if (!marks->priv->storedMarks || !marks->priv->renderer)
    return;

  visu_gl_text_initFontList();

  id = 0;
  /* Draw the colour inverse list. */
  visu_gl_ext_startDrawing(ext);
  glEnable(GL_DEPTH_TEST);
  glClear(GL_DEPTH_BUFFER_BIT);
  /*       glDisable(GL_DEPTH_TEST); */
  glDisable(GL_LIGHTING);
  glDisable(GL_FOG);
  glDisable(GL_CULL_FACE);
  /*   glDisable(GL_DITHER); */
  glEnable(GL_BLEND);
  glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
  dataObj = VISU_DATA(visu_node_array_renderer_getNodeArray(marks->priv->renderer));
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      DBG_fprintf(stderr, " | draw mark of type %d.\n", mark->type);
      switch (mark->type)
        {
        case MARK_BIG_SQUARE:
        case MARK_SMALL_SQUARE:
          drawMarkDot(dataObj, mark->idNode1, mark->type);
          break;
        case MARK_DISTANCE:
          drawMarkDistance(dataObj, mark->idNode1, mark->idNode2, mark->type);
          break;
        case MARK_LINE:
          drawMarkLine(dataObj, mark->coord, mark->size);
          break;
        case MARK_ANGLE:
          drawMarkAngle(dataObj, mark->idNode1, mark->idNode2, mark->idNode3, id);
          id += 1;
          break;
        default:
          break;
        }
    }
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  visu_gl_ext_completeDrawing(ext);
}

/******************************************/
/* XML files for marks and other exports. */
/******************************************/

/**
 * visu_gl_ext_marks_getMeasurementLabels:
 * @marks: a #VisuGlExtMarks object.
 *
 * Exports as a string the ids of nodes for measurement marks.
 *
 * Since: 3.6
 *
 * Returns: a newly allocated string.
 */
gchar* visu_gl_ext_marks_getMeasurementLabels(VisuGlExtMarks *marks)
{
  GString *str;
  GList *tmpLst;
  struct MarkInfo_struct *mark;
  guint i;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), (gchar*)0);

  str = g_string_new("#");

  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks, i = 0; tmpLst && i < 6;
       tmpLst = g_list_next(tmpLst), i += 1)
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == MARK_DISTANCE)
        g_string_append_printf(str, "      %4d-%4d", mark->idNode1 + 1, mark->idNode2 + 1);
      else if (mark->type == MARK_ANGLE)
        g_string_append_printf(str, " %4d-%4d-%4d",
                               mark->idNode3 + 1, mark->idNode1 + 1, mark->idNode2 + 1);
    }
  if (!tmpLst)
    g_string_append(str, "\n");
  else
    g_string_append(str, " (truncated list)\n");

  return g_string_free(str, FALSE);
}
/**
 * visu_gl_ext_marks_getMeasurementStrings:
 * @marks: a #VisuGlExtMarks object.
 *
 * Exports as a string all measurements stored in @marks.
 *
 * Since: 3.6
 *
 * Returns: a newly allocated string.
 */
gchar* visu_gl_ext_marks_getMeasurementStrings(VisuGlExtMarks *marks)
{
  VisuData *dataObj;
  GString *str;
  GList *tmpLst;
  struct MarkInfo_struct *mark;
  float posSelect[3], posRef1[3], posRef2[3], dx, dy, dz, dr;
  float dx1, dy1, dz1, dx2, dy2, dz2, dr1, dr2, ang;
  VisuNode *nodes[3];
  gchar *lbl;
  gboolean export;
  guint i;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks) && marks->priv->renderer, (gchar*)0);

  str = g_string_new(" ");
  export = FALSE;
  dataObj = VISU_DATA(visu_node_array_renderer_getNodeArray(marks->priv->renderer));
  /* Look for the mark. */
  for (tmpLst = marks->priv->storedMarks, i = 0; tmpLst && i < 6;
       tmpLst = g_list_next(tmpLst), i += 1)
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      nodes[0] = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), mark->idNode1);
      nodes[1] = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), mark->idNode2);
      nodes[2] = visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), mark->idNode3);
      if (mark->type == MARK_DISTANCE)
	{
	  DBG_fprintf(stderr, "Visu Marks: export this distance.\n");
          visu_data_getNodePosition(dataObj, nodes[0], posRef1);
          visu_data_getNodePosition(dataObj, nodes[1], posSelect);
          dx = posSelect[0] - posRef1[0];
          dy = posSelect[1] - posRef1[1];
          dz = posSelect[2] - posRef1[2];
          dr = sqrt(dx*dx + dy*dy + dz*dz);
          g_string_append_printf(str, "   %12.6g", dr);
          export = TRUE;
	}
      else if (mark->type == MARK_ANGLE)
        {
	  DBG_fprintf(stderr, "Visu Marks: export this angle.\n");
          visu_data_getNodePosition(dataObj, nodes[0], posRef1);
          visu_data_getNodePosition(dataObj, nodes[1], posRef2);
          visu_data_getNodePosition(dataObj, nodes[2], posSelect);
          dx1 = posSelect[0] - posRef1[0];
          dy1 = posSelect[1] - posRef1[1];
          dz1 = posSelect[2] - posRef1[2];
          dx2 = posRef2[0] - posRef1[0];
          dy2 = posRef2[1] - posRef1[1];
          dz2 = posRef2[2] - posRef1[2];
          dr1 = sqrt(dx1*dx1 + dy1*dy1 + dz1*dz1);
          dr2 = sqrt(dx2*dx2 + dy2*dy2 + dz2*dz2);
          ang = acos((dx2*dx1+dy2*dy1+dz2*dz1)/(dr2*dr1))/TOOL_PI180;
          g_string_append_printf(str, "   %12.6g", ang);
          export = TRUE;
        }
    }
  if (!export)
    {
      g_string_free(str, TRUE);
      return (gchar*)0;
    }
  if (VISU_IS_DATA_LOADABLE(dataObj))
    {
      g_object_get(dataObj, "label", &lbl, NULL);
      g_string_append_printf(str, " # %s\n", lbl);
      g_free(lbl);
    }

  return g_string_free(str, FALSE);
}

/**
 * visu_gl_ext_marks_getHidingMode:
 * @marks: a #VisuGlExtMarks object.
 *
 * Retrieves the hiding mode of @marks, see #VisuGlExtMarksHidingModes.
 *
 * Since: 3.8
 *
 * Returns: a #VisuGlExtMarksHidingModes value.
 **/
VisuGlExtMarksHidingModes visu_gl_ext_marks_getHidingMode(const VisuGlExtMarks *marks)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), HIDE_NONE);

  return marks->priv->hidingMode;
}
/**
 * visu_gl_ext_marks_setHidingMode:
 * @marks: a #VisuGlExtMarks object.
 * @mode: a #VisuGlExtMarksHidingModes value.
 *
 * Change the hiding mode of @marks.
 *
 * Returns: TRUE if value is actually changed.
 **/
gboolean visu_gl_ext_marks_setHidingMode(VisuGlExtMarks *marks,
                                         VisuGlExtMarksHidingModes mode)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks), FALSE);

  DBG_fprintf(stderr, "Visu Ext Marks: setting hiding mode to %d (%d).\n",
              mode, marks->priv->hidingMode);

  if (marks->priv->hidingMode == mode)
    return FALSE;

  marks->priv->hidingMode = mode;
  g_object_notify_by_pspec(G_OBJECT(marks), properties[HIDING_PROP]);
  visu_node_masker_emitDirty(VISU_NODE_MASKER(marks));

  return TRUE;
}

static gboolean _maskApply(const VisuNodeMasker *self, VisuNodeArray *array)
{
  VisuGlExtMarks *marks;
  gboolean redraw;
  GArray *ids;
  guint i;
  guint nHl, *hl, j;
  guint min, max;
  VisuNodeArrayIter iter;
  gboolean hide;

  g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(self), FALSE);

  marks = VISU_GL_EXT_MARKS(self);

  if (marks->priv->hidingMode == HIDE_NONE)
    return FALSE;

  redraw = FALSE;
  ids = visu_gl_ext_marks_getHighlighted(marks);
  if (marks->priv->hidingMode == HIDE_NON_HIGHLIGHT)
    {
      /* We switch off all nodes except the highlighted ones. To avoid
         to run the list of highlighted nodes to many times, we create
         a temporary array and store the min and max indexes. */
      if (!ids || !ids->len)
        return FALSE;

      visu_node_array_iter_new(array, &iter);

      hl = g_malloc(sizeof(gint) * ids->len);
      nHl = 0;
      min = iter.nAllStoredNodes;
      max = 0;
      for (i = 0; i < ids->len; i++)
        {
          hl[nHl] = g_array_index(ids, guint, i);
          min = MIN(min, hl[nHl]);
          max = MAX(max, hl[nHl]);
          nHl += 1;
        }

      for (visu_node_array_iterStart(array, &iter); iter.node;
           visu_node_array_iterNext(array, &iter))
        if (iter.node->number >= min && iter.node->number <= max)
          {
            hide = TRUE;
            for (j = 0; hide && j < nHl; j++)
              hide = (iter.node->number != hl[j]);
            if (hide)
              redraw = visu_node_setVisibility(iter.node, FALSE) || redraw;
          }
        else
          redraw = visu_node_setVisibility(iter.node, FALSE) || redraw;

      g_free(hl);
    }
  else if (marks->priv->hidingMode == HIDE_HIGHLIGHT)
    for (i = 0; i < ids->len; i++)
      {
        redraw = visu_node_setVisibility
          (visu_node_array_getFromId(array, g_array_index(ids, guint, i)),
           FALSE) || redraw;
      }

  return redraw;
}

/**
 * visu_data_getDistanceList:
 * @data: a #VisuData object ;
 * @nodeId: a node id.
 * @minVal: a location for a float.
 *
 * This routine creates an array of #VisuNodeInfo, storing for each
 * node its node id and its distance to @nodeId. The periodicity is
 * NOT taken into account. The array is not distance sorted, but if
 * @minVal is provided, it will contain the minimal distance between
 * @nodeId and the other nodes.
 *
 * Since: 3.5
 *
 * Returns: an array of #VisuNodeInfo of size the number of nodes. It
 * is terminated by @nodeId value itself.
 */
static VisuNodeInfo* _getDistanceList(VisuData *data, guint nodeId, float *minVal)
{
  VisuNodeInfo *infos;
  int nNodes;
  VisuNodeArrayIter iter;
  VisuNode *nodeRef;
  float xyz[3], xyzRef[3], min;

  if (minVal)
    *minVal = -1.f;
  
  g_return_val_if_fail(VISU_IS_DATA(data), (VisuNodeInfo*)0);

  DBG_fprintf(stderr, "Visu Data: get distance list for node %d.\n", nodeId);
  nodeRef = visu_node_array_getFromId(VISU_NODE_ARRAY(data), nodeId);
  g_return_val_if_fail(nodeRef, (VisuNodeInfo*)0);

  nNodes = visu_node_array_getNNodes(VISU_NODE_ARRAY(data));
  infos = g_malloc(sizeof(VisuNodeInfo) * nNodes);
  
  visu_data_getNodePosition(data, nodeRef, xyzRef);

  min = G_MAXFLOAT;
  nNodes = 0;
  visu_node_array_iter_new(VISU_NODE_ARRAY(data), &iter);
  for (visu_node_array_iterStart(VISU_NODE_ARRAY(data), &iter); iter.node;
       visu_node_array_iterNextVisible(VISU_NODE_ARRAY(data), &iter))
    {
      infos[nNodes].id = iter.node->number;
      visu_data_getNodePosition(data, iter.node, xyz);
      infos[nNodes].dist =
	(xyz[0] - xyzRef[0]) * (xyz[0] - xyzRef[0]) +
	(xyz[1] - xyzRef[1]) * (xyz[1] - xyzRef[1]) +
	(xyz[2] - xyzRef[2]) * (xyz[2] - xyzRef[2]);
      if (infos[nNodes].dist > 0.0001f)
	{
	  min = MIN(min, infos[nNodes].dist);
	  nNodes += 1;
	}
    }
  infos[nNodes].id = nodeId;

  DBG_fprintf(stderr, " | min value = %g.\n", min);
  if (minVal)
    *minVal = min;

  return infos;
}


/*
   <pick data-mode="selected" data-info="">
     <node id="23" />
     <node id="16" />
     <distance ref="45" id="23" />
   </pick>
*/

/* Known elements. */
#define PICK_PARSER_ELEMENT_PICK  "pick"
#define PICK_PARSER_ELEMENT_NODE  "node"
#define PICK_PARSER_ELEMENT_DIST  "distance"
#define PICK_PARSER_ELEMENT_ANGL  "angle"
/* Known attributes. */
#define PICK_PARSER_ATTRIBUTES_MODE "info-mode"
#define PICK_PARSER_ATTRIBUTES_INFO "info-data"
#define PICK_PARSER_ATTRIBUTES_ID   "id"
#define PICK_PARSER_ATTRIBUTES_REF  "ref"
#define PICK_PARSER_ATTRIBUTES_REF2 "ref2"
#define PICK_PARSER_ATTRIBUTES_HLT  "highlight"

static gboolean startPick;
static VisuGlExtInfosDrawId mode;
static guint info;

/* This method is called for every element that is parsed.
   The user_data must be a GList of _pick_xml. When a 'surface'
   element, a new struct instance is created and prepend in the list.
   When 'hidden-by-planes' or other qualificative elements are
   found, the first surface of the list is modified accordingly. */
static void pickXML_element(GMarkupParseContext *context _U_,
			    const gchar         *element_name,
			    const gchar        **attribute_names,
			    const gchar        **attribute_values,
			    gpointer             user_data,
			    GError             **error)
{
  GList **pickList;
  int i, n;
  guint val, val2, val3;
  gboolean highlight;

  g_return_if_fail(user_data);
  pickList = (GList **)user_data;

  DBG_fprintf(stderr, "Pick parser: found '%s' element.\n", element_name);
  if (!strcmp(element_name, PICK_PARSER_ELEMENT_PICK))
    {
      /* Initialise the pickList. */
      if (*pickList)
	{
	  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
		      _("DTD error: element '%s' should appear only once."),
		      PICK_PARSER_ELEMENT_PICK);
	  return;
	}
      *pickList = (GList*)0;
      startPick = TRUE;
      /* Should have 2 mandatory attributes. */
      for (i = 0; attribute_names[i]; i++)
	{
	  if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_MODE))
	    {
	      if (!strcmp(attribute_values[i], "never"))
		mode = DRAW_NEVER;
	      else if (!strcmp(attribute_values[i], "selected"))
		mode = DRAW_SELECTED;
	      else if (!strcmp(attribute_values[i], "always"))
		mode = DRAW_ALWAYS;
	      else
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_MODE, attribute_values[i]);
		  return;
		}
	    }
	  else if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_INFO))
	    {
	      n = sscanf(attribute_values[i], "%u", &info);
	      if (n != 1 || info < 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_INFO, attribute_values[i]);
		  return;
		}
	    }
	}
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_NODE))
    {
      if (!startPick)
	{
	  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
		      _("DTD error: parent element '%s' of element '%s' is missing."),
		      PICK_PARSER_ELEMENT_PICK, PICK_PARSER_ELEMENT_NODE);
	  return;
	}
      highlight = FALSE;
      /* We parse the attributes. */
      for (i = 0; attribute_names[i]; i++)
	{
	  if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_ID))
	    {
	      n = sscanf(attribute_values[i], "%u", &val);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_ID, attribute_values[i]);
		  return;
		}
	    }
	  else if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_HLT))
	    highlight = (!strcmp(attribute_values[i], "yes") ||
			 !strcmp(attribute_values[i], "Yes"));
	}
      if (highlight)
	*pickList = g_list_prepend(*pickList, GINT_TO_POINTER(PICK_HIGHLIGHT));
      else
	*pickList = g_list_prepend(*pickList, GINT_TO_POINTER(PICK_SELECTED));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val));
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_DIST))
    {
      if (!startPick)
	{
	  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
		      _("DTD error: parent element '%s' of element '%s' is missing."),
		      PICK_PARSER_ELEMENT_PICK, PICK_PARSER_ELEMENT_DIST);
	  return;
	}

      /* We parse the attributes. */
      for (i = 0; attribute_names[i]; i++)
	{
	  if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_ID))
	    {
	      n = sscanf(attribute_values[i], "%u", &val);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_ID, attribute_values[i]);
		  return;
		}
	    }
	  else if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_REF))
	    {
	      n = sscanf(attribute_values[i], "%u", &val2);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_REF, attribute_values[i]);
		  return;
		}
	    }
	}
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(PICK_DISTANCE));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val2));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val));
    }
  else if (!strcmp(element_name, PICK_PARSER_ELEMENT_ANGL))
    {
      if (!startPick)
	{
	  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
		      _("DTD error: parent element '%s' of element '%s' is missing."),
		      PICK_PARSER_ELEMENT_PICK, PICK_PARSER_ELEMENT_ANGL);
	  return;
	}

      /* We parse the attributes. */
      for (i = 0; attribute_names[i]; i++)
	{
	  if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_ID))
	    {
	      n = sscanf(attribute_values[i], "%u", &val);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_ID, attribute_values[i]);
		  return;
		}
	    }
	  else if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_REF))
	    {
	      n = sscanf(attribute_values[i], "%u", &val2);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_REF, attribute_values[i]);
		  return;
		}
	    }
	  else if (!strcmp(attribute_names[i], PICK_PARSER_ATTRIBUTES_REF2))
	    {
	      n = sscanf(attribute_values[i], "%u", &val3);
	      if (n != 1)
		{
		  g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
			      _("DTD error: attribute '%s' has an unknown value '%s'."),
			      PICK_PARSER_ATTRIBUTES_REF2, attribute_values[i]);
		  return;
		}
	    }
	}
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(PICK_ANGLE));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val3));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val2));
      *pickList = g_list_prepend(*pickList, GINT_TO_POINTER(val));
    }
  else if (startPick)
    {
      /* We silently ignore the element if pickList is unset, but
	 raise an error if pickList has been set. */
      g_set_error(error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT,
		  _("Unexpected element '%s'."), element_name);
    }
}

/* Check when a element is closed that everything required has been set. */
static void pickXML_end(GMarkupParseContext *context _U_,
			const gchar         *element_name,
			gpointer             user_data _U_,
			GError             **error _U_)
{
  if (!strcmp(element_name, PICK_PARSER_ELEMENT_PICK))
    startPick = FALSE;
}

/* What to do when an error is raised. */
static void pickXML_error(GMarkupParseContext *context _U_,
			  GError              *error,
			  gpointer             user_data)
{
  DBG_fprintf(stderr, "Pick parser: error raised '%s'.\n", error->message);
  g_return_if_fail(user_data);

  /* We free the current list of pick. */
  g_list_free(*(GList**)user_data);
}

/**
 * visu_gl_ext_marks_parseXMLFile:
 * @marks: a #VisuGlExtMarks object.
 * @filename: a location to save to.
 * @infos: (element-type guint32) (out): a location to a #GList.
 * @drawingMode: a location to a flag.
 * @drawingInfos: a location to a flag.
 * @error: a location to store an error.
 *
 * This routines read from an XML file the description of selected
 * nodes, @mark is updated accordingly.
 *
 * Since: 3.5
 *
 * Returns: TRUE if no error.
 */
gboolean visu_gl_ext_marks_parseXMLFile(VisuGlExtMarks *marks, const gchar* filename,
                                        GList **infos, VisuGlExtInfosDrawId *drawingMode,
                                        guint *drawingInfos, GError **error)
{
  GMarkupParseContext* xmlContext;
  GMarkupParser parser;
  gboolean status;
  gchar *buffer;
  gsize size;
  GList *tmpLst;
  guint id1, id2, id3;
  VisuData *dataObj;

  g_return_val_if_fail(filename, FALSE);
  g_return_val_if_fail(infos && drawingMode && drawingInfos, FALSE);

  buffer = (gchar*)0;
  if (!g_file_get_contents(filename, &buffer, &size, error))
    return FALSE;

  /* Create context. */
  *infos = (GList*)0;
  parser.start_element = pickXML_element;
  parser.end_element   = pickXML_end;
  parser.text          = NULL;
  parser.passthrough   = NULL;
  parser.error         = pickXML_error;
  xmlContext = g_markup_parse_context_new(&parser, 0, infos, NULL);

  /* Parse data. */
  startPick = FALSE;
  status = g_markup_parse_context_parse(xmlContext, buffer, size, error);

  /* Free buffers. */
  g_markup_parse_context_free(xmlContext);
  g_free(buffer);

  if (!status)
    return FALSE;

  if (!*infos)
    {
      *error = g_error_new(G_MARKUP_ERROR, G_MARKUP_ERROR_EMPTY,
			  _("No picked node found."));
      return FALSE;
    }

  /* Need to reverse the list since elements have been prepended. */
  *infos        = g_list_reverse(*infos);
  *drawingMode  = mode;
  *drawingInfos = info;

  /* Update the current marks. */
  if (marks)
    {
      g_return_val_if_fail(VISU_IS_GL_EXT_MARKS(marks) && marks->priv->renderer, FALSE);
      
      dataObj = VISU_DATA(visu_node_array_renderer_getNodeArray(marks->priv->renderer));
      tmpLst = *infos;
      while(tmpLst)
	{
	  if (GPOINTER_TO_INT(tmpLst->data) == PICK_SELECTED)
	    tmpLst = g_list_next(tmpLst);
	  else if (GPOINTER_TO_INT(tmpLst->data) == PICK_HIGHLIGHT)
	    {
	      tmpLst = g_list_next(tmpLst);
	      id1 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      /* We silently ignore out of bound values. */
	      if (visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id1))
		toggleHighlight(marks, id1, MARKS_STATUS_SET, (gboolean*)0);
	    }
	  else if (GPOINTER_TO_INT(tmpLst->data) == PICK_DISTANCE)
	    {
	      tmpLst = g_list_next(tmpLst);
	      id1 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      tmpLst = g_list_next(tmpLst);
	      id2 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      /* We silently ignore out of bound values. */
	      if (visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id1) &&
		  visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id2))
		toggleDistance(marks, id1, id2, TRUE);
	    }
	  else if (GPOINTER_TO_INT(tmpLst->data) == PICK_ANGLE)
	    {
	      tmpLst = g_list_next(tmpLst);
	      id1 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      tmpLst = g_list_next(tmpLst);
	      id2 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      tmpLst = g_list_next(tmpLst);
	      id3 = (guint)GPOINTER_TO_INT(tmpLst->data) - 1;
	      /* We silently ignore out of bound values. */
	      if (visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id1) &&
		  visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id2) &&
		  visu_node_array_getFromId(VISU_NODE_ARRAY(dataObj), id3))
		toggleAngle(marks, id2, id1, id3, TRUE);
	    }
	  else
	    {
	      g_error("Should not be here!");
	    }
      
	  tmpLst = g_list_next(tmpLst);
	}

      g_object_notify_by_pspec(G_OBJECT(marks), properties[HIGHLIGHT_PROP]);
      if (marks->priv->hidingMode != HIDE_NONE)
        visu_node_masker_emitDirty(VISU_NODE_MASKER(marks));
      g_signal_emit(G_OBJECT(marks), signals[MEASUREMENT_CHANGE_SIGNAL], 0, dataObj);

      visu_gl_ext_setDirty(VISU_GL_EXT(marks), TRUE);
      visu_gl_ext_setDirty(VISU_GL_EXT(marks->priv->extNode), TRUE);
    }

  return TRUE;
}

/**
 * visu_gl_ext_marks_exportXMLFile:
 * @marks: a #VisuGlExtMarks object.
 * @filename: a location to save to.
 * @nodes: (element-type uint): an array of node ids.
 * @drawingMode: a flag.
 * @drawingInfos: a flag.
 * @error: a location to store an error.
 *
 * This routines export to an XML file a description of selected
 * @nodes. If @nodes is NULL, the nodes stored in the @mark will be
 * used instead.
 *
 * Since: 3.5
 *
 * Returns: TRUE if no error.
 */
gboolean visu_gl_ext_marks_exportXMLFile(VisuGlExtMarks *marks, const gchar* filename,
                                         GArray *nodes, VisuGlExtInfosDrawId drawingMode,
                                         guint drawingInfos, GError **error)
{
  gboolean valid, set;
  GString *output;
  guint i;
  char *modes[] = {"never", "selected", "always"};
  GList *tmpLst;
  struct MarkInfo_struct *mark;

  g_return_val_if_fail(marks && filename, FALSE);

  /*
   <pick data-mode="selected" data-info="0">
     <node id="23" />
     <node id="16" highlight="yes" />
     <distance ref="45" id="23" />
     <angle ref="45" ref2="65" id="23" />
   </pick>
  */

  output = g_string_new("  <pick");
  g_string_append_printf(output, " data-mode=\"%s\" data-infos=\"%d\">\n",
			 modes[drawingMode], drawingInfos);
  if (nodes)
    for (i = 0; i < nodes->len; i++)
      {
	set = FALSE;
	/* We print them only if they are not part of marks. */
	for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
	  {
	    mark = (struct MarkInfo_struct*)(tmpLst->data);
	    set = set || ( (mark->type == MARK_DISTANCE &&
			    g_array_index(nodes, guint, i) == mark->idNode2) ||
			   (mark->type == MARK_HIGHLIGHT &&
			    g_array_index(nodes, guint, i) == mark->idNode1) );
	  }
	if (!set)
	  g_string_append_printf(output, "    <node id=\"%d\" />\n", g_array_index(nodes, guint, i) + 1);
      }
  for (tmpLst = marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)(tmpLst->data);
      if (mark->type == MARK_DISTANCE)
	g_string_append_printf(output, "    <distance ref=\"%d\" id=\"%d\" />\n",
			       mark->idNode1 + 1, mark->idNode2 + 1);
      else if (mark->type == MARK_ANGLE)
	g_string_append_printf(output,
			       "    <angle ref=\"%d\" ref2=\"%d\" id=\"%d\" />\n",
			       mark->idNode1 + 1, mark->idNode2 + 1,
			       mark->idNode3 + 1);
      else if (mark->type == MARK_HIGHLIGHT)
	g_string_append_printf(output, "    <node id=\"%d\" highlight=\"yes\" />\n",
			       mark->idNode1 + 1);
    }
  g_string_append(output, "  </pick>");

  valid = tool_XML_substitute(output, filename, "pick", error);
  if (!valid)
    {
      g_string_free(output, TRUE);
      return FALSE;
    }
  
  valid = g_file_set_contents(filename, output->str, -1, error);
  g_string_free(output, TRUE);
  return valid;
}


/* Local VisuGlExt for direct node marks. */
typedef struct _ExtNodeClass ExtNodeClass;
struct _ExtNodeClass
{
  VisuGlExtClass parent;
};

static void ext_node_rebuild(VisuGlExt *ext);
static void ext_node_draw(VisuGlExt *ext);

#define TYPE_EXT_NODE          (ext_node_get_type())
GType ext_node_get_type(void);

#define EXT_NODE(obj)          (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_EXT_NODE, ExtNode))

G_DEFINE_TYPE (ExtNode, ext_node, VISU_TYPE_GL_EXT)

static void ext_node_class_init(ExtNodeClass *class)
{
  DBG_fprintf(stderr, "Visu Marks: create internal ExtNode class.\n");
  VISU_GL_EXT_CLASS(class)->rebuild = ext_node_rebuild;
  VISU_GL_EXT_CLASS(class)->draw = ext_node_draw;
}

static void ext_node_init(ExtNode *marks)
{
  DBG_fprintf(stderr, "Visu Marks: initialise new ExtNode object.\n");
  marks->marks = (VisuGlExtMarks*)0;
}

static ExtNode* ext_node_new(VisuGlExtMarks *marks)
{
  ExtNode *ext;

  ext = EXT_NODE(g_object_new(TYPE_EXT_NODE,
                              "name", "Marks", "label", _("Marks - classical"),
                              "description", _("Draw some marks on element."),
                              "nGlObj", 1, "priority", VISU_GL_EXT_PRIORITY_LOW, NULL));
  
  ext->marks = marks;

  return ext;
}

static void ext_node_rebuild(VisuGlExt *ext)
{
  ext_node_draw(ext);
}
static void ext_node_draw(VisuGlExt *ext)
{
  ExtNode *extNode = EXT_NODE(ext);
  struct MarkInfo_struct *mark;
  GList *tmpLst;
  VisuData *dataObj;

  DBG_fprintf(stderr, "Visu Marks: update the list %p"
	      " of all marks.\n", (gpointer)extNode->marks->priv->storedMarks);

  glDeleteLists(visu_gl_ext_getGlList(ext), 1);
  visu_gl_ext_setDirty(ext, FALSE);

  if (!extNode->marks->priv->storedMarks || !extNode->marks->priv->renderer)
    return;

  /* Draw the classical list. */
  visu_gl_ext_startDrawing(ext);
  glEnable(GL_LIGHTING);
  dataObj = VISU_DATA(visu_node_array_renderer_getNodeArray(extNode->marks->priv->renderer));
  for (tmpLst = extNode->marks->priv->storedMarks; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      mark = (struct MarkInfo_struct*)tmpLst->data;
      if (mark->type == MARK_HIGHLIGHT)
        {
          DBG_fprintf(stderr, " | draw mark of type %d.\n", mark->type);
          drawMarkHighlight(dataObj, mark->idNode1, extNode->marks->priv->view, extNode->marks->priv->renderer);
        }
    }
  visu_gl_ext_completeDrawing(ext);
}
