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

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

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

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

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

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

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

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

	The fact that you are presently reading this means that you have had
	knowledge of the CeCILL license and that you accept its terms. You can
	find a copy of this licence shipped with this software at Documentation/licence.en.txt.
*/
#include "gtk_pairs.h"
#include "pairsModeling/externalGtkPairsExtensions.h"
#include "gtk_main.h"
#include "extraGtkFunctions/gtk_colorComboBoxWidget.h"
#include "gtk_renderingWindowWidget.h"

#include "callbacks.h"
#include "interface.h"
#include "support.h"

#include "visu_tools.h"
#include "visu_object.h"
#include "visu_data.h"
#include "visu_pairs.h"

struct specBuildFunc_struct
{
  buildPairsWindowFunc area;
  buildGtkPairsSpecificLabelFunc label;
  signalPairsSelectedFunc selected;
};

GtkWidget *widgetColor;
GtkWidget *hboxCheckWidgets;
gulong widgetColorSignalId;
gulong gtkPairs_spinMinSignalId, gtkPairs_spinMaxSignalId;

GtkWidget *gtkPairs_spinMin, *gtkPairs_spinMax;

/* This enum is used to access the column
   of the liststore have the data of pairs. */
enum
  {
    GTK_PAIRS_COLUMN_DRAWN,
    GTK_PAIRS_COLUMN_LABEL_ELE1,
    GTK_PAIRS_COLUMN_LABEL_ELE2,
    GTK_PAIRS_COLUMN_LABEL_DISTANCE,
    GTK_PAIRS_COLUMN_PIXBUF_COLOR,
    GTK_PAIRS_COLUMN_LABEL_USER,
    GTK_PAIRS_COLUMN_POINTER_TO_PAIR,
    N_GTK_PAIRS_COLUMN
  };
/* The list store that controls the drawing of the pairs data. */
GtkListStore *gtkPairs_listStore;
GtkTreeSelection *gtkPairs_treeSelection;

/* Combobox model. */
GtkListStore *gtkPairs_comboModel;
enum
  {
    GTK_PAIRS_COLUMN_PAIRS_ICON, /* A pixmap to represent the pairs */
    GTK_PAIRS_COLUMN_PAIRS_NAME, /* The label shown */
    GTK_PAIRS_COLUMN_PAIRS_POINTER,  /* Pointer to the pairs extension. */
    GTK_PAIRS_COLUMN_PAIRS_SPEC_BUILD,  /* Pointer to the pairs specBuildFunc_struct. */
    N_GTK_PAIRS_METHODS_PAIRS
  };
GtkWidget *gtkPairs_comboWidget;


signalPairsSelectedFunc currentSignalSelectedFunc;
void addLabelToColorCombo(GtkListStore *list, GtkTreeIter *iter, Color* color);
void onGtkPairsDrawnToggled(GtkCellRendererToggle *cell_renderer,
			    gchar *path, gpointer user_data);
void minMaxChanged(GtkSpinButton *spin, GtkTreeSelection *tree, int minMax);
void selectionChanged(GtkTreeSelection *tree, gpointer data);
void minChanged(GtkSpinButton *spin, gpointer userData);
void maxChanged(GtkSpinButton *spin, gpointer userData);
void pairsMethodChanged(GtkComboBox *combobox, gpointer userData);
void colorChanged(ColorComboBox *colorWd, Color *color, gpointer userData);
void rebuildPairsViewportChild(GObject *obj, VisuData *dataObj, gpointer bool);
static void rebuildPairsOnResources(GObject *obj, gpointer bool);

#define GTK_PAIRS_HELP_TEXT _("Modifications, as color or wire width, are applied only to selected rows. Use <control> to select more than one row at a time.")

/* Create the label for the distance, need to free result. */
gchar* createDistanceLabel(PairsData *data);
/* Create the area for specific widgets of current rendering pairs method. */
void setSpecificArea(struct specBuildFunc_struct *specBuild);

