/*   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/gtk.h>
#include <glib.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h> /* For the access markers R_OK, W_OK ... */

#include "interface.h"
#include "support.h"
#include "callbacks.h"
#include "gtk_main.h"
#include "gtk_observePick.h"
#include "gtk_pairs.h"
#include "gtk_about.h"
#include "gtk_renderingWindowWidget.h"
#include "extraGtkFunctions/gtk_colorComboBoxWidget.h"

#include "panelModules/externalModules.h"
#include "panelModules/externalRenderingMethods_gtk.h"
#include "panelModules/panelDataFile.h"
#include "panelModules/panelPlanes.h"
#include "renderingMethods/renderingAtomic.h"
#include "renderingMethods/renderingSpin.h"

#include "opengl.h"
#include "visu_object.h"
#include "visu_rendering.h"
#include "visu_data.h"
#include "visu_configFile.h"
#include "visu_basic.h"            /* To have loadDataFromFile */
#include "visu_pairs.h"            /* To have setPairsOnOff */
#include "visu_commandLine.h"      /* To have getArgFilename and getXWindowGeometry*/
#include "visu_pickMesure.h"
#include "OSOpenGL/visu_OSInterface.h"
#include "renderingBackend/visu_windowInterface.h"
#include "coreTools/toolShade.h"


/****************/
/* Private part */
/****************/

/* Initialisation */
int initGtkMain();
void createCallBacksMain();
gboolean initVisuPostGTK(gpointer data);
gboolean commandLineLoadWithGtk(gpointer data);

/* Fuctions to rule the combobox. */
void initSubPanel();
void initListeSubPanel();

/* Fuctions to rule the notebook. */
void freeSubPanel(subPanel* panel);
void makeSubPanel(subPanel* sb);

/* pointer on different buttons. */
GtkWidget *loadButton;
GtkWidget *pairsButton;
GtkWidget *pickObserveButton;

/* Miscelaneous functions with gtk */
void loadFileWithGtk(VisuData **visuData);
void setRenderingButtonSensitive(gboolean bool);

/* Local callbacks */
void checkPairsChanged(GtkToggleButton *togglebutton, gpointer user_data);
void setFileButtonSensitive(GObject *obj, gpointer bool);
static void onResourcesLoaded(GObject *visu, gpointer data);
void onFileChange(GObject *obj, VisuData *dataObj, gpointer bool);
void onHideNextTime(GtkToggleButton *button, gpointer data);
void onQuitButton(GtkButton *button, gpointer data);

/* Parameter to store the position of windows. */
int rememberWindowPosition;
GHashTable *windowPosition;
#define PARAMETER_GTKMAIN_REMEMBER_DEFAULT 1

/* Parameter to change the policy of the warning quit dialog. */
gboolean warningWhenQuit;
#define FLAG_PARAMETER_GTKMAIN_QUIT    "confirmQuit"
#define DESC_PARAMETER_GTKMAIN_QUIT    "Show up a dialog to confirm when quit button is clicked ; boolean 0 or 1"
#define PARAMETER_GTKMAIN_QUIT_DEFAULT TRUE
gboolean readMainQuitPolicy(gchar **lines, int nbLines,
			    int position, GString *errorMessage);
gboolean exportParametersGtkMain(GString *data, int *nbLinesWritten);


/* Store the last open directory. It is initialised
   to current working directory. */
gchar *lastOpenDirectory;

/* Number of time V_Sim try to attach the OpenGL context. */
#define GTK_MAIN_N_MAX_OPENGL 10

/* Store some informations on gtk method associated
   with different rendering methods. */
struct renderingMethod_gtkMain_struct
{
  createGtkWidgetFunc createOpenAddOn;
  createGtkLoadWidgetFunc createLoadAction;
};
GHashTable *renderingMethod_gtkMain;

/* Local variable for the statusbar stack */
gboolean statusInfo_isPicked;

/* Model to store all subpanel. */
GtkListStore *gtkMain_subpanelListStore;
/* Pointeur sur le combo qui affiche la liste
   des éléments disponibles dans le subpanel. */
GtkWidget *comboboxListeSubPanel;
/* pointeur sur le gtk_notebook qui correspond
   au subpanel. */
GtkWidget *tabSubPanel;



int mainWithGtk(int argc, char *argv[])
{
  int res;
  int width, height;

  gtk_init (&argc, &argv);

  /* Creating the visu object to handle the signals. */
  visu = g_object_new (VISU_TYPE, NULL);

  /* Règle l'accès aux images */
  add_pixmap_directory(v_sim_pixmaps_dir);

  DBG_fprintf(stderr,"--- Initialising the rendering window ---\n");
  commandLineGet_XWindowGeometry(&width, &height);
  currentRenderingWindow = renderingWindow_new("V_Sim", "v_sim_render", "V_Sim",
					       width, height);
  if (!currentRenderingWindow)
    exit(1);

  initVisuMain();

  DBG_fprintf(stderr,"--- Initialising the new gtk classes ---\n");
  /* Force the creation of the ColorComboBoxClass. */
  g_type_class_ref(colorComboBox_get_type());
  /* Force the creation of the VisuData class. */
  g_type_class_ref(visu_data_get_type());

  initGtkMain();

  /* Others elements linked to the main window. */
  DBG_fprintf(stderr, " | Gtk Pick and Observe");
  initObserveAndPickWindow();
  DBG_fprintf(stderr, " ... OK.\n");
  DBG_fprintf(stderr, " | Gtk Pairs");
  initPairsWindow();
  DBG_fprintf(stderr, " ... OK.\n");
  DBG_fprintf(stderr, " | Gtk About");
  initAboutWindow();
  DBG_fprintf(stderr, " ... OK.\n");

  DBG_fprintf(stderr, "GTK Main : adding loading parameters and resources"
	      " callbacks in the queue.\n");
  g_idle_add(initVisuPostGTK, (gpointer)0);

  createCallBacksMain();
  DBG_fprintf(stderr, "GTK Main : Creating callbacks.\n");

  DBG_fprintf(stderr, "Gtk Main : starting main GTK loop.\n");
  gtk_main ();

  return 0;
}

