/*   EXTRAITS DE LA LICENCE
	Copyright CEA, contributeurs : Damien CALISTE, Tristan
	BERTHELOT, laboratoire L_Sim, (2001-2010)
  
	Adresse mèl :
	CALISTE, damien P caliste AT cea P fr.
	BERTHELOT, tristan P berthelot AT isen P fr.

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

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

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

/*   LICENCE SUM UP
	Copyright CEA, contributors: Damien CALISTE, Tristan
	BERTHELOT, L_Sim laboratory, (2001-2010)

	E-mail address:
	CALISTE, damien P caliste AT cea P fr.
	BERTHELOT, tristan P berthelot AT isen 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 <glib.h>
#include <gtk/gtk.h>
#include <gdk/gdkkeysyms.h>

#include <Python.h>

#include <support.h>
#include <visu_basic.h>
#include <visu_gtk.h>
#include <visu_commandLine.h>
#include <coreTools/toolOptions.h>
#include <extraGtkFunctions/gtk_toolPanelWidget.h>
#include <visu_commandLine.h>
#include <visu_configFile.h>

#define PYTHONGI_DESCRIPTION _("<span size=\"smaller\">"		\
			       "This plug-in allows to execute\n"	\
			       "Python scripts using GObject\n"		\
			       "introspection.</span>")
#define PYTHONGI_AUTHORS     "Caliste Damien &\nTristan Berthelot"

#define FLAG_PARAMETER_INIT_SCRIPTS "init_scripts"
#define DESC_PARAMETER_INIT_SCRIPTS "Scripts loaded on startup ; paths separated by ':'"

enum
  {
    SCRIPT_LABEL,
    SCRIPT_PATH,
    N_SCRIPTS
  };

/* Local variables. */
static gchar *iconPath;
static GtkWidget *panelPython, *pyFileChooser, *pyExecute, *pyTextView;
static gboolean isPanelInitialised = FALSE, isPythonInitialised = FALSE;
static GtkListStore *initScripts;
static GtkTextBuffer *pyTextBuffer;
static GtkTextTag *monoTag, *errorTag, *boldTag;
static GList *history, *curHist;

/* Local methods. */
static void initialisePython();
static void initialisePanel(VisuUiPanel *panel);
static gboolean loadScript(gpointer file);
static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent);
static void exportParameters(GString *data, VisuData *dataObj);
static gboolean readInitScripts(VisuConfigFileEntry *entry, gchar **lines,
                                int nbLines, int position, GError **error);
static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error);

/* Callbacks. */
static void onPanelEnter(VisuUiPanel *panel, gpointer data);
static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data);
static void onScriptExecute(GtkButton *widget, gpointer user_data);
static void onInitScriptAdded(GtkToolButton *bt, gpointer data);
static void onInitScriptRemoved(GtkToolButton *bt, gpointer data);
static void onInitScriptReload(GtkToolButton *bt, gpointer data);
static gboolean onPyIO(GIOChannel *source, GIOCondition condition, gpointer data);
static void onOutputCleared(GtkToolButton *bt, gpointer data);
static void onOutputCancel(GtkToolButton *bt, gpointer data);
static void onOutputGetData(GtkToolButton *bt, gpointer data);
#if GTK_MAJOR_VERSION >= 3
static void onVScroll(GtkAdjustment *adj, gpointer data);
#endif
static void onInteractiveEntry(GtkEntry *entry, gpointer data);
static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data);

