/*   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 "visuConfig.h"
#include "externalRenderingMethods_gtk.h"

#include <visu_tools.h>
#include <visu_object.h>
#include <visu_basic.h>
#include <visu_data.h>
#include <visu_configFile.h>
#include <gtk_main.h>
#include <extraGtkFunctions/gtk_numericalEntryWidget.h>
#include <renderingBackend/visu_windowInterface.h>
#include <support.h>

#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>


#define FLAG_PARAMETER_TABVIEW_CONFIG   "config_subPanelTabView"
#define FLAG_PARAMETER_SKIN_CONFIG      "config_skin"
#define FLAG_PARAMETER_REFRESH_CONFIG   "config_refreshIsOn"
#define FLAG_PARAMETER_PERIOD_CONFIG    "config_refreshPeriod"
#define DESC_PARAMETER_TABVIEW_CONFIG   "See or not the tab for the subpanel view ; boolean 0 or 1"
#define DESC_PARAMETER_SKIN_CONFIG      "Path to a gtkrc file ; chain"
#define DESC_PARAMETER_REFRESH_CONFIG   "When on V_Sim reloads the file at periodic time ; boolean 0 or 1"
#define DESC_PARAMETER_PERIOD_CONFIG    "The period of reloading in ms ; integer (10 < v < 10000)"
#define PARAMETER_CONFIG_TABVIEW_DEFAULT   0
#define PARAMETER_CONFIG_SKIN_DEFAULT      "None"
#define PARAMETER_CONFIG_REFRESH_DEFAULT   0
#define PARAMETER_CONFIG_PERIOD_DEFAULT    250
int config_subPanelTabView;
gchar *config_skin;
char *config_resourcesPath;
int config_refreshIsOn;
float config_refreshPeriod;
gdouble config_spinBoundsValue;


/* Private functions. */
GtkWidget *createInteriorConfig();
void createCallBacksConfig();
void setSkin(char* label);
void setAutomaticRefresh(int bool);
void setRefreshPeriod(float val);
gboolean automaticReload(gpointer data);
gboolean onConfigEnter(gpointer data);

/* This function details how to read the preferedNumberOfViews
   parameter. */
gboolean readConfigTabView(gchar **lines, int nbLines,
			   int position, GString *errorMessage);
gboolean readConfigSkin(gchar **lines, int nbLines,
			int position, GString *errorMessage);
gboolean readConfigResPath(gchar **lines, int nbLines,
			   int position, GString *errorMessage);
gboolean readConfigRefresh(gchar **lines, int nbLines,
			   int position, GString *errorMessage);
gboolean readConfigPeriod(gchar **lines, int nbLines,
			   int position, GString *errorMessage);
/* These functions write all the element list to export there associated resources. */
gboolean exportParametersPanelConfig(GString *data, int *nbLinesWritten);

void initPanelConfigGtkPart();

static char *defaultSkinPath;

gboolean isPanelConfigInitialized;

/* Specific widgets used in this panel. */
GtkWidget *vBoxPanelConfig;
GtkWidget *entryFactorSpin;
GtkWidget *checkShowTab;
GtkWidget *buttonRcFiles;
GtkWidget *comboInputMethods;
GtkWidget *descrMethod;
GtkWidget *spinRefresh;
GtkWidget *checkRefreshAuto;
GtkWidget *checkStorePositions;
GtkWidget *buttonMoveUpPath;
GtkWidget *buttonMoveDownPath;
GtkWidget *buttonAddPath;
GtkWidget *buttonRemovePath;

/* Callbacks */
void checkShowTabToggled(GtkToggleButton *button, gpointer data);
void checkRememberToggled(GtkToggleButton *button, gpointer data);
void entryRcFilesChanged(GtkEntry *entry, gpointer data);
void comboInputMethodsChanged(GtkComboBox *combo, gpointer data);
void directorySelectedForResources(GtkButton *button, gpointer user_data);
void openRcFileSelector(GtkButton *button, gpointer data);
void checkRefreshToggled(GtkToggleButton *button, gpointer data);
void refreshPeriodChanged(GtkSpinButton* button, gpointer data);
void onDataReadySignal(GObject *obj, VisuData *dataObj, gpointer data);
gboolean treePathClicked(GtkWidget *widget, GdkEventButton *event, gpointer user_data);
void directoryRemoveFromResources(GtkButton *button, gpointer user_data);
void renderingHasChanged(GObject *obj, gpointer data);
void onReturnEntrySpinBounds(NumericalEntry *entry, double value, gpointer data);

GtkListStore *listStoreRenderingMethods;
enum
  {
    COLUMN_ICON, /* A pixmap to represent the method */
    COLUMN_NAME,  /* The label shown */
    COLUMN_POINTER_TO_DATA,  /* Pointer to the rendering method. */
    N_COLUMNS
  };

