[anjuta] gdb: Add support for pretty printers (gdb > 7.0)



commit 37f55f6b57a4718f96f930def8f05ad13cbf9825
Author: Sébastien Granjoux <seb sfo free fr>
Date:   Sun Jun 27 12:58:04 2010 +0200

    gdb: Add support for pretty printers (gdb > 7.0)

 libanjuta/interfaces/libanjuta.idl |    1 +
 plugins/debug-manager/debug_tree.c |    4 +-
 plugins/gdb/Makefile.am            |    6 +-
 plugins/gdb/anjuta-gdb.plugin.in   |    2 +-
 plugins/gdb/anjuta-gdb.ui          |  124 ++++++++++
 plugins/gdb/debugger.c             |  122 +++++++++-
 plugins/gdb/debugger.h             |   11 +-
 plugins/gdb/plugin.c               |   66 +++++-
 plugins/gdb/preferences.c          |  467 ++++++++++++++++++++++++++++++++++++
 plugins/gdb/preferences.h          |   48 ++++
 plugins/js-debugger/debugger-js.c  |    2 +
 11 files changed, 832 insertions(+), 21 deletions(-)
---
diff --git a/libanjuta/interfaces/libanjuta.idl b/libanjuta/interfaces/libanjuta.idl
index edcc4ae..a70920a 100644
--- a/libanjuta/interfaces/libanjuta.idl
+++ b/libanjuta/interfaces/libanjuta.idl
@@ -4560,6 +4560,7 @@ interface IAnjutaDebugger
 			gboolean exited;
 			gboolean deleted;
 			gint children;
+			gboolean has_more;
 		}
 
 		/**
diff --git a/plugins/debug-manager/debug_tree.c b/plugins/debug-manager/debug_tree.c
index f270def..ed196df 100644
--- a/plugins/debug-manager/debug_tree.c
+++ b/plugins/debug-manager/debug_tree.c
@@ -585,7 +585,7 @@ gdb_var_create (IAnjutaDebuggerVariableObject *variable, gpointer user_data, GEr
 					   VALUE_COLUMN, variable->value, -1);
 	
 
-	if (variable->children == -1)
+	if ((variable->children == -1) || variable->has_more)
 	{
 		/* Find the number of children */
 		DmaVariablePacket *pack_child =
@@ -985,7 +985,7 @@ debug_tree_add_watch (DebugTree *tree, const IAnjutaDebuggerVariableObject* var,
 							(IAnjutaDebuggerCallback)gdb_var_evaluate_expression,
 							pack);
 				}
-				if (var->children == -1)
+				if ((var->children == -1) || var->has_more)
 				{
 					/* Get number of children */
 					DmaVariablePacket *pack =
diff --git a/plugins/gdb/Makefile.am b/plugins/gdb/Makefile.am
index 1ea0faf..22e7c5b 100644
--- a/plugins/gdb/Makefile.am
+++ b/plugins/gdb/Makefile.am
@@ -4,7 +4,7 @@ gdb_ui_DATA =
 
 # Plugin glade file
 anjuta_gladedir = $(anjuta_glade_dir)
-anjuta_glade_DATA =
+anjuta_glade_DATA = anjuta-gdb.ui
 
 anjuta_datadir = $(anjuta_data_dir)
 anjuta_data_DATA = gdb.init
@@ -43,7 +43,9 @@ libanjuta_gdb_la_SOURCES = \
 	plugin.c \
 	plugin.h \
 	utilities.c \
-	utilities.h
+	utilities.h \
+	preferences.c \
+	preferences.h
 
 noinst_PROGRAMS = gdbmi-test
 gdbmi_test_SOURCES = gdbmi-test.c gdbmi.c gdbmi.h debugger.h
diff --git a/plugins/gdb/anjuta-gdb.plugin.in b/plugins/gdb/anjuta-gdb.plugin.in
index f13d5ed..437711f 100644
--- a/plugins/gdb/anjuta-gdb.plugin.in
+++ b/plugins/gdb/anjuta-gdb.plugin.in
@@ -3,7 +3,7 @@ _Name=Gdb
 _Description=GNU Debugger Plugin
 Location=anjuta-gdb:GdbPlugin
 Icon=anjuta-gdb.plugin.png
-UserActivatable=no
+UserActivatable=yes
 Interfaces=IAnjutaDebugger
 
 [File Loader]
diff --git a/plugins/gdb/anjuta-gdb.ui b/plugins/gdb/anjuta-gdb.ui
new file mode 100644
index 0000000..369da85
--- /dev/null
+++ b/plugins/gdb/anjuta-gdb.ui
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkWindow" id="gdb_preferences_window">
+    <child>
+      <object class="GtkVBox" id="gdb_preferences_container">
+        <property name="visible">True</property>
+        <signal name="destroy" handler="gdb_on_destroy_preferences" swapped="no"/>
+        <child>
+          <object class="GtkFrame" id="frame2">
+            <property name="visible">True</property>
+            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="left_padding">12</property>
+                <child>
+                  <object class="GtkHBox" id="hbox1">
+                    <property name="visible">True</property>
+                    <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkScrolledWindow" id="scrolledwindow1">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="hscrollbar_policy">automatic</property>
+                        <property name="vscrollbar_policy">automatic</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="printers_treeview">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK</property>
+                            <signal name="row-activated" handler="atp_on_tool_activated" swapped="no"/>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">True</property>
+                        <property name="fill">True</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkVButtonBox" id="vbuttonbox1">
+                        <property name="visible">True</property>
+                        <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                        <property name="border_width">5</property>
+                        <property name="spacing">6</property>
+                        <property name="layout_style">start</property>
+                        <child>
+                          <object class="GtkButton" id="add_button">
+                            <property name="label">gtk-add</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_stock">True</property>
+                            <signal name="clicked" handler="gdb_on_printer_add" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="remove_button">
+                            <property name="label">gtk-remove</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                            <property name="use_action_appearance">False</property>
+                            <property name="use_stock">True</property>
+                            <signal name="clicked" handler="gdb_on_printer_remove" swapped="no"/>
+                          </object>
+                          <packing>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="fill">True</property>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                </child>
+              </object>
+            </child>
+            <child type="label">
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
+                <property name="ypad">6</property>
+                <property name="label" translatable="yes">Available pretty printers</property>
+                <attributes>
+                  <attribute name="weight" value="bold"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/plugins/gdb/debugger.c b/plugins/gdb/debugger.c
index 94a9b9b..140d70b 100644
--- a/plugins/gdb/debugger.c
+++ b/plugins/gdb/debugger.c
@@ -135,6 +135,9 @@ struct _DebuggerPriv
 	gboolean has_python_support;
 	gboolean has_thread_info;
 	gboolean has_frozen_varobjs;
+	
+	/* Pretty printers command */
+	gchar *load_pretty_printer;
 };
 
 static gpointer parent_class;