gboolean pythongiInit(void)
{
  GHashTable *options;
  ToolOption *opt;
  const gchar *file;
  VisuConfigFileEntry *entry;

  DBG_fprintf(stderr, "Plugin PythonGI: initialisation.\n");

  iconPath = g_build_filename(V_SIM_PIXMAPS_DIR, "pythongi.png", NULL);

  initScripts = gtk_list_store_new(N_SCRIPTS, G_TYPE_STRING, G_TYPE_STRING);
  pyTextBuffer = gtk_text_buffer_new((GtkTextTagTable*)0);
  monoTag = gtk_text_buffer_create_tag(pyTextBuffer, "typewriter",
                                       "family", "monospace", NULL);
  errorTag = gtk_text_buffer_create_tag(pyTextBuffer, "error",
                                        "foreground", "Tomato", NULL);  
  boldTag = gtk_text_buffer_create_tag(pyTextBuffer, "bold",
                                       "weight", PANGO_WEIGHT_BOLD, NULL);
  history = curHist = (GList*)0;

  isPythonInitialised = FALSE;
  options = commandLineGet_options();
  if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScriptInit")))
    {
      DBG_fprintf(stderr, "Plugin PythonGI: will run init script.\n");
      initialisePython();
      file = g_value_get_string(tool_option_getValue(opt));
      addInitScript(file, TRUE, (GtkWindow*)0);
    }
  if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
    {
      DBG_fprintf(stderr, "Plugin PythonGI: will run py script.\n");
      initialisePython();
      file = g_value_get_string(tool_option_getValue(opt));
      g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)file, NULL);
    }

  visu_config_file_addKnownTag("python");
  entry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
                                   FLAG_PARAMETER_INIT_SCRIPTS,
                                   DESC_PARAMETER_INIT_SCRIPTS,
                                    1, readInitScripts);
  visu_config_file_entry_setVersion(entry, 3.7f);
  visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
                                    exportParameters);

  return TRUE;
}
gboolean pythongiInitGtk(void)
{
  /* Long description */
  gchar *cl = _("Python scripting");
  /* Short description */
  gchar *tl = _("Python");
  GHashTable *options;
  ToolOption *opt;
  const gchar *file;

  isPanelInitialised = FALSE;

  panelPython = visu_ui_panel_newWithIconFromPath("Panel_python", cl,
                                                  tl, "stock-pythongi_20.png");
  visu_ui_panel_setDockable(VISU_UI_PANEL(panelPython), TRUE);
  visu_ui_panel_attach(VISU_UI_PANEL(panelPython), visu_ui_panel_class_getCommandPanel());
  g_signal_connect(G_OBJECT(panelPython), "page-entered",
		   G_CALLBACK(onPanelEnter), (gpointer)0);

  options = commandLineGet_options();
  if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
    {
      initialisePanel(VISU_UI_PANEL(panelPython));
      file = g_value_get_string(tool_option_getValue(opt));
      gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(pyFileChooser), file);
      gtk_widget_set_sensitive(pyExecute, TRUE);
    }

  return TRUE;
}
const char* pythongiGet_description(void)
{
  return PYTHONGI_DESCRIPTION;
}

const char* pythongiGet_authors(void)
{
  return PYTHONGI_AUTHORS;
}

const char* pythongiGet_icon(void)
{
  return iconPath;
}

void pythongiFree(void)
{
  DBG_fprintf(stderr, "Panel PythonGI: finalise.\n");
  
  g_free(iconPath);
  g_object_unref(initScripts);

  if (!isPythonInitialised)
    return;

  Py_Finalize();
}