/* This is a hashtable to store the pointers to methods used
   to display some specific options of rendering methods.
   The key are pointer to valid RenderingMethod and the values
   are pointer to struct RenderingExpanderWidget_struct. */
GHashTable *listOfRenderingSpecificConfigWidget;
/* Since C doesn't accept to (gpointer)-ize pointers to methods
   the hashtable can't store directly createGtkWidgetFunc() so
   they are encapsulated in this struct. */
struct RenderingExpanderWidget_struct
{
  createGtkWidgetFunc create;
  destroyFunc destroy;
};
static destroyFunc currentDestroyFunc;

GtkWidget *expanderRenderingConfig;

subPanel* initVisuConfig()
{
  char *cl = _("Configure the interface");
  char *tl = _("Config");
  GtkWidget *child;
  int i;
  VisuConfigFileEntry *resourceEntry;

  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_TABVIEW_CONFIG,
					  DESC_PARAMETER_TABVIEW_CONFIG,
					  1, readConfigTabView);
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_SKIN_CONFIG,
					  DESC_PARAMETER_SKIN_CONFIG,
					  1, readConfigSkin);
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_REFRESH_CONFIG,
					  DESC_PARAMETER_REFRESH_CONFIG,
					  1, readConfigRefresh);
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_PERIOD_CONFIG,
					  DESC_PARAMETER_PERIOD_CONFIG,
					  1, readConfigPeriod);
  visuConfigFileAdd_exportFunction(VISU_CONFIGFILE_PARAMETER,
				   exportParametersPanelConfig);

  defaultSkinPath = g_build_filename(v_sim_data_dir, "v_sim.rc", NULL);
  config_subPanelTabView = PARAMETER_CONFIG_TABVIEW_DEFAULT;
  config_refreshPeriod = -1;
  config_refreshIsOn = -1;
  config_spinBoundsValue = 1.;
  panelConfigSet_refreshPeriod((RenderingWindow*)0, PARAMETER_CONFIG_PERIOD_DEFAULT);
  panelConfigSet_automaticRefresh((RenderingWindow*)0, PARAMETER_CONFIG_REFRESH_DEFAULT);
  currentDestroyFunc = (destroyFunc)0;

  /* Now that everything has a value, we create the panel. */
  vBoxPanelConfig = gtk_vbox_new (FALSE, 0);
  visuConfigData = createSubPanelWithIconFromStock(cl, tl, GTK_STOCK_PREFERENCES,
						   vBoxPanelConfig, onConfigEnter,
						   (onChangePanelFunc)0);
  if (!visuConfigData)
    return (subPanel*)0;

  isPanelConfigInitialized = FALSE;

  /* Initialise the rendering specific widgets */
  listOfRenderingSpecificConfigWidget = (GHashTable*)0;
  for (i = 0; listInitRendenringConfigGtkFunc[i]; i++)
    listInitRendenringConfigGtkFunc[i]();


  /* call the visu add routine */
  addSubPanel(visuConfigData);

  /* Set global callbacks. */
  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering",
		   G_CALLBACK(onDataReadySignal), (gpointer)0);


  return visuConfigData;
}

void initPanelConfigGtkPart()
{
  if (isPanelConfigInitialized)
    return;

  DBG_fprintf(stderr, "Panel Config : creating the config panel on demand.\n");
  isPanelConfigInitialized = TRUE;

  createInteriorConfig();

  /* Force the callbacks to initialise the values. */
  checkShowTabToggled((GtkToggleButton*)0, (gpointer)0);
}


GtkWidget* createTreeListRenderingMethod(int *iterSelected)
{
  GtkTreeIter iter;
  GList *methodLst;
  GtkWidget *methodsWd;
  GtkCellRenderer *renderer;
  int i;
  RenderingMethod *methodInUse;
  GdkPixbuf *pixbufIconBox;

  /* We create the structure that store the methods */
  listStoreRenderingMethods = gtk_list_store_new (3,
						  GDK_TYPE_PIXBUF,
						  G_TYPE_STRING,
						  G_TYPE_POINTER);
  gtk_list_store_append(listStoreRenderingMethods, &iter);
  gtk_list_store_set(listStoreRenderingMethods, &iter,
		     0, NULL,
		     1, _("None"),
		     2, NULL,
		     -1);
  *iterSelected = 0;
  i = 1;
  methodInUse = getRenderingMethodInUse();
  methodLst = renderingMethodGet_AllMethods();
  while (methodLst)
    {
      if (((RenderingMethod*)methodLst->data)->icon)
	pixbufIconBox = gdk_pixbuf_new_from_file_at_size(((RenderingMethod*)methodLst->data)->icon,
							   16, 16, (GError**)0);
      else
	pixbufIconBox = (GdkPixbuf*)0;
      gtk_list_store_append(listStoreRenderingMethods, &iter);
      gtk_list_store_set(listStoreRenderingMethods, &iter,
			 0, pixbufIconBox,
			 1, ((RenderingMethod*)methodLst->data)->printName,
			 2, methodLst->data,
			 -1);
      if (((RenderingMethod*)methodLst->data) == methodInUse)
	*iterSelected = i;
      i += 1;
      methodLst = g_list_next(methodLst);
    }
  /* We create the tree widget that show the methods. */
  methodsWd = gtk_combo_box_new_with_model(GTK_TREE_MODEL(listStoreRenderingMethods));
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(methodsWd), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(methodsWd), renderer, "pixbuf", 0);
  renderer = gtk_cell_renderer_text_new();