int initGtkMain()
{
  int i;
  VisuConfigFileEntry *resourceEntry;

  /* Init the signal object for gtk stuffs. */
  visuGtkObject = g_object_new(VISU_GTK_TYPE, NULL);

  /* Set local variables */
  statusInfo_isPicked = FALSE;
  /* Retrieve the current working directory. */
  setLastOpenDirectory((char*)g_get_current_dir());
  /* Create the hashtable to store windows and their position. */
  windowPosition = g_hash_table_new_full(g_direct_hash,
					 g_direct_equal,
					 NULL, freeInt);
  setRememberPosition(PARAMETER_GTKMAIN_REMEMBER_DEFAULT);
  /* Create parameters. */
  warningWhenQuit = PARAMETER_GTKMAIN_QUIT_DEFAULT;
  resourceEntry = visuConfigFileAdd_entry(VISU_CONFIGFILE_PARAMETER,
					  FLAG_PARAMETER_GTKMAIN_QUIT,
					  DESC_PARAMETER_GTKMAIN_QUIT,
					  1, readMainQuitPolicy);

  /* Create the hashtable of rendering method gtk functions. */
  renderingMethod_gtkMain = g_hash_table_new_full(g_direct_hash,
						  g_direct_equal,
						  NULL, freeGPointer);
  for (i = 0; listInitRendenringGtkFunc[i]; i++)
    listInitRendenringGtkFunc[i]();

  /* Initialise dependant gtk elements. */
  DBG_fprintf(stderr, "--- Initialising the GTK interface ---\n");
  DBG_fprintf(stderr, " | Gtk Main");
  mainWindow = create_main_window ();
  gtk_window_set_wmclass(GTK_WINDOW(mainWindow), "v_sim_commandPanel", "V_Sim");
  DBG_fprintf(stderr, " ... OK.\n");
  /* Signals and naming of widgets specific to the main window. */
  comboboxListeSubPanel = lookup_widget(mainWindow, "comboboxListeSubPanel");
  tabSubPanel = lookup_widget(mainWindow, "subPanel");
  loadButton = lookup_widget(mainWindow, "loadButton");
  pairsButton = lookup_widget(mainWindow, "pairsButton");
  pickObserveButton = lookup_widget(mainWindow, "pickObserveButton");
  /* init the sub panel contains. */
  initSubPanel();

  return 1;
}

void createCallBacksMain()
{
  GtkWidget *wd;

  /* Initialise the interface. */
  if (currentSubPanel->onEnter)
    currentSubPanel->onEnter((gpointer)0);
  wd = lookup_widget(mainWindow, "labelShowSub");
  gtk_widget_set_name(wd, "subpanel_title");
  wd = lookup_widget(mainWindow, "checkPairs");
  g_signal_connect(G_OBJECT(wd), "toggled",
		   G_CALLBACK(checkPairsChanged), (gpointer)0);
  wd = lookup_widget(mainWindow, "buttonQuit");
  g_signal_connect(G_OBJECT(wd), "clicked",
		   G_CALLBACK(onQuitButton), (gpointer)0);

  g_signal_connect(G_OBJECT(visu), "dataReadyForRendering", 
		   G_CALLBACK(onFileChange), (gpointer)0);
  g_signal_connect(G_OBJECT(visu), "renderingChanged",
		   G_CALLBACK(setFileButtonSensitive),
		   (gpointer)0);
  g_signal_connect(G_OBJECT(visu), "resourcesLoaded",
		   G_CALLBACK(onResourcesLoaded), (gpointer)0);
}