@@ -303,6 +306,8 @@ debugger_initialize (Debugger *debugger)
 	debugger->priv->gdb_log = anjuta_log && (atoi(anjuta_log) > DEBUGGER_LOG_LEVEL);
 
 	debugger->priv->environment = NULL;
+	
+	debugger->priv->load_pretty_printer = NULL;
 }
 
 static void
@@ -809,6 +814,74 @@ debugger_set_environment (Debugger *debugger, gchar **variables)
 	return TRUE;
 }
 
+gboolean
+debugger_set_pretty_printers (Debugger *debugger, const GList *pretty_printers)
+{
+	GString *load = g_string_new (NULL);
+	GList *item;
+	GList *directories = NULL;
+
+	/* Unload previous pretty printers */
+	g_free (debugger->priv->load_pretty_printer);
+
+	/* Get all necessary directories */
+	for (item = g_list_first ((GList *)pretty_printers); item != NULL; item = g_list_next (item))
+	{
+		GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
+		gchar *dir;
+		
+		if (printer->enable)
+		{
+			dir = g_path_get_dirname (printer->path);
+			if (g_list_find_custom (directories, dir, (GCompareFunc)strcmp) == NULL)
+			{
+				directories = g_list_prepend (directories, dir);
+			}
+			else
+			{
+				g_free (dir);
+			}
+		}
+	}
+	/* Add them in the command */
+	if (directories != NULL)
+	{
+		g_string_append (load, "python\nimport sys\n");
+
+		for (item = g_list_first (directories); item != NULL; item = g_list_next (item))
+		{
+			g_string_append_printf (load, "sys.path.insert(0,'%s')\n", (gchar *)item->data);
+			g_free (item->data);
+		}
+		g_list_free (directories);
+
+		/* Import all modules and call register function*/
+		for (item = g_list_first ((GList *)pretty_printers); item != NULL; item = g_list_next (item))
+		{
+			GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
+			gchar *name;
+
+			if (printer->enable && (printer->function != NULL))
+			{
+				/* Remove .py extension */
+				name = g_path_get_basename (printer->path);
+				if (g_str_has_suffix (name, ".py"))
+				{
+					name[strlen (name) - 3] = '\0';
+				}
+
+				if (printer->function != NULL)
+				g_string_append_printf (load, "import %s\n%s.%s(None)\n", name, name, printer->function);
+			}
+		}
+		g_string_append (load, "end");
+	}
+
+	debugger->priv->load_pretty_printer = g_string_free (load, FALSE);
+
+	return TRUE;
+}
+
 static void
 debugger_list_features_completed (Debugger *debugger,
 									const GDBMIValue *mi_result,
@@ -859,6 +932,12 @@ debugger_list_features_completed (Debugger *debugger,
 	{
 		debugger_queue_command (debugger, "set stop-on-solib-events 1", DEBUGGER_COMMAND_PREPEND, NULL, NULL, NULL);
 	}
+
+	if (debugger->priv->has_python_support && (debugger->priv->load_pretty_printer != NULL))
+	{
+		debugger_queue_command (debugger, debugger->priv->load_pretty_printer, 0, NULL, NULL, NULL);
+		debugger_queue_command (debugger, "-enable-pretty-printing", 0, NULL, NULL, NULL);
+	}
 }
 
 static gboolean
@@ -874,8 +953,10 @@ debugger_list_features (Debugger *debugger)
 }
 
 gboolean
