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

	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 Documentation/licence.en.txt.
*/

#include "ui_pairtree.h"

#include <stdlib.h>
#include <string.h>

#include <support.h>
#include <interface.h>

#include <visu_gtk.h>
#include <pairsModeling/link.h>
#include <pairsModeling/iface_wire.h>
#include <pairsModeling/iface_cylinder.h>
#include <pairsModeling/wire_renderer.h>
#include <pairsModeling/cylinder_renderer.h>
#include <extraGtkFunctions/gtk_elementComboBox.h>

/**
 * SECTION:ui_pairtree
 * @short_description: Defines a widget to setup a link.
 *
 * <para>A set of widgets to setup the rendring of a link.</para>
 */

/**
 * VisuUiPairTreeClass:
 * @parent: the parent class;
 *
 * A short way to identify #_VisuUiPairTreeClass structure.
 *
 * Since: 3.8
 */
/**
 * VisuUiPairTree:
 *
 * An opaque structure.
 *
 * Since: 3.8
 */
/**
 * VisuUiPairTreePrivate:
 *
 * Private fields for #VisuUiPairTree objects.
 *
 * Since: 3.8
 */
struct _VisuUiPairTreePrivate
{
  gboolean dispose_has_run;

  VisuGlExtPairs *ext;
  gulong renderer_sig;
  VisuPairSet *model;
  GBinding *model_bind;
  gulong pairs_sig;

  GtkTreeModel *treemodel, *filtermodel, *sortmodel;

  GtkWidget *toolbar, *filter;
  GtkToolItem *hideBt;

  VisuPairLink *selectedLink;
};

static void visu_ui_pair_tree_finalize(GObject* obj);
static void visu_ui_pair_tree_dispose(GObject* obj);
static void visu_ui_pair_tree_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec);
static void visu_ui_pair_tree_set_property(GObject* obj, guint property_id,
                                           const GValue *value, GParamSpec *pspec);

enum
  {
    PROP_0,
    MODEL_PROP,
    EXT_PROP,
    LINK_PROP,
    N_PROP
  };
static GParamSpec *_properties[N_PROP];

enum {
  SEL_SIGNAL,
  LAST_SIGNAL
};
static guint _signals[LAST_SIGNAL] = { 0 };

/* This enum is used to access the column
   of the liststore have the data of pairs. */
enum
  {
    POINTER_TO_LINK,
    COL_SIGNALS,
    N_COLUMNS
  };

typedef struct _RowLink
{
  guint refcount;
  
  VisuPairLink *link;
  gulong notify_sig;

  VisuPair *pair;
  gulong links_sig;
} RowLink;
#define TYPE_ROW_LINK (row_link_get_type())
static GType row_link_get_type(void);