static void onPanelEnter(VisuUiPanel *panel, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel PythonGI: caught the 'page-entered' signal %d.\n",
	      isPanelInitialised);
  if (!isPythonInitialised)
    initialisePython();
  if (!isPanelInitialised)
    initialisePanel(panel);
}
static void initialisePython()
{
  if (isPythonInitialised)
    return;

  DBG_fprintf(stderr, "Panel PythonGI: initialise Python.\n");
  Py_SetProgramName((char*)commandLineGet_programName());
  Py_Initialize();
  DBG_fprintf(stderr, "Panel PythonGI: import sys.\n"
              "import os;\n"
              "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\")\n");
  PyRun_SimpleString("import os;"
                     "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\");"
                     "import sys;"
                     "sys.argv = ['']\n"
                     "from gi.repository import v_sim");
  DBG_fprintf(stderr, " | done.\n");

  isPythonInitialised = TRUE;
}
static void addFilters(GtkWidget *wd)
{
  GtkFileFilter *flt;

  flt = gtk_file_filter_new();
  gtk_file_filter_set_name(flt, _("Python scripts for V_Sim"));
  gtk_file_filter_add_mime_type(flt, "text/x-script.python");
  gtk_file_filter_add_mime_type(flt, "text/x-python");
  gtk_file_filter_add_mime_type(flt, "application/x-python");
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
  flt = gtk_file_filter_new();
  gtk_file_filter_set_name(flt, _("All"));
  gtk_file_filter_add_pattern(flt, "*");
  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
}
static void initialisePanel(VisuUiPanel *panel)
{
  int fd[2];
  gchar *string;
  GIOChannel *pyIO;
  GtkWidget *vbox, *hbox, *wd, *scrolled;
  GtkTreeViewColumn *column;
  GtkCellRenderer *renderer;
  GtkToolItem *item;
  GtkTreeSelection *selection;

  g_return_if_fail(!isPanelInitialised);

  /* Redirect stdout. */
  if (pipe(fd))
    g_warning("Cannot create pipes for Python process.");
  else
    {
      string = g_strdup_printf("sys.stdout = os.fdopen(%d, 'w', 0)", fd[1]);
      DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
      PyRun_SimpleString(string);
      g_free(string);
      pyIO = g_io_channel_unix_new(fd[0]);
      g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
      g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(FALSE));
    }
  /* Redirect stderr. */
  if (pipe(fd))
    g_warning("Cannot create pipes for Python process.");
  else
    {
      string = g_strdup_printf("sys.stderr = os.fdopen(%d, 'w', 0)", fd[1]);
      DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
      PyRun_SimpleString(string);
      g_free(string);
      pyIO = g_io_channel_unix_new(fd[0]);
      g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
      g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(TRUE));
    }

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_set_border_width(GTK_CONTAINER(panel), 5);
  gtk_container_add(GTK_CONTAINER(panel), vbox);

  wd = gtk_label_new(_("<b>Scripts loaded on startup</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_widget_set_name(wd, "label_head");
  gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);

  scrolled = gtk_scrolled_window_new(NULL, NULL);
  gtk_widget_set_size_request(scrolled, -1, 100);
  gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
				 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
				      GTK_SHADOW_ETCHED_IN);
  gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);

  wd = gtk_tree_view_new_with_model(GTK_TREE_MODEL(initScripts));
  gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(wd), FALSE);
  gtk_widget_set_tooltip_text(wd,
                              _("This list is saved with the parameter file."));
  gtk_container_add(GTK_CONTAINER(scrolled), wd);

  renderer = gtk_cell_renderer_text_new();
  column = gtk_tree_view_column_new_with_attributes("", renderer,
                                                    "text", SCRIPT_LABEL,
                                                    NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
  renderer = gtk_cell_renderer_text_new();
  g_object_set(G_OBJECT(renderer), "scale", 0.75, NULL);
  column = gtk_tree_view_column_new_with_attributes("", renderer,
                                                    "text", SCRIPT_PATH,
                                                    NULL);
  gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
  selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(wd));

  wd = gtk_toolbar_new();
  gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
  gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
  gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-add");
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onInitScriptAdded), (gpointer)0);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-remove");
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onInitScriptRemoved), (gpointer)selection);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "view-refresh");
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onInitScriptReload), (gpointer)selection);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);

  wd = gtk_label_new(_("<b>Interactive scripting</b>"));
  gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
  gtk_label_set_xalign(GTK_LABEL(wd), 0.);
  gtk_widget_set_name(wd, "label_head");
  gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  hbox = gtk_hbox_new(FALSE, 3);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
  wd = gtk_label_new(_("Load:"));
  gtk_widget_set_margin_start(wd, 5);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
  pyFileChooser = gtk_file_chooser_button_new(_("Choose a Python script"),
					      GTK_FILE_CHOOSER_ACTION_OPEN);
  addFilters(pyFileChooser);
  gtk_box_pack_start(GTK_BOX(hbox), pyFileChooser, TRUE, TRUE, 0);
  pyExecute = gtk_button_new_from_icon_name("system-run", GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_widget_set_margin_end(pyExecute, 5);
  gtk_widget_set_sensitive(pyExecute, FALSE);
  gtk_box_pack_start(GTK_BOX(hbox), pyExecute, FALSE, FALSE, 0);

  g_signal_connect(G_OBJECT(pyFileChooser), "file-set",
		   G_CALLBACK(onScriptChosen), (gpointer)pyExecute);
  g_signal_connect(G_OBJECT(pyExecute), "clicked",
		   G_CALLBACK(onScriptExecute), (gpointer)pyFileChooser);

  hbox = gtk_hbox_new(FALSE, 0);
  gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 3);

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

  pyTextView = gtk_text_view_new_with_buffer(pyTextBuffer);
  g_object_unref(pyTextBuffer);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(pyTextView), FALSE);
  gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(pyTextView), FALSE);
  gtk_container_add(GTK_CONTAINER(scrolled), pyTextView);