/* Connect the signal and initialise values. */
void initPairsWindow()
{
  GList *pnt;
  int i;
  struct specBuildFunc_struct *specBuild;
  GtkTreeIter iter;

  pairsDialog = (GtkWidget*)0;
  specificWindow = (GtkWidget*)0;
  pairsIsHide = 1;
  pairsIsOutOfDate = 0;
  currentSignalSelectedFunc = (signalPairsSelectedFunc)0;

  /* Create the listModel for pairs method*/
  gtkPairs_comboModel = gtk_list_store_new(N_GTK_PAIRS_METHODS_PAIRS,
					   GDK_TYPE_PIXBUF,
					   G_TYPE_STRING,
					   G_TYPE_POINTER,
					   G_TYPE_POINTER);
  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(gtkPairs_comboModel),
				       GTK_PAIRS_COLUMN_PAIRS_NAME, GTK_SORT_ASCENDING);
  i = 0;
  pnt = getAllPairsMethods();
  while(pnt)
    {
      if (listGtkPairsInitFunc[i] && listGtkPairsBuildFunc[i] &&
	  listGtkPairsLabelFunc[i] && listGtkPairsSignalFunc[i])
	{
	  listGtkPairsInitFunc[i]();
	  specBuild = g_malloc(sizeof(struct specBuildFunc_struct));
	  specBuild->area = listGtkPairsBuildFunc[i];
	  specBuild->label = listGtkPairsLabelFunc[i];
	  specBuild->selected = listGtkPairsSignalFunc[i];
	  gtk_list_store_append(gtkPairs_comboModel, &iter);
	  gtk_list_store_set(gtkPairs_comboModel, &iter,
			     GTK_PAIRS_COLUMN_PAIRS_ICON, (GdkPixbuf*)0,
			     GTK_PAIRS_COLUMN_PAIRS_NAME, ((PairsExtension*)pnt->data)->printName,
			     GTK_PAIRS_COLUMN_PAIRS_POINTER, pnt->data,
			     GTK_PAIRS_COLUMN_PAIRS_SPEC_BUILD, (gpointer)specBuild,
			     -1);
	}
      i++;
      pnt = g_list_next(pnt);
    }

  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		   G_CALLBACK(rebuildPairsViewportChild),
		   (gpointer)0);
  g_signal_connect(G_OBJECT(visu), "resourcesLoaded",
		   G_CALLBACK(rebuildPairsOnResources),
		   (gpointer)0);
}