gboolean initVisuPostGTK(gpointer data)
{
  char* path;
  int res;
  char *filename, *spin_filename;
  GString *message;
  gchar *fileName, *dirname, *normDir;
  GtkWidget *wd;
  VisuData *newData;
  GString *errorMessage;
  int width, height, nIt;
/*   struct timespec timeToSleep; */

  DBG_fprintf(stderr,"Gtk Main : attach the OpenGL context to the rendering window.\n");
  res = visuRenderingWindowSet_context(visuRenderingWindowGet_current(), FALSE);
/*   for (nIt = 0; nIt < GTK_MAIN_N_MAX_OPENGL && res; nIt++) */
/*     { */
/*       timeToSleep.tv_sec = 0; */
/*       timeToSleep.tv_nsec = 250000000; */
/*       nanosleep(&timeToSleep, NULL); */
/*       visuWaitFunction(); */
/*       DBG_fprintf(stderr, " | try number %d.\n", nIt); */
/*       if (nIt > GTK_MAIN_N_MAX_OPENGL / 2) */
/* 	res = visuRenderingWindowSet_context(visuRenderingWindowGet_current(), TRUE); */
/*       else */
/* 	res = visuRenderingWindowSet_context(visuRenderingWindowGet_current(), FALSE); */
/*     } */
  if (res)
    {
      fprintf(stderr, "INTERNAL ERROR! Cannot set the OpenGL context"
	      " to the default rendering window.\n");
      exit(1);
    }
  commandLineGet_XWindowGeometry(&width, &height);
  OpenGLViewSet_windowSize((OpenGLView*)0, (guint)width, (guint)height);
      

  DBG_fprintf(stderr,"--- Initialising the parameters ---\n");
  /* Save the gtk tag as a known tag to allow to read such parameters. */
  visuConfigFileAdd_knownTag("gtk");
  /* Look for parameters file */
  path = visuConfigFileGet_validPath(VISU_CONFIGFILE_PARAMETER, R_OK, 0);
  if (path)
    {
      errorMessage = (GString*)0;
      res = visuConfigFileLoad(VISU_CONFIGFILE_PARAMETER, path, &errorMessage);
      if (!res && errorMessage)
	raiseAlertDialogWithScrollView(errorMessage->str);
      if (errorMessage)
	g_string_free(errorMessage, TRUE);
      free(path);
    }
  else
    raiseAlertDialog("WARNING! Unable to find a valid parameters file. Starting with defaults.");

  DBG_fprintf(stderr,"--- Initialising resources ---\n");
  /* Look for resources file */
  path = visuConfigFileGet_validPath(VISU_CONFIGFILE_RESOURCE, R_OK, 0);
  if (path)
    {
      errorMessage = (GString*)0;
      res = visuConfigFileLoad(VISU_CONFIGFILE_RESOURCE, path, &errorMessage);
      if (!res && errorMessage)
	raiseAlertDialogWithScrollView(errorMessage->str);
      if (errorMessage)
	g_string_free(errorMessage, TRUE);
      free(path);
    }

  newData = (VisuData*)0;
  filename = commandLineGet_ArgFilename();
  spin_filename = commandLineGet_ArgSpinFileName();
  if (spin_filename && filename)
    {
      setRenderingMethodInUse(pointerOnRenderingSpinMethod);
      newData = visuDataNew();
      if (!newData)
	exit(1);

      dirname = g_path_get_dirname(filename);
      normDir = normalize_path(dirname);
      setLastOpenDirectory((char*)normDir);
      g_free(dirname);
      g_free(normDir);

      visuDataAdd_file(newData, filename, FILE_KIND_POSITION);
      visuDataAdd_file(newData, spin_filename, FILE_KIND_SPIN);
      free(spin_filename);
      free(filename);
      spin_filename = (char*)0;
      filename = (char*)0;

      DBG_fprintf(stderr, "GTK Main : adding the load call back in the queue.\n");
      g_idle_add(commandLineLoadWithGtk, (gpointer)newData);
    }
  else if (filename)
    {
      setRenderingMethodInUse(pointerOnRenderingAtomicMethod);
      newData = visuDataNew();
      if (!newData)
	exit(1);

      dirname = g_path_get_dirname(filename);
      normDir = normalize_path(dirname);
      setLastOpenDirectory((char*)normDir);
      g_free(dirname);
      g_free(normDir);

      visuDataAdd_file(newData, filename, FILE_KIND_POSITION);
      free(filename);
      filename = (char*)0;

      DBG_fprintf(stderr, "GTK Main : adding the load call back in the queue.\n");
      g_idle_add(commandLineLoadWithGtk, (gpointer)newData);
    }
  else
    {
      setRenderingButtonSensitive(FALSE);
      drawToEmpty();
    }
  if (spin_filename)
    free(spin_filename);
  if (filename)
    free(filename);


  /* Initialise the first subPanel now that resources have been loaded. */
  if (currentSubPanel->onEnter)
    currentSubPanel->onEnter((gpointer)0);
  
  /* Initialise the buttons of the main interface. */
  wd = lookup_widget(mainWindow, "checkPairs");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd),
			       (gboolean)getPairsOnOff());

  gtk_widget_show (mainWindow);

  return FALSE;
}

gboolean commandLineLoadWithGtk(gpointer data)
{
  int i, somethingIsLoaded, presetShade;
  gboolean res;
  gchar *colorizeFilename, *planeFile;
  int *colUsed;
  float *translations;
  GList *list;
  Shade *shade;
  VisuData *obj;
  GError *error;

  obj = (VisuData*)data;
  loadFileWithGtk(&obj);

  /* Post loading sequence, corresponding to all other arguments given to command line. */
  /* translate argument */
  if (obj)
    {
      translations = commandLineGet_translation();
      if (translations)
	{
	  visuDataSet_XYZtranslation(obj, translations);
	  visuData_constrainedInTheBox(obj);
	}
    }

  visuRenderingWindowSet_visuData(visuRenderingWindowGet_current(),
				  obj);

  if (!obj)
    {
      drawToEmpty();
      return FALSE;
    }

  /* Post loading sequence, corresponding to all other arguments given to command line. */
  /* colorize argument */
  colorizeFilename = commandLineGet_colorizeFileName();
  if (colorizeFilename)
    {
      somethingIsLoaded = panelDataFileLoad_file(obj, colorizeFilename);
      if (somethingIsLoaded)
	{
	  colUsed = commandLineGet_colorizeColUsed();
	  if (colUsed)
	    for (i = 0; i < 3; i++)
	      dataFileSet_colUsed(obj, colUsed[i] - 1, i);
	  else
	    {
	      presetShade = commandLineGet_colorizePresetColor();
	      if (presetShade < 0)
		presetShade = 0;
	      list = dataFileBuild_presetShadeList();
	      if (list)
		{
		  shade = (Shade*)g_list_nth_data(list, presetShade);
		  if (shade)
		    dataFileSet_shade(obj, shade);
		}
	    }
	}
    }
  /* plane file argument */
  planeFile = commandLineGet_planesFileName();
  if (planeFile)
    {
      panelPlanesSet_use(obj, TRUE);
      error = (GError*)0;
      res = panelPlanesLoad_file(obj, planeFile, &error);
      if (error)
	{
	  raiseAlertDialog(error->message);
	  g_clear_error(&error);
	}
      else if (res)
	panelPlanesApply_hidingScheme(obj);
    }
  
  visuData_createAllNodes(obj);
  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		 0 , NULL);

  return FALSE;
}