/*   g_object_set(G_OBJECT(renderer), "size-set", TRUE, NULL); */
/*   g_object_get(G_OBJECT(renderer), "size-points", &fontSize, NULL); */
/*   g_object_set(G_OBJECT(renderer), "size-points", fontSize * 0.67, NULL); */
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(methodsWd), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(methodsWd), renderer, "text", 1);

  return methodsWd;
}

GtkWidget *createInteriorConfig()
{
  GtkWidget *vbox, *vbox2;
  GtkWidget *hbox;
  GtkWidget *label;
  GtkWidget *table;
  GtkWidget *align;
  GtkObject *adj;
  GtkWidget *scrollView;
  GtkWidget *button;
  GtkWidget *image;
  int iterSelected;
  
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();

  scrollView = gtk_scrolled_window_new((GtkAdjustment*)0,
				       (GtkAdjustment*)0);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollView),
				  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrollView), GTK_SHADOW_NONE);

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollView), vbox);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
  gtk_widget_show(hbox);
  label = gtk_label_new(_("Show tabs in subpanel :"));
  gtk_widget_set_name(label, "label_head");
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  gtk_widget_show(label);
  checkShowTab = gtk_check_button_new();
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkShowTab),
			       (gboolean)config_subPanelTabView);
  gtk_box_pack_start(GTK_BOX(hbox), checkShowTab, FALSE, FALSE, 2);
  gtk_widget_show(checkShowTab);

  table = gtk_table_new(2, 1, FALSE);
  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 5);
  gtk_widget_show(table);
  align = gtk_alignment_new(0., 0.5, 0., 0.);
  gtk_table_attach(GTK_TABLE(table), align, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);
  gtk_widget_show(align);
  label = gtk_label_new(_("Choose your input method :"));
  gtk_widget_set_name(label, "label_head");
  gtk_container_add(GTK_CONTAINER(align), label);
  gtk_widget_show(label);
  align = gtk_alignment_new(1.0, 0.5, 0., 0.);
  gtk_table_attach(GTK_TABLE(table), align, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);
  gtk_widget_show(align);

  comboInputMethods = createTreeListRenderingMethod(&iterSelected);
  gtk_container_add(GTK_CONTAINER(align), comboInputMethods);
  gtk_widget_show(comboInputMethods);
  descrMethod = gtk_label_new("");
  gtk_widget_set_name(descrMethod, "label_info");
  gtk_box_pack_start(GTK_BOX(vbox), descrMethod, FALSE, FALSE, 0);
  gtk_label_set_use_markup(GTK_LABEL(descrMethod), TRUE);
  gtk_label_set_line_wrap(GTK_LABEL(descrMethod), TRUE);
  gtk_label_set_justify(GTK_LABEL(descrMethod), GTK_JUSTIFY_FILL);
  gtk_widget_show(descrMethod);
  expanderRenderingConfig = gtk_expander_new_with_mnemonic(_("_Show/hide rendering options"));
  gtk_widget_show(expanderRenderingConfig);
  gtk_box_pack_start(GTK_BOX (vbox), expanderRenderingConfig, FALSE, FALSE, 0);

  table = gtk_table_new(2, 1, FALSE);
  gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 5);
  gtk_widget_show(table);
  align = gtk_alignment_new(0., 0.5, 0., 0.);
  gtk_widget_show(align);
  gtk_table_attach(GTK_TABLE(table), align, 0, 1, 0, 1, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  gtk_container_add(GTK_CONTAINER(align), hbox);
  label = gtk_label_new(_("Automatic refresh : "));
  gtk_widget_set_name(label, "label_head");
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  checkRefreshAuto = gtk_check_button_new();
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkRefreshAuto),
			       (gboolean)config_refreshIsOn);
  gtk_widget_show(checkRefreshAuto);
  gtk_box_pack_start(GTK_BOX(hbox), checkRefreshAuto, FALSE, FALSE, 0);
  align = gtk_alignment_new(1., 0.5, 0., 0.);
  gtk_widget_show(align);
  gtk_table_attach(GTK_TABLE(table), align, 0, 1, 1, 2, GTK_FILL | GTK_EXPAND, GTK_SHRINK, 2, 0);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  gtk_container_add(GTK_CONTAINER(align), hbox);
  label = gtk_label_new(_("Refresh period : "));
  gtk_widget_set_name(label, "label_head");
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  adj = gtk_adjustment_new(100., 10., 10000., 10., 100., 100.);
  spinRefresh = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 0.05, 0);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinRefresh),
			    config_refreshPeriod);
  gtk_widget_show(spinRefresh);
  gtk_box_pack_start(GTK_BOX(hbox), spinRefresh, FALSE, FALSE, 0);
  label = gtk_label_new(_(" ms"));
  gtk_widget_set_name(label, "label_head");
  gtk_widget_show(label);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);

  vbox2 = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 5);
  gtk_widget_show(vbox2);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);
  label = gtk_label_new(_("Remember windows positions :"));
  gtk_widget_set_name(label, "label_head");
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  gtk_widget_show(label);
  checkStorePositions = gtk_check_button_new();
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkStorePositions),
			       (gboolean)TRUE);
  gtk_box_pack_start(GTK_BOX(hbox), checkStorePositions, FALSE, FALSE, 2);
  gtk_widget_show(checkStorePositions);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);
  label = gtk_label_new(_("(because I've a bad WM)"));
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 1);
  gtk_widget_show(label);

  vbox2 = gtk_vbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), vbox2, FALSE, FALSE, 5);
  gtk_widget_show(vbox2);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, FALSE, 0);
  gtk_widget_show(hbox);
  label = gtk_label_new(_("Set factor for spin buttons :"));
  gtk_widget_set_name(label, "label_head");
  gtk_misc_set_alignment(GTK_MISC(label), 0., 0.5);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  gtk_widget_show(label);
  entryFactorSpin = numericalEntry_new(1.);
  gtk_tooltips_set_tip(tooltips, entryFactorSpin,
		       _("All values of spin buttons related to the box size"
			 " are multiplied by this value before being used. For"
			 " example, a spin of 0.23 with a multiplier of 1e-5 gives"
			 " a value of 2.3e-6."), NULL);
  gtk_entry_set_width_chars(GTK_ENTRY(entryFactorSpin), 6);
  gtk_box_pack_start(GTK_BOX(hbox), entryFactorSpin, TRUE, TRUE, 2);
  gtk_widget_show(entryFactorSpin);
  label = gtk_label_new(_("(Modify min/max values relative to the box)"));
  gtk_misc_set_alignment(GTK_MISC(label), 1., 0.5);
  gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 1);
  gtk_widget_show(label);

  gtk_widget_show(vbox);
  gtk_widget_show(scrollView);

  gtk_box_pack_start(GTK_BOX(vBoxPanelConfig), scrollView, TRUE, TRUE, 0);
  gtk_widget_show(vBoxPanelConfig);

  /* Set the callbacks. */
  createCallBacksConfig();
  g_signal_connect((gpointer)checkRefreshAuto, "toggled",
		   GTK_SIGNAL_FUNC(checkRefreshToggled), (gpointer)0);
  g_signal_connect((gpointer)spinRefresh, "value-changed",
		   G_CALLBACK(refreshPeriodChanged), (gpointer)0);
  
  
  gtk_combo_box_set_active(GTK_COMBO_BOX(comboInputMethods), iterSelected);

  return scrollView;
}

