/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien
	CALISTE, laboratoire L_Sim, (2001-2013)
  
	Adresse 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 : Damien
	CALISTE, laboratoire L_Sim, (2001-2013)

	E-mail address:
	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 COPYING.
*/
#include "infos.h"

#include <GL/gl.h>

#include <openGLFunctions/objectList.h>
#include <openGLFunctions/text.h>
#include <renderingMethods/elementAtomic.h>

/**
 * SECTION:infos
 * @short_description: give the capability to draw some information
 * near each node.
 *
 * <para>This part is used to draw some information near the
 * nodes. This information can be the one of a #VisuNodeProperty or
 * something else. When read from a #VisuNodeProperty, just giving the
 * name will produce the right output. In other cases a print routine
 * must be given.</para>
 */

/* Interface for a routine to draw the informations into a label. */
typedef void (*DrawInfosFunc)(VisuData *data, VisuElement *element,
			      VisuNode *node, VisuNodeValues *values);
static void drawNumber(VisuData *data, VisuElement *element,
		       VisuNode *node, VisuNodeValues *dataNode);
static void drawElement(VisuData *data, VisuElement *element,
			VisuNode *node, VisuNodeValues *dataNode);
static void drawProps(VisuData *data, VisuElement *element,
		      VisuNode *node, VisuNodeValues *values);

/**
 * VisuGlExtInfosClass:
 * @parent: the parent class;
 *
 * A short way to identify #_VisuGlExtInfosClass structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtInfos:
 *
 * An opaque structure.
 *
 * Since: 3.7
 */
/**
 * VisuGlExtInfosPrivate:
 *
 * Private fields for #VisuGlExtInfos objects.
 *
 * Since: 3.7
 */
struct _VisuGlExtInfosPrivate
{
  gboolean dispose_has_run;

  /* What to draw. */
  GArray *nodes;
  DrawInfosFunc draw;
  
  /* Object signals. */
  VisuNodeArrayRenderer *renderer;
  gulong popDec, popInc, pos_sig, col_sig, vis_sig, siz_sig;
  VisuGlView *view;
  gulong thetaChg, phiChg, omegaChg;
  VisuNodeValues *values;
  gulong nodeChgd;
};

enum
  {
    PROP_0,
    SELECTION_PROP,
    VALUES_PROP,
    N_PROP
  };
static GParamSpec *_properties[N_PROP];

static void visu_gl_ext_infos_finalize(GObject* obj);
static void visu_gl_ext_infos_dispose(GObject* obj);
static void visu_gl_ext_infos_get_property(GObject* obj, guint property_id,
                                            GValue *value, GParamSpec *pspec);
static void visu_gl_ext_infos_set_property(GObject* obj, guint property_id,
                                            const GValue *value, GParamSpec *pspec);
static void visu_gl_ext_infos_rebuild(VisuGlExt *ext);
static void visu_gl_ext_infos_draw(VisuGlExt *infos);
static gboolean visu_gl_ext_infos_setGlView(VisuGlExt *infos, VisuGlView *view);

/* Callbacks. */
static void onPopulationIncrease(VisuNodeArrayRenderer *renderer, GArray *newNodes, gpointer user_data);
static void onPopulationDecrease(VisuNodeArrayRenderer *renderer, GArray *oldNodes, gpointer user_data);
static void onNodeValuesChanged(VisuNodeValues *values, VisuNode *node, gpointer user_data);

static void _setNodeValues(VisuGlExtInfos *infos, VisuNodeValues *values);
static void _setDirty(VisuGlExt *ext);

G_DEFINE_TYPE_WITH_CODE(VisuGlExtInfos, visu_gl_ext_infos, VISU_TYPE_GL_EXT,
                        G_ADD_PRIVATE(VisuGlExtInfos))