void buildPairsDialog()
{
  GtkWidget *wd;
  GtkWidget *viewport;
  PairsExtension* pairs, *tmpPairs;
  GtkWidget *vbox;
  GtkWidget *label;
  GtkObject *adj;

  GtkWidget *gtkPairs_treeView;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;
  GtkTreeIter iter;
  gboolean validIter;
  struct specBuildFunc_struct *specBuild;
  char* markup;

  /* Create the listModel for pairs data*/
  gtkPairs_listStore = gtk_list_store_new (N_GTK_PAIRS_COLUMN,
					   G_TYPE_BOOLEAN,
					   G_TYPE_STRING,
					   G_TYPE_STRING,
					   G_TYPE_STRING,
					   GDK_TYPE_PIXBUF,
					   G_TYPE_STRING,
					   G_TYPE_POINTER);
  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(gtkPairs_listStore),
				       GTK_PAIRS_COLUMN_LABEL_ELE1, GTK_SORT_ASCENDING);

  pairsDialog = create_pairsDialog();
  gtk_window_set_type_hint(GTK_WINDOW(pairsDialog), GDK_WINDOW_TYPE_HINT_UTILITY);
  
  gtk_widget_set_name(pairsDialog, "message");

  wd = lookup_widget(pairsDialog, "labelTitlePairs");
  gtk_widget_set_name(wd, "message_title");

  wd = lookup_widget(pairsDialog, "labelInfoPairs");
  gtk_widget_set_name(wd, "label_info");
  markup = g_markup_printf_escaped("<span size=\"smaller\">%s</span>",
				   GTK_PAIRS_HELP_TEXT);
  gtk_label_set_markup(GTK_LABEL(wd), markup);
  g_free(markup);

  viewport = lookup_widget(pairsDialog, "viewport1");
  gtk_widget_set_name(viewport, "message_viewport");

  wd = gtk_bin_get_child(GTK_BIN(viewport));
  if (wd)
    gtk_widget_destroy(wd);

  /* Create the TreeView to represent the pairs data. */
  gtkPairs_treeView = gtk_tree_view_new ();
  gtkPairs_treeSelection = gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkPairs_treeView));
  gtk_widget_show(gtkPairs_treeView);
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(gtkPairs_treeView), TRUE);
  gtk_container_add(GTK_CONTAINER(viewport), gtkPairs_treeView);

  vboxPairsDialog = lookup_widget(pairsDialog, "vboxPairsDialog");

  /* Create widgets that apply to all checked pairs. */
  hboxCheckWidgets = gtk_hbox_new (FALSE, 0);
  gtk_widget_set_sensitive(hboxCheckWidgets, FALSE);
  gtk_widget_show (hboxCheckWidgets);
  gtk_box_pack_start (GTK_BOX (vboxPairsDialog), hboxCheckWidgets, FALSE, FALSE, 0);

  label = gtk_label_new(_("From : "));
  gtk_widget_show (label);
  gtk_box_pack_start(GTK_BOX(hboxCheckWidgets), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
  adj = gtk_adjustment_new(0, 0, 100, 0.1, 0.5, 10);
  gtkPairs_spinMin = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 3);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(gtkPairs_spinMin), TRUE);
  gtk_widget_show(gtkPairs_spinMin);
  gtk_box_pack_start(GTK_BOX(hboxCheckWidgets), gtkPairs_spinMin, FALSE, FALSE, 0);

  label = gtk_label_new(_("to : "));
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hboxCheckWidgets), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment(GTK_MISC(label), 1.0, 0.5);
  adj = gtk_adjustment_new(0, 0, 100, 0.1, 0.5, 10);
  gtkPairs_spinMax = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 3);
  gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(gtkPairs_spinMax), TRUE);
  gtk_widget_show(gtkPairs_spinMax);
  gtk_box_pack_start(GTK_BOX(hboxCheckWidgets), gtkPairs_spinMax, FALSE, FALSE, 0);

  label = gtk_label_new (_("Color: "));
  gtk_widget_show (label);
  gtk_box_pack_start (GTK_BOX (hboxCheckWidgets), label, TRUE, TRUE, 0);
  gtk_misc_set_alignment (GTK_MISC (label), 1, 0.5);

  widgetColor = colorComboBox_new(FALSE);
  gtk_widget_show(widgetColor);
  gtk_box_pack_start(GTK_BOX(hboxCheckWidgets), widgetColor, FALSE, FALSE, 0);

  /* Render the associated tree */
  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onGtkPairsDrawnToggled), (gpointer)0);
  column = gtk_tree_view_column_new_with_attributes ("", renderer,
						     "active", GTK_PAIRS_COLUMN_DRAWN,
						     NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Ele."), renderer,
						     "text", GTK_PAIRS_COLUMN_LABEL_ELE1,
						     NULL);
  gtk_tree_view_column_set_sort_column_id(column, GTK_PAIRS_COLUMN_LABEL_ELE1);
  gtk_tree_view_column_set_sort_indicator(column, TRUE);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Ele."), renderer,
						     "text", GTK_PAIRS_COLUMN_LABEL_ELE2,
						     NULL);
  gtk_tree_view_column_set_sort_column_id(column, GTK_PAIRS_COLUMN_LABEL_ELE2);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Distances"), renderer,
						     "text", GTK_PAIRS_COLUMN_LABEL_DISTANCE,
						     NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);
  renderer = gtk_cell_renderer_pixbuf_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Color"), renderer,
						     "pixbuf", GTK_PAIRS_COLUMN_PIXBUF_COLOR,
						     NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);
  renderer = gtk_cell_renderer_text_new ();
  column = gtk_tree_view_column_new_with_attributes (_("Specific values"), renderer,
						     "text", GTK_PAIRS_COLUMN_LABEL_USER,
						     NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(gtkPairs_treeView), column);

  /* Set parameters for the tree */
  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkPairs_treeView)),
			      GTK_SELECTION_MULTIPLE);
  gtk_tree_view_set_model(GTK_TREE_VIEW(gtkPairs_treeView), GTK_TREE_MODEL(gtkPairs_listStore));

  /* We create the tree widget that show the methods. */
  gtkPairs_comboWidget = gtk_combo_box_new();
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkPairs_comboWidget), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(gtkPairs_comboWidget), renderer, "pixbuf",
				GTK_PAIRS_COLUMN_PAIRS_ICON);
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(gtkPairs_comboWidget), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(gtkPairs_comboWidget), renderer, "text",
				GTK_PAIRS_COLUMN_PAIRS_NAME);
  gtk_widget_show(gtkPairs_comboWidget);

  g_signal_connect(G_OBJECT(gtkPairs_comboWidget), "changed",
		   G_CALLBACK(pairsMethodChanged), (gpointer)0);

  gtk_combo_box_set_model(GTK_COMBO_BOX(gtkPairs_comboWidget),
			  GTK_TREE_MODEL(gtkPairs_comboModel));

  wd = lookup_widget(pairsDialog, "hboxPairsModel");
  pairs = getCurrentPairsMethods();
  if (!getAllPairsMethods())
    {
      gtk_list_store_append(gtkPairs_comboModel, &iter);
      /* Entry of the pairs combo box : no method is available. */
      gtk_list_store_set(gtkPairs_comboModel, &iter,
			 GTK_PAIRS_COLUMN_PAIRS_ICON, (GdkPixbuf*)0,
			 GTK_PAIRS_COLUMN_PAIRS_NAME, _("No method available to draw pairs..."),
			 GTK_PAIRS_COLUMN_PAIRS_POINTER, (gpointer)0,
			 -1);
    }
  validIter = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkPairs_comboModel), &iter);
  while (validIter)
    {
      gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_comboModel), &iter,
			 GTK_PAIRS_COLUMN_PAIRS_POINTER, &tmpPairs,
			 GTK_PAIRS_COLUMN_PAIRS_SPEC_BUILD, &specBuild,
			 -1);
      if (pairs == tmpPairs)
	{
	  gtk_combo_box_set_active_iter(GTK_COMBO_BOX(gtkPairs_comboWidget), &iter);
	  validIter = FALSE;
	}
      else
	validIter = gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkPairs_comboModel), &iter);
    }
  gtk_box_pack_start(GTK_BOX(wd), gtkPairs_comboWidget, TRUE, TRUE, 0);
  

  /* Connect local signals. */
  g_signal_connect(G_OBJECT(gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkPairs_treeView))),
		   "changed", G_CALLBACK(selectionChanged), (gpointer)0);
  widgetColorSignalId = g_signal_connect(G_OBJECT(widgetColor), "color-selected",
					 G_CALLBACK(colorChanged), (gpointer)gtkPairs_treeView);
  gtkPairs_spinMinSignalId = g_signal_connect(G_OBJECT(gtkPairs_spinMin), "value_changed",
					      G_CALLBACK(minChanged),
					      (gpointer)gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkPairs_treeView)));
  gtkPairs_spinMaxSignalId = g_signal_connect(G_OBJECT(gtkPairs_spinMax), "value_changed",
					      G_CALLBACK(maxChanged),
					      (gpointer)gtk_tree_view_get_selection(GTK_TREE_VIEW(gtkPairs_treeView)));

  /* Set the flag that indicates that interior is not built yet. */
  pairsIsOutOfDate = 1;

  DBG_fprintf(stderr, "Gtk Pairs : Pairs are first built.\n");
}

