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

	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 "panelBrowser.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

#include <glib/gstdio.h>
#include <gtk/gtk.h>

#include <visu_gtk.h>
#include <visu_tools.h>
#include <visu_basic.h>
#include <visu_configFile.h>
#include <visu_dataatomic.h>
#include <visu_dataspin.h>

#include <support.h>
#include <gtk_main.h>
#include <extraGtkFunctions/gtk_dumpDialogWidget.h>
#include <gtk_openGLWidget.h>
#include <gtk_renderingWindowWidget.h>

/**
 * SECTION: panelBrowser
 * @short_description: A tab to view a list of files and quickly
 * change from one to another.
 *
 * <para>One can display a message about the file list by calling
 * visu_ui_panel_browser_setMessage(). It is possible also to change
 * the browser directory or directories with
 * visu_ui_panel_browser_setCurrentDirectory().</para>
 */

static GtkWidget *panelBrowser, *vbox1;
static GtkTreeStore *treeStoreFiles;
static GtkTreeModel *treeStoreFilesFilter;
enum
  {
    COLUMN_BOOLEAN,       /* Rendered or not. */
    COLUMN_NAME,          /* File name in locale of the file system. */
    COLUMN_NAME_UTF8,     /* File name in UTF8 (to view it in GTK). */
    COLUMN_DATE,          /* The modified time for the file. */
    COLUMN_DATA,          /* File info (size / date). */
    COLUMN_ACTIVE,        /* A boolean to set if this file is filtered or not. */
    COLUMN_FILE_KIND,     /* An integer that correspond to the file type. */
    COLUMN_FILE_VALID,    /* TRUE if the file is parsable. */
    COLUMN_FILE_ERROR_ID, /* a stock icon id. */
    N_COLUMNS
  };
static gboolean dirDirty;
static GtkWidget *fileTree;
static GtkWidget *scrolledwindow1, *infoBar, *labelInfo;
static GtkWidget *labelDirectory;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 17
static GtkWidget *infoImg;
#endif
/* A list of array of directory names and the current position in the list. */
static GList *historyBrowseredDirectory, *currentHistory;
/* A NULL terminated array of directories names, currently printed. */
static gchar **currentBrowseredDirectory;
/* The string common to all paths in currentBrowseredDirectory. */
static gchar *commonBrowseredDirectory;
#define HISTORY_TOOLTIP_PREV "Go to previously visited directories in history."
#define HISTORY_TOOLTIP_NEXT "Go to next visited directories in history."

static GtkWidget *entryFilterBrowser;
enum
  {
    PANEL_BROWSER_COLUMN_FILTER_LABEL,
    PANEL_BROWSER_COLUMN_FILTER_ID,
    PANEL_BROWSER_N_COLUMN_FILTER
  };
static GtkWidget *comboFilter;
struct TimerInfo_
{
  GtkWidget *bt;
  guint timer, label, nbFiles;
  gboolean abort;
};

static GtkWidget *imagePlay;
static GtkWidget *imageStop;
static GtkWidget *buttonPlayStop;
static GtkWidget *spinDelay;

static GtkWidget *buttonPrevious;
static GtkWidget *buttonNext;
static GtkWidget *buttonDirPrev;
static GtkWidget *buttonDirNext;
static GtkWidget *buttonSelectAll;
static GtkWidget *buttonUnselectAll;
static GtkWidget *buttonDirectory;
static GtkWidget *buttonRecurse;
static GtkWidget *radioGoAround, *radioGoOnce, *radioGoAndBack;
static int currentBrowseDirection;
static GtkTreePath *startBrowsePath;
static GtkWidget *buttonDumpAll;
/* GtkProgressBar *progressBarDump; */

static GtkWidget *createInteriorBrowser();
static void browseDirectory();
static void addParsedDirectory(int commonPathLen, const gchar *root,
			       GDir *gdir, gboolean recurse,
                               GList *atFmt, gint atKind, GList *spFmt, gint spKind,
			       struct TimerInfo_ *timer);
static gboolean showProgressBar(gpointer data);
static void hideProgressBar(struct TimerInfo_ *timer);
/* Load and render the given file. The iter is given in the filter
   model. */
static gboolean browserLoad(gchar *filename, gchar* fileUTF8,
                            GtkTreeIter *iter, int nSet);
/* For the given iter load it. The iter must be given
   in the filter model. */
static gboolean navigateInFiles(GtkTreePath *path, GtkTreeIter *iterSelected);
static void selectFile(GtkTreePath *path, GtkTreeIter *iter);

gboolean gtk_tree_model_get_iter_last(GtkTreeModel *model, GtkTreeIter *last, GtkTreePath **path);
void panelBrowserSet_labelCurrentDir();

/* Callbacks */
static void onCheckFile(GtkCellRendererToggle *cell_renderer, gchar *path,
			gpointer user_data);
static void onTreeviewActivated(GtkTreeView *treeview, GtkTreePath *path,
                                GtkTreeViewColumn *col, gpointer user_data);
static void navigateClicked(GtkButton *button, gpointer data);
static void checkFiles(GtkButton *button, gpointer data);
static void onDirectoryClicked(GtkButton *button, gpointer data);
static void onPlayStopClicked(GtkToggleButton *button _U_, gpointer data _U_);
static void stopPlayStop(gpointer data);
static void onSpinDelayChangeValue(GtkSpinButton *spinbutton, gpointer user_data);
void onDumpButtonClicked(GtkButton *button, gpointer user_data);
static void onFilterChanged(GtkEditable *entry, gpointer user_data);
void abortDumpAll(GtkButton *button, gpointer data);
void updateDumpAllProgressBar(gpointer data);
static gboolean panelBrowserIsIterVisible(GtkTreeModel *model,
					  GtkTreeIter *iter, gpointer data);
static void onRefreshDir(GtkButton *button, gpointer data);
static void onEnter(VisuUiPanel *visu_ui_panel, gpointer data);
static gboolean checkFile(GtkTreeModel *model, GtkTreePath *path,
			  GtkTreeIter *iter, gpointer data);
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 17
static void onParseAbortClicked(GtkButton *button, gpointer data);
#else
static void onParseAbortClicked(GtkInfoBar *infoBar, gint response, gpointer data);
#endif
static void onRecurseToggled(GtkToggleButton *toggle, gpointer data);
static void onNewDir(GObject *obj, VisuUiDirectoryType type, gpointer userData);
static void onPrevClicked(GtkButton *button, gpointer data);
static void onNextClicked(GtkButton *button, gpointer data);
static gint onSortNames(GtkTreeModel *model, GtkTreeIter *a,
			GtkTreeIter *b, gpointer user_data);
static void onNextPrevFile(VisuUiRenderingWindow *window, gpointer data);

static void updateHistory();
static void updateDirectionalTooltips();

/* Parameters customising the dialog. */
#define FLAG_PARAMETER_BROWSER_HEADERS    "browser_headersVisibility"
#define DESC_PARAMETER_BROWSER_HEADERS    "Show or hide the headers in the treeview ; boolean 0 or 1"
static gboolean showHeaders;
#define FLAG_PARAMETER_BROWSER_DATE    "browser_dateVisibility"
#define DESC_PARAMETER_BROWSER_DATE    "Show or hide the date column in the treeview ; boolean 0 or 1"
static gboolean showDate;
static void exportParameters(GString *data, VisuData *dataObj);

VisuUiPanel* visu_ui_panel_browser_init(VisuUiMain *ui)
{
  char *cl = _("Browser");
  char *tl = _("Browser");
  VisuConfigFileEntry *entry;

  panelBrowser = visu_ui_panel_newWithIconFromPath("Panel_browser", cl, tl,
                                                   "stock-browser_20.png");
  if (!panelBrowser)
    return (VisuUiPanel*)0;
  vbox1 = gtk_vbox_new(FALSE, 0);
  fileTree = (GtkWidget*)0;
  buttonDirPrev = (GtkWidget*)0;
  buttonDirNext = (GtkWidget*)0;
  gtk_container_add(GTK_CONTAINER(panelBrowser), vbox1);
  gtk_container_set_border_width(GTK_CONTAINER(panelBrowser), 5);
  visu_ui_panel_setDockable(VISU_UI_PANEL(panelBrowser), TRUE);

  /* Create the configuration file entries. */
  entry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                           FLAG_PARAMETER_BROWSER_HEADERS,
                                           DESC_PARAMETER_BROWSER_HEADERS,
                                           &showHeaders, FALSE);
  visu_config_file_entry_setVersion(entry, 3.5f);
  entry = visu_config_file_addBooleanEntry(VISU_CONFIG_FILE_PARAMETER,
                                           FLAG_PARAMETER_BROWSER_DATE,
                                           DESC_PARAMETER_BROWSER_DATE,
                                           &showDate, FALSE);
  visu_config_file_entry_setVersion(entry, 3.5f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
				   exportParameters);
						 
  /* Create the tree structure. */
  treeStoreFiles = gtk_tree_store_new(N_COLUMNS,
				      G_TYPE_BOOLEAN,
				      G_TYPE_STRING,
				      G_TYPE_STRING,
				      G_TYPE_UINT,
				      G_TYPE_STRING,
				      G_TYPE_BOOLEAN,
				      G_TYPE_INT,
				      G_TYPE_BOOLEAN,
				      G_TYPE_STRING);
  /* Use the COLUMN_ACTIVE as a flag to hide or not the rows that 
     don't match the filter value. */
  treeStoreFilesFilter = gtk_tree_model_filter_new(GTK_TREE_MODEL(treeStoreFiles), NULL);
  gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter),
					 panelBrowserIsIterVisible, (gpointer)0,
					 (GDestroyNotify)0);
/*   gtk_tree_model_filter_set_visible_column(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter), COLUMN_ACTIVE); */

  gtk_tree_sortable_set_sort_func(GTK_TREE_SORTABLE(treeStoreFiles),
				  COLUMN_NAME_UTF8, onSortNames,
				  (gpointer)0, (GDestroyNotify)0);

  /* Add a new row to the model */
  gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(treeStoreFiles),
				       COLUMN_NAME_UTF8, GTK_SORT_ASCENDING);

  /* Initialise the values. */
  currentBrowseredDirectory = (gchar**)0;
  commonBrowseredDirectory  = (gchar*)0;
  historyBrowseredDirectory = (GList*)0;
  currentHistory            = (GList*)0;
  currentBrowseDirection    = VISU_UI_PANEL_BROWSER_NEXT;
  showHeaders               = FALSE;
  showDate                  = FALSE;
  dirDirty                  = FALSE;

  /* Create the callbacks of all the sensitive widgets. */
  g_signal_connect(G_OBJECT(panelBrowser), "page-entered",
		   G_CALLBACK(onEnter), (gpointer)0);
  g_signal_connect(ui, "DirectoryChanged",
		   G_CALLBACK(onNewDir), (gpointer)0);
  g_signal_connect(G_OBJECT(visu_ui_main_class_getDefaultRendering()), "load-next-file",
                   G_CALLBACK(onNextPrevFile), GINT_TO_POINTER(VISU_UI_PANEL_BROWSER_NEXT));
  g_signal_connect(G_OBJECT(visu_ui_main_class_getDefaultRendering()), "load-prev-file",
                   G_CALLBACK(onNextPrevFile), GINT_TO_POINTER(VISU_UI_PANEL_BROWSER_PREVIOUS));

  return VISU_UI_PANEL(panelBrowser);
}