static void _setRenderer(VisuUiPairTree *tree, VisuGlExtPairs *ext);
static void _formatElements(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                            GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _formatDrawn(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _formatDistance(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                            GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _formatPrintLength(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _formatColor(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _formatDescription(GtkTreeViewColumn *column, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static gint _sortLinks(GtkTreeModel *model, GtkTreeIter *a,
                       GtkTreeIter *b, gpointer data);
static gboolean _filter(GtkTreeModel *model, GtkTreeIter *iter, gpointer data);
static void _expandLink(VisuUiPairTree *tree, VisuPairLink *link);
static void _selectLink(VisuUiPairTree *tree, VisuPairLink *link);
static void _selectPair(VisuUiPairTree *tree, VisuPair *pair);

/* Local callbacks. */
static void onPairsNotified(VisuPairSet *set, GParamSpec *pspec, VisuUiPairTree *tree);
static void onRendererChanged(VisuUiPairTree *tree, VisuPairLink *link, VisuGlExtPairs *ext);
static void onDrawnToggled(GtkCellRendererToggle *cell_renderer,
                           gchar *path, VisuUiPairTree *self);
static void onDistanceMinEdited(GtkCellRendererText *cellrenderertext,
                                gchar *path, gchar *text, VisuUiPairTree *self);
static void onDistanceMaxEdited(GtkCellRendererText *cellrenderertext,
                                gchar *path, gchar *text, VisuUiPairTree *self);
static void onLengthToggled(GtkCellRendererToggle *cell_renderer,
                            gchar *path, VisuUiPairTree *self);
static void onSelectionChanged(VisuUiPairTree *self, GtkTreeSelection *selection);
static void onLinkNotified(GtkTreeRowReference *ref, GParamSpec *pspec, VisuPairLink *link);
static void onLinksNotified(GtkTreeRowReference *ref, GParamSpec *pspec, VisuPair *pair);
static void onAdd(VisuUiPairTree *tree, GtkButton *button);
static void onRemove(VisuUiPairTree *tree, GtkButton *button);
static void onFilter(VisuUiPairTree *tree, GtkButton *button);
static void onFilterChanged(VisuUiPairTree *tree, GList *elements, VisuUiElementCombobox *combobox);

G_DEFINE_TYPE_WITH_CODE(VisuUiPairTree, visu_ui_pair_tree, GTK_TYPE_TREE_VIEW,
                        G_ADD_PRIVATE(VisuUiPairTree))

static void visu_ui_pair_tree_class_init(VisuUiPairTreeClass *klass)
{
  DBG_fprintf(stderr, "Ui PairTree: creating the class of the widget.\n");
  DBG_fprintf(stderr, "                     - adding new signals ;\n");

  /* Connect freeing methods. */
  G_OBJECT_CLASS(klass)->dispose = visu_ui_pair_tree_dispose;
  G_OBJECT_CLASS(klass)->finalize = visu_ui_pair_tree_finalize;
  G_OBJECT_CLASS(klass)->set_property = visu_ui_pair_tree_set_property;
  G_OBJECT_CLASS(klass)->get_property = visu_ui_pair_tree_get_property;

  /**
   * VisuUiPairTree::model:
   *
   * Store the link to display properties of.
   *
   * Since: 3.8
   */
  _properties[MODEL_PROP] = g_param_spec_object("model", "Model",
                                                "link to display properties of",
                                                VISU_TYPE_PAIR_LINK, G_PARAM_READWRITE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), MODEL_PROP,
				  _properties[MODEL_PROP]);
  /**
   * VisuUiPairTree::renderer:
   *
   * Store the renderer used to draw links.
   *
   * Since: 3.8
   */
  _properties[EXT_PROP] = g_param_spec_object("renderer", "Renderer",
                                              "renderer object to draw links",
                                              VISU_TYPE_GL_EXT_PAIRS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
  g_object_class_install_property(G_OBJECT_CLASS(klass), EXT_PROP,
				  _properties[EXT_PROP]);
  /**
   * VisuUiPairTree::selected-link:
   *
   * Store the currently selected link. In case of multi-selection,
   * this property stores the first selection in the list.
   *
   * Since: 3.8
   */
  _properties[LINK_PROP] = g_param_spec_object("selected-link", "Selected link",
                                               "currently selected link (first one in a list)",
                                               VISU_TYPE_PAIR_LINK, G_PARAM_READABLE);
  g_object_class_install_property(G_OBJECT_CLASS(klass), LINK_PROP,
				  _properties[LINK_PROP]);

  /**
   * VisuUiPairTree::selection-changed:
   * @tree: the object which emits the signal ;
   * @links: (type VisuPairLink) (transfer none): a list of
   * #VisuPairLink objects.
   *
   * Gets emitted when the selection change.
   *
   * Since: 3.8
   */
  _signals[SEL_SIGNAL] =
    g_signal_new("selection-changed", G_TYPE_FROM_CLASS(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
                 0 , NULL, NULL, g_cclosure_marshal_VOID__POINTER,
                 G_TYPE_NONE, 1, G_TYPE_POINTER);
}
static void visu_ui_pair_tree_get_property(GObject* obj, guint property_id,
                                           GValue *value, GParamSpec *pspec)
{
  VisuUiPairTree *self = VISU_UI_PAIR_TREE(obj);

  DBG_fprintf(stderr, "Ui PairTree: get property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case MODEL_PROP:
      g_value_set_object(value, self->priv->model);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->model);
      break;
    case EXT_PROP:
      g_value_set_object(value, self->priv->ext);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->ext);
      break;
    case LINK_PROP:
      g_value_set_object(value, self->priv->selectedLink);
      DBG_fprintf(stderr, "%p.\n", (gpointer)self->priv->selectedLink);
      break;
    default:
      /* We don't have any other property... */
      G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, property_id, pspec);
      break;
    }
}
static void visu_ui_pair_tree_set_property(GObject* obj, guint property_id,
                                      const GValue *value, GParamSpec *pspec)
{
  VisuUiPairTree *self = VISU_UI_PAIR_TREE(obj);

  DBG_fprintf(stderr, "Ui PairTree: set property '%s' -> ",
	      g_param_spec_get_name(pspec));
  switch (property_id)
    {
    case MODEL_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      visu_ui_pair_tree_bind(self, VISU_PAIR_SET(g_value_get_object(value)));
      break;
    case EXT_PROP:
      DBG_fprintf(stderr, "%p.\n", (gpointer)g_value_get_object(value));
      _setRenderer(self, VISU_GL_EXT_PAIRS(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_ui_pair_tree_dispose(GObject *obj)
{
  VisuUiPairTree *self = VISU_UI_PAIR_TREE(obj);
  DBG_fprintf(stderr, "Ui PairTree: dispose object %p.\n", (gpointer)obj);

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

  visu_ui_pair_tree_bind(self, (VisuPairSet*)0);
  _setRenderer(self, (VisuGlExtPairs*)0);
  if (self->priv->selectedLink)
    g_object_unref(self->priv->selectedLink);
  if (self->priv->toolbar)
    g_object_unref(self->priv->toolbar);
  if (self->priv->filter)
    g_object_unref(self->priv->filter);
  gtk_tree_store_clear(GTK_TREE_STORE(VISU_UI_PAIR_TREE(obj)->priv->treemodel));

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_pair_tree_parent_class)->dispose(obj);
}
static void visu_ui_pair_tree_finalize(GObject *obj)
{
  g_return_if_fail(obj);

  DBG_fprintf(stderr, "Ui PairTree: finalize object %p.\n", (gpointer)obj);
  DBG_fprintf(stderr, " | sort has (%d) ref counts.\n", G_OBJECT(VISU_UI_PAIR_TREE(obj)->priv->sortmodel)->ref_count);
  g_object_unref(VISU_UI_PAIR_TREE(obj)->priv->sortmodel);
  DBG_fprintf(stderr, " | filter has (%d) ref counts.\n", G_OBJECT(VISU_UI_PAIR_TREE(obj)->priv->filtermodel)->ref_count);
  g_object_unref(VISU_UI_PAIR_TREE(obj)->priv->filtermodel);
  DBG_fprintf(stderr, " | model has (%d) ref counts.\n", G_OBJECT(VISU_UI_PAIR_TREE(obj)->priv->treemodel)->ref_count);
  g_object_unref(VISU_UI_PAIR_TREE(obj)->priv->treemodel);

  /* Chain up to the parent class */
  G_OBJECT_CLASS(visu_ui_pair_tree_parent_class)->finalize(obj);
  DBG_fprintf(stderr, " | freeing ... OK.\n");
}

static void visu_ui_pair_tree_init(VisuUiPairTree *obj)
{
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;
  tooltips = gtk_tooltips_new ();
#endif

  DBG_fprintf(stderr, "Extension PairTree: initializing a new object (%p).\n",
	      (gpointer)obj);
  
  obj->priv = visu_ui_pair_tree_get_instance_private(obj);
  obj->priv->dispose_has_run = FALSE;

  obj->priv->model = (VisuPairSet*)0;
  obj->priv->model_bind = (GBinding*)0;
  obj->priv->ext   = (VisuGlExtPairs*)0;

  obj->priv->selectedLink = (VisuPairLink*)0;

  obj->priv->toolbar = (GtkWidget*)0;
  obj->priv->hideBt  = (GtkToolItem*)0;

  g_object_set(G_OBJECT(obj), "rules-hint", TRUE, NULL);
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(obj), TRUE);

  /* Create the listModel for pairs data*/
  obj->priv->treemodel =
    GTK_TREE_MODEL(gtk_tree_store_new(N_COLUMNS, VISU_TYPE_PAIR_LINK, TYPE_ROW_LINK));

  /* Render the associated tree */
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("Pair"));
  gtk_tree_view_column_set_sort_indicator(column, TRUE);
  gtk_tree_view_column_set_sort_order(column, GTK_SORT_ASCENDING);
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatElements,
                                          (gpointer)0, (GDestroyNotify)0);
  gtk_tree_view_column_set_sort_column_id(column, POINTER_TO_LINK);
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(onDrawnToggled), obj);
  gtk_tree_view_column_pack_end(column, renderer, FALSE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatDrawn,
                                          (gpointer)0, (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(obj), column);

  renderer = gtk_cell_renderer_text_new();
  g_object_set(G_OBJECT(renderer), "editable", TRUE, "foreground", "blue", NULL);
  g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(onDistanceMinEdited), obj);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("From"));
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatDistance,
                                          GINT_TO_POINTER(VISU_DISTANCE_MIN),
                                          (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(obj), column);
  renderer = gtk_cell_renderer_text_new();
  g_object_set(G_OBJECT(renderer), "editable", TRUE, "foreground", "blue", NULL);
  g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(onDistanceMaxEdited), obj);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("To"));
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatDistance,
                                          GINT_TO_POINTER(VISU_DISTANCE_MAX),
                                          (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(obj), column);

  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled", G_CALLBACK(onLengthToggled), obj);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("Lg."));
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatPrintLength,
                                          (gpointer)0, (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(obj), column);

  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(column, _("Parameters"));
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_tree_view_column_pack_start(column, renderer, FALSE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatColor,
                                          (gpointer)0, (GDestroyNotify)0);
  renderer = gtk_cell_renderer_text_new();
  gtk_tree_view_column_pack_start(column, renderer, TRUE);
  gtk_tree_view_column_set_cell_data_func(column, renderer, _formatDescription,
                                          (gpointer)obj, (GDestroyNotify)0);
  gtk_tree_view_append_column(GTK_TREE_VIEW(obj), column);
  
  gtk_widget_show_all(GTK_WIDGET(obj));

  DBG_fprintf(stderr, "Ui PairTree: model has %d ref counts.\n", G_OBJECT(obj->priv->treemodel)->ref_count);
  obj->priv->filtermodel = gtk_tree_model_filter_new(obj->priv->treemodel, NULL);
  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(obj->priv->filtermodel),
					 _filter, (gpointer)obj, (GDestroyNotify)0);
  obj->priv->sortmodel = gtk_tree_model_sort_new_with_model(obj->priv->filtermodel);
  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(obj->priv->sortmodel),
				       POINTER_TO_LINK, GTK_SORT_ASCENDING);
  gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(obj->priv->sortmodel),
				  POINTER_TO_LINK, _sortLinks, (gpointer)0, NULL);

  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(obj)),
			      GTK_SELECTION_MULTIPLE);
  gtk_tree_view_set_model(GTK_TREE_VIEW(obj), obj->priv->sortmodel);
  g_signal_connect_swapped(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(obj))), "changed",
                           G_CALLBACK(onSelectionChanged), obj);
  DBG_fprintf(stderr, "Ui PairTree: model has %d ref counts.\n", G_OBJECT(obj->priv->treemodel)->ref_count);
}