void createCallBacksConfig()
{
  g_signal_connect((gpointer)checkShowTab, "toggled",
		   GTK_SIGNAL_FUNC(checkShowTabToggled), (gpointer)0);
  g_signal_connect((gpointer)checkStorePositions, "toggled",
		   GTK_SIGNAL_FUNC(checkRememberToggled), (gpointer)0);
  g_signal_connect(G_OBJECT(comboInputMethods), "changed",
		   G_CALLBACK(comboInputMethodsChanged), (gpointer)0);
  g_signal_connect(G_OBJECT(visu), "renderingChanged",
		   G_CALLBACK(renderingHasChanged), (gpointer)1);
  g_signal_connect(G_OBJECT(entryFactorSpin), "value-changed",
		   G_CALLBACK(onReturnEntrySpinBounds), (gpointer)1);
}

/*************/
/* Callbacks */
/*************/
void checkShowTabToggled(GtkToggleButton *button, gpointer data)
{
  panelConfigSet_tabView(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkShowTab)));
}
void checkRememberToggled(GtkToggleButton *button, gpointer data)
{
  setRememberPosition((int)gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
}
void setRenderingSpecificWidget(RenderingMethod *method)
{
  createGtkWidgetFunc create;
  destroyFunc destroy;
  GtkWidget *wd;

  wd = gtk_bin_get_child(GTK_BIN(expanderRenderingConfig));
  if (wd)
    {
      if (currentDestroyFunc)
	currentDestroyFunc();
      gtk_widget_destroy(wd);
    }

  if (!method)
    {
      DBG_fprintf(stderr, "Panel Config : no rendering specific"
		  " widget since no method is specified.\n");
      gtk_expander_set_expanded(GTK_EXPANDER(expanderRenderingConfig), FALSE);
      gtk_widget_set_sensitive(expanderRenderingConfig, FALSE);
      return;
    }

  panelConfigGet_renderingInterfaceCreate(method, &create);
  panelConfigGet_renderingInterfaceDestroy(method, &currentDestroyFunc);
  if (create)
    {
      DBG_fprintf(stderr, "Panel Config : creating the rendering specific"
		  " widget for method '%s'.\n", method->name);
      wd = create();
      gtk_widget_show(wd);
      gtk_container_add(GTK_CONTAINER(expanderRenderingConfig), wd);
      gtk_widget_set_sensitive(expanderRenderingConfig, TRUE);
    }
  else
    {
      DBG_fprintf(stderr, "Panel Config : no rendering specific"
		  " widget for method '%s'.\n", method->name);
      wd = (GtkWidget*)0;
      gtk_expander_set_expanded(GTK_EXPANDER(expanderRenderingConfig), FALSE);
      gtk_widget_set_sensitive(expanderRenderingConfig, FALSE);
    }
}
void comboInputMethodsChanged(GtkComboBox *combo, gpointer data)
{
  GtkTreeModel *tree;
  gboolean selected;
  GtkTreeIter iter;
  RenderingMethod *meth;

  Color **prevColor;
  GtkWidget *selection;
  int code;
  GdkColor gdkcolor;
  float rgba[4];
  int alpha;
  char* markup;

  DBG_fprintf(stderr, "Panel Config : Change occured on the method combo.\n");
  tree = gtk_combo_box_get_model(combo);
  selected = gtk_combo_box_get_active_iter(combo, &iter);
  if (selected)
    {
      gtk_tree_model_get(tree, &iter, COLUMN_POINTER_TO_DATA, &meth, -1);
      if (meth && meth->description)
	{
	  DBG_fprintf(stderr, "Panel Config : set the rendering method to '%s'.\n", meth->name);
	  markup = g_markup_printf_escaped("<span size=\"smaller\">%s</span>",
					   meth->description);
	  gtk_label_set_markup(GTK_LABEL(descrMethod), markup);
	  g_free(markup);
	}
      else
	{
	  DBG_fprintf(stderr, "Panel Config : set the rendering method to 'null'.\n");
	  gtk_label_set_text(GTK_LABEL(descrMethod), "");
	}
      setRenderingMethodInUse(meth);
      setRenderingSpecificWidget(meth);
    }
  else
    {
      fprintf(stderr, "WARNING! Can't find the selected method in the"
	      " combobox of the config panel.\n");
      gtk_label_set_text(GTK_LABEL(descrMethod), "");
      setRenderingSpecificWidget((RenderingMethod*)0);
    }
}
gboolean onConfigEnter(gpointer data)
{
  if (!isPanelConfigInitialized)
    initPanelConfigGtkPart();

  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkStorePositions),
			       getRememberPosition());

  
  return FALSE;
}
void checkRefreshToggled(GtkToggleButton *button, gpointer data)
{
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);

  panelConfigSet_automaticRefresh(window, gtk_toggle_button_get_active(button));
}
void refreshPeriodChanged(GtkSpinButton* button, gpointer data)
{
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  g_return_if_fail(window);

  panelConfigSet_refreshPeriod(window, (float)gtk_spin_button_get_value(button));
}
void openRcFileSelector(GtkButton *button, gpointer data)
{
}
void onReturnEntrySpinBounds(NumericalEntry *entry, double value, gpointer data)
{
  DBG_fprintf(stderr, "Panel Config : grep value from GtkEntry for"
	      " the bounding values of spins.\n");

  panelConfigSet_spinBoundsValue(value);
}
void setSkin(char* label)
{
  GtkSettings *settings;
  int fileOk;
  char *tmp;

  if (!label || label[0] == '\0' || !strcmp(label, "None"))
    return;

  if (!strcmp(label, "V_Sim"))
    tmp = defaultSkinPath;
  else
    tmp = label;

  settings = gtk_settings_get_default ();
/*   gtk_rc_reset_styles (settings); */
/*   gtk_rc_reparse_all_for_settings(settings, TRUE); */
  /* test if the file exists or fall back on default installation path */
  fileOk = !access(tmp, R_OK); /* return 0 if success */
  if (!fileOk)
    return;
  DBG_fprintf(stderr,"Reading rc file : %s...\n", tmp);
  gtk_rc_parse(tmp);
  gtk_rc_reparse_all_for_settings(settings, TRUE);
}
void autoReloadAddTimeout(VisuData *data, guint time, gpointer user_data)
{
  guint *timeoutId;

  g_return_if_fail(data);
  
  timeoutId = (guint*)visuDataGet_property(data, "autoLoad_timeoutId");
  if (!timeoutId)
    {
      timeoutId = g_malloc(sizeof(guint));
      visuDataSet_property(data, "autoLoad_timeoutId", (gpointer)timeoutId);
      *timeoutId = 0;
    }
  if (!*timeoutId)
    *timeoutId = visuDataAdd_timeout(data, time, automaticReload, user_data);
}
void autoReloadRemoveTimeout(VisuData *data)
{
  guint *timeoutId;

  g_return_if_fail(data);
  
  timeoutId = (guint*)visuDataGet_property(data, "autoLoad_timeoutId");
  if (timeoutId)
    {
      visuDataRemove_timeout(data, *timeoutId);
      *timeoutId = 0;
    }
}
gboolean automaticReload(gpointer data)
{
  struct stat statBuf;
  int res;
  char *file;
  time_t modTime;
  int kind, nbKind;
  gboolean reloadNeeded;
  GenericRenderingWindow window;
  VisuData *dataObj;
  time_t *lastReadTime;

  DBG_fprintf(stderr, "Panel Config : automatic reload called.\n");
  window = RENDERING_WINDOW(data);
  g_return_val_if_fail(window, TRUE);

  dataObj = renderingWindowGet_visuData(window);
  if (!dataObj)
    {
      DBG_fprintf(stderr, "Panel Config : automatic reload aborted, no file available.\n");
      return TRUE;
    }
  
  lastReadTime = (time_t*)visuDataGet_property(dataObj, "autoLoad_accessTime");
  g_return_val_if_fail(lastReadTime, TRUE);

  reloadNeeded = FALSE;
  nbKind = renderingMethodGet_nbFileType(getRenderingMethodInUse());
  for (kind = 0; kind < nbKind; kind++)
    {
      file = visuDataGet_file(dataObj, kind);
      if (!file)
	return TRUE;

      res = stat(file, &statBuf);
      if (!res)
	{
	  modTime = statBuf.st_ctime;
	  if (modTime > lastReadTime[kind])
	    {
	      reloadNeeded = TRUE;
	      lastReadTime[kind] = modTime;
	    }
	}
    }
  if (reloadNeeded)
    {
      DBG_fprintf(stderr, "Panel Config : automatic reloading in progress...\n");
      visuDataFree_population(dataObj);
      loadDataFromFile(dataObj, (GString**)0);
      visuRenderingWindowSet_visuData(window, dataObj);
      visuData_createAllNodes(dataObj);

      g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		     0 , NULL);
    }
  return TRUE;
}
void onDataReadySignal(GObject *obj, VisuData *dataObj, gpointer data)
{
  struct stat statBuf;
  int res;
  char *file;
  int kind, nbKind;
  time_t *lastReadTime;

  if (!dataObj)
    return;

  DBG_fprintf(stderr, "Panel Config : caught the 'dataReadyForRendering' signal,"
	      " set acces time for VisuData %p.\n", (gpointer)dataObj);
  nbKind = renderingMethodGet_nbFileType(getRenderingMethodInUse());
  lastReadTime = g_malloc(sizeof(time_t) * nbKind);
  for (kind = 0; kind < nbKind; kind++)
    {
      file = visuDataGet_file(dataObj, kind);
      if (!file)
	lastReadTime[kind] = 0;
      else
	{
	  res = stat(file, &statBuf);
	  if (!res)
	    lastReadTime[kind] = statBuf.st_ctime;
	  else
	    lastReadTime[kind] = 0;
	}
    }
  visuDataSet_property(dataObj, "autoLoad_accessTime", lastReadTime);
  if (config_refreshIsOn)
    autoReloadAddTimeout(dataObj, config_refreshPeriod,
			 (gpointer)visuDataGet_renderingWindow(dataObj));
}

