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 <glib/gi18n.h>
37 #include <gconf/gconf-client.h>
38 #include <libanjuta/anjuta-utils.h>
40 #include "vgrule-list.h"
41 #include "vgmarshal.h"
44 typedef struct _RuleNode
{
45 struct _RuleNode
*next
;
46 struct _RuleNode
*prev
;
53 COL_POINTER_RULE_NODE
,
57 static GType col_types
[] = {
68 static unsigned int signals
[LAST_SIGNAL
] = { 0 };
70 #define SUPPRESSIONS_KEY "/apps/anjuta/valgrind/general/suppressions"
73 static void vg_rule_list_class_init (VgRuleListClass
*klass
);
74 static void vg_rule_list_init (VgRuleList
*list
);
75 static void vg_rule_list_destroy (GtkObject
*obj
);
76 static void vg_rule_list_finalize (GObject
*obj
);
79 static GtkVBoxClass
*parent_class
= NULL
;
83 vg_rule_list_get_type (void)
85 static GType type
= 0;
88 static const GTypeInfo info
= {
89 sizeof (VgRuleListClass
),
90 NULL
, /* base_class_init */
91 NULL
, /* base_class_finalize */
92 (GClassInitFunc
) vg_rule_list_class_init
,
93 NULL
, /* class_finalize */
94 NULL
, /* class_data */
97 (GInstanceInitFunc
) vg_rule_list_init
,
100 type
= g_type_register_static (GTK_TYPE_VBOX
, "VgRuleList", &info
, 0);
107 vg_rule_list_class_init (VgRuleListClass
*klass
)
109 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
110 GtkObjectClass
*gtk_object_class
= GTK_OBJECT_CLASS (klass
);
112 parent_class
= g_type_class_ref (GTK_TYPE_VBOX
);
114 object_class
->finalize
= vg_rule_list_finalize
;
115 gtk_object_class
->destroy
= vg_rule_list_destroy
;
118 = g_signal_new ("rule-added",
119 G_OBJECT_CLASS_TYPE (object_class
),
121 G_STRUCT_OFFSET (VgRuleListClass
, rule_added
),
123 vg_marshal_NONE__POINTER
,
130 rule_editor_dialog_new (GtkWindow
*parent
, VgRule
*rule
)
132 GtkWidget
*dialog
, *editor
;
134 /* FIXME: we should really get this title from somewhere else? */
135 dialog
= gtk_dialog_new_with_buttons (_("Valgrind Suppression"), parent
,
136 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
137 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
138 GTK_STOCK_OK
, GTK_RESPONSE_OK
, NULL
);
141 editor
= vg_rule_editor_new_from_rule (rule
);
143 editor
= vg_rule_editor_new ();
145 gtk_container_set_border_width (GTK_CONTAINER (editor
), 6);
146 gtk_widget_show (editor
);
148 gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), 3);
149 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), editor
, TRUE
, TRUE
, 0);
151 g_object_set_data (G_OBJECT (dialog
), "editor", editor
);
157 add_response_cb (GtkDialog
*dialog
, int response
, gpointer user_data
)
159 VgRuleList
*list
= user_data
;
165 if (response
== GTK_RESPONSE_OK
) {
169 editor
= g_object_get_data (G_OBJECT (dialog
), "editor");
171 name
= vg_rule_editor_get_name (VG_RULE_EDITOR (editor
));
172 if (!name
|| *name
== '\0') {
173 msg
= gtk_message_dialog_new (GTK_WINDOW (dialog
), GTK_DIALOG_MODAL
|
174 GTK_DIALOG_DESTROY_WITH_PARENT
,
177 _("You have forgotten to name your suppression rule."));
179 gtk_dialog_run (GTK_DIALOG (msg
));
180 gtk_widget_destroy (msg
);
184 list
->changed
= TRUE
;
186 rule
= vg_rule_editor_get_rule (VG_RULE_EDITOR (editor
));
188 node
= g_new (RuleNode
, 1);
191 list_append_node (&list
->rules
, (ListNode
*) node
);
193 vg_rule_list_save (list
);
195 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
197 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
198 COL_STRING_NAME
, rule
->name
,
199 COL_POINTER_RULE
, rule
,
200 COL_POINTER_RULE_NODE
, node
, -1);
202 g_signal_emit (list
, signals
[RULE_ADDED
], 0, rule
);
205 gtk_widget_destroy (GTK_WIDGET (dialog
));
209 add_cb (GtkWidget
*button
, gpointer user_data
)
211 VgRuleList
*list
= user_data
;
215 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
216 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
218 dialog
= rule_editor_dialog_new (GTK_WINDOW (parent
), NULL
);
219 g_signal_connect (dialog
, "response", G_CALLBACK (add_response_cb
), list
);
221 gtk_widget_show (dialog
);
225 edit_response_cb (GtkDialog
*dialog
, int response
, gpointer user_data
)
227 VgRuleList
*list
= user_data
;
234 if (response
== GTK_RESPONSE_OK
) {
238 editor
= g_object_get_data (G_OBJECT (dialog
), "editor");
240 name
= vg_rule_editor_get_name (VG_RULE_EDITOR (editor
));
241 if (!name
|| *name
== '\0') {
242 msg
= gtk_message_dialog_new (GTK_WINDOW (dialog
), GTK_DIALOG_MODAL
|
243 GTK_DIALOG_DESTROY_WITH_PARENT
,
246 _("You have forgotten to name your suppression rule."));
248 gtk_dialog_run (GTK_DIALOG (msg
));
249 gtk_widget_destroy (msg
);
253 list
->changed
= TRUE
;
255 rule
= vg_rule_editor_get_rule (VG_RULE_EDITOR (editor
));
257 path
= g_object_get_data (G_OBJECT (dialog
), "path");
258 if (gtk_tree_model_get_iter (list
->model
, &iter
, path
)) {
259 /* replace the old rule node... */
260 gtk_tree_model_get (list
->model
, &iter
, COL_POINTER_RULE_NODE
, &node
, -1);
261 vg_rule_free (node
->rule
);
264 /* create a new rule node... */
265 node
= g_new (RuleNode
, 1);
268 list_append_node (&list
->rules
, (ListNode
*) node
);
270 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
273 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
274 COL_STRING_NAME
, rule
->name
,
275 COL_POINTER_RULE
, rule
,
276 COL_POINTER_RULE_NODE
, node
, -1);
278 /* FIXME: only emit if we've changed something that matters? */
279 g_signal_emit (list
, signals
[RULE_ADDED
], 0, rule
);
282 gtk_widget_destroy (GTK_WIDGET (dialog
));
286 edit_cb (GtkWidget
*button
, gpointer user_data
)
288 VgRuleList
*list
= user_data
;
289 GtkTreeSelection
*selection
;
290 GtkTreeModel
*model
= NULL
;
297 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
298 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
299 gtk_tree_model_get (model
, &iter
, COL_POINTER_RULE
, &rule
, -1);
304 path
= gtk_tree_model_get_path (model
, &iter
);
306 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
307 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
309 dialog
= rule_editor_dialog_new (GTK_WINDOW (parent
), rule
);
310 g_signal_connect (dialog
, "response", G_CALLBACK (edit_response_cb
), list
);
311 g_object_set_data_full (G_OBJECT (dialog
), "path", path
, (GDestroyNotify
) gtk_tree_path_free
);
313 gtk_widget_show (dialog
);
317 remove_cb (GtkWidget
*button
, gpointer user_data
)
319 VgRuleList
*list
= user_data
;
320 GtkTreeSelection
*selection
;
321 GtkTreeModel
*model
= NULL
;
322 RuleNode
*node
= NULL
;
326 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
327 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
))
328 gtk_tree_model_get (model
, &iter
, COL_POINTER_RULE
, &rule
, COL_POINTER_RULE_NODE
, &node
, -1);
333 list
->changed
= TRUE
;
335 gtk_list_store_remove (GTK_LIST_STORE (model
), &iter
);
336 list_node_unlink ((ListNode
*) node
);
342 row_activate_cb (GtkTreeView
*treeview
, GtkTreePath
*path
, GtkTreeViewColumn
*column
, gpointer user_data
)
344 /* double-clicked on a rule, pop the rule up in an editor */
345 edit_cb (NULL
, user_data
);
349 selection_change_cb (GtkTreeSelection
*selection
, gpointer user_data
)
351 VgRuleList
*list
= user_data
;
356 state
= gtk_tree_selection_get_selected (selection
, &model
, &iter
);
358 gtk_widget_grab_focus (list
->add
);
360 gtk_widget_set_sensitive (list
->edit
, state
);
361 gtk_widget_set_sensitive (list
->remove
, state
);
365 vg_rule_list_init (VgRuleList
*list
)
367 GtkTreeSelection
*selection
;
368 GtkCellRenderer
*renderer
;
369 GtkWidget
*hbox
, *vbox
;
372 hbox
= gtk_hbox_new (FALSE
, 6);
374 scrolled
= gtk_scrolled_window_new (NULL
, NULL
);
375 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled
),
376 GTK_POLICY_AUTOMATIC
, GTK_POLICY_AUTOMATIC
);
377 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled
), GTK_SHADOW_IN
);
379 list
->model
= GTK_TREE_MODEL (gtk_list_store_newv (COL_LAST
, col_types
));
380 list
->list
= gtk_tree_view_new_with_model (list
->model
);
382 renderer
= gtk_cell_renderer_text_new ();
383 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (list
->list
), -1, "",
384 renderer
, "text", 0, NULL
);
386 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (list
->list
));
387 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_SINGLE
);
388 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (list
->list
), FALSE
);
390 g_signal_connect (selection
, "changed", G_CALLBACK (selection_change_cb
), list
);
391 g_signal_connect (list
->list
, "row-activated", G_CALLBACK (row_activate_cb
), list
);
393 gtk_widget_show (list
->list
);
394 gtk_container_add (GTK_CONTAINER (scrolled
), list
->list
);
396 gtk_widget_show (scrolled
);
397 gtk_box_pack_start (GTK_BOX (hbox
), scrolled
, TRUE
, TRUE
, 0);
399 vbox
= gtk_vbox_new (FALSE
, 3);
401 list
->add
= gtk_button_new_from_stock (GTK_STOCK_ADD
);
402 g_signal_connect (list
->add
, "clicked", G_CALLBACK (add_cb
), list
);
403 gtk_widget_show (list
->add
);
404 gtk_box_pack_start (GTK_BOX (vbox
), list
->add
, FALSE
, FALSE
, 0);
406 list
->edit
= gtk_button_new_with_mnemonic (_("_Edit"));
407 gtk_widget_set_sensitive (list
->edit
, FALSE
);
408 g_signal_connect (list
->edit
, "clicked", G_CALLBACK (edit_cb
), list
);
409 gtk_widget_show (list
->edit
);
410 gtk_box_pack_start (GTK_BOX (vbox
), list
->edit
, FALSE
, FALSE
, 0);
412 list
->remove
= gtk_button_new_from_stock (GTK_STOCK_REMOVE
);
413 gtk_widget_set_sensitive (list
->remove
, FALSE
);
414 g_signal_connect (list
->remove
, "clicked", G_CALLBACK (remove_cb
), list
);
415 gtk_widget_show (list
->remove
);
416 gtk_box_pack_start (GTK_BOX (vbox
), list
->remove
, FALSE
, FALSE
, 0);
418 gtk_widget_show (vbox
);
419 gtk_box_pack_start (GTK_BOX (hbox
), vbox
, FALSE
, FALSE
, 0);
421 gtk_widget_show (hbox
);
422 gtk_container_add (GTK_CONTAINER (list
), hbox
);
425 list_init (&list
->rules
);
426 list
->changed
= FALSE
;
427 list
->filename
= NULL
;
435 vg_rule_list_finalize (GObject
*obj
)
437 VgRuleList
*list
= (VgRuleList
*) obj
;
440 g_free (list
->filename
);
442 vg_rule_parser_free (list
->parser
);
444 n
= (RuleNode
*) list
->rules
.head
;
445 while (n
->next
!= NULL
) {
447 vg_rule_free (n
->rule
);
452 G_OBJECT_CLASS (parent_class
)->finalize (obj
);
456 vg_rule_list_destroy (GtkObject
*obj
)
458 VgRuleList
*list
= (VgRuleList
*) obj
;
460 if (list
->gio
!= NULL
) {
461 g_io_channel_close (list
->gio
);
462 g_io_channel_unref (list
->gio
);
467 GTK_OBJECT_CLASS (parent_class
)->destroy (obj
);
472 load_rule_cb (VgRuleParser
*parser
, VgRule
*rule
, gpointer user_data
)
474 VgRuleList
*list
= user_data
;
478 node
= g_new (RuleNode
, 1);
481 list_append_node (&list
->rules
, (ListNode
*) node
);
483 gtk_list_store_append (GTK_LIST_STORE (list
->model
), &iter
);
484 gtk_list_store_set (GTK_LIST_STORE (list
->model
), &iter
,
485 COL_STRING_NAME
, rule
->name
,
486 COL_POINTER_RULE
, rule
,
487 COL_POINTER_RULE_NODE
, node
, -1);
491 load_rules_step_cb (GIOChannel
*gio
, GIOCondition condition
, gpointer user_data
)
493 VgRuleList
*list
= user_data
;
495 if ((condition
& G_IO_IN
) && vg_rule_parser_step (list
->parser
) <= 0)
498 if (condition
& G_IO_HUP
)
505 vg_rule_parser_free (list
->parser
);
508 g_io_channel_close (list
->gio
);
509 g_io_channel_unref (list
->gio
);
517 load_rules (GtkWidget
*widget
, gpointer user_data
)
519 VgRuleList
*list
= user_data
;
522 list
->changed
= FALSE
;
524 if (list
->show_id
!= 0) {
525 g_signal_handler_disconnect (list
, list
->show_id
);
529 if (list
->filename
== NULL
)
532 if ((fd
= open (list
->filename
, O_RDONLY
)) == -1)
535 list
->parser
= vg_rule_parser_new (fd
, load_rule_cb
, list
);
536 list
->gio
= g_io_channel_unix_new (fd
);
537 list
->load_id
= g_io_add_watch (list
->gio
, G_IO_IN
| G_IO_HUP
, load_rules_step_cb
, list
);
542 vg_rule_list_new (const char *filename
)
546 list
= g_object_new (VG_TYPE_RULE_LIST
, NULL
);
547 list
->filename
= g_strdup (filename
);
549 /* suppressions file may be large, so don't actually load it
550 until the user shows it for the first time */
551 list
->show_id
= g_signal_connect (list
, "map", G_CALLBACK (load_rules
), list
);
553 return GTK_WIDGET (list
);
558 vg_rule_list_set_filename (VgRuleList
*list
, const char *filename
)
562 g_free (list
->filename
);
563 list
->filename
= g_strdup (filename
);
565 if (list
->show_id
!= 0) {
566 /* good, the user hasn't shown the dialog yet.
567 * this means that we don't have to do anything special!
570 if (list
->load_id
!= 0) {
571 /* this means the file is currently being loaded. ugh */
572 vg_rule_parser_free (list
->parser
);
573 g_io_channel_close (list
->gio
);
574 g_io_channel_unref (list
->gio
);
579 n
= (RuleNode
*) list
->rules
.head
;
580 while (n
->next
!= NULL
) {
582 vg_rule_free (n
->rule
);
587 gtk_list_store_clear (GTK_LIST_STORE (list
->model
));
589 if (!GTK_WIDGET_MAPPED (list
))
590 list
->show_id
= g_signal_connect (list
, "map", G_CALLBACK (load_rules
), list
);
592 load_rules (GTK_WIDGET (list
), list
);
598 vg_rule_list_save (VgRuleList
*list
)
600 GtkWidget
*parent
, *msg
;
601 char *filename
= NULL
;
602 const char *basename
;
606 /* only save if we need to... */
610 if (list
->filename
== NULL
)
613 if (!(basename
= strrchr (list
->filename
, '/')))
614 basename
= list
->filename
;
618 filename
= g_strdup_printf ("%.*s.#%s", (basename
- list
->filename
),
619 list
->filename
, basename
);
621 if ((fd
= open (filename
, O_WRONLY
| O_CREAT
| O_TRUNC
| O_EXCL
, 0666)) == -1)
624 if (vg_suppressions_file_write_header (fd
, "This Valgrind suppresion file was generated by Alleyoop") == -1)
627 node
= (RuleNode
*) list
->rules
.head
;
628 while (node
->next
!= NULL
) {
629 if (vg_suppressions_file_append_rule (fd
, node
->rule
) == -1)
637 if (rename (filename
, list
->filename
) == -1)
646 parent
= gtk_widget_get_toplevel (GTK_WIDGET (list
));
647 parent
= GTK_WIDGET_TOPLEVEL (parent
) ? parent
: NULL
;
649 msg
= gtk_message_dialog_new (GTK_WINDOW (parent
), GTK_DIALOG_MODAL
,
650 GTK_MESSAGE_ERROR
, GTK_BUTTONS_CLOSE
,
651 _("Cannot save suppression rules: %s"),
652 list
->filename
? g_strerror (errno
) :
653 _("You have not set a suppressions file in your settings."));
655 g_signal_connect_swapped (msg
, "response", G_CALLBACK (gtk_widget_destroy
), msg
);
657 gtk_widget_show (msg
);
672 vg_rule_list_add_rule (VgRuleList
*list
, const char *title
, GtkWindow
*parent
, VgErrorSummary
*summary
)
674 GtkWidget
*dialog
, *editor
;
677 dialog
= gtk_dialog_new_with_buttons (title
, parent
, GTK_DIALOG_MODAL
|
678 GTK_DIALOG_DESTROY_WITH_PARENT
,
679 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
680 GTK_STOCK_OK
, GTK_RESPONSE_OK
, NULL
);
683 editor
= vg_rule_editor_new_from_summary (summary
);
685 editor
= vg_rule_editor_new ();
687 gtk_widget_show (editor
);
688 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog
)->vbox
), editor
, TRUE
, TRUE
, 0);
689 g_signal_connect (dialog
, "response", G_CALLBACK (add_response_cb
), list
);
690 g_object_set_data (G_OBJECT (dialog
), "editor", editor
);
692 if (list
->filename
== NULL
) {
693 gconf
= gconf_client_get_default ();
695 // FIXME: hardcoded path
696 list
->filename
= anjuta_util_get_user_config_file_path ("valgrind.supp", NULL
);
697 gconf_client_set_string (gconf
, SUPPRESSIONS_KEY
, list
->filename
, NULL
);
698 g_object_unref (gconf
);
701 /* FIXME: what if the rules haven't finished loading before the user adds this rule? */
703 gtk_widget_show (dialog
);