void gtkPairsBuild_viewport()
{
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  rebuildPairsViewportChild((GObject*)0, renderingWindowGet_visuData(window), (gpointer)0);
}
static void rebuildPairsOnResources(GObject *obj, gpointer bool)
{
  GList *dataObjList;

  dataObjList = visuDataGet_allObjects();
  /* Update the viewport with the first list element only. */
  if (dataObjList)
    rebuildPairsViewportChild(obj, VISU_DATA(dataObjList->data), bool);
}
void rebuildPairsViewportChild(GObject *obj, VisuData *dataObj, gpointer bool)
{
  int i, j;
  PairsData *data;
  gchar *lblDist;
  Color *color;
  GdkPixbuf *pixColor;
  GtkTreeIter iter;
  struct specBuildFunc_struct *specBuild;
  gchar *label;
  gboolean validIter, drawn;

  DBG_fprintf(stderr, "Gtk Pairs : caught 'dataReadyForRendering' signal,"
	      " rebuilding tree.\n");
  if (!pairsDialog || pairsIsHide)
    {
      DBG_fprintf(stderr, "Gtk Pairs : Pairs become out of date.\n");
      pairsIsOutOfDate = 1;
      return;
    }

  if (!dataObj)
    {
      DBG_fprintf(stderr,"Gtk Pairs : not rebuilding tree since no VisuData.\n");
      return;
    }

  if (!pairsIsOutOfDate && visuDataGet_changeElementFlag((VisuData*)dataObj))
    {
      DBG_fprintf(stderr,"Gtk Pairs : not rebuilding tree since no element changed.\n");
      return;
    }
  
  gtk_list_store_clear(GTK_LIST_STORE(gtkPairs_listStore));

  validIter = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(gtkPairs_comboWidget), &iter);
  if (!validIter)
    {
      fprintf(stderr, "WARNING! Can't find the selected pairs extension.\n");
      return;
    }
  specBuild = (struct specBuildFunc_struct*)0;
  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_comboModel), &iter,
		     GTK_PAIRS_COLUMN_PAIRS_SPEC_BUILD, &specBuild,
		     -1);
		     
  for(i = 0; i < ((VisuData*)dataObj)->ntype; i++)
    {
      for (j = 0; j <= i; j++)
	{
	  DBG_fprintf(stderr, " | pairs %s - %s",
		      ((VisuData*)dataObj)->fromIntToVisuElement[i]->name,
		      ((VisuData*)dataObj)->fromIntToVisuElement[j]->name);

	  data = getPairsData(((VisuData*)dataObj)->fromIntToVisuElement[i],
			      ((VisuData*)dataObj)->fromIntToVisuElement[j]);

	  DBG_fprintf(stderr, " (%d).\n", GPOINTER_TO_INT(data));

	  /* Add the PairsData to the liststore. */
	  drawn = getPairsDrawn(data);
	  lblDist = createDistanceLabel(data);
	  color = getPairsProperty(data, "color");
	  pixColor = colorComboBoxGet_pixbufFromColor(COLOR_COMBOX(widgetColor), color);
	  if (specBuild && specBuild->label)
	    label = specBuild->label(data);
	  else
	    label = (gchar*)0;
	  gtk_list_store_append(gtkPairs_listStore, &iter);
	  gtk_list_store_set(gtkPairs_listStore, &iter,
			     GTK_PAIRS_COLUMN_DRAWN, drawn,
			     GTK_PAIRS_COLUMN_LABEL_ELE1,
			     ((VisuData*)dataObj)->fromIntToVisuElement[i]->name,
			     GTK_PAIRS_COLUMN_LABEL_ELE2,
			     ((VisuData*)dataObj)->fromIntToVisuElement[j]->name,
			     GTK_PAIRS_COLUMN_LABEL_DISTANCE, lblDist,
			     GTK_PAIRS_COLUMN_PIXBUF_COLOR, pixColor,
			     GTK_PAIRS_COLUMN_LABEL_USER, label,
			     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, (gpointer)data,
			     -1);
	  g_free(lblDist);
	  if (label)
	    g_free(label);
	}
    }
  validIter = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkPairs_listStore), &iter);
  if (validIter)
    gtk_tree_selection_select_iter(gtkPairs_treeSelection, &iter);

  pairsIsOutOfDate = 0;
  DBG_fprintf(stderr, "Gtk Pairs : Pairs are rebuilt.\n");
}