static void visu_gl_ext_infos_class_init(VisuGlExtInfosClass *klass)
{
  DBG_fprintf(stderr, "Extension Infos: creating the class of the object.\n");
  /* DBG_fprintf(stderr, "                - adding new signals ;\n"); */

  /* DBG_fprintf(stderr, "                - adding new resources ;\n"); */

  /* Connect the overloading methods. */
  G_OBJECT_CLASS(klass)->dispose  = visu_gl_ext_infos_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_gl_ext_infos_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_gl_ext_infos_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_gl_ext_infos_get_property;
  VISU_GL_EXT_CLASS(klass)->rebuild = visu_gl_ext_infos_rebuild;
  VISU_GL_EXT_CLASS(klass)->draw = visu_gl_ext_infos_draw;
  VISU_GL_EXT_CLASS(klass)->setGlView = visu_gl_ext_infos_setGlView;

  /**
   * VisuGlExtInfos::selection:
   *
   * The ids of selected nodes.
   *
   * Since: 3.8
   */
  _properties[SELECTION_PROP] = g_param_spec_boxed
    ("selection", "Selection", "ids of selected nodes.",
     G_TYPE_ARRAY, G_PARAM_READWRITE);
  
  /**
   * VisuGlExtInfos::values:
   *
   * Some #VisuNodeValues to display the values of.
   *
   * Since: 3.8
   */
  _properties[VALUES_PROP] = g_param_spec_object
    ("values", "Values", "some node values to display.",
     VISU_TYPE_NODE_VALUES, G_PARAM_READWRITE);
  
  g_object_class_install_properties(G_OBJECT_CLASS(klass), N_PROP, _properties);
}