static GtkWidget *createInteriorBrowser()
{
  /* include from glade */
  GtkWidget *image2;
  GtkWidget *image3;
  GtkWidget *image1;
  GtkWidget *hbox2;
  GtkWidget *label4;
  GtkWidget *label1;
  GtkWidget *hbox5;
  GtkWidget *image4;
  GtkWidget *refreshDirButton;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *column;

  GtkWidget *label;
  GtkWidget *hbox, *vbox;

#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();
#endif

  GSList *radiobuttonCycle_group;

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
  /* This is a label in the browser panel to introduce the
     entry that allows to enter a filter for files shown. */
  label = gtk_label_new(_("Filter: "));
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 2);
  entryFilterBrowser = gtk_entry_new();
  gtk_editable_set_editable(GTK_EDITABLE(entryFilterBrowser), TRUE);
  gtk_entry_set_text(GTK_ENTRY(entryFilterBrowser), "*");
  gtk_box_pack_start(GTK_BOX(hbox), entryFilterBrowser, TRUE, TRUE, 0);
  comboFilter = gtk_combo_box_text_new();
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(comboFilter),
                            "atomic", _("atomic rendering"));
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(comboFilter),
                            "spin", _("spin rendering"));
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(comboFilter),
                            "spin_at", _("atomic part"));
  gtk_combo_box_text_append(GTK_COMBO_BOX_TEXT(comboFilter),
                            "spin_sp", _("spin part"));
  gtk_box_pack_start(GTK_BOX(hbox), comboFilter, FALSE, FALSE, 2);
  gtk_combo_box_set_active(GTK_COMBO_BOX(comboFilter), 0);

  /*******************/
  /* File lists part */
  /*******************/
  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox1), hbox, TRUE, TRUE, 0);
  
  vbox = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 2);

  buttonDirectory = gtk_button_new ();
  gtk_box_pack_start (GTK_BOX (vbox), buttonDirectory, FALSE, FALSE, 1);
  gtk_widget_set_tooltip_text(buttonDirectory,
			_("Choose a different directory."));

  image3 = gtk_image_new_from_icon_name("document-open", GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (buttonDirectory), image3);

  refreshDirButton = gtk_button_new ();
  gtk_widget_set_tooltip_text(refreshDirButton,
			_("Rescan current directory."));
  gtk_box_pack_start(GTK_BOX(vbox), refreshDirButton, FALSE, FALSE, 1);
  image3 = gtk_image_new_from_icon_name("view-refresh", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(refreshDirButton), image3);
  g_signal_connect(G_OBJECT(refreshDirButton), "clicked",
		   G_CALLBACK(onRefreshDir), (gpointer)entryFilterBrowser);

  buttonRecurse = gtk_toggle_button_new();
  gtk_widget_set_tooltip_text(buttonRecurse,
			_("Read directories recursively."));
  gtk_box_pack_start(GTK_BOX(vbox), buttonRecurse, FALSE, FALSE, 1);
  image3 = gtk_image_new_from_icon_name("zoom-in", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonRecurse), image3);
  g_signal_connect(G_OBJECT(buttonRecurse), "toggled",
		   G_CALLBACK(onRecurseToggled), (gpointer)entryFilterBrowser);

  buttonDirPrev = gtk_button_new();
  gtk_widget_set_sensitive(buttonDirPrev, FALSE);
  gtk_widget_set_tooltip_text(buttonDirPrev, _(HISTORY_TOOLTIP_PREV));
  gtk_box_pack_start(GTK_BOX(vbox), buttonDirPrev, FALSE, FALSE, 1);
  image3 = gtk_image_new_from_icon_name("go-previous", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonDirPrev), image3);
  g_signal_connect(G_OBJECT(buttonDirPrev), "clicked",
		   G_CALLBACK(onPrevClicked), (gpointer)0);

  buttonDirNext = gtk_button_new();
  gtk_widget_set_sensitive(buttonDirNext, FALSE);
  gtk_widget_set_tooltip_text(buttonDirNext, _(HISTORY_TOOLTIP_NEXT));
  gtk_box_pack_start(GTK_BOX(vbox), buttonDirNext, FALSE, FALSE, 1);
  image3 = gtk_image_new_from_icon_name("go-next", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonDirNext), image3);
  g_signal_connect(G_OBJECT(buttonDirNext), "clicked",
		   G_CALLBACK(onNextClicked), (gpointer)0);

#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 5
  label = gtk_label_new(_("Dir.:"));
  gtk_label_set_angle(GTK_LABEL(label), 90.);
  gtk_box_pack_end(GTK_BOX(vbox), label, TRUE, TRUE, 0);
#endif

  /* The infobar for cancel if to long and other messages. */
  vbox = gtk_vbox_new(FALSE, 3);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0);
  labelInfo = gtk_label_new("");
  gtk_label_set_xalign(GTK_LABEL(labelInfo), 0.);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
  infoBar = gtk_info_bar_new();
  gtk_container_add(GTK_CONTAINER(gtk_info_bar_get_content_area(GTK_INFO_BAR(infoBar))),
		    labelInfo);
#else
  infoBar = gtk_hbox_new(FALSE, 5);
  infoImg = gtk_image_new_from_stock(GTK_STOCK_INFO, GTK_ICON_SIZE_MENU);
  gtk_box_pack_start(GTK_BOX(infoBar), infoImg, FALSE, FALSE, 0);
  gtk_box_pack_start(GTK_BOX(infoBar), labelInfo, TRUE, TRUE, 0);
#endif
  gtk_widget_set_no_show_all(infoBar, TRUE);
  gtk_box_pack_start(GTK_BOX(vbox), infoBar, FALSE, FALSE, 0);

  scrolledwindow1 = gtk_scrolled_window_new (NULL, NULL);
  gtk_box_pack_start(GTK_BOX(vbox), scrolledwindow1, TRUE, TRUE, 0);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwindow1),
				 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwindow1),
				      GTK_SHADOW_ETCHED_IN);

  fileTree = gtk_tree_view_new ();
  gtk_widget_set_tooltip_text(fileTree,
			_("Double click on a file to render it."));
  gtk_container_add (GTK_CONTAINER (scrolledwindow1), fileTree);
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(fileTree), showHeaders);

  renderer = gtk_cell_renderer_toggle_new ();
  g_signal_connect(G_OBJECT(renderer), "toggled",
		   G_CALLBACK(onCheckFile), (gpointer)0);
  column = gtk_tree_view_column_new_with_attributes ("",
						     renderer,
						     "active", COLUMN_BOOLEAN,
						     "sensitive", COLUMN_FILE_VALID,
						     "activatable", COLUMN_FILE_VALID,
						     NULL);
  gtk_tree_view_append_column (GTK_TREE_VIEW (fileTree), column);
  column = gtk_tree_view_column_new();
  gtk_tree_view_column_set_title(GTK_TREE_VIEW_COLUMN(column), _("File"));
  renderer = gtk_cell_renderer_text_new ();
  gtk_tree_view_column_pack_end(GTK_TREE_VIEW_COLUMN(column), renderer, TRUE);
  gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer,
                                      "markup", COLUMN_NAME_UTF8,
                                      "sensitive", COLUMN_FILE_VALID,
                                      NULL);
  renderer = gtk_cell_renderer_pixbuf_new();
  gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), renderer, FALSE);
  gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer,
                                      "icon-name", COLUMN_FILE_ERROR_ID,
                                      NULL);
  gtk_tree_view_column_set_sort_column_id(column, COLUMN_NAME_UTF8);
  gtk_tree_view_column_set_expand(column, TRUE);
  gtk_tree_view_append_column(GTK_TREE_VIEW (fileTree), column);
  if (showDate)
    {
      renderer = gtk_cell_renderer_text_new ();
      column = gtk_tree_view_column_new_with_attributes (_("Date"),
							 renderer,
							 "text", COLUMN_DATA,
							 "sensitive", COLUMN_FILE_VALID,
							 NULL);
      gtk_tree_view_column_set_sort_column_id(column, COLUMN_DATE);
      gtk_tree_view_append_column (GTK_TREE_VIEW (fileTree), column);
    }
  
  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(fileTree)),
			      GTK_SELECTION_SINGLE);

  vbox = gtk_vbox_new (FALSE, 0);
  gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 2);

  buttonUnselectAll = gtk_button_new();
  gtk_widget_set_tooltip_text(buttonUnselectAll, _("Unselect all files."));
  gtk_box_pack_start(GTK_BOX(vbox), buttonUnselectAll, FALSE, FALSE, 1);
  image2 = create_pixmap ((GtkWidget*)0, "stock-unselect-all_20.png");
  gtk_container_add(GTK_CONTAINER(buttonUnselectAll), image2);

  buttonSelectAll = gtk_button_new();
  gtk_widget_set_tooltip_text(buttonSelectAll, _("Select all files."));
  gtk_box_pack_start(GTK_BOX (vbox), buttonSelectAll, FALSE, FALSE, 1);
  image2 = create_pixmap ((GtkWidget*)0, "stock-select-all_20.png");
  gtk_container_add (GTK_CONTAINER (buttonSelectAll), image2);

  buttonDumpAll = gtk_button_new();
  gtk_box_pack_start(GTK_BOX(vbox), buttonDumpAll, TRUE, FALSE, 0);
  image4 = gtk_image_new_from_icon_name("document-save-as", GTK_ICON_SIZE_BUTTON);
  gtk_container_add(GTK_CONTAINER(buttonDumpAll), image4);
  gtk_widget_set_tooltip_text(buttonDumpAll,
		       _("Export the rendering of all selected files"
			 " in other formats."));

  buttonNext = gtk_button_new ();
  gtk_box_pack_end(GTK_BOX (vbox), buttonNext, FALSE, FALSE, 1);
  gtk_widget_set_tooltip_text(buttonNext,
		       _("Render the next selected file."));
  image1 = gtk_image_new_from_icon_name("go-down", GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (buttonNext), image1);

  buttonPrevious = gtk_button_new ();
  gtk_box_pack_end(GTK_BOX (vbox), buttonPrevious, FALSE, FALSE, 1);
  gtk_widget_set_tooltip_text(buttonPrevious,
		       _("Render the previous selected file."));
  image2 = gtk_image_new_from_icon_name("go-up", GTK_ICON_SIZE_BUTTON);
  gtk_container_add (GTK_CONTAINER (buttonPrevious), image2);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox1), hbox, FALSE, FALSE, 0);
  
  label = gtk_label_new(_("<span size=\"smaller\">Current dir.: </span>"));
  gtk_label_set_use_markup(GTK_LABEL(label), TRUE);
  gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
  
  labelDirectory = gtk_label_new("");
  gtk_label_set_use_markup(GTK_LABEL(labelDirectory), TRUE);
  gtk_label_set_xalign(GTK_LABEL(labelDirectory), 0.);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 5
  gtk_label_set_ellipsize(GTK_LABEL(labelDirectory), PANGO_ELLIPSIZE_START);