#if GTK_MAJOR_VERSION >= 3
  g_signal_connect(G_OBJECT(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(pyTextView))),
                   "changed", G_CALLBACK(onVScroll), (gpointer)0);
#endif

  wd = gtk_toolbar_new();
  gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
  gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
  gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
  gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);

  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-clear");
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onOutputCleared), (gpointer)0);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Clear Python output."));
  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "process-stop");
  gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onOutputCancel), (gpointer)0);
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  item = gtk_tool_button_new(NULL, NULL);
  gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-find");
  gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
  gtk_widget_set_tooltip_text(GTK_WIDGET(item),
                              _("Get the current VisuData object as 'data'"
                                " variable and the current VisuGlView as 'view'."));

  wd = gtk_entry_new();
#if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION > 1)
  gtk_entry_set_placeholder_text(GTK_ENTRY(wd), _("Python interactive command line"));
#endif
  g_signal_connect(G_OBJECT(wd), "activate",
                   G_CALLBACK(onInteractiveEntry), (gpointer)0);
  g_signal_connect(G_OBJECT(wd), "key-press-event",
                   G_CALLBACK(onKeyPressed), (gpointer)0);
  g_signal_connect(G_OBJECT(item), "clicked",
                   G_CALLBACK(onOutputGetData), (gpointer)wd);
  gtk_box_pack_end(GTK_BOX(vbox), wd, FALSE, FALSE, 0);

  gtk_widget_show_all(vbox);

  isPanelInitialised = TRUE;
}

static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data)
{
  gchar *file;

  file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
  gtk_widget_set_sensitive(GTK_WIDGET(user_data), (file != (gchar*)0));
  if (file)
    g_free(file);
}
static void onScriptExecute(GtkButton *widget _U_, gpointer user_data)
{
  gchar *file;

  file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(user_data));
  g_return_if_fail(file);

  g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, loadScript, (gpointer)file, g_free);
}


static gboolean loadScript(gpointer file)
{
  gchar *script, *label, *name;
  GError *error;

  DBG_fprintf(stderr, "Panel PythonGI: load script '%s'.\n", (gchar*)file);

  g_return_val_if_fail(isPythonInitialised, FALSE);

  error = (GError*)0;
  if (!g_file_get_contents((gchar*)file, &script, (gsize*)0, &error))
    {
      if (error)
        {
          visu_ui_raiseWarning(_("Load a Python script"), error->message,
                               visu_ui_panel_getContainerWindow
                               (VISU_UI_PANEL(panelPython)));
          g_error_free(error);
        }
      return FALSE;
    }

  name = g_path_get_basename((const gchar*)file);
  label = g_strdup_printf(_("Load script \"%s\"\n"), name);
  g_free(name);
  _bufSetText(label, boldTag, FALSE);
  g_free(label);

  PyRun_SimpleString(script);
  g_free(script);

  return FALSE;
}