gboolean loadAndRenderFileWithGtk(gpointer data)
{
  VisuData *obj;

  obj = VISU_DATA(data);
  loadFileWithGtk(&obj);

  visuRenderingWindowSet_visuData(visuRenderingWindowGet_current(),
				  obj);

  if (!obj)
    return FALSE;

  visuData_createAllNodes(obj);
  g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		 0 , NULL);

  return FALSE;
}




/**********************************/
/* Fuctions to rule the notebook. */
/**********************************/
void freeSubPanel(subPanel* panel)
{
  if (panel)
    {
      if (panel->comboLabel)
	free(panel->comboLabel);
      if (panel->tabLabel)
	free(panel->tabLabel);
      free(panel);
    }
}
subPanel* createSubPanel(char* name, char *tabName, GtkWidget *child,
			 onChangePanelFunc enter, onChangePanelFunc leave)
{
  subPanel* panel;

  /* allocate the structure */
  panel = malloc(sizeof(subPanel));
  if (!panel)
    {
      allocationProblems();
      return (subPanel*)0;
    }
    

  /* initialise the values. */
  if (!name || !name[0] || !tabName || !tabName[0] || !child)
    return (subPanel*)0;
  /* allocate the variables */
  panel->comboLabel = malloc(sizeof(char) * (strlen(name) + 1));
  if (!panel->comboLabel)
    {
      allocationProblems();
      freeSubPanel(panel);
      return (subPanel*)0;
    }
  panel->tabLabel = malloc(sizeof(char) * (strlen(tabName) + 1));
  if (!panel->tabLabel)
    {
      allocationProblems();
      freeSubPanel(panel);
      return (subPanel*)0;
    }
  /* initialise the values. */
  strcpy(panel->comboLabel, name);
  strcpy(panel->tabLabel, tabName);
  panel->icon = (GtkWidget*)0;
  panel->child = child;
  panel->onEnter = enter;
  panel->onLeave = leave;
  
  return panel;
}
subPanel* createSubPanelWithIconFromPath(char* name, char *tabName,
					 char* iconPath, GtkWidget *child,
					 onChangePanelFunc enter, onChangePanelFunc leave)
{
  subPanel *panel;

  panel = createSubPanel(name, tabName, child, enter, leave);
  if (!panel)
    return (subPanel*)0;

  /* TODO : mettre un truc qui limite la taille des images
     effichées à 20x20 par exemple en chargeant de GdkImage et en
     regardant sa taille. */
  panel->icon = create_pixmap((GtkWidget*)0,
			      (const gchar *)iconPath);
  return panel;
}
subPanel* createSubPanelWithIconFromStock(char* name, char *tabName,
					  gchar *id, GtkWidget *child,
					  onChangePanelFunc enter, onChangePanelFunc leave)
{
  subPanel *panel;

  panel = createSubPanel(name, tabName, child, enter, leave);
  if (!panel)
    return (subPanel*)0;

  panel->icon = gtk_image_new_from_stock(id, GTK_ICON_SIZE_MENU);
  return panel;
}

void makeSubPanel(subPanel *sb)
{
  GtkWidget *label;
  GtkWidget *hbox;

  char *lb;
  char *defaultLabel = "Void tab";

  g_return_if_fail(sb);

  if (sb->tabLabel)
    lb = sb->tabLabel;
  else
    lb = defaultLabel;

  label = gtk_label_new(lb);
  gtk_widget_show(label);
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_widget_show(hbox);
  if (sb->icon)
    {
      gtk_box_pack_start(GTK_BOX(hbox), sb->icon, FALSE, FALSE, 0);
      gtk_widget_show(sb->icon);
    }
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  gtk_notebook_append_page(GTK_NOTEBOOK(tabSubPanel), sb->child, hbox);
  gtk_widget_show(sb->child);
  DBG_fprintf(stderr, "Gtk Main : created '%s' subpanel OK.\n", sb->comboLabel);
}

void addSubPanel(subPanel *sp)
{
  GtkTreeIter iter;

  g_return_if_fail(sp && sp->comboLabel);

  gtk_list_store_append(gtkMain_subpanelListStore, &iter);
  gtk_list_store_set(gtkMain_subpanelListStore, &iter,
/* 		     GTKMAIN_COLUMN_ICON, NULL, */
		     GTKMAIN_COLUMN_NAME, sp->comboLabel,
		     GTKMAIN_COLUMN_POINTER_TO_DATA, sp,
		     -1);

  makeSubPanel(sp);
}

