1 /* X-Chat
2  * Copyright (C) 1998 Peter Zelezny.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <fcntl.h>
25 #include "fe-gtk.h"
26 
27 #include "../common/hexchat.h"
28 #include "../common/ignore.h"
29 #include "../common/cfgfiles.h"
30 #include "../common/fe.h"
31 #include "gtkutil.h"
32 #include "maingui.h"
33 
34 enum
35 {
36 	MASK_COLUMN,
37 	CHAN_COLUMN,
38 	PRIV_COLUMN,
39 	NOTICE_COLUMN,
40 	CTCP_COLUMN,
41 	DCC_COLUMN,
42 	INVITE_COLUMN,
43 	UNIGNORE_COLUMN,
44 	N_COLUMNS
45 };
46 
47 static GtkWidget *ignorewin = 0;
48 
49 static GtkWidget *num_ctcp;
50 static GtkWidget *num_priv;
51 static GtkWidget *num_chan;
52 static GtkWidget *num_noti;
53 static GtkWidget *num_invi;
54 
55 static GtkTreeModel *
get_store(void)56 get_store (void)
57 {
58 	return gtk_tree_view_get_model (g_object_get_data (G_OBJECT (ignorewin), "view"));
59 }
60 
61 static int
ignore_get_flags(GtkTreeModel * model,GtkTreeIter * iter)62 ignore_get_flags (GtkTreeModel *model, GtkTreeIter *iter)
63 {
64 	gboolean chan, priv, noti, ctcp, dcc, invi, unig;
65 	int flags = 0;
66 
67 	gtk_tree_model_get (model, iter, 1, &chan, 2, &priv, 3, &noti,
68 	                    4, &ctcp, 5, &dcc, 6, &invi, 7, &unig, -1);
69 	if (chan)
70 		flags |= IG_CHAN;
71 	if (priv)
72 		flags |= IG_PRIV;
73 	if (noti)
74 		flags |= IG_NOTI;
75 	if (ctcp)
76 		flags |= IG_CTCP;
77 	if (dcc)
78 		flags |= IG_DCC;
79 	if (invi)
80 		flags |= IG_INVI;
81 	if (unig)
82 		flags |= IG_UNIG;
83 	return flags;
84 }
85 
86 static void
mask_edited(GtkCellRendererText * render,gchar * path,gchar * new,gpointer dat)87 mask_edited (GtkCellRendererText *render, gchar *path, gchar *new, gpointer dat)
88 {
89 	GtkListStore *store = GTK_LIST_STORE (get_store ());
90 	GtkTreeIter iter;
91 	char *old;
92 	int flags;
93 
94 	gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
95 	gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &old, -1);
96 
97 	if (!strcmp (old, new))	/* no change */
98 		;
99 	else if (ignore_exists (new))	/* duplicate, ignore */
100 		fe_message (_("That mask already exists."), FE_MSG_ERROR);
101 	else
102 	{
103 		/* delete old mask, and add new one with original flags */
104 		ignore_del (old, NULL);
105 		flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
106 		ignore_add (new, flags, TRUE);
107 
108 		/* update tree */
109 		gtk_list_store_set (store, &iter, MASK_COLUMN, new, -1);
110 	}
111 	g_free (old);
112 
113 }
114 
115 static void
option_toggled(GtkCellRendererToggle * render,gchar * path,gpointer data)116 option_toggled (GtkCellRendererToggle *render, gchar *path, gpointer data)
117 {
118 	GtkListStore *store = GTK_LIST_STORE (get_store ());
119 	GtkTreeIter iter;
120 	int col_id = GPOINTER_TO_INT (data);
121 	gboolean active;
122 	char *mask;
123 	int flags;
124 
125 	gtkutil_treemodel_string_to_iter (GTK_TREE_MODEL (store), path, &iter);
126 
127 	/* update model */
128 	gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, col_id, &active, -1);
129 	gtk_list_store_set (store, &iter, col_id, !active, -1);
130 
131 	/* update ignore list */
132 	gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &mask, -1);
133 	flags = ignore_get_flags (GTK_TREE_MODEL (store), &iter);
134 	if (ignore_add (mask, flags, TRUE) != 2)
135 		g_warning ("ignore treeview is out of sync!\n");
136 
137 	g_free (mask);
138 }
139 
140 static GtkWidget *
ignore_treeview_new(GtkWidget * box)141 ignore_treeview_new (GtkWidget *box)
142 {
143 	GtkListStore *store;
144 	GtkWidget *view;
145 	GtkTreeViewColumn *col;
146 	GtkCellRenderer *render;
147 	int col_id;
148 
149 	store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING,
150 	                            G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
151 	                            G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
152 	                            G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
153 	                            G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
154 	g_return_val_if_fail (store != NULL, NULL);
155 
156 	view = gtkutil_treeview_new (box, GTK_TREE_MODEL (store),
157 	                             NULL,
158 	                             MASK_COLUMN, _("Mask"),
159 	                             CHAN_COLUMN, _("Channel"),
160 	                             PRIV_COLUMN, _("Private"),
161 	                             NOTICE_COLUMN, _("Notice"),
162 	                             CTCP_COLUMN, _("CTCP"),
163 	                             DCC_COLUMN, _("DCC"),
164 	                             INVITE_COLUMN, _("Invite"),
165 	                             UNIGNORE_COLUMN, _("Unignore"),
166 	                             -1);
167 
168 	gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
169 	gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 0), TRUE);
170 
171 	/* attach to signals and customise columns */
172 	for (col_id=0; (col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), col_id));
173 	     col_id++)
174 	{
175 		GList *list = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (col));
176 		GList *tmp;
177 
178 		for (tmp = list; tmp; tmp = tmp->next)
179 		{
180 			render = tmp->data;
181 			if (col_id > 0)	/* it's a toggle button column */
182 			{
183 				g_signal_connect (render, "toggled", G_CALLBACK (option_toggled),
184 				                  GINT_TO_POINTER (col_id));
185 			} else	/* mask column */
186 			{
187 				g_object_set (G_OBJECT (render), "editable", TRUE, NULL);
188 				g_signal_connect (render, "edited", G_CALLBACK (mask_edited), NULL);
189 				/* make this column sortable */
190 				gtk_tree_view_column_set_sort_column_id (col, col_id);
191 				gtk_tree_view_column_set_min_width (col, 272);
192 			}
193 			/* centre titles */
194 			gtk_tree_view_column_set_alignment (col, 0.5);
195 		}
196 
197 		g_list_free (list);
198 	}
199 
200 	gtk_widget_show (view);
201 	return view;
202 }
203 
204 static void
ignore_delete_entry_clicked(GtkWidget * wid,struct session * sess)205 ignore_delete_entry_clicked (GtkWidget * wid, struct session *sess)
206 {
207 	GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
208 	GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view));
209 	GtkTreeIter iter;
210 	GtkTreePath *path;
211 	char *mask = NULL;
212 
213 	if (gtkutil_treeview_get_selected (view, &iter, 0, &mask, -1))
214 	{
215 		/* delete this row, select next one */
216 		if (gtk_list_store_remove (store, &iter))
217 		{
218 			path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
219 			gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
220 			gtk_tree_view_set_cursor (view, path, NULL, FALSE);
221 			gtk_tree_path_free (path);
222 		}
223 
224 		ignore_del (mask, NULL);
225 		g_free (mask);
226 	}
227 }
228 
229 static void
ignore_store_new(int cancel,char * mask,gpointer data)230 ignore_store_new (int cancel, char *mask, gpointer data)
231 {
232 	GtkTreeView *view = g_object_get_data (G_OBJECT (ignorewin), "view");
233 	GtkListStore *store = GTK_LIST_STORE (get_store ());
234 	GtkTreeIter iter;
235 	GtkTreePath *path;
236 	int flags = IG_CHAN | IG_PRIV | IG_NOTI | IG_CTCP | IG_DCC | IG_INVI;
237 
238 	if (cancel)
239 		return;
240 	/* check if it already exists */
241 	if (ignore_exists (mask))
242 	{
243 		fe_message (_("That mask already exists."), FE_MSG_ERROR);
244 		return;
245 	}
246 
247 	ignore_add (mask, flags, TRUE);
248 
249 	gtk_list_store_append (store, &iter);
250 	/* ignore everything by default */
251 	gtk_list_store_set (store, &iter, 0, mask, 1, TRUE, 2, TRUE, 3, TRUE,
252 	                    4, TRUE, 5, TRUE, 6, TRUE, 7, FALSE, -1);
253 	/* make sure the new row is visible and selected */
254 	path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter);
255 	gtk_tree_view_scroll_to_cell (view, path, NULL, TRUE, 1.0, 0.0);
256 	gtk_tree_view_set_cursor (view, path, NULL, FALSE);
257 	gtk_tree_path_free (path);
258 }
259 
260 static void
ignore_clear_cb(GtkDialog * dialog,gint response)261 ignore_clear_cb (GtkDialog *dialog, gint response)
262 {
263 	GtkListStore *store = GTK_LIST_STORE (get_store ());
264 	GtkTreeIter iter;
265 	char *mask;
266 
267 	gtk_widget_destroy (GTK_WIDGET (dialog));
268 
269 	if (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter) && response == GTK_RESPONSE_OK)
270 	{
271 		/* remove from ignore_list */
272 		do
273 		{
274 			mask = NULL;
275 			gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, MASK_COLUMN, &mask, -1);
276 			ignore_del (mask, NULL);
277 			g_free (mask);
278 		}
279 		while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
280 
281 		/* remove from GUI */
282 		gtk_list_store_clear (store);
283 	}
284 }
285 
286 static void
ignore_clear_entry_clicked(GtkWidget * wid)287 ignore_clear_entry_clicked (GtkWidget * wid)
288 {
289 	GtkWidget *dialog;
290 
291 	dialog = gtk_message_dialog_new (NULL, 0,
292 								GTK_MESSAGE_QUESTION, GTK_BUTTONS_OK_CANCEL,
293 					_("Are you sure you want to remove all ignores?"));
294 	g_signal_connect (G_OBJECT (dialog), "response",
295 							G_CALLBACK (ignore_clear_cb), NULL);
296 	gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
297 	gtk_widget_show (dialog);
298 }
299 
300 static void
ignore_new_entry_clicked(GtkWidget * wid,struct session * sess)301 ignore_new_entry_clicked (GtkWidget * wid, struct session *sess)
302 {
303 	fe_get_str (_("Enter mask to ignore:"), "nick!userid@host.com",
304 	            ignore_store_new, NULL);
305 
306 }
307 
308 static void
close_ignore_gui_callback(void)309 close_ignore_gui_callback (void)
310 {
311 	ignore_save ();
312 	ignorewin = 0;
313 }
314 
315 static GtkWidget *
ignore_stats_entry(GtkWidget * box,char * label,int value)316 ignore_stats_entry (GtkWidget * box, char *label, int value)
317 {
318 	GtkWidget *wid;
319 	char buf[16];
320 
321 	sprintf (buf, "%d", value);
322 	gtkutil_label_new (label, box);
323 	wid = gtkutil_entry_new (16, box, 0, 0);
324 	gtk_widget_set_size_request (wid, 30, -1);
325 	gtk_editable_set_editable (GTK_EDITABLE (wid), FALSE);
326 	gtk_widget_set_sensitive (GTK_WIDGET (wid), FALSE);
327 	gtk_entry_set_text (GTK_ENTRY (wid), buf);
328 
329 	return wid;
330 }
331 
332 void
ignore_gui_open()333 ignore_gui_open ()
334 {
335 	GtkWidget *vbox, *box, *stat_box, *frame;
336 	GtkWidget *view;
337 	GtkListStore *store;
338 	GtkTreeIter iter;
339 	GSList *temp = ignore_list;
340 	char *mask;
341 	gboolean private, chan, notice, ctcp, dcc, invite, unignore;
342 	char buf[128];
343 
344 	if (ignorewin)
345 	{
346 		mg_bring_tofront (ignorewin);
347 		return;
348 	}
349 
350 	g_snprintf(buf, sizeof(buf), _("Ignore list - %s"), _(DISPLAY_NAME));
351 	ignorewin =
352 			  mg_create_generic_tab ("IgnoreList", buf, FALSE, TRUE,
353 											close_ignore_gui_callback,
354 											NULL, 700, 300, &vbox, 0);
355 	gtkutil_destroy_on_esc (ignorewin);
356 
357 	view = ignore_treeview_new (vbox);
358 	g_object_set_data (G_OBJECT (ignorewin), "view", view);
359 
360 	frame = gtk_frame_new (_("Ignore Stats:"));
361 	gtk_widget_show (frame);
362 
363 	stat_box = gtk_hbox_new (0, 2);
364 	gtk_container_set_border_width (GTK_CONTAINER (stat_box), 6);
365 	gtk_container_add (GTK_CONTAINER (frame), stat_box);
366 	gtk_widget_show (stat_box);
367 
368 	num_chan = ignore_stats_entry (stat_box, _("Channel:"), ignored_chan);
369 	num_priv = ignore_stats_entry (stat_box, _("Private:"), ignored_priv);
370 	num_noti = ignore_stats_entry (stat_box, _("Notice:"), ignored_noti);
371 	num_ctcp = ignore_stats_entry (stat_box, _("CTCP:"), ignored_ctcp);
372 	num_invi = ignore_stats_entry (stat_box, _("Invite:"), ignored_invi);
373 
374 	gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, 5);
375 
376 	box = gtk_hbutton_box_new ();
377 	gtk_button_box_set_layout (GTK_BUTTON_BOX (box), GTK_BUTTONBOX_SPREAD);
378 	gtk_box_pack_start (GTK_BOX (vbox), box, FALSE, FALSE, 2);
379 	gtk_container_set_border_width (GTK_CONTAINER (box), 5);
380 	gtk_widget_show (box);
381 
382 	gtkutil_button (box, GTK_STOCK_NEW, 0, ignore_new_entry_clicked, 0,
383 						 _("Add..."));
384 	gtkutil_button (box, GTK_STOCK_DELETE, 0, ignore_delete_entry_clicked,
385 						 0, _("Delete"));
386 	gtkutil_button (box, GTK_STOCK_CLEAR, 0, ignore_clear_entry_clicked,
387 						 0, _("Clear"));
388 
389 	store = GTK_LIST_STORE (gtk_tree_view_get_model (GTK_TREE_VIEW (view)));
390 
391 	while (temp)
392 	{
393 		struct ignore *ignore = temp->data;
394 
395 		mask = ignore->mask;
396 		chan = (ignore->type & IG_CHAN);
397 		private = (ignore->type & IG_PRIV);
398 		notice = (ignore->type & IG_NOTI);
399 		ctcp = (ignore->type & IG_CTCP);
400 		dcc = (ignore->type & IG_DCC);
401 		invite = (ignore->type & IG_INVI);
402 		unignore = (ignore->type & IG_UNIG);
403 
404 		gtk_list_store_append (store, &iter);
405 		gtk_list_store_set (store, &iter,
406 		                    MASK_COLUMN, mask,
407 		                    CHAN_COLUMN, chan,
408 		                    PRIV_COLUMN, private,
409 		                    NOTICE_COLUMN, notice,
410 		                    CTCP_COLUMN, ctcp,
411 		                    DCC_COLUMN, dcc,
412 		                    INVITE_COLUMN, invite,
413 		                    UNIGNORE_COLUMN, unignore,
414 		                    -1);
415 
416 		temp = temp->next;
417 	}
418 	gtk_widget_show (ignorewin);
419 }
420 
421 void
fe_ignore_update(int level)422 fe_ignore_update (int level)
423 {
424 	/* some ignores have changed via /ignore, we should update
425 	   the gui now */
426 	/* level 1 = the list only. */
427 	/* level 2 = the numbers only. */
428 	/* for now, ignore level 1, since the ignore GUI isn't realtime,
429 	   only saved when you click OK */
430 	char buf[16];
431 
432 	if (level == 2 && ignorewin)
433 	{
434 		sprintf (buf, "%d", ignored_ctcp);
435 		gtk_entry_set_text (GTK_ENTRY (num_ctcp), buf);
436 
437 		sprintf (buf, "%d", ignored_noti);
438 		gtk_entry_set_text (GTK_ENTRY (num_noti), buf);
439 
440 		sprintf (buf, "%d", ignored_chan);
441 		gtk_entry_set_text (GTK_ENTRY (num_chan), buf);
442 
443 		sprintf (buf, "%d", ignored_invi);
444 		gtk_entry_set_text (GTK_ENTRY (num_invi), buf);
445 
446 		sprintf (buf, "%d", ignored_priv);
447 		gtk_entry_set_text (GTK_ENTRY (num_priv), buf);
448 	}
449 }
450