/**
 * visu_ui_pair_tree_new:
 * @pairs: a #VisuGlExtPairs object.
 *
 * Creates a new #VisuUiPairTree to allow to setup link rendering characteristics.
 *
 * Since: 3.8
 *
 * Returns: a pointer to the newly created widget.
 */
GtkWidget* visu_ui_pair_tree_new(VisuGlExtPairs *pairs)
{
  DBG_fprintf(stderr,"Ui PairTree: new object.\n");
  
  return GTK_WIDGET(g_object_new(VISU_TYPE_UI_PAIR_TREE, "renderer", pairs, NULL));
}
/**
 * visu_ui_pair_tree_bind:
 * @tree: a #VisuUiPairTree object.
 * @model: a #VisuPairSet object.
 *
 * Binds @model to @tree, so every #VisuPairLink of @model are always
 * listed into @tree.
 *
 * Since: 3.8
 **/
void visu_ui_pair_tree_bind(VisuUiPairTree *tree, VisuPairSet *model)
{
  if (tree->priv->model)
    {
      if (tree->priv->model_bind && tree->priv->filter)
        g_object_unref(tree->priv->model_bind);
      /* g_signal_handler_disconnect(G_OBJECT(tree->priv->ext), tree->priv->renderer_sig); */
      g_signal_handler_disconnect(tree->priv->model, tree->priv->pairs_sig);
      g_object_unref(tree->priv->model);
    }
  if (model)
    {
      g_object_ref(model);
      /* tree->priv->renderer_sig = g_signal_connect_swapped(G_OBJECT(ext), "renderer-changed", */
      /*                                                     G_CALLBACK(onRendererChanged), tree); */
      if (tree->priv->filter)
        tree->priv->model_bind =
          g_object_bind_property(model, "data", tree->priv->filter, "nodes",
                                 G_BINDING_SYNC_CREATE);
      tree->priv->pairs_sig = g_signal_connect(model, "notify::pairs",
                                               G_CALLBACK(onPairsNotified), tree);
      onPairsNotified(model, (GParamSpec*)0, tree);
    }
  tree->priv->model = model;
  g_object_notify_by_pspec(G_OBJECT(tree), _properties[MODEL_PROP]);
}