void renderingHasChanged(GObject *obj, gpointer data)
{
  RenderingMethod *meth;
  GtkTreeIter iter;
  gboolean valid;
  RenderingMethod* method;
  int i;

  meth = getRenderingMethodInUse();
  if (meth)
    {
      DBG_fprintf(stderr, "Panel Config : changing rendering method to '%s'.\n", meth->name);
      i = 0;
      /* Get the first iter in the list */
      valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(listStoreRenderingMethods), &iter);
      while (valid)
	{
	  /* Walk through the list, reading each row */

	  /* Make sure you terminate calls to gtk_tree_model_get()
	   * with a '-1' value
	   */
	  gtk_tree_model_get(GTK_TREE_MODEL(listStoreRenderingMethods), &iter, 
			     COLUMN_POINTER_TO_DATA, &method,
			     -1);
	  if (method == meth)
	    {
	      gtk_combo_box_set_active(GTK_COMBO_BOX(comboInputMethods), i);
	      DBG_fprintf(stderr, "Panel Config : ok, new method found at position %d.\n", i);
	      return;
	    }
	  valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(listStoreRenderingMethods), &iter);
	  i += 1;
	}
      fprintf(stderr, "WARNING! Can't find the current rendering method ('%s')"
	      " is the combobox of the config panel.\n", meth->name);
    }
  else
    {
      DBG_fprintf(stderr, "Panel Config : rendering method changed to 'null'.\n");
      gtk_combo_box_set_active(GTK_COMBO_BOX(comboInputMethods), 0);
    }
}