-debugger_start (Debugger *debugger, const GList *search_dirs,
-				const gchar *prog, gboolean is_libtool_prog)
+debugger_start (Debugger *debugger,
+				const GList *search_dirs,
+				const gchar *prog,
+				gboolean is_libtool_prog)
 {
 	gchar *command_str, *dir, *tmp, *text, *msg;
 	gboolean ret;
@@ -943,7 +1024,7 @@ debugger_start (Debugger *debugger, const GList *search_dirs,
 		node = g_list_next (node);
 	}
 	
-	/* Now save the dir list. Order is automatically revesed */
+	/* Now save the dir list. Order is automatically reversed */
 	node = dir_list;
 	while (node)
 	{
@@ -952,7 +1033,7 @@ debugger_start (Debugger *debugger, const GList *search_dirs,
 		node = g_list_next (node);
 	}
 	g_list_free (dir_list);
-	
+
 	if (prog && strlen(prog) > 0)
 	{
 		gchar *quoted_prog = gdb_quote (prog);
@@ -1085,7 +1166,7 @@ debugger_start (Debugger *debugger, const GList *search_dirs,
 	debugger_list_features (debugger);
 
 	debugger_queue_command (debugger, "handle SIGINT stop print nopass", 0, NULL, NULL, NULL);
-	
+
 	return TRUE;
 }
 
@@ -3798,31 +3879,35 @@ gdb_var_list_children (Debugger *debugger,
 		for(i = 0 ; i < numchild; ++i)
 		{
 			const GDBMIValue *const gdbmi_chl = 
-                                gdbmi_value_list_get_nth (children, i);
+							gdbmi_value_list_get_nth (children, i);
 			IAnjutaDebuggerVariableObject *var;
-		
+
 			var = g_new0 (IAnjutaDebuggerVariableObject, 1);
 
-		       	literal  = gdbmi_value_hash_lookup (gdbmi_chl, "name");
+			literal  = gdbmi_value_hash_lookup (gdbmi_chl, "name");
 			if (literal)
-  				var->name = (gchar *)gdbmi_value_literal_get (literal);
+				var->name = (gchar *)gdbmi_value_literal_get (literal);
 
 			literal = gdbmi_value_hash_lookup (gdbmi_chl, "exp");
 			if (literal)
-				var->expression = (gchar *)gdbmi_value_literal_get(literal);
-                
+			var->expression = (gchar *)gdbmi_value_literal_get(literal);
+
 			literal = gdbmi_value_hash_lookup (gdbmi_chl, "type");
 			if (literal)
 				var->type = (gchar *)gdbmi_value_literal_get(literal);
 
-        		literal = gdbmi_value_hash_lookup (gdbmi_chl, "value");
+			literal = gdbmi_value_hash_lookup (gdbmi_chl, "value");
 			if (literal)
 				var->value = (gchar *)gdbmi_value_literal_get(literal);
 
-        		literal = gdbmi_value_hash_lookup (gdbmi_chl, "numchild");
+			literal = gdbmi_value_hash_lookup (gdbmi_chl, "numchild");
 			if (literal)
 				var->children = strtoul(gdbmi_value_literal_get(literal), NULL, 10);
 
+			literal = gdbmi_value_hash_lookup (gdbmi_chl, "has_more");
+			if (literal)
+				var->has_more = *gdbmi_value_literal_get(literal) == '1' ? TRUE : FALSE;
+
 			list = g_list_prepend (list, var);
 		}
 		list = g_list_reverse (list);
@@ -3866,6 +3951,16 @@ gdb_var_create (Debugger *debugger,
 
 		result = gdbmi_value_hash_lookup (mi_results, "numchild");
 		var.children = strtoul (gdbmi_value_literal_get(result), NULL, 10);
+
+		result = gdbmi_value_hash_lookup (mi_results, "has_more");
+		if (result != NULL)
+		{
+			var.has_more = *gdbmi_value_literal_get(result) == '1' ? TRUE : FALSE;
+		}
+		else
+		{
+			var.has_more = FALSE;
+		}
 	}
 	callback (&var, user_data, error);
 
@@ -4018,6 +4113,7 @@ debugger_finalize (GObject *obj)
 	g_string_free (debugger->priv->stdo_acc, TRUE);
 	g_string_free (debugger->priv->stde_line, TRUE);
 	g_free (debugger->priv->remote_server);
+	g_free (debugger->priv->load_pretty_printer);
 	g_free (debugger->priv);
 	G_OBJECT_CLASS (parent_class)->finalize (obj);
 }
diff --git a/plugins/gdb/debugger.h b/plugins/gdb/debugger.h
index 5c92567..1640733 100644
--- a/plugins/gdb/debugger.h
+++ b/plugins/gdb/debugger.h
@@ -25,6 +25,8 @@
 #include <gtk/gtk.h>
 #include "gdbmi.h"
 
+#include "preferences.h"
+
 #include <libanjuta/interfaces/ianjuta-message-view.h>
 #include <libanjuta/interfaces/ianjuta-debugger.h>
 
@@ -84,8 +86,13 @@ GType debugger_get_type (void);
 Debugger* debugger_new (GtkWindow *parent_win, GObject* instance);
 void debugger_free (Debugger *debugger);
 
-gboolean debugger_start (Debugger *debugger, const GList *search_dirs,
-							const gchar *prog, gboolean is_libtool_prog);
+gboolean debugger_set_pretty_printers (Debugger *debugger,
+									const GList *pretty_printers);
+
+gboolean debugger_start (Debugger *debugger,
+						const GList *search_dirs,
+						const gchar *prog,
+						gboolean is_libtool_prog);
 
 gboolean debugger_stop (Debugger *debugger);
 gboolean debugger_abort (Debugger *debugger);
diff --git a/plugins/gdb/plugin.c b/plugins/gdb/plugin.c
index 370d9be..20e0637 100644
--- a/plugins/gdb/plugin.c
+++ b/plugins/gdb/plugin.c
@@ -34,6 +34,7 @@
 #include <sys/stat.h>
 
 #include "debugger.h"
+#include "preferences.h"
 
 #include <libanjuta/interfaces/ianjuta-debugger.h>
 #include <libanjuta/interfaces/ianjuta-debugger-breakpoint.h>
@@ -42,6 +43,7 @@
 #include <libanjuta/interfaces/ianjuta-debugger-instruction.h>
 #include <libanjuta/interfaces/ianjuta-debugger-variable.h>
 #include <libanjuta/interfaces/ianjuta-terminal.h>
+#include <libanjuta/interfaces/ianjuta-preferences.h>
 #include <libanjuta/anjuta-plugin.h>
 #include <signal.h>
 
@@ -64,6 +66,9 @@ struct _GdbPlugin
 
 	/* Terminal */
 	pid_t term_pid;
+
+	/* Pretty printer list */
+	GList *pretty_printers;
 };
 
 struct _GdbPluginClass
@@ -234,6 +239,30 @@ gdb_plugin_initialize (GdbPlugin *this)
 	g_signal_connect_swapped (this, "debugger-stopped", G_CALLBACK (on_debugger_stopped), this);
 	debugger_set_output_callback (this->debugger, this->output_callback, this->output_user_data);
 	if (this->view) debugger_set_log (this->debugger, this->view);
+	
+	debugger_set_pretty_printers (this->debugger, this->pretty_printers);
+}
+
+/* Callback for saving session
+ *---------------------------------------------------------------------------*/
+
+static void
+on_session_save (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, GdbPlugin *this)
+{
+	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
+		return;
+
+	gdb_save_pretty_printers (session, this->pretty_printers);
+}
+
+static void on_session_load (AnjutaShell *shell, AnjutaSessionPhase phase, AnjutaSession *session, GdbPlugin *this)
+{
+	if (phase != ANJUTA_SESSION_PHASE_NORMAL)
+		return;
+
+	g_list_foreach (this->pretty_printers, (GFunc)gdb_pretty_printer_free, NULL);
+	g_list_free (this->pretty_printers);
+	this->pretty_printers = gdb_load_pretty_printers (session);
 }
 
 /* AnjutaPlugin functions
@@ -242,10 +271,18 @@ gdb_plugin_initialize (GdbPlugin *this)
 static gboolean
 gdb_plugin_activate_plugin (AnjutaPlugin* plugin)
 {
-	/* GdbPlugin *this = ANJUTA_PLUGIN_GDB (plugin); */
+	GdbPlugin *this = ANJUTA_PLUGIN_GDB (plugin);
 
 	DEBUG_PRINT ("%s", "GDB: Activating Gdb plugin...");
+	this->pretty_printers = NULL;
 
+	/* Connect to session signal */
+	g_signal_connect (plugin->shell, "save-session",
+					  G_CALLBACK (on_session_save), this);
+	g_signal_connect (plugin->shell, "load-session",
+					  G_CALLBACK (on_session_load), this);
+	
+	
 	return TRUE;
 }
 