static void _setRenderer(VisuUiPairTree *tree, VisuGlExtPairs *ext)
{
  if (tree->priv->ext)
    {
      g_signal_handler_disconnect(G_OBJECT(tree->priv->ext), tree->priv->renderer_sig);
      g_object_unref(tree->priv->ext);
    }
  if (ext)
    {
      g_object_ref(ext);
      tree->priv->renderer_sig = g_signal_connect_swapped(G_OBJECT(ext), "renderer-changed",
                                                          G_CALLBACK(onRendererChanged), tree);
    }
  tree->priv->ext = ext;
}
static void _notifyFor(GtkTreeModel *model, GtkTreeIter *iter, VisuPairLink *link)
{
  VisuPairLink *tmp;
  GtkTreePath *path;

  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &tmp, -1);
  if (tmp == link)
    {
      path = gtk_tree_model_get_path(model, iter);
      gtk_tree_model_row_changed(model, path, iter);
      gtk_tree_path_free(path);
    }
  g_object_unref(G_OBJECT(tmp));
}
static void onRendererChanged(VisuUiPairTree *tree, VisuPairLink *link, VisuGlExtPairs *ext _U_)
{
  GtkTreeIter it, child;
  gboolean valid, valid2;

  DBG_fprintf(stderr, "Ui PairTree: (onRendererChanged) model has %d ref counts.\n", G_OBJECT(tree->priv->treemodel)->ref_count);
  for (valid = gtk_tree_model_get_iter_first(tree->priv->treemodel, &it);
       valid; valid = gtk_tree_model_iter_next(tree->priv->treemodel, &it))
    {
      _notifyFor(tree->priv->treemodel, &it, link);
      for (valid2 = gtk_tree_model_iter_children(tree->priv->treemodel, &child, &it);
           valid2; valid2 = gtk_tree_model_iter_next(tree->priv->treemodel, &child))
        _notifyFor(tree->priv->treemodel, &child, link);
    }
  DBG_fprintf(stderr, "Ui PairTree: (onRendererChanged) model has %d ref counts.\n", G_OBJECT(tree->priv->treemodel)->ref_count);
}
static void _formatElements(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                            GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  gchar lbl[128];
  VisuElement *ele1, *ele2;
  VisuPairLink *link;
  GtkTreeIter parent;
  
  if (gtk_tree_model_iter_parent(model, &parent, iter))
    lbl[0] = '\0';
  else
    {
      gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
      ele1 = visu_pair_link_getFirstElement(link);
      ele2 = visu_pair_link_getSecondElement(link);
      if (strcmp(ele1->name, ele2->name) < 0)
        sprintf(lbl, "%s - %s", ele1->name, ele2->name);
      else
        sprintf(lbl, "%s - %s", ele2->name, ele1->name);
      g_object_unref(G_OBJECT(link));
      g_object_unref(G_OBJECT(ele1));
      g_object_unref(G_OBJECT(ele2));
    }
  g_object_set(G_OBJECT(cell), "text", lbl, NULL);
}
static void _formatDrawn(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  VisuPairLink *link;
  
  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  g_object_set(G_OBJECT(cell), "active", visu_pair_link_getDrawn(link), NULL);
  g_object_unref(G_OBJECT(link));
}
static void _formatDistance(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                            GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  gchar dist[18];
  VisuPairLink *link;
  ToolUnits units;
  
  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  units = visu_pair_link_getUnits(link);
  if (units == TOOL_UNITS_UNDEFINED)
    sprintf(dist, "%6.3f", visu_pair_link_getDistance(link, GPOINTER_TO_INT(data)));
  else
    sprintf(dist, "%6.3f %s", visu_pair_link_getDistance(link, GPOINTER_TO_INT(data)),
            tool_physic_getUnitLabel(units));
  g_object_set(G_OBJECT(cell), "markup", dist, NULL);
  g_object_unref(G_OBJECT(link));
}
static void _formatPrintLength(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  VisuPairLink *link;
  
  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  g_object_set(G_OBJECT(cell), "active", visu_pair_link_getPrintLength(link), NULL);
  g_object_unref(G_OBJECT(link));
}
static void _formatColor(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                         GtkTreeModel *model, GtkTreeIter *iter, gpointer data _U_)
{
  VisuPairLink *link;
  ToolColor *color;
  GdkPixbuf *pixColor;
  
  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  color = visu_pair_link_getColor(link);
  pixColor = tool_color_get_stamp(color, TRUE);
  g_object_unref(link);

  g_object_set(G_OBJECT(cell), "pixbuf", pixColor, NULL);
  g_object_unref(pixColor);
}
static void _formatDescription(GtkTreeViewColumn *column _U_, GtkCellRenderer *cell,
                               GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  VisuPairLink *link;
  VisuPairLinkRenderer *renderer;
  gchar *str;
  ToolUnits units;
  static gchar* cylinderTypes[VISU_CYLINDER_N_COLOR] = {
    "user color",
    "element color",
    "node color",
  };
  
  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  renderer = visu_gl_ext_pairs_getLinkRenderer(VISU_UI_PAIR_TREE(data)->priv->ext, link);

  str = (gchar*)0;
  if (VISU_IS_PAIR_WIRE_RENDERER(renderer))
    /* px is for pixels and pat. for pattern. */
    str = g_strdup_printf("%s %2d%s, %s %d", _("wire:"),
                          visu_pair_wire_getWidth(VISU_PAIR_WIRE(link)),
                          _("px"), _("pattern:"),
                          visu_pair_wire_getStipple(VISU_PAIR_WIRE(link)));
  else if (VISU_IS_PAIR_CYLINDER_RENDERER(renderer))
    {
      units = visu_pair_link_getUnits(link);
      if (units == TOOL_UNITS_UNDEFINED)
        /* a.u. is for arbitrary units. */
        str = g_strdup_printf("%s %3.2f %s, %s", _("radius:"),
                              visu_pair_cylinder_getRadius(VISU_PAIR_CYLINDER(link)), _("a.u."),
                              _(cylinderTypes[visu_pair_cylinder_getColorType(VISU_PAIR_CYLINDER(link))]));
      else
        str = g_strdup_printf("%s %3.2f %s, %s", _("radius:"),
                              visu_pair_cylinder_getRadius(VISU_PAIR_CYLINDER(link)), tool_physic_getUnitLabel(units),
                              _(cylinderTypes[visu_pair_cylinder_getColorType(VISU_PAIR_CYLINDER(link))]));
    }
  g_object_unref(link);

  g_object_set(G_OBJECT(cell), "text", str, NULL);
  g_free(str);
}
static gint _sortLinks(GtkTreeModel *model, GtkTreeIter *a,
                       GtkTreeIter *b, gpointer data _U_)
{
  VisuPairLink *aLink, *bLink;
  VisuElement *a_ele1, *a_ele2;
  VisuElement *b_ele1, *b_ele2;
  int cmp1, cmp2;
  gint diff;
  float a_from, a_to, b_from, b_to;

  gtk_tree_model_get(model, a, POINTER_TO_LINK, &aLink, -1);
  gtk_tree_model_get(model, b, POINTER_TO_LINK, &bLink, -1);

  a_ele1 = visu_pair_link_getFirstElement(aLink);
  a_ele2 = visu_pair_link_getSecondElement(aLink);
  b_ele1 = visu_pair_link_getFirstElement(bLink);
  b_ele2 = visu_pair_link_getSecondElement(bLink);

  cmp1 = strcmp(a_ele1->name, b_ele1->name);
  cmp2 = strcmp(a_ele2->name, b_ele2->name);
  DBG_fprintf(stderr, "Ui Pair Tree: comparing %d - %d.\n", cmp1, cmp2);

  diff = (cmp1 == 0) ? cmp2 : cmp1;
  if (diff == 0)
    {
      a_from = visu_pair_link_getDistance(aLink, VISU_DISTANCE_MIN);
      a_to   = visu_pair_link_getDistance(aLink, VISU_DISTANCE_MAX);
      b_from = visu_pair_link_getDistance(bLink, VISU_DISTANCE_MIN);
      b_to   = visu_pair_link_getDistance(bLink, VISU_DISTANCE_MAX);
      DBG_fprintf(stderr, "Ui Pair Tree: compare distances %g / %g.\n",
                  (a_to + a_from), (b_to + b_from));
      if ((a_to + a_from) == (b_to + b_from))
        diff = 0;
      else
        diff = ((a_to + a_from) < (b_to + b_from)) ? -1 : +1;
    }

  DBG_fprintf(stderr, "Ui Pair Tree: sort '%s - %s' with '%s - %s' -> %d.\n",
	      a_ele1->name, a_ele2->name, b_ele1->name, b_ele2->name, diff);

  g_object_unref(G_OBJECT(a_ele1));
  g_object_unref(G_OBJECT(a_ele2));
  g_object_unref(G_OBJECT(b_ele1));
  g_object_unref(G_OBJECT(b_ele2));

  g_object_unref(G_OBJECT(aLink));
  g_object_unref(G_OBJECT(bLink));

  return diff;
}
static gboolean _filter(GtkTreeModel *model, GtkTreeIter *iter, gpointer data)
{
  VisuUiPairTree *tree = VISU_UI_PAIR_TREE(data);
  VisuPairLink *link;
  GList *elements;
  float start, stop;
  VisuElement *ele1, *ele2;
  const gchar *label;
  gboolean visible;

  gtk_tree_model_get(model, iter, POINTER_TO_LINK, &link, -1);
  start = visu_pair_link_getDistance(link, VISU_DISTANCE_MIN);
  stop  = visu_pair_link_getDistance(link, VISU_DISTANCE_MAX);
  ele1 = visu_pair_link_getFirstElement(link);
  ele2 = visu_pair_link_getSecondElement(link);
  g_object_unref(link);

  elements = (tree->priv->filter) ? visu_ui_element_combobox_getSelection(VISU_UI_ELEMENT_COMBOBOX(tree->priv->filter)) : (GList*)0;
  if (elements)
    {
      label = visu_element_getName(VISU_ELEMENT(elements->data));
      visible = !strcmp(label, ele1->name) || !strcmp(label, ele2->name);
      g_list_free(elements);
    }
  else
    visible = TRUE;

  g_object_unref(ele1);
  g_object_unref(ele2);

  /* Add the visibility tag due to the hide button. */
  visible = visible &&
    (!tree->priv->hideBt ||
     !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(tree->priv->hideBt)) ||
     (stop > start));

  return visible;
}

