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