1 /*   EXTRAITS DE LA LICENCE
2 	Copyright CEA, contributeurs : Damien CALISTE, Tristan
3 	BERTHELOT, laboratoire L_Sim, (2001-2010)
4 
5 	Adresse m�l :
6 	CALISTE, damien P caliste AT cea P fr.
7 	BERTHELOT, tristan P berthelot AT isen P fr.
8 
9 	Ce logiciel est un programme informatique servant � visualiser des
10 	structures atomiques dans un rendu pseudo-3D.
11 
12 	Ce logiciel est r�gi par la licence CeCILL soumise au droit fran�ais et
13 	respectant les principes de diffusion des logiciels libres. Vous pouvez
14 	utiliser, modifier et/ou redistribuer ce programme sous les conditions
15 	de la licence CeCILL telle que diffus�e par le CEA, le CNRS et l'INRIA
16 	sur le site "http://www.cecill.info".
17 
18 	Le fait que vous puissiez acc�der � cet en-t�te signifie que vous avez
19 	pris connaissance de la licence CeCILL, et que vous en avez accept� les
20 	termes (cf. le fichier Documentation/licence.fr.txt fourni avec ce logiciel).
21 */
22 
23 /*   LICENCE SUM UP
24 	Copyright CEA, contributors: Damien CALISTE, Tristan
25 	BERTHELOT, L_Sim laboratory, (2001-2010)
26 
27 	E-mail address:
28 	CALISTE, damien P caliste AT cea P fr.
29 	BERTHELOT, tristan P berthelot AT isen P fr.
30 
31 	This software is a computer program whose purpose is to visualize atomic
32 	configurations in 3D.
33 
34 	This software is governed by the CeCILL  license under French law and
35 	abiding by the rules of distribution of free software.  You can  use,
36 	modify and/ or redistribute the software under the terms of the CeCILL
37 	license as circulated by CEA, CNRS and INRIA at the following URL
38 	"http://www.cecill.info".
39 
40 	The fact that you are presently reading this means that you have had
41 	knowledge of the CeCILL license and that you accept its terms. You can
42 	find a copy of this licence shipped with this software at
43         Documentation/licence.en.txt.
44 */
45 
46 #include <glib.h>
47 #include <gtk/gtk.h>
48 #include <gdk/gdkkeysyms.h>
49 
50 #include <Python.h>
51 
52 #include <support.h>
53 #include <visu_basic.h>
54 #include <visu_gtk.h>
55 #include <visu_commandLine.h>
56 #include <coreTools/toolOptions.h>
57 #include <extraGtkFunctions/gtk_toolPanelWidget.h>
58 #include <visu_commandLine.h>
59 #include <visu_configFile.h>
60 
61 #define PYTHONGI_DESCRIPTION _("<span size=\"smaller\">"		\
62 			       "This plug-in allows to execute\n"	\
63 			       "Python scripts using GObject\n"		\
64 			       "introspection.</span>")
65 #define PYTHONGI_AUTHORS     "Caliste Damien &\nTristan Berthelot"
66 
67 #define FLAG_PARAMETER_INIT_SCRIPTS "init_scripts"
68 #define DESC_PARAMETER_INIT_SCRIPTS "Scripts loaded on startup ; paths separated by ':'"
69 
70 enum
71   {
72     SCRIPT_LABEL,
73     SCRIPT_PATH,
74     N_SCRIPTS
75   };
76 
77 /* Local variables. */
78 static gchar *iconPath;
79 static GtkWidget *panelPython, *pyFileChooser, *pyExecute, *pyTextView;
80 static gboolean isPanelInitialised = FALSE, isPythonInitialised = FALSE;
81 static GtkListStore *initScripts;
82 static GtkTextBuffer *pyTextBuffer;
83 static GtkTextTag *monoTag, *errorTag, *boldTag;
84 static GList *history, *curHist;
85 
86 /* Local methods. */
87 static void initialisePython();
88 static void initialisePanel(VisuUiPanel *panel);
89 static gboolean loadScript(gpointer file);
90 static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent);
91 static void exportParameters(GString *data, VisuData *dataObj);
92 static gboolean readInitScripts(VisuConfigFileEntry *entry, gchar **lines,
93                                 int nbLines, int position, GError **error);
94 static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error);
95 
96 /* Callbacks. */
97 static void onPanelEnter(VisuUiPanel *panel, gpointer data);
98 static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data);
99 static void onScriptExecute(GtkButton *widget, gpointer user_data);
100 static void onInitScriptAdded(GtkToolButton *bt, gpointer data);
101 static void onInitScriptRemoved(GtkToolButton *bt, gpointer data);
102 static void onInitScriptReload(GtkToolButton *bt, gpointer data);
103 static gboolean onPyIO(GIOChannel *source, GIOCondition condition, gpointer data);
104 static void onOutputCleared(GtkToolButton *bt, gpointer data);
105 static void onOutputCancel(GtkToolButton *bt, gpointer data);
106 static void onOutputGetData(GtkToolButton *bt, gpointer data);
107 #if GTK_MAJOR_VERSION >= 3
108 static void onVScroll(GtkAdjustment *adj, gpointer data);
109 #endif
110 static void onInteractiveEntry(GtkEntry *entry, gpointer data);
111 static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data);
112 
pythongiInit(void)113 gboolean pythongiInit(void)
114 {
115   GHashTable *options;
116   ToolOption *opt;
117   const gchar *file;
118   VisuConfigFileEntry *entry;
119 
120   DBG_fprintf(stderr, "Plugin PythonGI: initialisation.\n");
121 
122   iconPath = g_build_filename(V_SIM_PIXMAPS_DIR, "pythongi.png", NULL);
123 
124   initScripts = gtk_list_store_new(N_SCRIPTS, G_TYPE_STRING, G_TYPE_STRING);
125   pyTextBuffer = gtk_text_buffer_new((GtkTextTagTable*)0);
126   monoTag = gtk_text_buffer_create_tag(pyTextBuffer, "typewriter",
127                                        "family", "monospace", NULL);
128   errorTag = gtk_text_buffer_create_tag(pyTextBuffer, "error",
129                                         "foreground", "Tomato", NULL);
130   boldTag = gtk_text_buffer_create_tag(pyTextBuffer, "bold",
131                                        "weight", PANGO_WEIGHT_BOLD, NULL);
132   history = curHist = (GList*)0;
133 
134   isPythonInitialised = FALSE;
135   options = commandLineGet_options();
136   if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScriptInit")))
137     {
138       DBG_fprintf(stderr, "Plugin PythonGI: will run init script.\n");
139       initialisePython();
140       file = g_value_get_string(tool_option_getValue(opt));
141       addInitScript(file, TRUE, (GtkWindow*)0);
142     }
143   if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
144     {
145       DBG_fprintf(stderr, "Plugin PythonGI: will run py script.\n");
146       initialisePython();
147       file = g_value_get_string(tool_option_getValue(opt));
148       g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)file, NULL);
149     }
150 
151   visu_config_file_addKnownTag("python");
152   entry = visu_config_file_addEntry(VISU_CONFIG_FILE_PARAMETER,
153                                    FLAG_PARAMETER_INIT_SCRIPTS,
154                                    DESC_PARAMETER_INIT_SCRIPTS,
155                                     1, readInitScripts);
156   visu_config_file_entry_setVersion(entry, 3.7f);
157   visu_config_file_addExportFunction(VISU_CONFIG_FILE_PARAMETER,
158                                     exportParameters);
159 
160   return TRUE;
161 }
pythongiInitGtk(void)162 gboolean pythongiInitGtk(void)
163 {
164   /* Long description */
165   gchar *cl = _("Python scripting");
166   /* Short description */
167   gchar *tl = _("Python");
168   GHashTable *options;
169   ToolOption *opt;
170   const gchar *file;
171 
172   isPanelInitialised = FALSE;
173 
174   panelPython = visu_ui_panel_newWithIconFromPath("Panel_python", cl,
175                                                   tl, "stock-pythongi_20.png");
176   visu_ui_panel_setDockable(VISU_UI_PANEL(panelPython), TRUE);
177   visu_ui_panel_attach(VISU_UI_PANEL(panelPython), visu_ui_panel_class_getCommandPanel());
178   g_signal_connect(G_OBJECT(panelPython), "page-entered",
179 		   G_CALLBACK(onPanelEnter), (gpointer)0);
180 
181   options = commandLineGet_options();
182   if (options && (opt = (ToolOption*)g_hash_table_lookup(options, "pyScript")))
183     {
184       initialisePanel(VISU_UI_PANEL(panelPython));
185       file = g_value_get_string(tool_option_getValue(opt));
186       gtk_file_chooser_select_filename(GTK_FILE_CHOOSER(pyFileChooser), file);
187       gtk_widget_set_sensitive(pyExecute, TRUE);
188     }
189 
190   return TRUE;
191 }
pythongiGet_description(void)192 const char* pythongiGet_description(void)
193 {
194   return PYTHONGI_DESCRIPTION;
195 }
196 
pythongiGet_authors(void)197 const char* pythongiGet_authors(void)
198 {
199   return PYTHONGI_AUTHORS;
200 }
201 
pythongiGet_icon(void)202 const char* pythongiGet_icon(void)
203 {
204   return iconPath;
205 }
206 
pythongiFree(void)207 void pythongiFree(void)
208 {
209   DBG_fprintf(stderr, "Panel PythonGI: finalise.\n");
210 
211   g_free(iconPath);
212   g_object_unref(initScripts);
213 
214   if (!isPythonInitialised)
215     return;
216 
217   Py_Finalize();
218 }
219 
onPanelEnter(VisuUiPanel * panel,gpointer data _U_)220 static void onPanelEnter(VisuUiPanel *panel, gpointer data _U_)
221 {
222   DBG_fprintf(stderr, "Panel PythonGI: caught the 'page-entered' signal %d.\n",
223 	      isPanelInitialised);
224   if (!isPythonInitialised)
225     initialisePython();
226   if (!isPanelInitialised)
227     initialisePanel(panel);
228 }
initialisePython()229 static void initialisePython()
230 {
231   if (isPythonInitialised)
232     return;
233 
234   DBG_fprintf(stderr, "Panel PythonGI: initialise Python.\n");
235   Py_SetProgramName((char*)commandLineGet_programName());
236   Py_Initialize();
237   DBG_fprintf(stderr, "Panel PythonGI: import sys.\n"
238               "import os;\n"
239               "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\")\n");
240   PyRun_SimpleString("import os;"
241                      "os.putenv(\"GI_TYPELIB_PATH\", \"" VISU_TYPELIBS_DIR "\");"
242                      "import sys;"
243                      "sys.argv = ['']\n"
244                      "from gi.repository import v_sim");
245   DBG_fprintf(stderr, " | done.\n");
246 
247   isPythonInitialised = TRUE;
248 }
addFilters(GtkWidget * wd)249 static void addFilters(GtkWidget *wd)
250 {
251   GtkFileFilter *flt;
252 
253   flt = gtk_file_filter_new();
254   gtk_file_filter_set_name(flt, _("Python scripts for V_Sim"));
255   gtk_file_filter_add_mime_type(flt, "text/x-script.python");
256   gtk_file_filter_add_mime_type(flt, "text/x-python");
257   gtk_file_filter_add_mime_type(flt, "application/x-python");
258   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
259   flt = gtk_file_filter_new();
260   gtk_file_filter_set_name(flt, _("All"));
261   gtk_file_filter_add_pattern(flt, "*");
262   gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(wd), flt);
263 }
initialisePanel(VisuUiPanel * panel)264 static void initialisePanel(VisuUiPanel *panel)
265 {
266   int fd[2];
267   gchar *string;
268   GIOChannel *pyIO;
269   GtkWidget *vbox, *hbox, *wd, *scrolled;
270   GtkTreeViewColumn *column;
271   GtkCellRenderer *renderer;
272   GtkToolItem *item;
273   GtkTreeSelection *selection;
274 
275   g_return_if_fail(!isPanelInitialised);
276 
277   /* Redirect stdout. */
278   if (pipe(fd))
279     g_warning("Cannot create pipes for Python process.");
280   else
281     {
282       string = g_strdup_printf("sys.stdout = os.fdopen(%d, 'w', 0)", fd[1]);
283       DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
284       PyRun_SimpleString(string);
285       g_free(string);
286       pyIO = g_io_channel_unix_new(fd[0]);
287       g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
288       g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(FALSE));
289     }
290   /* Redirect stderr. */
291   if (pipe(fd))
292     g_warning("Cannot create pipes for Python process.");
293   else
294     {
295       string = g_strdup_printf("sys.stderr = os.fdopen(%d, 'w', 0)", fd[1]);
296       DBG_fprintf(stderr, "Panel PythonGI: run '%s'.\n", string);
297       PyRun_SimpleString(string);
298       g_free(string);
299       pyIO = g_io_channel_unix_new(fd[0]);
300       g_io_channel_set_flags(pyIO, G_IO_FLAG_NONBLOCK, NULL);
301       g_io_add_watch(pyIO, G_IO_IN | G_IO_PRI, onPyIO, GINT_TO_POINTER(TRUE));
302     }
303 
304   vbox = gtk_vbox_new(FALSE, 0);
305   gtk_container_set_border_width(GTK_CONTAINER(panel), 5);
306   gtk_container_add(GTK_CONTAINER(panel), vbox);
307 
308   wd = gtk_label_new(_("<b>Scripts loaded on startup</b>"));
309   gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
310   gtk_label_set_xalign(GTK_LABEL(wd), 0.);
311   gtk_widget_set_name(wd, "label_head");
312   gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
313 
314   hbox = gtk_hbox_new(FALSE, 0);
315   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
316 
317   scrolled = gtk_scrolled_window_new(NULL, NULL);
318   gtk_widget_set_size_request(scrolled, -1, 100);
319   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
320 				 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
321   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
322 				      GTK_SHADOW_ETCHED_IN);
323   gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
324 
325   wd = gtk_tree_view_new_with_model(GTK_TREE_MODEL(initScripts));
326   gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(wd), FALSE);
327   gtk_widget_set_tooltip_text(wd,
328                               _("This list is saved with the parameter file."));
329   gtk_container_add(GTK_CONTAINER(scrolled), wd);
330 
331   renderer = gtk_cell_renderer_text_new();
332   column = gtk_tree_view_column_new_with_attributes("", renderer,
333                                                     "text", SCRIPT_LABEL,
334                                                     NULL);
335   gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
336   renderer = gtk_cell_renderer_text_new();
337   g_object_set(G_OBJECT(renderer), "scale", 0.75, NULL);
338   column = gtk_tree_view_column_new_with_attributes("", renderer,
339                                                     "text", SCRIPT_PATH,
340                                                     NULL);
341   gtk_tree_view_append_column(GTK_TREE_VIEW(wd), column);
342   selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(wd));
343 
344   wd = gtk_toolbar_new();
345   gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
346   gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
347   gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
348   gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
349 
350   item = gtk_tool_button_new(NULL, NULL);
351   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-add");
352   g_signal_connect(G_OBJECT(item), "clicked",
353                    G_CALLBACK(onInitScriptAdded), (gpointer)0);
354   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
355   item = gtk_tool_button_new(NULL, NULL);
356   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "list-remove");
357   g_signal_connect(G_OBJECT(item), "clicked",
358                    G_CALLBACK(onInitScriptRemoved), (gpointer)selection);
359   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
360   item = gtk_tool_button_new(NULL, NULL);
361   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "view-refresh");
362   g_signal_connect(G_OBJECT(item), "clicked",
363                    G_CALLBACK(onInitScriptReload), (gpointer)selection);
364   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
365 
366   wd = gtk_label_new(_("<b>Interactive scripting</b>"));
367   gtk_label_set_use_markup(GTK_LABEL(wd), TRUE);
368   gtk_label_set_xalign(GTK_LABEL(wd), 0.);
369   gtk_widget_set_name(wd, "label_head");
370   gtk_box_pack_start(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
371 
372   hbox = gtk_hbox_new(FALSE, 3);
373   gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 3);
374   wd = gtk_label_new(_("Load:"));
375   gtk_widget_set_margin_start(wd, 5);
376   gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
377   pyFileChooser = gtk_file_chooser_button_new(_("Choose a Python script"),
378 					      GTK_FILE_CHOOSER_ACTION_OPEN);
379   addFilters(pyFileChooser);
380   gtk_box_pack_start(GTK_BOX(hbox), pyFileChooser, TRUE, TRUE, 0);
381   pyExecute = gtk_button_new_from_icon_name("system-run", GTK_ICON_SIZE_SMALL_TOOLBAR);
382   gtk_widget_set_margin_end(pyExecute, 5);
383   gtk_widget_set_sensitive(pyExecute, FALSE);
384   gtk_box_pack_start(GTK_BOX(hbox), pyExecute, FALSE, FALSE, 0);
385 
386   g_signal_connect(G_OBJECT(pyFileChooser), "file-set",
387 		   G_CALLBACK(onScriptChosen), (gpointer)pyExecute);
388   g_signal_connect(G_OBJECT(pyExecute), "clicked",
389 		   G_CALLBACK(onScriptExecute), (gpointer)pyFileChooser);
390 
391   hbox = gtk_hbox_new(FALSE, 0);
392   gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 3);
393 
394   scrolled = gtk_scrolled_window_new(NULL, NULL);
395   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled),
396         			 GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
397   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled),
398         			      GTK_SHADOW_ETCHED_IN);
399   gtk_box_pack_start(GTK_BOX(hbox), scrolled, TRUE, TRUE, 0);
400 
401   pyTextView = gtk_text_view_new_with_buffer(pyTextBuffer);
402   g_object_unref(pyTextBuffer);
403   gtk_text_view_set_editable(GTK_TEXT_VIEW(pyTextView), FALSE);
404   gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(pyTextView), FALSE);
405   gtk_container_add(GTK_CONTAINER(scrolled), pyTextView);
406 #if GTK_MAJOR_VERSION >= 3
407   g_signal_connect(G_OBJECT(gtk_scrollable_get_vadjustment(GTK_SCROLLABLE(pyTextView))),
408                    "changed", G_CALLBACK(onVScroll), (gpointer)0);
409 #endif
410 
411   wd = gtk_toolbar_new();
412   gtk_orientable_set_orientation(GTK_ORIENTABLE(wd), GTK_ORIENTATION_VERTICAL);
413   gtk_toolbar_set_style(GTK_TOOLBAR(wd), GTK_TOOLBAR_ICONS);
414   gtk_toolbar_set_icon_size(GTK_TOOLBAR(wd), GTK_ICON_SIZE_SMALL_TOOLBAR);
415   gtk_box_pack_start(GTK_BOX(hbox), wd, FALSE, FALSE, 0);
416 
417   item = gtk_tool_button_new(NULL, NULL);
418   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-clear");
419   g_signal_connect(G_OBJECT(item), "clicked",
420                    G_CALLBACK(onOutputCleared), (gpointer)0);
421   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
422   gtk_widget_set_tooltip_text(GTK_WIDGET(item), _("Clear Python output."));
423   item = gtk_tool_button_new(NULL, NULL);
424   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "process-stop");
425   gtk_widget_set_sensitive(GTK_WIDGET(item), FALSE);
426   g_signal_connect(G_OBJECT(item), "clicked",
427                    G_CALLBACK(onOutputCancel), (gpointer)0);
428   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
429   item = gtk_tool_button_new(NULL, NULL);
430   gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(item), "edit-find");
431   gtk_toolbar_insert(GTK_TOOLBAR(wd), item, -1);
432   gtk_widget_set_tooltip_text(GTK_WIDGET(item),
433                               _("Get the current VisuData object as 'data'"
434                                 " variable and the current VisuGlView as 'view'."));
435 
436   wd = gtk_entry_new();
437 #if GTK_MAJOR_VERSION > 3 || (GTK_MAJOR_VERSION == 3 && GTK_MINOR_VERSION > 1)
438   gtk_entry_set_placeholder_text(GTK_ENTRY(wd), _("Python interactive command line"));
439 #endif
440   g_signal_connect(G_OBJECT(wd), "activate",
441                    G_CALLBACK(onInteractiveEntry), (gpointer)0);
442   g_signal_connect(G_OBJECT(wd), "key-press-event",
443                    G_CALLBACK(onKeyPressed), (gpointer)0);
444   g_signal_connect(G_OBJECT(item), "clicked",
445                    G_CALLBACK(onOutputGetData), (gpointer)wd);
446   gtk_box_pack_end(GTK_BOX(vbox), wd, FALSE, FALSE, 0);
447 
448   gtk_widget_show_all(vbox);
449 
450   isPanelInitialised = TRUE;
451 }
452 
onScriptChosen(GtkFileChooserButton * widget,gpointer user_data)453 static void onScriptChosen(GtkFileChooserButton *widget, gpointer user_data)
454 {
455   gchar *file;
456 
457   file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(widget));
458   gtk_widget_set_sensitive(GTK_WIDGET(user_data), (file != (gchar*)0));
459   if (file)
460     g_free(file);
461 }
onScriptExecute(GtkButton * widget _U_,gpointer user_data)462 static void onScriptExecute(GtkButton *widget _U_, gpointer user_data)
463 {
464   gchar *file;
465 
466   file = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(user_data));
467   g_return_if_fail(file);
468 
469   g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, loadScript, (gpointer)file, g_free);
470 }
471 
472 
loadScript(gpointer file)473 static gboolean loadScript(gpointer file)
474 {
475   gchar *script, *label, *name;
476   GError *error;
477 
478   DBG_fprintf(stderr, "Panel PythonGI: load script '%s'.\n", (gchar*)file);
479 
480   g_return_val_if_fail(isPythonInitialised, FALSE);
481 
482   error = (GError*)0;
483   if (!g_file_get_contents((gchar*)file, &script, (gsize*)0, &error))
484     {
485       if (error)
486         {
487           visu_ui_raiseWarning(_("Load a Python script"), error->message,
488                                visu_ui_panel_getContainerWindow
489                                (VISU_UI_PANEL(panelPython)));
490           g_error_free(error);
491         }
492       return FALSE;
493     }
494 
495   name = g_path_get_basename((const gchar*)file);
496   label = g_strdup_printf(_("Load script \"%s\"\n"), name);
497   g_free(name);
498   _bufSetText(label, boldTag, FALSE);
499   g_free(label);
500 
501   PyRun_SimpleString(script);
502   g_free(script);
503 
504   return FALSE;
505 }
506 
addInitScript(const gchar * file,gboolean immediate,GtkWindow * parent)507 static void addInitScript(const gchar *file, gboolean immediate, GtkWindow *parent)
508 {
509   GtkTreeIter iter;
510   gchar *path, *name;
511 
512   if (!g_file_test(file, G_FILE_TEST_IS_REGULAR))
513     {
514       if (parent)
515         visu_ui_raiseWarning(_("Choose a Python script"),
516                              _("Not a regular file."), parent);
517       return;
518     }
519 
520   path = tool_path_normalize(file);
521   name = g_path_get_basename(file);
522   gtk_list_store_append(initScripts, &iter);
523   gtk_list_store_set(initScripts, &iter, SCRIPT_PATH, path, SCRIPT_LABEL, name, -1);
524   g_free(name);
525 
526   if (immediate)
527     {
528       loadScript((gpointer)path);
529       g_free(path);
530     }
531   else
532     g_idle_add_full(G_PRIORITY_LOW, loadScript, (gpointer)path, g_free);
533 }
onInitScriptAdded(GtkToolButton * bt _U_,gpointer data _U_)534 static void onInitScriptAdded(GtkToolButton *bt _U_, gpointer data _U_)
535 {
536   GtkWidget *dialog;
537   gchar *filename;
538   GtkWindow *parent;
539 
540   parent = visu_ui_panel_getContainerWindow(VISU_UI_PANEL(panelPython));
541   dialog = gtk_file_chooser_dialog_new(_("Choose a Python script"), parent,
542                                        GTK_FILE_CHOOSER_ACTION_OPEN,
543                                        TOOL_ICON_CANCEL, GTK_RESPONSE_CANCEL,
544                                        TOOL_ICON_OPEN, GTK_RESPONSE_ACCEPT,
545                                        NULL);
546   addFilters(dialog);
547 
548   if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
549     {
550       filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
551       addInitScript(filename, FALSE, parent);
552       g_free(filename);
553     }
554 
555   gtk_widget_destroy(dialog);
556 }
onInitScriptRemoved(GtkToolButton * bt _U_,gpointer data)557 static void onInitScriptRemoved(GtkToolButton *bt _U_, gpointer data)
558 {
559   gboolean valid;
560   GtkTreeModel *model;
561   GtkTreeIter iter;
562 
563   valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
564   if (!valid)
565     return;
566 
567   gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
568 }
onInitScriptReload(GtkToolButton * bt _U_,gpointer data)569 static void onInitScriptReload(GtkToolButton *bt _U_, gpointer data)
570 {
571   gboolean valid;
572   GtkTreeModel *model;
573   GtkTreeIter iter;
574   gchar *path;
575 
576   valid = gtk_tree_selection_get_selected(GTK_TREE_SELECTION(data), &model, &iter);
577   if (!valid)
578     return;
579 
580   gtk_tree_model_get(model, &iter, SCRIPT_PATH, &path, -1);
581   loadScript((gpointer)path);
582   g_free(path);
583 }
onPyIO(GIOChannel * source,GIOCondition condition _U_,gpointer data)584 static gboolean onPyIO(GIOChannel *source,
585                        GIOCondition condition _U_, gpointer data)
586 {
587 #define size 256
588   gchar string[size];
589   gsize lg;
590   GError *error;
591 
592   error = (GError*)0;
593   /* DBG_fprintf(stderr, "Panel PythonGI: receive buffer data.\n"); */
594   if (g_io_channel_read_chars(source, string, size, &lg, &error) !=   G_IO_STATUS_NORMAL)
595     {
596       g_warning("%s", error->message);
597       g_error_free(error);
598       return TRUE;
599     }
600   string[lg] = '\0';
601   /* DBG_fprintf(stderr, "Panel PythonGI: got\n%s\n", string); */
602 
603   _bufSetText(string, monoTag, GPOINTER_TO_INT(data));
604 
605   /* Flush Gtk events. */
606   visu_ui_wait();
607 
608   return TRUE;
609 }
_bufSetText(const gchar * text,GtkTextTag * tag,gboolean error)610 static void _bufSetText(const gchar *text, GtkTextTag *tag, gboolean error)
611 {
612   GtkTextIter iter;
613 
614   gtk_text_buffer_get_end_iter(pyTextBuffer, &iter);
615   if (!error)
616     gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, NULL);
617   else
618     gtk_text_buffer_insert_with_tags(pyTextBuffer, &iter, text, -1, tag, errorTag, NULL);
619 }
onOutputCleared(GtkToolButton * bt _U_,gpointer data _U_)620 static void onOutputCleared(GtkToolButton *bt _U_, gpointer data _U_)
621 {
622   GtkTextIter start, end;
623 
624   gtk_text_buffer_get_bounds(pyTextBuffer, &start, &end);
625   gtk_text_buffer_delete(pyTextBuffer, &start, &end);
626 }
onOutputCancel(GtkToolButton * bt _U_,gpointer data _U_)627 static void onOutputCancel(GtkToolButton *bt _U_, gpointer data _U_)
628 {
629   DBG_fprintf(stderr, "Panel PythonGI: try to interrupt.\n");
630   PyErr_SetString(PyExc_TypeError, "script interrupted.");
631 }
onOutputGetData(GtkToolButton * bt _U_,gpointer data)632 static void onOutputGetData(GtkToolButton *bt _U_, gpointer data)
633 {
634   DBG_fprintf(stderr, "Panel PythonGI: get current data.\n");
635   gtk_entry_set_text(GTK_ENTRY(data), "scene = v_sim.UiMainClass.getDefaultRendering().getGlScene()");
636   g_signal_emit_by_name(G_OBJECT(data), "activate");
637   gtk_entry_set_text(GTK_ENTRY(data), "data = scene.getData()");
638   g_signal_emit_by_name(G_OBJECT(data), "activate");
639   gtk_entry_set_text(GTK_ENTRY(data), "view = scene.getGlView()");
640   g_signal_emit_by_name(G_OBJECT(data), "activate");
641   gtk_entry_set_text(GTK_ENTRY(data), "selection = v_sim.ui_interactive_pick_getSelection()");
642   g_signal_emit_by_name(G_OBJECT(data), "activate");
643 }
644 #if GTK_MAJOR_VERSION >= 3
onVScroll(GtkAdjustment * adj,gpointer data _U_)645 static void onVScroll(GtkAdjustment *adj, gpointer data _U_)
646 {
647   guint oldUpper;
648 
649   oldUpper = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(adj), "old-upper"));
650   if (gtk_adjustment_get_value(adj) + gtk_adjustment_get_page_size(adj) == oldUpper)
651     /* Scroll to the end. */
652     gtk_adjustment_set_value(adj, gtk_adjustment_get_upper(adj) -
653                              gtk_adjustment_get_page_size(adj));
654   g_object_set_data(G_OBJECT(adj), "old-upper",
655                     GINT_TO_POINTER(gtk_adjustment_get_upper(adj)));
656 }
657 #endif
onInteractiveEntry(GtkEntry * entry,gpointer data _U_)658 static void onInteractiveEntry(GtkEntry *entry, gpointer data _U_)
659 {
660   _bufSetText("> ", NULL, FALSE);
661   _bufSetText(gtk_entry_get_text(entry), monoTag, FALSE);
662   _bufSetText("\n", NULL, FALSE);
663   PyRun_SimpleString(gtk_entry_get_text(entry));
664   history = g_list_prepend(history, g_strdup(gtk_entry_get_text(entry)));
665   curHist = (GList*)0;
666   gtk_entry_set_text(entry, "");
667 }
onKeyPressed(GtkWidget * widget,GdkEventKey * event,gpointer data _U_)668 static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *event, gpointer data _U_)
669 {
670   if (event->keyval != GDK_KEY_Up &&
671       event->keyval != GDK_KEY_Down)
672     return FALSE;
673 
674   if (event->keyval == GDK_KEY_Up)
675     curHist = (curHist)?((curHist->next)?curHist->next:curHist):history;
676   else if (event->keyval == GDK_KEY_Down && curHist)
677     curHist = curHist->prev;
678 
679   gtk_entry_set_text(GTK_ENTRY(widget), (curHist)?(const gchar*)curHist->data:"");
680   gtk_editable_set_position(GTK_EDITABLE(widget), -1);
681 
682   return TRUE;
683 }
684 
685 /**************/
686 /* Parameters */
687 /**************/
readInitScripts(VisuConfigFileEntry * entry _U_,gchar ** lines,int nbLines,int position _U_,GError ** error _U_)688 static gboolean readInitScripts(VisuConfigFileEntry *entry _U_, gchar **lines,
689                                 int nbLines, int position _U_, GError **error _U_)
690 {
691   gchar **paths;
692   guint i;
693 
694   g_return_val_if_fail(nbLines == 1, FALSE);
695 
696   DBG_fprintf(stderr, "Plugin PythonGI: initialisation scripts.\n");
697 
698   initialisePython();
699 
700   paths = g_strsplit_set(lines[0], ":\n", 100);
701   for (i = 0; paths[i]; i++)
702     {
703       g_strstrip(paths[i]);
704       DBG_fprintf(stderr, "Panel PythonGI: test file '%s'.\n", paths[i]);
705       addInitScript(paths[i], TRUE, (GtkWindow*)0);
706     }
707   g_strfreev(paths);
708 
709   return TRUE;
710 }
exportParameters(GString * data,VisuData * dataObj _U_)711 static void exportParameters(GString *data, VisuData *dataObj _U_)
712 {
713   gboolean valid;
714   GtkTreeIter iter;
715   gchar *file;
716 
717   g_string_append_printf(data, "# %s\n", DESC_PARAMETER_INIT_SCRIPTS);
718       g_string_append_printf(data, "%s[python]: ", FLAG_PARAMETER_INIT_SCRIPTS);
719   for (valid = gtk_tree_model_get_iter_first(GTK_TREE_MODEL(initScripts), &iter);
720        valid; valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(initScripts), &iter))
721     {
722       gtk_tree_model_get(GTK_TREE_MODEL(initScripts), &iter, SCRIPT_PATH, &file, -1);
723       g_string_append_printf(data, "%s:", file);
724       g_free(file);
725     }
726   g_string_append_printf(data, "\n\n");
727 }
728