static RowLink* row_link_copy(RowLink *row)
{
  if (row)
    {
      row->refcount += 1;
      DBG_fprintf(stderr, "Ui Pair Tree: ref row (%d).\n", row->refcount);
    }
  return row;
}
static void row_link_free(RowLink *row)
{
  if (!row)
    return;

  DBG_fprintf(stderr, "Ui Pair Tree: unref row (%d).\n", row->refcount);
  row->refcount -= 1;
  if (!row->refcount)
    {
      if (row->link)
        {
          g_signal_handler_disconnect(row->link, row->notify_sig);
          g_object_unref(row->link);
        }
      if (row->pair)
        {
          g_signal_handler_disconnect(row->pair, row->links_sig);
          g_object_unref(row->pair);
        }
      g_free(row);
    }
}
static GType row_link_get_type(void)
{
  static GType g_define_type_id = 0;

  if (g_define_type_id == 0)
    g_define_type_id =
      g_boxed_type_register_static("RowLink", (GBoxedCopyFunc)row_link_copy,
                                   (GBoxedFreeFunc)row_link_free);
  return g_define_type_id;
}
static void _freeRow(GtkTreeRowReference *ref, GClosure *closure _U_)
{
  gtk_tree_row_reference_free(ref);
}
static void _addLink(GtkTreeStore *store, GtkTreeIter *iter,
                     GtkTreeIter *parent, VisuPair *pair, VisuPairLink *link)
{
  GtkTreePath *path;
  GtkTreeRowReference *ref;
  RowLink *row;

#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 9
  gtk_tree_store_insert_with_values(store, iter, parent, 0,
                                    POINTER_TO_LINK, link, -1);
#else
  gtk_tree_store_append(store, iter, parent);
  gtk_tree_store_set(store, iter, POINTER_TO_LINK, link, -1);
#endif
  path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), iter);
  ref = gtk_tree_row_reference_new(GTK_TREE_MODEL(store), path);
  gtk_tree_path_free(path);
  row = g_malloc0(sizeof(RowLink));
  row->link = g_object_ref(link);
  row->notify_sig = g_signal_connect_data(G_OBJECT(link), "notify",
                                          G_CALLBACK(onLinkNotified), ref,
                                          (GClosureNotify)_freeRow,
                                          G_CONNECT_SWAPPED);
  row->pair = pair;
  if (pair)
    {
      g_object_ref(pair);
      row->links_sig = g_signal_connect_swapped(G_OBJECT(pair), "notify::links",
                                                G_CALLBACK(onLinksNotified), ref);
    }
  gtk_tree_store_set(store, iter, COL_SIGNALS, row, -1);
}
static void onPairsNotified(VisuPairSet *set, GParamSpec *pspec _U_,
                            VisuUiPairTree *tree)
{
  guint i, j;
  GtkTreeIter iter, child;
  VisuPair *pair;
  VisuPairLink *link;

  DBG_fprintf(stderr, "Ui PairTree: (onPairsNotified) model has %d ref counts.\n", G_OBJECT(tree->priv->treemodel)->ref_count);
  gtk_tree_store_clear(GTK_TREE_STORE(tree->priv->treemodel));
  i = 0;
  while ((pair = visu_pair_set_getNthPair(set, i++)))
    {
      j = 0;
      link = visu_pair_getNthLink(pair, j++);
      _addLink(GTK_TREE_STORE(tree->priv->treemodel), &iter, (GtkTreeIter*)0, pair, link);
      while ((link = visu_pair_getNthLink(pair, j++)))
        _addLink(GTK_TREE_STORE(tree->priv->treemodel), &child, &iter, (VisuPair*)0, link);
    };
  DBG_fprintf(stderr, "Ui PairTree: (onPairsNotified) model has %d ref counts.\n", G_OBJECT(tree->priv->treemodel)->ref_count);
}
static void onDrawnToggled(GtkCellRendererToggle *cell_renderer,
                           gchar *path, VisuUiPairTree *self)
{
  GtkTreeIter iter;
  VisuPairLink *link;

  gtk_tree_model_get_iter_from_string(self->priv->sortmodel, &iter, path);
  gtk_tree_model_get(self->priv->sortmodel, &iter, POINTER_TO_LINK, &link, -1);
  visu_pair_link_setDrawn(link, !gtk_cell_renderer_toggle_get_active(cell_renderer));
  g_object_unref(G_OBJECT(link));
}
static void _changeDistance(VisuUiPairTree *self, const gchar *text, GtkTreeIter *iter,
                            VisuPairLinkDistances at)
{
  float value;
  char *error;
  GtkTreePath *_path;
  VisuPairLink *link;

  value = (float)strtod(text, &error);
  if ((value == 0.f && error == text) || value < 0.f)
    {
      _path = gtk_tree_model_get_path(self->priv->sortmodel, iter);
      gtk_tree_model_row_changed(self->priv->sortmodel, _path, iter);
      gtk_tree_path_free(_path);
    }
  else
    {
      gtk_tree_model_get(self->priv->sortmodel, iter, POINTER_TO_LINK, &link, -1);
      visu_pair_link_setDistance(link, value, at);
      g_object_unref(G_OBJECT(link));
    }
}
static void onDistanceMinEdited(GtkCellRendererText *cellrenderertext _U_,
                                gchar *path, gchar *text, VisuUiPairTree *self)
{
  GtkTreeIter iter;

  gtk_tree_model_get_iter_from_string(self->priv->sortmodel, &iter, path);
  _changeDistance(self, text, &iter, VISU_DISTANCE_MIN);
}
static void onDistanceMaxEdited(GtkCellRendererText *cellrenderertext _U_,
                                gchar *path, gchar *text, VisuUiPairTree *self)
{
  GtkTreeIter iter;

  gtk_tree_model_get_iter_from_string(self->priv->sortmodel, &iter, path);
  _changeDistance(self, text, &iter, VISU_DISTANCE_MAX);
}
static void onLengthToggled(GtkCellRendererToggle *cell_renderer,
                            gchar *path, VisuUiPairTree *self)
{
  GtkTreeIter iter;
  VisuPairLink *link;

  gtk_tree_model_get_iter_from_string(self->priv->sortmodel, &iter, path);
  gtk_tree_model_get(self->priv->sortmodel, &iter, POINTER_TO_LINK, &link, -1);
  visu_pair_link_setPrintLength(link, !gtk_cell_renderer_toggle_get_active(cell_renderer));
  g_object_unref(G_OBJECT(link));
}
static void onSelectionChanged(VisuUiPairTree *self, GtkTreeSelection *selection)
{
  GList *rows;
  GtkTreeModel *model;
  GtkTreeIter iter;
  GList *it, *links;
  VisuPairLink *link;

  if (self->priv->selectedLink)
    g_object_unref(self->priv->selectedLink);
  
  rows = it = gtk_tree_selection_get_selected_rows(selection, &model);

  self->priv->selectedLink = (VisuPairLink*)0;
  if (rows && gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)rows->data))
    gtk_tree_model_get(model, &iter, POINTER_TO_LINK, &self->priv->selectedLink, -1);

  g_object_notify_by_pspec(G_OBJECT(self), _properties[LINK_PROP]);

  links = (GList*)0;
  while (it && gtk_tree_model_get_iter(model, &iter, (GtkTreePath*)it->data))
    {
      gtk_tree_model_get(model, &iter, POINTER_TO_LINK, &link, -1);
      links = g_list_prepend(links, link);
      it = g_list_next(it);
    }
  g_signal_emit(G_OBJECT(self), _signals[SEL_SIGNAL], 0, links);
  g_list_free_full(links, (GDestroyNotify)g_object_unref);

  g_list_free_full(rows, (GDestroyNotify)gtk_tree_path_free);
}
static void onLinkNotified(GtkTreeRowReference *ref, GParamSpec *pspec _U_, VisuPairLink *link _U_)
{
  GtkTreeModel *model;
  GtkTreePath *path;
  GtkTreeIter iter;

  model = gtk_tree_row_reference_get_model(ref);
  path = gtk_tree_row_reference_get_path(ref);

  g_return_if_fail(gtk_tree_model_get_iter(model, &iter, path));

  gtk_tree_model_row_changed(model, path, &iter);

  gtk_tree_path_free(path);
}
static void onLinksNotified(GtkTreeRowReference *ref, GParamSpec *pspec _U_, VisuPair *pair)
{
  GtkTreeStore *model;
  GtkTreePath *path;
  GtkTreeIter iter, child;
  guint j;
  VisuPairLink *link;

  model = GTK_TREE_STORE(gtk_tree_row_reference_get_model(ref));
  path = gtk_tree_row_reference_get_path(ref);

  g_return_if_fail(gtk_tree_model_get_iter(GTK_TREE_MODEL(model), &iter, path));

  gtk_tree_store_remove(model, &iter);

  gtk_tree_path_free(path);

  j = 0;
  link = visu_pair_getNthLink(pair, j++);
  _addLink(model, &iter, (GtkTreeIter*)0, pair, link);
  while ((link = visu_pair_getNthLink(pair, j++)))
    _addLink(model, &child, &iter, (VisuPair*)0, link);
}