static void visu_gl_ext_infos_init(VisuGlExtInfos *obj)
{
  DBG_fprintf(stderr, "Extension Infos: initializing a new object (%p).\n",
	      (gpointer)obj);

  obj->priv = visu_gl_ext_infos_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  /* Private data. */
  obj->priv->nodes    = (GArray*)0;
  obj->priv->draw     = drawNumber;
  obj->priv->values   = (VisuNodeValues*)0;
  obj->priv->renderer = (VisuNodeArrayRenderer*)0;
  obj->priv->view     = (VisuGlView*)0;
}
static void visu_gl_ext_infos_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec)
{
  DBG_fprintf(stderr, "Extension Infos: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case SELECTION_PROP:
      g_value_set_boxed(value, VISU_GL_EXT_INFOS(obj)->priv->nodes);
      DBG_fprintf(stderr, "%p.\n", g_value_get_boxed(value));
      break;
    case VALUES_PROP:
      g_value_set_object(value, VISU_GL_EXT_INFOS(obj)->priv->values);
      DBG_fprintf(stderr, "%p.\n", g_value_get_object(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_infos_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec)
{
  VisuGlExtInfos *self = VISU_GL_EXT_INFOS(obj);

  DBG_fprintf(stderr, "Extension Infos: set property '%s'.\n",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case SELECTION_PROP:
      if (self->priv->nodes)
        g_array_unref(self->priv->nodes);
      self->priv->nodes = g_value_dup_boxed(value);
      break;
    case VALUES_PROP:
      _setNodeValues(self, VISU_NODE_VALUES(g_value_get_object(value)));
      self->priv->draw = g_value_get_object(value) ? drawProps : NULL;
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
  visu_gl_ext_setDirty(VISU_GL_EXT(obj), TRUE);
}

/* This method can be called several times.
   It should unref all of its reference to
   GObjects. */
static void visu_gl_ext_infos_dispose(GObject* obj)
{
  VisuGlExtInfos *infos;

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

  infos = VISU_GL_EXT_INFOS(obj);
  if (infos->priv->dispose_has_run)
    return;
  infos->priv->dispose_has_run = TRUE;

  /* Disconnect signals. */
  visu_gl_ext_infos_setGlView(VISU_GL_EXT(infos), (VisuGlView*)0);
  visu_gl_ext_infos_setDataRenderer(infos, (VisuNodeArrayRenderer*)0);
  _setNodeValues(infos, (VisuNodeValues*)0);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_gl_ext_infos_parent_class)->dispose(obj);
}
/* This method is called once only. */
static void visu_gl_ext_infos_finalize(GObject* obj)
{
  VisuGlExtInfos *infos;

  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Extension Infos: finalize object %p.\n", (gpointer)obj);

  infos = VISU_GL_EXT_INFOS(obj);
  /* Free privs elements. */
  DBG_fprintf(stderr, "Extension Infos: free private infos.\n");
  if (infos->priv->nodes)
    g_array_unref(infos->priv->nodes);

  /* Chain up to the parent class */
  DBG_fprintf(stderr, "Extension Infos: chain to parent.\n");
  G_OBJECT_CLASS(visu_gl_ext_infos_parent_class)->finalize(obj);
  DBG_fprintf(stderr, "Extension Infos: freeing ... OK.\n");
}
/**
 * visu_gl_ext_infos_new:
 * @name: (allow-none): the name of the #VisuGlExt.
 *
 * Create a new #VisuGlExt to represent information on nodes.
 *
 * Since: 3.7
 *
 * Returns: a new #VisuGlExtInfos object.
 **/
VisuGlExtInfos* visu_gl_ext_infos_new(const gchar *name)
{
  char *name_ = VISU_GL_EXT_INFOS_ID;
  char *description = _("Draw informations on nodes.");

  DBG_fprintf(stderr,"Extension Infos: new object.\n");
  
  return g_object_new(VISU_TYPE_GL_EXT_INFOS,
                      "name", (name)?name:name_, "label", _(name),
                      "description", description, "nGlObj", 1,
                      "priority", VISU_GL_EXT_PRIORITY_HIGH, NULL);
}

/********************/
/* Public routines. */
/********************/
/**
 * visu_gl_ext_infos_setDataRenderer:
 * @infos: The #VisuGlExtInfos to attached to.
 * @renderer: the #VisuNodeArrayRenderer displaying the data.
 *
 * Attach a #VisuNodeArrayRenderer to render to and setup the infos.
 *
 * Since: 3.7
 *
 * Returns: TRUE if the model was actually changed.
 **/
gboolean visu_gl_ext_infos_setDataRenderer(VisuGlExtInfos *infos,
                                           VisuNodeArrayRenderer *renderer)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_INFOS(infos), FALSE);

  if (renderer == infos->priv->renderer)
    return FALSE;

  if (infos->priv->renderer)
    {
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->vis_sig);
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->pos_sig);
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->siz_sig);
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->col_sig);
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->popDec);
      g_signal_handler_disconnect(G_OBJECT(infos->priv->renderer), infos->priv->popInc);
      g_object_unref(infos->priv->renderer);
    }
  if (renderer)
    {
      g_object_ref(renderer);
      infos->priv->vis_sig = g_signal_connect_swapped(G_OBJECT(renderer), "nodes::visibility",
                                                      G_CALLBACK(_setDirty), (gpointer)infos);
      infos->priv->pos_sig = g_signal_connect_swapped(G_OBJECT(renderer), "nodes::position",
                                                      G_CALLBACK(_setDirty), (gpointer)infos);
      infos->priv->siz_sig = g_signal_connect_swapped(G_OBJECT(renderer), "element-size-changed",
                                                      G_CALLBACK(_setDirty), (gpointer)infos);
      infos->priv->col_sig = g_signal_connect_swapped(G_OBJECT(renderer), "element-notify::color",
                                                      G_CALLBACK(_setDirty), (gpointer)infos);
      infos->priv->popDec = g_signal_connect(G_OBJECT(renderer), "nodes::population-decrease",
                                             G_CALLBACK(onPopulationDecrease), (gpointer)infos);
      infos->priv->popInc = g_signal_connect(G_OBJECT(renderer), "nodes::population-increase",
                                             G_CALLBACK(onPopulationIncrease), (gpointer)infos);
    }
  infos->priv->renderer = renderer;

  visu_gl_ext_setDirty(VISU_GL_EXT(infos), TRUE);
  return TRUE;
}
static void _setNodeValues(VisuGlExtInfos *infos, VisuNodeValues *values)
{
  g_return_if_fail(VISU_IS_GL_EXT_INFOS(infos));

  if (values == infos->priv->values)
    return;

  if (infos->priv->values)
    {
      g_signal_handler_disconnect(G_OBJECT(infos->priv->values), infos->priv->nodeChgd);
      g_object_unref(infos->priv->values);
    }
  if (values)
    {
      g_object_ref(values);
      infos->priv->nodeChgd =
        g_signal_connect(G_OBJECT(values), "changed",
                         G_CALLBACK(onNodeValuesChanged), (gpointer)infos);
    }
  infos->priv->values = values;
  g_object_notify_by_pspec(G_OBJECT(infos), _properties[VALUES_PROP]);
}
/**
 * visu_gl_ext_infos_drawIds:
 * @infos: the #VisuGlExtInfos object to update.
 * @nodes: (element-type guint) (transfer full): an integer list.
 *
 * With this extension,
 * some the number of nodes will be drawn on them. Numbers can be drawn and
 * all nodes (set @nodes to a NULL pointer), or to a restricted list of nodes
 * represented by their numbers. In this case, @nodes can have whatever length
 * but must be terminated by a negative integer. This array is then owned by the
 * extension and should not be freed.
 *
 * Returns: TRUE if the status was actually changed.
 */