void initSubPanel()
{
  int i;
  subPanel *panel;
  GtkCellRenderer *renderer;
  GtkTreeIter iter;
  gboolean selected;

  /* We create the structure that store the subpanels. */
  gtkMain_subpanelListStore = gtk_list_store_new(GTKMAIN_N_COLUMNS,
/* 						 GDK_TYPE_PIXBUF, */
						 G_TYPE_STRING,
						 G_TYPE_POINTER);

  for (i = 0; listSubPanelInitFunc[i]; i++)
    {
      panel = listSubPanelInitFunc[i]();
      if (!panel)
	g_error("Can't initialise subpanel number %d.\n", i);
      
      DBG_fprintf(stderr, "Gtk Main : initialise '%s' subpanel OK.\n", panel->comboLabel);
    }

  /* We create the tree widget that show the subpanel. */
  gtk_combo_box_set_model(GTK_COMBO_BOX(comboboxListeSubPanel),
			  GTK_TREE_MODEL(gtkMain_subpanelListStore));
  /* W remove all layout added by glade... */
  gtk_cell_layout_clear(GTK_CELL_LAYOUT(comboboxListeSubPanel));
  renderer = gtk_cell_renderer_text_new();
  gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(comboboxListeSubPanel), renderer, FALSE);
  gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(comboboxListeSubPanel),
				renderer, "text", GTKMAIN_COLUMN_NAME);
  gtk_combo_box_set_active(GTK_COMBO_BOX(comboboxListeSubPanel), 0);

  gtk_notebook_remove_page(GTK_NOTEBOOK(tabSubPanel), 0);
  /* Add the signals. */
  comboboxListeSubPanelSignalChanged =
    g_signal_connect(G_OBJECT(comboboxListeSubPanel), "changed",
		     G_CALLBACK(comboboxChanged), (gpointer)tabSubPanel);
  tabSubPanelSignalChanged =
    g_signal_connect(G_OBJECT(tabSubPanel), "switch-page",
		     G_CALLBACK(subPanelChanged), (gpointer)comboboxListeSubPanel);
  gtk_notebook_set_current_page(GTK_NOTEBOOK(tabSubPanel), 0);
  /* Call the refresh function for the viewed subPanel. */
  currentSubPanel = (subPanel*)0;
  selected = gtk_combo_box_get_active_iter(GTK_COMBO_BOX(comboboxListeSubPanel), &iter);
  g_return_if_fail(selected);
  gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(comboboxListeSubPanel)), &iter,
		     GTKMAIN_COLUMN_POINTER_TO_DATA, &currentSubPanel,
		     -1);
}



/*******************/
/* Local callbacks */
/*******************/

void setRenderingButtonSensitive(gboolean bool)
{
  gtk_widget_set_sensitive(pairsButton, bool);
  gtk_widget_set_sensitive(pickObserveButton, bool);
}
void onFileChange(GObject *obj, VisuData *dataObj, gpointer bool)
{ 
  if (dataObj)
    setRenderingButtonSensitive(TRUE);
  else
    setRenderingButtonSensitive(FALSE);
}
static void onResourcesLoaded(GObject *visu, gpointer data)
{
  GtkWidget *wd;

  wd = lookup_widget(mainWindow, "checkPairs");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(wd), (gboolean)getPairsOnOff());
}
void setFileButtonSensitive(GObject *obj, gpointer bool)
{
  if (getRenderingMethodInUse())
    gtk_widget_set_sensitive(loadButton, TRUE);
  else
    gtk_widget_set_sensitive(loadButton, FALSE);
  setRenderingButtonSensitive(FALSE);
}
void checkPairsChanged(GtkToggleButton *togglebutton, gpointer user_data)
{
  gboolean res;
  RenderingWindow *window;

  res = gtk_toggle_button_get_active(togglebutton);
  setPairsOnOff(res);
  if (res)
    {
      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);
    }
  else
    g_signal_emit (visu, VISU_GET_CLASS (visu)->OpenGLAskForReDraw_signal_id,
		   0 , NULL);
}



/*****************/
/* Miscellaneous */
/*****************/

char* getLastOpenDirectory()
{
  DBG_fprintf(stderr, "Gtk Main : get the last open directory : '%s'.\n", lastOpenDirectory);
  return lastOpenDirectory;
}
void setLastOpenDirectory(char* directory)
{
  if (lastOpenDirectory)
    g_free(lastOpenDirectory);

  if (!g_path_is_absolute(directory))
    lastOpenDirectory = g_build_filename(g_get_current_dir(), directory, NULL);
  else
    lastOpenDirectory = g_build_filename(directory, NULL);
  DBG_fprintf(stderr, "Gtk Main : set the last open directory to '%s', OK.\n", lastOpenDirectory);
}

/* This method loads the general function to load data from file
   and deals with errors with gtkDialogs. */
void loadFileWithGtk(VisuData **visuData)
{
  GString *buffer;
  VisuData *prevData;
  int res;
  gboolean changeElement;
  RenderingWindow *window;

  if (!visuData)
    return;

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

  prevData = renderingWindowGet_visuData(window);
  DBG_fprintf(stderr, "Gtk main : loading process ... %p points to"
	      " previous VisuData.\n", (gpointer)prevData);

  res = loadDataFromFile(*visuData, &buffer);

  if (!res)
    {
      g_object_unref(*visuData);
      *visuData = (VisuData*)0;
      raiseAlertDialog(buffer->str);
    }

  DBG_fprintf(stderr, "Gtk main : loading process ... %p points to"
	      " previous VisuData.\n", (gpointer)prevData);

  if (prevData && *visuData)
    {
      changeElement = visuData_compareElements(prevData, *visuData);
      visuDataSet_changeElementFlag(*visuData, changeElement);
    }
  
  if (buffer)
    g_string_free(buffer, TRUE);

  return;
}