static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent)
{
  GtkTreeIter iter;
  gchar *path, *name;

  if (!g_file_test(file, G_FILE_TEST_IS_REGULAR))
    {
      if (parent)
        visu_ui_raiseWarning(_("Choose a Python script"),
                             _("Not a regular file."), parent);
      return;
    }

  path = tool_path_normalize(file);
  name = g_path_get_basename(file);
  gtk_list_store_append(initScripts, &iter);
  gtk_list_store_set(initScripts, &iter, SCRIPT_PATH, path, SCRIPT_LABEL, name, -1);
  g_free(name);
  
  if (immediate)
    {
      loadScript((gpointer)path);
      g_free(path);
    }
  else
    g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)path, g_free);
}
static void onInitScriptAdded(GtkToolButton *bt _U_, gpointer data _U_)
{
  GtkWidget *dialog;
  gchar *filename;
  GtkWindow *parent;

  parent = visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelPython));
  dialog = gtk_file_chooser_dialog_new(_("Choose a Python script"), parent,
                                       GTK_FILE_CHOOSER_ACTION_OPEN,
                                       TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
                                       TOOL_ICON_OPEN, GTK_RESPONSE_ACCEPT,
                                       NULL);
  addFilters(dialog);

  if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
    {
      filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
      addInitScript(filename, FALSE, parent);
      g_free(filename);
    }

  gtk_widget_destroy(dialog);
}
static void onInitScriptRemoved(GtkToolButton *bt _U_, gpointer data)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter;

  valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
  if (!valid)
    return;

  gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
}
static void onInitScriptReload(GtkToolButton *bt _U_, gpointer data)
{
  gboolean valid;
  GtkTreeModel *model;
  GtkTreeIter iter;
  gchar *path;

  valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
  if (!valid)
    return;

  gtk_tree_model_get(model, &iter, SCRIPT_PATH, &path, -1);
  loadScript((gpointer)path);
  g_free(path);
}
static gboolean onPyIO(GIOChannel *source,
                       GIOCondition condition _U_, gpointer data)
{
#define size 256
  gchar string[size];
  gsize lg;
  GError *error;

  error = (GError*)0;
  /* DBG_fprintf(stderr, "Panel PythonGI: receive buffer data.\n"); */
  if (g_io_channel_read_chars(source, string, size, &lg, &error) !=   G_IO_STATUS_NORMAL)
    {
      g_warning("%s", error->message);
      g_error_free(error);
      return TRUE;
    }
  string[lg] = '\0';
  /* DBG_fprintf(stderr, "Panel PythonGI: got\n%s\n", string); */

  _bufSetText(string, monoTag, GPOINTER_TO_INT(data));

  /* Flush Gtk events. */
  visu_ui_wait();

  return TRUE;
}
static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error)
{
  GtkTextIter iter;

  gtk_text_buffer_get_end_iter(pyTextBuffer, &iter);
  if (!error)
    gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, NULL);
  else
    gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, errorTag, NULL);
}
static void onOutputCleared(GtkToolButton *bt _U_, gpointer data _U_)
{
  GtkTextIter start, end;

  gtk_text_buffer_get_bounds(pyTextBuffer, &start, &end);
  gtk_text_buffer_delete(pyTextBuffer, &start, &end);
}
static void onOutputCancel(GtkToolButton *bt _U_, gpointer data _U_)
{
  DBG_fprintf(stderr, "Panel PythonGI: try to interrupt.\n");
  PyErr_SetString(PyExc_TypeError, "script interrupted.");
}
static void onOutputGetData(GtkToolButton *bt _U_, gpointer data)
{
  DBG_fprintf(stderr, "Panel PythonGI: get current data.\n");
  gtk_entry_set_text(GTK_ENTRY(data), "scene = v_sim.UiMainClass.getDefaultRendering().getGlScene()");
  g_signal_emit_by_name(G_OBJECT(data), "activate");
  gtk_entry_set_text(GTK_ENTRY(data), "data = scene.getData()");
  g_signal_emit_by_name(G_OBJECT(data), "activate");
  gtk_entry_set_text(GTK_ENTRY(data), "view = scene.getGlView()");
  g_signal_emit_by_name(G_OBJECT(data), "activate");
  gtk_entry_set_text(GTK_ENTRY(data), "selection = v_sim.ui_interactive_pick_getSelection()");
  g_signal_emit_by_name(G_OBJECT(data), "activate");
}
#if GTK_MAJOR_VERSION >= 3
static void onVScroll(GtkAdjustment *adj, gpointer data _U_)
{
  guint oldUpper;

  oldUpper = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(adj), "old-upper"));
  if (gtk_adjustment_get_value(adj) + gtk_adjustment_get_page_size(adj) == oldUpper)
    /* Scroll to the end. */
    gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) -
                             gtk_adjustment_get_page_size(adj));
  g_object_set_data(G_OBJECT(adj), "old-upper",
                    GINT_TO_POINTER(gtk_adjustment_get_upper(adj)));
}
#endif
static void onInteractiveEntry(GtkEntry *entry, gpointer data _U_)
{
  _bufSetText("> ", NULL, FALSE);
  _bufSetText(gtk_entry_get_text(entry), monoTag, FALSE);
  _bufSetText("\n", NULL, FALSE);
  PyRun_SimpleString(gtk_entry_get_text(entry));
  history = g_list_prepend(history, g_strdup(gtk_entry_get_text(entry)));
  curHist = (GList*)0;
  gtk_entry_set_text(entry, "");
}
static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data _U_)
{
  if (event->keyval != GDK_KEY_Up &&
      event->keyval != GDK_KEY_Down)
    return FALSE;
  
  if (event->keyval == GDK_KEY_Up)
    curHist = (curHist)?((curHist->next)?curHist->next:curHist):history;
  else if (event->keyval == GDK_KEY_Down && curHist)
    curHist = curHist->prev;

  gtk_entry_set_text(GTK_ENTRY(widget), (curHist)?(const gchar*)curHist->data:"");
  gtk_editable_set_position(GTK_EDITABLE(widget), -1);

  return TRUE;
}