#endif
  gtk_box_pack_start(GTK_BOX(hbox), labelDirectory, TRUE, TRUE, 0);
  panelBrowserSet_labelCurrentDir();

  /***************/
  /* Action part */
  /***************/
  hbox2 = gtk_hbox_new (FALSE, 0);
  gtk_box_pack_start (GTK_BOX (vbox1), hbox2, FALSE, TRUE, 1);

  hbox5 = gtk_hbox_new(FALSE, 0);
  gtk_widget_set_margin_end(hbox5, 5);
  gtk_box_pack_start(GTK_BOX(hbox2), hbox5, FALSE, FALSE, 0);
  
  radioGoAround = gtk_radio_button_new(NULL);
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radioGoAround), (GSList*)0);
  radiobuttonCycle_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radioGoAround));
  gtk_box_pack_start(GTK_BOX(hbox5), radioGoAround, FALSE, FALSE, 0);
  image1 = create_pixmap((GtkWidget*)0, "stock-go-around.png");
  gtk_container_add(GTK_CONTAINER(radioGoAround), image1);

  radioGoOnce = gtk_radio_button_new(NULL);
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radioGoOnce), radiobuttonCycle_group);
  radiobuttonCycle_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radioGoOnce));
  gtk_box_pack_start(GTK_BOX(hbox5), radioGoOnce, FALSE, FALSE, 0);
  image1 = create_pixmap((GtkWidget*)0, "stock-go-once.png");
  gtk_container_add(GTK_CONTAINER(radioGoOnce), image1);

  radioGoAndBack = gtk_radio_button_new(NULL);
  gtk_radio_button_set_group(GTK_RADIO_BUTTON(radioGoAndBack), radiobuttonCycle_group);
  radiobuttonCycle_group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(radioGoAndBack));
  gtk_box_pack_start(GTK_BOX(hbox5), radioGoAndBack, FALSE, FALSE, 0);
  image1 = create_pixmap((GtkWidget*)0, "stock-go-and-back.png");
  gtk_container_add(GTK_CONTAINER(radioGoAndBack), image1);

  label4 = gtk_label_new (_("Play at "));
  gtk_box_pack_start (GTK_BOX (hbox2), label4, TRUE, TRUE, 0);
  gtk_label_set_xalign(GTK_LABEL(label4), 1.);

  spinDelay = gtk_spin_button_new_with_range(10, 10000, 25);
  gtk_spin_button_set_value(GTK_SPIN_BUTTON(spinDelay), 500);
  gtk_box_pack_start (GTK_BOX (hbox2), spinDelay, FALSE, TRUE, 0);
  gtk_spin_button_set_numeric (GTK_SPIN_BUTTON (spinDelay), TRUE);

  /* Units: milliseconds */
  label1 = gtk_label_new (_(" ms"));
  gtk_box_pack_start (GTK_BOX (hbox2), label1, TRUE, TRUE, 0);
  gtk_label_set_xalign(GTK_LABEL(label1), 0.);

  buttonPlayStop = gtk_toggle_button_new();
  gtk_widget_set_tooltip_text(buttonPlayStop, _("Cycle through the selected files at the given rate."));
  gtk_box_pack_start (GTK_BOX (hbox2), buttonPlayStop, FALSE, FALSE, 5);

  hbox5 = gtk_hbox_new (FALSE, 0);
  gtk_container_add (GTK_CONTAINER (buttonPlayStop), hbox5);

#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 5
  imagePlay = create_pixmap ((GtkWidget*)0, "stock_media-play.png");
  imageStop = create_pixmap ((GtkWidget*)0, "stock_media-stop.png");
#else
  imagePlay = gtk_image_new_from_icon_name("media-playback-start", GTK_ICON_SIZE_MENU);
  imageStop = gtk_image_new_from_icon_name("media-playback-stop", GTK_ICON_SIZE_MENU);
#endif
  gtk_widget_set_no_show_all(imageStop, TRUE);
  gtk_box_pack_start (GTK_BOX (hbox5), imagePlay, TRUE, TRUE, 0);
  gtk_box_pack_start (GTK_BOX (hbox5), imageStop, TRUE, TRUE, 0);

  g_signal_connect(G_OBJECT(fileTree), "row-activated",
		   G_CALLBACK(onTreeviewActivated), (gpointer)0);
  g_signal_connect(G_OBJECT(buttonPrevious), "clicked",
		   G_CALLBACK(navigateClicked), GINT_TO_POINTER(VISU_UI_PANEL_BROWSER_PREVIOUS));
  g_signal_connect(G_OBJECT(buttonNext), "clicked",
		   G_CALLBACK(navigateClicked), GINT_TO_POINTER(VISU_UI_PANEL_BROWSER_NEXT));
  g_signal_connect(G_OBJECT(buttonSelectAll), "clicked",
		   G_CALLBACK(checkFiles), GINT_TO_POINTER(TRUE));
  g_signal_connect(G_OBJECT(buttonUnselectAll), "clicked",
		   G_CALLBACK(checkFiles), GINT_TO_POINTER(FALSE));
  g_signal_connect(G_OBJECT(buttonDirectory), "clicked",
		   G_CALLBACK(onDirectoryClicked), (gpointer)entryFilterBrowser);
  g_signal_connect(G_OBJECT(buttonPlayStop), "toggled",
		   G_CALLBACK(onPlayStopClicked), (gpointer)0);
  g_signal_connect(G_OBJECT(spinDelay), "value-changed",
		   G_CALLBACK(onSpinDelayChangeValue), (gpointer)buttonPlayStop);
  g_signal_connect(G_OBJECT(buttonDumpAll), "clicked",
		   G_CALLBACK(onDumpButtonClicked), (gpointer)0);
  g_signal_connect(G_OBJECT(entryFilterBrowser), "changed",
		   G_CALLBACK(onFilterChanged), (gpointer)0);
  g_signal_connect_swapped(G_OBJECT(comboFilter), "changed",
                           G_CALLBACK(gtk_tree_model_filter_refilter),
                           treeStoreFilesFilter);

  gtk_widget_show_all(vbox1);

  return vbox1;
}

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

static void onFilterChanged(GtkEditable *entry, gpointer user_data _U_)
{
  GPatternSpec *pattern;
  gboolean valid;
  GtkTreeIter iter;
  gboolean match;
  gchar *fileUTF8;

/*   fprintf(stderr, "'%s'\n", gtk_entry_get_text(GTK_ENTRY(entry))); */

  pattern = g_pattern_spec_new(gtk_entry_get_text(GTK_ENTRY(entry)));

  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStoreFiles), &iter);
       valid;
       valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(treeStoreFiles), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &iter,
			 COLUMN_NAME_UTF8, &fileUTF8, -1);
      match = g_pattern_match_string(pattern, fileUTF8);
      gtk_tree_store_set(treeStoreFiles, &iter,
			 COLUMN_ACTIVE, match, -1);
      g_free(fileUTF8);
    }
  gtk_tree_model_filter_refilter(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter));

  g_pattern_spec_free(pattern);
}

static void onCheckFile(GtkCellRendererToggle *cell_renderer _U_, gchar *path,
			gpointer user_data _U_)
{
  GtkTreeIter iter, childIter;
  gboolean checked;
  GtkTreePath *currentPath;

  currentPath = gtk_tree_path_new_from_string(path);
  gtk_tree_model_get_iter(GTK_TREE_MODEL(treeStoreFilesFilter), &iter, currentPath);
  gtk_tree_path_free(currentPath);

  gtk_tree_model_filter_convert_iter_to_child_iter
    (GTK_TREE_MODEL_FILTER(treeStoreFilesFilter), &childIter, &iter);
  
  gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &childIter,
		     COLUMN_BOOLEAN, &checked, -1);
  gtk_tree_store_set(treeStoreFiles, &childIter,
		     COLUMN_BOOLEAN, !checked, -1);

}

static void onTreeviewActivated(GtkTreeView *treeview _U_, GtkTreePath *path,
				GtkTreeViewColumn *col _U_, gpointer user_data _U_)
{
  GtkTreeIter iter, childIter, parentIter;
  gboolean checked;
  gchar* filename, *utf8;
  int nSet;

  DBG_fprintf(stderr, "Panel Browser: double click detected.\n");
  gtk_tree_model_get_iter(GTK_TREE_MODEL(treeStoreFilesFilter), &iter, path);
  gtk_tree_model_filter_convert_iter_to_child_iter
    (GTK_TREE_MODEL_FILTER(treeStoreFilesFilter), &childIter, &iter);
  gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &childIter,
		     COLUMN_NAME, &filename,
		     COLUMN_NAME_UTF8, &utf8,
		     COLUMN_BOOLEAN, &checked, -1);
  if (!checked)
    gtk_tree_store_set(GTK_TREE_STORE(treeStoreFiles), &childIter,
		       COLUMN_BOOLEAN, TRUE, -1);
  if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(treeStoreFiles),
				 &parentIter, &childIter))
    gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &childIter,
		       COLUMN_DATE, &nSet, -1);
  else
    nSet = 0;

  DBG_fprintf(stderr, "Panel Browser: double click asks for loading (%s).\n",
	      filename);
  browserLoad(filename, utf8, &childIter, nSet);
  g_free(filename);
  g_free(utf8);
}
static void navigateClicked(GtkButton *button _U_, gpointer data)
{
  GtkTreeIter iter;
  GtkTreePath *path;
  gboolean res;

  res = visu_ui_panel_browser_getNextSelected(&path, &iter, GPOINTER_TO_INT(data));
  if (!res)
    return;

  res = navigateInFiles(path, &iter);
  gtk_tree_path_free(path);
}
static void onNextPrevFile(VisuUiRenderingWindow *window _U_, gpointer data)
{
  GtkTreeIter iter;
  GtkTreePath *path;
  gboolean res;

  res = visu_ui_panel_browser_getNextSelected(&path, &iter, GPOINTER_TO_INT(data));
  if (!res)
    return;

  res = navigateInFiles(path, &iter);
  gtk_tree_path_free(path);
}
static void checkFiles(GtkButton *button _U_, gpointer data)
{
  gtk_tree_model_foreach(GTK_TREE_MODEL(treeStoreFilesFilter), checkFile, data);
}
static void onRefreshDir(GtkButton *button _U_, gpointer data _U_)
{
  browseDirectory((gchar*)0);
}
gboolean playSelectedFiles(gpointer data _U_)
{
  GtkTreeIter iter;
  gboolean res, load;
  GtkTreePath *path;

  g_return_val_if_fail(startBrowsePath, FALSE);

  /* We try to get the next iter. */
  res = visu_ui_panel_browser_getNextSelected(&path, &iter, currentBrowseDirection);
  if (!res)
    return FALSE;

  /* We follow the cycle policy to select the file. */
  load = TRUE;
  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioGoAndBack)))
    {
      if (!gtk_tree_path_compare(path, startBrowsePath))
	{
	  DBG_fprintf(stderr, "Panel browser: One round is done, applyng policy.\n");
	  if (currentBrowseDirection == VISU_UI_PANEL_BROWSER_PREVIOUS)
	    currentBrowseDirection = VISU_UI_PANEL_BROWSER_NEXT;
	  else
	    {
	      currentBrowseDirection = VISU_UI_PANEL_BROWSER_PREVIOUS;
	      gtk_tree_path_free(path);  
	      res = visu_ui_panel_browser_getNextSelected(&path, &iter,
						 currentBrowseDirection);
	      g_return_val_if_fail(res, FALSE);
	    }
	}
    }
  else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radioGoOnce)))
    {
      if (!gtk_tree_path_compare(path, startBrowsePath))
	{
	  DBG_fprintf(stderr, "Panel browser: One round is done, applyng policy.\n");
	  res = FALSE;
	  load = FALSE;
	}
    }

  if (load)
    {
      /* We advance the select one. */
      selectFile(path, &iter);
      /* We render it. */
      res = navigateInFiles(path, &iter);
    }

  gtk_tree_path_free(path);  

  return res;
}

