13b45d64e59622c6db86440b3a981f6326304efe
[obnox/wireshark/wip.git] / gtk / decode_as_dlg.c
1 /* decode_as_dlg.c
2  *
3  * $Id: decode_as_dlg.c,v 1.21 2001/12/18 19:09:07 gram Exp $
4  *
5  * Routines to modify dissector tables on the fly.
6  *
7  * By David Hampton <dhampton@mac.com>
8  * Copyright 2001 David Hampton
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  * 
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 # include "config.h"
27 #endif
28
29 #include <gtk/gtk.h>
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <string.h>
34
35 #include <errno.h>
36
37 #ifdef HAVE_SYS_TYPES_H
38 #include <sys/types.h>
39 #endif
40
41 #include "decode_as_dlg.h"
42 #include "dlg_utils.h"
43 #include "globals.h"
44 #include "simple_dialog.h"
45 #include "packet.h"
46 #include "ipproto.h"
47 #include "ui_util.h"
48 #include "epan_dissect.h"
49
50 #undef DEBUG
51
52 /**************************************************/
53 /*                Typedefs & Enums                */
54 /**************************************************/
55
56 /*
57  * Enum used to track which radio button is currently selected in the
58  * dialog. These buttons are labeled "Decode" and "Do not decode".
59  */
60 enum action_type {
61     /* The "Decode" button is currently selected. */
62     E_DECODE_YES,
63
64     /* The "Do not decode" button is currently selected. */
65     E_DECODE_NO
66 };
67
68 /*
69  * Enum used to track which transport layer port menu item is
70  * currently selected in the dialog.  These items are labeled "source",
71  * "destination", and "source/destination".
72  */
73 enum srcdst_type {
74     /* The "source port" menu item is currently selected. */
75     E_DECODE_SPORT,
76     /* The "destination port" menu item is currently selected. */
77     E_DECODE_DPORT,
78     /* The "source/destination port" menu item is currently selected. */
79     E_DECODE_BPORT
80 };
81
82 #define E_DECODE_MIN_HEIGHT 100
83 #define E_NOTEBOOK "notebook"
84
85 #define E_MENU_SRCDST "menu_src_dst"
86
87 #define E_PAGE_ACTION "notebook_page_action"
88 #define E_PAGE_CLIST  "notebook_page_clist"
89 #define E_PAGE_TABLE  "notebook_page_table_name"
90 #define E_PAGE_TITLE  "notebook_page_title"
91 #define E_PAGE_VALUE  "notebook_page_value"
92
93 /*
94  * Clist columns for a "Select" clist.
95  * Note that most of these columns aren't displayed; they're attached
96  * to the row of the table as additional information.
97  */
98 #define E_CLIST_S_PROTO_NAME 0
99 #define E_CLIST_S_TABLE      1
100 /* The following is for debugging in decode_add_to_clist */
101 #define E_CLIST_S_MAX        E_CLIST_S_TABLE
102 #define E_CLIST_S_COLUMNS   (E_CLIST_S_MAX + 1)
103
104 /*
105  * Clist columns for a "Display" clist
106  */
107 #define E_CLIST_D_TABLE      0
108 #define E_CLIST_D_PORT       1
109 #define E_CLIST_D_INITIAL    2
110 #define E_CLIST_D_CURRENT    3
111 #define E_CLIST_D_MAX        E_CLIST_D_CURRENT
112 #define E_CLIST_D_COLUMNS   (E_CLIST_D_MAX + 1)
113
114 /**************************************************/
115 /*             File Global Variables              */
116 /**************************************************/
117
118 /*
119  * Keep a static pointer to the current "Decode As" window.  This is
120  * kept so that if somebody tries to do "Tools:Decode As" while
121  * there's already a "Decode As" window up, we just pop up the
122  * existing one, rather than creating a new one.
123  */
124 static GtkWidget *decode_w = NULL;
125
126 /*
127  * A static pointer to the current "Decode As:Show" window.  This is
128  * kept so that if somebody tries to do clock the "Show Current"
129  * button or slect the "Display:User Specified Decodes" menu item
130  * while there's already a "Decode As:Show" window up, we just pop up
131  * the existing one, rather than creating a new one.
132  */
133 static GtkWidget *decode_show_w = NULL;
134
135 /*
136  * A list of the dialog items that only have meaning when the user has
137  * selected the "Decode" radio button.  When the "Do not decode"
138  * button is selected these items should be dimmed.
139  */
140 static GSList *decode_dimmable = NULL;
141
142 /*
143  * Remember the "action" radio button that is currently selected in
144  * the dialog.  This value is initialized when the dialog is created,
145  * modified in a callback routine, and read in the routine that
146  * handles a click in the "OK" button for the dialog.
147  */
148 static enum action_type requested_action = -1;
149
150 /**************************************************/
151 /*            Resett Changed Dissectors           */
152 /**************************************************/
153
154 /*
155  * Data structure for tracking which dissector need to be reset.  This
156  * structure is necessary as a hash table entry cannot be removed
157  * while a g_hash_table_foreach walk is in progress.
158  */
159 struct dissector_delete_item {
160     /* The name of the dissector table */
161     const gchar *ddi_table_name;
162     /* The port number in the dissector table */
163     guint   ddi_port;
164 };
165
166 /*
167  * A typedef for the data structure to track the original dissector
168  * used for any given port on any given protocol.
169  */
170 typedef struct dissector_delete_item dissector_delete_item_t;
171
172 /*
173  * A list of dissectors that need to be reset.
174  */
175 GSList *dissector_reset_list = NULL;
176
177 /*
178  * This routine creates one entry in the list of protocol dissector
179  * that need to be reset. It is called by the g_hash_table_foreach
180  * routine once for each changed entry in a dissector table.
181  * Unfortunately it cannot delete the entry immediately as this screws
182  * up the foreach function, so it builds a list of dissectors to be
183  * reset once the foreach routine finishes.
184  *
185  * @param table_name The table name in which this dissector is found.
186  *
187  * @param key A pointer to the key for this entry in the dissector
188  * hash table.  This is generally the numeric selector of the
189  * protocol, i.e. the ethernet type code, IP port number, TCP port
190  * number, etc.
191  *
192  * @param value A pointer to the value for this entry in the dissector
193  * hash table.  This is an opaque pointer that can only be handed back
194  * to routine in the file packet.c
195  *
196  * @param user_data Unused.
197  */
198 static void
199 decode_build_reset_list (gchar *table_name, gpointer key,
200                          gpointer value, gpointer user_data)
201 {
202     dissector_delete_item_t *item;
203
204     item = g_malloc(sizeof(dissector_delete_item_t));
205     item->ddi_table_name = table_name;
206     item->ddi_port = GPOINTER_TO_UINT(key);
207     dissector_reset_list = g_slist_prepend(dissector_reset_list, item);
208 }
209
210
211 /**************************************************/
212 /*             Show Changed Dissectors            */
213 /**************************************************/
214
215 /*
216  * This routine creates one entry in the list of protocol dissector
217  * that have been changed.  It is called by the g_hash_foreach routine
218  * once for each changed entry in a dissector table.
219  *
220  * @param table_name The table name in which this dissector is found.
221  *
222  * @param key A pointer to the key for this entry in the dissector
223  * hash table.  This is generally the numeric selector of the
224  * protocol, i.e. the ethernet type code, IP port number, TCP port
225  * number, etc.
226  *
227  * @param value A pointer to the value for this entry in the dissector
228  * hash table.  This is an opaque pointer that can only be handed back
229  * to routine in the file packet.c
230  *
231  * @param user_data A pointer to the clist in which this information
232  * should be stored.
233  */
234 static void
235 decode_build_show_list (gchar *table_name, gpointer key,
236                         gpointer value, gpointer user_data)
237 {
238     GtkCList  *clist;
239     dissector_handle_t current, initial;
240     gchar     *current_proto_name, *initial_proto_name, *text[E_CLIST_D_COLUMNS];
241     gchar      string1[20];
242     gint       row;
243
244     g_assert(user_data);
245     g_assert(value);
246
247     clist = (GtkCList *)user_data;
248     current = dtbl_entry_get_handle(value);
249     if (current == NULL)
250         current_proto_name = "(none)";
251     else
252         current_proto_name = dissector_handle_get_short_name(current);
253     initial = dtbl_entry_get_initial_handle(value);
254     if (initial == NULL)
255         initial_proto_name = "(none)";
256     else
257         initial_proto_name = dissector_handle_get_short_name(initial);
258
259     text[E_CLIST_D_TABLE] = get_dissector_table_ui_name(table_name);
260     switch (get_dissector_table_base(table_name)) {
261
262     case BASE_DEC:
263         sprintf(string1, "%u", GPOINTER_TO_UINT(key));
264         break;
265
266     case BASE_HEX:
267         switch (get_dissector_table_type(table_name)) {
268
269         case FT_UINT8:
270             sprintf(string1, "0x%02x", GPOINTER_TO_UINT(key));
271             break;
272
273         case FT_UINT16:
274             sprintf(string1, "0x%04x", GPOINTER_TO_UINT(key));
275             break;
276
277         case FT_UINT24:
278             sprintf(string1, "0x%06x", GPOINTER_TO_UINT(key));
279             break;
280
281         case FT_UINT32:
282             sprintf(string1, "0x%08x", GPOINTER_TO_UINT(key));
283             break;
284
285         default:
286             g_assert_not_reached();
287             break;
288         }
289         break;
290
291     case BASE_OCT:
292         sprintf(string1, "%#o", GPOINTER_TO_UINT(key));
293         break;
294     }
295     text[E_CLIST_D_PORT] = string1;
296     text[E_CLIST_D_INITIAL] = initial_proto_name;
297     text[E_CLIST_D_CURRENT] = current_proto_name;
298     row = gtk_clist_prepend(clist, text);
299 }
300
301
302 /*
303  * This routine is called when the user clicks the "OK" button in
304  * the "Decode As:Show..." dialog window.  This routine destroys the
305  * dialog box and performs other housekeeping functions.
306  *
307  * @param GtkWidget * A pointer to the "OK" button.
308  *
309  * @param gpointer A pointer to the dialog window.
310  */
311 static void
312 decode_show_ok_cb (GtkWidget *ok_bt, gpointer parent_w)
313 {
314     gtk_widget_destroy(GTK_WIDGET(parent_w));
315 }
316
317
318 /*
319  * This routine is called when the user clicks the "Reset" button in
320  * the "Decode As:Show..." dialog window.  This routine resets all the
321  * dissector values and then destroys the dialog box and performs
322  * other housekeeping functions.
323  *
324  * @param GtkWidget * A pointer to the "Reset" button.
325  *
326  * @param gpointer A pointer to the dialog window.
327  */
328 static void
329 decode_show_reset_cb (GtkWidget *reset_bt, gpointer parent_w)
330 {
331     dissector_delete_item_t *item;
332     GSList *tmp;
333     
334     dissector_all_tables_foreach_changed(decode_build_reset_list, NULL);
335
336     for (tmp = dissector_reset_list; tmp; tmp = g_slist_next(tmp)) {
337         item = tmp->data;
338         dissector_reset(item->ddi_table_name, item->ddi_port);
339         g_free(item);
340     }
341     g_slist_free(dissector_reset_list);
342     dissector_reset_list = NULL;
343
344     redissect_packets(&cfile);
345
346     gtk_widget_destroy(GTK_WIDGET(parent_w));
347 }
348
349
350 /*
351  * This routine is called when the user clicks the "Close" button in
352  * the "Decode As:Show..." dialog window.  This routine simply calls the
353  * cancel routine as if the user had clicked the cancel button instead
354  * of the close button.
355  *
356  * @param GtkWidget * A pointer to the dialog box.
357  *
358  * @param gpointer Unknown
359  */
360 static gboolean
361 decode_show_delete_cb (GtkWidget *decode_w, gpointer dummy)
362 {
363     decode_show_ok_cb(NULL, decode_show_w);
364     return FALSE;
365 }
366
367
368 /*
369  * This routine is called at the destruction of the "Decode As:Show"
370  * dialog box.  It clears the pointer maintained by this file, so that
371  * the next time the user clicks the "Decode As:Show" button a new
372  * dialog box will be created.
373  *
374  * @param GtkWidget * A pointer to the dialog box.
375  *
376  * @param gpointer Unknown
377  */
378 static void
379 decode_show_destroy_cb (GtkWidget *win, gpointer user_data)
380 {
381     /* Note that we no longer have a "Decode As:Show" dialog box. */
382     decode_show_w = NULL;
383 }
384
385
386 /*
387  * This routine creates the "Decode As:Show" dialog box. This dialog box
388  * shows the user which protocols have had their dissectors changed.
389  *
390  * @param w Unknown
391  * @param data Unknown
392  */
393 void
394 decode_show_cb (GtkWidget * w, gpointer data)
395 {
396     GtkWidget *main_vb, *bbox, *ok_bt, *button, *scrolled_window;
397     GtkCList  *clist;
398     gchar     *titles[E_CLIST_D_COLUMNS] = {"Table", "Port", "Initial", "Current"};
399     gint       column;
400
401     if (decode_show_w != NULL) {
402         /* There's already a "Decode As" dialog box; reactivate it. */
403         reactivate_window(decode_show_w);
404         return;
405     }
406
407     decode_show_w = dlg_window_new("Ethereal: Decode As: Show");
408     gtk_signal_connect(GTK_OBJECT(decode_show_w), "delete_event",
409                        GTK_SIGNAL_FUNC(decode_show_delete_cb), NULL);
410     gtk_signal_connect(GTK_OBJECT(decode_show_w), "destroy",
411                        GTK_SIGNAL_FUNC(decode_show_destroy_cb), NULL);
412   
413     /* Container for each row of widgets */
414     main_vb = gtk_vbox_new(FALSE, 2);
415     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
416     gtk_container_add(GTK_CONTAINER(decode_show_w), main_vb);
417
418     {
419         /* Initialize clist */
420         clist = GTK_CLIST(gtk_clist_new_with_titles(E_CLIST_D_COLUMNS, titles));
421         gtk_clist_column_titles_passive(clist);
422         for (column = 0; column < E_CLIST_D_COLUMNS; column++)
423             gtk_clist_set_column_auto_resize(clist, column, TRUE);
424         gtk_clist_set_selection_mode(clist, GTK_SELECTION_EXTENDED);
425
426         /* Add data */
427         dissector_all_tables_foreach_changed(decode_build_show_list, clist);
428         gtk_clist_sort(clist);
429
430         /* Put clist into a scrolled window */
431         scrolled_window = gtk_scrolled_window_new(NULL, NULL);
432         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
433                                        GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
434         gtk_container_add(GTK_CONTAINER(scrolled_window),
435                                               GTK_WIDGET(clist));
436         gtk_box_pack_start(GTK_BOX(main_vb), scrolled_window, TRUE, TRUE, 0);
437         /* Provide a minimum of a couple of rows worth of data */
438         gtk_widget_set_usize(scrolled_window, 0, E_DECODE_MIN_HEIGHT);
439     }
440
441     /* Button row: OK and reset buttons */
442     bbox = gtk_hbutton_box_new();
443     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
444     gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
445     gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
446
447     button = gtk_button_new_with_label("Reset Changes");
448     gtk_signal_connect(GTK_OBJECT(button), "clicked",
449                        GTK_SIGNAL_FUNC(decode_show_reset_cb),
450                        GTK_OBJECT(decode_show_w));
451     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
452     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
453     gtk_widget_set_sensitive(button, (clist->rows != 0));
454
455     ok_bt = gtk_button_new_with_label("OK");
456     gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
457                        GTK_SIGNAL_FUNC(decode_show_ok_cb),
458                        GTK_OBJECT(decode_show_w));
459     GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
460     gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
461     gtk_widget_grab_default(ok_bt);
462     dlg_set_cancel(decode_show_w, ok_bt);
463
464     gtk_widget_show_all(decode_show_w);
465 }
466
467
468 /**************************************************/
469 /*         Modify the dissector routines          */
470 /**************************************************/
471
472 /*
473  * Modify a single dissector.  This routine first takes care of
474  * updating the internal table of original protocol/port/dissector
475  * combinations by adding a new entry (or removing an existing entry
476  * if the value is being set back to its default).  This routine then
477  * performs the actual modification to the packet dissector tables.
478  *
479  * @param s Pointer to a string buffer.  This buffer is used to build
480  * up a message indicating which ports have had their dissector
481  * changed. This output will be displayed all at once after all
482  * dissectors have been modified.
483  *
484  * @param table_name The table name in which the dissector should be
485  * modified.
486  *
487  * @param selector An enum value indication which selector value
488  * (i.e. IP protocol number, TCP port number, etc.)is to be changed.
489  *
490  * @param clist The CList in which all the selection information can
491  * be found.
492  *
493  * @return gchar * Pointer to the next free location in the string
494  * buffer.
495  */
496 static void
497 decode_change_one_dissector (gchar *table_name, gint selector, GtkCList *clist)
498 {
499     dissector_handle_t handle;
500     gchar              *abbrev;
501     gint               row;
502
503     if (!clist->selection) {
504         abbrev = "(NULL)";
505         handle = NULL;
506     } else {
507         row = GPOINTER_TO_INT(clist->selection->data);
508         handle = gtk_clist_get_row_data(clist, row);
509         gtk_clist_get_text(clist, row, E_CLIST_S_PROTO_NAME, &abbrev);
510     }
511
512     if (strcmp(abbrev, "(default)") == 0) {
513         dissector_reset(table_name, selector);
514     } else {
515         dissector_change(table_name, selector, handle);
516     }
517 }
518
519
520
521 /**************************************************/
522 /* Action routines for the "Decode As..." dialog  */
523 /*   - called when the OK button pressed          */
524 /*   - one per notebook page                      */
525 /**************************************************/
526
527
528 #ifdef DEBUG
529 /*
530  * Print debugging information about clist selection.  Extract all
531  * information from the clist entry that was selected and print it to
532  * a dialog window.
533  *
534  * @param clist The clist to dump.
535  *
536  * @param leadin A string to print at the start of each line.
537  */
538 static void
539 decode_debug (GtkCList *clist, gchar *leadin)
540 {
541     gchar *string, *text[E_CLIST_S_COLUMNS];
542     dissector_handle_t handle;
543     gint row;
544
545     string = g_malloc(1024);
546     if (clist->selection) {
547         row = GPOINTER_TO_INT(clist->selection->data);
548         gtk_clist_get_text(clist, row, E_CLIST_S_PROTO_NAME, &text[E_CLIST_S_PROTO_NAME]);
549         gtk_clist_get_text(clist, row, E_CLIST_S_TABLE, &text[E_CLIST_S_TABLE]);
550         handle = gtk_clist_get_row_data(clist, row);
551         sprintf(string, "%s clist row %d: <put handle here>, name %s, table %s",
552                 leadin, row, text[E_CLIST_S_PROTO_NAME],
553                 text[E_CLIST_S_TABLE]);
554     } else {
555         sprintf(string, "%s clist row (none), aka do not decode", leadin);
556     }
557     simple_dialog(ESD_TYPE_INFO, NULL, string);
558     g_free(string);
559 }
560 #endif
561
562
563 /*
564  * This routine is called when the user clicks the "OK" button in the
565  * "Decode As..." dialog window and a 'simple' page is foremost.
566  * This routine takes care of making any changes requested to the
567  * dissector tables.  This routine is currently used for IP and
568  * Ethertypes.  Any 'single change' notebook page can use this
569  * routine.
570  *
571  * @param notebook_pg A pointer to the "network" notebook page.
572  */
573 static void
574 decode_simple (GtkWidget *notebook_pg)
575 {
576     GtkCList *clist;
577 #ifdef DEBUG
578     gchar *string;
579 #endif
580     gchar *table_name;
581     gint value;
582
583     clist = GTK_CLIST(gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_CLIST));
584     if (requested_action == E_DECODE_NO)
585         gtk_clist_unselect_all(clist);
586
587 #ifdef DEBUG
588     string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
589     decode_debug(clist, string);
590 #endif
591
592     table_name = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TABLE);
593     value = GPOINTER_TO_INT(gtk_object_get_data(GTK_OBJECT(notebook_pg),
594                                                 E_PAGE_VALUE));
595     decode_change_one_dissector(table_name, value, clist);
596 }
597
598
599 /*
600  * This routine is called when the user clicks the "OK" button in the
601  * "Decode As..." dialog window and the transport page is foremost.
602  * This routine takes care of making any changes requested to the TCP
603  * or UDP dissector tables.
604  *
605  * @param notebook_pg A pointer to the "transport" notebook page.
606  */
607 static void
608 decode_transport (GtkObject *notebook_pg)
609 {
610     GtkWidget *menu, *menuitem;
611     GtkCList *clist;
612     gchar *table_name;
613     gint requested_srcdst;
614
615     clist = GTK_CLIST(gtk_object_get_data(notebook_pg, E_PAGE_CLIST));
616     if (requested_action == E_DECODE_NO)
617         gtk_clist_unselect_all(clist);
618
619     menu = gtk_object_get_data(notebook_pg, E_MENU_SRCDST);
620     menuitem = gtk_menu_get_active(GTK_MENU(menu));
621     requested_srcdst = GPOINTER_TO_INT(gtk_object_get_user_data(GTK_OBJECT(menuitem)));
622
623 #ifdef DEBUG
624     string = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TITLE);
625     decode_debug(clist, string);
626 #endif
627
628     table_name = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_TABLE);
629     if (requested_srcdst != E_DECODE_DPORT)
630         decode_change_one_dissector(table_name, cfile.edt->pi.srcport, clist);
631     if (requested_srcdst != E_DECODE_SPORT)
632         decode_change_one_dissector(table_name, cfile.edt->pi.destport, clist);
633 }
634
635 /**************************************************/
636 /*      Signals from the "Decode As..." dialog    */
637 /**************************************************/
638
639 /*
640  * This routine is called when the user clicks the "OK" button in the
641  * "Decode As..." dialog window.  This routine calls various helper
642  * routines to set/clear dissector values as requested by the user.
643  * These routines accumulate information on what actions they have
644  * taken, and this summary information is printed by this routine.
645  * This routine then destroys the dialog box and performs other
646  * housekeeping functions.
647  *
648  * @param ok_bt A pointer to the "OK" button.
649  *
650  * @param parent_w A pointer to the dialog window.
651  */
652 static void
653 decode_ok_cb (GtkWidget *ok_bt, gpointer parent_w)
654 {
655     GtkWidget *notebook, *notebook_pg;
656     GtkSignalFunc func;
657     gint page_num;
658
659     /* Call the right routine for the page that was currently in front. */
660     notebook =  gtk_object_get_data(GTK_OBJECT(parent_w), E_NOTEBOOK);
661     page_num = gtk_notebook_get_current_page(GTK_NOTEBOOK(notebook));
662     notebook_pg = gtk_notebook_get_nth_page(GTK_NOTEBOOK(notebook), page_num);
663
664     func = gtk_object_get_data(GTK_OBJECT(notebook_pg), E_PAGE_ACTION);
665     func(notebook_pg);
666
667     /* Now destroy the "Decode As" dialog. */
668     gtk_widget_destroy(GTK_WIDGET(parent_w));
669     g_slist_free(decode_dimmable);
670     decode_dimmable = NULL;
671
672     redissect_packets(&cfile);
673 }
674
675
676 /*
677  * This routine is called when the user clicks the "Cancel" button in
678  * the "Decode As..." dialog window.  This routine then destroys the
679  * dialog box and performs other housekeeping functions.
680  *
681  * @param cancel_bt A pointer to the "Cancel" button.
682  *
683  * @param parent_w A pointer to the dialog window.
684  */
685 static void
686 decode_cancel_cb (GtkWidget *cancel_bt, gpointer parent_w)
687 {
688     gtk_widget_destroy(GTK_WIDGET(parent_w));
689     g_slist_free(decode_dimmable);
690     decode_dimmable = NULL;
691 }
692
693
694 /*
695  * This routine is called when the user clicks the "Close" button in
696  * the "Decode As..." dialog window.  This routine simply calls the
697  * cancel routine as if the user had clicked the cancel button instead
698  * of the close button.
699  *
700  * @param decode_w A pointer to the dialog box.
701  *
702  * @param dummy Unknown
703  */
704 static gboolean
705 decode_delete_cb (GtkWidget *decode_w, gpointer dummy)
706 {
707     decode_cancel_cb(NULL, decode_w);
708     return FALSE;
709 }
710
711
712 /*
713  * This routine is called at the destruction of the "Decode As..."
714  * dialog box.  It clears the pointer maintained by this file, so that
715  * the next time the user selects the "Decode As..." menu item a new
716  * dialog box will be created.
717  *
718  * @param decode_w A pointer to the dialog box.
719  *
720  * @param user_data Unknown
721  *
722  * @return void
723  */
724 static void
725 decode_destroy_cb (GtkWidget *win, gpointer user_data)
726 {
727     /* Note that we no longer have a "Decode As" dialog box. */
728     decode_w = NULL;
729 }
730
731
732 /**************************************************/
733 /*          Dialog setup - radio buttons          */
734 /**************************************************/
735
736 /*
737  * Update the requested action field of the dialog.  This routine is
738  * called by GTK when either of the two radio buttons in the dialog is
739  * clicked.
740  *
741  * @param w The radio button that was clicked.
742  *
743  * @param data The enum value assigned to this radio button.  This
744  * will be either E_DECODE_YES or E_DECODE_NO
745  */
746 static void
747 decode_update_action (GtkWidget *w, gpointer data)
748 {
749     GSList *tmp;
750     gboolean enable;
751
752     requested_action = GPOINTER_TO_INT(data);
753     enable = (requested_action == E_DECODE_YES);
754     for (tmp = decode_dimmable; tmp; tmp = g_slist_next(tmp)) {
755         gtk_widget_set_sensitive(tmp->data, enable);
756     }
757 }
758
759 /*
760  * This routine is called to create the "Decode" and "Do not decode"
761  * radio buttons.  These buttons are installed into a vbox, and set up
762  * as a format group.
763  *
764  * @return GtkWidget * A pointer to the vbox containing the buttons
765  */
766 static GtkWidget *
767 decode_add_yes_no (void)
768 {
769     GtkWidget   *format_vb, *radio_button;
770     GSList      *format_grp;
771
772     format_vb = gtk_vbox_new(FALSE, 2);
773
774     radio_button = gtk_radio_button_new_with_label(NULL, "Decode");
775     format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
776     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio_button), TRUE);
777     gtk_signal_connect(GTK_OBJECT(radio_button), "clicked",
778                        GTK_SIGNAL_FUNC(decode_update_action),
779                        GINT_TO_POINTER(E_DECODE_YES));
780     gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
781
782     radio_button = gtk_radio_button_new_with_label(format_grp, "Do not decode");
783     format_grp = gtk_radio_button_group(GTK_RADIO_BUTTON(radio_button));
784     gtk_signal_connect(GTK_OBJECT(radio_button), "clicked",
785                        GTK_SIGNAL_FUNC(decode_update_action),
786                        GINT_TO_POINTER(E_DECODE_NO));
787     gtk_box_pack_start(GTK_BOX(format_vb), radio_button, TRUE, TRUE, 0);
788
789     return(format_vb);
790 }
791
792 /**************************************************/
793 /*          Dialog setup - simple menus           */
794 /**************************************************/
795
796 /*
797  * This routine is called to pack an option menu into an aligment, so
798  * that it doesn't expand vertically to fill up the space available to
799  * it.
800  *
801  * @param optmenu A pointer to the option menu to be so packed.
802  *
803  * @return GtkWidget * A pointer to the newly created alignment.
804  */
805 static GtkWidget *
806 decode_add_pack_menu (GtkWidget *optmenu)
807 {
808     GtkWidget *alignment;
809
810     alignment = gtk_alignment_new(0.0, 0.5, 0.0, 0.0);
811     gtk_container_add(GTK_CONTAINER(alignment), optmenu);
812
813     return(alignment);
814 }
815
816
817 /*
818  * This routine is called to add the transport port selection menu to
819  * the dialog box.  This is a three choice menu: source, destination
820  * and both.  The default choice for the menu is set to the source
821  * port number of the currently selected packet.
822  *
823  * @param page A pointer notebook page that will contain all
824  * widgets created by this routine.
825  *
826  * @return GtkWidget * A pointer to the newly created alignment into
827  * which we've packed the newly created option menu.
828  */
829 static GtkWidget *
830 decode_add_srcdst_menu (GtkWidget *page)
831 {
832     GtkWidget *optmenu, *menu, *menuitem, *alignment;
833     gchar      tmp[100];
834
835     optmenu = gtk_option_menu_new();
836     menu = gtk_menu_new();
837     sprintf(tmp, "source (%u)", cfile.edt->pi.srcport);
838     menuitem = gtk_menu_item_new_with_label(tmp);
839     gtk_object_set_user_data(GTK_OBJECT(menuitem),
840                              GINT_TO_POINTER(E_DECODE_SPORT));
841     gtk_menu_append(GTK_MENU(menu), menuitem);
842     gtk_widget_show(menuitem);  /* gtk_widget_show_all() doesn't show this */
843
844     sprintf(tmp, "destination (%u)", cfile.edt->pi.destport);
845     menuitem = gtk_menu_item_new_with_label(tmp);
846     gtk_object_set_user_data(GTK_OBJECT(menuitem),
847                              GINT_TO_POINTER(E_DECODE_DPORT));
848     gtk_menu_append(GTK_MENU(menu), menuitem);
849     gtk_widget_show(menuitem);  /* gtk_widget_show_all() doesn't show this */
850
851     menuitem = gtk_menu_item_new_with_label("both");
852     gtk_object_set_user_data(GTK_OBJECT(menuitem),
853                              GINT_TO_POINTER(E_DECODE_BPORT));
854     gtk_menu_append(GTK_MENU(menu), menuitem);
855     gtk_widget_show(menuitem);  /* gtk_widget_show_all() doesn't show this */
856
857     gtk_object_set_data(GTK_OBJECT(page), E_MENU_SRCDST, menu);
858     gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
859
860     alignment = decode_add_pack_menu(optmenu);
861
862     return(alignment);
863 }
864
865 /**************************************************/
866 /*        Dialog setup - clist based menus        */
867 /**************************************************/
868
869
870 /*
871  * This routine creates one entry in the list of protocol dissector
872  * that can be used.  It is called by the dissector_table_foreach_handle
873  * routine once for each entry in a dissector table's list of handles
874  * for dissectors that could be used in that table.  It guarantees unique
875  * entries by iterating over the list of entries build up to this point,
876  * looking for a duplicate name.  If there is no duplicate, then this
877  * entry is added to the list of possible dissectors.
878  *
879  * @param table_name The name of the dissector table currently
880  * being walked.
881  *
882  * @param value The dissector handle for this entry.  This is an opaque
883  * pointer that can only be handed back to routines in the file packet.c
884  *
885  * @param user_data A data block passed into each instance of this
886  * routine.  It contains information from the caller of the foreach
887  * routine, specifying information about the dissector table and where
888  * to store any information generated by this routine.
889  */
890 static void
891 decode_add_to_clist (gchar *table_name, gpointer value, gpointer user_data)
892 {
893     GtkCList  *clist;
894     gchar     *proto_name;
895     gchar     *text[E_CLIST_S_COLUMNS];
896     dissector_handle_t handle;
897     gint       row;
898
899     g_assert(user_data);
900     g_assert(value);
901
902     clist = user_data;
903     handle = value;
904     proto_name = dissector_handle_get_short_name(handle);
905
906     row = gtk_clist_find_row_from_data(clist, handle);
907     if (row != -1) {
908         /*
909          * We already have an entry for this handle.
910          * XXX - will this ever happen?
911          */
912         return;
913     }
914
915     text[E_CLIST_S_PROTO_NAME] = proto_name;
916     text[E_CLIST_S_TABLE] = table_name;
917     row = gtk_clist_prepend(clist, text);
918     gtk_clist_set_row_data(clist, row, handle);
919 }
920
921
922 /*
923  * This routine starts the creation of a CList on a notebook page.  It
924  * creates both a scrolled window and a clist, adds the clist to the
925  * window, and attaches the clist as a data object on the page.
926  *
927  * @param page A pointer to the notebook page being created.
928  *
929  * @param clist_p Will be filled in with the address of a newly
930  * created CList.
931  *
932  * @param scrolled_win_p Will be filled in with the address of a newly
933  * created GtkScrolledWindow.
934  */
935 static void
936 decode_clist_menu_start (GtkWidget *page, GtkCList **clist_p,
937                          GtkWidget **scrolled_win_p)
938 {
939     gchar *titles[E_CLIST_S_COLUMNS] = {"Short Name", "Table Name"};
940     GtkCList  *clist;
941     GtkWidget *window;
942     gint column;
943
944     *clist_p = clist =
945         GTK_CLIST(gtk_clist_new_with_titles(E_CLIST_S_COLUMNS, titles));
946     gtk_clist_column_titles_passive(clist);
947 #ifndef DEBUG
948     gtk_clist_column_titles_hide(clist);
949     for (column = 1; column < E_CLIST_S_COLUMNS; column++)
950         gtk_clist_set_column_visibility (clist, column, FALSE);
951 #endif
952     for (column = 0; column < E_CLIST_S_COLUMNS; column++)
953         gtk_clist_set_column_auto_resize(clist, column, TRUE);
954     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_CLIST, clist);
955
956     *scrolled_win_p = window = gtk_scrolled_window_new(NULL, NULL);
957     /* Provide a minimum of a couple of rows worth of data */
958     gtk_widget_set_usize(window, 0, E_DECODE_MIN_HEIGHT);
959     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(window),
960                                    GTK_POLICY_NEVER, GTK_POLICY_ALWAYS);
961     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(window),
962                                           GTK_WIDGET(clist));
963 }
964
965 /*
966  * This routine finishes the creation of a CList on a notebook page.
967  * It adds the default entry, sets the default entry as the
968  * highlighted entry, and sorts the CList.
969  *
970  * @param clist A pointer the the CList to finish.
971  */
972 static void
973 decode_clist_menu_finish (GtkCList *clist)
974 {
975     gchar *text[E_CLIST_S_COLUMNS];
976     gint row;
977
978     text[E_CLIST_S_PROTO_NAME] = "(default)";
979     text[E_CLIST_S_TABLE] = "(none)";
980     row = gtk_clist_prepend(clist, text);
981     gtk_clist_set_row_data(clist, row, NULL);
982
983     gtk_clist_select_row(clist, 0, -1);
984     gtk_clist_sort(clist);
985 }
986
987 /*
988  * This routine is called to add the dissector selection list to a
989  * notebook page.  This scrolled list contains an entry labeled
990  * "default", and an entry for each protocol that has had a dissector
991  * registered.  The default choice for the list is set to the
992  * "default" choice, which will return the protocol/port selections to
993  * their original dissector(s).
994  *
995  * @param page A pointer to the notebook page currently being created.
996  *
997  * @param table_name The name of the dissector table to use to build
998  * this (clist) menu.
999  *
1000  * @return GtkWidget * A pointer to the newly created clist within a
1001  * scrolled window.
1002  */
1003 static GtkWidget *
1004 decode_add_simple_menu (GtkWidget *page, gchar *table_name)
1005 {
1006     GtkWidget *scrolled_window;
1007     GtkCList  *clist;
1008
1009     decode_clist_menu_start(page, &clist, &scrolled_window);
1010     dissector_table_foreach_handle(table_name, decode_add_to_clist, clist);
1011     decode_clist_menu_finish(clist);
1012     return(scrolled_window);
1013 }
1014
1015 /**************************************************/
1016 /*                  Dialog setup                  */
1017 /**************************************************/
1018
1019 /*
1020  * This routine creates a sample notebook page in the dialog box.
1021  * This notebook page provides a prompt specifying what is being
1022  * changed and its current value (e.g. "IP Protocol number (17)"), and
1023  * a clist specifying all the available choices.  The list of choices
1024  * is conditionally enabled, based upon the setting of the
1025  * "decode"/"do not decode" radio buttons.
1026  *
1027  * @param prompt The prompt for this notebook page
1028  *
1029  * @param title A title for this page to use when debugging.
1030  *
1031  * @param table_name The name of the dissector table to use to
1032  * build this page.
1033  *
1034  * @param value The protocol/port value that is to be changed.
1035  *
1036  * @return GtkWidget * A pointer to the notebook page created by this
1037  * routine.
1038  */
1039 static GtkWidget *
1040 decode_add_simple_page (gchar *prompt, gchar *title, gchar *table_name,
1041                         gint value)
1042 {
1043     GtkWidget   *page, *label, *scrolled_window;
1044
1045     page = gtk_hbox_new(FALSE, 5);
1046     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_simple);
1047     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TABLE, table_name);
1048     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, title);
1049     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_VALUE, GINT_TO_POINTER(value));
1050
1051     /* Always enabled */
1052     label = gtk_label_new(prompt);
1053     gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
1054
1055     /* Conditionally enabled - only when decoding packets */
1056     label = gtk_label_new("as");
1057     gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
1058     decode_dimmable = g_slist_prepend(decode_dimmable, label);
1059     scrolled_window = decode_add_simple_menu(page, table_name);
1060     gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1061     decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
1062
1063     return(page);
1064 }
1065
1066
1067 /*
1068  * This routine creates the TCP or UDP notebook page in the dialog box.
1069  * All items created by this routine are packed into a single
1070  * horizontal box.  First is a label indicating whether the port(s) for
1071  * which the user can set the dissection is a TCP port or a UDP port.
1072  * Second is a menu allowing the user to select whether the source port,
1073  * destination port, or both ports will have dissectors added for them.
1074  * Last is a (conditionally enabled) popup menu listing all possible
1075  * dissectors that can be used to decode the packets, and the choice
1076  * or returning to the default dissector for these ports.
1077  *
1078  * The defaults for these items are the transport layer protocol of
1079  * the currently selected packet, the source port of the currently
1080  * selected packet, and the "default dissector".
1081  *
1082  * @param prompt The prompt for this notebook page
1083  *
1084  * @param table_name The name of the dissector table to use to
1085  * build this page.
1086  *
1087  * @return GtkWidget * A pointer to the notebook page created by
1088  * this routine.
1089  */
1090 static GtkWidget *
1091 decode_add_tcpudp_page (gchar *prompt, gchar *table_name)
1092 {
1093     GtkWidget   *page, *label, *scrolled_window, *optmenu;
1094
1095     page = gtk_hbox_new(FALSE, 5);
1096     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_transport);
1097     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TABLE, table_name);
1098     gtk_object_set_data(GTK_OBJECT(page), E_PAGE_TITLE, "Transport");
1099
1100     /* Always enabled */
1101     label = gtk_label_new(prompt);
1102     gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
1103     optmenu = decode_add_srcdst_menu(page);
1104     gtk_box_pack_start(GTK_BOX(page), optmenu, TRUE, TRUE, 0);
1105     label = gtk_label_new("port(s)");
1106     gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
1107
1108     /* Conditionally enabled - only when decoding packets */
1109     label = gtk_label_new("as");
1110     gtk_box_pack_start(GTK_BOX(page), label, TRUE, TRUE, 0);
1111     decode_dimmable = g_slist_prepend(decode_dimmable, label);
1112     scrolled_window = decode_add_simple_menu(page, table_name);
1113     gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
1114     decode_dimmable = g_slist_prepend(decode_dimmable, scrolled_window);
1115
1116     return(page);
1117 }
1118
1119 /*
1120  * This routine indicates whether we'd actually have any pages in the
1121  * notebook in a "Decode As" dialog box; if there wouldn't be, we
1122  * inactivate the menu item for "Decode As".
1123  */
1124 gboolean
1125 decode_as_ok(void)
1126 {
1127     return cfile.edt->pi.ethertype || cfile.edt->pi.ipproto ||
1128         cfile.edt->pi.ptype == PT_TCP || cfile.edt->pi.ptype == PT_UDP;
1129 }
1130
1131
1132 /*
1133  * This routine creates the bulk of the "Decode As" dialog box.  All
1134  * items created by this routine are packed as pages into a notebook.
1135  * There will be a page for each protocol layer that can be change.
1136  *
1137  * @param GtkWidget * A pointer to the widget in which the notebook
1138  * should be installed.
1139  */
1140 static void
1141 decode_add_notebook (GtkWidget *format_hb)
1142 {
1143     GtkWidget *notebook, *page, *label;
1144     gchar buffer[40];
1145
1146     /* Start a nootbook for flipping between sets of changes */
1147     notebook = gtk_notebook_new();
1148     gtk_container_add(GTK_CONTAINER(format_hb), notebook);
1149     gtk_object_set_data(GTK_OBJECT(decode_w), E_NOTEBOOK, notebook);
1150
1151     /* Add link level selection page */
1152     if (cfile.edt->pi.ethertype) {
1153         sprintf(buffer, "Ethertype 0x%04x", cfile.edt->pi.ethertype);
1154         page = decode_add_simple_page(buffer, "Link", "ethertype", cfile.edt->pi.ethertype);
1155         label = gtk_label_new("Link");
1156         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1157     }
1158
1159     /* Add network selection page */
1160     if (cfile.edt->pi.ipproto) {
1161         /*
1162          * The network-layer protocol is IP.
1163          */
1164         sprintf(buffer, "IP protocol %u", cfile.edt->pi.ipproto);
1165         page = decode_add_simple_page(buffer, "Network", "ip.proto", cfile.edt->pi.ipproto);
1166         gtk_object_set_data(GTK_OBJECT(page), E_PAGE_ACTION, decode_simple);
1167         label = gtk_label_new("Network");
1168         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1169     }
1170
1171     /* Add transport selection page */
1172     switch (cfile.edt->pi.ptype) {
1173
1174     case PT_TCP:
1175         page = decode_add_tcpudp_page("TCP", "tcp.port");
1176         break;
1177
1178     case PT_UDP:
1179         page = decode_add_tcpudp_page("UDP", "udp.port");
1180         break;
1181
1182     default:
1183         page = NULL;
1184         break;
1185     }
1186     if (page != NULL) {
1187         label = gtk_label_new("Transport");
1188         gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
1189     }
1190
1191     /* Select the last added page (selects first by default) */
1192     /* Notebook must be visible for set_page to work. */
1193     gtk_widget_show_all(notebook);
1194     gtk_notebook_set_page(GTK_NOTEBOOK(notebook), -1);
1195 }
1196
1197
1198 /*
1199  * This routine creates the "Decode As" dialog box. This dialog box
1200  * asks the user which protocol to use for decoding the currently
1201  * selected packet.  This will affect the last packet that we called a
1202  * dissection routine on belongs (this might be the most recently
1203  * selected packet, or it might be the last packet in the file).
1204  *
1205  * This routine uses an auxiliary function to create the bulk of the
1206  * dialog box, and then hand crafts the button box at the bottom of
1207  * the dialog.
1208  *
1209  * @param w Unknown
1210  * @param data Unknown
1211  */
1212 void
1213 decode_as_cb (GtkWidget * w, gpointer data)
1214 {
1215     GtkWidget   *main_vb, *format_hb, *bbox, *ok_bt, *cancel_bt, *button;
1216     GtkWidget   *button_vb;
1217
1218     if (decode_w != NULL) {
1219         /* There's already a "Decode As" dialog box; reactivate it. */
1220         reactivate_window(decode_w);
1221         return;
1222     }
1223
1224     requested_action = E_DECODE_YES;
1225     decode_w = dlg_window_new("Ethereal: Decode As");
1226     gtk_signal_connect(GTK_OBJECT(decode_w), "delete_event",
1227                        GTK_SIGNAL_FUNC(decode_delete_cb), NULL);
1228     gtk_signal_connect(GTK_OBJECT(decode_w), "destroy",
1229                        GTK_SIGNAL_FUNC(decode_destroy_cb), NULL);
1230   
1231     /* Container for each row of widgets */
1232     main_vb = gtk_vbox_new(FALSE, 2);
1233     gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
1234     gtk_container_add(GTK_CONTAINER(decode_w), main_vb);
1235
1236     /* First row - Buttons and Notebook */
1237     {
1238         format_hb = gtk_hbox_new(FALSE, 5);
1239         gtk_box_pack_start(GTK_BOX(main_vb), format_hb, TRUE, TRUE, 10);
1240
1241         button_vb = decode_add_yes_no();
1242         gtk_box_pack_start(GTK_BOX(format_hb), button_vb, TRUE, TRUE, 10);
1243
1244         decode_add_notebook(format_hb);
1245     }
1246
1247     /* Button row: OK and cancel buttons */
1248     bbox = gtk_hbutton_box_new();
1249     gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_END);
1250     gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
1251     gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 10);
1252
1253     ok_bt = gtk_button_new_with_label("OK");
1254     gtk_signal_connect(GTK_OBJECT(ok_bt), "clicked",
1255                        GTK_SIGNAL_FUNC(decode_ok_cb),
1256                        GTK_OBJECT(decode_w));
1257     GTK_WIDGET_SET_FLAGS(ok_bt, GTK_CAN_DEFAULT);
1258     gtk_box_pack_start(GTK_BOX(bbox), ok_bt, FALSE, FALSE, 0);
1259     gtk_widget_grab_default(ok_bt);
1260
1261     button = gtk_button_new_with_label("Show Current");
1262     gtk_signal_connect(GTK_OBJECT(button), "clicked",
1263                        GTK_SIGNAL_FUNC(decode_show_cb),
1264                        GTK_OBJECT(decode_w));
1265     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
1266     gtk_box_pack_start(GTK_BOX(bbox), button, FALSE, FALSE, 0);
1267
1268     cancel_bt = gtk_button_new_with_label("Cancel");
1269     gtk_signal_connect(GTK_OBJECT(cancel_bt), "clicked",
1270                        GTK_SIGNAL_FUNC(decode_cancel_cb),
1271                        GTK_OBJECT(decode_w));
1272     GTK_WIDGET_SET_FLAGS(cancel_bt, GTK_CAN_DEFAULT);
1273     gtk_box_pack_start(GTK_BOX(bbox), cancel_bt, FALSE, FALSE, 0);
1274
1275     /*
1276      * Catch the "key_press_event" signal in the window, so that
1277      * we can catch the ESC key being pressed and act as if the
1278      * "Cancel" button had been selected.
1279      */
1280     dlg_set_cancel(decode_w, cancel_bt);
1281
1282     gtk_widget_show_all(decode_w);
1283 }
1284
1285
1286 /*
1287  * Local Variables:
1288  * mode:c
1289  * c-basic-offset: 4
1290  * End:
1291  */