/**********************************/
/* Public method to change values */
/**********************************/

void panelConfigSet_renderingInterface(RenderingMethod *method,
				       createGtkWidgetFunc create,
				       destroyFunc destroy)
{
  struct RenderingExpanderWidget_struct *str;

  g_return_if_fail(method && (!destroy || (destroy && create)));
  
  if (!create)
    return;

  if (!listOfRenderingSpecificConfigWidget)
    listOfRenderingSpecificConfigWidget = g_hash_table_new_full(g_direct_hash,
								g_direct_equal,
								NULL, freeGPointer);
  str = g_malloc(sizeof(struct RenderingExpanderWidget_struct));
  str->create = create;
  str->destroy = destroy;
  g_hash_table_insert(listOfRenderingSpecificConfigWidget,
		      (gpointer)method, (gpointer)str);
}
void panelConfigGet_renderingInterfaceCreate(RenderingMethod *method,
					     createGtkWidgetFunc *create)
{
  struct RenderingExpanderWidget_struct *str;

  g_return_if_fail(method && create);

  if (!listOfRenderingSpecificConfigWidget)
    {
      *create = (createGtkWidgetFunc)0;
      return;
    }
  str = (struct RenderingExpanderWidget_struct*)0;
  str = (struct RenderingExpanderWidget_struct*)
    g_hash_table_lookup(listOfRenderingSpecificConfigWidget, (gpointer)method);
  if (str)
    *create = str->create;
  else
    *create = (createGtkWidgetFunc)0;
}
void panelConfigGet_renderingInterfaceDestroy(RenderingMethod *method,
					      destroyFunc *destroy)
{
  struct RenderingExpanderWidget_struct *str;

  g_return_if_fail(method && destroy);

  if (!listOfRenderingSpecificConfigWidget)
    {
      *destroy = (destroyFunc)0;
      return;
    }
  str = (struct RenderingExpanderWidget_struct*)0;
  str = (struct RenderingExpanderWidget_struct*)
    g_hash_table_lookup(listOfRenderingSpecificConfigWidget, (gpointer)method);
  if (str)
    *destroy = str->destroy;
  else
    *destroy = (destroyFunc)0;
}
void panelConfigSet_tabView(gboolean viewed)
{
  GtkWidget *tabSubPanel;

  DBG_fprintf(stderr,"Panel Config : toggle tab view -> %d (previously %d)\n",
	      (int)viewed, config_subPanelTabView);
  if (viewed == config_subPanelTabView)
    return;

  tabSubPanel = lookup_widget(mainWindow, "subPanel");
  gtk_notebook_set_show_tabs(GTK_NOTEBOOK(tabSubPanel), viewed);
  
  /* set the associated parameter. */
  config_subPanelTabView = viewed;
}
void panelConfigSet_automaticRefresh(RenderingWindow *window, int bool)
{
  DBG_fprintf(stderr, "Panel Config : set refresh to %d (previously %d).\n",
	      bool, config_refreshIsOn);

  if (config_refreshIsOn == bool)
    return;

  config_refreshIsOn = bool;
  if (window && renderingWindowGet_visuData(window))
    {
      if (bool)
	autoReloadAddTimeout(renderingWindowGet_visuData(window),
			     config_refreshPeriod, (gpointer)window);
      else
	autoReloadRemoveTimeout(renderingWindowGet_visuData(window));
    }
}
void panelConfigSet_refreshPeriod(RenderingWindow *window, float val)
{
  DBG_fprintf(stderr, "Panel Config : set the refresh period to %f (previously %f).\n",
	      val, config_refreshPeriod);

  if (config_refreshPeriod == val)
    return;

  config_refreshPeriod = val;
  if (window && renderingWindowGet_visuData(window) && config_refreshIsOn)
    {
      DBG_fprintf(stderr, "Panel Config : stopping the callback of refresh "
		  "to change the period.\n");
      autoReloadRemoveTimeout(renderingWindowGet_visuData(window));
      autoReloadAddTimeout(renderingWindowGet_visuData(window),
			   config_refreshPeriod, (gpointer)window);
    }  
}
void panelConfigSet_spinBoundsValue(gdouble val)
{
  float prec;

  DBG_fprintf(stderr, "Panel Config : set the spin bounds value to %f (previously %f).\n",
	      val, config_spinBoundsValue);
  if (val == config_spinBoundsValue)
    return;

  prec = config_spinBoundsValue;
  config_spinBoundsValue = val;
  /* Emitting appropriated signal. */
  g_signal_emit(visuGtkObject,
		VISU_GTK_GET_CLASS(visuGtkObject)->spinBoundsChanged_signal_id,
		0 , (gfloat)val / prec, NULL);
  
  /* Asking for redraw */
  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		 0 , NULL);
}
gdouble panelConfigGet_spinBoundsValue()
{
  return config_spinBoundsValue;
}