static void onPlayStopClicked(GtkToggleButton *button, gpointer data _U_)
{
  GtkTreeIter startIter;
  gboolean res, checked, hasChild;
  gulong *playCallbackId;

  DBG_fprintf(stderr, "Panel Browser: toggle on play button.\n");
/*   if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(panelBrowserListFilter), &startIter)) */
/*     return; */

  if (gtk_toggle_button_get_active(button))
    {
      /* Button has been pushed. */
      checked = FALSE;
      res = visu_ui_panel_browser_getCurrentSelected(&startBrowsePath, &startIter);
      if (res)
        {
          gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFilesFilter), &startIter,
                             COLUMN_BOOLEAN, &checked, -1);
          hasChild = gtk_tree_model_iter_has_child(GTK_TREE_MODEL(treeStoreFilesFilter),
                                                   &startIter);
        }
      if (!res || !checked || hasChild)
	{
	  res = visu_ui_panel_browser_getNextSelected(&startBrowsePath, &startIter,
					     VISU_UI_PANEL_BROWSER_NEXT);
	  if (!res)
	    {
	      startBrowsePath = gtk_tree_path_new();
	      /* No file selected. */
	      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonPlayStop), FALSE);
	      return;
	    }
	}
      navigateInFiles(startBrowsePath, &startIter);

      /* Launch play */
      gtk_widget_hide(imagePlay);
      gtk_widget_show(imageStop);

      currentBrowseDirection = VISU_UI_PANEL_BROWSER_NEXT;
      playCallbackId = g_malloc(sizeof(gulong));
      *playCallbackId =
	g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE,
			   (gint)gtk_spin_button_get_value(GTK_SPIN_BUTTON(spinDelay)),
			   playSelectedFiles, (gpointer)0,
			   stopPlayStop);
      g_object_set_data(G_OBJECT(button), "playCallbackId",
			(gpointer)playCallbackId);
      DBG_fprintf(stderr, " | start play\n");
    }
  else
    {
      /* Stop play */
      playCallbackId = (gulong*)g_object_get_data(G_OBJECT(button), "playCallbackId");
      if (playCallbackId)
	g_source_remove(*playCallbackId);
      DBG_fprintf(stderr, " | stop play\n");
    }
}
void stopPlayStop(gpointer data _U_)
{
  gtk_widget_hide(imageStop);
  gtk_widget_show(imagePlay);
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonPlayStop), FALSE);
  g_return_if_fail(startBrowsePath);
  gtk_tree_path_free(startBrowsePath);
}
static void onSpinDelayChangeValue(GtkSpinButton *spinbutton _U_, gpointer user_data)
{
  GtkTreePath *startPath;
  GtkToggleButton *button;
  gulong *playCallbackId;

  g_return_if_fail(GTK_IS_TOGGLE_BUTTON(user_data));
  button = GTK_TOGGLE_BUTTON(user_data);

  if (gtk_toggle_button_get_active(button))
    {
      playCallbackId = (gulong*)g_object_get_data(G_OBJECT(button), "playCallbackId");
      g_return_if_fail(playCallbackId);

      startPath = gtk_tree_path_copy(startBrowsePath);
      /* Stop playing at this rate */
      g_source_remove(*playCallbackId);
      startBrowsePath = startPath;
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(buttonPlayStop), TRUE);
    }
}
void onDumpButtonClicked(GtkButton *button _U_, gpointer user_data _U_)
{
  GtkWidget *dump;
  GtkProgressBar *progressBarDump;
  char *filename;
  VisuDump *format;
  GError *error;
  GString *buffer;
  GtkButton *abort;
  char *chr, *chr2;
  int goodPattern, flagAbort, i;
  gint response;
  GString *fileNumbered;
  GtkTreeIter iter;
  gboolean valid, errors;
  GtkTreePath *currentPath, *startPath;
  VisuGlView *view;

  view = visu_ui_panel_getView(VISU_UI_PANEL(panelBrowser));
  dump = visu_ui_dump_dialog_new
    (visu_ui_panel_getData(VISU_UI_PANEL(panelBrowser)),
     visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelBrowser)),
     _("foo%02d.png"), view->window.width, view->window.height);
  
  do
    {
      response = gtk_dialog_run(GTK_DIALOG(dump));

      if (response != GTK_RESPONSE_ACCEPT)
	{
	  gtk_widget_destroy(dump);
	  return;
	}

      filename = visu_ui_dump_dialog_getFilename(VISU_UI_DUMP_DIALOG(dump));
      format = visu_ui_dump_dialog_getType(VISU_UI_DUMP_DIALOG(dump));
      g_return_if_fail(filename && format);

      DBG_fprintf(stderr, "Panel browser: dump all returns this filename"
		  " pattern '%s' (format : %s)\n", filename,
                  tool_file_format_getName(TOOL_FILE_FORMAT(format)));

      buffer = g_string_new(_("Dumping all selected files to images,"));
      g_string_append_printf(buffer, _(" format '%s'.\n\n"),
                             tool_file_format_getName(TOOL_FILE_FORMAT(format)));
      /* Verify the name is regular */
      if (g_pattern_match_simple("*%0?d*", filename))
	{
	  chr = strchr(filename, '%');
	  if ((int)*(chr + 2) <= '0' || (int)*(chr + 2) > '9')
	    {
	      goodPattern = 0;
	      g_string_append_printf(buffer, _("Error! The numbering pattern is"
					       " wrong.\n"));
	    }
	  else
	    {
	      chr2 = strchr(chr + 1, '%');
	      if (chr2)
		{
		  goodPattern = 0;
		  g_string_append_printf(buffer,
					 _("Error! Only one '%s' character"
					   " is allowed in the file name.\n"), "%");
		}
	      else
		goodPattern = 1;
	    }
	}
      else
	{
	  goodPattern = 0;
	  g_string_append_printf(buffer, _("Error! Missing pattern in the filename.\n"));
	}
      if (!goodPattern)
	{
	  g_string_append_printf(buffer,
				 _("\nHelp : you must specify '%s' in"
				   " the filename, where 'x' is a number [|1;9|]."
				   " This allows V_Sim to number the dumped"
				   " files.\n\n For example, with a pattern like this"
				   " : 'foo%s.pdf', dumped files will be named"
				   " : foo00.pdf, foo01.pdf..."),
				 "%0xd", "%02d");
	  visu_ui_raiseWarning(_("Exporting files"), buffer->str, (GtkWindow*)0);
	  g_string_free(buffer, TRUE);
	}
    }
  while (!goodPattern);

  error = (GError*)0;
  abort = visu_ui_dump_dialog_getCancelButton(VISU_UI_DUMP_DIALOG(dump));
  progressBarDump = visu_ui_dump_dialog_getProgressBar(VISU_UI_DUMP_DIALOG(dump));
  visu_ui_dump_dialog_start(VISU_UI_DUMP_DIALOG(dump));

  g_signal_connect (G_OBJECT(abort), "clicked",
		    G_CALLBACK(visu_dump_abort), (gpointer)&flagAbort);
  g_signal_connect (G_OBJECT(abort), "clicked",
		    G_CALLBACK(abortDumpAll), (gpointer)progressBarDump);

  gtk_progress_bar_set_fraction(progressBarDump, 0.);
  fileNumbered = g_string_new("");
  i = 0;
  flagAbort = 0;

  /* We write it only once to avoid blinking effects. */
  gtk_progress_bar_set_text(progressBarDump,
			    _("Waiting for generating image in memory..."));
  visu_ui_wait();

  gtk_tree_selection_unselect_all(gtk_tree_view_get_selection(GTK_TREE_VIEW(fileTree)));

  errors = FALSE;
  valid = visu_ui_panel_browser_getNextSelected(&startPath, &iter, VISU_UI_PANEL_BROWSER_NEXT);
  if (valid)
    valid = navigateInFiles(startPath, &iter);
  while (valid && !errors && !flagAbort)
    {
      g_string_append_printf(buffer, _("Write to file %d ..."), i);
      g_string_printf(fileNumbered, filename, i);
      DBG_fprintf(stderr, "Panel browser: write '%s'\n", fileNumbered->str);

      errors = !visu_gl_node_scene_dump(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()),
                                        format, fileNumbered->str,
                                        visu_ui_dump_dialog_getWidth(VISU_UI_DUMP_DIALOG(dump)),
                                        visu_ui_dump_dialog_getHeight(VISU_UI_DUMP_DIALOG(dump)),
                                        updateDumpAllProgressBar, (gpointer)progressBarDump,
                                        &error);
      if (errors)
        g_string_append_printf(buffer, _(" error\n"));
      else
        g_string_append_printf(buffer, _(" OK\n"));
      i += 1;

      valid = visu_ui_panel_browser_getNextSelected(&currentPath, &iter, VISU_UI_PANEL_BROWSER_NEXT);
      if (!gtk_tree_path_compare(currentPath, startPath))
        valid = FALSE;
      if (valid)
        valid = navigateInFiles(currentPath, &iter);
      gtk_tree_path_free(currentPath);
    }
  gtk_tree_path_free(startPath);
  if (error)
    {
      visu_ui_raiseWarning(_("Exporting files"), error->message, (GtkWindow*)0);
      g_error_free(error);
    }
  g_string_free(fileNumbered, TRUE);

  gtk_widget_destroy(dump);
}
void updateDumpAllProgressBar(gpointer data)
{
  gdouble val;
  gdouble percentage;
  gdouble nEle;

  g_return_if_fail(GTK_PROGRESS_BAR(data));

  nEle = (gdouble)gtk_tree_model_iter_n_children(GTK_TREE_MODEL(treeStoreFilesFilter), NULL);
  val = gtk_progress_bar_get_fraction(GTK_PROGRESS_BAR(data));
/*   if (((int)(val * nEle)) % 100 == 1) */
    gtk_progress_bar_set_text(GTK_PROGRESS_BAR(data), "");

  percentage = val + 0.01 / nEle;
  if (percentage > 1.0)
    percentage = 1.0;
  if (percentage < 0.)
    percentage = 0.;
  gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(data), percentage);
  visu_ui_wait();
}
void abortDumpAll(GtkButton *button _U_, gpointer data)
{
  gtk_progress_bar_set_text(GTK_PROGRESS_BAR(data),
			    _("Abortion request, please wait..."));
}





/******************/
/* Public methods */
/******************/

