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, ¬i,
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