gchar* createDistanceLabel(PairsData *data)
{
  GString *label;

  if (!data)
    {
      fprintf(stderr, "WARNING! Wrong argument 'data' in a call"
	      " to 'createDistanceLabel'.\n");
      return (gchar*)0;
    }

  label = g_string_new("");
  g_string_printf(label, _("from %7.3f to %7.3f"),
		  data->minMax[PAIRS_MIN],
		  data->minMax[PAIRS_MAX]);
  return g_string_free(label, FALSE);
}

void setSpecificArea(struct specBuildFunc_struct *specBuild)
{
  buildPairsWindowFunc buildSpecific;
  GtkWidget *wdlabel;


  /* change the specific area. */
  DBG_fprintf(stderr, "Gtk Pairs : previous specific widget : %d\n",
	      GPOINTER_TO_INT(specificWindow));
  if (specificWindow)
    gtk_widget_destroy(specificWindow);

  if (specBuild)
    buildSpecific = specBuild->area;
  else
    buildSpecific = (buildPairsWindowFunc)0;
  DBG_fprintf(stderr, "Gtk Pairs : method to build specific widgets : %d\n",
	      GPOINTER_TO_INT(buildSpecific));
  if (buildSpecific)
    specificWindow = buildSpecific();
  else
    {
      wdlabel = gtk_label_new(_("No parameters for these kinds of pairs."));
      specificWindow = wdlabel;
    }
  gtk_box_pack_start(GTK_BOX(vboxPairsDialog), specificWindow, FALSE, FALSE, 10);
  gtk_widget_show(specificWindow);
}