@@ -262,6 +299,10 @@ gdb_plugin_deactivate_plugin (AnjutaPlugin* plugin)
 		this->debugger = NULL;
 	}
 	
+	g_list_foreach (this->pretty_printers, (GFunc)gdb_pretty_printer_free, NULL);
+	g_list_free (this->pretty_printers);
+	this->pretty_printers = NULL;
+	
 	return TRUE;
 }
 
@@ -1153,6 +1194,28 @@ idebugger_variable_iface_init (IAnjutaDebuggerVariableIface *iface)
 	iface->update = idebugger_variable_update;
 }
 
+/* Implementation of IAnjutaPreference interface
+ *---------------------------------------------------------------------------*/
+
+static void
+ipreferences_merge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** error)
+{
+	gdb_merge_preferences (prefs, &(ANJUTA_PLUGIN_GDB (ipref)->pretty_printers));
+}
+
+static void
+ipreferences_unmerge(IAnjutaPreferences* ipref, AnjutaPreferences* prefs, GError** error)
+{
+	gdb_unmerge_preferences (prefs);
+}
+
+static void
+ipreferences_iface_init(IAnjutaPreferencesIface* iface)
+{
+	iface->merge = ipreferences_merge;
+	iface->unmerge = ipreferences_unmerge;
+}
+
 ANJUTA_PLUGIN_BEGIN (GdbPlugin, gdb_plugin);
 ANJUTA_PLUGIN_ADD_INTERFACE(idebugger, IANJUTA_TYPE_DEBUGGER);
 ANJUTA_PLUGIN_ADD_INTERFACE(idebugger_breakpoint, IANJUTA_TYPE_DEBUGGER_BREAKPOINT);