/********************/
/* In/Out functions */
/********************/

/* This function details how to read the preferedNumberOfViews
   parameter. */
gboolean readConfigTabView(gchar **lines, int nbLines,
			   int position, GString *errorMessage)
{
  int res;
  int viewed;

  res = sscanf(lines[0],"%d", &viewed);
  if (res != 1)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 boolean value must appear"
					       " after the %s markup.\n"),
			       position, FLAG_PARAMETER_TABVIEW_CONFIG);
      panelConfigSet_tabView(PARAMETER_CONFIG_TABVIEW_DEFAULT);
      return FALSE;
    }
  panelConfigSet_tabView((gboolean)viewed);
  
  return TRUE;
}
gboolean readConfigSkin(gchar **lines, int nbLines,
			int position, GString *errorMessage)
{
  lines[0] = g_strstrip(lines[0]);

  if (!lines[0][0])
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 string value must appear after the"
					       " %s markup.\n"),
			       position, FLAG_PARAMETER_SKIN_CONFIG);
      config_skin = g_strdup(PARAMETER_CONFIG_SKIN_DEFAULT);
      setSkin(config_skin);
      return FALSE;
    }
  config_skin = g_strdup(lines[0]);
  
  setSkin(config_skin);

  return TRUE;
}
gboolean readConfigRefresh(gchar **lines, int nbLines,
			   int position, GString *errorMessage)
{
  int res, bool;
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  res = sscanf(lines[0],"%d", &bool);
  if (res != 1)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 boolean value must appear"
					       " after the %s markup.\n"),
			       position, FLAG_PARAMETER_REFRESH_CONFIG);
      panelConfigSet_automaticRefresh(window, PARAMETER_CONFIG_REFRESH_DEFAULT);
      return FALSE;
    }
  panelConfigSet_automaticRefresh(window, bool);

  return TRUE;
}
gboolean readConfigPeriod(gchar **lines, int nbLines,
			  int position, GString *errorMessage)
{
  int res;
  RenderingWindow *window;

  window = RENDERING_WINDOW(visuRenderingWindowGet_current());
  res = sscanf(lines[0],"%f", &config_refreshPeriod);
  if (res != 1)
    {
      if (errorMessage)
	g_string_append_printf(errorMessage, _("WARNING! Parse error at line %d,"
					       " 1 string value must appear after"
					       " the %s markup.\n"),
			       position, FLAG_PARAMETER_PERIOD_CONFIG);
      panelConfigSet_refreshPeriod(window, PARAMETER_CONFIG_PERIOD_DEFAULT);
      return FALSE;
    }
  panelConfigSet_refreshPeriod(window, config_refreshPeriod);

  return TRUE;
}

/* These functions write all the element list to export there associated resources. */
gboolean exportParametersPanelConfig(GString *data, int *nbLinesWritten)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_TABVIEW_CONFIG);
  g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_TABVIEW_CONFIG,
			 config_subPanelTabView);
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_SKIN_CONFIG);
  g_string_append_printf(data, "%s[gtk]: %s\n\n", FLAG_PARAMETER_SKIN_CONFIG,
			 config_skin);
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_REFRESH_CONFIG);
  g_string_append_printf(data, "%s[gtk]: %d\n\n", FLAG_PARAMETER_REFRESH_CONFIG,
			 config_refreshIsOn);
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_PERIOD_CONFIG);
  g_string_append_printf(data, "%s[gtk]: %i\n\n", FLAG_PARAMETER_PERIOD_CONFIG,
			 (int)config_refreshPeriod);
  *nbLinesWritten = 12;

  return TRUE;
}