/**
 * visu_ui_pair_tree_getToolbar:
 * @tree: a #VisuUiPairTree object.
 *
 * Creates a #GtkToolbar with the action button used to add or remove
 * #VisuPairLink in a @tree.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #GtkToolbar object.
 **/
GtkWidget* visu_ui_pair_tree_getToolbar(VisuUiPairTree *tree)
{
  GtkToolItem *item;
  GtkWidget *wd;

  g_return_val_if_fail(VISU_IS_UI_PAIR_TREE(tree), (GtkWidget*)0);

  if (tree->priv->toolbar)
    return tree->priv->toolbar;

  tree->priv->toolbar = gtk_vbox_new(FALSE, 0);

#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 5
  wd = gtk_label_new(_("Manage links: "));
  gtk_label_set_angle(GTK_LABEL(wd), 90.);
  gtk_box_pack_end(GTK_BOX(tree->priv->toolbar), wd, FALSE, FALSE, 0);
#endif

  wd = gtk_toolbar_new();
  gtk_box_pack_start(GTK_BOX(tree->priv->toolbar), wd, TRUE, TRUE, 0);
  gtk_orientable_set_orientation(GTK_ORIENTABLE(wd),
                                 GTK_ORIENTATION_VERTICAL);
  gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
  gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);

  item = gtk_toggle_tool_button_new();
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-find");
  g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(onFilter), (gpointer)tree);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  gtk_tool_item_set_tooltip_text(item, _("Show/hide the undrawn pairs."));
  tree->priv->hideBt = item;

  item = gtk_tool_button_new(NULL, NULL);
  g_object_bind_property(tree, "selected-link", item, "sensitive", G_BINDING_SYNC_CREATE);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-add");
  g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(onAdd), (gpointer)tree);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  item = gtk_tool_button_new(NULL, NULL);
  g_object_bind_property(tree, "selected-link", item, "sensitive", G_BINDING_SYNC_CREATE);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-remove");
  g_signal_connect_swapped(G_OBJECT(item), "clicked", G_CALLBACK(onRemove), (gpointer)tree);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);

  gtk_widget_show_all(tree->priv->toolbar);

  return g_object_ref(tree->priv->toolbar);
}
static void onAdd(VisuUiPairTree *tree, GtkButton *button _U_)
{
  VisuPair *pair;
  gfloat zeros[2] = {0.f, 0.f};
  VisuPairLink *link;

  pair = visu_pair_set_getFromLink(tree->priv->model, tree->priv->selectedLink);
  link = visu_pair_addLink(pair, zeros);
  _expandLink(tree, link);
  _selectLink(tree, link);
}
static void onRemove(VisuUiPairTree *tree, GtkButton *button _U_)
{
  VisuPair *pair;

  pair = visu_pair_set_getFromLink(tree->priv->model, tree->priv->selectedLink);
  visu_pair_removeLink(pair, tree->priv->selectedLink);
  _selectPair(tree, pair);
}
static void onFilter(VisuUiPairTree *tree, GtkButton *button _U_)
{
  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(tree->priv->filtermodel));
}