gboolean visu_gl_ext_infos_drawIds(VisuGlExtInfos *infos, GArray *nodes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_INFOS(infos), FALSE);

  if (infos->priv->nodes)
    g_array_unref(infos->priv->nodes);
  infos->priv->nodes    = nodes ? g_array_ref(nodes) : (GArray*)0;
  g_object_notify_by_pspec(G_OBJECT(infos), _properties[SELECTION_PROP]);
  infos->priv->draw     = drawNumber;
  _setNodeValues(infos, (VisuNodeValues*)0);

  visu_gl_ext_setDirty(VISU_GL_EXT(infos), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_infos_drawElements:
 * @infos: the #VisuGlExtInfos object to update.
 * @nodes: (element-type guint) (transfer full): an integer list.
 *
 * As visu_gl_ext_infos_drawIds(), but draw the names of elements instead of their
 * numbers.
 *
 * Returns: TRUE if the status was actually changed.
 */
gboolean visu_gl_ext_infos_drawElements(VisuGlExtInfos *infos, GArray *nodes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_INFOS(infos), FALSE);

  if (infos->priv->nodes)
    g_array_unref(infos->priv->nodes);
  infos->priv->nodes    = nodes ? g_array_ref(nodes) : (GArray*)0;
  g_object_notify_by_pspec(G_OBJECT(infos), _properties[SELECTION_PROP]);
  infos->priv->draw     = drawElement;
  _setNodeValues(infos, (VisuNodeValues*)0);

  visu_gl_ext_setDirty(VISU_GL_EXT(infos), TRUE);
  return TRUE;
}
/**
 * visu_gl_ext_infos_drawNodeProperties:
 * @infos: the #VisuGlExtInfos object to update.
 * @values: the #VisuNodeValues to render on nodes.
 * @nodes: (element-type guint) (transfer full): an integer list.
 *
 * Draw @values on selected @nodes.
 *
 * Since: 3.8
 *
 * Returns: TRUE.
 **/
gboolean visu_gl_ext_infos_drawNodeProperties(VisuGlExtInfos *infos,
                                              VisuNodeValues *values, GArray *nodes)
{
  g_return_val_if_fail(VISU_IS_GL_EXT_INFOS(infos) &&
                       VISU_IS_NODE_VALUES(values), FALSE);

  if (infos->priv->nodes)
    g_array_unref(infos->priv->nodes);
  infos->priv->nodes    = nodes ? g_array_ref(nodes) : (GArray*)0;
  g_object_notify_by_pspec(G_OBJECT(infos), _properties[SELECTION_PROP]);
  infos->priv->draw     = (DrawInfosFunc)drawProps;
  _setNodeValues(infos, values);

  visu_gl_ext_setDirty(VISU_GL_EXT(infos), TRUE);
  return TRUE;
}