gboolean visu_ui_panel_browser_getCurrentSelected(GtkTreePath **path, GtkTreeIter *iterSelected)
{
  GtkTreeSelection *selection;
  gboolean res;
  GtkTreeModel *model;

  g_return_val_if_fail(path && iterSelected, FALSE);

  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStoreFilesFilter), iterSelected))
    return FALSE;

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fileTree));
  res = gtk_tree_selection_get_selected(selection, &model, iterSelected);

  if (res)
    *path = gtk_tree_model_get_path(model, iterSelected);
  return res;
}

gboolean visu_ui_panel_browser_getNextSelected(GtkTreePath **path, GtkTreeIter *iterSelected,
				      int direction)
{
  GtkTreeSelection *selection;
  GtkTreePath *currentPath, *firstPath;
  GtkTreeIter iter, child, parent;
  gboolean res, checked;
  GtkTreeModel *model;
  int loopComplete;

  g_return_val_if_fail(path && iterSelected &&
		       (direction == VISU_UI_PANEL_BROWSER_NEXT ||
			direction == VISU_UI_PANEL_BROWSER_PREVIOUS), FALSE);

  *path = (GtkTreePath*)0;

  if (!gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStoreFilesFilter), &iter))
    return FALSE;

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fileTree));
  res = gtk_tree_selection_get_selected(selection, &model, &iter);

  /* Nothing is selected. */
  if (!res)
    {
      if (direction == VISU_UI_PANEL_BROWSER_NEXT)
	res = gtk_tree_model_get_iter_last(GTK_TREE_MODEL(treeStoreFilesFilter),
					   &iter, (GtkTreePath**)0);
      else if (GPOINTER_TO_INT(direction) == VISU_UI_PANEL_BROWSER_PREVIOUS)
	res = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStoreFilesFilter),
					    &iter);
    }
  g_return_val_if_fail(res, FALSE);

  currentPath = gtk_tree_model_get_path(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
  firstPath = gtk_tree_path_copy(currentPath);
  do
    {
      if (direction == VISU_UI_PANEL_BROWSER_NEXT)
	{
	  /* If the current iter has child, we select the first. */
	  if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(treeStoreFilesFilter),
					    &iter))
	    {
	      gtk_tree_model_iter_children(GTK_TREE_MODEL(treeStoreFilesFilter),
					   &child, &iter);
	      iter = child;
	      gtk_tree_path_free(currentPath);
	      currentPath =
		gtk_tree_model_get_path(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
	    }
	  /* If the current iter has no child, we go next. */
	  else
	    {
	      /* Avance d'un pas dans la liste */
	      gtk_tree_path_next(currentPath);
	      child = iter;
	      res = gtk_tree_model_get_iter(GTK_TREE_MODEL(treeStoreFilesFilter),
					    &iter, currentPath);
	      /* In case, there's no next iter. */
	      if (!res)
		{
		  /* If the iter has no parent we go top. */
		  if (!gtk_tree_model_iter_parent
		      (GTK_TREE_MODEL(treeStoreFilesFilter), &parent, &child))
		    {
		      gtk_tree_model_get_iter_first
			(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
		      gtk_tree_path_free(currentPath);
		      currentPath =
			gtk_tree_model_get_path
			(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
		    }
		  /* If the iter has parent, we go next parent. */
		  else
		    {
		      iter = parent;
		      gtk_tree_path_free(currentPath);
		      currentPath = gtk_tree_model_get_path
			(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
		      gtk_tree_path_next(currentPath);
		      res = gtk_tree_model_get_iter
			(GTK_TREE_MODEL(treeStoreFilesFilter), &iter, currentPath);
		      if (!res)
			{
			  gtk_tree_model_get_iter_first
			    (GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
			  gtk_tree_path_free(currentPath);
			  currentPath = gtk_tree_model_get_path
			    (GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
			}
		    }
		}
	    }
	}
      else if (direction == VISU_UI_PANEL_BROWSER_PREVIOUS)
	{
	  /* Recule d'un pas dans la liste */
	  res = gtk_tree_path_prev(currentPath);
	  /* Recup�re l'it�ration recul�e d'un pas */
	  child = iter;
	  res = res && gtk_tree_model_get_iter(GTK_TREE_MODEL(treeStoreFilesFilter),
					       &iter, currentPath);
	  /* If this prev iter does not exist. */
	  if (!res)
	    {
	      /* In the case the current iter has a parent, we select it. */
	      if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(treeStoreFilesFilter),
					     &parent, &child))
		{
		  iter = parent;
		  gtk_tree_path_free(currentPath);
		  currentPath = gtk_tree_model_get_path
		    (GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
		}
	      /* Si ce path n'existe pas ou 
		 si cette iteration n'existe pas on va � la fin. */
	      else
		{
		  gtk_tree_path_free(currentPath);
		  res = gtk_tree_model_get_iter_last
		    (GTK_TREE_MODEL(treeStoreFilesFilter), &iter, &currentPath);
		  if (!res)
		    {
		      g_warning("Panel browser: impossible to find"
				" the end of the list.\n");
		      gtk_tree_path_free(currentPath);
		      gtk_tree_path_free(firstPath);
		      return FALSE;
		    }
		}
	    }
	  /* If the previous iter has children, we select last. */
	  else if (gtk_tree_model_iter_has_child(GTK_TREE_MODEL(treeStoreFilesFilter),
						 &iter))
	    {
	      parent = iter;
	      res = gtk_tree_model_iter_nth_child
		(GTK_TREE_MODEL(treeStoreFilesFilter), &iter, &parent,
		 gtk_tree_model_iter_n_children(GTK_TREE_MODEL(treeStoreFilesFilter),
						&parent) - 1);
	      if (!res)
		{
		  g_warning("Panel browser: impossible to find"
			    " the last child of this iter.\n");
		  gtk_tree_path_free(currentPath);
		  gtk_tree_path_free(firstPath);
		  return FALSE;
		}
	      gtk_tree_path_free(currentPath);
	      currentPath = gtk_tree_model_get_path
		(GTK_TREE_MODEL(treeStoreFilesFilter), &iter);
	    }
	}

      checked = FALSE;
      gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFilesFilter), &iter,
			 COLUMN_BOOLEAN, &checked, -1);
      checked = checked && !gtk_tree_model_iter_has_child(GTK_TREE_MODEL(treeStoreFilesFilter),
                                                          &iter);
/*       fprintf(stderr, "%d '%s'\n", nbSteps, filename); */
      loopComplete = !gtk_tree_path_compare(firstPath, currentPath);
    }
  while ( !checked && !loopComplete);
  gtk_tree_path_free(firstPath);
  if (!loopComplete)
    {
      *path = currentPath;
      *iterSelected = iter;
      return TRUE;
    }
  else
    {
      gtk_tree_path_free(currentPath);
      return FALSE;
    }
}

static void selectFile(GtkTreePath *path, GtkTreeIter *iter)
{
  GtkTreeSelection *selection;

  g_return_if_fail(path && iter);

  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(fileTree));
  gtk_tree_selection_select_iter(selection, iter);
  gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(fileTree), path, NULL, FALSE, 0., 0.);
}
static gboolean navigateInFiles(GtkTreePath *path, GtkTreeIter *iterSelected)
{
  gboolean res;
  gchar *filename, *utf8;
  int nSet;
  GtkTreeIter parentIter, iter;

  g_return_val_if_fail(path && iterSelected, FALSE);

  /* We select the given iter. */
  selectFile(path, iterSelected);

  /* Load the new selected file */
  gtk_tree_model_filter_convert_iter_to_child_iter
    (GTK_TREE_MODEL_FILTER(treeStoreFilesFilter), &iter, iterSelected);
  if (gtk_tree_model_iter_parent(GTK_TREE_MODEL(treeStoreFiles),
				 &parentIter, &iter))
    gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &iter,
		       COLUMN_DATE, &nSet, -1);
  else
    nSet = 0;
  gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &iter,
		     COLUMN_NAME, &filename,
		     COLUMN_NAME_UTF8, &utf8, -1);
  res = browserLoad(filename, utf8, &iter, nSet);
  g_free(filename);
  g_free(utf8);
  return res;
}

static VisuDataLoadable* setFiles(const gchar *filename, const gchar *utf8,
                                  VisuData *oldData)
{
  gchar *pt, **tokens, *name, *name2;
  VisuDataAtomic *atomic;
  VisuDataSpin *spin;

  switch (gtk_combo_box_get_active(GTK_COMBO_BOX(comboFilter)))
    {
    case 0:
      atomic = visu_data_atomic_new(filename, (VisuDataLoader*)0);
      return VISU_DATA_LOADABLE(atomic);
    case 1:
      /* The case where the name is used for all files. */
      pt = strrchr(utf8, ']');
      *pt = '\0';
      pt = strrchr(utf8, '[');
      tokens = g_strsplit(pt + 1, ",", 2);
      name = g_strdup_printf("%s.%s", filename, tokens[0]);
      name2 = g_strdup_printf("%s.%s", filename, tokens[1]);
      g_strfreev(tokens);
      spin = visu_data_spin_new(name, name2, (VisuDataLoader*)0, (VisuDataLoader*)0);
      g_free(name);
      g_free(name2);
      return VISU_DATA_LOADABLE(spin);
    case 2:
      g_return_val_if_fail(VISU_IS_DATA_LOADABLE(oldData), (VisuDataLoadable*)0);
      spin = visu_data_spin_new(filename, visu_data_loadable_getFilename(VISU_DATA_LOADABLE(oldData), 1), (VisuDataLoader*)0, (VisuDataLoader*)0);
      return VISU_DATA_LOADABLE(spin);
    case 3:
      g_return_val_if_fail(VISU_IS_DATA_LOADABLE(oldData), (VisuDataLoadable*)0);
      spin = visu_data_spin_new(visu_data_loadable_getFilename(VISU_DATA_LOADABLE(oldData), 0), filename, (VisuDataLoader*)0, (VisuDataLoader*)0);
      return VISU_DATA_LOADABLE(spin);
    default:
      return (VisuDataLoadable*)0;
    }
}