/**
 * visu_ui_pair_tree_getFilter:
 * @tree: a #VisuUiPairTree object.
 *
 * Creates a #VisuElement combo widget to be used to filter the list
 * of pairs.
 *
 * Since: 3.8
 *
 * Returns: (transfer none): a #VisuUiElementCombobox object.
 **/
GtkWidget* visu_ui_pair_tree_getFilter(VisuUiPairTree *tree)
{
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 11
  GList *lst;
#endif

  g_return_val_if_fail(VISU_IS_UI_PAIR_TREE(tree), (GtkWidget*)0);

  if (tree->priv->filter)
    return tree->priv->filter;

  tree->priv->filter = visu_ui_element_combobox_new(FALSE, TRUE, (const gchar*)0);
  g_signal_connect(tree->priv->filter, "destroy",
                   G_CALLBACK(gtk_widget_destroyed), &tree->priv->filter);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 11
  lst = gtk_cell_layout_get_cells(GTK_CELL_LAYOUT(tree->priv->filter));
  if (lst)
    {
      g_object_set(G_OBJECT(lst->data), "scale", 0.75, NULL);
      g_list_free(lst);
    }
#endif
  if (tree->priv->model)
    tree->priv->model_bind =
      g_object_bind_property(tree->priv->model, "data", tree->priv->filter, "nodes",
                             G_BINDING_SYNC_CREATE);
  g_signal_connect_swapped(G_OBJECT(tree->priv->filter), "element-selected",
                           G_CALLBACK(onFilterChanged), (gpointer)tree);

  return g_object_ref(tree->priv->filter);
}
static void onFilterChanged(VisuUiPairTree *tree, GList *elements _U_,
                            VisuUiElementCombobox *combobox _U_)
{
  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(tree->priv->filtermodel));
}