int gtkPairsSet_specificLabels(buildGtkPairsSpecificLabelFunc getLabelFunc,
			       setGtkPairsSpecificValueFunc setValueFunc,
			       gpointer userData)
{
  int res;
  GList* selectedRows, *tmpLst;
  gboolean validIter;
  GdkPixbuf *pixColor;
  PairsData *pdata;
  gchar *lblSpec;
  GtkTreeIter iter;

  res = 0;
  selectedRows = gtk_tree_selection_get_selected_rows(gtkPairs_treeSelection, NULL);
  tmpLst = selectedRows;
  while (tmpLst)
    {
      validIter = gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkPairs_listStore),
					  &iter, (GtkTreePath*)tmpLst->data);
      gtk_tree_path_free((GtkTreePath*)tmpLst->data);
      if (!validIter)
	fprintf(stderr, "INTERNAL ERROR! Wrong 'path' variable in"
		" 'gtkPairsSet_specificLabels' method.\n");
      else
	{
	  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &pdata,
			     -1);

	  res = setValueFunc(pdata, userData) || res;

	  lblSpec = getLabelFunc(pdata);
	  gtk_list_store_set(GTK_LIST_STORE(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_LABEL_USER, lblSpec,
			     -1);
	  g_free(lblSpec);
	}
      tmpLst = g_list_next(tmpLst);
    }
  g_list_free(selectedRows);
  return res;
}


/*************/
/* Callbacks */
/*************/