static gboolean browserLoad(gchar *filename, gchar *utf8, GtkTreeIter *iter, int nSet)
{
  GError *error;
  VisuDataLoadable *data;
  VisuData *prevData;
  gboolean valid;
  GtkTreeIter parentIter, childIter;
  int iSet, nSets;
  GtkTreePath *path;
  const gchar *comment;
  gchar *buf, *commentFormat;

  g_return_val_if_fail(iter, FALSE);

  prevData = visu_ui_panel_getData(VISU_UI_PANEL(panelBrowser));
  data = setFiles(filename, utf8, prevData);
  if (!data)
    {
      visu_ui_raiseWarning(_("Loading a file"),
			   _("Can't load this file through the browser because it"
			     " requires to read several files. You should use the 'Open'"
			     " button on the main panel and then use the browser to vary"
			     " one kind of file at a time."), (GtkWindow*)0);
      g_object_unref(data);

      gtk_tree_store_set(treeStoreFiles, iter,
			 COLUMN_FILE_VALID, FALSE,
			 COLUMN_BOOLEAN, FALSE,
			 -1);
      return FALSE;
    }

  gtk_widget_set_sensitive(buttonPrevious, FALSE);
  gtk_widget_set_sensitive(buttonNext, FALSE);
  error = (GError*)0;
  if (!visu_gl_node_scene_loadData(visu_ui_rendering_window_getGlScene(visu_ui_main_class_getDefaultRendering()), data, nSet, (GCancellable*)0, &error))
    {
      if (error)
        {
          visu_ui_raiseWarning(_("Loading a file"), error->message, (GtkWindow*)0);
          g_error_free(error);
        }
      g_object_unref(data);
      data = (VisuDataLoadable*)0;
    }
  gtk_widget_set_sensitive(buttonPrevious, TRUE);
  gtk_widget_set_sensitive(buttonNext, TRUE);

  if (!data)
    gtk_tree_store_set(treeStoreFiles, iter,
		       COLUMN_FILE_ERROR_ID, "dialog-error",
		       COLUMN_FILE_VALID, FALSE,
		       COLUMN_BOOLEAN, FALSE,
		       -1);
  else
    gtk_tree_store_set(treeStoreFiles, iter,
		       COLUMN_FILE_ERROR_ID, (gchar*)0,
		       COLUMN_FILE_VALID, TRUE,
		       -1);
  
  if (data)
    {
      /* If the file has more than one node set, we create the child
	 entries in the tree view. */
      nSets = visu_data_loadable_getNSets(data);
      if (nSets > 1)
	{
	  /* We get the parent iter. */
	  if (!gtk_tree_model_iter_parent(GTK_TREE_MODEL(treeStoreFiles),
					  &parentIter, iter))
	    {
	      /* We clear its children. */
	      valid = gtk_tree_model_iter_children
		(GTK_TREE_MODEL(treeStoreFiles), &childIter, iter);
	      while (valid)
		{
		  gtk_tree_store_remove(treeStoreFiles, &childIter);
		  valid = gtk_tree_model_iter_children
		    (GTK_TREE_MODEL(treeStoreFiles), &childIter, iter);
		}
	      /* We create the new ones. */
	      commentFormat = g_strdup_printf(_("data set %s0%dd"), "%",
					      (int)log10(nSets) + 1);
	      for (iSet = 0; iSet < nSets; iSet++)
		{
		  comment = visu_data_loadable_getSetLabel(data, iSet);
		  if (!comment || !comment[0])
		    {
		      buf = g_strdup_printf(commentFormat, iSet + 1);
		      comment = buf;
		    }
		  else
		    buf = (gchar*)0;
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 9
		  gtk_tree_store_insert_with_values(treeStoreFiles,
						    &childIter, iter, iSet,
						    COLUMN_BOOLEAN, TRUE,
						    COLUMN_NAME, filename,
						    COLUMN_NAME_UTF8, comment,
						    COLUMN_ACTIVE, TRUE,
						    COLUMN_DATE, iSet,
						    COLUMN_FILE_VALID, TRUE,
						    -1);
#else							
		  gtk_tree_store_insert(treeStoreFiles, &childIter,
					iter, iSet);
		  gtk_tree_store_set(treeStoreFiles, &childIter,
				     COLUMN_BOOLEAN, TRUE,
				     COLUMN_NAME, filename,
				     COLUMN_NAME_UTF8, comment,
				     COLUMN_ACTIVE, TRUE,
				     COLUMN_DATE, iSet,
				     COLUMN_FILE_VALID, TRUE,
				     -1);
#endif
		  if (buf)
		    g_free(buf);
		}
	      g_free(commentFormat);
	      /* We expand the row. */
	      gtk_tree_model_filter_convert_child_iter_to_iter
		(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter),
		 &parentIter, iter);
	      path = gtk_tree_model_get_path(GTK_TREE_MODEL(treeStoreFilesFilter),
					     &parentIter);
	      gtk_tree_view_expand_row(GTK_TREE_VIEW(fileTree), path, TRUE);
              /* We select the first. */
              /* selectFile(path, iterSelected); */
	      gtk_tree_path_free(path);
	    }
	}
      g_object_unref(data);
    }

  if (data)
    return TRUE;
  else
    return FALSE;
}

static gboolean checkFile(GtkTreeModel *model, GtkTreePath *path _U_,
			  GtkTreeIter *iter, gpointer data)
{
  gboolean filterOk, valid;
  GtkTreeIter iterList;
  GtkTreeStore *tree;

  /* If we check the row, the checkbutton is on only
     if the name is in accordance with the filter. */
  gtk_tree_model_filter_convert_iter_to_child_iter(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter),
						   &iterList, iter);
  tree = GTK_TREE_STORE(gtk_tree_model_filter_get_model(GTK_TREE_MODEL_FILTER(treeStoreFilesFilter)));
  if (GPOINTER_TO_INT(data))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(model), iter,
			 COLUMN_ACTIVE, &filterOk, COLUMN_FILE_VALID, &valid, -1);
      
      if (filterOk && valid)
	gtk_tree_store_set(tree, &iterList, COLUMN_BOOLEAN,
			   TRUE, -1);
/*       else */
/* 	gtk_tree_store_set(tree, &iterList, COLUMN_BOOLEAN, */
/* 			   FALSE, -1); */
    }
  else
    {
      gtk_tree_store_set(tree, &iterList, COLUMN_BOOLEAN,
			 FALSE, -1);
    }
  return FALSE;
}

void visu_ui_panel_browser_setCurrentDirectory(const gchar *dir)
{
  g_return_if_fail(dir && dir[0]);

  if (currentBrowseredDirectory)
    DBG_fprintf(stderr, "Panel Browser: compare dirs '%s' and '%s'.\n",
		currentBrowseredDirectory[0], dir);

  /* Test if the new directory is not already the current one. */
  if (currentBrowseredDirectory && currentBrowseredDirectory[0] &&
      !strcmp(currentBrowseredDirectory[0], dir) && !currentBrowseredDirectory[1])
    return;

  /* Set the new directory current. */
  currentBrowseredDirectory = g_malloc(sizeof(gchar*) * 2);
  currentBrowseredDirectory[0] = g_strdup(dir);
  currentBrowseredDirectory[1] = (gchar*)0;
  DBG_fprintf(stderr, "Panel Browser: set currentBrowseredDirectory to '%s'.\n",
	      currentBrowseredDirectory[0]);
  if (commonBrowseredDirectory)
    g_free(commonBrowseredDirectory);
  commonBrowseredDirectory = tool_path_normalize(dir);
  DBG_fprintf(stderr, "Panel Browser: set common path to '%s'.\n",
	      commonBrowseredDirectory);

  /* Save the new list of current directories. */
  updateHistory();

  /* Refresh the list if visible, else let the page-enter
     signal do it. */
  DBG_fprintf(stderr, "Panel Browser: ask for refresh.\n");
  if (visu_ui_panel_getVisible(VISU_UI_PANEL(panelBrowser)))
    browseDirectory();
  else
    dirDirty = TRUE;

  visu_ui_main_setLastOpenDirectory(visu_ui_main_class_getCurrentPanel(),
                                    commonBrowseredDirectory, VISU_UI_DIR_BROWSER);
}
static void setCurrentDirectories(gchar **dirs)
{
  int i, j;
  gchar *tmp;

  /* Set the new directory current. */
  currentBrowseredDirectory = dirs;

  DBG_fprintf(stderr, "Panel Browser: set currentBrowseredDirectory to the list:\n");
  /* Try to find a common path for all directories. */
  if (commonBrowseredDirectory)
    g_free(commonBrowseredDirectory);
  commonBrowseredDirectory = g_strdup(dirs[0]);
  for (i = 0; dirs[i]; i++)
    {
      DBG_fprintf(stderr, " | '%s'\n", dirs[i]);
      for (j = 0; dirs[i][j] && commonBrowseredDirectory[j]; j++)
	if (dirs[i][j] != commonBrowseredDirectory[j])
	  commonBrowseredDirectory[j] = '\0';
    }
  tmp = commonBrowseredDirectory;
  commonBrowseredDirectory = tool_path_normalize(tmp);
  g_free(tmp);
  DBG_fprintf(stderr, "Panel Browser: set common path to '%s'.\n",
	      commonBrowseredDirectory);

  /* Refresh the list if visible, else let the page-enter
     signal do it. */
  if (visu_ui_panel_getVisible(VISU_UI_PANEL(panelBrowser)))
    browseDirectory();
  else
    dirDirty = TRUE;

  visu_ui_main_setLastOpenDirectory(visu_ui_main_class_getCurrentPanel(),
                                    commonBrowseredDirectory, VISU_UI_DIR_BROWSER);
}
static void updateHistory()
{
  GList *tmpLst, *del;

  g_return_if_fail(currentBrowseredDirectory);

  DBG_fprintf(stderr, "Panel Browser: update the history.\n");

  /* We kill the history between historyBrowseredDirectory and
     currentHistory. */
  tmpLst = historyBrowseredDirectory;
  while (tmpLst != currentHistory)
    {
      DBG_fprintf(stderr, "Panel Browser: removing a group of"
		  " directories from history.\n");
      g_strfreev((gchar**)tmpLst->data);
      del = tmpLst;
      tmpLst = g_list_next(tmpLst);
      g_list_free_1(del);
    }
  if (currentHistory)
    currentHistory->prev = (GList*)0;
  historyBrowseredDirectory = currentHistory;
  DBG_fprintf(stderr, "Panel Browser: adding a group of"
	      " directories to history.\n");
  historyBrowseredDirectory = g_list_prepend(historyBrowseredDirectory,
					     currentBrowseredDirectory);
  currentHistory = historyBrowseredDirectory;
  DBG_fprintf(stderr, "Panel Browser: set current history to %p (%d).\n",
	      (gpointer)currentHistory, g_list_length(currentHistory));

  if (buttonDirPrev) gtk_widget_set_sensitive(buttonDirPrev, TRUE);
  if (buttonDirNext) gtk_widget_set_sensitive(buttonDirNext, FALSE);

  DBG_fprintf(stderr, "Panel Browser: history updated OK.\n");

  updateDirectionalTooltips();
}
void visu_ui_panel_browser_setCurrentDirectories(gchar **dirs)
{
  g_return_if_fail(dirs && dirs[0]);

  /* Change the current directories. */
  setCurrentDirectories(dirs);

  /* Save the new list of current directories. */
  updateHistory();
  DBG_fprintf(stderr, "Panel Browser: directories updated OK.\n");
}
static void updateDirectionalTooltips()
{
  GString *lbl;
  int i;
  gchar **history;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 12
  GtkTooltips *tooltips;

  tooltips = gtk_tooltips_new ();
#endif

  DBG_fprintf(stderr, "Panel Browser: update directional tooltips.\n");

  if (currentHistory && currentHistory->prev && buttonDirNext)
    {
      history = (gchar**)(currentHistory->prev->data);

      lbl = g_string_new(_(HISTORY_TOOLTIP_NEXT));
      if (history)
	{
	  g_string_append_printf(lbl, "\n\n(%s", history[0]);
	  for (i = 1; history[i]; i++)
	    g_string_append_printf(lbl, ", %s", history[i]);
	  g_string_append_printf(lbl, ")");
	}
      gtk_widget_set_tooltip_text(buttonDirNext, lbl->str);
      g_string_free(lbl, TRUE);
    }
  if (currentHistory && currentHistory->next && buttonDirPrev)
    {
      history = (gchar**)(currentHistory->next->data);

      lbl = g_string_new(_(HISTORY_TOOLTIP_PREV));
      if (history)
	{
	  g_string_append_printf(lbl, "\n\n(%s", history[0]);
	  for (i = 1; history[i]; i++)
	    g_string_append_printf(lbl, ", %s", history[i]);
	  g_string_append_printf(lbl, ")");
	}
      gtk_widget_set_tooltip_text(buttonDirPrev, lbl->str);
      g_string_free(lbl, TRUE);
    }
}
gboolean panelBrowserSet_previousHistoryDirectories()
{
  if (!currentHistory || !g_list_next(currentHistory))
    return FALSE;

  currentHistory = g_list_next(currentHistory);
  DBG_fprintf(stderr, "Panel Browser: set current history to %p (%d).\n",
	      (gpointer)currentHistory, g_list_length(currentHistory));

  setCurrentDirectories((gchar**)(currentHistory->data));
  updateDirectionalTooltips();

  gtk_widget_set_sensitive(buttonDirPrev, g_list_next(currentHistory) != (gpointer)0);
  gtk_widget_set_sensitive(buttonDirNext, TRUE);

  return TRUE;
}
gboolean panelBrowserSet_nextHistoryDirectories()
{
  if (!currentHistory || !g_list_previous(currentHistory))
    return FALSE;

  currentHistory = g_list_previous(currentHistory);
  DBG_fprintf(stderr, "Panel Browser: set current history to %p (%d).\n",
	      (gpointer)currentHistory, g_list_length(currentHistory));

  setCurrentDirectories((gchar**)(currentHistory->data));
  updateDirectionalTooltips();

  gtk_widget_set_sensitive(buttonDirPrev, TRUE);
  gtk_widget_set_sensitive(buttonDirNext, g_list_previous(currentHistory) != (gpointer)0);

  return TRUE;
}