@@ -1160,6 +1223,7 @@ ANJUTA_PLUGIN_ADD_INTERFACE(idebugger_register, IANJUTA_TYPE_DEBUGGER_REGISTER);
 ANJUTA_PLUGIN_ADD_INTERFACE(idebugger_memory, IANJUTA_TYPE_DEBUGGER_MEMORY);
 ANJUTA_PLUGIN_ADD_INTERFACE(idebugger_instruction, IANJUTA_TYPE_DEBUGGER_INSTRUCTION);
 ANJUTA_PLUGIN_ADD_INTERFACE(idebugger_variable, IANJUTA_TYPE_DEBUGGER_VARIABLE);
+ANJUTA_PLUGIN_ADD_INTERFACE (ipreferences, IANJUTA_TYPE_PREFERENCES);
 ANJUTA_PLUGIN_END;
 
 ANJUTA_SIMPLE_PLUGIN (GdbPlugin, gdb_plugin);
diff --git a/plugins/gdb/preferences.c b/plugins/gdb/preferences.c
new file mode 100644
index 0000000..358422a
--- /dev/null
+++ b/plugins/gdb/preferences.c
@@ -0,0 +1,467 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
+/* preferences.c
+ *
+ * Copyright (C) 2010  Sébastien Granjoux
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "preferences.h"
+
+
+#include <libanjuta/anjuta-utils.h>
+
+
+#define BUILDER_FILE PACKAGE_DATA_DIR"/glade/anjuta-gdb.ui"
+
+#define BUILD_PREFS_DIALOG "preferences_dialog_build"
+#define GDB_PREFS_ROOT "gdb_preferences_container"
+#define GDB_PRINTER_TREEVIEW "printers_treeview"
+#define GDB_PRINTER_REMOVE_BUTTON "remove_button"
+
+
+#define ICON_FILE "anjuta-gdb.plugin.png"
+
+#define GDB_SECTION "Gdb"
+#define GDB_PRINTER_KEY "PrettyPrinter"
+
+
+/* column of the printer list view */
+enum {
+	GDB_PP_ACTIVE_COLUMN,
+	GDB_PP_FILENAME_COLUMN,
+	GDB_PP_REGISTER_COLUMN,
+	GDB_PP_N_COLUMNS
+};
+
+/* Node types
+ *---------------------------------------------------------------------------*/
+
+typedef struct
+{
+	GtkTreeView *treeview;
+	GtkListStore *model;
+	GtkWidget *remove_button;
+	GList **list;
+} PreferenceDialog;
+ 
+ 
+/* Private functions
+ *---------------------------------------------------------------------------*/
+
+static gboolean
+gdb_append_missing_register_function (GString *msg, GtkTreeModel *model, GtkTreeIter *iter)
+{
+	gboolean active;
+	gchar *path;
+	gchar *function;
+	gboolean missing;
+
+	gtk_tree_model_get (model, iter,
+						GDB_PP_ACTIVE_COLUMN, &active,
+						GDB_PP_FILENAME_COLUMN, &path,
+						GDB_PP_REGISTER_COLUMN, &function, -1);
+	if (function != NULL) function = g_strstrip (function);
+
+	missing = active && ((function == NULL) || (*function == '\0'));
+	if (missing)
+	{
+		g_string_append (msg, path);
+		g_string_append (msg, "\n");
+		gtk_list_store_set (GTK_LIST_STORE (model), iter, GDB_PP_ACTIVE_COLUMN, FALSE, -1);
+	}
+	g_free (path);
+	g_free (function);
+	
+	return missing;
+}
+ 
+static void
+gdb_check_register_function (PreferenceDialog *dlg, GtkTreeIter *iter)
+{
+	GString *list;
+
+	list = g_string_new (NULL);
+	if (iter == NULL)
+	{
+		GtkTreeIter iter;
+		gboolean valid;
+		
+		for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dlg->model), &iter);
+			valid; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (dlg->model), &iter))
+		{
+			gdb_append_missing_register_function (list, GTK_TREE_MODEL (dlg->model), &iter);
+		}
+	}
+	else
+	{
+		gdb_append_missing_register_function (list, GTK_TREE_MODEL (dlg->model), iter);
+	}
+
+	if (list->len > 0)
+	{
+		gchar *msg;
+
+		msg = g_strdup_printf(_("The following pretty printers, without a register functions, have been disabled:\n %s"), list->str);
+		anjuta_util_dialog_warning (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (dlg->treeview))), msg);
+		g_free (msg);
+		g_string_free (list, TRUE);
+	}
+}
+
+static gchar *
+gdb_find_register_function (const gchar *path)
+{
+	GFile *file;
+	gchar *function = NULL;
+	gchar *content = NULL;
+
+	file = g_file_new_for_path (path);
+
+	if (g_file_load_contents (file, NULL, &content, NULL, NULL, NULL))
+	{
+		GRegex *regex;
+		GMatchInfo *match;
+
+		regex = g_regex_new ("^def\\s+(register\w*)\\s*\\(\\w+\\)\\s*:", G_REGEX_CASELESS | G_REGEX_MULTILINE, 0, NULL);
+		if (g_regex_match (regex, content, 0, &match))
+		{
+			function = g_match_info_fetch (match, 1);
+			g_match_info_free (match);
+		}
+		g_regex_unref (regex);
+		g_free (content);
+	}
+
+	g_object_unref (file);
+
+	return function;
+}
+
+static gboolean
+on_add_printer_in_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
+{
+	GList** list = (GList **)user_data;
+	gchar *filepath;
+	gchar *function;
+	gboolean active;
+	GdbPrettyPrinter *printer;
+
+	gtk_tree_model_get (model, iter, GDB_PP_ACTIVE_COLUMN, &active,
+						GDB_PP_FILENAME_COLUMN, &filepath,
+						GDB_PP_REGISTER_COLUMN, &function,
+						-1);
+	printer = g_slice_new0 (GdbPrettyPrinter);
+	printer->enable = active;
+	printer->path = filepath;
+	printer->function = function;
+	*list = g_list_prepend (*list, printer);
+
+	return FALSE;
+}
+
+/* Call backs
+ *---------------------------------------------------------------------------*/
+
+ /* Gtk builder callbacks */
+void gdb_on_printer_add (GtkButton *button, gpointer user_data);
+void gdb_on_printer_remove (GtkButton *button, gpointer user_data);
+void gdb_on_destroy_preferences (GtkObject *object, gpointer user_data);
+
+void
+gdb_on_destroy_preferences (GtkObject *object, gpointer user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GList *new_list;
+
+	/* Free previous list and replace with new one */
+	g_list_foreach (*(dlg->list), (GFunc)gdb_pretty_printer_free, NULL);
+	g_list_free (*(dlg->list));
+	*(dlg->list) = NULL;
+
+	/* Replace with new one */
+	new_list = NULL;
+	gtk_tree_model_foreach (GTK_TREE_MODEL (dlg->model), on_add_printer_in_list, &new_list);
+	new_list = g_list_reverse (new_list);
+	*(dlg->list) = new_list;
+
+	g_free (dlg);
+}
+
+void
+gdb_on_printer_add (GtkButton *button, gpointer user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GtkWidget *chooser;
+	GtkFileFilter *filter;
+	
+	chooser = gtk_file_chooser_dialog_new (_("Select a pretty printer file"),
+					GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
+					GTK_FILE_CHOOSER_ACTION_OPEN,
+					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+					GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
+					NULL);
+	filter = gtk_file_filter_new ();
+	gtk_file_filter_add_mime_type (filter, "text/x-python");
+	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
+	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),filter);
+
+	if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
+	{
+		GSList *filenames;
+		GSList *item;
+		
+		filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (chooser));
+
+		for (item = filenames; item != NULL; item = g_slist_next (item))
+		{
+			GtkTreeIter iter;
+			gchar *path = (gchar *)item->data;
+			gchar *function;
+			
+			function = gdb_find_register_function (path);
+			
+			gtk_list_store_append (dlg->model, &iter);
+			gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, TRUE,
+												GDB_PP_FILENAME_COLUMN, path,
+												GDB_PP_REGISTER_COLUMN, function,
+												-1);
+			g_free (path);
+			g_free (function);
+			
+			gdb_check_register_function (dlg, &iter);
+		}
+		g_slist_free (filenames);
+	}
+	gtk_widget_destroy (chooser);
+}
+
+void
+gdb_on_printer_remove (GtkButton *button, gpointer user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GtkTreeIter iter;
+	GtkTreeSelection* sel;
+
+	sel = gtk_tree_view_get_selection (dlg->treeview);
+	if (gtk_tree_selection_get_selected (sel, NULL, &iter))
+	{
+		gtk_list_store_remove (dlg->model, &iter);
+	}
+}
+
+static void
+gdb_on_printer_activate (GtkCellRendererToggle *cell_renderer, const gchar *path, gpointer user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GtkTreeIter iter;
+
+	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dlg->model), &iter, path))
+	{
+		gboolean enable;
+		
+		gtk_tree_model_get (GTK_TREE_MODEL (dlg->model), &iter, GDB_PP_ACTIVE_COLUMN, &enable, -1);
+		enable = !enable;
+		gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, enable, -1);
+		
+		if (enable) gdb_check_register_function (dlg, &iter);
+	}
+}
+
+static void
+gdb_on_printer_function_changed (GtkCellRendererText *renderer,
+								gchar		*path,
+								gchar		*new_text,
+								gpointer	user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GtkTreeIter iter;
+
+	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dlg->model), &iter, path))
+	{
+		gchar *function = g_strstrip (new_text);
+		gtk_list_store_set (dlg->model, &iter, GDB_PP_REGISTER_COLUMN, function, -1);
+	}
+}
+
+static void
+gdb_on_printer_selection_changed (GtkTreeSelection *selection, gpointer user_data)
+{
+	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
+	GtkTreeIter iter;
+	GtkTreeSelection* sel;
+	gboolean selected;
+
+	sel = gtk_tree_view_get_selection (dlg->treeview);
+	selected = gtk_tree_selection_get_selected (sel, NULL, &iter);
+	gtk_widget_set_sensitive(dlg->remove_button, selected);
+}
+ 
+/* Public functions
+ *---------------------------------------------------------------------------*/
+
+void
+gdb_merge_preferences (AnjutaPreferences* prefs, GList **list)
+{
+	GtkBuilder *bxml;
+	GtkCellRenderer *renderer;
+	GtkTreeViewColumn *column;
+	GtkTreeSelection *selection;
+	PreferenceDialog *dlg;
+	GList *item;
+
+	g_return_if_fail (list != NULL);
+	
+	/* Create the preferences page */
+	bxml =  anjuta_util_builder_new (BUILDER_FILE, NULL);
+	if (!bxml) return;
+
+	dlg = g_new0 (PreferenceDialog, 1);
+	
+	/* Get widgets */
+	anjuta_util_builder_get_objects (bxml,
+		GDB_PRINTER_TREEVIEW, &dlg->treeview,
+		GDB_PRINTER_REMOVE_BUTTON, &dlg->remove_button,
+		NULL);
+
+	/* Create tree view */	
+	dlg->model = gtk_list_store_new (GDB_PP_N_COLUMNS,
+		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
+	gtk_tree_view_set_model (dlg->treeview, GTK_TREE_MODEL (dlg->model));
+	g_object_unref (dlg->model);
+
+	renderer = gtk_cell_renderer_toggle_new ();
+	g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (gdb_on_printer_activate), dlg);
+	column = gtk_tree_view_column_new_with_attributes (_("Activate"), renderer,
+		"active", GDB_PP_ACTIVE_COLUMN, NULL);
+	gtk_tree_view_append_column (dlg->treeview, column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	column = gtk_tree_view_column_new_with_attributes (_("File"), renderer,
+		"text", GDB_PP_FILENAME_COLUMN, NULL);
+	gtk_tree_view_append_column (dlg->treeview, column);
+
+	renderer = gtk_cell_renderer_text_new ();
+	g_object_set(renderer, "editable", TRUE, NULL);	
+	g_signal_connect(renderer, "edited", G_CALLBACK (gdb_on_printer_function_changed), dlg);
+	column = gtk_tree_view_column_new_with_attributes (_("Register Function"), renderer,
+		"text", GDB_PP_REGISTER_COLUMN, NULL);
+	gtk_tree_view_append_column (dlg->treeview, column);
+
+	/* Connect all signals */
+	gtk_builder_connect_signals (bxml, dlg);
+	selection = gtk_tree_view_get_selection (dlg->treeview);
+	g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (gdb_on_printer_selection_changed), dlg);
+
+	
+	/* Fill tree view */
+	dlg->list = list;
+	for (item = g_list_first (*list); item != NULL; item = g_list_next (item))
+	{
+		GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
+		GtkTreeIter iter;
+			
+		gtk_list_store_append (dlg->model, &iter);
+		gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, printer->enable ? TRUE : FALSE,
+											GDB_PP_FILENAME_COLUMN, printer->path,
+											GDB_PP_REGISTER_COLUMN, printer->function,
+											-1);
+	}
+	
+	anjuta_preferences_add_from_builder (prefs, bxml, GDB_PREFS_ROOT, _("Gdb Debugger"),  ICON_FILE);
+
+	g_object_unref (bxml);
+}
+
+void
+gdb_unmerge_preferences (AnjutaPreferences* prefs)
+{
+	anjuta_preferences_remove_page(prefs, _("Gdb Debugger"));
+}
+
+GList *
+gdb_load_pretty_printers (AnjutaSession *session)
+{
+	GList *session_list;
+	GList *list = NULL;
+	GList *item;
+
+	session_list = anjuta_session_get_string_list (session, GDB_SECTION, GDB_PRINTER_KEY);
+	for (item = g_list_first (session_list); item != NULL; item = g_list_next (item))
+	{
+		GdbPrettyPrinter *printer;
+		gchar *name = (gchar *)item->data;
+		gchar *ptr;
+		
+		printer = g_slice_new0 (GdbPrettyPrinter);
+		ptr = strchr (name, ':');
+		if (ptr != NULL)
+		{
+			if (*name == 'E') printer->enable = TRUE;
+			name = ptr + 1;
+		}
+		ptr = strrchr (name, ':');
+		if (ptr != NULL)
+		{
+			*ptr = '\0';
+			printer->function = g_strdup (ptr + 1);
+		}
+		printer->path = g_strdup (name);
+		
+		list = g_list_prepend (list, printer);
+	}
+	
+	g_list_foreach (session_list, (GFunc)g_free, NULL);
+	g_list_free (session_list);
+	
+	return list;
+}
+
+gboolean
+gdb_save_pretty_printers (AnjutaSession *session, GList *list)
+{
+	GList *session_list = NULL;
+	GList *item;
+	
+	for (item = g_list_first (list); item != NULL; item = g_list_next (item))
+	{
+		GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
+		gchar *name;
+		
+		name = g_strconcat (printer->enable ? "E:" : "D:", printer->path, ":", printer->function == NULL ? "" : printer->function, NULL);
+
+		session_list = g_list_prepend (session_list, name);
+	}
+	session_list = g_list_reverse (session_list);
+	anjuta_session_set_string_list (session, GDB_SECTION, GDB_PRINTER_KEY, session_list);
+	g_list_foreach (session_list, (GFunc)g_free, NULL);
+	g_list_free (session_list);
+	
+	return FALSE;
+}
+
+void
+gdb_pretty_printer_free (GdbPrettyPrinter *printer)
+{
+	g_free (printer->path);
+	g_free (printer->function);
+	g_slice_free (GdbPrettyPrinter, printer);
+}
diff --git a/plugins/gdb/preferences.h b/plugins/gdb/preferences.h
new file mode 100644
index 0000000..a832deb
--- /dev/null
+++ b/plugins/gdb/preferences.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
+/* preferences.h
+ *
+ * Copyright (C) 2010  Sébastien Granjoux
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _PREFERENCES_H_
+#define _PREFERENCES_H_
+
+#include <libanjuta/anjuta-preferences.h>
+#include <libanjuta/anjuta-session.h>
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+typedef struct
+{
+	gboolean enable;
+	gchar *path;
+	gchar *function;
+} GdbPrettyPrinter;
+
+GList *gdb_load_pretty_printers (AnjutaSession *session);
+gboolean gdb_save_pretty_printers (AnjutaSession *session, GList *list);
+
+void gdb_pretty_printer_free (GdbPrettyPrinter *printer);
+
+void gdb_merge_preferences (AnjutaPreferences* prefs, GList **list);
+void gdb_unmerge_preferences (AnjutaPreferences* prefs);
+
+G_END_DECLS
+
+#endif /* _PREFERENCES_H_ */
diff --git a/plugins/js-debugger/debugger-js.c b/plugins/js-debugger/debugger-js.c
index 887cd11..79297d5 100644
--- a/plugins/js-debugger/debugger-js.c
+++ b/plugins/js-debugger/debugger-js.c
@@ -179,6 +179,7 @@ variable_create (DebuggerJs* object, struct Task *task)
 	var->type = g_strdup ("object");
 	var->value = g_strdup ("object");
 	var->children = 0;
+	var->has_more = FALSE;
 	var->changed = TRUE;
 	var->exited = FALSE;
 	var->deleted = FALSE;
@@ -337,6 +338,7 @@ varibale_list_children (DebuggerJs* object, struct Task *task)
 				var->type = g_strdup (str + k + 1);
 				var->value = g_strdup ("");
 				var->children = 1;
+				var->has_more = FALSE;
 				k = i;
 			}
 			else if (j == 1)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]