void colorChanged(ColorComboBox *colorWd, Color *color, gpointer userData)
{
  int res;
  PairsData *data;
  GtkTreeIter iter;
  GList *chkLst;
  GtkWidget *chk, *image;

  GList* selectedRows, *tmpLst;
  gboolean validIter;
  GdkPixbuf *pixColor;

  if (!userData)
    {
      fprintf(stderr, "WARNING! 'colorChanged' with a null 'userData' argument.\n");
      return;
    }

  pixColor = colorComboBoxGet_selectedPixbuf(colorWd);

  res = 0;
  selectedRows = gtk_tree_selection_get_selected_rows(gtk_tree_view_get_selection(GTK_TREE_VIEW(userData)), NULL);
  tmpLst = selectedRows;
  while (tmpLst)
    {
      validIter = gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkPairs_listStore),
					  &iter, (GtkTreePath*)tmpLst->data);
      gtk_tree_path_free((GtkTreePath*)tmpLst->data);
      if (!validIter)
	fprintf(stderr, "INTERNAL ERROR! Wrong 'path' variable in"
		" 'colorChanged' method.\n");
      else
	{
	  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &data,
			     -1);
	  res = setPairsColor(data, color) || res;
	  gtk_list_store_set(GTK_LIST_STORE(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_PIXBUF_COLOR, pixColor,
			     -1);
	}
      tmpLst = g_list_next(tmpLst);
    }
  g_list_free(selectedRows);

  if (res)
    gtkPairs_createPairs();
}
void minChanged(GtkSpinButton *spin, gpointer userData)
{
  minMaxChanged(spin, GTK_TREE_SELECTION(userData), PAIRS_MIN);
}
void maxChanged(GtkSpinButton *spin, gpointer userData)
{
  minMaxChanged(spin, GTK_TREE_SELECTION(userData), PAIRS_MAX);
}
void pairsMethodChanged(GtkComboBox *combobox, gpointer userData)
{
  gchar *label;
  int res;
  GList *tmpChk;
  PairsData *data;
  GtkWidget *specWd;
  struct specBuildFunc_struct *specBuild;
  GtkTreeIter iter;
  gboolean validIter;
  PairsExtension *pairsMethod;
  RenderingWindow *window;

  validIter = gtk_combo_box_get_active_iter(combobox, &iter);
  if (!validIter)
    {
      fprintf(stderr, "WARNING! No pairs method have been found.\n");
      return;
    }
  specBuild = (struct specBuildFunc_struct*)0;
  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_comboModel), &iter,
		     GTK_PAIRS_COLUMN_PAIRS_POINTER, &pairsMethod,
		     GTK_PAIRS_COLUMN_PAIRS_SPEC_BUILD, &specBuild,
		     -1);

  DBG_fprintf(stderr, "Gtk Pairs : set combobox to '%s'.\n", pairsMethod->name);

  /* change the specific area. */
  setSpecificArea(specBuild);

  /* change the label area. */
  DBG_fprintf(stderr, "Gtk Pairs : changing specific labels.\n");
  if (specBuild && specBuild->label)
    {
      validIter = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(gtkPairs_listStore), &iter);
      while (validIter)
	{
	  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &data,
			     -1);
	  label = specBuild->label(data);
	  gtk_list_store_set(GTK_LIST_STORE(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_LABEL_USER, label,
			     -1);
	  g_free(label);
	  validIter = gtk_tree_model_iter_next(GTK_TREE_MODEL(gtkPairs_listStore), &iter);
	}
    }
  /* Change the pointer on the current method that signals
     to specific area that a new data has been checked. */
  currentSignalSelectedFunc = specBuild->selected;

  res = setPairsMethod(pairsMethod);
  if (res)
    gtkPairs_createPairs();
}
void onGtkPairsDrawnToggled(GtkCellRendererToggle *cell_renderer,
			    gchar *path, gpointer user_data)
{
  gboolean validIter, drawn;
  GtkTreeIter iter;
  int res;
  PairsData *pdata;

  validIter = gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(gtkPairs_listStore), &iter, path);
  if (!validIter)
    {
      fprintf(stderr, "INTERNAL ERROR! Wrong 'path' argument in"
	      " 'onGtkPairsDrawnToggled' method.\n");
      return;
    }

  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
		     GTK_PAIRS_COLUMN_DRAWN, &drawn,
		     -1);
  gtk_list_store_set(GTK_LIST_STORE(gtkPairs_listStore), &iter,
		     GTK_PAIRS_COLUMN_DRAWN, !drawn,
		     -1);

  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
		     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &pdata,
		     -1);
  res = setPairsDrawn(pdata, !drawn);

  if (res)
    gtkPairs_createPairs();
}
void minMaxChanged(GtkSpinButton *spin, GtkTreeSelection *tree, int minMax)
{
  int res;
  GList* selectedRows, *tmpLst;
  gboolean validIter;
  GdkPixbuf *pixColor;
  PairsData *pdata;
  gchar *lblDist;
  GtkTreeIter iter;

  res = 0;
  selectedRows = gtk_tree_selection_get_selected_rows(tree, NULL);
  tmpLst = selectedRows;
  while (tmpLst)
    {
      validIter = gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkPairs_listStore),
					  &iter, (GtkTreePath*)tmpLst->data);
      gtk_tree_path_free((GtkTreePath*)tmpLst->data);
      if (!validIter)
	fprintf(stderr, "INTERNAL ERROR! Wrong 'path' variable in"
		" 'colorChanged' method.\n");
      else
	{
	  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &pdata,
			     -1);

	  res = setPairsDistance((float)gtk_spin_button_get_value(spin),
				 pdata, minMax) || res;

	  lblDist = createDistanceLabel(pdata);
	  gtk_list_store_set(GTK_LIST_STORE(gtkPairs_listStore), &iter,
			     GTK_PAIRS_COLUMN_LABEL_DISTANCE, lblDist,
			     -1);
	  g_free(lblDist);
	}
      tmpLst = g_list_next(tmpLst);
    }
  g_list_free(selectedRows);
  if (res)
    gtkPairs_createPairs();
}
void selectionChanged(GtkTreeSelection *tree, gpointer data)
{
  GList* selectedRows, *tmpLst;
  gboolean validIter;
  GdkPixbuf *pixColor;
  Color *color;
  PairsData *pdata;
  GtkTreeIter iter;

  selectedRows = gtk_tree_selection_get_selected_rows(tree, NULL);

  /* Set sensitive or not the modifing widgets. */
  if (!selectedRows)
    gtk_widget_set_sensitive(hboxCheckWidgets, FALSE);
  else
    gtk_widget_set_sensitive(hboxCheckWidgets, TRUE);
  
  tmpLst = selectedRows;
  while (tmpLst)
    {
      if (!tmpLst->next)
	{
	  validIter = gtk_tree_model_get_iter(GTK_TREE_MODEL(gtkPairs_listStore),
					      &iter, (GtkTreePath*)tmpLst->data);
	  if (!validIter)
	    fprintf(stderr, "INTERNAL ERROR! Wrong 'path' variable in"
		    " 'selectionChanged' method.\n");
	  else
	    {
	      gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_listStore), &iter,
				 GTK_PAIRS_COLUMN_POINTER_TO_PAIR, &pdata,
				 -1);
	      /* Set the min/max values */
	      g_signal_handler_block(G_OBJECT(gtkPairs_spinMin),
				     gtkPairs_spinMinSignalId);
	      g_signal_handler_block(G_OBJECT(gtkPairs_spinMax),
				     gtkPairs_spinMaxSignalId);
	      gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkPairs_spinMin),
					pdata->minMax[PAIRS_MIN]);
	      gtk_spin_button_set_value(GTK_SPIN_BUTTON(gtkPairs_spinMax),
					pdata->minMax[PAIRS_MAX]);
	      g_signal_handler_unblock(G_OBJECT(gtkPairs_spinMax),
				     gtkPairs_spinMaxSignalId);
	      g_signal_handler_unblock(G_OBJECT(gtkPairs_spinMin),
				     gtkPairs_spinMinSignalId);
	      
	      /* Set the color */
	      color = (Color*)getPairsProperty(pdata, "color");
	      if (color)
		{
		  g_signal_handler_block(G_OBJECT(widgetColor), widgetColorSignalId);
		  colorComboBoxSet_selectionByColor(COLOR_COMBOX(widgetColor), color);
		  g_signal_handler_unblock(G_OBJECT(widgetColor), widgetColorSignalId);
		}

	      /* Set the user label */
	      if (currentSignalSelectedFunc)
		currentSignalSelectedFunc(pdata);
	    }
	}
      gtk_tree_path_free((GtkTreePath*)tmpLst->data);
      tmpLst = g_list_next(tmpLst);
    }
  g_list_free(selectedRows);

}

gboolean gtkPairs_isPairExtensionInUse(PairsExtension *pairs)
{
  GtkTreeIter iter;
  gboolean validIter;
  PairsExtension *pairsMethod;

  if (!pairsDialog || pairsIsHide)
    return FALSE;

  validIter = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(gtkPairs_comboWidget), &iter);
  if (!validIter)
    {
      fprintf(stderr, "WARNING! No pairs method have been found.\n");
      return FALSE;
    }
  gtk_tree_model_get(GTK_TREE_MODEL(gtkPairs_comboModel), &iter,
		     GTK_PAIRS_COLUMN_PAIRS_POINTER, &pairsMethod,
		     -1);
  DBG_fprintf(stderr, "Gtk Pairs : testing pair extension %p with selected method %p.\n",
	      (gpointer)pairs, (gpointer)pairsMethod);
  return (pairs == pairsMethod);
}

void gtkPairs_createPairs()
{
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);
  if (createPairs(renderingWindowGet_visuData(window)))
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 , NULL);
}
