merge_all_tap_menus() has been moved to menus.c.
[obnox/wireshark/wip.git] / gtk / proto_dlg.c
1 /* proto_dlg.c
2  *
3  * $Id$
4  *
5  * Laurent Deniel <laurent.deniel@free.fr>
6  *
7  * Wireshark - Network traffic analyzer
8  * By Gerald Combs <gerald@wireshark.org>
9  * Copyright 2000 Gerald Combs
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32 #include <gdk/gdkkeysyms.h>
33 #if GTK_CHECK_VERSION(3,0,0)
34 # include <gdk/gdkkeysyms-compat.h>
35 #endif
36
37 #include <epan/prefs.h>
38 #include <epan/filesystem.h>
39
40 #include "../util.h"
41 #include "../simple_dialog.h"
42 #include "../disabled_protos.h"
43
44 #include "gtk/main.h"
45 #include "gtk/gui_utils.h"
46 #include "gtk/dlg_utils.h"
47 #include "gtk/proto_dlg.h"
48 #include "gtk/help_dlg.h"
49
50
51 static gboolean proto_delete_event_cb(GtkWidget *, GdkEvent *, gpointer);
52 static void proto_ok_cb(GtkWidget *, gpointer);
53 static void proto_apply_cb(GtkWidget *, gpointer);
54 static void proto_save_cb(GtkWidget *, gpointer);
55 static void proto_cancel_cb(GtkWidget *, gpointer);
56 static void proto_destroy_cb(GtkWidget *, gpointer);
57
58 static void show_proto_selection(GtkListStore *proto_store);
59 static gboolean set_proto_selection(GtkWidget *);
60 static gboolean revert_proto_selection(void);
61
62 static void proto_col_clicked_cb(GtkWidget *col _U_, GtkWidget *proto_list);
63
64 static void toggle_all_cb(GtkWidget *button, gpointer parent_w);
65 static void enable_all_cb(GtkWidget *button, gpointer parent_w);
66 static void disable_all_cb(GtkWidget *button, gpointer parent_w);
67 static void status_toggled(GtkCellRendererToggle *, gchar *, gpointer);
68
69 static GtkWidget *proto_w = NULL;
70
71 /* list of protocols */
72 static GSList *protocol_list = NULL;
73
74 typedef struct protocol_data {
75   const char  *name;
76   const char  *abbrev;
77   int         hfinfo_index;
78   gboolean    enabled;
79   gboolean    was_enabled;
80   GtkTreeIter iter;
81 } protocol_data_t;
82
83 #define DISABLED "Disabled"
84 #define STATUS_TXT(x) ((x) ? "" : DISABLED)
85
86 void
87 proto_cb(GtkWidget *w _U_, gpointer data _U_)
88 {
89
90   GtkWidget *main_vb, *bbox, *proto_list, *label, *proto_sw, *proto_frame,
91             *proto_vb, *button, *ok_bt, *apply_bt, *save_bt, *cancel_bt, *help_bt;
92   const gchar *titles[] = { "Status", "Protocol", "Description" };
93   GtkListStore *proto_store;
94   GtkCellRenderer *proto_rend;
95   GtkTreeViewColumn *proto_col;
96
97
98   if (proto_w != NULL) {
99     reactivate_window(proto_w);
100     return;
101   }
102
103   proto_w = dlg_conf_window_new("Wireshark: Enabled Protocols");
104   gtk_window_set_default_size(GTK_WINDOW(proto_w), DEF_WIDTH , DEF_HEIGHT);
105
106   /* Container for each row of widgets */
107
108   main_vb = gtk_vbox_new(FALSE, 6);
109   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
110   gtk_container_add(GTK_CONTAINER(proto_w), main_vb);
111   gtk_widget_show(main_vb);
112
113   /* Protocol selection list ("enable/disable" protocols) */
114
115   proto_frame = gtk_frame_new("Enabled Protocols");
116   gtk_box_pack_start(GTK_BOX(main_vb), proto_frame, TRUE, TRUE, 0);
117   gtk_widget_show(proto_frame);
118
119   /* Protocol list */
120
121   proto_vb = gtk_vbox_new(FALSE, 0);
122   gtk_container_add(GTK_CONTAINER(proto_frame), proto_vb);
123   gtk_container_set_border_width(GTK_CONTAINER(proto_vb), 5);
124   gtk_widget_show(proto_vb);
125
126   proto_sw = scrolled_window_new(NULL, NULL);
127   gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(proto_sw),
128                                    GTK_SHADOW_IN);
129   gtk_box_pack_start(GTK_BOX(proto_vb), proto_sw, TRUE, TRUE, 0);
130   gtk_widget_show(proto_sw);
131
132   proto_store = gtk_list_store_new(4, G_TYPE_BOOLEAN, G_TYPE_STRING,
133                                    G_TYPE_STRING, G_TYPE_POINTER);
134   show_proto_selection(proto_store);
135   /* default sort on "abbrev" column */
136   gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(proto_store), 1,
137                                        GTK_SORT_ASCENDING);
138
139   proto_list = tree_view_new(GTK_TREE_MODEL(proto_store));
140   gtk_container_add(GTK_CONTAINER(proto_sw), proto_list);
141
142   proto_rend = gtk_cell_renderer_toggle_new();
143   g_signal_connect(proto_rend, "toggled", G_CALLBACK(status_toggled), proto_store);
144   proto_col = gtk_tree_view_column_new_with_attributes(titles[0], proto_rend, "active", 0, NULL);
145   gtk_tree_view_column_set_sort_column_id(proto_col, 0);
146   g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
147   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
148
149   proto_rend = gtk_cell_renderer_text_new();
150   proto_col = gtk_tree_view_column_new_with_attributes(titles[1], proto_rend, "text", 1, NULL);
151   gtk_tree_view_column_set_sort_column_id(proto_col, 1);
152   g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
153   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
154
155   proto_rend = gtk_cell_renderer_text_new();
156   proto_col = gtk_tree_view_column_new_with_attributes(titles[2], proto_rend, "text", 2, NULL);
157   gtk_tree_view_column_set_sort_column_id(proto_col, 2);
158   g_signal_connect(proto_col, "clicked", G_CALLBACK(proto_col_clicked_cb), proto_list);
159   gtk_tree_view_append_column(GTK_TREE_VIEW(proto_list), proto_col);
160
161   gtk_tree_view_set_search_column(GTK_TREE_VIEW(proto_list), 1); /* col 1 in the *model* */
162   g_object_unref(G_OBJECT(proto_store));
163   gtk_widget_show(proto_list);
164
165   label = gtk_label_new("Disabling a protocol prevents higher "
166                         "layer protocols from being displayed");
167   gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.5f);
168   gtk_widget_show(label);
169   gtk_box_pack_start(GTK_BOX(proto_vb), label, FALSE, FALSE, 5);
170
171   bbox = gtk_hbutton_box_new();
172   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
173   gtk_box_set_spacing(GTK_BOX(bbox), 5);
174   gtk_box_pack_start(GTK_BOX(proto_vb), bbox, FALSE, FALSE, 0);
175   gtk_widget_show(bbox);
176
177   /* Enable All */
178   button = gtk_button_new_with_label("Enable All");
179   g_signal_connect(button, "clicked", G_CALLBACK(enable_all_cb), proto_list);
180   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
181   gtk_widget_show(button);
182
183   /* Disable All */
184   button = gtk_button_new_with_label("Disable All");
185   g_signal_connect(button, "clicked", G_CALLBACK(disable_all_cb), proto_list);
186   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
187   gtk_widget_show(button);
188
189   /* Invert */
190   button = gtk_button_new_with_label("Invert");
191   g_signal_connect(button, "clicked", G_CALLBACK(toggle_all_cb), proto_list);
192   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
193   gtk_widget_show(button);
194
195
196   /* Button row */
197   bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_APPLY, GTK_STOCK_SAVE, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
198   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
199   gtk_widget_show(bbox);
200
201   ok_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
202   g_signal_connect(ok_bt, "clicked", G_CALLBACK(proto_ok_cb), proto_w);
203   gtk_widget_grab_default(ok_bt);
204
205   apply_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_APPLY);
206   g_signal_connect(apply_bt, "clicked", G_CALLBACK(proto_apply_cb), proto_w);
207
208   save_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
209   g_signal_connect(save_bt, "clicked", G_CALLBACK(proto_save_cb), proto_w);
210
211   cancel_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
212   window_set_cancel_button(proto_w, cancel_bt, proto_cancel_cb);
213
214   help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
215   g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_ENABLED_PROTOCOLS_DIALOG);
216
217   g_signal_connect(proto_w, "delete_event", G_CALLBACK(proto_delete_event_cb), NULL);
218   g_signal_connect(proto_w, "destroy", G_CALLBACK(proto_destroy_cb), NULL);
219
220   gtk_widget_show(proto_w);
221
222   gtk_widget_grab_focus(proto_list); /* XXX: force focus to the tree_view. This hack req'd so "type-ahead find"
223                                       *  will be effective after the window is displayed. The issue is
224                                       *  that any call to gtk_tree_view_column_set_sort_column_id above
225                                       *  apparently sets the focus to the column header button and thus
226                                       *  type-ahead find is, in effect, disabled on the column.
227                                       *  Also required: a grab_focus whenever the column header is
228                                       *  clicked to change the column sort order since the click
229                                       *  also changes the focus to the column header button.
230                                       *  Is there a better way to do this ?
231                                       */
232
233   /* hide the Save button if the user uses implicit save */
234   if(!prefs.gui_use_pref_save) {
235     gtk_widget_hide(save_bt);
236   }
237
238   window_present(proto_w);
239 } /* proto_cb */
240
241 /* protocol list column header clicked (to change sort)       */
242 /*  grab_focus(treeview) req'd so that type-ahead find works. */
243 /*  (See comment above).                                      */
244 static void
245 proto_col_clicked_cb(GtkWidget *col _U_, GtkWidget *proto_list) {
246   gtk_widget_grab_focus(proto_list);
247 }
248
249 /* Status toggled */
250 static void
251 status_toggled(GtkCellRendererToggle *cell _U_, gchar *path_str, gpointer data)
252 {
253   GtkTreeModel    *model = (GtkTreeModel *)data;
254   GtkTreeIter      iter;
255   GtkTreePath     *path = gtk_tree_path_new_from_string(path_str);
256   protocol_data_t *p;
257
258   gtk_tree_model_get_iter(model, &iter, path);
259   gtk_tree_model_get(model, &iter, 3, &p, -1);
260
261   if (p->enabled)
262     p->enabled = FALSE;
263   else
264     p->enabled = TRUE;
265
266   gtk_list_store_set(GTK_LIST_STORE(model), &iter, 0, p->enabled, -1);
267
268   gtk_tree_path_free(path);
269 } /* status toggled */
270
271 /* XXX - We need callbacks for Gtk2 */
272
273 /* Toggle All */
274 static void
275 toggle_all_cb(GtkWidget *button _U_, gpointer pl)
276 {
277   GSList *entry;
278   GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(pl)));
279
280   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
281     protocol_data_t *p = entry->data;
282
283     if (p->enabled)
284       p->enabled = FALSE;
285     else
286       p->enabled = TRUE;
287
288     gtk_list_store_set(s, &p->iter, 0, p->enabled, -1);
289   }
290 }
291
292 /* Enable/Disable All Helper */
293 static void
294 set_active_all(GtkWidget *w, gboolean new_state)
295 {
296   GtkListStore *s = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(w)));
297   GSList *entry;
298
299   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
300     protocol_data_t *p = entry->data;
301
302     p->enabled = new_state;
303     gtk_list_store_set(s, &p->iter, 0, new_state, -1);
304   }
305 }
306
307 /* Enable All */
308 static void
309 enable_all_cb(GtkWidget *button _U_, gpointer pl)
310 {
311   set_active_all(pl, TRUE);
312 }
313
314 /* Disable All */
315 static void
316 disable_all_cb(GtkWidget *button _U_, gpointer pl)
317 {
318   set_active_all(pl, FALSE);
319 }
320
321 static void
322 proto_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
323 {
324   GSList *entry;
325
326   proto_w = NULL;
327   /* remove protocol list */
328   if (protocol_list) {
329     for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
330       g_free(entry->data);
331     }
332     g_slist_free(protocol_list);
333     protocol_list = NULL;
334   }
335 }
336
337 /* Treat this as a cancel, by calling "proto_cancel_cb()".
338    XXX - that'll destroy the Protocols dialog; will that upset
339    a higher-level handler that says "OK, we've been asked to delete
340    this, so destroy it"? */
341 static gboolean
342 proto_delete_event_cb(GtkWidget *proto_w_lcl, GdkEvent *event _U_,
343                       gpointer dummy _U_)
344 {
345   proto_cancel_cb(NULL, proto_w_lcl);
346   return FALSE;
347 }
348
349 /* Update protocol_list 'was_enabled' to current value of 'enabled' */
350 static void
351 update_was_enabled(void)
352 {
353   GSList *entry;
354
355   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
356     protocol_data_t *p = entry->data;
357     p->was_enabled = p->enabled;
358   }
359 }
360
361 static void
362 proto_write(gpointer parent_w _U_)
363 {
364   char *pf_dir_path;
365   char *pf_path;
366   int pf_save_errno;
367
368   /* Create the directory that holds personal configuration files, if
369      necessary.  */
370   if (create_persconffile_dir(&pf_dir_path) == -1) {
371     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
372                   "Can't create directory\n\"%s\"\nfor disabled protocols file: %s.", pf_dir_path,
373                   g_strerror(errno));
374     g_free(pf_dir_path);
375   } else {
376     save_disabled_protos_list(&pf_path, &pf_save_errno);
377     if (pf_path != NULL) {
378       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
379                     "Could not save to your disabled protocols file\n\"%s\": %s.",
380                     pf_path, g_strerror(pf_save_errno));
381       g_free(pf_path);
382     }
383   }
384 }
385
386 static void
387 proto_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
388 {
389   gboolean redissect;
390
391   /* update the selection now, so we'll save the right things */
392   redissect = set_proto_selection(GTK_WIDGET(parent_w));
393
394   /* if we don't have a Save button, just save the settings now */
395   if (!prefs.gui_use_pref_save) {
396     proto_write(parent_w);
397   }
398
399   window_destroy(GTK_WIDGET(parent_w));
400   if (redissect)
401     redissect_packets();
402 }
403
404 static void
405 proto_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
406 {
407   gboolean redissect;
408
409   /* update the selection now, so we'll save the right things */
410   redissect = set_proto_selection(GTK_WIDGET(parent_w));
411
412   /* if we don't have a Save button, just save the settings now */
413   if (!prefs.gui_use_pref_save) {
414     proto_write(parent_w);
415     update_was_enabled();
416   }
417
418   if (redissect)
419     redissect_packets();
420 }
421
422 static void
423 proto_save_cb(GtkWidget *save_bt _U_, gpointer parent_w)
424 {
425
426   proto_write(parent_w);
427
428   if (set_proto_selection(GTK_WIDGET(parent_w))) {
429     /* Redissect all the packets, and re-evaluate the display filter. */
430     redissect_packets();
431   }
432 }
433
434 static void
435 proto_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
436 {
437   gboolean redissect;
438
439   redissect = revert_proto_selection();
440   window_destroy(GTK_WIDGET(parent_w));
441   if (redissect)
442     redissect_packets();
443 }
444
445 static gboolean
446 set_proto_selection(GtkWidget *parent_w _U_)
447 {
448   GSList *entry;
449   gboolean need_redissect = FALSE;
450
451   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
452     protocol_data_t *p = entry->data;
453     protocol_t *protocol;
454
455     protocol = find_protocol_by_id(p->hfinfo_index);
456     if (proto_is_protocol_enabled(protocol) != p->enabled) {
457       proto_set_decoding(p->hfinfo_index, p->enabled);
458       need_redissect = TRUE;
459     }
460   }
461
462   return need_redissect;
463
464 } /* set_proto_selection */
465
466 static gboolean
467 revert_proto_selection(void)
468 {
469   GSList *entry;
470   gboolean need_redissect = FALSE;
471
472   /*
473    * Undo all the changes we've made to protocol enable flags.
474    */
475   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
476     protocol_data_t *p = entry->data;
477     protocol_t *protocol;
478
479     protocol = find_protocol_by_id(p->hfinfo_index);
480     if (proto_is_protocol_enabled(protocol) != p->was_enabled) {
481       proto_set_decoding(p->hfinfo_index, p->was_enabled);
482       need_redissect = TRUE;
483     }
484   }
485
486   return need_redissect;
487
488 } /* revert_proto_selection */
489
490 static gint
491 protocol_data_compare(gconstpointer a, gconstpointer b)
492 {
493   const protocol_data_t *ap = (const protocol_data_t *)a;
494   const protocol_data_t *bp = (const protocol_data_t *)b;
495
496   return strcmp(ap->abbrev, bp->abbrev);
497 }
498
499 static void
500 create_protocol_list(void)
501 {
502   gint i;
503   void *cookie;
504   protocol_t *protocol;
505   protocol_data_t *p;
506
507   /* Iterate over all the protocols */
508
509   for (i = proto_get_first_protocol(&cookie); i != -1;
510        i = proto_get_next_protocol(&cookie)) {
511       if (proto_can_toggle_protocol(i)) {
512         p = g_malloc(sizeof(protocol_data_t));
513         protocol = find_protocol_by_id(i);
514         p->name = proto_get_protocol_name(i);
515         p->abbrev = proto_get_protocol_short_name(protocol);
516         p->hfinfo_index = i;
517         p->enabled = proto_is_protocol_enabled(protocol);
518         p->was_enabled = p->enabled;
519         protocol_list = g_slist_insert_sorted(protocol_list,
520                                             p, protocol_data_compare);
521       }
522   }
523 }
524
525 static void
526 show_proto_selection(GtkListStore *proto_store)
527 {
528   GSList *entry;
529   protocol_data_t *p;
530
531   if (protocol_list == NULL)
532     create_protocol_list();
533
534   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
535     p = entry->data;
536
537     gtk_list_store_append(proto_store, &p->iter);
538     gtk_list_store_set(proto_store, &p->iter,
539                        0, p->enabled,
540                        1, p->abbrev,
541                        2, p->name,
542                        3, p,
543                       -1);
544   }
545
546 } /* show_proto_selection */
547
548 static void
549 proto_disable_dialog_cb(gpointer dialog _U_, gint btn, gpointer data)
550 {
551   protocol_t *protocol;
552   gint id = GPOINTER_TO_INT(data);
553
554   if (btn == ESD_BTN_OK) {
555     /* Allow proto_dlg to work with the original settings */
556     if (protocol_list == NULL)
557       create_protocol_list();
558     /* Toggle the protocol if it's enabled and allowed */
559     protocol = find_protocol_by_id(id);
560     if (proto_is_protocol_enabled(protocol) == TRUE) {
561       if (proto_can_toggle_protocol(id) == TRUE) {
562         proto_set_decoding(id, FALSE);
563         redissect_packets();
564       }
565     }
566   }
567 }
568
569 void
570 proto_disable_cb(GtkWidget *w _U_, gpointer data _U_)
571 {
572   header_field_info *hfinfo;
573   gint id;
574   gpointer dialog;
575
576   if (cfile.finfo_selected == NULL) {
577     /* There is no field selected */
578     return;
579   }
580
581   /* Find the id for the protocol for the selected field. */
582   hfinfo = cfile.finfo_selected->hfinfo;
583   if (hfinfo->parent == -1)
584     id = proto_get_id((protocol_t *)hfinfo->strings);
585   else
586     id = hfinfo->parent;
587
588   dialog = simple_dialog(ESD_TYPE_CONFIRMATION, ESD_BTNS_OK_CANCEL,
589     "Do you want to temporarily disable protocol: %s ?",
590     proto_registrar_get_abbrev(id));
591
592   simple_dialog_set_cb(dialog, proto_disable_dialog_cb, GINT_TO_POINTER(id));
593 }