static void associateFiles(int nbKind, int commonPathLen)
{
  gboolean valid;
  GtkTreeIter iter;
  int kind, searchKind, i;
  gchar *filename, *searchName, *pt, *fileUTF8;
  GList *tmpLst, *storeLst;
  gsize lu, ecrit;
  gchar **ext;

  /* Read all the stored files and try to associate them on their names.
     When the name matches the previous one without the extension, then the kind
     is stored. If a full set of names can be retrieved, then it is stored. The
     extensions are stored in the ext array. */
  searchName = g_strdup(".");
  searchKind = 0;
  storeLst = (GList*)0;
  ext = g_malloc(sizeof(gchar*) * (nbKind + 1));
  memset(ext, 0, sizeof(gchar*) * (nbKind + 1));
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(treeStoreFiles), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(treeStoreFiles), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(treeStoreFiles), &iter,
			 COLUMN_FILE_KIND, &kind, COLUMN_NAME, &filename, -1);
      pt = strrchr(filename, '.');
      if (pt)
	*pt = '\0';
      if (kind >= 0 && kind < nbKind)
	ext[kind] = g_strdup(pt + 1);
/*       fprintf(stderr, "'%s' %d %d\n", filename, kind, searchKind); */
      if (kind >= 0 && kind < nbKind && !strcmp(searchName, filename))
	{
	  /* Ok, match, we continue. */
	  searchKind += kind;
	  g_free(filename);
	  if (searchKind == nbKind * (nbKind - 1) / 2)
	    {
	      pt = g_strjoinv(",", ext);
	      /* We found a complete set. */
	      storeLst =
		g_list_prepend(storeLst, g_markup_printf_escaped("%s<span size=\"smaller\" foreground=\"grey\">.[%s]</span>", searchName, pt));
	      g_free(pt);
	      for (i = 0; i < nbKind; i++)
		g_free(ext[i]);
	      memset(ext, 0, sizeof(gchar*) * (nbKind + 1));
	    }
	}
      else
	{
	  /* Doesn't match, try for new. */
	  g_free(searchName);
	  for (i = 0; i < nbKind; i++)
	    if (ext[i])
	      g_free(ext[i]);
	  memset(ext, 0, sizeof(gchar*) * (nbKind + 1));
	  searchName = filename;
	  searchKind = kind;
	  if (kind >= 0 && kind < nbKind)
	    ext[kind] = g_strdup(pt + 1);
	}
    }
  g_free(searchName);
  /* Store now the associated list. */
  for (tmpLst = storeLst; tmpLst; tmpLst = g_list_next(tmpLst))
    {
      fileUTF8 = g_filename_to_utf8((gchar*)tmpLst->data + commonPathLen + 1,
				    -1, &lu, &ecrit, NULL);
      pt = g_strrstr((gchar*)tmpLst->data, "<span ");
      *pt = '\0';
/*       fprintf(stderr, "Panel Browser: found association for '%s'.\n", fileUTF8); */
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 9
      gtk_tree_store_insert_with_values(treeStoreFiles, &iter, (GtkTreeIter*)0, 0,
					COLUMN_BOOLEAN, FALSE,
					COLUMN_NAME, tmpLst->data,
					COLUMN_NAME_UTF8, fileUTF8,
					COLUMN_ACTIVE, TRUE,
					COLUMN_FILE_KIND, nbKind,
					COLUMN_FILE_VALID, TRUE,
					-1);
#else							
      gtk_tree_store_prepend(treeStoreFiles, &iter, (GtkTreeIter*)0);
      gtk_tree_store_set(treeStoreFiles, &iter,
			 COLUMN_BOOLEAN, FALSE,
			 COLUMN_NAME, tmpLst->data,
			 COLUMN_NAME_UTF8, fileUTF8,
			 COLUMN_ACTIVE, TRUE,
			 COLUMN_FILE_KIND, nbKind,
			 COLUMN_FILE_VALID, TRUE,
			 -1);
#endif
      g_free(tmpLst->data);
      if (fileUTF8)
	g_free(fileUTF8);
    }
  g_list_free(storeLst);
}

static void browseDirectory()
{
  GDir *gdir;
  gint len;
  int i;
  GList *atFmt, *spFmt;
  gint atKind, spKind;
  gboolean success;
  struct TimerInfo_ info;

  if (!currentBrowseredDirectory)
    return;

  dirDirty = FALSE;

  atFmt = spFmt = (GList*)0;
  atKind = spKind = -1;
  switch (gtk_combo_box_get_active(GTK_COMBO_BOX(comboFilter)))
    {
    case 0:
      atKind = 0;
      atFmt = visu_data_atomic_class_getLoaders();
      break;
    case 1:
      atFmt = visu_data_atomic_class_getLoaders();
      spFmt = visu_data_spin_class_getLoaders();
      atKind = 2;
      spKind = 3;
      break;
    case 2:
      atFmt = visu_data_atomic_class_getLoaders();
      atKind = 2;
      break;
    case 3:
      spFmt = visu_data_spin_class_getLoaders();
      spKind = 3;
      break;
    default:
      break;
    }
  if (!atFmt && !spFmt)
    return;

  DBG_fprintf(stderr, "Panel browser: cleaning of the list.\n");
  gtk_tree_store_clear(treeStoreFiles);
  
  /* Remove temporary the model. */
  gtk_tree_view_set_model(GTK_TREE_VIEW(fileTree),
			  (GtkTreeModel*)0);
  gtk_widget_set_sensitive(scrolledwindow1, FALSE);

  /* Add a timeout to show the progress bar if the scan is too long. */
  info.abort   = FALSE;
  info.nbFiles = 0;
  info.bt      = (GtkWidget*)0;
#if GLIB_MINOR_VERSION < 13
  info.timer   = g_timeout_add(1000, showProgressBar, (gpointer)(&info));
#else
  info.timer   = g_timeout_add_seconds(1, showProgressBar, (gpointer)(&info));
#endif
  info.label   = 0;

  success = TRUE;
  len = strlen(commonBrowseredDirectory);
  for (i = 0; success && currentBrowseredDirectory[i] && !info.abort; i++)
    {
      DBG_fprintf(stderr, "Panel browser: scanning directory '%s'.\n",
		  currentBrowseredDirectory[i]);
      gdir = g_dir_open(currentBrowseredDirectory[i], 0, NULL);
      if (gdir)
	{
	  addParsedDirectory(len, currentBrowseredDirectory[i], gdir,
			     gtk_toggle_button_get_active
			     (GTK_TOGGLE_BUTTON(buttonRecurse)),
			     atFmt, atKind, spFmt, spKind, &info);
	  g_dir_close(gdir);
	}
      else
	success = FALSE;
    }
  hideProgressBar(&info);

  panelBrowserSet_labelCurrentDir();

  if (gtk_combo_box_get_active(GTK_COMBO_BOX(comboFilter)) == 1)
    associateFiles(2, len);

  onFilterChanged(GTK_EDITABLE(entryFilterBrowser), (gpointer)0);

  /* Reattach the model. */
  gtk_tree_view_set_model(GTK_TREE_VIEW(fileTree), GTK_TREE_MODEL(treeStoreFilesFilter));
  gtk_widget_set_sensitive(scrolledwindow1, TRUE);

  if (!success)
    visu_ui_raiseWarning(_("Browsing a directory"),
			 _("The specified directory is unreadable."),
			 (GtkWindow*)0);
}

static void addParsedDirectory(int commonPathLen, const gchar *root,
			       GDir *gdir, gboolean recurse,
                               GList *atFmt, gint atKind, GList *spFmt, gint spKind,
			       struct TimerInfo_ *timer)
{
  const gchar *fileFromDir;
  gchar *fileUTF8, *file;
  gint kind;
  gsize lu, ecrit;
  GDir *recursedDir;
  GtkTreeIter iter;
  GList *lst;
#if GLIB_MINOR_VERSION > 5
  struct stat buf;
  struct tm *tm;
  gchar data[256];
#endif