static void _expandLink(VisuUiPairTree *tree, VisuPairLink *link)
{
  gboolean valid;
  GtkTreeIter iter;
  RowLink *row;
  GtkTreePath *path;

  for (valid = gtk_tree_model_get_iter_first(tree->priv->sortmodel, &iter);
       valid; valid = gtk_tree_model_iter_next(tree->priv->sortmodel, &iter))
    {
      gtk_tree_model_get(tree->priv->sortmodel, &iter, COL_SIGNALS, &row, -1);
      if (visu_pair_contains(row->pair, link))
        {
          g_boxed_free(TYPE_ROW_LINK, row);
          path = gtk_tree_model_get_path(tree->priv->sortmodel, &iter);
          gtk_tree_view_expand_row(GTK_TREE_VIEW(tree), path, TRUE);
          gtk_tree_path_free(path);
          return;
        }
      g_boxed_free(TYPE_ROW_LINK, row);
    }
}
static gboolean _selectLinkAt(VisuUiPairTree *tree, VisuPairLink *link, GtkTreeIter *at)
{
  RowLink *row;

  gtk_tree_model_get(tree->priv->sortmodel, at, COL_SIGNALS, &row, -1);
  if (row->link == link)
    {
      g_boxed_free(TYPE_ROW_LINK, row);
      gtk_tree_selection_select_iter
        (gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), at);
      return TRUE;
    }
  g_boxed_free(TYPE_ROW_LINK, row);
  return FALSE;
}
static void _selectLink(VisuUiPairTree *tree, VisuPairLink *link)
{
  gboolean valid, valid2;
  GtkTreeIter iter, child;

  gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)));
  for (valid = gtk_tree_model_get_iter_first(tree->priv->sortmodel, &iter);
       valid; valid = gtk_tree_model_iter_next(tree->priv->sortmodel, &iter))
    {
      if (_selectLinkAt(tree, link, &iter))
        return;
      for (valid2 = gtk_tree_model_iter_children(tree->priv->sortmodel, &child, &iter);
           valid2; valid2 = gtk_tree_model_iter_next(tree->priv->sortmodel, &child))
        if (_selectLinkAt(tree, link, &child))
          return;
    }  
}
static void _selectPair(VisuUiPairTree *tree, VisuPair *pair)
{
  gboolean valid;
  GtkTreeIter iter;
  RowLink *row;

  gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)));
  for (valid = gtk_tree_model_get_iter_first(tree->priv->sortmodel, &iter);
       valid; valid = gtk_tree_model_iter_next(tree->priv->sortmodel, &iter))
    {
      gtk_tree_model_get(tree->priv->sortmodel, &iter, COL_SIGNALS, &row, -1);
      if (row->pair == pair)
        {
          g_boxed_free(TYPE_ROW_LINK, row);
          gtk_tree_selection_select_iter
            (gtk_tree_view_get_selection(GTK_TREE_VIEW(tree)), &iter);
          return;
        }
      g_boxed_free(TYPE_ROW_LINK, row);
    }  
}