static gboolean visu_gl_ext_infos_setGlView(VisuGlExt *infos, VisuGlView *view)
{
  VisuGlExtInfosPrivate *priv = VISU_GL_EXT_INFOS(infos)->priv;

  if (view == priv->view)
    return FALSE;

  if (priv->view)
    {
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->thetaChg);
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->phiChg);
      g_signal_handler_disconnect(G_OBJECT(priv->view), priv->omegaChg);
      g_clear_object(&priv->view);
    }
  if (view)
    {
      priv->view = g_object_ref(view);
      priv->thetaChg =
        g_signal_connect_swapped(G_OBJECT(view), "notify::theta",
                                 G_CALLBACK(_setDirty), (gpointer)infos);
      priv->phiChg =
        g_signal_connect_swapped(G_OBJECT(view), "notify::phi",
                                 G_CALLBACK(_setDirty), (gpointer)infos);
      priv->omegaChg =
        g_signal_connect_swapped(G_OBJECT(view), "notify::omega",
                                 G_CALLBACK(_setDirty), (gpointer)infos);
    }

  visu_gl_ext_setDirty(VISU_GL_EXT(infos), TRUE);
  return TRUE;
}
static void visu_gl_ext_infos_draw(VisuGlExt *infos)
{
  float modelView[16];
  float delta[3], xyz[3], size;
  VisuNodeArray *nodes;
  VisuNodeArrayRendererIter iter;
  guint i;
  gboolean valid;
  VisuGlExtInfosPrivate *priv = VISU_GL_EXT_INFOS(infos)->priv;

  glDeleteLists(visu_gl_ext_getGlList(VISU_GL_EXT(infos)), 1);
  visu_gl_ext_setDirty(VISU_GL_EXT(infos), FALSE);

  /* Nothing to draw; */
  if(!priv->view || !priv->renderer || !priv->draw)
    return;

  /* Get the camera orientation. */
  glGetFloatv(GL_MODELVIEW_MATRIX, modelView);

  visu_gl_text_initFontList();
  visu_gl_ext_startDrawing(infos);

  glPushAttrib(GL_ENABLE_BIT);
  glDisable(GL_LIGHTING);

  /* If nodes is NULL, we draw for all nodes. */
  nodes = visu_node_array_renderer_getNodeArray(priv->renderer);
  if (!priv->nodes || !priv->nodes->len)
    {
      DBG_fprintf(stderr, " | use all the nodes.\n");
      for (valid = visu_node_array_renderer_iter_new(priv->renderer, &iter, TRUE);
           valid; valid = visu_node_array_renderer_iter_next(&iter))
        if (visu_element_getRendered(iter.element))
          {
            visu_element_renderer_colorize(iter.renderer, VISU_ELEMENT_RENDERER_INVERT);
            size = visu_element_renderer_getExtent(iter.renderer);
            delta[0] = size * modelView[2];
            delta[1] = size * modelView[6];
            delta[2] = size * modelView[10];
            for(visu_node_array_iterRestartNode(iter.parent.array, &iter.parent);
                iter.parent.node;
                visu_node_array_iterNextNode(iter.parent.array, &iter.parent))
              if (iter.parent.node->rendered)
                {
                  visu_data_getNodePosition(VISU_DATA(iter.parent.array), iter.parent.node, xyz);
                  glRasterPos3f(xyz[0] + delta[0],
                                xyz[1] + delta[1],
                                xyz[2] + delta[2]);
                  priv->draw(VISU_DATA(nodes), iter.element, iter.parent.node,
                             priv->values);
                }
          }
    }
  else
    {
      DBG_fprintf(stderr, " | use a restricted list of nodes.\n");
      /* nodes is not NULL, we draw for the given nodes only. */
      for (i = 0; i < priv->nodes->len; i++)
	{
          DBG_fprintf(stderr, " | %d\n", g_array_index(priv->nodes, guint, i));
	  iter.parent.node = visu_node_array_getFromId
            (nodes, g_array_index(priv->nodes, guint, i));
	  g_return_if_fail(iter.parent.node);
          iter.element = visu_node_array_getElement(nodes, iter.parent.node);
	  if (visu_element_getRendered(iter.element) && iter.parent.node->rendered)
	    {
              iter.renderer = visu_node_array_renderer_get(priv->renderer, iter.element);
              visu_element_renderer_colorize(iter.renderer, VISU_ELEMENT_RENDERER_INVERT);
	      size = visu_element_renderer_getExtent(iter.renderer);
	      delta[0] = size * modelView[2];
	      delta[1] = size * modelView[6];
	      delta[2] = size * modelView[10];
	      visu_data_getNodePosition(VISU_DATA(nodes), iter.parent.node, xyz);
	      glRasterPos3f(xyz[0] + delta[0],
			    xyz[1] + delta[1],
			    xyz[2] + delta[2]);
	      priv->draw(VISU_DATA(nodes), iter.element, iter.parent.node,
                         priv->values);
	    }
	}
    }
  glPopAttrib();
  visu_gl_ext_completeDrawing(infos);
}