  g_return_if_fail(root && root[0]);
  DBG_fprintf(stderr, "Panel Browser: read dir '%s' %d.\n", root, (int)recurse);
  fileFromDir = g_dir_read_name(gdir);
  while(fileFromDir && !timer->abort)
    {
      file = g_build_filename(root, fileFromDir, NULL);
      fileUTF8 = g_filename_to_utf8(file + commonPathLen + 1,
				    -1, &lu, &ecrit, NULL);

      if(fileUTF8)
	{
	  if (g_file_test(file, G_FILE_TEST_IS_DIR))
	    {
	      DBG_fprintf(stderr, "Panel Browser: read dir '%s'\n", file);
	      if (recurse)
		{
		  recursedDir = g_dir_open(file, 0, NULL);
		  if (recursedDir)
		    {
		      addParsedDirectory(commonPathLen, file, recursedDir, TRUE,
					 atFmt, atKind, spFmt, spKind, timer);
		      g_dir_close(recursedDir);
		    }
		}
	    }
	  else
	    {
	      kind = -1;
	      for (lst = atFmt; lst && kind < 0; lst = g_list_next(lst))
                if (tool_file_format_validate(TOOL_FILE_FORMAT(lst->data), fileUTF8))
                  kind = atKind;
	      for (lst = spFmt; lst && kind < 0; lst = g_list_next(lst))
                if (tool_file_format_validate(TOOL_FILE_FORMAT(lst->data), fileUTF8))
                  kind = spKind;
#if GLIB_MINOR_VERSION > 5
	      if (showDate)
		{
		  if (g_stat(file, &buf))
		    buf.st_mtime = 0;
		  DBG_fprintf(stderr, "Panel Browser: mtime %d.\n", (int)buf.st_mtime);
		  tm = localtime(&buf.st_mtime);
		  strftime(data, 256, _("%Y-%m-%d %H:%M"), tm);
		}
	      else
		{
		  buf.st_mtime = 0;
		  data[0] = '\0';
		}
#endif	      
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 9
	      gtk_tree_store_insert_with_values(treeStoreFiles, &iter,
						(GtkTreeIter*)0, 0,
						COLUMN_BOOLEAN, FALSE,
						COLUMN_NAME, file,
						COLUMN_NAME_UTF8, fileUTF8,
						COLUMN_ACTIVE, TRUE,
                                                COLUMN_FILE_KIND, kind,
						COLUMN_FILE_VALID, (kind >= 0),
#if GLIB_MINOR_VERSION > 5
						COLUMN_DATE, buf.st_mtime,
						COLUMN_DATA, data,
#endif
						-1);
#else							
	      gtk_tree_store_append(treeStoreFiles, &iter, (GtkTreeIter*)0);
	      gtk_tree_store_set(treeStoreFiles, &iter,
				 COLUMN_BOOLEAN, FALSE,
				 COLUMN_NAME, file,
				 COLUMN_NAME_UTF8, fileUTF8,
                                 COLUMN_FILE_KIND, kind,
				 COLUMN_ACTIVE, TRUE,
				 COLUMN_FILE_VALID, (kind >= 0),
#if GLIB_MINOR_VERSION > 5
				 COLUMN_DATE, buf.st_mtime,
				 COLUMN_DATA, data,
#endif
				 -1);
#endif
	      timer->nbFiles += 1;
	    }
	  g_free(fileUTF8);
	}
      g_free(file);
      visu_ui_wait();
      fileFromDir = g_dir_read_name(gdir);
    }
}

#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 17
static void onParseAbortClicked(GtkButton *button _U_, gpointer data)
{
  (*(gboolean*)data) = TRUE;
}
#else
static void onParseAbortClicked(GtkInfoBar *infoBar _U_, gint response,
				gpointer data)
{
  (*(gboolean*)data) = (response == GTK_RESPONSE_CANCEL);
}
#endif

static void onEnter(VisuUiPanel *visu_ui_panel _U_, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel Browser: enter, parse directory (%d).\n",
	      !fileTree);
  if (!fileTree)
    createInteriorBrowser();
  if (gtk_tree_model_iter_n_children(GTK_TREE_MODEL(treeStoreFiles), (GtkTreeIter*)0) == 0 || dirDirty)
    browseDirectory();
}

static void onDirectoryClicked(GtkButton *button _U_, gpointer data _U_)
{
  gchar **filenames;

  filenames = visu_ui_main_getSelectedDirectory
    (visu_ui_main_class_getCurrentPanel(),
     visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelBrowser)),
     TRUE, commonBrowseredDirectory);

  if (!filenames)
    return;

  visu_ui_panel_browser_setCurrentDirectories(filenames);
}

static void onRecurseToggled(GtkToggleButton *toggle _U_, gpointer data _U_)
{
  browseDirectory();
}

static void onPrevClicked(GtkButton *button _U_, gpointer data _U_)
{
  panelBrowserSet_previousHistoryDirectories();
}
static void onNextClicked(GtkButton *button _U_, gpointer data _U_)
{
  panelBrowserSet_nextHistoryDirectories();
}

static gboolean pulseProgressBar(gpointer data)
{
  struct TimerInfo_ *timer;
  gchar nbFilesLabel[36];

  timer = (struct TimerInfo_*)data;
  sprintf(nbFilesLabel, _("%4d files found."), timer->nbFiles);
  gtk_label_set_text(GTK_LABEL(labelInfo), nbFilesLabel);
  DBG_fprintf(stderr, "Panel browser: update label to '%s'.\n", nbFilesLabel);

  return TRUE;
}
static gboolean showProgressBar(gpointer data)
{
  gchar nbFilesLabel[36];
  struct TimerInfo_ *timer;

  DBG_fprintf(stderr, "Panel browser: scanning is too slow, showing progress bar.\n");

  timer = (struct TimerInfo_*)data;
#if GTK_MAJOR_VERSION == 2 && GTK_MINOR_VERSION < 17
  timer->bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
  gtk_box_pack_end(GTK_BOX(infoBar), timer->bt, FALSE, FALSE, 0);
  g_signal_connect(G_OBJECT(timer->bt), "clicked",
		   G_CALLBACK(onParseAbortClicked), (gpointer)&timer->abort);
#else
  timer->bt = gtk_info_bar_add_button(GTK_INFO_BAR(infoBar),
				      TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL);
  g_signal_connect(G_OBJECT(infoBar), "response",
		   G_CALLBACK(onParseAbortClicked), (gpointer)&timer->abort);
#endif

  sprintf(nbFilesLabel, _("%4d files found."), timer->nbFiles);
  visu_ui_panel_browser_setMessage(nbFilesLabel, GTK_MESSAGE_INFO);

  timer->label = g_timeout_add(250, pulseProgressBar, data);

  return FALSE;
}
static void hideProgressBar(struct TimerInfo_ *timer)
{
  DBG_fprintf(stderr, "Panel browser: scanning is finished, hiding progress bar.\n");

  g_source_remove(timer->timer);
  if (timer->label)
    {
      g_source_remove(timer->label);
      visu_ui_panel_browser_setMessage((const gchar*)0, GTK_MESSAGE_INFO);
    }
  if (timer->bt)
    gtk_widget_destroy(timer->bt);
}


/***************************/
/* Miscellaneous functions */
/***************************/

gboolean gtk_tree_model_get_iter_last(GtkTreeModel *model, GtkTreeIter *last, GtkTreePath **path)
{
  gboolean valid;
  gint n;

  g_return_val_if_fail(model && last, FALSE);

  n = gtk_tree_model_iter_n_children(model, (GtkTreeIter*)0);
  if (n == 0)
    return FALSE;

  valid = gtk_tree_model_iter_nth_child(model, last, (GtkTreeIter*)0, n - 1);
  g_return_val_if_fail(valid, FALSE);

  if (path)
    *path = gtk_tree_model_get_path(model, last);
  
  return TRUE;
}

static gboolean panelBrowserIsIterVisible(GtkTreeModel *model, GtkTreeIter *iter,
					  gpointer data _U_)
{
  gboolean passUserFilter;
  gint kind;
  
  gtk_tree_model_get(model, iter,
		     COLUMN_FILE_KIND, &kind,
		     COLUMN_ACTIVE, &passUserFilter,
		     -1);
  return (passUserFilter && (kind == gtk_combo_box_get_active(GTK_COMBO_BOX(comboFilter)) || kind < 0));
}

static gint onSortNames(GtkTreeModel *model, GtkTreeIter *a,
			GtkTreeIter *b, gpointer user_data _U_)
{
  gint kindA, kindB;
  gchar *lblA, *lblB;
  gint diff, iSetA, iSetB;
  GtkTreeIter parentA, parentB;

  /* We always sort the kind first. */
  gtk_tree_model_get(model, a, COLUMN_FILE_KIND, &kindA, -1);
  gtk_tree_model_get(model, b, COLUMN_FILE_KIND, &kindB, -1);
  
  if ((kindA < 0 && kindB >= 0) || (kindA >= 0 && kindB < 0))
    return (kindB - kindA);
  else
    {
      if (gtk_tree_model_iter_parent(model, &parentA, a) &&
	  gtk_tree_model_iter_parent(model, &parentB, b))
	{
	  gtk_tree_model_get(model, a, COLUMN_DATE, &iSetA, -1);
	  gtk_tree_model_get(model, b, COLUMN_DATE, &iSetB, -1);

	  diff = iSetA - iSetB;
	}
      else
	{
	  gtk_tree_model_get(model, a, COLUMN_NAME_UTF8, &lblA, -1);
	  gtk_tree_model_get(model, b, COLUMN_NAME_UTF8, &lblB, -1);

	  diff = g_utf8_collate(lblA, lblB);

	  g_free(lblA);
	  g_free(lblB);
	}

      return diff;
    }
}

void panelBrowserSet_labelCurrentDir()
{
  gchar *directoryUTF8, *markup;

  if (!commonBrowseredDirectory)
    return;

  directoryUTF8 = g_filename_to_utf8(commonBrowseredDirectory, -1, NULL, NULL, NULL);
  g_return_if_fail(directoryUTF8);

  markup = g_markup_printf_escaped(_("<span style=\"italic\" size=\"smaller\">%s</span>"),
				   directoryUTF8);
  g_free(directoryUTF8);
  gtk_label_set_markup(GTK_LABEL(labelDirectory), markup);
  g_free(markup);
}

static void onNewDir(GObject *obj _U_, VisuUiDirectoryType type, gpointer user _U_)
{
  DBG_fprintf(stderr, "Panel Browser: caught 'DirectoryChanged'"
	      " signal for type %d.\n", type);
  if (type == VISU_UI_DIR_FILE)
    visu_ui_panel_browser_setCurrentDirectory(visu_ui_main_getLastOpenDirectory(visu_ui_main_class_getCurrentPanel()));
}

static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_BROWSER_HEADERS);
  g_string_append_printf(data, "%s[gtk]: %i\n\n", FLAG_PARAMETER_BROWSER_HEADERS,
			 (int)showHeaders);
  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_BROWSER_DATE);
  g_string_append_printf(data, "%s[gtk]: %i\n\n", FLAG_PARAMETER_BROWSER_DATE,
			 (int)showDate);
}
/**
 * visu_ui_panel_browser_setMessage:
 * @message: a string to be displaied.
 * @message_type: the kind of message.
 *
 * This routine is used to give the user a message. This message can
 * be mere information or a warning or an error.
 *
 * Since: 3.6
 */
void visu_ui_panel_browser_setMessage(const gchar* message, GtkMessageType message_type)
{
  if (!fileTree)
    createInteriorBrowser();

  if (message)
    {
      gtk_label_set_text(GTK_LABEL(labelInfo), message);
#if GTK_MAJOR_VERSION > 2 || GTK_MINOR_VERSION > 17
      gtk_info_bar_set_message_type(GTK_INFO_BAR(infoBar), message_type);
#else
      switch (message_type)
	{
	case GTK_MESSAGE_INFO:
	  gtk_image_set_from_stock(GTK_IMAGE(infoImg),
				   GTK_STOCK_INFO, GTK_ICON_SIZE_BUTTON);
	  break;
	default:
	  gtk_image_clear(GTK_IMAGE(infoImg));
	  break;
	}
#endif
      gtk_widget_set_no_show_all(infoBar, FALSE);
      gtk_widget_show_all(infoBar);
    }
  else
    {
      gtk_widget_hide(infoBar);
    }
}
