1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4; coding: utf-8 -*- */
2 /* preferences.c
3  *
4  * Copyright (C) 2010  Sébastien Granjoux
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include "preferences.h"
28 
29 
30 #include <libanjuta/anjuta-utils.h>
31 
32 
33 #define BUILDER_FILE PACKAGE_DATA_DIR"/glade/anjuta-gdb.ui"
34 
35 #define BUILD_PREFS_DIALOG "preferences_dialog_build"
36 #define GDB_PREFS_ROOT "gdb_preferences_container"
37 #define GDB_PRINTER_TREEVIEW "printers_treeview"
38 #define GDB_PRINTER_REMOVE_BUTTON "remove_button"
39 
40 
41 #define ICON_FILE "anjuta-gdb.plugin.png"
42 
43 #define GDB_SECTION "Gdb"
44 #define GDB_PRINTER_KEY "PrettyPrinter"
45 
46 
47 /* column of the printer list view */
48 enum {
49 	GDB_PP_ACTIVE_COLUMN,
50 	GDB_PP_FILENAME_COLUMN,
51 	GDB_PP_REGISTER_COLUMN,
52 	GDB_PP_N_COLUMNS
53 };
54 
55 /* Node types
56  *---------------------------------------------------------------------------*/
57 
58 typedef struct
59 {
60 	GtkTreeView *treeview;
61 	GtkListStore *model;
62 	GtkWidget *remove_button;
63 	GList **list;
64 } PreferenceDialog;
65 
66 
67 /* Private functions
68  *---------------------------------------------------------------------------*/
69 
70 static gboolean
gdb_append_missing_register_function(GString * msg,GtkTreeModel * model,GtkTreeIter * iter)71 gdb_append_missing_register_function (GString *msg, GtkTreeModel *model, GtkTreeIter *iter)
72 {
73 	gboolean active;
74 	gchar *path;
75 	gchar *function;
76 	gboolean missing;
77 
78 	gtk_tree_model_get (model, iter,
79 						GDB_PP_ACTIVE_COLUMN, &active,
80 						GDB_PP_FILENAME_COLUMN, &path,
81 						GDB_PP_REGISTER_COLUMN, &function, -1);
82 	if (function != NULL) function = g_strstrip (function);
83 
84 	missing = active && ((function == NULL) || (*function == '\0'));
85 	if (missing)
86 	{
87 		g_string_append (msg, path);
88 		g_string_append (msg, "\n");
89 		gtk_list_store_set (GTK_LIST_STORE (model), iter, GDB_PP_ACTIVE_COLUMN, FALSE, -1);
90 	}
91 	g_free (path);
92 	g_free (function);
93 
94 	return missing;
95 }
96 
97 static void
gdb_check_register_function(PreferenceDialog * dlg,GtkTreeIter * iter)98 gdb_check_register_function (PreferenceDialog *dlg, GtkTreeIter *iter)
99 {
100 	GString *list;
101 
102 	list = g_string_new (NULL);
103 	if (iter == NULL)
104 	{
105 		GtkTreeIter iter;
106 		gboolean valid;
107 
108 		for (valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dlg->model), &iter);
109 			valid; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (dlg->model), &iter))
110 		{
111 			gdb_append_missing_register_function (list, GTK_TREE_MODEL (dlg->model), &iter);
112 		}
113 	}
114 	else
115 	{
116 		gdb_append_missing_register_function (list, GTK_TREE_MODEL (dlg->model), iter);
117 	}
118 
119 	if (list->len > 0)
120 	{
121 		gchar *msg;
122 
123 		/* Translators: pretty printer file is a script containing functions allowing gdb
124 		 * to display variable content in a simpler way, typically removing
125 		 * implementation details.
126 		 * The register function is an additional function in the script. It defines
127 		 * which function is used for each type of variables. */
128 		msg = g_strdup_printf(_("The register function hasn't been found automatically in the following pretty printer files:\n"
129 								"%s\nYou need to fill yourself the register function columns before enabling the rows. "
130 								"Most of the time the register function name contains the word \"register\"."), list->str);
131 		anjuta_util_dialog_warning (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (dlg->treeview))), msg);
132 		g_free (msg);
133 		g_string_free (list, TRUE);
134 	}
135 }
136 
137 static gchar *
gdb_find_register_function(const gchar * path)138 gdb_find_register_function (const gchar *path)
139 {
140 	GFile *file;
141 	gchar *function = NULL;
142 	gchar *content = NULL;
143 
144 	file = g_file_new_for_path (path);
145 
146 	if (g_file_load_contents (file, NULL, &content, NULL, NULL, NULL))
147 	{
148 		GRegex *regex;
149 		GMatchInfo *match;
150 
151 		regex = g_regex_new ("^def\\s+(register\\w*)\\s*\\(\\w+\\)\\s*:", G_REGEX_CASELESS | G_REGEX_MULTILINE, 0, NULL);
152 		if (g_regex_match (regex, content, 0, &match))
153 		{
154 			function = g_match_info_fetch (match, 1);
155 			g_match_info_free (match);
156 		}
157 		g_regex_unref (regex);
158 		g_free (content);
159 	}
160 
161 	g_object_unref (file);
162 
163 	return function;
164 }
165 
166 static gboolean
on_add_printer_in_list(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer user_data)167 on_add_printer_in_list (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
168 {
169 	GList** list = (GList **)user_data;
170 	gchar *filepath;
171 	gchar *function;
172 	gboolean active;
173 	GdbPrettyPrinter *printer;
174 
175 	gtk_tree_model_get (model, iter, GDB_PP_ACTIVE_COLUMN, &active,
176 						GDB_PP_FILENAME_COLUMN, &filepath,
177 						GDB_PP_REGISTER_COLUMN, &function,
178 						-1);
179 	printer = g_slice_new0 (GdbPrettyPrinter);
180 	printer->enable = active;
181 	printer->path = filepath;
182 	printer->function = function;
183 	*list = g_list_prepend (*list, printer);
184 
185 	return FALSE;
186 }
187 
188 /* Call backs
189  *---------------------------------------------------------------------------*/
190 
191  /* Gtk builder callbacks */
192 void gdb_on_printer_add (GtkButton *button, gpointer user_data);
193 void gdb_on_printer_remove (GtkButton *button, gpointer user_data);
194 void gdb_on_destroy_preferences (GtkWidget *object, gpointer user_data);
195 
196 void
gdb_on_destroy_preferences(GtkWidget * object,gpointer user_data)197 gdb_on_destroy_preferences (GtkWidget *object, gpointer user_data)
198 {
199 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
200 	GList *new_list;
201 
202 	/* Free previous list and replace with new one */
203 	g_list_foreach (*(dlg->list), (GFunc)gdb_pretty_printer_free, NULL);
204 	g_list_free (*(dlg->list));
205 	*(dlg->list) = NULL;
206 
207 	/* Replace with new one */
208 	new_list = NULL;
209 	gtk_tree_model_foreach (GTK_TREE_MODEL (dlg->model), on_add_printer_in_list, &new_list);
210 	new_list = g_list_reverse (new_list);
211 	*(dlg->list) = new_list;
212 
213 	g_free (dlg);
214 }
215 
216 void
gdb_on_printer_add(GtkButton * button,gpointer user_data)217 gdb_on_printer_add (GtkButton *button, gpointer user_data)
218 {
219 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
220 	GtkWidget *chooser;
221 	GtkFileFilter *filter;
222 
223 	chooser = gtk_file_chooser_dialog_new (_("Select a pretty printer file"),
224 					GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (button))),
225 					GTK_FILE_CHOOSER_ACTION_OPEN,
226 					GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
227 					GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
228 					NULL);
229 	filter = gtk_file_filter_new ();
230 	gtk_file_filter_add_mime_type (filter, "text/x-python");
231 	gtk_file_chooser_set_select_multiple (GTK_FILE_CHOOSER (chooser), TRUE);
232 	gtk_file_chooser_set_filter (GTK_FILE_CHOOSER (chooser),filter);
233 
234 	if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT)
235 	{
236 		GSList *filenames;
237 		GSList *item;
238 
239 		filenames = gtk_file_chooser_get_filenames (GTK_FILE_CHOOSER (chooser));
240 
241 		for (item = filenames; item != NULL; item = g_slist_next (item))
242 		{
243 			GtkTreeIter iter;
244 			gchar *path = (gchar *)item->data;
245 			gchar *function;
246 
247 			function = gdb_find_register_function (path);
248 
249 			gtk_list_store_append (dlg->model, &iter);
250 			gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, TRUE,
251 												GDB_PP_FILENAME_COLUMN, path,
252 												GDB_PP_REGISTER_COLUMN, function,
253 												-1);
254 			g_free (path);
255 			g_free (function);
256 
257 			gdb_check_register_function (dlg, &iter);
258 		}
259 		g_slist_free (filenames);
260 	}
261 	gtk_widget_destroy (chooser);
262 }
263 
264 void
gdb_on_printer_remove(GtkButton * button,gpointer user_data)265 gdb_on_printer_remove (GtkButton *button, gpointer user_data)
266 {
267 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
268 	GtkTreeIter iter;
269 	GtkTreeSelection* sel;
270 
271 	sel = gtk_tree_view_get_selection (dlg->treeview);
272 	if (gtk_tree_selection_get_selected (sel, NULL, &iter))
273 	{
274 		gtk_list_store_remove (dlg->model, &iter);
275 	}
276 }
277 
278 static void
gdb_on_printer_activate(GtkCellRendererToggle * cell_renderer,const gchar * path,gpointer user_data)279 gdb_on_printer_activate (GtkCellRendererToggle *cell_renderer, const gchar *path, gpointer user_data)
280 {
281 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
282 	GtkTreeIter iter;
283 
284 	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dlg->model), &iter, path))
285 	{
286 		gboolean enable;
287 
288 		gtk_tree_model_get (GTK_TREE_MODEL (dlg->model), &iter, GDB_PP_ACTIVE_COLUMN, &enable, -1);
289 		enable = !enable;
290 		gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, enable, -1);
291 	}
292 }
293 
294 static void
gdb_on_printer_function_changed(GtkCellRendererText * renderer,gchar * path,gchar * new_text,gpointer user_data)295 gdb_on_printer_function_changed (GtkCellRendererText *renderer,
296 								gchar		*path,
297 								gchar		*new_text,
298 								gpointer	user_data)
299 {
300 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
301 	GtkTreeIter iter;
302 
303 	if (gtk_tree_model_get_iter_from_string (GTK_TREE_MODEL (dlg->model), &iter, path))
304 	{
305 		gchar *function = g_strstrip (new_text);
306 		gtk_list_store_set (dlg->model, &iter, GDB_PP_REGISTER_COLUMN, function, -1);
307 	}
308 }
309 
310 static void
gdb_on_printer_selection_changed(GtkTreeSelection * selection,gpointer user_data)311 gdb_on_printer_selection_changed (GtkTreeSelection *selection, gpointer user_data)
312 {
313 	PreferenceDialog *dlg = (PreferenceDialog *)user_data;
314 	GtkTreeIter iter;
315 	GtkTreeSelection* sel;
316 	gboolean selected;
317 
318 	sel = gtk_tree_view_get_selection (dlg->treeview);
319 	selected = gtk_tree_selection_get_selected (sel, NULL, &iter);
320 	gtk_widget_set_sensitive(dlg->remove_button, selected);
321 }
322 
323 /* Public functions
324  *---------------------------------------------------------------------------*/
325 
326 void
gdb_merge_preferences(AnjutaPreferences * prefs,GList ** list)327 gdb_merge_preferences (AnjutaPreferences* prefs, GList **list)
328 {
329 	GtkBuilder *bxml;
330 	GtkCellRenderer *renderer;
331 	GtkTreeViewColumn *column;
332 	GtkTreeSelection *selection;
333 	PreferenceDialog *dlg;
334 	GList *item;
335 
336 	g_return_if_fail (list != NULL);
337 
338 	/* Create the preferences page */
339 	bxml =  anjuta_util_builder_new (BUILDER_FILE, NULL);
340 	if (!bxml) return;
341 
342 	dlg = g_new0 (PreferenceDialog, 1);
343 
344 	/* Get widgets */
345 	anjuta_util_builder_get_objects (bxml,
346 		GDB_PRINTER_TREEVIEW, &dlg->treeview,
347 		GDB_PRINTER_REMOVE_BUTTON, &dlg->remove_button,
348 		NULL);
349 
350 	/* Create tree view */
351 	dlg->model = gtk_list_store_new (GDB_PP_N_COLUMNS,
352 		G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING);
353 	gtk_tree_view_set_model (dlg->treeview, GTK_TREE_MODEL (dlg->model));
354 	g_object_unref (dlg->model);
355 
356 	renderer = gtk_cell_renderer_toggle_new ();
357 	g_signal_connect (G_OBJECT (renderer), "toggled", G_CALLBACK (gdb_on_printer_activate), dlg);
358 	column = gtk_tree_view_column_new_with_attributes (_("Activate"), renderer,
359 		"active", GDB_PP_ACTIVE_COLUMN, NULL);
360 	gtk_tree_view_append_column (dlg->treeview, column);
361 
362 	renderer = gtk_cell_renderer_text_new ();
363 	column = gtk_tree_view_column_new_with_attributes (_("File"), renderer,
364 		"text", GDB_PP_FILENAME_COLUMN, NULL);
365 	gtk_tree_view_append_column (dlg->treeview, column);
366 
367 	renderer = gtk_cell_renderer_text_new ();
368 	g_object_set(renderer, "editable", TRUE, NULL);
369 	g_signal_connect(renderer, "edited", G_CALLBACK (gdb_on_printer_function_changed), dlg);
370 	/* Translators: The "Register Function" column contains the name of a
371 	 * function used to register pretty printers in gdb. */
372 	column = gtk_tree_view_column_new_with_attributes (_("Register Function"), renderer,
373 		"text", GDB_PP_REGISTER_COLUMN, NULL);
374 	gtk_tree_view_append_column (dlg->treeview, column);
375 
376 	/* Connect all signals */
377 	gtk_builder_connect_signals (bxml, dlg);
378 	selection = gtk_tree_view_get_selection (dlg->treeview);
379 	g_signal_connect (G_OBJECT (selection), "changed", G_CALLBACK (gdb_on_printer_selection_changed), dlg);
380 
381 
382 	/* Fill tree view */
383 	dlg->list = list;
384 	for (item = g_list_first (*list); item != NULL; item = g_list_next (item))
385 	{
386 		GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
387 		GtkTreeIter iter;
388 
389 		gtk_list_store_append (dlg->model, &iter);
390 		gtk_list_store_set (dlg->model, &iter, GDB_PP_ACTIVE_COLUMN, printer->enable ? TRUE : FALSE,
391 											GDB_PP_FILENAME_COLUMN, printer->path,
392 											GDB_PP_REGISTER_COLUMN, printer->function,
393 											-1);
394 	}
395 
396 	anjuta_preferences_add_from_builder (prefs, bxml, NULL, GDB_PREFS_ROOT, _("Gdb Debugger"),  ICON_FILE);
397 
398 	g_object_unref (bxml);
399 }
400 
401 void
gdb_unmerge_preferences(AnjutaPreferences * prefs)402 gdb_unmerge_preferences (AnjutaPreferences* prefs)
403 {
404 	anjuta_preferences_remove_page(prefs, _("Gdb Debugger"));
405 }
406 
407 GList *
gdb_load_pretty_printers(AnjutaSession * session)408 gdb_load_pretty_printers (AnjutaSession *session)
409 {
410 	GList *session_list;
411 	GList *list = NULL;
412 	GList *item;
413 
414 	session_list = anjuta_session_get_string_list (session, GDB_SECTION, GDB_PRINTER_KEY);
415 	for (item = g_list_first (session_list); item != NULL; item = g_list_next (item))
416 	{
417 		GdbPrettyPrinter *printer;
418 		gchar *name = (gchar *)item->data;
419 		gchar *ptr;
420 
421 		printer = g_slice_new0 (GdbPrettyPrinter);
422 		ptr = strchr (name, ':');
423 		if (ptr != NULL)
424 		{
425 			if (*name == 'E') printer->enable = TRUE;
426 			name = ptr + 1;
427 		}
428 		ptr = strrchr (name, ':');
429 		if (ptr != NULL)
430 		{
431 			*ptr = '\0';
432 			printer->function = g_strdup (ptr + 1);
433 		}
434 		printer->path = g_strdup (name);
435 
436 		list = g_list_prepend (list, printer);
437 	}
438 
439 	g_list_foreach (session_list, (GFunc)g_free, NULL);
440 	g_list_free (session_list);
441 
442 	return list;
443 }
444 
445 gboolean
gdb_save_pretty_printers(AnjutaSession * session,GList * list)446 gdb_save_pretty_printers (AnjutaSession *session, GList *list)
447 {
448 	GList *session_list = NULL;
449 	GList *item;
450 
451 	for (item = g_list_first (list); item != NULL; item = g_list_next (item))
452 	{
453 		GdbPrettyPrinter *printer = (GdbPrettyPrinter *)item->data;
454 		gchar *name;
455 
456 		name = g_strconcat (printer->enable ? "E:" : "D:", printer->path, ":", printer->function == NULL ? "" : printer->function, NULL);
457 
458 		session_list = g_list_prepend (session_list, name);
459 	}
460 	session_list = g_list_reverse (session_list);
461 	anjuta_session_set_string_list (session, GDB_SECTION, GDB_PRINTER_KEY, session_list);
462 	g_list_foreach (session_list, (GFunc)g_free, NULL);
463 	g_list_free (session_list);
464 
465 	return FALSE;
466 }
467 
468 void
gdb_pretty_printer_free(GdbPrettyPrinter * printer)469 gdb_pretty_printer_free (GdbPrettyPrinter *printer)
470 {
471 	g_free (printer->path);
472 	g_free (printer->function);
473 	g_slice_free (GdbPrettyPrinter, printer);
474 }
475