/*********************/
/* Private routines. */
/*********************/
static void visu_gl_ext_infos_rebuild(VisuGlExt *ext)
{
  visu_gl_text_rebuildFontList();
  visu_gl_ext_setDirty(ext, TRUE);
  visu_gl_ext_infos_draw(ext);
}
static void drawNumber(VisuData *data _U_, VisuElement *element _U_,
		       VisuNode *node, VisuNodeValues *dataNode _U_)
{
  gchar str[10];

  sprintf(str, "%d", node->number + 1);
  visu_gl_text_drawChars(str, VISU_GL_TEXT_NORMAL);
}
static void drawElement(VisuData *data _U_, VisuElement *element,
			VisuNode *node _U_, VisuNodeValues *dataNode _U_)
{
  visu_gl_text_drawChars(element->name, VISU_GL_TEXT_NORMAL);
}
static void drawProps(VisuData *data _U_, VisuElement *element _U_,
		      VisuNode *node, VisuNodeValues *values)
{
  gchar *label;

  label = visu_node_values_toString(values, node);
  if (label)
    visu_gl_text_drawChars(label, VISU_GL_TEXT_NORMAL);
  g_free(label);
}

/*************/
/* Callbacks */
/*************/
static void _setDirty(VisuGlExt *ext)
{
  visu_gl_ext_setDirty(ext, TRUE);
}
static void onPopulationIncrease(VisuNodeArrayRenderer *renderer _U_, GArray *newNodes _U_,
				 gpointer user_data)
{
  VisuGlExtInfos *infos = VISU_GL_EXT_INFOS(user_data);

  /* If we draw all nodes, then we must redraw. */
  if (!infos->priv->nodes || !infos->priv->nodes->len)
    visu_gl_ext_setDirty(VISU_GL_EXT(user_data), TRUE);
}
static void onPopulationDecrease(VisuNodeArrayRenderer *renderer _U_, GArray *oldNodes, gpointer user_data)
{
  VisuGlExtInfos *infos = VISU_GL_EXT_INFOS(user_data);
  guint i;

  DBG_fprintf(stderr, "Extension Informations: caught the 'PopulationDecrease'"
	      " signal, update and rebuild in list nodes case.\n");
  /* If we draw a list of nodes, then we must remove some and redraw. */

  /* We remove all old nodes. */
  if (infos->priv->nodes)
    {
      for (i = 0; i < oldNodes->len; i++)
        g_array_remove_index_fast(infos->priv->nodes, g_array_index(oldNodes, guint, i));
      g_object_notify_by_pspec(G_OBJECT(infos), _properties[SELECTION_PROP]);
    }
  visu_gl_ext_setDirty(VISU_GL_EXT(user_data), TRUE);
}
static void onNodeValuesChanged(VisuNodeValues *values _U_, VisuNode *node, gpointer user_data)
{
  VisuGlExtInfos *infos = VISU_GL_EXT_INFOS(user_data);
  guint i;

  DBG_fprintf(stderr, "Extension Informations: caught the 'changed'"
	      " signal, rebuild.\n");
  if (!infos->priv->nodes || !infos->priv->nodes->len || !node)
    {
      visu_gl_ext_setDirty(VISU_GL_EXT(user_data), TRUE);
      return;
    }

  for (i = 0; i < infos->priv->nodes->len; i++)
    if (g_array_index(infos->priv->nodes, guint, i) == node->number)
      {
        visu_gl_ext_setDirty(VISU_GL_EXT(user_data), TRUE);
        return;
      }
}