void setRememberPosition(int val)
{
  DBG_fprintf(stderr, "GTK Main : set the remember parameter to %d.\n", val);
  rememberWindowPosition = val;
}
int getRememberPosition()
{
  return rememberWindowPosition;
}

gchar* getSelectedDirectory()
{
  GtkWidget *file_selector;
  gchar *dirname;
  char *directory;

  /* Create the selector */
  file_selector = gtk_file_chooser_dialog_new(_("Choose a directory"),
					      GTK_WINDOW(mainWindow),
					      GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					      GTK_STOCK_OPEN, GTK_RESPONSE_OK,
					      NULL);
  directory = getLastOpenDirectory();
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(file_selector), directory);
  gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(file_selector),
				       FALSE);

  gtk_widget_set_name(file_selector, "filesel");
  gtk_window_set_position(GTK_WINDOW(file_selector), GTK_WIN_POS_CENTER_ON_PARENT);
  gtk_window_set_modal(GTK_WINDOW (file_selector), TRUE);

  if (gtk_dialog_run (GTK_DIALOG (file_selector)) == GTK_RESPONSE_OK)
    {
      dirname = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (file_selector));
      setLastOpenDirectory(dirname);
    }
  else
    dirname = (gchar*)0;

  gtk_widget_destroy (file_selector);

  return dirname;
}


/* This method hides the specified window and save its coordinates if
   it is specified by a parameter. */
void visuHideWindow(GtkWindow *win)
{
  int *val;

  if (!win)
    return;

  if (rememberWindowPosition)
    {
      val = (int*)g_hash_table_lookup(windowPosition, win);
      if (!val)
	{
	  val = malloc(sizeof(int) * 2);
	  if (!val)
	    {
	      allocationProblems();
	      exit(1);
	    }
	  g_hash_table_insert(windowPosition, (gpointer)win, (gpointer)val);
	}
      gtk_window_get_position(win, &val[0], &val[1]);
      DBG_fprintf(stderr, "Gtk Main : store position (%d,%d) for window %d.\n", 
		  val[0], val[1], GPOINTER_TO_INT(win));
    }
  gtk_widget_hide(GTK_WIDGET(win));
}

/* This method shows the specified window and try to put it at its
   former position if the good parameter is used. */
void visuShowWindow(GtkWindow *win)
{
  int *val;

  if (!win)
    return;

  if (rememberWindowPosition)
    {
      val = (int*)g_hash_table_lookup(windowPosition, win);
      if (val)
	{
	  gtk_window_move(win, val[0], val[1]);
	  DBG_fprintf(stderr, "Gtk Main : set position (%d,%d) for window %d.\n", 
		      val[0], val[1], GPOINTER_TO_INT(win));
	}
    }
  gtk_widget_show(GTK_WIDGET(win));
}






/* Draw the alert window with the specified message. */
void raiseAlertDialog(char *message)
{
  GtkWidget *alert;
  GtkWidget *label;
  GtkWidget *wd;

  alert = create_alertDialog ();
  label = lookup_widget(alert, "alertLabel");
  gtk_label_set_text(GTK_LABEL(label), message);
  gtk_widget_set_name(alert, "error");
  wd = lookup_widget(alert, "buttonAlert");
  gtk_widget_set_name(wd, "error_button");
  gtk_widget_show (alert);
  /* block in a loop waiting for reply. */
  gtk_dialog_run (GTK_DIALOG (alert));
  gtk_widget_destroy(alert);
}
/* Draw the alert window with the specified message. */
void raiseAlertDialogWithScrollView(char *message)
{
  GtkWidget *alert;
  GtkWidget *text;
  GtkWidget *wd;
  GtkTextBuffer *buf;

  alert = create_alertDialogLong ();
  text = lookup_widget(alert, "alertText");
  buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW(text));
  if (message)
    gtk_text_buffer_set_text (GTK_TEXT_BUFFER(buf), message, -1);
  else
    gtk_text_buffer_set_text (GTK_TEXT_BUFFER(buf), _("INTERNAL ERROR! No message available!"), -1);
  gtk_widget_set_name(alert, "error");
  wd = lookup_widget(alert, "buttonAlert");
  gtk_widget_set_name(wd, "error_button");
  gtk_widget_show (alert);
  /* block in a loop waiting for reply. */
  gtk_dialog_run (GTK_DIALOG (alert));
  gtk_widget_destroy(alert);
}
/* This function must be called in a blocking loop to update
   different things like the gtk for example. */
void visuWaitFunction()
{
  while(gtk_events_pending())
    gtk_main_iteration();
}

