1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2003 Ximian, Inc. (www.ximian.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
31 #include <sys/types.h>
36 #include <libgnome/gnome-i18n.h>
37 #include <gconf/gconf-client.h>
39 #include "vgrule-list.h"
40 #include "vgmarshal.h"
43 typedef struct _RuleNode
{
44 struct _RuleNode
*next
;
45 struct _RuleNode
*prev
;
52 COL_POINTER_RULE_NODE
,
56 static GType col_types
[] = {
67 static unsigned int signals
[LAST_SIGNAL
] = { 0 };
69 #define SUPPRESSIONS_KEY "/apps/anjuta/valgrind/general/suppressions"
72 static void vg_rule_list_class_init (VgRuleListClass
*klass
);
73 static void vg_rule_list_init (VgRuleList
*list
);
74 static void vg_rule_list_destroy (GtkObject
*obj
);
75 static void vg_rule_list_finalize (GObject
*obj
);
78 static GtkVBoxClass
*parent_class
= NULL
;
82 vg_rule_list_get_type (void)
84 static GType type
= 0;
87 static const GTypeInfo info
= {
88 sizeof (VgRuleListClass
),
89 NULL
, /* base_class_init */
90 NULL
, /* base_class_finalize */
91 (GClassInitFunc
) vg_rule_list_class_init
,
92 NULL
, /* class_finalize */
93 NULL
, /* class_data */
96 (GInstanceInitFunc
) vg_rule_list_init
,
99 type
= g_type_register_static (GTK_TYPE_VBOX
, "VgRuleList", &info
, 0);
106 vg_rule_list_class_init (VgRuleListClass
*klass
)
108 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
109 GtkObjectClass
*gtk_object_class
= GTK_OBJECT_CLASS (klass
);
111 parent_class
= g_type_class_ref (GTK_TYPE_VBOX
);
113 object_class
->finalize
= vg_rule_list_finalize
;
114 gtk_object_class
->destroy
= vg_rule_list_destroy
;
117 = g_signal_new ("rule-added",
118 G_OBJECT_CLASS_TYPE (object_class
),
120 G_STRUCT_OFFSET (VgRuleListClass
, rule_added
),
122 vg_marshal_NONE__POINTER
,
129 rule_editor_dialog_new (GtkWindow
*parent
, VgRule
*rule
)
131 GtkWidget
*dialog
, *editor
;
133 /* FIXME: we should really get this title from somewhere else? */
134 dialog
= gtk_dialog_new_with_buttons (_("Valgrind Suppression"), parent
,
135 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
136 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
137 GTK_STOCK_OK
, GTK_RESPONSE_OK
, NULL
);
140 editor
= vg_rule_editor_new_from_rule (rule
);
142 editor
= vg_rule_editor_new ();
144 gtk_container_set_border_width (GTK_CONTAINER (editor
), 6);
145 gtk_widget_show (editor
);
147 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), 3);
148 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), editor
, TRUE
, TRUE
, 0);
150 g_object_set_data (G_OBJECT (dialog
), "editor", editor
);
156 add_response_cb (GtkDialog
*dialog
, int response
, gpointer user_data
)
158 VgRuleList
*list
= user_data
;
164 if (response
== GTK_RESPONSE_OK
) {
168 editor
= g_object_get_data (G_OBJECT (dialog
), "editor");
170 name
= vg_rule_editor_get_name (VG_RULE_EDITOR (editor
));
171 if (!name
|| *name
== '\0') {
172 msg
= gtk_message_dialog_new (GTK_WINDOW (dialog
), GTK_DIALOG_MODAL
|
173 GTK_DIALOG_DESTROY_WITH_PARENT
,
176 _("You have forgotten to name your suppression rule."));
178 gtk_dialog_run (GTK_DIALOG (msg
));
179 gtk_widget_destroy (msg
);
183 list
->changed
= TRUE
;
185 rule
= vg_rule_editor_get_rule (VG_RULE_EDITOR (editor
));
187 node
= g_new (RuleNode
, 1);
190 list_append_node (&list
->rules
, (ListNode
*) node
);
192 vg_rule_list_save (list
);
194 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
196 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
197 COL_STRING_NAME
, rule
->name
,
198 COL_POINTER_RULE
, rule
,
199 COL_POINTER_RULE_NODE
, node
, -1);
201 g_signal_emit (list
, signals
[RULE_ADDED
], 0, rule
);
204 gtk_widget_destroy (GTK_WIDGET (dialog
));
208 add_cb (GtkWidget
*button
, gpointer user_data
)
210 VgRuleList
*list
= user_data
;
214 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
215 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
217 dialog
= rule_editor_dialog_new (GTK_WINDOW (parent
), NULL
);
218 g_signal_connect (dialog
, "response", G_CALLBACK (add_response_cb
), list
);
220 gtk_widget_show (dialog
);
224 edit_response_cb (GtkDialog
*dialog
, int response
, gpointer user_data
)
226 VgRuleList
*list
= user_data
;
233 if (response
== GTK_RESPONSE_OK
) {
237 editor
= g_object_get_data (G_OBJECT (dialog
), "editor");
239 name
= vg_rule_editor_get_name (VG_RULE_EDITOR (editor
));
240 if (!name
|| *name
== '\0') {
241 msg
= gtk_message_dialog_new (GTK_WINDOW (dialog
), GTK_DIALOG_MODAL
|
242 GTK_DIALOG_DESTROY_WITH_PARENT
,
245 _("You have forgotten to name your suppression rule."));
247 gtk_dialog_run (GTK_DIALOG (msg
));
248 gtk_widget_destroy (msg
);
252 list
->changed
= TRUE
;
254 rule
= vg_rule_editor_get_rule (VG_RULE_EDITOR (editor
));
256 path
= g_object_get_data (G_OBJECT (dialog
), "path");
257 if (gtk_tree_model_get_iter (list
->model
, &iter
, path
)) {
258 /* replace the old rule node... */
259 gtk_tree_model_get (list
->model
, &iter
, COL_POINTER_RULE_NODE
, &node
, -1);
260 vg_rule_free (node
->rule
);
263 /* create a new rule node... */
264 node
= g_new (RuleNode
, 1);
267 list_append_node (&list
->rules
, (ListNode
*) node
);
269 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
272 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
273 COL_STRING_NAME
, rule
->name
,
274 COL_POINTER_RULE
, rule
,
275 COL_POINTER_RULE_NODE
, node
, -1);
277 /* FIXME: only emit if we've changed something that matters? */
278 g_signal_emit (list
, signals
[RULE_ADDED
], 0, rule
);
281 gtk_widget_destroy (GTK_WIDGET (dialog
));
285 edit_cb (GtkWidget
*button
, gpointer user_data
)
287 VgRuleList
*list
= user_data
;
288 GtkTreeSelection
*selection
;
289 GtkTreeModel
*model
= NULL
;
296 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
297 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
298 gtk_tree_model_get (model
, &iter
, COL_POINTER_RULE
, &rule
, -1);
303 path
= gtk_tree_model_get_path (model
, &iter
);
305 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
306 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
308 dialog
= rule_editor_dialog_new (GTK_WINDOW (parent
), rule
);
309 g_signal_connect (dialog
, "response", G_CALLBACK (edit_response_cb
), list
);
310 g_object_set_data_full (G_OBJECT (dialog
), "path", path
, (GDestroyNotify
) gtk_tree_path_free
);
312 gtk_widget_show (dialog
);
316 remove_cb (GtkWidget
*button
, gpointer user_data
)
318 VgRuleList
*list
= user_data
;
319 GtkTreeSelection
*selection
;
320 GtkTreeModel
*model
= NULL
;
321 RuleNode
*node
= NULL
;
325 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
326 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
327 gtk_tree_model_get (model
, &iter
, COL_POINTER_RULE
, &rule
, COL_POINTER_RULE_NODE
, &node
, -1);
332 list
->changed
= TRUE
;
334 gtk_list_store_remove (GTK_LIST_STORE (model
), &iter
);
335 list_node_unlink ((ListNode
*) node
);
341 row_activate_cb (GtkTreeView
*treeview
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
)
343 /* double-clicked on a rule, pop the rule up in an editor */
344 edit_cb (NULL
, user_data
);
348 selection_change_cb (GtkTreeSelection
*selection
, gpointer user_data
)
350 VgRuleList
*list
= user_data
;
355 state
= gtk_tree_selection_get_selected (selection
, &model
, &iter
);
357 gtk_widget_grab_focus (list
->add
);
359 gtk_widget_set_sensitive (list
->edit
, state
);
360 gtk_widget_set_sensitive (list
->remove
, state
);
364 vg_rule_list_init (VgRuleList
*list
)
366 GtkTreeSelection
*selection
;
367 GtkCellRenderer
*renderer
;
368 GtkWidget
*hbox
, *vbox
;
371 hbox
= gtk_hbox_new (FALSE
, 6);
373 scrolled
= gtk_scrolled_window_new (NULL
, NULL
);
374 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled
),
375 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
376 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled
), GTK_SHADOW_IN
);
378 list
->model
= GTK_TREE_MODEL (gtk_list_store_newv (COL_LAST
, col_types
));
379 list
->list
= gtk_tree_view_new_with_model (list
->model
);
381 renderer
= gtk_cell_renderer_text_new ();
382 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (list
->list
), -1, "",
383 renderer
, "text", 0, NULL
);
385 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
386 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_SINGLE
);
387 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list
->list
), FALSE
);
389 g_signal_connect (selection
, "changed", G_CALLBACK (selection_change_cb
), list
);
390 g_signal_connect (list
->list
, "row-activated", G_CALLBACK (row_activate_cb
), list
);
392 gtk_widget_show (list
->list
);
393 gtk_container_add (GTK_CONTAINER (scrolled
), list
->list
);
395 gtk_widget_show (scrolled
);
396 gtk_box_pack_start (GTK_BOX (hbox
), scrolled
, TRUE
, TRUE
, 0);
398 vbox
= gtk_vbox_new (FALSE
, 3);
400 list
->add
= gtk_button_new_from_stock (GTK_STOCK_ADD
);
401 g_signal_connect (list
->add
, "clicked", G_CALLBACK (add_cb
), list
);
402 gtk_widget_show (list
->add
);
403 gtk_box_pack_start (GTK_BOX (vbox
), list
->add
, FALSE
, FALSE
, 0);
405 list
->edit
= gtk_button_new_with_mnemonic (_("_Edit"));
406 gtk_widget_set_sensitive (list
->edit
, FALSE
);
407 g_signal_connect (list
->edit
, "clicked", G_CALLBACK (edit_cb
), list
);
408 gtk_widget_show (list
->edit
);
409 gtk_box_pack_start (GTK_BOX (vbox
), list
->edit
, FALSE
, FALSE
, 0);
411 list
->remove
= gtk_button_new_from_stock (GTK_STOCK_REMOVE
);
412 gtk_widget_set_sensitive (list
->remove
, FALSE
);
413 g_signal_connect (list
->remove
, "clicked", G_CALLBACK (remove_cb
), list
);
414 gtk_widget_show (list
->remove
);
415 gtk_box_pack_start (GTK_BOX (vbox
), list
->remove
, FALSE
, FALSE
, 0);
417 gtk_widget_show (vbox
);
418 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, FALSE
, FALSE
, 0);
420 gtk_widget_show (hbox
);
421 gtk_container_add (GTK_CONTAINER (list
), hbox
);
424 list_init (&list
->rules
);
425 list
->changed
= FALSE
;
426 list
->filename
= NULL
;
434 vg_rule_list_finalize (GObject
*obj
)
436 VgRuleList
*list
= (VgRuleList
*) obj
;
439 g_free (list
->filename
);
441 vg_rule_parser_free (list
->parser
);
443 n
= (RuleNode
*) list
->rules
.head
;
444 while (n
->next
!= NULL
) {
446 vg_rule_free (n
->rule
);
451 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
455 vg_rule_list_destroy (GtkObject
*obj
)
457 VgRuleList
*list
= (VgRuleList
*) obj
;
459 if (list
->gio
!= NULL
) {
460 g_io_channel_close (list
->gio
);
461 g_io_channel_unref (list
->gio
);
466 GTK_OBJECT_CLASS (parent_class
)->destroy (obj
);
471 load_rule_cb (VgRuleParser
*parser
, VgRule
*rule
, gpointer user_data
)
473 VgRuleList
*list
= user_data
;
477 node
= g_new (RuleNode
, 1);
480 list_append_node (&list
->rules
, (ListNode
*) node
);
482 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
483 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
484 COL_STRING_NAME
, rule
->name
,
485 COL_POINTER_RULE
, rule
,
486 COL_POINTER_RULE_NODE
, node
, -1);
490 load_rules_step_cb (GIOChannel
*gio
, GIOCondition condition
, gpointer user_data
)
492 VgRuleList
*list
= user_data
;
494 if ((condition
& G_IO_IN
) && vg_rule_parser_step (list
->parser
) <= 0)
497 if (condition
& G_IO_HUP
)
504 vg_rule_parser_free (list
->parser
);
507 g_io_channel_close (list
->gio
);
508 g_io_channel_unref (list
->gio
);
516 load_rules (GtkWidget
*widget
, gpointer user_data
)
518 VgRuleList
*list
= user_data
;
521 list
->changed
= FALSE
;
523 if (list
->show_id
!= 0) {
524 g_signal_handler_disconnect (list
, list
->show_id
);
528 if (list
->filename
== NULL
)
531 if ((fd
= open (list
->filename
, O_RDONLY
)) == -1)
534 list
->parser
= vg_rule_parser_new (fd
, load_rule_cb
, list
);
535 list
->gio
= g_io_channel_unix_new (fd
);
536 list
->load_id
= g_io_add_watch (list
->gio
, G_IO_IN
| G_IO_HUP
, load_rules_step_cb
, list
);
541 vg_rule_list_new (const char *filename
)
545 list
= g_object_new (VG_TYPE_RULE_LIST
, NULL
);
546 list
->filename
= g_strdup (filename
);
548 /* suppressions file may be large, so don't actually load it
549 until the user shows it for the first time */
550 list
->show_id
= g_signal_connect (list
, "map", G_CALLBACK (load_rules
), list
);
552 return GTK_WIDGET (list
);
557 vg_rule_list_set_filename (VgRuleList
*list
, const char *filename
)
561 g_free (list
->filename
);
562 list
->filename
= g_strdup (filename
);
564 if (list
->show_id
!= 0) {
565 /* good, the user hasn't shown the dialog yet.
566 * this means that we don't have to do anything special!
569 if (list
->load_id
!= 0) {
570 /* this means the file is currently being loaded. ugh */
571 vg_rule_parser_free (list
->parser
);
572 g_io_channel_close (list
->gio
);
573 g_io_channel_unref (list
->gio
);
578 n
= (RuleNode
*) list
->rules
.head
;
579 while (n
->next
!= NULL
) {
581 vg_rule_free (n
->rule
);
586 gtk_list_store_clear (GTK_LIST_STORE (list
->model
));
588 if (!GTK_WIDGET_MAPPED (list
))
589 list
->show_id
= g_signal_connect (list
, "map", G_CALLBACK (load_rules
), list
);
591 load_rules (GTK_WIDGET (list
), list
);
597 vg_rule_list_save (VgRuleList
*list
)
599 GtkWidget
*parent
, *msg
;
600 char *filename
= NULL
;
601 const char *basename
;
605 /* only save if we need to... */
609 if (list
->filename
== NULL
)
612 if (!(basename
= strrchr (list
->filename
, '/')))
613 basename
= list
->filename
;
617 filename
= g_strdup_printf ("%.*s.#%s", (basename
- list
->filename
),
618 list
->filename
, basename
);
620 if ((fd
= open (filename
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
, 0666)) == -1)
623 if (vg_suppressions_file_write_header (fd
, "This Valgrind suppresion file was generated by Alleyoop") == -1)
626 node
= (RuleNode
*) list
->rules
.head
;
627 while (node
->next
!= NULL
) {
628 if (vg_suppressions_file_append_rule (fd
, node
->rule
) == -1)
636 if (rename (filename
, list
->filename
) == -1)
645 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
646 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
648 msg
= gtk_message_dialog_new (GTK_WINDOW (parent
), GTK_DIALOG_MODAL
,
649 GTK_MESSAGE_ERROR
, GTK_BUTTONS_CLOSE
,
650 _("Cannot save suppression rules: %s"),
651 list
->filename
? g_strerror (errno
) :
652 _("You have not set a suppressions file in your settings."));
654 g_signal_connect_swapped (msg
, "response", G_CALLBACK (gtk_widget_destroy
), msg
);
656 gtk_widget_show (msg
);
671 vg_rule_list_add_rule (VgRuleList
*list
, const char *title
, GtkWindow
*parent
, VgErrorSummary
*summary
)
673 GtkWidget
*dialog
, *editor
;
676 dialog
= gtk_dialog_new_with_buttons (title
, parent
, GTK_DIALOG_MODAL
|
677 GTK_DIALOG_DESTROY_WITH_PARENT
,
678 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
679 GTK_STOCK_OK
, GTK_RESPONSE_OK
, NULL
);
682 editor
= vg_rule_editor_new_from_summary (summary
);
684 editor
= vg_rule_editor_new ();
686 gtk_widget_show (editor
);
687 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), editor
, TRUE
, TRUE
, 0);
688 g_signal_connect (dialog
, "response", G_CALLBACK (add_response_cb
), list
);
689 g_object_set_data (G_OBJECT (dialog
), "editor", editor
);
691 if (list
->filename
== NULL
) {
692 gconf
= gconf_client_get_default ();
694 // FIXME: hardcoded path
695 list
->filename
= g_strdup_printf ("%s/.anjuta/valgrind.supp", getenv ("HOME"));
696 gconf_client_set_string (gconf
, SUPPRESSIONS_KEY
, list
->filename
, NULL
);
697 g_object_unref (gconf
);
700 /* FIXME: what if the rules haven't finished loading before the user adds this rule? */
702 gtk_widget_show (dialog
);