/**************/
/* Parameters */
/**************/
static gboolean readInitScripts(VisuConfigFileEntry *entry _U_, gchar **lines,
                                int nbLines, int position _U_, GError **error _U_)
{
  gchar **paths;
  guint i;

  g_return_val_if_fail(nbLines == 1, FALSE);

  DBG_fprintf(stderr, "Plugin PythonGI: initialisation scripts.\n");
  
  initialisePython();

  paths = g_strsplit_set(lines[0], ":\n", 100);
  for (i = 0; paths[i]; i++)
    {
      g_strstrip(paths[i]);
      DBG_fprintf(stderr, "Panel PythonGI: test file '%s'.\n", paths[i]);
      addInitScript(paths[i], TRUE, (GtkWindow*)0);
    }
  g_strfreev(paths);

  return TRUE;
}
static void exportParameters(GString *data, VisuData *dataObj _U_)
{
  gboolean valid;
  GtkTreeIter iter;
  gchar *file;

  g_string_append_printf(data, "# %s\n", DESC_PARAMETER_INIT_SCRIPTS);
      g_string_append_printf(data, "%s[python]: ", FLAG_PARAMETER_INIT_SCRIPTS);
  for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(initScripts), &iter);
       valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(initScripts), &iter))
    {
      gtk_tree_model_get(GTK_TREE_MODEL(initScripts), &iter, SCRIPT_PATH, &file, -1);
      g_string_append_printf(data, "%s:", file);
      g_free(file);
    }
  g_string_append_printf(data, "\n\n");
}