GList* gtkMainCreate_fileChooserFilter(GList *list, GtkFileFilter **filterDefault)
{
  GtkFileFilter *filter, *filterAll;
  GList *tmpLst, *tmpLst2;
  char *name;
  GList *returnedFilters;

  returnedFilters = (GList*)0;
  filterAll = gtk_file_filter_new ();
  gtk_file_filter_set_name(filterAll, _("All supported formats"));
  tmpLst = list;
  while(tmpLst)
    {
      filter = gtk_file_filter_new ();
      name = fileFormatGet_label((FileFormat*)tmpLst->data);
      if (name)
	gtk_file_filter_set_name(filter, name);
      else
	gtk_file_filter_set_name(filter, _("No description"));
      tmpLst2 = ((FileFormat*)tmpLst->data)->fileType;
      while (tmpLst2)
	{
	  gtk_file_filter_add_pattern (filter, (char*)tmpLst2->data);
	  gtk_file_filter_add_pattern (filterAll, (char*)tmpLst2->data);
	  tmpLst2 = g_list_next(tmpLst2);
	}
      tmpLst = g_list_next(tmpLst);
      returnedFilters = g_list_append(returnedFilters, filter);
    }
  returnedFilters = g_list_append(returnedFilters, filterAll);
  filter = gtk_file_filter_new ();
  gtk_file_filter_set_name(filter, "All files");
  gtk_file_filter_add_pattern (filter, "*");
  returnedFilters = g_list_append(returnedFilters, filter);
  
  *filterDefault = filterAll;

  return returnedFilters;
}
int gtkMainCreate_defaultFileChooser(VisuData *data, GtkFileChooser **fileChooser,
				     GtkWidget **fileWidget)
{
  GtkWidget *fileSelection;
  GList *filters, *tmpLst;
  GtkWidget *dialog;
  RenderingMethod *method;
  gchar* directory, *filename;
  GtkFileFilter *filterDefault;

  if (!data)
    return 0;

  filterDefault = (GtkFileFilter*)0;
  method = getRenderingMethodInUse();
  if (!method)
    filters = (GList*)0;
  else
    filters = gtkMainCreate_fileChooserFilter(method->fileType[0], &filterDefault);
  
  fileSelection = gtk_file_chooser_dialog_new(_("Load session"),
					      GTK_WINDOW(mainWindow),
					      GTK_FILE_CHOOSER_ACTION_OPEN,
					      GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
					      GTK_STOCK_OPEN, GTK_RESPONSE_OK,
					      NULL);
  *fileChooser = GTK_FILE_CHOOSER(fileSelection);
  *fileWidget = fileSelection;
  directory = getLastOpenDirectory();
  if (directory)
    gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(fileSelection), directory);
  tmpLst = filters;
  while(tmpLst)
    {
      gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(fileSelection),
				  (GtkFileFilter*)(tmpLst->data));
      tmpLst = g_list_next(tmpLst);
    }
  if (filters && filterDefault)
    gtk_file_chooser_set_filter(GTK_FILE_CHOOSER(fileSelection), filterDefault);
  g_list_free(filters);

  gtk_widget_set_name(fileSelection, "filesel");
  gtk_window_set_position(GTK_WINDOW(fileSelection), GTK_WIN_POS_CENTER_ON_PARENT);
  gtk_window_set_modal(GTK_WINDOW(fileSelection), TRUE);

  if (gtk_dialog_run(GTK_DIALOG(fileSelection)) == GTK_RESPONSE_OK)
    {
      filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fileSelection));
      visuDataAdd_file(data, filename, 0);
      g_free(filename);
      return 1;
    }
  else
    return 0;
}

void gtkMainSet_renderingSpecificMethods(RenderingMethod *method,
					 createGtkWidgetFunc createLoadWidget,
					 createGtkLoadWidgetFunc methodLoad)
{
  struct renderingMethod_gtkMain_struct *container;

  if (!method)
    return;

  container = malloc(sizeof(struct renderingMethod_gtkMain_struct));
  if (!container)
    {
      allocationProblems();
      exit(1);
    }
  container->createOpenAddOn = createLoadWidget;
  container->createLoadAction = methodLoad;
  
  g_hash_table_insert(renderingMethod_gtkMain, (gpointer)method, (gpointer)container);
}
createGtkLoadWidgetFunc gtkMainGet_renderingSpecificOpen(RenderingMethod *method)
{
  struct renderingMethod_gtkMain_struct *container;

  if (!method)
    return (createGtkLoadWidgetFunc)0;

  DBG_fprintf(stderr, "Gtk Main : looking for a specific load interface for rendering"
	      " method '%s'...\n", method->name);
  
  container = (struct renderingMethod_gtkMain_struct *)
    g_hash_table_lookup(renderingMethod_gtkMain, (gpointer)method);

  if (!container)
    return (createGtkLoadWidgetFunc)0;
  
  if (container->createLoadAction)
    return container->createLoadAction;
  else
    return gtkMainCreate_defaultFileChooser;
}

