Use gtk1/gtk2 compatibility macros to reduce #ifdefs.
[obnox/wireshark/wip.git] / gtk / proto_dlg.c
1 /* proto_dlg.c
2  *
3  * $Id: proto_dlg.c,v 1.17 2002/11/11 15:39:06 oabad Exp $
4  *
5  * Laurent Deniel <deniel@worldnet.fr>
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@ethereal.com>
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
30 #include <gtk/gtk.h>
31 #include <string.h>
32
33 #include "prefs.h"
34 #include "globals.h"
35 #include "main.h"
36 #include "util.h"
37 #include "ui_util.h"
38 #include "dlg_utils.h"
39 #include "proto_dlg.h"
40 #include "compat_macros.h"
41
42 static gboolean proto_delete_cb(GtkWidget *, gpointer);
43 static void proto_ok_cb(GtkWidget *, gpointer);
44 static void proto_apply_cb(GtkWidget *, gpointer);
45 static void proto_cancel_cb(GtkWidget *, gpointer);
46 static void proto_destroy_cb(GtkWidget *, gpointer);
47
48 static void show_proto_selection(GtkWidget *main, GtkWidget *container);
49 static gboolean set_proto_selection(GtkWidget *);
50 static gboolean revert_proto_selection(void);
51
52 static void toggle_all_cb(GtkWidget *button, gpointer parent_w);
53 static void enable_all_cb(GtkWidget *button, gpointer parent_w);
54 static void disable_all_cb(GtkWidget *button, gpointer parent_w);
55
56 static GtkWidget *proto_w = NULL;
57
58 /* list of protocols */
59 static GSList *protocol_list = NULL;
60
61 typedef struct protocol_data {
62   char     *name;
63   char     *abbrev;
64   int      hfinfo_index;
65   gboolean was_enabled;
66 } protocol_data_t;
67
68 void proto_cb(GtkWidget *w _U_, gpointer data _U_)
69 {
70
71   GtkWidget *main_vb, *bbox, *proto_nb, *apply_bt, *cancel_bt, *ok_bt,
72     *label, *scrolled_w, *selection_vb, *button;
73
74   if (proto_w != NULL) {
75     reactivate_window(proto_w);
76     return;
77   }
78
79   proto_w = dlg_window_new("Ethereal: Protocol");
80   SIGNAL_CONNECT(proto_w, "delete_event", proto_delete_cb, NULL);
81   SIGNAL_CONNECT(proto_w, "destroy", proto_destroy_cb, NULL);
82   WIDGET_SET_SIZE(proto_w, DEF_WIDTH * 2/3, DEF_HEIGHT * 2/3);
83
84   /* Container for each row of widgets */
85
86   main_vb = gtk_vbox_new(FALSE, 0);
87   gtk_container_border_width(GTK_CONTAINER(main_vb), 1);
88   gtk_container_add(GTK_CONTAINER(proto_w), main_vb);
89   gtk_widget_show(main_vb);
90
91   /* Protocol topics container */
92
93   proto_nb = gtk_notebook_new();
94   gtk_container_add(GTK_CONTAINER(main_vb), proto_nb);
95   /* XXX do not know why I need this to fill all space around buttons */
96   WIDGET_SET_SIZE(proto_nb, DEF_WIDTH * 2/3 - 50, DEF_HEIGHT * 2/3 - 50);
97
98   /* Protocol selection panel ("enable/disable" protocols) */
99
100   selection_vb = gtk_vbox_new(FALSE, 0);
101   gtk_container_border_width(GTK_CONTAINER(selection_vb), 1);
102   label = gtk_label_new("Button pressed: protocol decoding is enabled");
103   gtk_widget_show(label);
104   gtk_box_pack_start(GTK_BOX(selection_vb), label, FALSE, FALSE, 0);
105   scrolled_w = scrolled_window_new(NULL, NULL);
106   gtk_container_set_border_width(GTK_CONTAINER(scrolled_w), 1);
107   gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_w),
108                                  GTK_POLICY_AUTOMATIC,
109                                  GTK_POLICY_ALWAYS);
110   gtk_box_pack_start(GTK_BOX(selection_vb), scrolled_w, TRUE, TRUE, 0);
111   show_proto_selection(proto_w, scrolled_w);
112   gtk_widget_show(scrolled_w);
113   gtk_widget_show(selection_vb);
114   label = gtk_label_new("Decoding");
115   gtk_notebook_append_page(GTK_NOTEBOOK(proto_nb), selection_vb, label);
116   label = gtk_label_new("Note that when a protocol is disabled, "
117                         "all linked sub-protocols are as well");
118   gtk_widget_show(label);
119   gtk_box_pack_start(GTK_BOX(selection_vb), label, FALSE, FALSE, 0);
120
121
122   bbox = gtk_hbutton_box_new();
123   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
124   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
125   gtk_box_pack_start(GTK_BOX(selection_vb), bbox, FALSE, FALSE, 0);
126   gtk_widget_show(bbox);
127
128   /* Toggle All */
129   button = gtk_button_new_with_label("Toggle All");
130   SIGNAL_CONNECT(button, "clicked", toggle_all_cb, proto_w);
131   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
132   gtk_widget_show(button);
133
134   /* Enable All */
135   button = gtk_button_new_with_label("Enable All");
136   SIGNAL_CONNECT(button, "clicked", enable_all_cb, proto_w);
137   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
138   gtk_widget_show(button);
139
140   /* Disable All */
141   button = gtk_button_new_with_label("Disable All");
142   SIGNAL_CONNECT(button, "clicked", disable_all_cb, proto_w);
143   gtk_box_pack_start(GTK_BOX(bbox), button, TRUE, TRUE, 0);
144   gtk_widget_show(button);
145
146
147   /* XXX add other protocol-related panels here ... */
148
149   gtk_widget_show(proto_nb);
150
151   /* Ok, Apply, Cancel Buttons */
152
153   bbox = gtk_hbutton_box_new();
154   gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
155   gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
156   gtk_container_add(GTK_CONTAINER(main_vb), bbox);
157   gtk_widget_show(bbox);
158
159 #if GTK_MAJOR_VERSION < 2
160   ok_bt = gtk_button_new_with_label ("OK");
161 #else
162   ok_bt = gtk_button_new_from_stock(GTK_STOCK_OK);
163 #endif
164   SIGNAL_CONNECT(ok_bt, "clicked", proto_ok_cb, proto_w);
165   GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
166   gtk_box_pack_start(GTK_BOX (bbox), ok_bt, TRUE, TRUE, 0);
167   gtk_widget_grab_default(ok_bt);
168   gtk_widget_show(ok_bt);
169
170 #if GTK_MAJOR_VERSION < 2
171   apply_bt = gtk_button_new_with_label ("Apply");
172 #else
173   apply_bt = gtk_button_new_from_stock(GTK_STOCK_APPLY);
174 #endif
175   SIGNAL_CONNECT(apply_bt, "clicked", proto_apply_cb, proto_w);
176   GTK_WIDGET_SET_FLAGS(apply_bt, GTK_CAN_DEFAULT);
177   gtk_box_pack_start(GTK_BOX (bbox), apply_bt, TRUE, TRUE, 0);
178   gtk_widget_show(apply_bt);
179
180 #if GTK_MAJOR_VERSION < 2
181   cancel_bt = gtk_button_new_with_label ("Cancel");
182 #else
183   cancel_bt = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
184 #endif
185   SIGNAL_CONNECT(cancel_bt, "clicked", proto_cancel_cb, proto_w);
186   GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
187   gtk_box_pack_start(GTK_BOX (bbox), cancel_bt, TRUE, TRUE, 0);
188   gtk_widget_show(cancel_bt);
189
190   dlg_set_cancel(proto_w, cancel_bt);
191
192   gtk_quit_add_destroy(gtk_main_level(), proto_w);
193   gtk_widget_show(proto_w);
194
195 } /* proto_cb */
196
197
198 /* Toggle All */
199 static void
200 toggle_all_cb(GtkWidget *button _U_, gpointer parent_w)
201 {
202
203   GSList *entry;
204
205   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
206     GtkWidget *button;
207     protocol_data_t *p = entry->data;
208
209     button = (GtkWidget *)OBJECT_GET_DATA(parent_w, p->abbrev);
210     /* gtk_toggle_button_toggled() didn't work for me... */
211     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),
212                     !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button)));
213   }
214 }
215
216 /* Enable/Disable All Helper */
217 static void
218 set_active_all(gpointer parent_w, gboolean new_state)
219 {
220
221   GSList *entry;
222
223   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
224     GtkWidget *button;
225     protocol_data_t *p = entry->data;
226
227     button = (GtkWidget *)OBJECT_GET_DATA(parent_w, p->abbrev);
228     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button), new_state);
229   }
230 }
231
232 /* Enable All */
233 static void
234 enable_all_cb(GtkWidget *button _U_, gpointer parent_w)
235 {
236         set_active_all(parent_w, TRUE);
237 }
238
239 /* Disable All */
240 static void
241 disable_all_cb(GtkWidget *button _U_, gpointer parent_w)
242 {
243         set_active_all(parent_w, FALSE);
244 }
245
246 static void proto_destroy_cb(GtkWidget *w _U_, gpointer data _U_)
247 {
248   GSList *entry;
249
250   if (proto_w)
251     gtk_widget_destroy(proto_w);
252   proto_w = NULL;
253
254   /* remove protocol list */
255   if (protocol_list) {
256     for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
257       g_free(entry->data);
258     }
259     g_slist_free(protocol_list);
260     protocol_list = NULL;
261   }
262 }
263
264 /* Treat this as a cancel, by calling "proto_cancel_cb()".
265    XXX - that'll destroy the Protocols dialog; will that upset
266    a higher-level handler that says "OK, we've been asked to delete
267    this, so destroy it"? */
268 static gboolean proto_delete_cb(GtkWidget *proto_w, gpointer dummy _U_)
269 {
270   proto_cancel_cb(NULL, proto_w);
271   return FALSE;
272 }
273
274 static void proto_ok_cb(GtkWidget *ok_bt _U_, gpointer parent_w)
275 {
276   gboolean redissect;
277
278   redissect = set_proto_selection(GTK_WIDGET(parent_w));
279   gtk_widget_destroy(GTK_WIDGET(parent_w));
280   if (redissect)
281     redissect_packets(&cfile);
282 }
283
284 static void proto_apply_cb(GtkWidget *apply_bt _U_, gpointer parent_w)
285 {
286   if (set_proto_selection(GTK_WIDGET(parent_w)))
287     redissect_packets(&cfile);
288 }
289
290 static void proto_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
291 {
292   gboolean redissect;
293
294   redissect = revert_proto_selection();
295   gtk_widget_destroy(GTK_WIDGET(parent_w));
296   if (redissect)
297     redissect_packets(&cfile);
298 }
299
300 static gboolean set_proto_selection(GtkWidget *parent_w)
301 {
302   GSList *entry;
303   gboolean need_redissect = FALSE;
304
305   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
306     GtkWidget *button;
307     protocol_data_t *p = entry->data;
308
309     button = (GtkWidget *)OBJECT_GET_DATA(parent_w, p->abbrev);
310     if (proto_is_protocol_enabled(p->hfinfo_index) != GTK_TOGGLE_BUTTON (button)->active) {
311       proto_set_decoding(p->hfinfo_index, GTK_TOGGLE_BUTTON (button)->active);
312       need_redissect = TRUE;
313     }
314   }
315
316   return need_redissect;
317
318 } /* set_proto_selection */
319
320 static gboolean revert_proto_selection(void)
321 {
322   GSList *entry;
323   gboolean need_redissect = FALSE;
324
325   /*
326    * Undo all the changes we've made to protocol enable flags.
327    */
328   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
329     protocol_data_t *p = entry->data;
330
331     if (proto_is_protocol_enabled(p->hfinfo_index) != p->was_enabled) {
332       proto_set_decoding(p->hfinfo_index, p->was_enabled);
333       need_redissect = TRUE;
334     }
335   }
336
337   return need_redissect;
338
339 } /* revert_proto_selection */
340
341 gint protocol_data_compare(gconstpointer a, gconstpointer b)
342 {
343   return strcmp(((protocol_data_t *)a)->abbrev,
344                 ((protocol_data_t *)b)->abbrev);
345 }
346
347 static void show_proto_selection(GtkWidget *main, GtkWidget *container)
348 {
349
350 #define NB_COL  7
351
352   GSList *entry;
353   GtkTooltips *tooltips;
354   GtkWidget *table;
355   int i, t = 0, l = 0, nb_line, nb_proto = 0;
356   void *cookie;
357   protocol_data_t *p;
358
359   /* Iterate over all the protocols */
360
361   for (i = proto_get_first_protocol(&cookie); i != -1;
362        i = proto_get_next_protocol(&cookie)) {
363       if (proto_can_disable_protocol(i)) {
364         p = g_malloc(sizeof(protocol_data_t));
365         p->name = proto_get_protocol_name(i);
366         p->abbrev = proto_get_protocol_filter_name(i);
367         p->hfinfo_index = i;
368         p->was_enabled = proto_is_protocol_enabled(i);
369         protocol_list = g_slist_insert_sorted(protocol_list,
370                                             p, protocol_data_compare);
371         nb_proto ++;
372       }
373   }
374
375   /* create a table (n x NB_COL) of buttons */
376
377   nb_line = (nb_proto % NB_COL) ? nb_proto / NB_COL + 1 : nb_proto / NB_COL;
378   table = gtk_table_new (nb_line, NB_COL, FALSE);
379   gtk_table_set_row_spacings(GTK_TABLE (table), 1);
380   gtk_table_set_col_spacings(GTK_TABLE (table), 1);
381   gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(container), table);
382   gtk_widget_show(table);
383
384   tooltips = gtk_tooltips_new();
385
386   nb_proto = 0;
387
388   for (entry = protocol_list; entry != NULL; entry = g_slist_next(entry)) {
389     GtkWidget *button;
390
391     p = entry->data;
392     /* button label is the protocol abbrev */
393     button = gtk_toggle_button_new_with_label(p->abbrev);
394     /* tip is the complete protocol name */
395     gtk_tooltips_set_tip(tooltips, button, p->name, NULL);
396     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(button),
397                                 proto_is_protocol_enabled(p->hfinfo_index));
398     OBJECT_SET_DATA(main, p->abbrev, button);
399     gtk_table_attach_defaults (GTK_TABLE (table), button, l, l+1, t, t+1);
400     gtk_widget_show (button);
401     if (++nb_proto % NB_COL) {
402       l++;
403     }
404     else {
405       l = 0;
406       t++;
407     }
408   }
409
410 } /* show_proto_selection */