1 /*
2 ** 2009-12-28 -	Implementation of the nagging configuration. This mainly gives the user an option to forget
3 **		which dialogs have been suppressed, and hooks into the save/load of configure data.
4 */
5 
6 #include "gentoo.h"
7 
8 #include "configure.h"
9 #include "xmlutil.h"
10 
11 #include "cfg_nag.h"
12 
13 #define	NODE	"Nagging"
14 
15 /* ----------------------------------------------------------------------------------------- */
16 
17 typedef struct {
18 	GtkWidget	*vbox;
19 	GtkWidget	*label;
20 	GtkWidget	*reset;
21 	gboolean	do_reset;
22 
23 	MainInfo	*min;
24 	gboolean	modified;
25 } P_Nag;
26 
27 static P_Nag	the_page;
28 
29 /* ----------------------------------------------------------------------------------------- */
30 
cng_initialize(NagInfo * ni)31 void cng_initialize(NagInfo *ni)
32 {
33 	ni->ignored = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
34 }
35 
cng_num_ignored(const NagInfo * ni)36 gsize cng_num_ignored(const NagInfo *ni)
37 {
38 	return ni != NULL ? g_hash_table_size(ni->ignored) : 0u;
39 }
40 
cng_is_ignored(const NagInfo * ni,const gchar * tag)41 gboolean cng_is_ignored(const NagInfo *ni, const gchar *tag)
42 {
43 	if(ni == NULL || tag == NULL)
44 		return FALSE;
45 	if(g_hash_table_lookup_extended(ni->ignored, tag, NULL, NULL))
46 		return TRUE;
47 	return FALSE;
48 }
49 
50 /* 2009-12-29 -	Adds the given tag to the set of ignored tags. The tag is copied. */
cng_ignore(NagInfo * ni,const gchar * tag)51 void cng_ignore(NagInfo *ni, const gchar *tag)
52 {
53 	gpointer	key;
54 
55 	if(ni == NULL || tag == NULL)
56 		return;
57 	/* Protect against multiple insertions, since that would leak memory. */
58 	if(g_hash_table_lookup_extended(ni->ignored, (gpointer) tag, NULL, NULL))
59 		return;
60 	if((key = g_strdup(tag)) != NULL)
61 	{
62 		g_hash_table_insert(ni->ignored, key, NULL);
63 	}
64 }
65 
cng_reset(NagInfo * ni)66 void cng_reset(NagInfo *ni)
67 {
68 	if(ni != NULL)
69 		g_hash_table_remove_all(ni->ignored);
70 }
71 
72 /* ----------------------------------------------------------------------------------------- */
73 
set_label(GtkWidget * label,gsize num)74 static void set_label(GtkWidget *label, gsize num)
75 {
76 	gchar	buf[256];
77 
78 	g_snprintf(buf, sizeof buf, _("Click the button below to reset the %zu stored 'Don't show this dialog again' responses."), num);
79 	gtk_label_set_markup(GTK_LABEL(label), buf);
80 }
81 
evt_reset_clicked(GtkWidget * wid,gpointer user)82 static void evt_reset_clicked(GtkWidget *wid, gpointer user)
83 {
84 	P_Nag	*page = user;
85 
86 	page->do_reset = TRUE;
87 	page->modified = TRUE;
88 	set_label(page->label, 0);
89 	gtk_widget_set_sensitive(page->reset, FALSE);
90 }
91 
cng_init(MainInfo * min,gchar ** name)92 static GtkWidget * cng_init(MainInfo *min, gchar **name)
93 {
94 	P_Nag	*page = &the_page;
95 	gsize	num;
96 
97 	if(name == NULL)
98 		return NULL;
99 
100 	*name = _("Nagging");
101 
102 	page->min = min;
103 	page->modified = FALSE;
104 
105 	page->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
106 	page->label = gtk_label_new(NULL);
107 	num = cng_num_ignored(&min->cfg.nag);
108 	set_label(page->label, num);
109 	gtk_box_pack_start(GTK_BOX(page->vbox), page->label, FALSE, FALSE, 5);
110 	page->reset = gtk_button_new_with_label(_("Reset All"));
111 	gtk_widget_set_sensitive(page->reset, num > 0);
112 	g_signal_connect(G_OBJECT(page->reset), "clicked", G_CALLBACK(evt_reset_clicked), page);
113 	gtk_box_pack_start(GTK_BOX(page->vbox), page->reset, FALSE, FALSE, 0);
114 
115 	gtk_widget_show_all(page->vbox);
116 
117 	return page->vbox;
118 }
119 
120 /* ----------------------------------------------------------------------------------------- */
121 
cng_update(MainInfo * min)122 static void cng_update(MainInfo *min)
123 {
124 	P_Nag	*page = &the_page;
125 
126 	page->do_reset = FALSE;
127 	page->modified = FALSE;
128 }
129 
130 /* ----------------------------------------------------------------------------------------- */
131 
cng_accept(MainInfo * min)132 static void cng_accept(MainInfo *min)
133 {
134 	P_Nag	*page = &the_page;
135 
136 	if(!page->modified)
137 		return;
138 	if(page->do_reset)
139 		cng_reset(&min->cfg.nag);
140 	page->modified = FALSE;
141 }
142 
143 /* ----------------------------------------------------------------------------------------- */
144 
cng_save(MainInfo * min,FILE * out)145 static gint cng_save(MainInfo *min, FILE *out)
146 {
147 	GHashTableIter	iter;
148 	gpointer	key;
149 
150 	xml_put_node_open(out, NODE);
151 	g_hash_table_iter_init(&iter, min->cfg.nag.ignored);
152 	while(g_hash_table_iter_next(&iter, &key, NULL))
153 	{
154 		xml_put_text(out, "ignore", key);
155 	}
156 	xml_put_node_close(out, NODE);
157 
158 	return TRUE;
159 }
160 
visit_ignore(const XmlNode * child,gpointer user)161 static void visit_ignore(const XmlNode *child, gpointer user)
162 {
163 	const gchar	*text;
164 
165 	if(xml_get_text(child, "ignore", &text))
166 		cng_ignore(&((MainInfo *) user)->cfg.nag, text);
167 }
168 
cng_load(MainInfo * min,const XmlNode * node)169 static void cng_load(MainInfo *min, const XmlNode *node)
170 {
171 	const XmlNode	*root;
172 
173 	if((root = xml_tree_search(node, NODE)) != NULL)
174 		xml_node_visit_children(root, visit_ignore, min);
175 }
176 
177 /* ----------------------------------------------------------------------------------------- */
178 
cng_describe(MainInfo * min)179 const CfgModule * cng_describe(MainInfo *min)
180 {
181 	static const CfgModule	desc = { NODE, cng_init, cng_update, cng_accept, cng_save, cng_load, NULL };
182 
183 	return &desc;
184 }
185