void onHideNextTime(GtkToggleButton *button, gpointer data)
{
  char *posNext;
  gchar *path, *bufferR, *pos;
  GString *errorMessage, *bufferW, *bufferW2;
  gboolean resOk;
  int lines;
  GIOChannel *file;
  GError *err;
  gsize taille;
  GIOStatus statOK;

  g_return_if_fail(data);
  path = (gchar*)data;
  
  DBG_fprintf(stderr, "Gtk Main : change the warning dialog parameter in file '%s'.\n", path);
  warningWhenQuit = !gtk_toggle_button_get_active(button);

  /* If no file exists in the given path, we create it. */
  if (!g_file_test(path, G_FILE_TEST_EXISTS))
    {
      errorMessage = (GString*)0;
      resOk = visuConfigFileSave(VISU_CONFIGFILE_PARAMETER, path, &errorMessage, &lines);
      if (!resOk)
	{
	  if (errorMessage)
	    raiseAlertDialogWithScrollView(errorMessage->str);
	}
      if (errorMessage)
	{
	  DBG_fprintf(stderr, "Gtk Main : trace from exporting par...\n%s",
		      errorMessage->str);
	  g_string_free(errorMessage, TRUE);
	}
      return;
    }

  /* If a parameter file already exist, we then just change the right line. */
  err = (GError*)0;
  file = g_io_channel_new_file(path, "r", &err);
  if (err)
    {
      raiseAlertDialogWithScrollView(err->message);
      g_error_free(err);
      return;
    }

  bufferR = (gchar*)0;
  err = (GError*)0;
  statOK = g_io_channel_read_to_end(file, &bufferR, &taille, &err);
  if (err)
    {
      raiseAlertDialogWithScrollView(err->message);
      g_error_free(err);
      g_io_channel_shutdown(file, FALSE, (GError**)0);
      g_io_channel_unref(file);
      return;
    }

  
  g_io_channel_shutdown(file, FALSE, (GError**)0);
  g_io_channel_unref(file);

  /* We reopen the channel in write acces. */
  err = (GError*)0;
  file = g_io_channel_new_file(path, "w", &err);
  if (err)
    {
      raiseAlertDialogWithScrollView(err->message);
      g_error_free(err);
      return;
    }

  bufferW = g_string_new(bufferR);
  g_free(bufferR);
  /* Try to find the flag of the parameter. */
  pos = g_strrstr(bufferW->str, "\n"FLAG_PARAMETER_GTKMAIN_QUIT);
  if (!pos)
    {
      /* We append it at the end of the file. */
      DBG_fprintf(stderr, " | Can't find the option, appending it.\n");
      exportParametersGtkMain(bufferW, &lines);

      err = (GError*)0;
      statOK = g_io_channel_write_chars(file, bufferW->str, -1,
                                        &taille, &err);
      if (err)
	{
	  raiseAlertDialogWithScrollView(err->message);
	  g_error_free(err);
	}
    }
  else
    {
      DBG_fprintf(stderr, " | Option found, changing its value.\n");
      /* We erase the line and rewrite it. */
      *(pos + 1) = '\0';
      bufferW2 = g_string_new(bufferW->str);
      g_string_append_printf(bufferW2, "%s[gtk]: %i\n", FLAG_PARAMETER_GTKMAIN_QUIT,
			     (int)warningWhenQuit);
      posNext = strstr(pos + 2, "\n");
      if (posNext)
	g_string_append(bufferW2, posNext + 1);
      
      err = (GError*)0;
      statOK = g_io_channel_write_chars(file, bufferW2->str, -1,
                                        &taille, &err);
      if (err)
	{
	  raiseAlertDialogWithScrollView(err->message);
	  g_error_free(err);
	}
      g_string_free(bufferW2, TRUE);
    }
  g_io_channel_shutdown(file, TRUE, (GError**)0);
  g_io_channel_unref(file);

  g_string_free(bufferW, TRUE);
}
void onQuitButton(GtkButton *button, gpointer data)
{
  GtkWidget *quitDialog, *wd;
  GList *dirs;
  gchar *filename = "v_sim.par";
  gchar *path;

  if (!warningWhenQuit)
    {
      gtk_main_quit();
      return;
    }

  quitDialog = create_quitDialog();
  
  /* Try to find installDir/v_sim.par or ~/.v_sim/v_sim.par
     that is writable to store the preference of the hiding
     mode of the dialog. */
  dirs = (GList*)0;
  dirs = g_list_prepend(dirs, (gpointer)v_sim_conf_dir);
  dirs = g_list_prepend(dirs, (gpointer)v_sim_local_conf_dir);

  path = (gchar*)0;
  path = visuConfigFileGet_nextValidPath(VISU_CONFIGFILE_PARAMETER, W_OK, &dirs, 0);
  if (!path)
    {
      wd = lookup_widget(quitDialog, "hboxWarning");
      gtk_widget_show(wd);
    }

  wd = lookup_widget(quitDialog, "checkbuttonHideNextTime");
  g_signal_connect(G_OBJECT(wd), "toggled",
		   G_CALLBACK(onHideNextTime), (gpointer)path);
  if (!path)
    gtk_widget_set_sensitive(wd, FALSE);
  
  if (gtk_dialog_run(GTK_DIALOG(quitDialog)) == GTK_RESPONSE_OK)
    gtk_main_quit();
  else
    gtk_widget_destroy(quitDialog);
}

gboolean readMainQuitPolicy(gchar **lines, int nbLines,
			    int position, GString *errorMessage)
{
  int res;
  int bool;

  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_GTKMAIN_QUIT);
      warningWhenQuit = PARAMETER_GTKMAIN_QUIT_DEFAULT;
      return FALSE;
    }
  
  if (bool)
    warningWhenQuit = TRUE;
  else
    warningWhenQuit = FALSE;
  
  return TRUE;
}
gboolean exportParametersGtkMain(GString *data, int *nbLinesWritten)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_GTKMAIN_QUIT);
  g_string_append_printf(data, "%s[gtk]: %i\n\n", FLAG_PARAMETER_GTKMAIN_QUIT,
			 (int)warningWhenQuit);
  *nbLinesWritten = 3;

  return TRUE;
}
